steel_core/worldgen/feature/features/
basalt_columns.rs1use super::super::prelude::*;
2use super::super::runner::FeatureDecorationRunner;
3
4const CLUSTERED_REACH: i32 = 5;
5const CLUSTERED_SIZE: i32 = 50;
6const UNCLUSTERED_REACH: i32 = 8;
7const UNCLUSTERED_SIZE: i32 = 15;
8
9impl FeatureDecorationRunner {
10 pub(in crate::worldgen::feature) fn place_basalt_columns_feature(
11 region: &mut WorldGenRegion<'_>,
12 random: &mut WorldgenRandom,
13 config: &BasaltColumnsConfiguration,
14 origin: BlockPos,
15 ) -> bool {
16 let lava_sea_level = region.sea_level();
17 if !Self::can_place_basalt_column_at(region, lava_sea_level, origin) {
18 return false;
19 }
20
21 let column_height = config.height.sample(random);
22 let generate_clustered = random.next_f32() < 0.9;
23 let reach = column_height.min(if generate_clustered {
24 CLUSTERED_REACH
25 } else {
26 UNCLUSTERED_REACH
27 });
28 let count = if generate_clustered {
29 CLUSTERED_SIZE
30 } else {
31 UNCLUSTERED_SIZE
32 };
33 let mut placed = false;
34
35 for _ in 0..count {
36 let pos = BlockPos::new(
37 origin.x() - reach + random.next_i32_bounded(reach * 2 + 1),
38 origin.y() + random.next_i32_bounded(1),
39 origin.z() - reach + random.next_i32_bounded(reach * 2 + 1),
40 );
41 let blocks_to_place_y = column_height - Self::manhattan_distance(pos, origin);
42 if blocks_to_place_y >= 0 {
43 placed |= Self::place_basalt_column(
44 region,
45 lava_sea_level,
46 pos,
47 blocks_to_place_y,
48 config.reach.sample(random),
49 );
50 }
51 }
52
53 placed
54 }
55
56 fn place_basalt_column(
57 region: &mut WorldGenRegion<'_>,
58 lava_sea_level: i32,
59 origin: BlockPos,
60 column_height: i32,
61 reach: i32,
62 ) -> bool {
63 let mut placed_any = false;
64
65 for z in origin.z() - reach..=origin.z() + reach {
66 for x in origin.x() - reach..=origin.x() + reach {
67 let pos = BlockPos::new(x, origin.y(), z);
68 let step_limit = Self::manhattan_distance(pos, origin);
69 let column_pos = if Self::is_air_or_lava_ocean(region, lava_sea_level, pos) {
70 Self::find_basalt_column_surface(region, lava_sea_level, pos, step_limit)
71 } else {
72 Self::find_basalt_column_air(region, pos, step_limit)
73 };
74
75 let Some(mut cursor) = column_pos else {
76 continue;
77 };
78
79 let mut blocks_y = column_height - step_limit / 2;
80 while blocks_y >= 0 {
81 if Self::is_air_or_lava_ocean(region, lava_sea_level, cursor) {
82 let _ = region.set_block_state(
83 cursor,
84 vanilla_blocks::BASALT.default_state(),
85 UpdateFlags::UPDATE_CLIENTS,
86 );
87 placed_any = true;
88 cursor = cursor.above();
89 } else {
90 if region.block_state(cursor).get_block() != &vanilla_blocks::BASALT {
91 break;
92 }
93 cursor = cursor.above();
94 }
95
96 blocks_y -= 1;
97 }
98 }
99 }
100
101 placed_any
102 }
103
104 fn find_basalt_column_surface(
105 region: &WorldGenRegion<'_>,
106 lava_sea_level: i32,
107 mut cursor: BlockPos,
108 mut limit: i32,
109 ) -> Option<BlockPos> {
110 while cursor.y() > region.min_y() + 1 && limit > 0 {
111 limit -= 1;
112 if Self::can_place_basalt_column_at(region, lava_sea_level, cursor) {
113 return Some(cursor);
114 }
115 cursor = cursor.below();
116 }
117
118 None
119 }
120
121 fn find_basalt_column_air(
122 region: &WorldGenRegion<'_>,
123 mut cursor: BlockPos,
124 mut limit: i32,
125 ) -> Option<BlockPos> {
126 while cursor.y() < region.max_y_exclusive() && limit > 0 {
127 limit -= 1;
128 let state = region.block_state(cursor);
129 if Self::basalt_columns_cannot_place_on(state.get_block()) {
130 return None;
131 }
132
133 if state.is_air() {
134 return Some(cursor);
135 }
136
137 cursor = cursor.above();
138 }
139
140 None
141 }
142
143 fn can_place_basalt_column_at(
144 region: &WorldGenRegion<'_>,
145 lava_sea_level: i32,
146 pos: BlockPos,
147 ) -> bool {
148 if !Self::is_air_or_lava_ocean(region, lava_sea_level, pos) {
149 return false;
150 }
151
152 let below = region.block_state(pos.below());
153 !below.is_air() && !Self::basalt_columns_cannot_place_on(below.get_block())
154 }
155
156 fn is_air_or_lava_ocean(
157 region: &WorldGenRegion<'_>,
158 lava_sea_level: i32,
159 pos: BlockPos,
160 ) -> bool {
161 let state = region.block_state(pos);
162 state.is_air() || state.get_block() == &vanilla_blocks::LAVA && pos.y() <= lava_sea_level
163 }
164
165 fn basalt_columns_cannot_place_on(block: BlockRef) -> bool {
166 block == &vanilla_blocks::LAVA
167 || block == &vanilla_blocks::BEDROCK
168 || block == &vanilla_blocks::MAGMA_BLOCK
169 || block == &vanilla_blocks::SOUL_SAND
170 || block == &vanilla_blocks::NETHER_BRICKS
171 || block == &vanilla_blocks::NETHER_BRICK_FENCE
172 || block == &vanilla_blocks::NETHER_BRICK_STAIRS
173 || block == &vanilla_blocks::NETHER_WART
174 || block == &vanilla_blocks::CHEST
175 || block == &vanilla_blocks::SPAWNER
176 }
177}