steel_core/worldgen/feature/features/
coral.rs1use 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}