Skip to main content

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}