Skip to main content

steel_core/worldgen/feature/features/tree/
root_system.rs

1use super::super::super::prelude::*;
2use super::super::super::runner::FeatureDecorationRunner;
3
4impl FeatureDecorationRunner {
5    pub(in crate::worldgen::feature) fn place_root_system_feature(
6        region: &mut WorldGenRegion<'_>,
7        registry: &Registry,
8        random: &mut WorldgenRandom,
9        config: &RootSystemConfiguration,
10        origin: BlockPos,
11        biome_zoom_seed: i64,
12    ) -> bool {
13        if !region.block_state(origin).is_air() {
14            return false;
15        }
16
17        let mut tree_pos = origin;
18        if Self::place_root_system_dirt_and_tree(
19            region,
20            registry,
21            random,
22            config,
23            &mut tree_pos,
24            origin,
25            biome_zoom_seed,
26        ) {
27            Self::place_root_system_hanging_roots(region, registry, random, config, origin);
28        }
29
30        true
31    }
32
33    fn place_root_system_dirt_and_tree(
34        region: &mut WorldGenRegion<'_>,
35        registry: &Registry,
36        random: &mut WorldgenRandom,
37        config: &RootSystemConfiguration,
38        tree_pos: &mut BlockPos,
39        origin: BlockPos,
40        biome_zoom_seed: i64,
41    ) -> bool {
42        for y in 0..config.root_column_max_height {
43            *tree_pos = tree_pos.above();
44            if region.height_at(HeightmapType::WorldSurfaceWg, tree_pos.x(), tree_pos.z())
45                < tree_pos.y()
46            {
47                return false;
48            }
49
50            if !Self::test_block_predicate(
51                region,
52                registry,
53                &config.allowed_tree_position,
54                *tree_pos,
55            ) || !Self::root_system_space_for_tree(region, config, *tree_pos)
56            {
57                continue;
58            }
59
60            let below_pos = tree_pos.below();
61            let below_state = region.block_state(below_pos);
62            if get_fluid_state_from_block(below_state).is_lava() || !below_state.is_solid() {
63                return false;
64            }
65
66            if Self::place_placed_feature_ref(
67                region,
68                registry,
69                random,
70                *tree_pos,
71                &config.feature,
72                biome_zoom_seed,
73            ) {
74                Self::place_root_system_dirt(
75                    region,
76                    registry,
77                    random,
78                    config,
79                    origin,
80                    origin.y() + y,
81                );
82                return true;
83            }
84        }
85
86        false
87    }
88
89    fn root_system_space_for_tree(
90        region: &WorldGenRegion<'_>,
91        config: &RootSystemConfiguration,
92        pos: BlockPos,
93    ) -> bool {
94        let mut column_pos = pos;
95        for blocks_above_origin in 1..=config.required_vertical_space_for_tree {
96            column_pos = column_pos.above();
97            let state = region.block_state(column_pos);
98            if !Self::root_system_allowed_tree_space(
99                state,
100                blocks_above_origin,
101                config.allowed_vertical_water_for_tree,
102            ) {
103                return false;
104            }
105        }
106
107        if config.level_test_distance > 0 {
108            for direction in [
109                Direction::South,
110                Direction::West,
111                Direction::North,
112                Direction::East,
113            ] {
114                let corner_pos = pos.relative_n(direction, config.level_test_distance);
115                let below = region.block_state(corner_pos.below_n(config.max_level_deviation));
116                let above = region.block_state(corner_pos.above_n(config.max_level_deviation));
117                if below.is_air() || !above.is_air() {
118                    return false;
119                }
120            }
121        }
122
123        true
124    }
125
126    fn root_system_allowed_tree_space(
127        state: BlockStateId,
128        blocks_above_origin: i32,
129        allowed_vertical_water_height: i32,
130    ) -> bool {
131        if state.is_air() {
132            return true;
133        }
134
135        let blocks_above_ground = blocks_above_origin + 1;
136        blocks_above_ground <= allowed_vertical_water_height
137            && get_fluid_state_from_block(state).is_water()
138    }
139
140    fn place_root_system_dirt(
141        region: &mut WorldGenRegion<'_>,
142        registry: &Registry,
143        random: &mut WorldgenRandom,
144        config: &RootSystemConfiguration,
145        origin: BlockPos,
146        target_height: i32,
147    ) {
148        for y in origin.y()..target_height {
149            Self::place_root_system_rooted_dirt(
150                region,
151                registry,
152                random,
153                config,
154                origin.at_y(y),
155                origin.x(),
156                origin.z(),
157            );
158        }
159    }
160
161    fn place_root_system_rooted_dirt(
162        region: &mut WorldGenRegion<'_>,
163        registry: &Registry,
164        random: &mut WorldgenRandom,
165        config: &RootSystemConfiguration,
166        mut pos: BlockPos,
167        origin_x: i32,
168        origin_z: i32,
169    ) {
170        for _ in 0..config.root_placement_attempts {
171            pos = pos.offset(
172                random.next_i32_bounded(config.root_radius)
173                    - random.next_i32_bounded(config.root_radius),
174                0,
175                random.next_i32_bounded(config.root_radius)
176                    - random.next_i32_bounded(config.root_radius),
177            );
178
179            let state = region.block_state(pos);
180            if Self::block_matches_holder_set(state.get_block(), &config.root_replaceable) {
181                let replacement = Self::sample_block_state_provider(
182                    region,
183                    registry,
184                    random,
185                    &config.root_state_provider,
186                    pos,
187                );
188                let _ = region.set_block_state(pos, replacement, UpdateFlags::UPDATE_CLIENTS);
189            }
190
191            pos = BlockPos::new(origin_x, pos.y(), origin_z);
192        }
193    }
194
195    fn place_root_system_hanging_roots(
196        region: &mut WorldGenRegion<'_>,
197        registry: &Registry,
198        random: &mut WorldgenRandom,
199        config: &RootSystemConfiguration,
200        origin: BlockPos,
201    ) {
202        for _ in 0..config.hanging_root_placement_attempts {
203            let pos = origin.offset(
204                random.next_i32_bounded(config.hanging_root_radius)
205                    - random.next_i32_bounded(config.hanging_root_radius),
206                random.next_i32_bounded(config.hanging_roots_vertical_span)
207                    - random.next_i32_bounded(config.hanging_roots_vertical_span),
208                random.next_i32_bounded(config.hanging_root_radius)
209                    - random.next_i32_bounded(config.hanging_root_radius),
210            );
211
212            if !region.block_state(pos).is_air() {
213                continue;
214            }
215
216            let state = Self::sample_block_state_provider(
217                region,
218                registry,
219                random,
220                &config.hanging_root_state_provider,
221                pos,
222            );
223            let behavior = BLOCK_BEHAVIORS.get_behavior(state.get_block());
224            if behavior.can_survive(state, region, pos) && {
225                let above_pos = pos.above();
226                region
227                    .block_state(above_pos)
228                    .is_face_sturdy_at(above_pos, Direction::Down)
229            } {
230                let _ = region.set_block_state(pos, state, UpdateFlags::UPDATE_CLIENTS);
231            }
232        }
233    }
234}