1use super::*;
2use crate::{
3 Land,
4 site::gen::{place_circular_as_vec, spiral_staircase},
5 util::{CARDINALS, DIAGONALS, RandomField, Sampler, within_distance},
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 = 21;
48 let platform_height = 40;
49 let top_floor = base + platform_height - 3;
50 let docking_positions = CARDINALS
51 .iter()
52 .map(|dir| (center + dir * 31).with_z(top_floor))
53 .collect::<Vec<_>>();
54 Self {
55 door_tile: door_tile_pos,
56 alt,
57 center,
58 length,
59 platform_height,
60 docking_positions,
61 }
62 }
63
64 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
65 SpawnRules {
66 trees: {
67 const AIRSHIP_MIN_TREE_DIST2: i32 = 89;
72 !within_distance(wpos, self.center, AIRSHIP_MIN_TREE_DIST2)
73 },
74 waypoints: false,
75 ..SpawnRules::default()
76 }
77 }
78}
79
80impl Structure for SavannahAirshipDock {
81 #[cfg(feature = "use-dyn-lib")]
82 const UPDATE_FN: &'static [u8] = b"render_savannah_airship_dock\0";
83
84 #[cfg_attr(
85 feature = "be-dyn-lib",
86 unsafe(export_name = "render_savannah_airship_dock")
87 )]
88 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
89 let base = self.alt + 1;
90 let center = self.center;
91 let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
92 let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
93 let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
94 let color = Fill::Sampling(Arc::new(|center| {
95 Some(match (RandomField::new(0).get(center)) % 7 {
96 0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
97 1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
98 2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
99 3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
100 4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
101 5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
102 _ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
103 })
104 }));
105 let length = self.length;
106 let height = length / 2;
107 let platform_height = self.platform_height;
108 let storeys = 1;
109 let radius = length + (length / 3);
110 let reed_var = (1 + RandomField::new(0).get(center.with_z(base)) % 4) as f32;
111 let reed_parts = 36_f32 + reed_var;
112 let phi = TAU / reed_parts;
113
114 painter
116 .cylinder(Aabb {
117 min: (center - length).with_z(base - 3),
118 max: (center + length + 1).with_z(base - 2),
119 })
120 .fill(clay.clone());
121 painter
122 .cylinder(Aabb {
123 min: (center - length - 1).with_z(base - 4),
124 max: (center + length + 2).with_z(base - 3),
125 })
126 .fill(clay.clone());
127 painter
128 .cylinder(Aabb {
129 min: (center - length - 2).with_z(base - 5),
130 max: (center + length + 3).with_z(base - 4),
131 })
132 .fill(clay.clone());
133 painter
134 .cylinder(Aabb {
135 min: (center - length - 3).with_z(base - height),
136 max: (center + length + 4).with_z(base - 5),
137 })
138 .fill(clay.clone());
139 painter
141 .cylinder(Aabb {
142 min: (center - (2 * (length / 3)) - 1).with_z(base + platform_height - 4),
143 max: (center + (2 * (length / 3)) + 1).with_z(base + platform_height - 3),
144 })
145 .fill(color.clone());
146 painter
147 .cylinder(Aabb {
148 min: (center - (2 * (length / 3))).with_z(base + platform_height - 4),
149 max: (center + (2 * (length / 3))).with_z(base + platform_height - 3),
150 })
151 .fill(clay.clone());
152 painter
153 .cylinder(Aabb {
154 min: (center - length - 2).with_z(base + platform_height - 3),
155 max: (center + length + 2).with_z(base + platform_height - 2),
156 })
157 .fill(color.clone());
158 painter
159 .cylinder(Aabb {
160 min: (center - length - 1).with_z(base + platform_height - 3),
161 max: (center + length + 1).with_z(base + platform_height - 2),
162 })
163 .fill(clay.clone());
164 for dir in CARDINALS {
166 let dock_pos = center + dir * 26;
167 painter
168 .cylinder(Aabb {
169 min: (dock_pos - 5).with_z(base + platform_height - 3),
170 max: (dock_pos + 5).with_z(base + platform_height - 2),
171 })
172 .fill(color.clone());
173 painter
174 .cylinder(Aabb {
175 min: (dock_pos - 4).with_z(base + platform_height - 3),
176 max: (dock_pos + 4).with_z(base + platform_height - 2),
177 })
178 .fill(wood_dark.clone());
179 }
180
181 for dir in CARDINALS {
183 let lantern_pos = center + (dir * length);
184
185 painter.sprite(
186 lantern_pos.with_z(base + platform_height - 2),
187 SpriteKind::Lantern,
188 );
189 }
190 for dir in DIAGONALS {
191 let cargo_pos = center + (dir * ((length / 2) - 1));
192 for dir in CARDINALS {
193 let sprite_pos = cargo_pos + dir;
194 let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
195 for r in 0..rows {
196 painter
197 .aabb(Aabb {
198 min: (sprite_pos).with_z(base + platform_height - 2 + r),
199 max: (sprite_pos + 1).with_z(base + platform_height - 1 + r),
200 })
201 .fill(Fill::Block(Block::air(
202 match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2) as i32
203 {
204 0 => SpriteKind::Barrel,
205 _ => SpriteKind::CrateBlock,
206 },
207 )));
208 if r > 0 {
209 painter.owned_resource_sprite(
210 sprite_pos.with_z(base + platform_height - 1 + r),
211 SpriteKind::Crate,
212 0,
213 );
214 }
215 }
216 }
217 }
218 let campfire_pos = (center - (2 * (length / 3)) - 1).with_z(base + platform_height);
220 painter.spawn(
221 EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
222 .into_special(SpecialEntity::Waypoint),
223 );
224 for b in 0..2 {
225 let base = base + (b * platform_height);
226 let radius = radius - (b * (radius / 3));
227 let length = length - (b * (length / 3));
228 painter
230 .cone(Aabb {
231 min: (center - radius).with_z(base + (storeys * height) - (height / 2) + 1),
232 max: (center + radius)
233 .with_z(base + (storeys * height) + (height / 2) - 1 + reed_var as i32),
234 })
235 .fill(reed.clone());
236 painter
237 .cone(Aabb {
238 min: (center - radius).with_z(base + (storeys * height) - (height / 2)),
239 max: (center + radius)
240 .with_z(base + (storeys * height) + (height / 2) - 2 + reed_var as i32),
241 })
242 .clear();
243
244 if b == 1 {
245 let agent_z_pos = base - 2 + ((storeys - 1) * (height + 2));
247 painter
248 .cylinder_with_radius(
249 center.with_z(agent_z_pos),
250 (length + 1 - (storeys - 1)) as f32,
251 6.0,
252 )
253 .intersect(painter.aabb(Aabb {
254 min: (Vec2::new(center.x - 3, center.y + 4)).with_z(agent_z_pos),
255 max: (Vec2::new(center.x - 30, center.y + 30)).with_z(agent_z_pos + 7),
256 }))
257 .fill(clay.clone());
258 painter
259 .cylinder_with_radius(
260 center.with_z(agent_z_pos),
261 (length + 1 - (storeys - 1)) as f32,
262 5.0,
263 )
264 .intersect(painter.aabb(Aabb {
265 min: (Vec2::new(center.x - 4, center.y + 5)).with_z(agent_z_pos),
266 max: (Vec2::new(center.x - 30, center.y + 30)).with_z(agent_z_pos + 6),
267 }))
268 .clear();
269 painter
270 .cylinder_with_radius(
271 Vec2::new(center.x - 4, center.y + 5).with_z(agent_z_pos),
272 (length - 4 - (storeys - 1)) as f32,
273 1.0,
274 )
275 .intersect(painter.aabb(Aabb {
276 min: (Vec2::new(center.x - 4, center.y + 5)).with_z(agent_z_pos),
277 max: (Vec2::new(center.x - 30, center.y + 30)).with_z(agent_z_pos + 2),
278 }))
279 .fill(color.clone());
280 painter
281 .cylinder_with_radius(
282 Vec2::new(center.x - 4, center.y + 5).with_z(agent_z_pos),
283 (length - 5 - (storeys - 1)) as f32,
284 1.0,
285 )
286 .intersect(painter.aabb(Aabb {
287 min: (Vec2::new(center.x - 4, center.y + 5)).with_z(agent_z_pos),
288 max: (Vec2::new(center.x - 30, center.y + 30)).with_z(agent_z_pos + 2),
289 }))
290 .clear();
291 }
292 for s in 0..storeys {
294 let room = painter.cylinder(Aabb {
295 min: (center - length + 2 + s).with_z(base - 2 + (s * height)),
296 max: (center + 1 + length - 2 - s).with_z(base + height + (s * height)),
297 });
298 room.fill(clay.clone());
299 for dir in DIAGONALS {
301 let decor_pos = center + dir * (length - 2 - s);
302 let decor = painter
303 .line(
304 center.with_z(base - 1 + (s * (height + 2))),
305 decor_pos.with_z(base - 1 + (s * (height + 2))),
306 5.0,
307 )
308 .intersect(room);
309 decor.fill(color.clone());
310 painter
311 .line(
312 center.with_z(base - 1 + (s * (height + 2))),
313 decor_pos.with_z(base - 1 + (s * (height + 2))),
314 4.0,
315 )
316 .intersect(decor)
317 .fill(clay.clone());
318 }
319 }
320
321 painter
323 .cylinder(Aabb {
324 min: (center - length + 4).with_z(base - 2),
325 max: (center + 1 + length - 4).with_z(base + (storeys * height)),
326 })
327 .clear();
328 painter
330 .cylinder(Aabb {
331 min: (center - length + 4).with_z(base - 1),
332 max: (center + 1 + length - 4).with_z(base),
333 })
334 .fill(wood_dark.clone());
335 painter
336 .cylinder(Aabb {
337 min: (center - length + 4).with_z(base + (storeys * height) - 1),
338 max: (center + 1 + length - 4).with_z(base + (storeys * height) + 1),
339 })
340 .fill(wood_dark.clone());
341
342 for s in 0..storeys {
343 for dir in CARDINALS {
345 let frame_pos = center + dir * (length - 2 - s);
346 let clear_pos = center + dir * (length + 2 - s);
347
348 painter
349 .line(
350 center.with_z(base - 1 + (s * (height + 2))),
351 frame_pos.with_z(base - 1 + (s * (height + 2))),
352 3.0,
353 )
354 .fill(color.clone());
355 painter
356 .line(
357 center.with_z(base - 1 + (s * (height + 2))),
358 clear_pos.with_z(base - 1 + (s * (height + 2))),
359 2.0,
360 )
361 .clear();
362 }
363 }
364 painter
366 .cylinder(Aabb {
367 min: (center - length + 5).with_z(base - 2),
368 max: (center + 1 + length - 5).with_z(base + (storeys * height) + 1),
369 })
370 .clear();
371 painter
373 .cylinder(Aabb {
374 min: (center - (length / 2) - 1).with_z(base - 3),
375 max: (center + (length / 2) + 1).with_z(base - 2),
376 })
377 .fill(color.clone());
378 painter
379 .cylinder(Aabb {
380 min: (center - (length / 2) + 1).with_z(base - 3),
381 max: (center + (length / 2) - 1).with_z(base - 2),
382 })
383 .fill(clay.clone());
384
385 for n in 1..=reed_parts as i32 {
387 let pos = Vec2::new(
388 center.x + ((radius as f32) * ((n as f32 * phi).cos())) as i32,
389 center.y + ((radius as f32) * ((n as f32 * phi).sin())) as i32,
390 );
391 painter
392 .line(
393 pos.with_z(base + (storeys * height) - (height / 2)),
394 center.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
395 1.0,
396 )
397 .fill(reed.clone());
398 }
399 }
400
401 let beams_low = place_circular_as_vec(center, (2 * (length / 3)) as f32, 10);
403 let beams_high = place_circular_as_vec(center, (2 * (length / 4)) as f32, 10);
404
405 for b in 0..beams_low.len() {
406 painter
407 .cylinder(Aabb {
408 min: (beams_low[b] - 4).with_z(base + height - 1),
409 max: (beams_low[b] + 4).with_z(base + height),
410 })
411 .fill(wood_dark.clone());
412
413 painter
414 .line(
415 beams_low[b].with_z(base + height),
416 beams_high[b].with_z(base + platform_height - 4),
417 1.5,
418 )
419 .fill(wood_dark.clone());
420 }
421 painter
423 .cylinder(Aabb {
424 min: (center - (length / 3)).with_z(base),
425 max: (center + (length / 3)).with_z(base + platform_height),
426 })
427 .clear();
428
429 let stairs = painter.cylinder(Aabb {
430 min: (center - (length / 3)).with_z(base - 3),
431 max: (center + (length / 3)).with_z(base + platform_height - 2),
432 });
433
434 stairs
435 .sample(spiral_staircase(
436 center.with_z(base - 3),
437 ((length / 3) + 1) as f32,
438 0.5,
439 (platform_height / 4) as f32,
440 ))
441 .fill(clay.clone());
442 }
443}