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