1use super::*;
2use crate::{
3 Land,
4 site2::gen::{place_circular_as_vec, spiral_staircase},
5 util::{CARDINALS, DIAGONALS, RandomField, Sampler},
6};
7use common::{
8 generation::SpecialEntity,
9 terrain::{BlockKind, SpriteKind},
10};
11use rand::prelude::*;
12use std::{f32::consts::TAU, sync::Arc};
13use vek::*;
14
15pub struct SavannahAirshipDock {
17 pub door_tile: Vec2<i32>,
19 pub(crate) alt: i32,
21 pub center: Vec2<i32>,
22 length: i32,
23 platform_height: i32,
24 pub docking_positions: Vec<Vec3<i32>>,
25}
26
27impl SavannahAirshipDock {
28 pub fn generate(
29 land: &Land,
30 _rng: &mut impl Rng,
31 site: &Site,
32 door_tile: Vec2<i32>,
33 door_dir: Vec2<i32>,
34 tile_aabr: Aabr<i32>,
35 alt: Option<i32>,
36 ) -> Self {
37 let door_tile_pos = site.tile_center_wpos(door_tile);
38 let bounds = Aabr {
39 min: site.tile_wpos(tile_aabr.min),
40 max: site.tile_wpos(tile_aabr.max),
41 };
42 let center = bounds.center();
43 let alt = alt.unwrap_or_else(|| {
44 land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
45 }) + 2;
46 let base = alt + 1;
47 let length = 18;
48 let platform_height = 40;
49 let mut docking_positions = vec![];
50 let top_floor = base + platform_height - 2;
51 for dir in CARDINALS {
52 let docking_pos = center + dir * (length + 5);
53 docking_positions.push(docking_pos.with_z(top_floor));
54 }
55
56 Self {
57 door_tile: door_tile_pos,
58 alt,
59 center,
60 length,
61 platform_height,
62 docking_positions,
63 }
64 }
65
66 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
67 SpawnRules {
68 trees: {
69 const AIRSHIP_MIN_TREE_DIST2: i32 = 59i32.pow(2);
74 wpos.distance_squared(self.center) > AIRSHIP_MIN_TREE_DIST2
75 },
76 waypoints: false,
77 ..SpawnRules::default()
78 }
79 }
80}
81
82impl Structure for SavannahAirshipDock {
83 #[cfg(feature = "use-dyn-lib")]
84 const UPDATE_FN: &'static [u8] = b"render_savannah_airship_dock\0";
85
86 #[cfg_attr(
87 feature = "be-dyn-lib",
88 unsafe(export_name = "render_savannah_airship_dock")
89 )]
90 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
91 let base = self.alt + 1;
92 let center = self.center;
93 let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
94 let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
95 let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
96 let color = Fill::Sampling(Arc::new(|center| {
97 Some(match (RandomField::new(0).get(center)) % 7 {
98 0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
99 1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
100 2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
101 3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
102 4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
103 5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
104 _ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
105 })
106 }));
107 let length = self.length;
108 let height = length / 2;
109 let platform_height = self.platform_height;
110 let storeys = 1;
111 let radius = length + (length / 3);
112 let reed_var = (1 + RandomField::new(0).get(center.with_z(base)) % 4) as f32;
113 let reed_parts = 36_f32 + reed_var;
114 let phi = TAU / reed_parts;
115
116 painter
118 .cylinder(Aabb {
119 min: (center - length).with_z(base - 3),
120 max: (center + length + 1).with_z(base - 2),
121 })
122 .fill(clay.clone());
123 painter
124 .cylinder(Aabb {
125 min: (center - length - 1).with_z(base - 4),
126 max: (center + length + 2).with_z(base - 3),
127 })
128 .fill(clay.clone());
129 painter
130 .cylinder(Aabb {
131 min: (center - length - 2).with_z(base - 5),
132 max: (center + length + 3).with_z(base - 4),
133 })
134 .fill(clay.clone());
135 painter
136 .cylinder(Aabb {
137 min: (center - length - 3).with_z(base - height),
138 max: (center + length + 4).with_z(base - 5),
139 })
140 .fill(clay.clone());
141 painter
143 .cylinder(Aabb {
144 min: (center - (2 * (length / 3)) - 1).with_z(base + platform_height - 4),
145 max: (center + (2 * (length / 3)) + 1).with_z(base + platform_height - 3),
146 })
147 .fill(color.clone());
148 painter
149 .cylinder(Aabb {
150 min: (center - (2 * (length / 3))).with_z(base + platform_height - 4),
151 max: (center + (2 * (length / 3))).with_z(base + platform_height - 3),
152 })
153 .fill(clay.clone());
154 painter
155 .cylinder(Aabb {
156 min: (center - length - 2).with_z(base + platform_height - 3),
157 max: (center + length + 2).with_z(base + platform_height - 2),
158 })
159 .fill(color.clone());
160 painter
161 .cylinder(Aabb {
162 min: (center - length - 1).with_z(base + platform_height - 3),
163 max: (center + length + 1).with_z(base + platform_height - 2),
164 })
165 .fill(clay.clone());
166 for dir in CARDINALS {
168 let dock_pos = center + dir * (length + 2);
169 painter
170 .cylinder(Aabb {
171 min: (dock_pos - 5).with_z(base + platform_height - 3),
172 max: (dock_pos + 5).with_z(base + platform_height - 2),
173 })
174 .fill(color.clone());
175 painter
176 .cylinder(Aabb {
177 min: (dock_pos - 4).with_z(base + platform_height - 3),
178 max: (dock_pos + 4).with_z(base + platform_height - 2),
179 })
180 .fill(wood_dark.clone());
181 }
182
183 for dir in CARDINALS {
185 let lantern_pos = center + (dir * length);
186
187 painter.sprite(
188 lantern_pos.with_z(base + platform_height - 2),
189 SpriteKind::Lantern,
190 );
191 }
192 for dir in DIAGONALS {
193 let cargo_pos = center + (dir * ((length / 2) - 1));
194 for dir in CARDINALS {
195 let sprite_pos = cargo_pos + dir;
196 let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
197 for r in 0..rows {
198 painter
199 .aabb(Aabb {
200 min: (sprite_pos).with_z(base + platform_height - 2 + r),
201 max: (sprite_pos + 1).with_z(base + platform_height - 1 + r),
202 })
203 .fill(Fill::Block(Block::air(
204 match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2) as i32
205 {
206 0 => SpriteKind::Barrel,
207 _ => SpriteKind::CrateBlock,
208 },
209 )));
210 if r > 0 {
211 painter.owned_resource_sprite(
212 sprite_pos.with_z(base + platform_height - 1 + r),
213 SpriteKind::Crate,
214 0,
215 );
216 }
217 }
218 }
219 }
220 let campfire_pos = (center - (2 * (length / 3)) - 1).with_z(base + platform_height);
222 painter.spawn(
223 EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
224 .into_special(SpecialEntity::Waypoint),
225 );
226 for b in 0..2 {
227 let base = base + (b * platform_height);
228 let radius = radius - (b * (radius / 3));
229 let length = length - (b * (length / 3));
230 painter
232 .cone(Aabb {
233 min: (center - radius).with_z(base + (storeys * height) - (height / 2) + 1),
234 max: (center + radius)
235 .with_z(base + (storeys * height) + (height / 2) - 1 + reed_var as i32),
236 })
237 .fill(reed.clone());
238 painter
239 .cone(Aabb {
240 min: (center - radius).with_z(base + (storeys * height) - (height / 2)),
241 max: (center + radius)
242 .with_z(base + (storeys * height) + (height / 2) - 2 + reed_var as i32),
243 })
244 .clear();
245 for s in 0..storeys {
247 let room = painter.cylinder(Aabb {
248 min: (center - length + 2 + s).with_z(base - 2 + (s * height)),
249 max: (center + 1 + length - 2 - s).with_z(base + height + (s * height)),
250 });
251 room.fill(clay.clone());
252 for dir in DIAGONALS {
254 let decor_pos = center + dir * (length - 2 - s);
255 let decor = painter
256 .line(
257 center.with_z(base - 1 + (s * (height + 2))),
258 decor_pos.with_z(base - 1 + (s * (height + 2))),
259 5.0,
260 )
261 .intersect(room);
262 decor.fill(color.clone());
263 painter
264 .line(
265 center.with_z(base - 1 + (s * (height + 2))),
266 decor_pos.with_z(base - 1 + (s * (height + 2))),
267 4.0,
268 )
269 .intersect(decor)
270 .fill(clay.clone());
271 }
272 }
273 painter
275 .cylinder(Aabb {
276 min: (center - length + 4).with_z(base - 2),
277 max: (center + 1 + length - 4).with_z(base + (storeys * height)),
278 })
279 .clear();
280 painter
282 .cylinder(Aabb {
283 min: (center - length + 4).with_z(base - 1),
284 max: (center + 1 + length - 4).with_z(base),
285 })
286 .fill(wood_dark.clone());
287 painter
288 .cylinder(Aabb {
289 min: (center - length + 4).with_z(base + (storeys * height) - 1),
290 max: (center + 1 + length - 4).with_z(base + (storeys * height) + 1),
291 })
292 .fill(wood_dark.clone());
293
294 for s in 0..storeys {
295 for dir in CARDINALS {
297 let frame_pos = center + dir * (length - 2 - s);
298 let clear_pos = center + dir * (length + 2 - s);
299
300 painter
301 .line(
302 center.with_z(base - 1 + (s * (height + 2))),
303 frame_pos.with_z(base - 1 + (s * (height + 2))),
304 3.0,
305 )
306 .fill(color.clone());
307 painter
308 .line(
309 center.with_z(base - 1 + (s * (height + 2))),
310 clear_pos.with_z(base - 1 + (s * (height + 2))),
311 2.0,
312 )
313 .clear();
314 }
315 }
316 painter
318 .cylinder(Aabb {
319 min: (center - length + 5).with_z(base - 2),
320 max: (center + 1 + length - 5).with_z(base + (storeys * height) + 1),
321 })
322 .clear();
323 painter
325 .cylinder(Aabb {
326 min: (center - (length / 2) - 1).with_z(base - 3),
327 max: (center + (length / 2) + 1).with_z(base - 2),
328 })
329 .fill(color.clone());
330 painter
331 .cylinder(Aabb {
332 min: (center - (length / 2) + 1).with_z(base - 3),
333 max: (center + (length / 2) - 1).with_z(base - 2),
334 })
335 .fill(clay.clone());
336
337 for n in 1..=reed_parts as i32 {
339 let pos = Vec2::new(
340 center.x + ((radius as f32) * ((n as f32 * phi).cos())) as i32,
341 center.y + ((radius as f32) * ((n as f32 * phi).sin())) as i32,
342 );
343 painter
344 .line(
345 pos.with_z(base + (storeys * height) - (height / 2)),
346 center.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
347 1.0,
348 )
349 .fill(reed.clone());
350 }
351 }
352
353 let beams_low = place_circular_as_vec(center, (2 * (length / 3)) as f32, 10);
355 let beams_high = place_circular_as_vec(center, (2 * (length / 4)) as f32, 10);
356
357 for b in 0..beams_low.len() {
358 painter
359 .cylinder(Aabb {
360 min: (beams_low[b] - 4).with_z(base + height - 1),
361 max: (beams_low[b] + 4).with_z(base + height),
362 })
363 .fill(wood_dark.clone());
364
365 painter
366 .line(
367 beams_low[b].with_z(base + height),
368 beams_high[b].with_z(base + platform_height - 4),
369 1.5,
370 )
371 .fill(wood_dark.clone());
372 }
373 painter
375 .cylinder(Aabb {
376 min: (center - (length / 3)).with_z(base),
377 max: (center + (length / 3)).with_z(base + platform_height),
378 })
379 .clear();
380
381 let stairs = painter.cylinder(Aabb {
382 min: (center - (length / 3)).with_z(base - 3),
383 max: (center + (length / 3)).with_z(base + platform_height - 2),
384 });
385
386 stairs
387 .sample(spiral_staircase(
388 center.with_z(base - 3),
389 ((length / 3) + 1) as f32,
390 0.5,
391 (platform_height / 4) as f32,
392 ))
393 .fill(clay.clone());
394 }
395}