steel_worldgen/structure/piece.rs
1//! Runtime structure-piece payload model used by feature-stage placement.
2
3use glam::IVec3;
4use steel_registry::structure::{
5 LiquidSettingsData, OceanRuinBiomeTempData, RuinedPortalPlacementData,
6};
7use steel_registry::template_pool::Projection;
8use steel_utils::{BoundingBox, Direction, Identifier, Rotation};
9
10use super::{
11 desert_pyramid, fortress, jigsaw, jungle_temple, mineshaft, ocean_monument, stronghold,
12 swamp_hut,
13};
14
15use steel_registry::Registry;
16use steel_registry::vanilla_blocks;
17use steel_utils::BlockStateId;
18
19/// Vanilla's `StructurePiece` runtime state.
20#[derive(Debug, Clone)]
21pub struct StructurePiece {
22 /// Piece type id (e.g., `minecraft:jigsaw`).
23 pub piece_type: Identifier,
24 /// World-space bounding box.
25 pub bounding_box: BoundingBox,
26 /// Distance from the start piece in the piece tree.
27 pub gen_depth: i32,
28 /// Horizontal orientation; `None` for unoriented pieces.
29 pub orientation: Option<Direction>,
30 /// Type-specific data used by the structure-piece placement stage.
31 pub payload: StructurePiecePayload,
32 /// Offset from piece minY to ground level. Used by Beardifier. Default 0 for non-jigsaw.
33 pub ground_level_delta: i32,
34 /// Junctions for Beardifier terrain adaptation.
35 pub junctions: Vec<jigsaw::JigsawJunction>,
36 /// Jigsaw projection. `None` for non-jigsaw pieces.
37 ///
38 /// Beardifier treats `Some(Rigid)` and `None` as terrain-adapting, but skips
39 /// `Some(TerrainMatching)` from the rigid set (still collecting junctions).
40 /// Mirrors vanilla's `piece instanceof PoolElementStructurePiece` + `Projection.RIGID` check.
41 pub projection: Option<Projection>,
42}
43
44impl StructurePiece {
45 /// Creates a non-jigsaw piece with vanilla's default non-pool metadata.
46 #[must_use]
47 pub const fn non_jigsaw(
48 piece_type: Identifier,
49 bounding_box: BoundingBox,
50 gen_depth: i32,
51 orientation: Option<Direction>,
52 ) -> Self {
53 Self {
54 piece_type,
55 bounding_box,
56 gen_depth,
57 orientation,
58 payload: StructurePiecePayload::Procedural(ProceduralPieceData::Unimplemented),
59 ground_level_delta: 0,
60 junctions: Vec::new(),
61 projection: None,
62 }
63 }
64}
65
66/// Type-specific structure-piece placement payload.
67///
68/// This is Steel's boundary between structure-start generation and feature-stage
69/// block placement. Common vanilla fields stay on [`StructurePiece`]; placement
70/// implementations dispatch on this enum instead of inferring behavior from a
71/// bounding box or legacy NBT blob.
72#[derive(Debug, Clone)]
73pub enum StructurePiecePayload {
74 /// Pool piece produced by jigsaw assembly.
75 Jigsaw(jigsaw::JigsawPieceData),
76 /// Template-backed vanilla piece outside the jigsaw system.
77 Template(TemplatePieceData),
78 /// Code-generated piece family whose blocks are emitted procedurally.
79 Procedural(ProceduralPieceData),
80}
81
82/// Template-backed non-jigsaw placement data.
83#[derive(Debug, Clone)]
84pub struct TemplatePieceData {
85 /// Structure template identifier.
86 pub template_id: Identifier,
87 /// World-space template origin before rotation/mirror transforms.
88 pub template_position: IVec3,
89 /// Template rotation.
90 pub rotation: Rotation,
91 /// Template mirror mode.
92 pub mirror: StructureMirror,
93 /// Rotation pivot in template-local block coordinates.
94 pub rotation_pivot: IVec3,
95 /// Block-ignore processor applied before the registry processor list.
96 pub block_ignore: StructureBlockIgnore,
97 /// Block-ignore processor applied after the registry processor list.
98 pub late_block_ignore: StructureBlockIgnore,
99 /// Processor list applied during placement.
100 pub processors: TemplateProcessorList,
101 /// Liquid handling mode used by vanilla template placement.
102 pub liquid_settings: LiquidSettingsData,
103 /// How structure-template data markers are handled for this family.
104 pub marker_handling: TemplateMarkerHandling,
105 /// Family-specific position adjustment before template block placement.
106 pub placement_adjustment: TemplatePlacementAdjustment,
107 /// Bounding box passed to vanilla template placement.
108 pub placement_clip: TemplatePlacementClip,
109 /// Family-specific work done after the template blocks are placed.
110 pub post_process: TemplatePostProcess,
111}
112
113/// Processors for template-backed non-jigsaw pieces.
114#[derive(Debug, Clone, PartialEq)]
115pub enum TemplateProcessorList {
116 /// No processors.
117 Empty,
118 /// Registry-backed vanilla processor list.
119 Registry(Identifier),
120 /// Vanilla's hardcoded ocean-ruin block-rot and archaeology processors.
121 OceanRuin {
122 /// Warm/cold ruin variant controls suspicious sand/gravel and archaeology loot.
123 biome_temp: OceanRuinBiomeTempData,
124 /// `BlockRotProcessor` keep probability.
125 integrity: f32,
126 },
127 /// Vanilla's hardcoded ruined-portal processor sequence.
128 RuinedPortal {
129 /// Vertical placement controls lava replacement.
130 vertical_placement: RuinedPortalPlacementData,
131 /// Ruined portal setup properties.
132 properties: RuinedPortalProperties,
133 },
134}
135
136/// Vanilla ruined-portal piece properties used by processors and postprocess.
137#[derive(Debug, Clone, Copy, PartialEq)]
138pub struct RuinedPortalProperties {
139 /// Whether cold lava/netherrack behavior is active.
140 pub cold: bool,
141 /// Vanilla block-age processor mossiness.
142 pub mossiness: f32,
143 /// Whether structure air is preserved.
144 pub air_pocket: bool,
145 /// Whether netherrack can grow jungle leaves.
146 pub overgrown: bool,
147 /// Whether vines can be added to sturdy sides.
148 pub vines: bool,
149 /// Whether stone ruin blocks are replaced with blackstone variants.
150 pub replace_with_blackstone: bool,
151}
152
153/// Vanilla `Mirror` modes used by template placement.
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum StructureMirror {
156 /// No mirror transform.
157 None,
158 /// Mirror across the template front/back axis.
159 FrontBack,
160 /// Mirror across the template left/right axis.
161 LeftRight,
162}
163
164/// Hardcoded vanilla block-ignore processors used by template placement.
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166pub enum StructureBlockIgnore {
167 /// Do not ignore any block states.
168 None,
169 /// Ignore structure blocks.
170 StructureBlock,
171 /// Ignore structure blocks and air.
172 StructureAndAir,
173}
174
175impl StructureBlockIgnore {
176 /// Checks whether the block state should be ignored during placement.
177 ///
178 /// # Panics
179 ///
180 /// Panics if the given block state ID is not registered in the blocks registry.
181 #[must_use]
182 pub fn ignores(self, registry: &Registry, state: BlockStateId) -> bool {
183 match self {
184 Self::None => false,
185 Self::StructureBlock => {
186 registry.blocks.by_state_id(state).expect("invalid state")
187 == &vanilla_blocks::STRUCTURE_BLOCK
188 }
189 Self::StructureAndAir => {
190 let block = registry.blocks.by_state_id(state).expect("invalid state");
191 block == &vanilla_blocks::STRUCTURE_BLOCK || block == &vanilla_blocks::AIR
192 }
193 }
194 }
195}
196
197/// Marker handling requested by a template-backed structure piece.
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
199pub enum TemplateMarkerHandling {
200 /// Ignore data markers.
201 Ignore,
202 /// Dispatch data markers to the structure-family placement code.
203 DataMarkers,
204 /// Ocean ruin chest and drowned markers.
205 OceanRuin {
206 /// Whether the ruin uses the large chest loot table.
207 is_large: bool,
208 },
209 /// Shipwreck map, supply, and treasure chest markers.
210 Shipwreck,
211 /// Igloo basement chest marker.
212 Igloo,
213 /// End-city chest, shulker, and Elytra frame markers.
214 EndCity,
215 /// Woodland mansion chest, illager, and allay markers.
216 WoodlandMansion,
217}
218
219/// Family-specific template position adjustment before block placement.
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221pub enum TemplatePlacementAdjustment {
222 /// Place at the persisted template position.
223 None,
224 /// Shipwreck height adjustment, persisted after the first placement call.
225 Shipwreck {
226 /// Whether this is the beached shipwreck variant.
227 is_beached: bool,
228 /// Vanilla `height_adjusted` flag.
229 height_adjusted: bool,
230 },
231 /// Igloo per-placement height adjustment.
232 Igloo {
233 /// Vanilla template offset for this igloo piece.
234 template_offset: (i32, i32, i32),
235 },
236 /// Ocean ruin terrain height adjustment.
237 OceanRuin,
238}
239
240/// Vanilla bounding box adjustment before calling `StructureTemplate.placeInWorld`.
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242#[expect(
243 clippy::enum_variant_names,
244 reason = "variants intentionally name the center-chunk clipping basis used by vanilla structure placement"
245)]
246pub enum TemplatePlacementClip {
247 /// Use the center chunk's writable box unchanged.
248 CenterChunk,
249 /// Expand the center chunk writable box to include this piece's transformed template box.
250 CenterChunkExpandedToTemplate,
251 /// Expand to the transformed template box only when its center is in the center chunk.
252 CenterChunkContainsTemplateCenterExpandedToTemplate,
253}
254
255/// Family-specific post-template processing for template-backed pieces.
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub enum TemplatePostProcess {
258 /// No family-specific postprocess.
259 None,
260 /// Nether fossil dried-ghast placement.
261 NetherFossil,
262 /// Igloo top-piece trapdoor snow-block fixup.
263 IglooTop,
264 /// Ruined portal netherrack spread, drip columns, vines, and overgrowth.
265 RuinedPortal,
266}
267
268/// Family-specific state for code-generated structure pieces.
269#[derive(Debug, Clone)]
270pub enum ProceduralPieceData {
271 /// Procedural family whose placement implementation has not been enabled yet.
272 Unimplemented,
273 /// Buried treasure chest placement.
274 BuriedTreasure,
275 /// Desert pyramid piece payload.
276 DesertPyramid(desert_pyramid::DesertPyramidPieceData),
277 /// Jungle temple piece payload.
278 JungleTemple(jungle_temple::JungleTemplePieceData),
279 /// Mineshaft room/corridor/crossing/stairs payload.
280 Mineshaft(mineshaft::MineshaftPiecePayload),
281 /// Nether fortress bridge/castle piece payload.
282 NetherFortress(fortress::FortressPieceData),
283 /// Ocean monument building payload.
284 OceanMonument(ocean_monument::OceanMonumentPieceData),
285 /// Stronghold recursive piece payload.
286 Stronghold(stronghold::StrongholdPieceData),
287 /// Swamp hut piece payload.
288 SwampHut(swamp_hut::SwampHutPieceData),
289}