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        );
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}