mc173/gen/
liquid.rs

1//! Liquids generation.
2
3use glam::{IVec3, DVec3};
4
5use crate::rand::JavaRandom;
6use crate::world::World;
7use crate::geom::Face;
8use crate::block;
9
10use super::FeatureGenerator;
11
12
13/// A generator for lakes.
14pub struct LakeGenerator {
15    fluid_id: u8,
16}
17
18impl LakeGenerator {
19
20    /// Create a new lake generator for the given block id.
21    #[inline]
22    pub fn new(fluid_id: u8) -> Self {
23        Self { fluid_id, }
24    }
25
26}
27
28impl FeatureGenerator for LakeGenerator {
29
30    fn generate(&mut self, world: &mut World, mut pos: IVec3, rand: &mut JavaRandom) -> bool {
31
32        // Lake have a maximum size of 16x8x16, so we subtract half.
33        pos -= IVec3::new(8, 0, 8);
34        while pos.y > 0 && world.is_block_air(pos) {
35            pos.y -= 1;
36        }
37        pos.y -= 4;
38
39        // [X][Z][Y]
40        let mut fill = Box::new([[[false; 8]; 16]; 16]);
41
42        let count = rand.next_int_bounded(4) + 4;
43        for _ in 0..count {
44
45            let a = rand.next_double_vec() * 
46                DVec3::new(6.0, 4.0, 6.0) + 
47                DVec3::new(3.0, 2.0, 3.0);
48
49            let b = rand.next_double_vec() * 
50                (DVec3::new(16.0, 8.0, 16.0) - a - DVec3::new(2.0, 4.0, 2.0)) +
51                DVec3::new(1.0, 2.0, 1.0) + a / 2.0;
52
53            let a = a / 2.0;
54
55            for dx in 1..15 {
56                for dz in 1..15 {
57                    for dy in 1..7 {
58                        let dist = (DVec3::new(dx as f64, dy as f64, dz as f64) - b) / a;
59                        if dist.length_squared() < 1.0 {
60                            fill[dx][dz][dy] = true;
61                        }
62                    }
63                }
64            }
65
66        }
67
68        for dx in 0..16 {
69            for dz in 0..16 {
70                for dy in 0..8 {
71
72                    let filled = !fill[dx][dz][dy] && (
73                        dx < 15 && fill[dx + 1][dz][dy] ||
74                        dx > 0  && fill[dx - 1][dz][dy] ||
75                        dz < 15 && fill[dx][dz + 1][dy] ||
76                        dz > 0  && fill[dx][dz - 1][dy] ||
77                        dy < 7  && fill[dx][dz][dy + 1] ||
78                        dy > 0  && fill[dx][dz][dy - 1]
79                    );
80
81                    if filled {
82                        let check_pos = pos + IVec3::new(dx as i32, dy as i32, dz as i32);
83                        let check_id = world.get_block(check_pos).map(|(id, _)| id).unwrap_or(block::AIR);
84                        let check_material = block::material::get_material(check_id);
85                        if dy >= 4 && check_material.is_fluid() {
86                            return false;
87                        } else if dy < 4 && !check_material.is_solid() && check_id != self.fluid_id {
88                            return false;
89                        }
90                    }
91
92                }
93            }
94        }
95
96        for dx in 0..16 {
97            for dz in 0..16 {
98                for dy in 0..8 {
99                    if fill[dx][dz][dy] {
100                        let place_pos = pos + IVec3::new(dx as i32, dy as i32, dz as i32);
101                        world.set_block(place_pos, if dy >= 4 { block::AIR } else { self.fluid_id }, 0);
102                    }
103                }
104            }
105        }
106
107        for dx in 0..16 {
108            for dz in 0..16 {
109                for dy in 4..8 {
110                    if fill[dx][dz][dy] {
111                        let check_pos = pos + IVec3::new(dx as i32, dy as i32 - 1, dz as i32);
112                        if world.is_block(check_pos, block::DIRT) {
113                            if world.get_light(check_pos).sky > 0 {
114                                world.set_block(check_pos, block::GRASS, 0);
115                            }
116                        }
117                    }
118                }
119            }
120        }
121
122        if let block::LAVA_STILL | block::LAVA_MOVING = self.fluid_id {
123            for dx in 0..16 {
124                for dz in 0..16 {
125                    for dy in 0..8 {
126
127                        let filled = !fill[dx][dz][dy] && (
128                            dx < 15 && fill[dx + 1][dz][dy] ||
129                            dx > 0  && fill[dx - 1][dz][dy] ||
130                            dz < 15 && fill[dx][dz + 1][dy] ||
131                            dz > 0  && fill[dx][dz - 1][dy] ||
132                            dy < 7  && fill[dx][dz][dy + 1] ||
133                            dy > 0  && fill[dx][dz][dy - 1]
134                        );
135
136                        if filled && (dy < 4 || rand.next_int_bounded(2) != 0) {
137                            let place_pos = pos + IVec3::new(dx as i32, dy as i32, dz as i32);
138                            if world.get_block_material(place_pos).is_solid() {
139                                world.set_block(place_pos, block::STONE, 0);
140                            }
141                        }
142
143                    }
144                }
145            }
146        }
147
148        true
149
150    }
151
152}
153
154
155/// A generator for single liquid blocks.
156pub struct LiquidGenerator {
157    fluid_id: u8,
158}
159
160impl LiquidGenerator {
161    
162    /// Create a new liquid generator for the given block id.
163    #[inline]
164    pub fn new(fluid_id: u8) -> Self {
165        Self { fluid_id, }
166    }
167
168}
169
170impl FeatureGenerator for LiquidGenerator {
171
172    fn generate(&mut self, world: &mut World, pos: IVec3, _rand: &mut JavaRandom) -> bool {
173        
174        if !world.is_block(pos + IVec3::Y, block::STONE) {
175            return false;
176        } else if !world.is_block(pos - IVec3::Y, block::STONE) {
177            return false;
178        } else if !matches!(world.get_block(pos), Some((block::AIR | block::STONE, _))) {
179            return false;
180        }
181
182        let mut stone_count = 0;
183        let mut air_count = 0;
184
185        for face in Face::HORIZONTAL {
186            match world.get_block(pos + face.delta()) {
187                Some((block::STONE, _)) => stone_count += 1,
188                None | Some((block::AIR, _)) => air_count += 1,
189                _ => {}
190            }
191        }
192
193        if stone_count == 3 && air_count == 1 {
194            world.set_block(pos, self.fluid_id, 0);
195        }
196
197        true
198
199    }
200
201}