steel_worldgen/structure/
shipwreck.rs1use 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
89pub 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}