Skip to main content

steel_core/worldgen/feature/features/
huge_mushroom.rs

1#![expect(
2    clippy::fn_params_excessive_bools,
3    reason = "mushroom cap side flags mirror vanilla block properties"
4)]
5
6use steel_registry::vanilla_block_tags::BlockTag;
7
8use super::super::prelude::*;
9use super::super::runner::FeatureDecorationRunner;
10
11#[derive(Clone, Copy)]
12enum HugeMushroomKind {
13    Brown,
14    Red,
15}
16
17impl FeatureDecorationRunner {
18    pub(in crate::worldgen::feature) fn place_huge_brown_mushroom_feature(
19        region: &mut WorldGenRegion<'_>,
20        registry: &Registry,
21        random: &mut WorldgenRandom,
22        config: &HugeMushroomConfiguration,
23        origin: BlockPos,
24    ) -> bool {
25        Self::place_huge_mushroom_feature(
26            region,
27            registry,
28            random,
29            config,
30            origin,
31            HugeMushroomKind::Brown,
32        )
33    }
34
35    pub(in crate::worldgen::feature) fn place_huge_red_mushroom_feature(
36        region: &mut WorldGenRegion<'_>,
37        registry: &Registry,
38        random: &mut WorldgenRandom,
39        config: &HugeMushroomConfiguration,
40        origin: BlockPos,
41    ) -> bool {
42        Self::place_huge_mushroom_feature(
43            region,
44            registry,
45            random,
46            config,
47            origin,
48            HugeMushroomKind::Red,
49        )
50    }
51
52    fn place_huge_mushroom_feature(
53        region: &mut WorldGenRegion<'_>,
54        registry: &Registry,
55        random: &mut WorldgenRandom,
56        config: &HugeMushroomConfiguration,
57        origin: BlockPos,
58        kind: HugeMushroomKind,
59    ) -> bool {
60        let tree_height = Self::huge_mushroom_tree_height(random);
61        if !Self::is_valid_huge_mushroom_position(
62            region,
63            registry,
64            config,
65            origin,
66            tree_height,
67            kind,
68        ) {
69            return false;
70        }
71
72        match kind {
73            HugeMushroomKind::Brown => {
74                Self::make_brown_mushroom_cap(
75                    region,
76                    registry,
77                    random,
78                    config,
79                    origin,
80                    tree_height,
81                );
82            }
83            HugeMushroomKind::Red => {
84                Self::make_red_mushroom_cap(region, registry, random, config, origin, tree_height);
85            }
86        }
87        Self::place_huge_mushroom_trunk(region, registry, random, config, origin, tree_height);
88        true
89    }
90
91    fn huge_mushroom_tree_height(random: &mut WorldgenRandom) -> i32 {
92        let mut tree_height = random.next_i32_bounded(3) + 4;
93        if random.next_i32_bounded(12) == 0 {
94            tree_height *= 2;
95        }
96        tree_height
97    }
98
99    fn is_valid_huge_mushroom_position(
100        region: &WorldGenRegion<'_>,
101        registry: &Registry,
102        config: &HugeMushroomConfiguration,
103        origin: BlockPos,
104        tree_height: i32,
105        kind: HugeMushroomKind,
106    ) -> bool {
107        if origin.y() < region.min_y() + 1
108            || origin.y() + tree_height + 2 > region.max_y_exclusive()
109        {
110            return false;
111        }
112
113        if !Self::test_block_predicate(region, registry, &config.can_place_on, origin.below()) {
114            return false;
115        }
116
117        for dy in 0..=tree_height {
118            let radius =
119                Self::huge_mushroom_tree_radius_for_height(kind, -1, -1, config.foliage_radius, dy);
120            for dx in -radius..=radius {
121                for dz in -radius..=radius {
122                    let state = region.block_state(origin.offset(dx, dy, dz));
123                    if !state.is_air() && !state.get_block().has_tag(&BlockTag::LEAVES) {
124                        return false;
125                    }
126                }
127            }
128        }
129
130        true
131    }
132
133    fn make_brown_mushroom_cap(
134        region: &mut WorldGenRegion<'_>,
135        registry: &Registry,
136        random: &mut WorldgenRandom,
137        config: &HugeMushroomConfiguration,
138        origin: BlockPos,
139        tree_height: i32,
140    ) {
141        let radius = config.foliage_radius;
142        for dx in -radius..=radius {
143            for dz in -radius..=radius {
144                let min_x = dx == -radius;
145                let max_x = dx == radius;
146                let min_z = dz == -radius;
147                let max_z = dz == radius;
148                let x_edge = min_x || max_x;
149                let z_edge = min_z || max_z;
150                if x_edge && z_edge {
151                    continue;
152                }
153
154                let pos = origin.offset(dx, tree_height, dz);
155                let west = min_x || z_edge && dx == 1 - radius;
156                let east = max_x || z_edge && dx == radius - 1;
157                let north = min_z || x_edge && dz == 1 - radius;
158                let south = max_z || x_edge && dz == radius - 1;
159                let mut state = Self::sample_block_state_provider(
160                    region,
161                    registry,
162                    random,
163                    &config.cap_provider,
164                    origin,
165                );
166
167                if Self::has_mushroom_horizontal_properties(state) {
168                    state =
169                        Self::set_mushroom_horizontal_properties(state, west, east, north, south);
170                }
171
172                Self::place_huge_mushroom_block(region, pos, state);
173            }
174        }
175    }
176
177    fn make_red_mushroom_cap(
178        region: &mut WorldGenRegion<'_>,
179        registry: &Registry,
180        random: &mut WorldgenRandom,
181        config: &HugeMushroomConfiguration,
182        origin: BlockPos,
183        tree_height: i32,
184    ) {
185        for dy in tree_height - 3..=tree_height {
186            let radius = if dy < tree_height {
187                config.foliage_radius
188            } else {
189                config.foliage_radius - 1
190            };
191            let center = config.foliage_radius - 2;
192
193            for dx in -radius..=radius {
194                for dz in -radius..=radius {
195                    let min_x = dx == -radius;
196                    let max_x = dx == radius;
197                    let min_z = dz == -radius;
198                    let max_z = dz == radius;
199                    let x_edge = min_x || max_x;
200                    let z_edge = min_z || max_z;
201                    if dy < tree_height && x_edge == z_edge {
202                        continue;
203                    }
204
205                    let pos = origin.offset(dx, dy, dz);
206                    let mut state = Self::sample_block_state_provider(
207                        region,
208                        registry,
209                        random,
210                        &config.cap_provider,
211                        origin,
212                    );
213
214                    if Self::has_mushroom_horizontal_properties(state)
215                        && state.try_get_value(&BlockStateProperties::UP).is_some()
216                    {
217                        state = state.set_value(&BlockStateProperties::UP, dy >= tree_height - 1);
218                        state = Self::set_mushroom_horizontal_properties(
219                            state,
220                            dx < -center,
221                            dx > center,
222                            dz < -center,
223                            dz > center,
224                        );
225                    }
226
227                    Self::place_huge_mushroom_block(region, pos, state);
228                }
229            }
230        }
231    }
232
233    fn place_huge_mushroom_trunk(
234        region: &mut WorldGenRegion<'_>,
235        registry: &Registry,
236        random: &mut WorldgenRandom,
237        config: &HugeMushroomConfiguration,
238        origin: BlockPos,
239        tree_height: i32,
240    ) {
241        for dy in 0..tree_height {
242            let pos = origin.above_n(dy);
243            let state = Self::sample_block_state_provider(
244                region,
245                registry,
246                random,
247                &config.stem_provider,
248                origin,
249            );
250            Self::place_huge_mushroom_block(region, pos, state);
251        }
252    }
253
254    fn place_huge_mushroom_block(
255        region: &mut WorldGenRegion<'_>,
256        pos: BlockPos,
257        state: BlockStateId,
258    ) {
259        let current_state = region.block_state(pos);
260        if current_state.is_air()
261            || current_state
262                .get_block()
263                .has_tag(&BlockTag::REPLACEABLE_BY_MUSHROOMS)
264        {
265            let _ = region.set_block_state(pos, state, UpdateFlags::UPDATE_ALL);
266        }
267    }
268
269    fn has_mushroom_horizontal_properties(state: BlockStateId) -> bool {
270        state.try_get_value(&BlockStateProperties::WEST).is_some()
271            && state.try_get_value(&BlockStateProperties::EAST).is_some()
272            && state.try_get_value(&BlockStateProperties::NORTH).is_some()
273            && state.try_get_value(&BlockStateProperties::SOUTH).is_some()
274    }
275
276    fn set_mushroom_horizontal_properties(
277        mut state: BlockStateId,
278        west: bool,
279        east: bool,
280        north: bool,
281        south: bool,
282    ) -> BlockStateId {
283        state = state.set_value(&BlockStateProperties::WEST, west);
284        state = state.set_value(&BlockStateProperties::EAST, east);
285        state = state.set_value(&BlockStateProperties::NORTH, north);
286        state.set_value(&BlockStateProperties::SOUTH, south)
287    }
288
289    const fn huge_mushroom_tree_radius_for_height(
290        kind: HugeMushroomKind,
291        _trunk_height: i32,
292        tree_height: i32,
293        leaf_radius: i32,
294        y_offset: i32,
295    ) -> i32 {
296        match kind {
297            HugeMushroomKind::Brown => {
298                if y_offset <= 3 {
299                    0
300                } else {
301                    leaf_radius
302                }
303            }
304            HugeMushroomKind::Red => {
305                if (y_offset < tree_height && y_offset >= tree_height - 3)
306                    || y_offset == tree_height
307                {
308                    leaf_radius
309                } else {
310                    0
311                }
312            }
313        }
314    }
315}
316
317#[cfg(test)]
318mod tests {
319    use super::*;
320
321    #[test]
322    fn huge_red_mushroom_validation_radius_matches_vanilla_sentinel_call() {
323        assert_eq!(
324            FeatureDecorationRunner::huge_mushroom_tree_radius_for_height(
325                HugeMushroomKind::Red,
326                -1,
327                -1,
328                3,
329                4,
330            ),
331            0
332        );
333    }
334
335    #[test]
336    fn huge_brown_mushroom_validation_radius_ignores_tree_height_like_vanilla() {
337        assert_eq!(
338            FeatureDecorationRunner::huge_mushroom_tree_radius_for_height(
339                HugeMushroomKind::Brown,
340                -1,
341                -1,
342                3,
343                4,
344            ),
345            3
346        );
347    }
348}