steel_core/worldgen/feature/
placement.rs1use super::prelude::*;
2use super::runner::FeatureDecorationRunner;
3
4impl FeatureDecorationRunner {
5 pub(super) const fn feature_heightmap_type(heightmap: FeatureHeightmap) -> HeightmapType {
6 match heightmap {
7 FeatureHeightmap::WorldSurface => HeightmapType::WorldSurface,
8 FeatureHeightmap::MotionBlocking => HeightmapType::MotionBlocking,
9 FeatureHeightmap::MotionBlockingNoLeaves => HeightmapType::MotionBlockingNoLeaves,
10 FeatureHeightmap::OceanFloor => HeightmapType::OceanFloor,
11 FeatureHeightmap::WorldSurfaceWg => HeightmapType::WorldSurfaceWg,
12 FeatureHeightmap::OceanFloorWg => HeightmapType::OceanFloorWg,
13 }
14 }
15
16 pub(super) fn count_on_every_layer_positions(
17 region: &WorldGenRegion<'_>,
18 random: &mut WorldgenRandom,
19 origin: BlockPos,
20 count: &IntProvider,
21 ) -> Vec<BlockPos> {
22 let mut positions = Vec::new();
23 let mut layer = 0;
24
25 loop {
26 let mut found_any = false;
27 for _ in 0..count.sample(random) {
28 let x = origin.x() + random.next_i32_bounded(16);
29 let z = origin.z() + random.next_i32_bounded(16);
30 let start_y = region.height_at(HeightmapType::MotionBlocking, x, z);
31 if let Some(y) = Self::find_on_ground_y_position(region, x, start_y, z, layer) {
32 positions.push(BlockPos::new(x, y, z));
33 found_any = true;
34 }
35 }
36
37 if !found_any {
38 break;
39 }
40 layer += 1;
41 }
42
43 positions
44 }
45
46 pub(super) fn find_on_ground_y_position(
47 region: &WorldGenRegion<'_>,
48 x: i32,
49 start_y: i32,
50 z: i32,
51 layer_to_place_on: i32,
52 ) -> Option<i32> {
53 let mut current_layer = 0;
54 let mut current_block = region.block_state(BlockPos::new(x, start_y, z));
55
56 for y in (region.min_y() + 1..=start_y).rev() {
57 let below_block = region.block_state(BlockPos::new(x, y - 1, z));
58 if !Self::is_empty_layer_block(below_block)
59 && Self::is_empty_layer_block(current_block)
60 && below_block.get_block() != &vanilla_blocks::BEDROCK
61 {
62 if current_layer == layer_to_place_on {
63 return Some(y);
64 }
65 current_layer += 1;
66 }
67
68 current_block = below_block;
69 }
70
71 None
72 }
73
74 pub(super) fn is_empty_layer_block(state: steel_utils::BlockStateId) -> bool {
75 state.is_air()
76 || state.get_block() == &vanilla_blocks::WATER
77 || state.get_block() == &vanilla_blocks::LAVA
78 }
79
80 pub(super) fn environment_scan_position(
81 region: &WorldGenRegion<'_>,
82 registry: &Registry,
83 origin: BlockPos,
84 direction_of_search: steel_utils::Direction,
85 target_condition: &BlockPredicate,
86 allowed_search_condition: Option<&BlockPredicate>,
87 max_steps: i32,
88 ) -> Option<BlockPos> {
89 assert!(
90 max_steps > 0,
91 "environment scan max_steps must be positive, got {max_steps}"
92 );
93
94 let mut position = origin;
95 if !Self::test_optional_block_predicate(
96 region,
97 registry,
98 allowed_search_condition,
99 position,
100 ) {
101 return None;
102 }
103
104 for _ in 0..max_steps {
105 if Self::test_block_predicate(region, registry, target_condition, position) {
106 return Some(position);
107 }
108
109 position = position.relative(direction_of_search);
110 if region.is_outside_build_height(position.y()) {
111 return None;
112 }
113
114 if !Self::test_optional_block_predicate(
115 region,
116 registry,
117 allowed_search_condition,
118 position,
119 ) {
120 break;
121 }
122 }
123
124 if Self::test_block_predicate(region, registry, target_condition, position) {
125 Some(position)
126 } else {
127 None
128 }
129 }
130}