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