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