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            .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}