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}