Skip to main content

steel_worldgen/structure/
swamp_hut.rs

1//! Swamp hut structure start generation.
2
3use steel_registry::structure::StructureData;
4use steel_utils::random::legacy_random::LegacyRandom;
5use steel_utils::{Direction, Identifier};
6
7use crate::structure::{
8    GenerationStub, ProceduralPieceData, Structure, StructureGenerationContext, StructurePiece,
9    StructurePiecePayload, make_oriented_piece_bounding_box, random_horizontal_direction,
10};
11
12pub(crate) const SWAMP_HUT_WIDTH: i32 = 7;
13pub(crate) const SWAMP_HUT_HEIGHT: i32 = 7;
14pub(crate) const SWAMP_HUT_DEPTH: i32 = 9;
15
16/// Runtime state for vanilla `SwampHutPiece`.
17#[derive(Debug, Clone, Default)]
18pub struct SwampHutPieceData {
19    /// Vanilla `ScatteredFeaturePiece.heightPosition`; `None` means not height-adjusted yet.
20    pub height_position: Option<i32>,
21    /// Whether the structure witch has already been spawned.
22    pub spawned_witch: bool,
23    /// Whether the structure black cat has already been spawned.
24    pub spawned_cat: bool,
25}
26
27impl SwampHutPieceData {
28    /// Creates a fresh swamp hut piece payload.
29    #[must_use]
30    pub const fn new() -> Self {
31        Self {
32            height_position: None,
33            spawned_witch: false,
34            spawned_cat: false,
35        }
36    }
37}
38
39/// Vanilla's `SwampHutStructure`.
40pub struct SwampHutStructure;
41
42const fn swamp_hut_piece(west: i32, north: i32, orientation: Direction) -> StructurePiece {
43    StructurePiece {
44        piece_type: Identifier::new_static("minecraft", "tesh"),
45        bounding_box: make_oriented_piece_bounding_box(
46            west,
47            64,
48            north,
49            orientation,
50            SWAMP_HUT_WIDTH,
51            SWAMP_HUT_HEIGHT,
52            SWAMP_HUT_DEPTH,
53        ),
54        gen_depth: 0,
55        orientation: Some(orientation),
56        payload: StructurePiecePayload::Procedural(ProceduralPieceData::SwampHut(
57            SwampHutPieceData::new(),
58        )),
59        ground_level_delta: 0,
60        junctions: Vec::new(),
61        projection: None,
62    }
63}
64
65impl Structure for SwampHutStructure {
66    fn find_generation_point(
67        &self,
68        ctx: &mut dyn StructureGenerationContext,
69        structure: &StructureData,
70        rng: &mut LegacyRandom,
71    ) -> Option<GenerationStub> {
72        let center_y = ctx.base_height(ctx.center_block_x(), ctx.center_block_z(), false) - 1;
73        let biome = ctx.biome_at(ctx.center_block_x(), center_y, ctx.center_block_z());
74        if !structure.allowed_biomes.contains(&biome.key) {
75            return None;
76        }
77
78        let orientation = random_horizontal_direction(rng);
79        Some(GenerationStub {
80            position: (ctx.center_block_x(), center_y, ctx.center_block_z()),
81            pieces: vec![swamp_hut_piece(
82                ctx.chunk_min_x(),
83                ctx.chunk_min_z(),
84                orientation,
85            )],
86        })
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn swamp_hut_piece_uses_full_procedural_payload() {
96        let piece = swamp_hut_piece(16, 32, Direction::West);
97
98        assert_eq!(
99            piece.piece_type,
100            Identifier::new_static("minecraft", "tesh")
101        );
102        assert_eq!(piece.gen_depth, 0);
103        assert_eq!(piece.orientation, Some(Direction::West));
104        let StructurePiecePayload::Procedural(ProceduralPieceData::SwampHut(data)) = piece.payload
105        else {
106            panic!("swamp hut piece should use procedural payload");
107        };
108        assert_eq!(data.height_position, None);
109        assert!(!data.spawned_witch);
110        assert!(!data.spawned_cat);
111    }
112}