Skip to main content

steel_core/worldgen/feature/
placement.rs

1use 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}