steel_core/worldgen/feature/features/tree/
roots.rs1#![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}