Skip to main content

steel_core/worldgen/template/
state_transforms.rs

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}