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}