Skip to main content

steel_core/worldgen/feature/features/
vegetation_patch.rs

1use super::super::prelude::*;
2use super::super::runner::FeatureDecorationRunner;
3use super::super::vanilla_collections::JavaBlockPosSet;
4
5impl FeatureDecorationRunner {
6    pub(in crate::worldgen::feature) fn place_vegetation_patch_feature(
7        region: &mut WorldGenRegion<'_>,
8        registry: &Registry,
9        random: &mut WorldgenRandom,
10        config: &VegetationPatchConfiguration,
11        origin: BlockPos,
12        biome_zoom_seed: i64,
13    ) -> bool {
14        let x_radius = config.xz_radius.sample(random) + 1;
15        let z_radius = config.xz_radius.sample(random) + 1;
16        let surface = Self::place_vegetation_patch_ground(
17            region, registry, random, config, origin, x_radius, z_radius,
18        );
19        Self::distribute_vegetation_patch(
20            region,
21            registry,
22            random,
23            config,
24            &surface,
25            biome_zoom_seed,
26            false,
27        );
28        !surface.is_empty()
29    }
30
31    pub(in crate::worldgen::feature) fn place_waterlogged_vegetation_patch_feature(
32        region: &mut WorldGenRegion<'_>,
33        registry: &Registry,
34        random: &mut WorldgenRandom,
35        config: &VegetationPatchConfiguration,
36        origin: BlockPos,
37        biome_zoom_seed: i64,
38    ) -> bool {
39        let x_radius = config.xz_radius.sample(random) + 1;
40        let z_radius = config.xz_radius.sample(random) + 1;
41        let surface = Self::place_vegetation_patch_ground(
42            region, registry, random, config, origin, x_radius, z_radius,
43        );
44        let water_surface = Self::waterlogged_vegetation_patch_surface(region, &surface);
45        for surface_pos in water_surface.java_ordered_positions() {
46            let _ = region.set_block_state(
47                surface_pos,
48                vanilla_blocks::WATER.default_state(),
49                UpdateFlags::UPDATE_CLIENTS,
50            );
51        }
52
53        Self::distribute_vegetation_patch(
54            region,
55            registry,
56            random,
57            config,
58            &water_surface,
59            biome_zoom_seed,
60            true,
61        );
62        !water_surface.is_empty()
63    }
64
65    fn place_vegetation_patch_ground(
66        region: &mut WorldGenRegion<'_>,
67        registry: &Registry,
68        random: &mut WorldgenRandom,
69        config: &VegetationPatchConfiguration,
70        origin: BlockPos,
71        x_radius: i32,
72        z_radius: i32,
73    ) -> JavaBlockPosSet {
74        let inwards = Self::vegetation_patch_surface_direction(config.surface);
75        let outwards = inwards.opposite();
76        let mut surface = JavaBlockPosSet::default();
77
78        for dx in -x_radius..=x_radius {
79            let at_longitude_limit = dx == -x_radius || dx == x_radius;
80            for dz in -z_radius..=z_radius {
81                let at_latitude_limit = dz == -z_radius || dz == z_radius;
82                let is_corner = at_longitude_limit && at_latitude_limit;
83                let is_edge_but_not_corner =
84                    (at_longitude_limit || at_latitude_limit) && !is_corner;
85
86                if is_corner {
87                    continue;
88                }
89                if is_edge_but_not_corner
90                    && (config.extra_edge_column_chance == 0.0
91                        || random.next_f32() > config.extra_edge_column_chance)
92                {
93                    continue;
94                }
95
96                let mut pos = origin.offset(dx, 0, dz);
97                for _ in 0..config.vertical_range {
98                    if !region.block_state(pos).is_air() {
99                        break;
100                    }
101                    pos = pos.relative(inwards);
102                }
103                for _ in 0..config.vertical_range {
104                    if region.block_state(pos).is_air() {
105                        break;
106                    }
107                    pos = pos.relative(outwards);
108                }
109
110                let below_pos = pos.relative(inwards);
111                let below_state = region.block_state(below_pos);
112                if !region.block_state(pos).is_air()
113                    || !below_state.is_face_sturdy_at(below_pos, outwards)
114                {
115                    continue;
116                }
117
118                let sampled_depth = config.depth.sample(random);
119                let extra_depth = i32::from(
120                    config.extra_bottom_block_chance > 0.0
121                        && random.next_f32() < config.extra_bottom_block_chance,
122                );
123                let depth = sampled_depth + extra_depth;
124                let mut ground_cursor = below_pos;
125                if Self::place_vegetation_patch_ground_column(
126                    region,
127                    registry,
128                    random,
129                    config,
130                    &mut ground_cursor,
131                    inwards,
132                    depth,
133                ) {
134                    surface.insert(below_pos);
135                }
136            }
137        }
138
139        surface
140    }
141
142    fn place_vegetation_patch_ground_column(
143        region: &mut WorldGenRegion<'_>,
144        registry: &Registry,
145        random: &mut WorldgenRandom,
146        config: &VegetationPatchConfiguration,
147        pos: &mut BlockPos,
148        inwards: Direction,
149        depth: i32,
150    ) -> bool {
151        for i in 0..depth {
152            let state_to_place = Self::sample_block_state_provider(
153                region,
154                registry,
155                random,
156                &config.ground_state,
157                *pos,
158            );
159            let current_state = region.block_state(*pos);
160            if state_to_place.get_block() != current_state.get_block() {
161                if !registry
162                    .blocks
163                    .is_in_tag(current_state.get_block(), &config.replaceable)
164                {
165                    return i != 0;
166                }
167
168                let _ = region.set_block_state(*pos, state_to_place, UpdateFlags::UPDATE_CLIENTS);
169                *pos = (*pos).relative(inwards);
170            }
171        }
172
173        true
174    }
175
176    fn distribute_vegetation_patch(
177        region: &mut WorldGenRegion<'_>,
178        registry: &Registry,
179        random: &mut WorldgenRandom,
180        config: &VegetationPatchConfiguration,
181        surface: &JavaBlockPosSet,
182        biome_zoom_seed: i64,
183        waterlogged: bool,
184    ) {
185        if config.vegetation_chance <= 0.0 {
186            return;
187        }
188
189        let outwards = Self::vegetation_patch_surface_direction(config.surface).opposite();
190        for surface_pos in surface.java_ordered_positions() {
191            if random.next_f32() >= config.vegetation_chance {
192                continue;
193            }
194
195            let vegetation_origin = if waterlogged {
196                surface_pos.below().relative(outwards)
197            } else {
198                surface_pos.relative(outwards)
199            };
200            let placed = Self::place_placed_feature_ref(
201                region,
202                registry,
203                random,
204                vegetation_origin,
205                &config.vegetation_feature,
206                biome_zoom_seed,
207            );
208            if waterlogged && placed {
209                let state = region.block_state(surface_pos);
210                if let Some(false) = state.try_get_value(&BlockStateProperties::WATERLOGGED) {
211                    let _ = region.set_block_state(
212                        surface_pos,
213                        state.set_value(&BlockStateProperties::WATERLOGGED, true),
214                        UpdateFlags::UPDATE_CLIENTS,
215                    );
216                }
217            }
218        }
219    }
220
221    fn waterlogged_vegetation_patch_surface(
222        region: &WorldGenRegion<'_>,
223        surface: &JavaBlockPosSet,
224    ) -> JavaBlockPosSet {
225        let mut water_surface = JavaBlockPosSet::default();
226        for pos in surface.java_ordered_positions() {
227            if !Self::vegetation_patch_surface_exposed(region, pos) {
228                water_surface.insert(pos);
229            }
230        }
231        water_surface
232    }
233
234    fn vegetation_patch_surface_exposed(region: &WorldGenRegion<'_>, pos: BlockPos) -> bool {
235        [
236            Direction::North,
237            Direction::East,
238            Direction::South,
239            Direction::West,
240            Direction::Down,
241        ]
242        .into_iter()
243        .any(|direction| {
244            let neighbor_pos = pos.relative(direction);
245            !region
246                .block_state(neighbor_pos)
247                .is_face_sturdy_at(neighbor_pos, direction.opposite())
248        })
249    }
250
251    const fn vegetation_patch_surface_direction(surface: VerticalSurface) -> Direction {
252        match surface {
253            VerticalSurface::Floor => Direction::Down,
254            VerticalSurface::Ceiling => Direction::Up,
255        }
256    }
257}