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