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 );
77 }
78
79 #[expect(
80 clippy::too_many_arguments,
81 reason = "mirrors vanilla FallenTreeFeature.placeFallenLog state"
82 )]
83 fn place_fallen_log(
84 region: &mut WorldGenRegion<'_>,
85 registry: &Registry,
86 random: &mut WorldgenRandom,
87 config: &FallenTreeConfiguration,
88 log_length: i32,
89 mut log_start_pos: BlockPos,
90 direction: Direction,
91 biome_zoom_seed: i64,
92 ) {
93 let mut placement = TreePlacement::default();
94
95 for _ in 0..log_length {
96 Self::place_fallen_log_block(
97 region,
98 registry,
99 random,
100 config,
101 log_start_pos,
102 Some(direction.axis()),
103 &mut placement,
104 );
105 log_start_pos = log_start_pos.relative(direction);
106 }
107
108 Self::place_tree_decorators(
109 region,
110 registry,
111 random,
112 &config.log_decorators,
113 &mut placement,
114 biome_zoom_seed,
115 );
116 }
117
118 fn can_place_entire_fallen_log(
119 region: &WorldGenRegion<'_>,
120 log_length: i32,
121 log_start_pos: &mut BlockPos,
122 direction: Direction,
123 ) -> bool {
124 let mut gap_in_ground = 0;
125
126 for _ in 0..log_length {
127 if !Self::tree_valid_pos(region, *log_start_pos) {
128 return false;
129 }
130
131 if Self::is_over_solid_ground_for_fallen_log(region, *log_start_pos) {
132 gap_in_ground = 0;
133 } else {
134 gap_in_ground += 1;
135 if gap_in_ground > 2 {
136 return false;
137 }
138 }
139
140 *log_start_pos = log_start_pos.relative(direction);
141 }
142
143 *log_start_pos = log_start_pos.relative_n(direction.opposite(), log_length);
144 true
145 }
146
147 fn may_place_fallen_log_on(region: &WorldGenRegion<'_>, pos: BlockPos) -> bool {
148 Self::tree_valid_pos(region, pos) && Self::is_over_solid_ground_for_fallen_log(region, pos)
149 }
150
151 fn is_over_solid_ground_for_fallen_log(region: &WorldGenRegion<'_>, pos: BlockPos) -> bool {
152 let below_pos = pos.below();
153 region
154 .block_state(below_pos)
155 .is_face_sturdy_at(below_pos, Direction::Up)
156 }
157
158 fn place_fallen_log_block(
159 region: &mut WorldGenRegion<'_>,
160 registry: &Registry,
161 random: &mut WorldgenRandom,
162 config: &FallenTreeConfiguration,
163 pos: BlockPos,
164 axis: Option<Axis>,
165 placement: &mut TreePlacement,
166 ) {
167 let mut state = Self::sample_block_state_provider(
168 region,
169 registry,
170 random,
171 &config.trunk_provider,
172 pos,
173 );
174 if let Some(axis) = axis
175 && state.try_get_value(&BlockStateProperties::AXIS).is_some()
176 {
177 state = state.set_value(&BlockStateProperties::AXIS, axis);
178 }
179
180 placement.set_trunk(region, pos, state);
181 Self::mark_above_for_postprocessing(region, pos);
182 }
183}