steel_core/worldgen/feature/features/
stepped_column_cluster.rs1use 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}