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}