steel_worldgen/structure/
desert_pyramid.rs1use steel_registry::structure::StructureData;
4use steel_utils::random::legacy_random::LegacyRandom;
5use steel_utils::{BlockPos, 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 DESERT_PYRAMID_WIDTH: i32 = 21;
13pub(crate) const DESERT_PYRAMID_HEIGHT: i32 = 15;
14pub(crate) const DESERT_PYRAMID_DEPTH: i32 = 21;
15
16#[derive(Debug, Clone)]
18pub struct DesertPyramidPieceData {
19 pub height_position: Option<i32>,
21 pub has_placed_chest: [bool; 4],
23 pub potential_suspicious_sand_world_positions: Vec<BlockPos>,
25 pub random_collapsed_roof_pos: BlockPos,
27}
28
29impl DesertPyramidPieceData {
30 #[must_use]
32 pub const fn new() -> Self {
33 Self {
34 height_position: None,
35 has_placed_chest: [false; 4],
36 potential_suspicious_sand_world_positions: Vec::new(),
37 random_collapsed_roof_pos: BlockPos::new(0, 0, 0),
38 }
39 }
40}
41
42impl Default for DesertPyramidPieceData {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48pub struct DesertPyramidStructure;
50
51const fn desert_pyramid_piece(
52 chunk_min_x: i32,
53 chunk_min_z: i32,
54 orientation: Direction,
55) -> StructurePiece {
56 StructurePiece {
57 piece_type: Identifier::new_static("minecraft", "tedp"),
58 bounding_box: make_oriented_piece_bounding_box(
59 chunk_min_x,
60 64,
61 chunk_min_z,
62 orientation,
63 DESERT_PYRAMID_WIDTH,
64 DESERT_PYRAMID_HEIGHT,
65 DESERT_PYRAMID_DEPTH,
66 ),
67 gen_depth: 0,
68 orientation: Some(orientation),
69 payload: StructurePiecePayload::Procedural(ProceduralPieceData::DesertPyramid(
70 DesertPyramidPieceData::new(),
71 )),
72 ground_level_delta: 0,
73 junctions: Vec::new(),
74 projection: None,
75 }
76}
77
78impl Structure for DesertPyramidStructure {
79 fn find_generation_point(
80 &self,
81 ctx: &mut dyn StructureGenerationContext,
82 structure: &StructureData,
83 rng: &mut LegacyRandom,
84 ) -> Option<GenerationStub> {
85 let (x0, z0) = (ctx.chunk_min_x(), ctx.chunk_min_z());
86 let h0 = ctx.base_height(x0, z0, false) - 1;
87 let h1 = ctx.base_height(x0, z0 + DESERT_PYRAMID_DEPTH, false) - 1;
88 let h2 = ctx.base_height(x0 + DESERT_PYRAMID_WIDTH, z0, false) - 1;
89 let h3 = ctx.base_height(x0 + DESERT_PYRAMID_WIDTH, z0 + DESERT_PYRAMID_DEPTH, false) - 1;
90 if h0.min(h1).min(h2).min(h3) < ctx.sea_level() {
91 return None;
92 }
93
94 let surface_y = ctx.surface_y();
95 let biome = ctx.biome_at(ctx.center_block_x(), surface_y, ctx.center_block_z());
96 if !structure.allowed_biomes.contains(&biome.key) {
97 return None;
98 }
99
100 let orientation = random_horizontal_direction(rng);
101 Some(GenerationStub {
102 position: (ctx.center_block_x(), surface_y, ctx.center_block_z()),
103 pieces: vec![desert_pyramid_piece(
104 ctx.chunk_min_x(),
105 ctx.chunk_min_z(),
106 orientation,
107 )],
108 })
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use glam::IVec3;
116 use steel_utils::BoundingBox;
117
118 #[test]
119 fn desert_pyramid_piece_uses_full_procedural_payload() {
120 let piece = desert_pyramid_piece(32, -48, Direction::East);
121
122 assert_eq!(
123 piece.piece_type,
124 Identifier::new_static("minecraft", "tedp")
125 );
126 assert_eq!(piece.gen_depth, 0);
127 assert_eq!(piece.orientation, Some(Direction::East));
128 assert_eq!(
129 piece.bounding_box,
130 BoundingBox::new(IVec3::new(32, 64, -48), IVec3::new(52, 78, -28))
131 );
132 let StructurePiecePayload::Procedural(ProceduralPieceData::DesertPyramid(data)) =
133 piece.payload
134 else {
135 panic!("desert pyramid should use its procedural payload");
136 };
137 assert_eq!(data.height_position, None);
138 assert_eq!(data.has_placed_chest, [false; 4]);
139 assert!(data.potential_suspicious_sand_world_positions.is_empty());
140 assert_eq!(data.random_collapsed_roof_pos, BlockPos::new(0, 0, 0));
141 }
142}