steel_core/worldgen/feature/features/tree/
fallen.rs1use super::super::super::prelude::*;
2use super::super::super::runner::FeatureDecorationRunner;
3use super::TreePlacement;
4
5impl FeatureDecorationRunner {
6 pub(in crate::worldgen::feature) fn place_fallen_tree_feature(
7 region: &mut WorldGenRegion<'_>,
8 registry: &Registry,
9 random: &mut WorldgenRandom,
10 config: &FallenTreeConfiguration,
11 origin: BlockPos,
12 biome_zoom_seed: i64,
13 ) -> bool {
14 Self::place_fallen_tree_stump(region, registry, random, config, origin, biome_zoom_seed);
15
16 let direction = Self::random_horizontal_direction(random);
17 let log_length = config.log_length.sample(random) - 2;
18 let mut log_start_pos = origin.relative_n(direction, 2 + random.next_i32_bounded(2));
19 Self::set_ground_height_for_fallen_log_start_pos(region, &mut log_start_pos);
20 if Self::can_place_entire_fallen_log(region, log_length, &mut log_start_pos, direction) {
21 Self::place_fallen_log(
22 region,
23 registry,
24 random,
25 config,
26 log_length,
27 log_start_pos,
28 direction,
29 biome_zoom_seed,
30 );
31 }
32
33 true
34 }
35
36 fn set_ground_height_for_fallen_log_start_pos(
37 region: &WorldGenRegion<'_>,
38 log_start_pos: &mut BlockPos,
39 ) {
40 *log_start_pos = log_start_pos.above();
41
42 for _ in 0..6 {
43 if Self::may_place_fallen_log_on(region, *log_start_pos) {
44 return;
45 }
46
47 *log_start_pos = log_start_pos.below();
48 }
49 }
50
51 fn place_fallen_tree_stump(
52 region: &mut WorldGenRegion<'_>,
53 registry: &Registry,
54 random: &mut WorldgenRandom,
55 config: &FallenTreeConfiguration,
56 origin: BlockPos,
57 biome_zoom_seed: i64,
58 ) {
59 let mut placement = TreePlacement::default();
60 Self::place_fallen_log_block(
61 region,
62 registry,
63 random,
64 config,
65 origin,
66 None,
67 &mut placement,
68 );
69 Self::place_tree_decorators(
70 region,
71 registry,
72 random,
73 &config.stump_decorators,
74 &mut placement,
75 biome_zoom_seed,
76 Self::place_configured_feature_kind,
77 );
78 }
79
80 #[expect(
81 clippy::too_many_arguments,
82 reason = "mirrors vanilla FallenTreeFeature.placeFallenLog state"
83 )]
84 fn place_fallen_log(
85 region: &mut WorldGenRegion<'_>,
86 registry: &Registry,
87 random: &mut WorldgenRandom,
88 config: &FallenTreeConfiguration,
89 log_length: i32,
90 mut log_start_pos: BlockPos,
91 direction: Direction,
92 biome_zoom_seed: i64,
93 ) {
94 let mut placement = TreePlacement::default();
95
96 for _ in 0..log_length {
97 Self::place_fallen_log_block(
98 region,
99 registry,
100 random,
101 config,
102 log_start_pos,
103 Some(direction.axis()),
104 &mut placement,
105 );
106 log_start_pos = log_start_pos.relative(direction);
107 }
108
109 Self::place_tree_decorators(
110 region,
111 registry,
112 random,
113 &config.log_decorators,
114 &mut placement,
115 biome_zoom_seed,
116 Self::place_configured_feature_kind,
117 );
118 }
119
120 fn can_place_entire_fallen_log(
121 region: &WorldGenRegion<'_>,
122 log_length: i32,
123 log_start_pos: &mut BlockPos,
124 direction: Direction,
125 ) -> bool {
126 let mut gap_in_ground = 0;
127
128 for _ in 0..log_length {
129 if !Self::tree_valid_pos(region, *log_start_pos) {
130 return false;
131 }
132
133 if Self::is_over_solid_ground_for_fallen_log(region, *log_start_pos) {
134 gap_in_ground = 0;
135 } else {
136 gap_in_ground += 1;
137 if gap_in_ground > 2 {
138 return false;
139 }
140 }
141
142 *log_start_pos = log_start_pos.relative(direction);
143 }
144
145 *log_start_pos = log_start_pos.relative_n(direction.opposite(), log_length);
146 true
147 }
148
149 fn may_place_fallen_log_on(region: &WorldGenRegion<'_>, pos: BlockPos) -> bool {
150 Self::tree_valid_pos(region, pos) && Self::is_over_solid_ground_for_fallen_log(region, pos)
151 }
152
153 fn is_over_solid_ground_for_fallen_log(region: &WorldGenRegion<'_>, pos: BlockPos) -> bool {
154 let below_pos = pos.below();
155 region
156 .block_state(below_pos)
157 .is_face_sturdy_at(below_pos, Direction::Up)
158 }
159
160 fn place_fallen_log_block(
161 region: &mut WorldGenRegion<'_>,
162 registry: &Registry,
163 random: &mut WorldgenRandom,
164 config: &FallenTreeConfiguration,
165 pos: BlockPos,
166 axis: Option<Axis>,
167 placement: &mut TreePlacement,
168 ) {
169 let mut state = Self::sample_block_state_provider(
170 region,
171 registry,
172 random,
173 &config.trunk_provider,
174 pos,
175 );
176 if let Some(axis) = axis
177 && state.try_get_value(&BlockStateProperties::AXIS).is_some()
178 {
179 state = state.set_value(&BlockStateProperties::AXIS, axis);
180 }
181
182 placement.set_trunk(region, pos, state);
183 Self::mark_above_for_postprocessing(region, pos);
184 }
185}