Skip to main content

steel_registry/
template_pool.rs

1//! Template pool and structure template data for jigsaw assembly.
2//!
3//! Parsed at build time from vanilla datapack JSONs and structure NBT files.
4//! Used by the jigsaw placement system to assemble structures from pools.
5
6use steel_utils::{Direction, Identifier, Rotation};
7
8/// Orientation of a jigsaw block, encoding both facing direction and up direction.
9///
10/// Vanilla's `FrontAndTop` enum — the orientation block state property.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum JigsawOrientation {
13    DownEast,
14    DownNorth,
15    DownSouth,
16    DownWest,
17    UpEast,
18    UpNorth,
19    UpSouth,
20    UpWest,
21    WestUp,
22    EastUp,
23    NorthUp,
24    SouthUp,
25}
26
27impl JigsawOrientation {
28    /// Parses from the block state property string (e.g., `"up_north"`).
29    #[must_use]
30    pub fn parse(s: &str) -> Option<Self> {
31        Some(match s {
32            "down_east" => Self::DownEast,
33            "down_north" => Self::DownNorth,
34            "down_south" => Self::DownSouth,
35            "down_west" => Self::DownWest,
36            "up_east" => Self::UpEast,
37            "up_north" => Self::UpNorth,
38            "up_south" => Self::UpSouth,
39            "up_west" => Self::UpWest,
40            "west_up" => Self::WestUp,
41            "east_up" => Self::EastUp,
42            "north_up" => Self::NorthUp,
43            "south_up" => Self::SouthUp,
44            _ => return None,
45        })
46    }
47
48    /// Returns the front-facing direction.
49    #[must_use]
50    pub const fn front_direction(self) -> Direction {
51        match self {
52            Self::DownEast | Self::DownNorth | Self::DownSouth | Self::DownWest => Direction::Down,
53            Self::UpEast | Self::UpNorth | Self::UpSouth | Self::UpWest => Direction::Up,
54            Self::WestUp => Direction::West,
55            Self::EastUp => Direction::East,
56            Self::NorthUp => Direction::North,
57            Self::SouthUp => Direction::South,
58        }
59    }
60
61    /// Returns the top direction.
62    #[must_use]
63    pub const fn top_direction(self) -> Direction {
64        match self {
65            Self::DownEast | Self::UpEast => Direction::East,
66            Self::DownNorth | Self::UpNorth => Direction::North,
67            Self::DownSouth | Self::UpSouth => Direction::South,
68            Self::DownWest | Self::UpWest => Direction::West,
69            Self::WestUp | Self::EastUp | Self::NorthUp | Self::SouthUp => Direction::Up,
70        }
71    }
72
73    /// Returns the front-facing direction offset as (dx, dy, dz).
74    #[must_use]
75    pub const fn front(self) -> (i32, i32, i32) {
76        self.front_direction().offset()
77    }
78
79    /// Constructs an orientation from front and top directions.
80    ///
81    /// Returns `None` if the combination is invalid.
82    #[must_use]
83    pub const fn from_directions(front: Direction, top: Direction) -> Option<Self> {
84        // Match vanilla's FrontAndTop lookup table
85        Some(match (front, top) {
86            (Direction::Down, Direction::East) => Self::DownEast,
87            (Direction::Down, Direction::North) => Self::DownNorth,
88            (Direction::Down, Direction::South) => Self::DownSouth,
89            (Direction::Down, Direction::West) => Self::DownWest,
90            (Direction::Up, Direction::East) => Self::UpEast,
91            (Direction::Up, Direction::North) => Self::UpNorth,
92            (Direction::Up, Direction::South) => Self::UpSouth,
93            (Direction::Up, Direction::West) => Self::UpWest,
94            (Direction::West, Direction::Up) => Self::WestUp,
95            (Direction::East, Direction::Up) => Self::EastUp,
96            (Direction::North, Direction::Up) => Self::NorthUp,
97            (Direction::South, Direction::Up) => Self::SouthUp,
98            _ => return None,
99        })
100    }
101
102    /// Rotates this orientation by the given rotation around the Y axis.
103    ///
104    /// Both the front and top directions are rotated, matching vanilla's
105    /// `BlockState.rotate(rotation)` for jigsaw blocks.
106    #[must_use]
107    pub fn rotate(self, rotation: Rotation) -> Self {
108        let front = rotation.rotate(self.front_direction());
109        let top = rotation.rotate(self.top_direction());
110        // Rotation of valid FrontAndTop always produces a valid FrontAndTop
111        Self::from_directions(front, top).expect("rotated orientation should be valid")
112    }
113}
114
115/// Joint type for jigsaw connections.
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub enum JointType {
118    /// Can rotate freely around the connection axis.
119    Rollable,
120    /// Must maintain alignment with the source piece.
121    Aligned,
122}
123
124/// A jigsaw connector block extracted from a structure template.
125#[derive(Debug, Clone)]
126pub struct JigsawBlock {
127    /// Position relative to template origin.
128    pub pos: [i32; 3],
129    /// Orientation (determines facing direction).
130    pub orientation: JigsawOrientation,
131    /// Name of this jigsaw connector.
132    pub name: Identifier,
133    /// Target connector name to attach to.
134    pub target: Identifier,
135    /// Pool to draw target pieces from.
136    pub pool: Identifier,
137    /// Joint type.
138    pub joint: JointType,
139    /// Block state to replace jigsaw with after placement.
140    pub final_state: Identifier,
141    /// Priority for selecting this jigsaw among siblings in a piece (higher = tried first).
142    pub selection_priority: i32,
143    /// Priority for BFS queue ordering when placing children (higher = processed first).
144    pub placement_priority: i32,
145}
146
147/// Extracted data from a structure template NBT file.
148///
149/// Contains only the information needed for jigsaw assembly — not the full
150/// block data (which is loaded separately for actual placement).
151#[derive(Debug, Clone)]
152pub struct TemplateData {
153    /// Template size in blocks (x, y, z).
154    pub size: [i32; 3],
155    /// Jigsaw connector blocks in this template.
156    pub jigsaws: Vec<JigsawBlock>,
157}
158
159/// Projection mode for pool elements.
160///
161/// Vanilla's `StructureTemplatePool.Projection`.
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum Projection {
164    /// Fixed Y position, stacked based on jigsaw positions.
165    Rigid,
166    /// Adjusts vertically to match terrain surface.
167    TerrainMatching,
168}
169
170impl Projection {
171    /// Returns the ground level delta for this projection.
172    ///
173    /// Vanilla's `StructurePoolElement.getGroundLevelDelta()` returns 1 by default.
174    /// This is the offset from the piece's minY to ground level.
175    #[must_use]
176    pub const fn ground_level_delta(self) -> i32 {
177        1
178    }
179}
180
181/// Vanilla's `Holder<StructureProcessorList>` on single pool elements.
182///
183/// The generated vanilla datapack currently uses registry references plus direct
184/// empty lists. If direct non-empty lists show up, codegen should preserve their
185/// processor entries instead of flattening them into this enum.
186#[derive(Debug, Clone, PartialEq, Eq)]
187pub enum ProcessorList {
188    /// Direct empty processor list: `{ "processors": [] }`.
189    Empty,
190    /// Registry-backed processor list, e.g. `minecraft:street_savanna`.
191    Registry(Identifier),
192}
193
194/// A pool element — one possible piece that can be drawn from a template pool.
195#[derive(Debug, Clone)]
196pub enum PoolElement {
197    /// Single structure template piece.
198    Single {
199        /// Template location (e.g., `minecraft:village/plains/houses/small_house_1`).
200        location: Identifier,
201        /// Processors applied during block placement.
202        processors: ProcessorList,
203        /// Vertical placement mode.
204        projection: Projection,
205    },
206    /// Legacy single piece (same as Single but uses legacy jigsaw processing).
207    LegacySingle {
208        /// Template location.
209        location: Identifier,
210        /// Processors applied during block placement.
211        processors: ProcessorList,
212        /// Vertical placement mode.
213        projection: Projection,
214    },
215    /// Empty placeholder element — signals no piece should be placed.
216    Empty,
217    /// A placed feature (not a structure template).
218    Feature {
219        /// Feature identifier.
220        feature: Identifier,
221        /// Vertical placement mode.
222        projection: Projection,
223    },
224    /// A list of elements placed as a group.
225    List {
226        /// Sub-elements.
227        elements: Vec<PoolElement>,
228        /// Vertical placement mode.
229        projection: Projection,
230    },
231}
232
233impl PoolElement {
234    /// Returns the projection mode, or `Rigid` for empty elements.
235    #[must_use]
236    pub fn projection(&self) -> Projection {
237        match self {
238            Self::Single { projection, .. }
239            | Self::LegacySingle { projection, .. }
240            | Self::Feature { projection, .. }
241            | Self::List { projection, .. } => *projection,
242            Self::Empty => Projection::Rigid,
243        }
244    }
245
246    /// Returns true if this is an empty placeholder element.
247    #[must_use]
248    pub fn is_empty(&self) -> bool {
249        matches!(self, Self::Empty)
250    }
251}
252
253/// A template pool — a collection of weighted pool elements.
254///
255/// Vanilla's `StructureTemplatePool`.
256#[derive(Debug, Clone)]
257pub struct TemplatePoolData {
258    /// Registry key (e.g., `minecraft:village/plains/town_centers`).
259    pub key: Identifier,
260    /// Fallback pool used when the main pool is exhausted.
261    pub fallback: Identifier,
262    /// Weighted elements. Each entry is (element, weight).
263    pub elements: Vec<(PoolElement, i32)>,
264}