Skip to main content

steel_core/worldgen/feature/features/
coral.rs

1use steel_registry::vanilla_block_tags::BlockTag;
2
3use super::super::prelude::*;
4use super::super::runner::FeatureDecorationRunner;
5
6impl FeatureDecorationRunner {
7    pub(in crate::worldgen::feature) fn place_coral_claw_feature(
8        region: &mut WorldGenRegion<'_>,
9        registry: &Registry,
10        random: &mut WorldgenRandom,
11        origin: BlockPos,
12    ) -> bool {
13        let Some(coral) = Self::random_block_from_tag(registry, random, &BlockTag::CORAL_BLOCKS)
14        else {
15            return false;
16        };
17
18        let state = coral.default_state();
19        if !Self::place_coral_block(region, registry, random, origin, state) {
20            return false;
21        }
22
23        let claw_direction = Self::random_horizontal_direction(random);
24        let n_branches = random.next_i32_bounded(2) + 2;
25        let possible_directions = Self::shuffled_directions(
26            random,
27            [
28                claw_direction,
29                claw_direction.rotate_y_clockwise(),
30                claw_direction.rotate_y_counter_clockwise(),
31            ],
32        );
33
34        for branch_direction in possible_directions.into_iter().take(n_branches as usize) {
35            let mut pos = origin.relative(branch_direction);
36            let sideway_length = random.next_i32_bounded(2) + 1;
37            let segment_direction;
38            let inway_length;
39            if branch_direction == claw_direction {
40                segment_direction = claw_direction;
41                inway_length = random.next_i32_bounded(3) + 2;
42            } else {
43                pos = pos.above();
44                segment_direction = if random.next_i32_bounded(2) == 0 {
45                    branch_direction
46                } else {
47                    Direction::Up
48                };
49                inway_length = random.next_i32_bounded(3) + 3;
50            }
51
52            for _ in 0..sideway_length {
53                if !Self::place_coral_block(region, registry, random, pos, state) {
54                    break;
55                }
56                pos = pos.relative(segment_direction);
57            }
58
59            pos = pos.relative(segment_direction.opposite()).above();
60
61            for _ in 0..inway_length {
62                pos = pos.relative(claw_direction);
63                if !Self::place_coral_block(region, registry, random, pos, state) {
64                    break;
65                }
66
67                if random.next_f32() < 0.25 {
68                    pos = pos.above();
69                }
70            }
71        }
72
73        true
74    }
75
76    pub(in crate::worldgen::feature) fn place_coral_mushroom_feature(
77        region: &mut WorldGenRegion<'_>,
78        registry: &Registry,
79        random: &mut WorldgenRandom,
80        origin: BlockPos,
81    ) -> bool {
82        let Some(coral) = Self::random_block_from_tag(registry, random, &BlockTag::CORAL_BLOCKS)
83        else {
84            return false;
85        };
86
87        let state = coral.default_state();
88        let height = random.next_i32_bounded(3) + 3;
89        let width = random.next_i32_bounded(3) + 3;
90        let length = random.next_i32_bounded(3) + 3;
91        let sink_value = random.next_i32_bounded(3) + 1;
92
93        for x in 0..=width {
94            for y in 0..=height {
95                for z in 0..=length {
96                    let pos = origin.offset(x, y - sink_value, z);
97                    if (y != 0 && y != height || x != 0 && x != width && z != 0 && z != length)
98                        && ((x != 0 && x != width) || (z != 0 && z != length))
99                        && (x == 0 || x == width || y == 0 || y == height || z == 0 || z == length)
100                        && random.next_f32() >= 0.1
101                    {
102                        let _ = Self::place_coral_block(region, registry, random, pos, state);
103                    }
104                }
105            }
106        }
107
108        true
109    }
110
111    pub(in crate::worldgen::feature) fn place_coral_tree_feature(
112        region: &mut WorldGenRegion<'_>,
113        registry: &Registry,
114        random: &mut WorldgenRandom,
115        origin: BlockPos,
116    ) -> bool {
117        let Some(coral) = Self::random_block_from_tag(registry, random, &BlockTag::CORAL_BLOCKS)
118        else {
119            return false;
120        };
121
122        let state = coral.default_state();
123        let mut pos = origin;
124        let trunk_height = random.next_i32_bounded(3) + 1;
125        for _ in 0..trunk_height {
126            if !Self::place_coral_block(region, registry, random, pos, state) {
127                return true;
128            }
129            pos = pos.above();
130        }
131
132        let trunk_top = pos;
133        let n_branches = random.next_i32_bounded(3) + 2;
134        let directions = Self::shuffled_directions(random, Self::VANILLA_HORIZONTAL_DIRECTIONS);
135
136        for branch_direction in directions.into_iter().take(n_branches as usize) {
137            pos = trunk_top.relative(branch_direction);
138            let branch_height = random.next_i32_bounded(5) + 2;
139            let mut segment_length = 0;
140
141            for j in 0..branch_height {
142                if !Self::place_coral_block(region, registry, random, pos, state) {
143                    break;
144                }
145                segment_length += 1;
146                pos = pos.above();
147                if j == 0 || (segment_length >= 2 && random.next_f32() < 0.25) {
148                    pos = pos.relative(branch_direction);
149                    segment_length = 0;
150                }
151            }
152        }
153
154        true
155    }
156
157    fn place_coral_block(
158        region: &mut WorldGenRegion<'_>,
159        registry: &Registry,
160        random: &mut WorldgenRandom,
161        pos: BlockPos,
162        state: BlockStateId,
163    ) -> bool {
164        let above = pos.above();
165        let target_state = region.block_state(pos);
166        if target_state.get_block() != &vanilla_blocks::WATER
167            && !registry
168                .blocks
169                .is_in_tag(target_state.get_block(), &BlockTag::CORALS)
170        {
171            return false;
172        }
173
174        if region.block_state(above).get_block() != &vanilla_blocks::WATER {
175            return false;
176        }
177
178        let _ = region.set_block_state(pos, state, UpdateFlags::UPDATE_ALL);
179        if random.next_f32() < 0.25 {
180            if let Some(coral) = Self::random_block_from_tag(registry, random, &BlockTag::CORALS) {
181                let _ = region.set_block_state(
182                    above,
183                    coral.default_state(),
184                    UpdateFlags::UPDATE_CLIENTS,
185                );
186            }
187        } else if random.next_f32() < 0.05 {
188            let pickles = random.next_i32_bounded(4) as u8 + 1;
189            let sea_pickle = vanilla_blocks::SEA_PICKLE
190                .default_state()
191                .set_value(&BlockStateProperties::PICKLES, pickles);
192            let _ = region.set_block_state(above, sea_pickle, UpdateFlags::UPDATE_CLIENTS);
193        }
194
195        for direction in Self::VANILLA_HORIZONTAL_DIRECTIONS {
196            if random.next_f32() >= 0.2 {
197                continue;
198            }
199
200            let relative_pos = pos.relative(direction);
201            if region.block_state(relative_pos).get_block() != &vanilla_blocks::WATER {
202                continue;
203            }
204
205            if let Some(coral) =
206                Self::random_block_from_tag(registry, random, &BlockTag::WALL_CORALS)
207            {
208                let mut coral_fan_state = coral.default_state();
209                if coral_fan_state
210                    .try_get_value(&BlockStateProperties::HORIZONTAL_FACING)
211                    .is_some()
212                {
213                    coral_fan_state = coral_fan_state
214                        .set_value(&BlockStateProperties::HORIZONTAL_FACING, direction);
215                }
216                let _ = region.set_block_state(
217                    relative_pos,
218                    coral_fan_state,
219                    UpdateFlags::UPDATE_CLIENTS,
220                );
221            }
222        }
223
224        true
225    }
226
227    pub(super) fn random_block_from_tag(
228        registry: &Registry,
229        random: &mut WorldgenRandom,
230        tag: &Identifier,
231    ) -> Option<BlockRef> {
232        let blocks = registry.blocks.get_tag(tag)?;
233        if blocks.is_empty() {
234            return None;
235        }
236
237        let Ok(block_count) = i32::try_from(blocks.len()) else {
238            panic!("block tag {tag} has too many entries to choose randomly");
239        };
240        Some(blocks[random.next_i32_bounded(block_count) as usize])
241    }
242}