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