Skip to main content

steel_worldgen/structure/
shipwreck.rs

1//! Shipwreck: picks a random template from the beached (11) or underwater (20) pool,
2//! places at `(chunkMinX, 90, chunkMinZ)` with random rotation and pivot `(4, 15)`.
3
4use glam::IVec3;
5use steel_registry::structure::{LiquidSettingsData, StructureConfigData, StructureData};
6use steel_utils::random::Random;
7use steel_utils::random::legacy_random::LegacyRandom;
8use steel_utils::{Direction, Identifier, Rotation};
9
10use crate::structure::{
11    GenerationStub, Structure, StructureBlockIgnore, StructureGenerationContext, StructureMirror,
12    StructurePiece, StructurePiecePayload, TemplateMarkerHandling, TemplatePieceData,
13    TemplatePlacementAdjustment, TemplatePlacementClip, TemplatePostProcess, TemplateProcessorList,
14};
15
16static BEACHED: &[&str] = &[
17    "shipwreck/with_mast",
18    "shipwreck/sideways_full",
19    "shipwreck/sideways_fronthalf",
20    "shipwreck/sideways_backhalf",
21    "shipwreck/rightsideup_full",
22    "shipwreck/rightsideup_fronthalf",
23    "shipwreck/rightsideup_backhalf",
24    "shipwreck/with_mast_degraded",
25    "shipwreck/rightsideup_full_degraded",
26    "shipwreck/rightsideup_fronthalf_degraded",
27    "shipwreck/rightsideup_backhalf_degraded",
28];
29
30static OCEAN: &[&str] = &[
31    "shipwreck/with_mast",
32    "shipwreck/upsidedown_full",
33    "shipwreck/upsidedown_fronthalf",
34    "shipwreck/upsidedown_backhalf",
35    "shipwreck/sideways_full",
36    "shipwreck/sideways_fronthalf",
37    "shipwreck/sideways_backhalf",
38    "shipwreck/rightsideup_full",
39    "shipwreck/rightsideup_fronthalf",
40    "shipwreck/rightsideup_backhalf",
41    "shipwreck/with_mast_degraded",
42    "shipwreck/upsidedown_full_degraded",
43    "shipwreck/upsidedown_fronthalf_degraded",
44    "shipwreck/upsidedown_backhalf_degraded",
45    "shipwreck/sideways_full_degraded",
46    "shipwreck/sideways_fronthalf_degraded",
47    "shipwreck/sideways_backhalf_degraded",
48    "shipwreck/rightsideup_full_degraded",
49    "shipwreck/rightsideup_fronthalf_degraded",
50    "shipwreck/rightsideup_backhalf_degraded",
51];
52
53fn make_shipwreck_piece(
54    template_id: Identifier,
55    position: IVec3,
56    rotation: Rotation,
57    size: IVec3,
58    is_beached: bool,
59) -> StructurePiece {
60    StructurePiece {
61        piece_type: Identifier::new_static("minecraft", "shipwreck"),
62        bounding_box: rotation.get_bounding_box_with_pivot(position, size, IVec3::new(4, 0, 15)),
63        gen_depth: 0,
64        orientation: Some(Direction::North),
65        payload: StructurePiecePayload::Template(TemplatePieceData {
66            template_id,
67            template_position: position,
68            rotation,
69            mirror: StructureMirror::None,
70            rotation_pivot: IVec3::new(4, 0, 15),
71            block_ignore: StructureBlockIgnore::StructureAndAir,
72            late_block_ignore: StructureBlockIgnore::None,
73            processors: TemplateProcessorList::Empty,
74            liquid_settings: LiquidSettingsData::ApplyWaterlogging,
75            marker_handling: TemplateMarkerHandling::Shipwreck,
76            placement_adjustment: TemplatePlacementAdjustment::Shipwreck {
77                is_beached,
78                height_adjusted: false,
79            },
80            placement_clip: TemplatePlacementClip::CenterChunk,
81            post_process: TemplatePostProcess::None,
82        }),
83        ground_level_delta: 0,
84        junctions: Vec::new(),
85        projection: None,
86    }
87}
88
89/// Registered under `"minecraft:shipwreck"`. Beached vs underwater is distinguished by
90/// `entry.structure.path`.
91pub struct ShipwreckStructure;
92
93impl Structure for ShipwreckStructure {
94    fn find_generation_point(
95        &self,
96        ctx: &mut dyn StructureGenerationContext,
97        structure: &StructureData,
98        rng: &mut LegacyRandom,
99    ) -> Option<GenerationStub> {
100        let surface_y = ctx.surface_y();
101        let biome = ctx.biome_at(ctx.center_block_x(), surface_y, ctx.center_block_z());
102        if !structure.allowed_biomes.contains(&biome.key) {
103            return None;
104        }
105
106        let StructureConfigData::Shipwreck { is_beached } = &structure.config else {
107            return None;
108        };
109        let templates_arr = if *is_beached { BEACHED } else { OCEAN };
110
111        let rotation = Rotation::get_random(rng);
112        let idx = rng.next_i32_bounded(templates_arr.len() as i32) as usize;
113        let template_id = Identifier::vanilla_static(templates_arr[idx]);
114        let tmpl = ctx.templates().get(&template_id)?;
115        let position = IVec3::new(ctx.chunk_min_x(), 90, ctx.chunk_min_z());
116
117        Some(GenerationStub {
118            position: (ctx.center_block_x(), surface_y, ctx.center_block_z()),
119            pieces: vec![make_shipwreck_piece(
120                template_id,
121                position,
122                rotation,
123                IVec3::from(tmpl.size),
124                *is_beached,
125            )],
126        })
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn shipwreck_piece_uses_template_payload_with_height_adjustment() {
136        let position = IVec3::new(32, 90, -48);
137        let size = IVec3::new(15, 12, 28);
138        let piece = make_shipwreck_piece(
139            Identifier::vanilla_static("shipwreck/with_mast"),
140            position,
141            Rotation::Clockwise180,
142            size,
143            true,
144        );
145
146        assert_eq!(
147            piece.piece_type,
148            Identifier::new_static("minecraft", "shipwreck")
149        );
150        assert_eq!(piece.gen_depth, 0);
151        assert_eq!(piece.orientation, Some(Direction::North));
152        assert_eq!(
153            piece.bounding_box,
154            Rotation::Clockwise180.get_bounding_box_with_pivot(
155                position,
156                size,
157                IVec3::new(4, 0, 15),
158            ),
159        );
160
161        let StructurePiecePayload::Template(data) = piece.payload else {
162            panic!("shipwreck piece should be template-backed");
163        };
164        assert_eq!(
165            data.template_id,
166            Identifier::vanilla_static("shipwreck/with_mast")
167        );
168        assert_eq!(data.template_position, position);
169        assert_eq!(data.rotation, Rotation::Clockwise180);
170        assert_eq!(data.mirror, StructureMirror::None);
171        assert_eq!(data.rotation_pivot, IVec3::new(4, 0, 15));
172        assert_eq!(data.block_ignore, StructureBlockIgnore::StructureAndAir);
173        assert_eq!(data.late_block_ignore, StructureBlockIgnore::None);
174        assert_eq!(data.processors, TemplateProcessorList::Empty);
175        assert_eq!(data.liquid_settings, LiquidSettingsData::ApplyWaterlogging);
176        assert_eq!(data.marker_handling, TemplateMarkerHandling::Shipwreck);
177        assert_eq!(
178            data.placement_adjustment,
179            TemplatePlacementAdjustment::Shipwreck {
180                is_beached: true,
181                height_adjusted: false,
182            }
183        );
184        assert_eq!(data.placement_clip, TemplatePlacementClip::CenterChunk);
185        assert_eq!(data.post_process, TemplatePostProcess::None);
186    }
187}