Skip to main content

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

1use 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}