steel_core/worldgen/feature/features/
huge_mushroom.rs1#![expect(
2 clippy::fn_params_excessive_bools,
3 reason = "mushroom cap side flags mirror vanilla block properties"
4)]
5
6use steel_registry::vanilla_block_tags::BlockTag;
7
8use super::super::prelude::*;
9use super::super::runner::FeatureDecorationRunner;
10
11#[derive(Clone, Copy)]
12enum HugeMushroomKind {
13 Brown,
14 Red,
15}
16
17impl FeatureDecorationRunner {
18 pub(in crate::worldgen::feature) fn place_huge_brown_mushroom_feature(
19 region: &mut WorldGenRegion<'_>,
20 registry: &Registry,
21 random: &mut WorldgenRandom,
22 config: &HugeMushroomConfiguration,
23 origin: BlockPos,
24 ) -> bool {
25 Self::place_huge_mushroom_feature(
26 region,
27 registry,
28 random,
29 config,
30 origin,
31 HugeMushroomKind::Brown,
32 )
33 }
34
35 pub(in crate::worldgen::feature) fn place_huge_red_mushroom_feature(
36 region: &mut WorldGenRegion<'_>,
37 registry: &Registry,
38 random: &mut WorldgenRandom,
39 config: &HugeMushroomConfiguration,
40 origin: BlockPos,
41 ) -> bool {
42 Self::place_huge_mushroom_feature(
43 region,
44 registry,
45 random,
46 config,
47 origin,
48 HugeMushroomKind::Red,
49 )
50 }
51
52 fn place_huge_mushroom_feature(
53 region: &mut WorldGenRegion<'_>,
54 registry: &Registry,
55 random: &mut WorldgenRandom,
56 config: &HugeMushroomConfiguration,
57 origin: BlockPos,
58 kind: HugeMushroomKind,
59 ) -> bool {
60 let tree_height = Self::huge_mushroom_tree_height(random);
61 if !Self::is_valid_huge_mushroom_position(
62 region,
63 registry,
64 config,
65 origin,
66 tree_height,
67 kind,
68 ) {
69 return false;
70 }
71
72 match kind {
73 HugeMushroomKind::Brown => {
74 Self::make_brown_mushroom_cap(
75 region,
76 registry,
77 random,
78 config,
79 origin,
80 tree_height,
81 );
82 }
83 HugeMushroomKind::Red => {
84 Self::make_red_mushroom_cap(region, registry, random, config, origin, tree_height);
85 }
86 }
87 Self::place_huge_mushroom_trunk(region, registry, random, config, origin, tree_height);
88 true
89 }
90
91 fn huge_mushroom_tree_height(random: &mut WorldgenRandom) -> i32 {
92 let mut tree_height = random.next_i32_bounded(3) + 4;
93 if random.next_i32_bounded(12) == 0 {
94 tree_height *= 2;
95 }
96 tree_height
97 }
98
99 fn is_valid_huge_mushroom_position(
100 region: &WorldGenRegion<'_>,
101 registry: &Registry,
102 config: &HugeMushroomConfiguration,
103 origin: BlockPos,
104 tree_height: i32,
105 kind: HugeMushroomKind,
106 ) -> bool {
107 if origin.y() < region.min_y() + 1
108 || origin.y() + tree_height + 2 > region.max_y_exclusive()
109 {
110 return false;
111 }
112
113 if !Self::test_block_predicate(region, registry, &config.can_place_on, origin.below()) {
114 return false;
115 }
116
117 for dy in 0..=tree_height {
118 let radius =
119 Self::huge_mushroom_tree_radius_for_height(kind, -1, -1, config.foliage_radius, dy);
120 for dx in -radius..=radius {
121 for dz in -radius..=radius {
122 let state = region.block_state(origin.offset(dx, dy, dz));
123 if !state.is_air() && !state.get_block().has_tag(&BlockTag::LEAVES) {
124 return false;
125 }
126 }
127 }
128 }
129
130 true
131 }
132
133 fn make_brown_mushroom_cap(
134 region: &mut WorldGenRegion<'_>,
135 registry: &Registry,
136 random: &mut WorldgenRandom,
137 config: &HugeMushroomConfiguration,
138 origin: BlockPos,
139 tree_height: i32,
140 ) {
141 let radius = config.foliage_radius;
142 for dx in -radius..=radius {
143 for dz in -radius..=radius {
144 let min_x = dx == -radius;
145 let max_x = dx == radius;
146 let min_z = dz == -radius;
147 let max_z = dz == radius;
148 let x_edge = min_x || max_x;
149 let z_edge = min_z || max_z;
150 if x_edge && z_edge {
151 continue;
152 }
153
154 let pos = origin.offset(dx, tree_height, dz);
155 let west = min_x || z_edge && dx == 1 - radius;
156 let east = max_x || z_edge && dx == radius - 1;
157 let north = min_z || x_edge && dz == 1 - radius;
158 let south = max_z || x_edge && dz == radius - 1;
159 let mut state = Self::sample_block_state_provider(
160 region,
161 registry,
162 random,
163 &config.cap_provider,
164 origin,
165 );
166
167 if Self::has_mushroom_horizontal_properties(state) {
168 state =
169 Self::set_mushroom_horizontal_properties(state, west, east, north, south);
170 }
171
172 Self::place_huge_mushroom_block(region, pos, state);
173 }
174 }
175 }
176
177 fn make_red_mushroom_cap(
178 region: &mut WorldGenRegion<'_>,
179 registry: &Registry,
180 random: &mut WorldgenRandom,
181 config: &HugeMushroomConfiguration,
182 origin: BlockPos,
183 tree_height: i32,
184 ) {
185 for dy in tree_height - 3..=tree_height {
186 let radius = if dy < tree_height {
187 config.foliage_radius
188 } else {
189 config.foliage_radius - 1
190 };
191 let center = config.foliage_radius - 2;
192
193 for dx in -radius..=radius {
194 for dz in -radius..=radius {
195 let min_x = dx == -radius;
196 let max_x = dx == radius;
197 let min_z = dz == -radius;
198 let max_z = dz == radius;
199 let x_edge = min_x || max_x;
200 let z_edge = min_z || max_z;
201 if dy < tree_height && x_edge == z_edge {
202 continue;
203 }
204
205 let pos = origin.offset(dx, dy, dz);
206 let mut state = Self::sample_block_state_provider(
207 region,
208 registry,
209 random,
210 &config.cap_provider,
211 origin,
212 );
213
214 if Self::has_mushroom_horizontal_properties(state)
215 && state.try_get_value(&BlockStateProperties::UP).is_some()
216 {
217 state = state.set_value(&BlockStateProperties::UP, dy >= tree_height - 1);
218 state = Self::set_mushroom_horizontal_properties(
219 state,
220 dx < -center,
221 dx > center,
222 dz < -center,
223 dz > center,
224 );
225 }
226
227 Self::place_huge_mushroom_block(region, pos, state);
228 }
229 }
230 }
231 }
232
233 fn place_huge_mushroom_trunk(
234 region: &mut WorldGenRegion<'_>,
235 registry: &Registry,
236 random: &mut WorldgenRandom,
237 config: &HugeMushroomConfiguration,
238 origin: BlockPos,
239 tree_height: i32,
240 ) {
241 for dy in 0..tree_height {
242 let pos = origin.above_n(dy);
243 let state = Self::sample_block_state_provider(
244 region,
245 registry,
246 random,
247 &config.stem_provider,
248 origin,
249 );
250 Self::place_huge_mushroom_block(region, pos, state);
251 }
252 }
253
254 fn place_huge_mushroom_block(
255 region: &mut WorldGenRegion<'_>,
256 pos: BlockPos,
257 state: BlockStateId,
258 ) {
259 let current_state = region.block_state(pos);
260 if current_state.is_air()
261 || current_state
262 .get_block()
263 .has_tag(&BlockTag::REPLACEABLE_BY_MUSHROOMS)
264 {
265 let _ = region.set_block_state(pos, state, UpdateFlags::UPDATE_ALL);
266 }
267 }
268
269 fn has_mushroom_horizontal_properties(state: BlockStateId) -> bool {
270 state.try_get_value(&BlockStateProperties::WEST).is_some()
271 && state.try_get_value(&BlockStateProperties::EAST).is_some()
272 && state.try_get_value(&BlockStateProperties::NORTH).is_some()
273 && state.try_get_value(&BlockStateProperties::SOUTH).is_some()
274 }
275
276 fn set_mushroom_horizontal_properties(
277 mut state: BlockStateId,
278 west: bool,
279 east: bool,
280 north: bool,
281 south: bool,
282 ) -> BlockStateId {
283 state = state.set_value(&BlockStateProperties::WEST, west);
284 state = state.set_value(&BlockStateProperties::EAST, east);
285 state = state.set_value(&BlockStateProperties::NORTH, north);
286 state.set_value(&BlockStateProperties::SOUTH, south)
287 }
288
289 const fn huge_mushroom_tree_radius_for_height(
290 kind: HugeMushroomKind,
291 _trunk_height: i32,
292 tree_height: i32,
293 leaf_radius: i32,
294 y_offset: i32,
295 ) -> i32 {
296 match kind {
297 HugeMushroomKind::Brown => {
298 if y_offset <= 3 {
299 0
300 } else {
301 leaf_radius
302 }
303 }
304 HugeMushroomKind::Red => {
305 if (y_offset < tree_height && y_offset >= tree_height - 3)
306 || y_offset == tree_height
307 {
308 leaf_radius
309 } else {
310 0
311 }
312 }
313 }
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[test]
322 fn huge_red_mushroom_validation_radius_matches_vanilla_sentinel_call() {
323 assert_eq!(
324 FeatureDecorationRunner::huge_mushroom_tree_radius_for_height(
325 HugeMushroomKind::Red,
326 -1,
327 -1,
328 3,
329 4,
330 ),
331 0
332 );
333 }
334
335 #[test]
336 fn huge_brown_mushroom_validation_radius_ignores_tree_height_like_vanilla() {
337 assert_eq!(
338 FeatureDecorationRunner::huge_mushroom_tree_radius_for_height(
339 HugeMushroomKind::Brown,
340 -1,
341 -1,
342 3,
343 4,
344 ),
345 3
346 );
347 }
348}