Skip to main content

steel_core/worldgen/feature/features/
basalt_columns.rs

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