steel_core/worldgen/feature/features/
vegetation_patch.rs1use super::super::prelude::*;
2use super::super::runner::FeatureDecorationRunner;
3use super::super::vanilla_collections::JavaBlockPosSet;
4
5impl FeatureDecorationRunner {
6 pub(in crate::worldgen::feature) fn place_vegetation_patch_feature(
7 region: &mut WorldGenRegion<'_>,
8 registry: &Registry,
9 random: &mut WorldgenRandom,
10 config: &VegetationPatchConfiguration,
11 origin: BlockPos,
12 biome_zoom_seed: i64,
13 ) -> bool {
14 let x_radius = config.xz_radius.sample(random) + 1;
15 let z_radius = config.xz_radius.sample(random) + 1;
16 let surface = Self::place_vegetation_patch_ground(
17 region, registry, random, config, origin, x_radius, z_radius,
18 );
19 Self::distribute_vegetation_patch(
20 region,
21 registry,
22 random,
23 config,
24 &surface,
25 biome_zoom_seed,
26 false,
27 );
28 !surface.is_empty()
29 }
30
31 pub(in crate::worldgen::feature) fn place_waterlogged_vegetation_patch_feature(
32 region: &mut WorldGenRegion<'_>,
33 registry: &Registry,
34 random: &mut WorldgenRandom,
35 config: &VegetationPatchConfiguration,
36 origin: BlockPos,
37 biome_zoom_seed: i64,
38 ) -> bool {
39 let x_radius = config.xz_radius.sample(random) + 1;
40 let z_radius = config.xz_radius.sample(random) + 1;
41 let surface = Self::place_vegetation_patch_ground(
42 region, registry, random, config, origin, x_radius, z_radius,
43 );
44 let water_surface = Self::waterlogged_vegetation_patch_surface(region, &surface);
45 for surface_pos in water_surface.java_ordered_positions() {
46 let _ = region.set_block_state(
47 surface_pos,
48 vanilla_blocks::WATER.default_state(),
49 UpdateFlags::UPDATE_CLIENTS,
50 );
51 }
52
53 Self::distribute_vegetation_patch(
54 region,
55 registry,
56 random,
57 config,
58 &water_surface,
59 biome_zoom_seed,
60 true,
61 );
62 !water_surface.is_empty()
63 }
64
65 fn place_vegetation_patch_ground(
66 region: &mut WorldGenRegion<'_>,
67 registry: &Registry,
68 random: &mut WorldgenRandom,
69 config: &VegetationPatchConfiguration,
70 origin: BlockPos,
71 x_radius: i32,
72 z_radius: i32,
73 ) -> JavaBlockPosSet {
74 let inwards = Self::vegetation_patch_surface_direction(config.surface);
75 let outwards = inwards.opposite();
76 let mut surface = JavaBlockPosSet::default();
77
78 for dx in -x_radius..=x_radius {
79 let at_longitude_limit = dx == -x_radius || dx == x_radius;
80 for dz in -z_radius..=z_radius {
81 let at_latitude_limit = dz == -z_radius || dz == z_radius;
82 let is_corner = at_longitude_limit && at_latitude_limit;
83 let is_edge_but_not_corner =
84 (at_longitude_limit || at_latitude_limit) && !is_corner;
85
86 if is_corner {
87 continue;
88 }
89 if is_edge_but_not_corner
90 && (config.extra_edge_column_chance == 0.0
91 || random.next_f32() > config.extra_edge_column_chance)
92 {
93 continue;
94 }
95
96 let mut pos = origin.offset(dx, 0, dz);
97 for _ in 0..config.vertical_range {
98 if !region.block_state(pos).is_air() {
99 break;
100 }
101 pos = pos.relative(inwards);
102 }
103 for _ in 0..config.vertical_range {
104 if region.block_state(pos).is_air() {
105 break;
106 }
107 pos = pos.relative(outwards);
108 }
109
110 let below_pos = pos.relative(inwards);
111 let below_state = region.block_state(below_pos);
112 if !region.block_state(pos).is_air()
113 || !below_state.is_face_sturdy_at(below_pos, outwards)
114 {
115 continue;
116 }
117
118 let sampled_depth = config.depth.sample(random);
119 let extra_depth = i32::from(
120 config.extra_bottom_block_chance > 0.0
121 && random.next_f32() < config.extra_bottom_block_chance,
122 );
123 let depth = sampled_depth + extra_depth;
124 let mut ground_cursor = below_pos;
125 if Self::place_vegetation_patch_ground_column(
126 region,
127 registry,
128 random,
129 config,
130 &mut ground_cursor,
131 inwards,
132 depth,
133 ) {
134 surface.insert(below_pos);
135 }
136 }
137 }
138
139 surface
140 }
141
142 fn place_vegetation_patch_ground_column(
143 region: &mut WorldGenRegion<'_>,
144 registry: &Registry,
145 random: &mut WorldgenRandom,
146 config: &VegetationPatchConfiguration,
147 pos: &mut BlockPos,
148 inwards: Direction,
149 depth: i32,
150 ) -> bool {
151 for i in 0..depth {
152 let state_to_place = Self::sample_block_state_provider(
153 region,
154 registry,
155 random,
156 &config.ground_state,
157 *pos,
158 );
159 let current_state = region.block_state(*pos);
160 if state_to_place.get_block() != current_state.get_block() {
161 if !registry
162 .blocks
163 .is_in_tag(current_state.get_block(), &config.replaceable)
164 {
165 return i != 0;
166 }
167
168 let _ = region.set_block_state(*pos, state_to_place, UpdateFlags::UPDATE_CLIENTS);
169 *pos = (*pos).relative(inwards);
170 }
171 }
172
173 true
174 }
175
176 fn distribute_vegetation_patch(
177 region: &mut WorldGenRegion<'_>,
178 registry: &Registry,
179 random: &mut WorldgenRandom,
180 config: &VegetationPatchConfiguration,
181 surface: &JavaBlockPosSet,
182 biome_zoom_seed: i64,
183 waterlogged: bool,
184 ) {
185 if config.vegetation_chance <= 0.0 {
186 return;
187 }
188
189 let outwards = Self::vegetation_patch_surface_direction(config.surface).opposite();
190 for surface_pos in surface.java_ordered_positions() {
191 if random.next_f32() >= config.vegetation_chance {
192 continue;
193 }
194
195 let vegetation_origin = if waterlogged {
196 surface_pos.below().relative(outwards)
197 } else {
198 surface_pos.relative(outwards)
199 };
200 let placed = Self::place_placed_feature_ref(
201 region,
202 registry,
203 random,
204 vegetation_origin,
205 &config.vegetation_feature,
206 biome_zoom_seed,
207 );
208 if waterlogged && placed {
209 let state = region.block_state(surface_pos);
210 if let Some(false) = state.try_get_value(&BlockStateProperties::WATERLOGGED) {
211 let _ = region.set_block_state(
212 surface_pos,
213 state.set_value(&BlockStateProperties::WATERLOGGED, true),
214 UpdateFlags::UPDATE_CLIENTS,
215 );
216 }
217 }
218 }
219 }
220
221 fn waterlogged_vegetation_patch_surface(
222 region: &WorldGenRegion<'_>,
223 surface: &JavaBlockPosSet,
224 ) -> JavaBlockPosSet {
225 let mut water_surface = JavaBlockPosSet::default();
226 for pos in surface.java_ordered_positions() {
227 if !Self::vegetation_patch_surface_exposed(region, pos) {
228 water_surface.insert(pos);
229 }
230 }
231 water_surface
232 }
233
234 fn vegetation_patch_surface_exposed(region: &WorldGenRegion<'_>, pos: BlockPos) -> bool {
235 [
236 Direction::North,
237 Direction::East,
238 Direction::South,
239 Direction::West,
240 Direction::Down,
241 ]
242 .into_iter()
243 .any(|direction| {
244 let neighbor_pos = pos.relative(direction);
245 !region
246 .block_state(neighbor_pos)
247 .is_face_sturdy_at(neighbor_pos, direction.opposite())
248 })
249 }
250
251 const fn vegetation_patch_surface_direction(surface: VerticalSurface) -> Direction {
252 match surface {
253 VerticalSurface::Floor => Direction::Down,
254 VerticalSurface::Ceiling => Direction::Up,
255 }
256 }
257}