1use super::{
2 BlockPos, BlockPropertyDirection, BlockRef, BlockStateId, Direction, Registry, Rotation,
3 StructureMirror, StructureTemplate,
4};
5
6impl StructureTemplate {
7 pub(crate) fn transform_state(
8 registry: &Registry,
9 state: BlockStateId,
10 mirror: StructureMirror,
11 rotation: Rotation,
12 ) -> BlockStateId {
13 if mirror == StructureMirror::None && rotation == Rotation::None {
14 return state;
15 }
16
17 let Some(block) = registry.blocks.by_state_id(state) else {
18 return state;
19 };
20 let mut properties = registry
21 .blocks
22 .get_properties(state)
23 .into_iter()
24 .map(|(name, value)| (name.to_owned(), value.to_owned()))
25 .collect::<Vec<_>>();
26
27 Self::mirror_string_properties(&mut properties, mirror);
28 Self::rotate_string_properties(&mut properties, rotation);
29 let property_refs = properties
30 .iter()
31 .map(|(name, value)| (name.as_str(), value.as_str()))
32 .collect::<Vec<_>>();
33 let Some(rotated) = registry
34 .blocks
35 .state_id_from_properties(&block.key, &property_refs)
36 else {
37 panic!(
38 "rotating block state {} produced invalid properties",
39 block.key
40 );
41 };
42 rotated
43 }
44
45 pub(super) fn block_for_state(registry: &Registry, state: BlockStateId) -> BlockRef {
46 let Some(block) = registry.blocks.by_state_id(state) else {
47 panic!(
48 "structure template references invalid block state {}",
49 state.0
50 );
51 };
52 block
53 }
54
55 pub(super) fn rotate_string_properties(
56 properties: &mut [(String, String)],
57 rotation: Rotation,
58 ) {
59 let original = properties.to_vec();
60 for (name, value) in properties.iter_mut() {
61 match name.as_str() {
62 "axis"
63 if matches!(
64 rotation,
65 Rotation::Clockwise90 | Rotation::CounterClockwise90
66 ) =>
67 {
68 match value.as_str() {
69 "x" => "z".clone_into(value),
70 "z" => "x".clone_into(value),
71 _ => {}
72 }
73 }
74 "facing" => {
75 if let Some(direction) = Self::parse_direction(value) {
76 rotation.rotate(direction).as_str().clone_into(value);
77 }
78 }
79 "rotation" => {
80 if let Ok(segment) = value.parse::<i32>() {
81 let rotated = match rotation {
82 Rotation::None => segment,
83 Rotation::Clockwise90 => segment + 4,
84 Rotation::Clockwise180 => segment + 8,
85 Rotation::CounterClockwise90 => segment + 12,
86 };
87 *value = (rotated & 15).to_string();
88 }
89 }
90 "shape" => {
91 if let Some(rotated) = Self::rotate_rail_shape(value, rotation) {
92 rotated.clone_into(value);
93 }
94 }
95 "north" | "east" | "south" | "west" => {
96 let from = Self::direction_from_property_name(name);
97 let source = Self::inverse_rotate_direction(rotation, from);
98 if let Some(source_name) = Self::property_name_from_direction(source)
99 && let Some((_, source_value)) = original
100 .iter()
101 .find(|(original_name, _)| original_name == source_name)
102 {
103 value.clone_from(source_value);
104 }
105 }
106 _ => {}
107 }
108 }
109 }
110
111 pub(super) fn mirror_string_properties(
112 properties: &mut [(String, String)],
113 mirror: StructureMirror,
114 ) {
115 if mirror == StructureMirror::None {
116 return;
117 }
118
119 let original = properties.to_vec();
120 let facing = original
121 .iter()
122 .find(|(name, _)| name == "facing")
123 .and_then(|(_, value)| Self::parse_direction(value));
124 let stair_shape = original
125 .iter()
126 .find(|(name, _)| name == "shape")
127 .and_then(|(_, value)| Self::parse_stair_shape(value));
128
129 let mirrored_stairs = facing
130 .zip(stair_shape)
131 .and_then(|(direction, shape)| Self::mirror_stair_shape(direction, shape, mirror));
132
133 for (name, value) in properties.iter_mut() {
134 match name.as_str() {
135 "facing" => {
136 if let Some((mirrored_facing, _)) = mirrored_stairs {
137 mirrored_facing.as_str().clone_into(value);
138 } else if let Some(direction) = Self::parse_direction(value) {
139 Self::mirror_direction(direction, mirror)
140 .as_str()
141 .clone_into(value);
142 }
143 }
144 "rotation" => {
145 if let Ok(segment) = value.parse::<i32>() {
146 *value = Self::mirror_rotation_segment(segment, 16, mirror).to_string();
147 }
148 }
149 "hinge" => match value.as_str() {
150 "left" => "right".clone_into(value),
151 "right" => "left".clone_into(value),
152 _ => {}
153 },
154 "shape" => {
155 if let Some((_, mirrored_shape)) = mirrored_stairs {
156 mirrored_shape.clone_into(value);
157 } else if let Some(mirrored_shape) = Self::mirror_rail_shape(value, mirror) {
158 mirrored_shape.clone_into(value);
159 }
160 }
161 "north" | "east" | "south" | "west" => {
162 let from = Self::direction_from_property_name(name);
163 let source = Self::mirror_direction(from, mirror);
164 if let Some(source_name) = Self::property_name_from_direction(source)
165 && let Some((_, source_value)) = original
166 .iter()
167 .find(|(original_name, _)| original_name == source_name)
168 {
169 value.clone_from(source_value);
170 }
171 }
172 _ => {}
173 }
174 }
175 }
176
177 pub(super) fn parse_direction(value: &str) -> Option<Direction> {
178 match value {
179 "down" => Some(BlockPropertyDirection::Down),
180 "up" => Some(BlockPropertyDirection::Up),
181 "north" => Some(BlockPropertyDirection::North),
182 "south" => Some(BlockPropertyDirection::South),
183 "west" => Some(BlockPropertyDirection::West),
184 "east" => Some(BlockPropertyDirection::East),
185 _ => None,
186 }
187 }
188
189 pub(super) fn direction_from_property_name(name: &str) -> Direction {
190 match name {
191 "east" => BlockPropertyDirection::East,
192 "south" => BlockPropertyDirection::South,
193 "west" => BlockPropertyDirection::West,
194 _ => BlockPropertyDirection::North,
195 }
196 }
197
198 pub(super) const fn mirror_direction(
199 direction: Direction,
200 mirror: StructureMirror,
201 ) -> Direction {
202 match mirror {
203 StructureMirror::FrontBack => match direction {
204 BlockPropertyDirection::West => BlockPropertyDirection::East,
205 BlockPropertyDirection::East => BlockPropertyDirection::West,
206 other => other,
207 },
208 StructureMirror::LeftRight => match direction {
209 BlockPropertyDirection::North => BlockPropertyDirection::South,
210 BlockPropertyDirection::South => BlockPropertyDirection::North,
211 other => other,
212 },
213 StructureMirror::None => direction,
214 }
215 }
216
217 const fn mirror_rotation_segment(rotation: i32, steps: i32, mirror: StructureMirror) -> i32 {
218 let half_steps = steps / 2;
219 let corrected = if rotation > half_steps {
220 rotation - steps
221 } else {
222 rotation
223 };
224 match mirror {
225 StructureMirror::LeftRight => (half_steps - corrected + steps) % steps,
226 StructureMirror::FrontBack => (steps - corrected) % steps,
227 StructureMirror::None => rotation,
228 }
229 }
230
231 const fn inverse_rotate_direction(rotation: Rotation, direction: Direction) -> Direction {
232 match rotation {
233 Rotation::None => direction,
234 Rotation::Clockwise90 => Rotation::CounterClockwise90.rotate(direction),
235 Rotation::Clockwise180 => Rotation::Clockwise180.rotate(direction),
236 Rotation::CounterClockwise90 => Rotation::Clockwise90.rotate(direction),
237 }
238 }
239
240 const fn property_name_from_direction(direction: Direction) -> Option<&'static str> {
241 match direction {
242 BlockPropertyDirection::North => Some("north"),
243 BlockPropertyDirection::East => Some("east"),
244 BlockPropertyDirection::South => Some("south"),
245 BlockPropertyDirection::West => Some("west"),
246 BlockPropertyDirection::Down | BlockPropertyDirection::Up => None,
247 }
248 }
249
250 pub(super) fn rotate_rail_shape(shape: &str, rotation: Rotation) -> Option<&'static str> {
251 match rotation {
252 Rotation::Clockwise180 => match shape {
253 "ascending_east" => Some("ascending_west"),
254 "ascending_west" => Some("ascending_east"),
255 "ascending_north" => Some("ascending_south"),
256 "ascending_south" => Some("ascending_north"),
257 "north_south" => Some("north_south"),
258 "east_west" => Some("east_west"),
259 "south_east" => Some("north_west"),
260 "south_west" => Some("north_east"),
261 "north_west" => Some("south_east"),
262 "north_east" => Some("south_west"),
263 _ => None,
264 },
265 Rotation::CounterClockwise90 => match shape {
266 "ascending_east" => Some("ascending_north"),
267 "ascending_west" => Some("ascending_south"),
268 "ascending_north" => Some("ascending_west"),
269 "ascending_south" => Some("ascending_east"),
270 "north_south" => Some("east_west"),
271 "east_west" => Some("north_south"),
272 "south_east" => Some("north_east"),
273 "south_west" => Some("south_east"),
274 "north_west" => Some("south_west"),
275 "north_east" => Some("north_west"),
276 _ => None,
277 },
278 Rotation::Clockwise90 => match shape {
279 "ascending_east" => Some("ascending_south"),
280 "ascending_west" => Some("ascending_north"),
281 "ascending_north" => Some("ascending_east"),
282 "ascending_south" => Some("ascending_west"),
283 "north_south" => Some("east_west"),
284 "east_west" => Some("north_south"),
285 "south_east" => Some("south_west"),
286 "south_west" => Some("north_west"),
287 "north_west" => Some("north_east"),
288 "north_east" => Some("south_east"),
289 _ => None,
290 },
291 Rotation::None => None,
292 }
293 }
294
295 pub(super) fn mirror_rail_shape(shape: &str, mirror: StructureMirror) -> Option<&'static str> {
296 match mirror {
297 StructureMirror::LeftRight => match shape {
298 "ascending_north" => Some("ascending_south"),
299 "ascending_south" => Some("ascending_north"),
300 "north_south" => Some("north_south"),
301 "east_west" => Some("east_west"),
302 "south_east" => Some("north_east"),
303 "south_west" => Some("north_west"),
304 "north_west" => Some("south_west"),
305 "north_east" => Some("south_east"),
306 _ => None,
307 },
308 StructureMirror::FrontBack => match shape {
309 "ascending_east" => Some("ascending_west"),
310 "ascending_west" => Some("ascending_east"),
311 "ascending_north" => Some("ascending_north"),
312 "ascending_south" => Some("ascending_south"),
313 "north_south" => Some("north_south"),
314 "east_west" => Some("east_west"),
315 "south_east" => Some("south_west"),
316 "south_west" => Some("south_east"),
317 "north_west" => Some("north_east"),
318 "north_east" => Some("north_west"),
319 _ => None,
320 },
321 StructureMirror::None => None,
322 }
323 }
324
325 pub(super) fn parse_stair_shape(shape: &str) -> Option<&'static str> {
326 match shape {
327 "straight" => Some("straight"),
328 "inner_left" => Some("inner_left"),
329 "inner_right" => Some("inner_right"),
330 "outer_left" => Some("outer_left"),
331 "outer_right" => Some("outer_right"),
332 _ => None,
333 }
334 }
335
336 pub(super) fn mirror_stair_shape(
337 direction: Direction,
338 shape: &str,
339 mirror: StructureMirror,
340 ) -> Option<(Direction, &'static str)> {
341 match mirror {
342 StructureMirror::LeftRight
343 if matches!(
344 direction,
345 BlockPropertyDirection::North | BlockPropertyDirection::South
346 ) =>
347 {
348 Some((
349 direction.opposite(),
350 match shape {
351 "outer_left" => "outer_right",
352 "inner_right" => "inner_left",
353 "inner_left" => "inner_right",
354 "outer_right" => "outer_left",
355 "straight" => "straight",
356 _ => return None,
357 },
358 ))
359 }
360 StructureMirror::FrontBack
361 if matches!(
362 direction,
363 BlockPropertyDirection::West | BlockPropertyDirection::East
364 ) =>
365 {
366 Some((
367 direction.opposite(),
368 match shape {
369 "outer_left" => "outer_right",
370 "outer_right" => "outer_left",
371 "inner_left" => "inner_left",
372 "inner_right" => "inner_right",
373 "straight" => "straight",
374 _ => return None,
375 },
376 ))
377 }
378 StructureMirror::None | StructureMirror::LeftRight | StructureMirror::FrontBack => None,
379 }
380 }
381
382 pub(super) fn block_pos_seed(pos: BlockPos) -> i64 {
383 let mut seed = i64::from(pos.x().wrapping_mul(3_129_871))
384 ^ i64::from(pos.z()).wrapping_mul(116_129_781)
385 ^ i64::from(pos.y());
386 seed = seed
387 .wrapping_mul(seed)
388 .wrapping_mul(42_317_861)
389 .wrapping_add(seed.wrapping_mul(11));
390 seed >> 16
391 }
392
393 pub(super) fn clamped_lerp_inverse(
394 value: i32,
395 min_dist: i32,
396 max_dist: i32,
397 min: f32,
398 max: f32,
399 ) -> f32 {
400 if min_dist == max_dist {
401 return max;
402 }
403 let delta = ((value - min_dist) as f32 / (max_dist - min_dist) as f32).clamp(0.0, 1.0);
404 min + delta * (max - min)
405 }
406}