Skip to main content

steel_core/worldgen/feature/features/
huge_fungus.rs

1use super::super::prelude::*;
2use super::super::runner::FeatureDecorationRunner;
3
4impl FeatureDecorationRunner {
5    pub(in crate::worldgen::feature) fn place_huge_fungus_feature(
6        region: &mut WorldGenRegion<'_>,
7        registry: &Registry,
8        random: &mut WorldgenRandom,
9        config: &HugeFungusConfiguration,
10        origin: BlockPos,
11    ) -> bool {
12        Self::place_huge_fungus(
13            region,
14            registry,
15            random,
16            config,
17            origin,
18            region.generation_height(),
19        )
20    }
21
22    pub(crate) fn place_planted_huge_fungus_feature(
23        level: &impl LevelAccessor,
24        registry: &Registry,
25        random: &mut WorldgenRandom,
26        config: &HugeFungusConfiguration,
27        origin: BlockPos,
28    ) -> bool {
29        Self::place_huge_fungus(
30            level,
31            registry,
32            random,
33            config,
34            origin,
35            level.max_y_exclusive(),
36        )
37    }
38
39    fn place_huge_fungus(
40        level: &impl LevelAccessor,
41        registry: &Registry,
42        random: &mut WorldgenRandom,
43        config: &HugeFungusConfiguration,
44        origin: BlockPos,
45        generation_height: i32,
46    ) -> bool {
47        let valid_base_state = Self::block_state_from_data(registry, &config.valid_base_block);
48        if level.get_block_state(origin.below()).get_block() != valid_base_state.get_block() {
49            return false;
50        }
51
52        let mut total_height = random.next_i32_between(4, 13);
53        if random.next_i32_bounded(12) == 0 {
54            total_height *= 2;
55        }
56
57        if !config.planted && origin.y() + total_height + 1 >= generation_height {
58            return false;
59        }
60
61        let stem_state = Self::block_state_from_data(registry, &config.stem_state);
62        let hat_state = Self::block_state_from_data(registry, &config.hat_state);
63        let decor_state = Self::block_state_from_data(registry, &config.decor_state);
64        let is_huge = !config.planted && random.next_f32() < 0.06;
65        let _ = level.set_block_state(
66            origin,
67            vanilla_blocks::AIR.default_state(),
68            UpdateFlags::UPDATE_NONE,
69        );
70
71        Self::place_huge_fungus_stem(
72            level,
73            registry,
74            random,
75            config,
76            origin,
77            stem_state,
78            total_height,
79            is_huge,
80        );
81        Self::place_huge_fungus_hat(
82            level,
83            registry,
84            random,
85            config,
86            origin,
87            hat_state,
88            decor_state,
89            total_height,
90            is_huge,
91        );
92        true
93    }
94
95    #[expect(
96        clippy::too_many_arguments,
97        reason = "mirrors vanilla HugeFungusFeature.placeStem state"
98    )]
99    fn place_huge_fungus_stem(
100        level: &impl LevelAccessor,
101        registry: &Registry,
102        random: &mut WorldgenRandom,
103        config: &HugeFungusConfiguration,
104        origin: BlockPos,
105        stem_state: BlockStateId,
106        total_height: i32,
107        is_huge: bool,
108    ) {
109        let stem_radius: i32 = i32::from(is_huge);
110
111        for dx in -stem_radius..=stem_radius {
112            for dz in -stem_radius..=stem_radius {
113                let corner_of_huge_stem =
114                    is_huge && dx.abs() == stem_radius && dz.abs() == stem_radius;
115
116                for dy in 0..total_height {
117                    let pos = origin.offset(dx, dy, dz);
118                    if !Self::huge_fungus_is_replaceable(level, registry, pos, config, true) {
119                        continue;
120                    }
121
122                    if config.planted {
123                        if !level.get_block_state(pos.below()).is_air() {
124                            let _ = level.destroy_block(pos, true);
125                        }
126
127                        let _ = level.set_block_state(pos, stem_state, UpdateFlags::UPDATE_ALL);
128                    } else if corner_of_huge_stem {
129                        if random.next_f32() < 0.1 {
130                            let _ = level.set_block_state(pos, stem_state, UpdateFlags::UPDATE_ALL);
131                        }
132                    } else {
133                        let _ = level.set_block_state(pos, stem_state, UpdateFlags::UPDATE_ALL);
134                    }
135                }
136            }
137        }
138    }
139
140    #[expect(
141        clippy::too_many_arguments,
142        reason = "mirrors vanilla HugeFungusFeature.placeHat state"
143    )]
144    fn place_huge_fungus_hat(
145        level: &impl LevelAccessor,
146        registry: &Registry,
147        random: &mut WorldgenRandom,
148        config: &HugeFungusConfiguration,
149        origin: BlockPos,
150        hat_state: BlockStateId,
151        decor_state: BlockStateId,
152        total_height: i32,
153        is_huge: bool,
154    ) {
155        let place_vines = hat_state.get_block() == &vanilla_blocks::NETHER_WART_BLOCK;
156        let hat_height = (random.next_i32_bounded(1 + total_height / 3) + 5).min(total_height);
157        let hat_start_y = total_height - hat_height;
158
159        for dy in hat_start_y..=total_height {
160            let mut radius = if dy < total_height - random.next_i32_bounded(3) {
161                2
162            } else {
163                1
164            };
165            if hat_height > 8 && dy < hat_start_y + 4 {
166                radius = 3;
167            }
168
169            if is_huge {
170                radius += 1;
171            }
172
173            for dx in -radius..=radius {
174                for dz in -radius..=radius {
175                    let is_edge_x = dx == -radius || dx == radius;
176                    let is_edge_z = dz == -radius || dz == radius;
177                    let inside = !is_edge_x && !is_edge_z && dy != total_height;
178                    let corner = is_edge_x && is_edge_z;
179                    let is_hat_bottom = dy < hat_start_y + 3;
180                    let pos = origin.offset(dx, dy, dz);
181
182                    if !Self::huge_fungus_is_replaceable(level, registry, pos, config, false) {
183                        continue;
184                    }
185
186                    if config.planted && !level.get_block_state(pos.below()).is_air() {
187                        let _ = level.destroy_block(pos, true);
188                    }
189
190                    if is_hat_bottom {
191                        if !inside {
192                            Self::place_huge_fungus_hat_drop_block(
193                                level,
194                                random,
195                                pos,
196                                hat_state,
197                                place_vines,
198                            );
199                        }
200                    } else if inside {
201                        Self::place_huge_fungus_hat_block(
202                            level,
203                            random,
204                            pos,
205                            hat_state,
206                            decor_state,
207                            0.1,
208                            0.2,
209                            if place_vines { 0.1 } else { 0.0 },
210                        );
211                    } else if corner {
212                        Self::place_huge_fungus_hat_block(
213                            level,
214                            random,
215                            pos,
216                            hat_state,
217                            decor_state,
218                            0.01,
219                            0.7,
220                            if place_vines { 0.083 } else { 0.0 },
221                        );
222                    } else {
223                        Self::place_huge_fungus_hat_block(
224                            level,
225                            random,
226                            pos,
227                            hat_state,
228                            decor_state,
229                            5.0E-4,
230                            0.98,
231                            if place_vines { 0.07 } else { 0.0 },
232                        );
233                    }
234                }
235            }
236        }
237    }
238
239    #[expect(
240        clippy::too_many_arguments,
241        reason = "vanilla hat placement uses three independent probabilities"
242    )]
243    fn place_huge_fungus_hat_block(
244        level: &impl LevelAccessor,
245        random: &mut WorldgenRandom,
246        pos: BlockPos,
247        hat_state: BlockStateId,
248        decor_state: BlockStateId,
249        decor_block_probability: f32,
250        hat_block_probability: f32,
251        vines_probability: f32,
252    ) {
253        if random.next_f32() < decor_block_probability {
254            let _ = level.set_block_state(pos, decor_state, UpdateFlags::UPDATE_ALL);
255        } else if random.next_f32() < hat_block_probability {
256            let _ = level.set_block_state(pos, hat_state, UpdateFlags::UPDATE_ALL);
257            if random.next_f32() < vines_probability {
258                Self::try_place_huge_fungus_weeping_vines(level, random, pos);
259            }
260        }
261    }
262
263    fn place_huge_fungus_hat_drop_block(
264        level: &impl LevelAccessor,
265        random: &mut WorldgenRandom,
266        pos: BlockPos,
267        hat_state: BlockStateId,
268        place_vines: bool,
269    ) {
270        if level.get_block_state(pos.below()).get_block() == hat_state.get_block() {
271            let _ = level.set_block_state(pos, hat_state, UpdateFlags::UPDATE_ALL);
272        } else if random.next_f32() < 0.15 {
273            let _ = level.set_block_state(pos, hat_state, UpdateFlags::UPDATE_ALL);
274            if place_vines && random.next_i32_bounded(11) == 0 {
275                Self::try_place_huge_fungus_weeping_vines(level, random, pos);
276            }
277        }
278    }
279
280    fn try_place_huge_fungus_weeping_vines(
281        level: &impl LevelAccessor,
282        random: &mut WorldgenRandom,
283        hat_pos: BlockPos,
284    ) {
285        let place_pos = hat_pos.below();
286        if !level.get_block_state(place_pos).is_air() {
287            return;
288        }
289
290        let mut goal_vine_height = random.next_i32_between(1, 5);
291        if random.next_i32_bounded(7) == 0 {
292            goal_vine_height *= 2;
293        }
294
295        Self::place_weeping_vines_column(level, random, place_pos, goal_vine_height, 23, 25);
296    }
297
298    fn huge_fungus_is_replaceable(
299        level: &impl LevelReader,
300        registry: &Registry,
301        pos: BlockPos,
302        config: &HugeFungusConfiguration,
303        check_non_replaceable_plants: bool,
304    ) -> bool {
305        if level.get_block_state(pos).is_replaceable() {
306            return true;
307        }
308
309        check_non_replaceable_plants
310            && Self::test_block_predicate(level, registry, &config.replaceable_blocks, pos)
311    }
312}