Skip to main content

steel_core/worldgen/feature/features/
lake.rs

1use super::super::prelude::*;
2use super::super::runner::FeatureDecorationRunner;
3
4impl FeatureDecorationRunner {
5    pub(in crate::worldgen::feature) fn place_lake_feature(
6        region: &mut WorldGenRegion<'_>,
7        registry: &Registry,
8        random: &mut WorldgenRandom,
9        config: &LakeConfiguration,
10        origin: BlockPos,
11        biome_zoom_seed: i64,
12    ) -> bool {
13        if origin.y() <= region.min_y() + 4 {
14            return false;
15        }
16
17        let origin = origin.offset(-8, -4, -8);
18        let mut grid = vec![false; 16 * 16 * 8];
19        let spots = random.next_i32_bounded(4) + 4;
20        for _ in 0..spots {
21            Self::carve_lake_ellipsoid(random, &mut grid);
22        }
23
24        let fluid = BlockStateProviderEvaluator::sample_block_state_provider(
25            region,
26            registry,
27            random,
28            &config.fluid,
29            origin,
30        );
31        if !Self::lake_boundary_valid(region, registry, config, &grid, origin, fluid) {
32            return false;
33        }
34
35        Self::place_lake_contents(region, registry, config, &grid, origin, fluid);
36        Self::place_lake_barrier(region, registry, random, config, &grid, origin);
37        if get_fluid_state_from_block(fluid).is_water() {
38            Self::freeze_lake_surface(region, registry, config, biome_zoom_seed, origin);
39        }
40
41        true
42    }
43
44    fn carve_lake_ellipsoid(random: &mut WorldgenRandom, grid: &mut [bool]) {
45        let x_radius = random.next_f64() * 6.0 + 3.0;
46        let y_radius = random.next_f64() * 4.0 + 2.0;
47        let z_radius = random.next_f64() * 6.0 + 3.0;
48        let x_center = random.next_f64() * (16.0 - x_radius - 2.0) + 1.0 + x_radius / 2.0;
49        let y_center = random.next_f64() * (8.0 - y_radius - 4.0) + 2.0 + y_radius / 2.0;
50        let z_center = random.next_f64() * (16.0 - z_radius - 2.0) + 1.0 + z_radius / 2.0;
51
52        for x in 1..15 {
53            for z in 1..15 {
54                for y in 1..7 {
55                    let x_delta = (f64::from(x) - x_center) / (x_radius / 2.0);
56                    let y_delta = (f64::from(y) - y_center) / (y_radius / 2.0);
57                    let z_delta = (f64::from(z) - z_center) / (z_radius / 2.0);
58                    if x_delta * x_delta + y_delta * y_delta + z_delta * z_delta < 1.0 {
59                        grid[Self::lake_index(x, y, z)] = true;
60                    }
61                }
62            }
63        }
64    }
65
66    fn lake_boundary_valid(
67        region: &WorldGenRegion<'_>,
68        registry: &Registry,
69        config: &LakeConfiguration,
70        grid: &[bool],
71        origin: BlockPos,
72        fluid: BlockStateId,
73    ) -> bool {
74        for x in 0..16 {
75            for z in 0..16 {
76                for y in 0..8 {
77                    if !Self::lake_is_boundary(grid, x, y, z) {
78                        continue;
79                    }
80
81                    let state = region.block_state(origin.offset(x, y, z));
82                    if y >= 4 && !get_fluid_state_from_block(state).is_empty() {
83                        return false;
84                    }
85
86                    if y < 4 && !state.is_solid() && state != fluid {
87                        return false;
88                    }
89
90                    if !BlockStateProviderEvaluator::test_block_predicate(
91                        region,
92                        registry,
93                        &config.can_place_feature,
94                        origin.offset(x, y, z),
95                    ) {
96                        return false;
97                    }
98                }
99            }
100        }
101
102        true
103    }
104
105    fn place_lake_contents(
106        region: &mut WorldGenRegion<'_>,
107        registry: &Registry,
108        config: &LakeConfiguration,
109        grid: &[bool],
110        origin: BlockPos,
111        fluid: BlockStateId,
112    ) {
113        let cave_air = vanilla_blocks::CAVE_AIR.default_state();
114        for x in 0..16 {
115            for z in 0..16 {
116                for y in 0..8 {
117                    if !grid[Self::lake_index(x, y, z)] {
118                        continue;
119                    }
120
121                    let pos = origin.offset(x, y, z);
122                    if !BlockStateProviderEvaluator::test_block_predicate(
123                        region,
124                        registry,
125                        &config.can_replace_with_air_or_fluid,
126                        pos,
127                    ) {
128                        continue;
129                    }
130
131                    if y >= 4 {
132                        let _ = region.set_block_state(pos, cave_air, UpdateFlags::UPDATE_CLIENTS);
133                        let _ = region.schedule_block_tick_default(pos, cave_air.get_block(), 0);
134                        Self::mark_above_for_postprocessing(region, pos);
135                    } else {
136                        let _ = region.set_block_state(pos, fluid, UpdateFlags::UPDATE_CLIENTS);
137                    }
138                }
139            }
140        }
141    }
142
143    fn place_lake_barrier(
144        region: &mut WorldGenRegion<'_>,
145        registry: &Registry,
146        random: &mut WorldgenRandom,
147        config: &LakeConfiguration,
148        grid: &[bool],
149        origin: BlockPos,
150    ) {
151        let barrier = BlockStateProviderEvaluator::sample_block_state_provider(
152            region,
153            registry,
154            random,
155            &config.barrier,
156            origin,
157        );
158        if barrier.is_air() {
159            return;
160        }
161
162        for x in 0..16 {
163            for z in 0..16 {
164                for y in 0..8 {
165                    if !Self::lake_is_boundary(grid, x, y, z)
166                        || (y >= 4 && random.next_i32_bounded(2) == 0)
167                    {
168                        continue;
169                    }
170
171                    let pos = origin.offset(x, y, z);
172                    let state = region.block_state(pos);
173                    if state.is_solid()
174                        && BlockStateProviderEvaluator::test_block_predicate(
175                            region,
176                            registry,
177                            &config.can_replace_with_barrier,
178                            pos,
179                        )
180                    {
181                        let _ = region.set_block_state(pos, barrier, UpdateFlags::UPDATE_CLIENTS);
182                        Self::mark_above_for_postprocessing(region, pos);
183                    }
184                }
185            }
186        }
187    }
188
189    fn freeze_lake_surface(
190        region: &mut WorldGenRegion<'_>,
191        registry: &Registry,
192        config: &LakeConfiguration,
193        biome_zoom_seed: i64,
194        origin: BlockPos,
195    ) {
196        let ice = vanilla_blocks::ICE.default_state();
197        for x in 0..16 {
198            for z in 0..16 {
199                let pos = origin.offset(x, 4, z);
200                if Self::should_freeze(region, registry, biome_zoom_seed, pos, false)
201                    && BlockStateProviderEvaluator::test_block_predicate(
202                        region,
203                        registry,
204                        &config.can_replace_with_air_or_fluid,
205                        pos,
206                    )
207                {
208                    let _ = region.set_block_state(pos, ice, UpdateFlags::UPDATE_CLIENTS);
209                }
210            }
211        }
212    }
213
214    const fn lake_is_boundary(grid: &[bool], x: i32, y: i32, z: i32) -> bool {
215        !grid[Self::lake_index(x, y, z)]
216            && ((x < 15 && grid[Self::lake_index(x + 1, y, z)])
217                || (x > 0 && grid[Self::lake_index(x - 1, y, z)])
218                || (z < 15 && grid[Self::lake_index(x, y, z + 1)])
219                || (z > 0 && grid[Self::lake_index(x, y, z - 1)])
220                || (y < 7 && grid[Self::lake_index(x, y + 1, z)])
221                || (y > 0 && grid[Self::lake_index(x, y - 1, z)]))
222    }
223
224    const fn lake_index(x: i32, y: i32, z: i32) -> usize {
225        ((x * 16 + z) * 8 + y) as usize
226    }
227}