Skip to main content

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

1#![expect(
2    clippy::too_many_arguments,
3    reason = "mangrove root simulation mirrors vanilla recursion state"
4)]
5
6use super::super::super::prelude::*;
7use super::super::super::runner::FeatureDecorationRunner;
8use super::TreePlacement;
9
10impl FeatureDecorationRunner {
11    pub(super) fn tree_root_origin(
12        random: &mut WorldgenRandom,
13        origin: BlockPos,
14        root_placer: Option<&RootPlacer>,
15    ) -> BlockPos {
16        match root_placer {
17            Some(RootPlacer::Mangrove(placer)) => {
18                origin.above_n(placer.trunk_offset_y.sample(random))
19            }
20            None => origin,
21        }
22    }
23
24    pub(super) fn place_tree_roots(
25        region: &mut WorldGenRegion<'_>,
26        registry: &Registry,
27        random: &mut WorldgenRandom,
28        origin: BlockPos,
29        trunk_origin: BlockPos,
30        config: &TreeConfiguration,
31        placement: &mut TreePlacement,
32    ) -> bool {
33        let Some(RootPlacer::Mangrove(placer)) = config.root_placer.as_ref() else {
34            return true;
35        };
36        Self::place_mangrove_tree_roots(
37            region,
38            registry,
39            random,
40            origin,
41            trunk_origin,
42            placer,
43            placement,
44        )
45    }
46
47    fn place_mangrove_tree_roots(
48        region: &mut WorldGenRegion<'_>,
49        registry: &Registry,
50        random: &mut WorldgenRandom,
51        origin: BlockPos,
52        trunk_origin: BlockPos,
53        placer: &MangroveRootPlacer,
54        placement: &mut TreePlacement,
55    ) -> bool {
56        let mut column_pos = origin;
57        while column_pos.y() < trunk_origin.y() {
58            if !Self::can_place_mangrove_tree_root(
59                region,
60                column_pos,
61                &placer.mangrove_root_placement,
62            ) {
63                return false;
64            }
65
66            column_pos = column_pos.above();
67        }
68
69        let mut root_positions = vec![trunk_origin.below()];
70        for direction in Self::VANILLA_HORIZONTAL_DIRECTIONS {
71            let root_pos = trunk_origin.relative(direction);
72            let mut positions_in_direction = Vec::new();
73            if !Self::simulate_mangrove_tree_roots(
74                region,
75                random,
76                root_pos,
77                direction,
78                trunk_origin,
79                &mut positions_in_direction,
80                0,
81                &placer.mangrove_root_placement,
82            ) {
83                return false;
84            }
85
86            root_positions.extend(positions_in_direction);
87            root_positions.push(root_pos);
88        }
89
90        for root_pos in root_positions {
91            Self::place_mangrove_tree_root(region, registry, random, root_pos, placer, placement);
92        }
93
94        true
95    }
96
97    fn simulate_mangrove_tree_roots(
98        region: &WorldGenRegion<'_>,
99        random: &mut WorldgenRandom,
100        root_pos: BlockPos,
101        direction: Direction,
102        root_origin: BlockPos,
103        root_positions: &mut Vec<BlockPos>,
104        layer: i32,
105        placement: &MangroveRootPlacement,
106    ) -> bool {
107        let Ok(position_count) = i32::try_from(root_positions.len()) else {
108            return false;
109        };
110        if layer == placement.max_root_length || position_count > placement.max_root_length {
111            return false;
112        }
113
114        for pos in Self::potential_mangrove_root_positions(
115            root_pos,
116            direction,
117            random,
118            root_origin,
119            placement,
120        ) {
121            if Self::can_place_mangrove_tree_root(region, pos, placement) {
122                root_positions.push(pos);
123                if !Self::simulate_mangrove_tree_roots(
124                    region,
125                    random,
126                    pos,
127                    direction,
128                    root_origin,
129                    root_positions,
130                    layer + 1,
131                    placement,
132                ) {
133                    return false;
134                }
135            }
136        }
137
138        true
139    }
140
141    fn potential_mangrove_root_positions(
142        pos: BlockPos,
143        previous_direction: Direction,
144        random: &mut WorldgenRandom,
145        root_origin: BlockPos,
146        placement: &MangroveRootPlacement,
147    ) -> Vec<BlockPos> {
148        let below = pos.below();
149        let next_to = pos.relative(previous_direction);
150        let width = Self::manhattan_distance(pos, root_origin);
151
152        if width > placement.max_root_width - 3 && width <= placement.max_root_width {
153            if random.next_f32() < placement.random_skew_chance {
154                vec![below, next_to.below()]
155            } else {
156                vec![below]
157            }
158        } else if width > placement.max_root_width
159            || random.next_f32() < placement.random_skew_chance
160        {
161            vec![below]
162        } else if random.next_bool() {
163            vec![next_to]
164        } else {
165            vec![below]
166        }
167    }
168
169    fn can_place_mangrove_tree_root(
170        region: &WorldGenRegion<'_>,
171
172        pos: BlockPos,
173        placement: &MangroveRootPlacement,
174    ) -> bool {
175        Self::tree_valid_pos_or_tag(region, pos, &placement.can_grow_through)
176    }
177
178    fn place_mangrove_tree_root(
179        region: &mut WorldGenRegion<'_>,
180        registry: &Registry,
181        random: &mut WorldgenRandom,
182        pos: BlockPos,
183        placer: &MangroveRootPlacer,
184        placement: &mut TreePlacement,
185    ) {
186        if Self::block_matches_identifiers(
187            registry,
188            region.block_state(pos),
189            &placer.mangrove_root_placement.muddy_roots_in,
190        ) {
191            let state = Self::sample_block_state_provider(
192                region,
193                registry,
194                random,
195                &placer.mangrove_root_placement.muddy_roots_provider,
196                pos,
197            );
198            let state = Self::copy_waterlogged_from(region, pos, state);
199            placement.set_root(region, pos, state);
200            return;
201        }
202
203        if !Self::can_place_mangrove_tree_root(region, pos, &placer.mangrove_root_placement) {
204            return;
205        }
206
207        let state =
208            Self::sample_block_state_provider(region, registry, random, &placer.root_provider, pos);
209        let state = Self::copy_waterlogged_from(region, pos, state);
210        placement.set_root(region, pos, state);
211
212        let above = pos.above();
213        if random.next_f32() < placer.above_root_placement.above_root_placement_chance
214            && region.block_state(above).is_air()
215        {
216            let state = Self::sample_block_state_provider(
217                region,
218                registry,
219                random,
220                &placer.above_root_placement.above_root_provider,
221                above,
222            );
223            let state = Self::copy_waterlogged_from(region, above, state);
224            placement.set_root(region, above, state);
225        }
226    }
227
228    fn block_matches_identifiers(
229        registry: &Registry,
230        state: BlockStateId,
231        blocks: &[Identifier],
232    ) -> bool {
233        blocks.iter().any(|block_key| {
234            let Some(block) = registry.blocks.by_key(block_key) else {
235                panic!("mangrove root placement references unknown block {block_key}");
236            };
237            state.get_block() == block
238        })
239    }
240}