1use super::super::prelude::*;
2use super::super::runner::FeatureDecorationRunner;
3use smallvec::SmallVec;
4use steel_registry::vanilla_block_tags::BlockTag;
5
6#[derive(Clone, Copy)]
7enum MultifaceSpreadType {
8 SamePosition,
9 SamePlane,
10 WrapAround,
11}
12
13struct MultifaceSpreadPos {
14 pos: BlockPos,
15 face: Direction,
16}
17
18struct ResolvedMultifaceGrowth<'a> {
19 raw: &'a MultifaceGrowthConfiguration,
20 place_block: BlockRef,
21 default_state: BlockStateId,
22 can_be_placed_on: &'a [BlockRef],
23 is_sculk_vein: bool,
24}
25
26type MultifaceDirections = SmallVec<[Direction; 6]>;
27
28impl FeatureDecorationRunner {
29 pub(in crate::worldgen::feature) fn place_multiface_growth_feature(
30 region: &mut WorldGenRegion<'_>,
31 registry: &Registry,
32 random: &mut WorldgenRandom,
33 config: &MultifaceGrowthConfiguration,
34 origin: BlockPos,
35 ) -> bool {
36 let origin_state = region.block_state(origin);
37 if !Self::multiface_is_air_or_water(origin_state) {
38 return false;
39 }
40
41 let resolved_config = Self::resolve_multiface_growth_config(registry, config);
42 let search_directions = Self::multiface_shuffled_valid_directions(random, &resolved_config);
43 if Self::place_multiface_growth_if_possible(
44 region,
45 random,
46 origin,
47 origin_state,
48 &resolved_config,
49 &search_directions,
50 ) {
51 return true;
52 }
53
54 for search_direction in &search_directions {
55 let placement_directions = Self::multiface_shuffled_valid_directions_except(
56 random,
57 &resolved_config,
58 search_direction.opposite(),
59 );
60
61 for _ in 0..resolved_config.raw.search_range {
62 let pos = origin.relative(*search_direction);
63 let state = region.block_state(pos);
64 if !Self::multiface_is_air_or_water(state)
65 && !Self::multiface_is_place_block(&resolved_config, state)
66 {
67 break;
68 }
69
70 if Self::place_multiface_growth_if_possible(
71 region,
72 random,
73 pos,
74 state,
75 &resolved_config,
76 &placement_directions,
77 ) {
78 return true;
79 }
80 }
81 }
82
83 false
84 }
85
86 fn place_multiface_growth_if_possible(
87 region: &mut WorldGenRegion<'_>,
88 random: &mut WorldgenRandom,
89 pos: BlockPos,
90 old_state: BlockStateId,
91 config: &ResolvedMultifaceGrowth<'_>,
92 placement_directions: &[Direction],
93 ) -> bool {
94 for placement_direction in placement_directions {
95 let neighbor_state = region.block_state(pos.relative(*placement_direction));
96 if !Self::multiface_can_be_placed_on(config, neighbor_state) {
97 continue;
98 }
99
100 let Some(new_state) = Self::multiface_state_for_placement(
101 region,
102 config,
103 old_state,
104 pos,
105 *placement_direction,
106 ) else {
107 return false;
108 };
109
110 let _ = region.set_block_state(pos, new_state, UpdateFlags::UPDATE_ALL);
111 region.mark_pos_for_postprocessing(pos);
112 if random.next_f32() < config.raw.chance_of_spreading {
113 let _ = Self::spread_multiface_from_face_toward_random_direction(
114 region,
115 random,
116 config,
117 new_state,
118 pos,
119 *placement_direction,
120 true,
121 );
122 }
123
124 return true;
125 }
126
127 false
128 }
129
130 fn spread_multiface_from_face_toward_random_direction(
131 region: &mut WorldGenRegion<'_>,
132
133 random: &mut WorldgenRandom,
134 config: &ResolvedMultifaceGrowth<'_>,
135 state: BlockStateId,
136 pos: BlockPos,
137 starting_face: Direction,
138 post_process: bool,
139 ) -> Option<MultifaceSpreadPos> {
140 let directions = Self::shuffled_directions(random, Self::VANILLA_DIRECTION_VALUES);
141 for spread_direction in directions {
142 if let Some(spread_pos) = Self::spread_multiface_from_face_toward_direction(
143 region,
144 config,
145 state,
146 pos,
147 starting_face,
148 spread_direction,
149 post_process,
150 ) {
151 return Some(spread_pos);
152 }
153 }
154
155 None
156 }
157
158 fn spread_multiface_from_face_toward_direction(
159 region: &mut WorldGenRegion<'_>,
160 config: &ResolvedMultifaceGrowth<'_>,
161 state: BlockStateId,
162 pos: BlockPos,
163 starting_face: Direction,
164 spread_direction: Direction,
165 post_process: bool,
166 ) -> Option<MultifaceSpreadPos> {
167 let spread_pos = Self::multiface_spread_from_face_toward_direction(
168 region,
169 config,
170 state,
171 pos,
172 starting_face,
173 spread_direction,
174 )?;
175 if Self::spread_multiface_to_face(region, config, &spread_pos, post_process) {
176 Some(spread_pos)
177 } else {
178 None
179 }
180 }
181
182 fn multiface_spread_from_face_toward_direction(
183 region: &WorldGenRegion<'_>,
184 config: &ResolvedMultifaceGrowth<'_>,
185 state: BlockStateId,
186 pos: BlockPos,
187 starting_face: Direction,
188 spread_direction: Direction,
189 ) -> Option<MultifaceSpreadPos> {
190 if spread_direction.axis() == starting_face.axis() {
191 return None;
192 }
193
194 if !Self::multiface_is_other_block_valid_as_source(config, state)
195 && (!Self::multiface_has_face(state, starting_face)
196 || Self::multiface_has_face(state, spread_direction))
197 {
198 return None;
199 }
200
201 for spread_type in Self::multiface_spread_types(config) {
202 let spread_pos =
203 Self::multiface_spread_pos(pos, spread_direction, starting_face, spread_type);
204 if Self::multiface_can_spread_into(region, config, pos, &spread_pos) {
205 return Some(spread_pos);
206 }
207 }
208
209 None
210 }
211
212 fn spread_multiface_to_face(
213 region: &mut WorldGenRegion<'_>,
214 config: &ResolvedMultifaceGrowth<'_>,
215 spread_pos: &MultifaceSpreadPos,
216 post_process: bool,
217 ) -> bool {
218 let old_state = region.block_state(spread_pos.pos);
219 let Some(spread_state) = Self::multiface_state_for_placement(
220 region,
221 config,
222 old_state,
223 spread_pos.pos,
224 spread_pos.face,
225 ) else {
226 return false;
227 };
228
229 if post_process {
230 region.mark_pos_for_postprocessing(spread_pos.pos);
231 }
232 region.set_block_state(spread_pos.pos, spread_state, UpdateFlags::UPDATE_CLIENTS)
233 }
234
235 fn multiface_can_spread_into(
236 region: &WorldGenRegion<'_>,
237
238 config: &ResolvedMultifaceGrowth<'_>,
239 source_pos: BlockPos,
240 spread_pos: &MultifaceSpreadPos,
241 ) -> bool {
242 let existing_state = region.block_state(spread_pos.pos);
243 Self::multiface_state_can_be_replaced(
244 region,
245 config,
246 source_pos,
247 spread_pos.pos,
248 spread_pos.face,
249 existing_state,
250 ) && Self::multiface_is_valid_state_for_placement(
251 region,
252 config,
253 existing_state,
254 spread_pos.pos,
255 spread_pos.face,
256 )
257 }
258
259 fn multiface_state_can_be_replaced(
260 region: &WorldGenRegion<'_>,
261
262 config: &ResolvedMultifaceGrowth<'_>,
263 source_pos: BlockPos,
264 placement_pos: BlockPos,
265 placement_direction: Direction,
266 existing_state: BlockStateId,
267 ) -> bool {
268 if config.is_sculk_vein {
269 return Self::sculk_vein_state_can_be_replaced(
270 region,
271 source_pos,
272 placement_pos,
273 placement_direction,
274 existing_state,
275 );
276 }
277
278 Self::default_multiface_state_can_be_replaced(config, existing_state)
279 }
280
281 fn sculk_vein_state_can_be_replaced(
282 region: &WorldGenRegion<'_>,
283 source_pos: BlockPos,
284 placement_pos: BlockPos,
285 placement_direction: Direction,
286 existing_state: BlockStateId,
287 ) -> bool {
288 let against_state = region.block_state(placement_pos.relative(placement_direction));
289 if against_state.get_block() == &vanilla_blocks::SCULK
290 || against_state.get_block() == &vanilla_blocks::SCULK_CATALYST
291 || against_state.get_block() == &vanilla_blocks::MOVING_PISTON
292 {
293 return false;
294 }
295
296 if Self::manhattan_distance(source_pos, placement_pos) == 2 {
297 let neighbor_pos = source_pos.relative(placement_direction.opposite());
298 if region
299 .block_state(neighbor_pos)
300 .is_face_sturdy_at(neighbor_pos, placement_direction)
301 {
302 return false;
303 }
304 }
305
306 let fluid_state = get_fluid_state_from_block(existing_state);
307 if !fluid_state.is_empty() && !fluid_state.is_water() {
308 return false;
309 }
310
311 if existing_state.get_block().has_tag(&BlockTag::FIRE) {
312 return false;
313 }
314
315 existing_state.is_replaceable()
316 || Self::default_multiface_state_can_be_replaced_for_block(
317 existing_state,
318 &vanilla_blocks::SCULK_VEIN,
319 )
320 }
321
322 fn default_multiface_state_can_be_replaced(
323 config: &ResolvedMultifaceGrowth<'_>,
324 existing_state: BlockStateId,
325 ) -> bool {
326 Self::default_multiface_state_can_be_replaced_for_block(existing_state, config.place_block)
327 }
328
329 fn default_multiface_state_can_be_replaced_for_block(
330 existing_state: BlockStateId,
331 place_block: BlockRef,
332 ) -> bool {
333 existing_state.is_air()
334 || existing_state.get_block() == place_block
335 || (existing_state.get_block() == &vanilla_blocks::WATER
336 && get_fluid_state_from_block(existing_state).is_source())
337 }
338
339 fn multiface_state_for_placement(
340 region: &WorldGenRegion<'_>,
341 config: &ResolvedMultifaceGrowth<'_>,
342 old_state: BlockStateId,
343 placement_pos: BlockPos,
344 placement_direction: Direction,
345 ) -> Option<BlockStateId> {
346 if !Self::multiface_is_valid_state_for_placement(
347 region,
348 config,
349 old_state,
350 placement_pos,
351 placement_direction,
352 ) {
353 return None;
354 }
355
356 let mut new_state = if old_state.get_block() == config.place_block {
357 old_state
358 } else {
359 let fluid_state = get_fluid_state_from_block(old_state);
360 if fluid_state.is_water() && fluid_state.is_source() {
361 config
362 .default_state
363 .set_value(&BlockStateProperties::WATERLOGGED, true)
364 } else {
365 config.default_state
366 }
367 };
368 new_state = new_state.set_value(Self::multiface_face_property(placement_direction), true);
369 Some(new_state)
370 }
371
372 fn multiface_is_valid_state_for_placement(
373 region: &WorldGenRegion<'_>,
374 config: &ResolvedMultifaceGrowth<'_>,
375 old_state: BlockStateId,
376 placement_pos: BlockPos,
377 placement_direction: Direction,
378 ) -> bool {
379 if old_state.get_block() == config.place_block
380 && Self::multiface_has_face(old_state, placement_direction)
381 {
382 return false;
383 }
384
385 Self::can_attach_to_multiface(region, placement_pos, placement_direction)
386 }
387
388 fn resolve_multiface_growth_config<'a>(
389 registry: &Registry,
390 config: &'a MultifaceGrowthConfiguration,
391 ) -> ResolvedMultifaceGrowth<'a> {
392 ResolvedMultifaceGrowth {
393 raw: config,
394 place_block: config.block,
395 default_state: registry.blocks.get_default_state_id(config.block),
396 can_be_placed_on: &config.can_be_placed_on,
397 is_sculk_vein: config.block == &vanilla_blocks::SCULK_VEIN,
398 }
399 }
400
401 fn multiface_can_be_placed_on(
402 config: &ResolvedMultifaceGrowth<'_>,
403 state: BlockStateId,
404 ) -> bool {
405 config
406 .can_be_placed_on
407 .iter()
408 .any(|block| state.get_block() == *block)
409 }
410
411 fn multiface_is_place_block(config: &ResolvedMultifaceGrowth<'_>, state: BlockStateId) -> bool {
412 state.get_block() == config.place_block
413 }
414
415 fn multiface_is_other_block_valid_as_source(
416 config: &ResolvedMultifaceGrowth<'_>,
417 state: BlockStateId,
418 ) -> bool {
419 config.is_sculk_vein && state.get_block() != &vanilla_blocks::SCULK_VEIN
420 }
421
422 const fn multiface_spread_types(
423 _config: &ResolvedMultifaceGrowth<'_>,
424 ) -> [MultifaceSpreadType; 3] {
425 [
426 MultifaceSpreadType::SamePosition,
427 MultifaceSpreadType::SamePlane,
428 MultifaceSpreadType::WrapAround,
429 ]
430 }
431
432 fn multiface_spread_pos(
433 pos: BlockPos,
434 spread_direction: Direction,
435 from_face: Direction,
436 spread_type: MultifaceSpreadType,
437 ) -> MultifaceSpreadPos {
438 match spread_type {
439 MultifaceSpreadType::SamePosition => MultifaceSpreadPos {
440 pos,
441 face: spread_direction,
442 },
443 MultifaceSpreadType::SamePlane => MultifaceSpreadPos {
444 pos: pos.relative(spread_direction),
445 face: from_face,
446 },
447 MultifaceSpreadType::WrapAround => MultifaceSpreadPos {
448 pos: pos.relative(spread_direction).relative(from_face),
449 face: spread_direction.opposite(),
450 },
451 }
452 }
453
454 fn multiface_shuffled_valid_directions(
455 random: &mut WorldgenRandom,
456 config: &ResolvedMultifaceGrowth<'_>,
457 ) -> MultifaceDirections {
458 let mut directions = Self::multiface_valid_directions(config);
459 Self::shuffle_multiface_directions(random, &mut directions);
460 directions
461 }
462
463 fn multiface_shuffled_valid_directions_except(
464 random: &mut WorldgenRandom,
465 config: &ResolvedMultifaceGrowth<'_>,
466 excluded: Direction,
467 ) -> MultifaceDirections {
468 let mut directions = Self::multiface_valid_directions(config);
469 directions.retain(|direction| *direction != excluded);
470 Self::shuffle_multiface_directions(random, &mut directions);
471 directions
472 }
473
474 fn multiface_valid_directions(config: &ResolvedMultifaceGrowth<'_>) -> MultifaceDirections {
475 let mut directions = MultifaceDirections::new();
476 if config.raw.can_place_on_ceiling {
477 directions.push(Direction::Up);
478 }
479 if config.raw.can_place_on_floor {
480 directions.push(Direction::Down);
481 }
482 if config.raw.can_place_on_wall {
483 directions.extend_from_slice(&Self::VANILLA_HORIZONTAL_DIRECTIONS);
484 }
485 directions
486 }
487
488 fn shuffle_multiface_directions(random: &mut WorldgenRandom, directions: &mut [Direction]) {
489 for i in (1..directions.len()).rev() {
490 let Ok(bound) = i32::try_from(i + 1) else {
491 panic!("multiface direction shuffle length exceeds i32 range");
492 };
493 let j = random.next_i32_bounded(bound) as usize;
494 directions.swap(i, j);
495 }
496 }
497
498 fn multiface_has_face(state: BlockStateId, direction: Direction) -> bool {
499 state
500 .try_get_value(Self::multiface_face_property(direction))
501 .unwrap_or(false)
502 }
503
504 const fn multiface_face_property(direction: Direction) -> &'static BoolProperty {
505 match direction {
506 Direction::Up => &BlockStateProperties::UP,
507 Direction::Down => &BlockStateProperties::DOWN,
508 Direction::North => &BlockStateProperties::NORTH,
509 Direction::South => &BlockStateProperties::SOUTH,
510 Direction::East => &BlockStateProperties::EAST,
511 Direction::West => &BlockStateProperties::WEST,
512 }
513 }
514
515 fn multiface_is_air_or_water(state: BlockStateId) -> bool {
516 state.is_air() || state.get_block() == &vanilla_blocks::WATER
517 }
518}