Skip to main content

steel_worldgen/structure/
jungle_temple.rs

1//! Jungle temple 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 JUNGLE_TEMPLE_WIDTH: i32 = 12;
13pub(crate) const JUNGLE_TEMPLE_HEIGHT: i32 = 10;
14pub(crate) const JUNGLE_TEMPLE_DEPTH: i32 = 15;
15
16/// Runtime state for vanilla `JungleTemplePiece`.
17#[derive(Debug, Clone, Default)]
18pub struct JungleTemplePieceData {
19    /// Vanilla `ScatteredFeaturePiece.heightPosition`; `None` means not height-adjusted yet.
20    pub height_position: Option<i32>,
21    /// Whether the main chest has already been placed.
22    pub placed_main_chest: bool,
23    /// Whether the hidden chest has already been placed.
24    pub placed_hidden_chest: bool,
25    /// Whether the first arrow-dispenser trap has already been placed.
26    pub placed_trap1: bool,
27    /// Whether the second arrow-dispenser trap has already been placed.
28    pub placed_trap2: bool,
29}
30
31impl JungleTemplePieceData {
32    /// Creates a fresh jungle temple piece payload.
33    #[must_use]
34    pub const fn new() -> Self {
35        Self {
36            height_position: None,
37            placed_main_chest: false,
38            placed_hidden_chest: false,
39            placed_trap1: false,
40            placed_trap2: false,
41        }
42    }
43}
44
45/// Vanilla's `JungleTempleStructure` / `SinglePieceStructure`.
46pub struct JungleTempleStructure;
47
48const fn jungle_temple_piece(west: i32, north: i32, orientation: Direction) -> StructurePiece {
49    StructurePiece {
50        piece_type: Identifier::new_static("minecraft", "tejp"),
51        bounding_box: make_oriented_piece_bounding_box(
52            west,
53            64,
54            north,
55            orientation,
56            JUNGLE_TEMPLE_WIDTH,
57            JUNGLE_TEMPLE_HEIGHT,
58            JUNGLE_TEMPLE_DEPTH,
59        ),
60        gen_depth: 0,
61        orientation: Some(orientation),
62        payload: StructurePiecePayload::Procedural(ProceduralPieceData::JungleTemple(
63            JungleTemplePieceData::new(),
64        )),
65        ground_level_delta: 0,
66        junctions: Vec::new(),
67        projection: None,
68    }
69}
70
71impl Structure for JungleTempleStructure {
72    fn find_generation_point(
73        &self,
74        ctx: &mut dyn StructureGenerationContext,
75        structure: &StructureData,
76        rng: &mut LegacyRandom,
77    ) -> Option<GenerationStub> {
78        let x0 = ctx.chunk_min_x();
79        let z0 = ctx.chunk_min_z();
80        let h0 = ctx.base_height(x0, z0, false) - 1;
81        let h1 = ctx.base_height(x0, z0 + JUNGLE_TEMPLE_DEPTH, false) - 1;
82        let h2 = ctx.base_height(x0 + JUNGLE_TEMPLE_WIDTH, z0, false) - 1;
83        let h3 = ctx.base_height(x0 + JUNGLE_TEMPLE_WIDTH, z0 + JUNGLE_TEMPLE_DEPTH, false) - 1;
84        if h0.min(h1).min(h2).min(h3) < ctx.sea_level() {
85            return None;
86        }
87
88        let center_y = ctx.base_height(ctx.center_block_x(), ctx.center_block_z(), false) - 1;
89        let biome = ctx.biome_at(ctx.center_block_x(), center_y, ctx.center_block_z());
90        if !structure.allowed_biomes.contains(&biome.key) {
91            return None;
92        }
93
94        let orientation = random_horizontal_direction(rng);
95        Some(GenerationStub {
96            position: (ctx.center_block_x(), center_y, ctx.center_block_z()),
97            pieces: vec![jungle_temple_piece(
98                ctx.chunk_min_x(),
99                ctx.chunk_min_z(),
100                orientation,
101            )],
102        })
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn jungle_temple_piece_uses_full_procedural_payload() {
112        let piece = jungle_temple_piece(16, 32, Direction::South);
113
114        assert_eq!(
115            piece.piece_type,
116            Identifier::new_static("minecraft", "tejp")
117        );
118        assert_eq!(piece.gen_depth, 0);
119        assert_eq!(piece.orientation, Some(Direction::South));
120        let StructurePiecePayload::Procedural(ProceduralPieceData::JungleTemple(data)) =
121            piece.payload
122        else {
123            panic!("jungle temple piece should use procedural payload");
124        };
125        assert_eq!(data.height_position, None);
126        assert!(!data.placed_main_chest);
127        assert!(!data.placed_hidden_chest);
128        assert!(!data.placed_trap1);
129        assert!(!data.placed_trap2);
130    }
131}