1use 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
13pub struct LakeGenerator {
15 fluid_id: u8,
16}
17
18impl LakeGenerator {
19
20 #[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 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 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
155pub struct LiquidGenerator {
157 fluid_id: u8,
158}
159
160impl LiquidGenerator {
161
162 #[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}