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