Skip to main content

steel_core/worldgen/feature/features/
stepped_column_cluster.rs

1use super::super::prelude::*;
2use super::super::runner::FeatureDecorationRunner;
3
4impl FeatureDecorationRunner {
5    pub(in crate::worldgen::feature) fn place_stepped_column_cluster_feature(
6        region: &mut WorldGenRegion<'_>,
7        registry: &Registry,
8        random: &mut WorldgenRandom,
9        config: &SteppedColumnClusterConfiguration,
10        origin: BlockPos,
11    ) -> bool {
12        if !Self::can_place_stepped_column_at(region, registry, config, origin) {
13            return false;
14        }
15
16        let column_height = config.height.sample(random);
17        let cluster_reach = column_height.min(config.cluster_reach.sample(random));
18        let column_count = config.column_count.sample(random);
19        let mut placed = false;
20
21        for _ in 0..column_count {
22            let pos = BlockPos::new(
23                origin.x() - cluster_reach + random.next_i32_bounded(cluster_reach * 2 + 1),
24                origin.y() + random.next_i32_bounded(1),
25                origin.z() - cluster_reach + random.next_i32_bounded(cluster_reach * 2 + 1),
26            );
27            let blocks_to_place = column_height - Self::manhattan_distance(pos, origin);
28            if blocks_to_place >= 0 {
29                let column_reach = config.column_reach.sample(random);
30                placed |= Self::place_stepped_column(
31                    region,
32                    registry,
33                    random,
34                    config,
35                    pos,
36                    blocks_to_place,
37                    column_reach,
38                );
39            }
40        }
41
42        placed
43    }
44
45    fn place_stepped_column(
46        region: &mut WorldGenRegion<'_>,
47        registry: &Registry,
48        random: &mut WorldgenRandom,
49        config: &SteppedColumnClusterConfiguration,
50        origin: BlockPos,
51        column_height: i32,
52        reach: i32,
53    ) -> bool {
54        let mut placed_any = false;
55        let min = origin.offset(-reach, 0, -reach);
56        let max = origin.offset(reach, 0, reach);
57
58        for pos in BlockPos::between_closed(min, max) {
59            let step_limit = Self::manhattan_distance(pos, origin);
60            let column_pos = if BlockStateProviderEvaluator::test_block_predicate(
61                region,
62                registry,
63                &config.can_replace,
64                pos,
65            ) {
66                Self::find_stepped_column_surface(region, registry, config, pos, step_limit)
67            } else {
68                Self::find_stepped_column_air(region, config, pos, step_limit)
69            };
70            let Some(mut cursor) = column_pos else {
71                continue;
72            };
73
74            let mut blocks_left = column_height - step_limit / 2;
75            while blocks_left >= 0 {
76                if BlockStateProviderEvaluator::test_block_predicate(
77                    region,
78                    registry,
79                    &config.can_replace,
80                    cursor,
81                ) {
82                    let state = BlockStateProviderEvaluator::sample_block_state_provider(
83                        region,
84                        registry,
85                        random,
86                        &config.block,
87                        cursor,
88                    );
89                    let _ = region.set_block_state(cursor, state, UpdateFlags::UPDATE_ALL);
90                    cursor = cursor.above();
91                    placed_any = true;
92                } else {
93                    if !BlockStateProviderEvaluator::test_block_predicate(
94                        region,
95                        registry,
96                        &config.continue_through,
97                        cursor,
98                    ) {
99                        break;
100                    }
101                    cursor = cursor.above();
102                }
103
104                blocks_left -= 1;
105            }
106        }
107
108        placed_any
109    }
110
111    fn find_stepped_column_surface(
112        region: &WorldGenRegion<'_>,
113        registry: &Registry,
114        config: &SteppedColumnClusterConfiguration,
115        mut cursor: BlockPos,
116        mut limit: i32,
117    ) -> Option<BlockPos> {
118        while cursor.y() > region.min_y() + 1 && limit > 0 {
119            limit -= 1;
120            if Self::can_place_stepped_column_at(region, registry, config, cursor) {
121                return Some(cursor);
122            }
123            cursor = cursor.below();
124        }
125
126        None
127    }
128
129    fn can_place_stepped_column_at(
130        region: &WorldGenRegion<'_>,
131        registry: &Registry,
132        config: &SteppedColumnClusterConfiguration,
133        pos: BlockPos,
134    ) -> bool {
135        if !BlockStateProviderEvaluator::test_block_predicate(
136            region,
137            registry,
138            &config.can_replace,
139            pos,
140        ) {
141            return false;
142        }
143
144        let below = region.block_state(pos.below());
145        !below.is_air()
146            && !Self::block_matches_holder_set(below.get_block(), &config.cannot_place_on)
147    }
148
149    fn find_stepped_column_air(
150        region: &WorldGenRegion<'_>,
151        config: &SteppedColumnClusterConfiguration,
152        mut cursor: BlockPos,
153        mut limit: i32,
154    ) -> Option<BlockPos> {
155        while cursor.y() < region.max_y_exclusive() && limit > 0 {
156            limit -= 1;
157            let state = region.block_state(cursor);
158            if Self::block_matches_holder_set(state.get_block(), &config.cannot_place_on) {
159                return None;
160            }
161            if state.is_air() {
162                return Some(cursor);
163            }
164            cursor = cursor.above();
165        }
166
167        None
168    }
169}