1use super::*;
2use crate::{
3 Land,
4 site2::util::gradient::WrapMode,
5 util::{DIAGONALS, LOCALITY, NEIGHBORS, RandomField, Sampler},
6};
7use common::{
8 generation::SpecialEntity,
9 terrain::{BlockKind, SpriteKind},
10};
11use rand::prelude::*;
12use std::{f32::consts::TAU, mem};
13use vek::*;
14
15pub struct CliffTownAirshipDock {
17 pub door_tile: Vec2<i32>,
19 pub(crate) alt: i32,
21 door_dir: Vec2<i32>,
22 surface_color: Rgb<f32>,
23 sub_surface_color: Rgb<f32>,
24 pub center: Vec2<i32>,
25 variant: i32,
26 storeys: i32,
27 platform_length: i32,
28 pub docking_positions: Vec<Vec3<i32>>,
29}
30
31impl CliffTownAirshipDock {
32 pub fn generate(
33 land: &Land,
34 index: IndexRef,
35 _rng: &mut impl Rng,
36 site: &Site,
37 door_tile: Vec2<i32>,
38 door_dir: Vec2<i32>,
39 tile_aabr: Aabr<i32>,
40 alt: Option<i32>,
41 ) -> Self {
42 let door_tile_pos = site.tile_center_wpos(door_tile);
43 let bounds = Aabr {
44 min: site.tile_wpos(tile_aabr.min),
45 max: site.tile_wpos(tile_aabr.max),
46 };
47 let center = bounds.center();
48 let alt = alt.unwrap_or_else(|| land.get_alt_approx(door_tile_pos) as i32);
49 let variant = 15;
50 let storeys = 5 + (variant / 2);
51 let platform_length = 2 * variant;
52 let mut docking_positions = vec![];
53 let mut platform_level = alt - 40;
54 let mut platform_height = 18 + variant / 2;
55 for s in 0..storeys {
56 if s == (storeys - 1) {
57 for dir in CARDINALS {
58 let docking_pos = center + dir * (platform_length + 7);
59 docking_positions.push(docking_pos.with_z(platform_level + 1));
60 }
61 }
62 platform_height += -1;
63 platform_level += platform_height;
64 }
65
66 let (surface_color, sub_surface_color) =
67 if let Some(sample) = land.column_sample(bounds.center(), index) {
68 (sample.surface_color, sample.sub_surface_color)
69 } else {
70 (Rgb::new(161.0, 116.0, 86.0), Rgb::new(88.0, 64.0, 64.0))
71 };
72 Self {
73 door_tile: door_tile_pos,
74 alt,
75 door_dir,
76 surface_color,
77 sub_surface_color,
78 center,
79 variant,
80 storeys,
81 platform_length,
82 docking_positions,
83 }
84 }
85
86 pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
87 SpawnRules {
88 trees: {
89 const AIRSHIP_MIN_TREE_DIST2: i32 = 53i32.pow(2);
94 wpos.distance_squared(self.center) > AIRSHIP_MIN_TREE_DIST2
95 },
96 waypoints: false,
97 ..SpawnRules::default()
98 }
99 }
100}
101
102impl Structure for CliffTownAirshipDock {
103 #[cfg(feature = "use-dyn-lib")]
104 const UPDATE_FN: &'static [u8] = b"render_cliff_town_airship_dock\0";
105
106 #[cfg_attr(
107 feature = "be-dyn-lib",
108 unsafe(export_name = "render_cliff_town_airship_dock")
109 )]
110 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
111 let base = self.alt;
112 let plot_center = self.center;
113 let door_dir = self.door_dir;
114
115 let surface_color = self.surface_color.map(|e| (e * 255.0) as u8);
116 let sub_surface_color = self.sub_surface_color.map(|e| (e * 255.0) as u8);
117 let gradient_center = Vec3::new(
118 plot_center.x as f32,
119 plot_center.y as f32,
120 (base + 1) as f32,
121 );
122 let gradient_var_1 = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 8;
123 let gradient_var_2 = RandomField::new(0).get(plot_center.with_z(base + 1)) as i32 % 10;
124
125 let brick = Fill::Gradient(
126 util::gradient::Gradient::new(
127 gradient_center,
128 8.0 + gradient_var_1 as f32,
129 util::gradient::Shape::Point,
130 (surface_color, sub_surface_color),
131 )
132 .with_repeat(if gradient_var_2 > 5 {
133 WrapMode::Repeat
134 } else {
135 WrapMode::PingPong
136 }),
137 BlockKind::Rock,
138 );
139
140 let wood = Fill::Brick(BlockKind::Wood, Rgb::new(106, 83, 51), 12);
141 let color = Fill::Block(Block::air(SpriteKind::CliffDecorBlock));
142 let window = Fill::Block(Block::air(SpriteKind::WindowArabic));
143 let window2 = Fill::Block(Block::air(SpriteKind::WindowArabic).with_ori(2).unwrap());
144 let rope = Fill::Block(Block::air(SpriteKind::Rope));
145
146 let tube_var = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 6;
147 let radius = 10.0 + tube_var as f32;
148 let tubes = 3.0 + tube_var as f32;
149 let phi = TAU / tubes;
150 for n in 1..=tubes as i32 {
151 let center = Vec2::new(
152 plot_center.x + (radius * ((n as f32 * phi).cos())) as i32,
153 plot_center.y + (radius * ((n as f32 * phi).sin())) as i32,
154 );
155 let sq_type = 3.5;
157 let storeys = self.storeys;
158 let variant = self.variant;
159 let mut length = 16 + (variant / 2);
160 let mut width = 7 * length / 8;
161 let mut height = 18 + variant / 2;
162 let mut floor_level = self.alt - 40;
163 let platform_length = self.platform_length;
164 let mut ground_entries = 0;
165 for s in 0..storeys {
166 let x_offset = RandomField::new(0).get((center - length).with_z(base)) as i32 % 10;
167 let y_offset = RandomField::new(0).get((center + length).with_z(base)) as i32 % 10;
168 let super_center =
169 Vec2::new(center.x - 3 + x_offset / 2, center.y - 3 + y_offset / 2);
170 painter
172 .cubic_bezier(
173 super_center.with_z(floor_level + (height / 2)),
174 (super_center - x_offset).with_z(floor_level + height),
175 (super_center - y_offset).with_z(floor_level + (height) + (height / 2)),
176 super_center.with_z(floor_level + (2 * height)),
177 (length - 1) as f32,
178 )
179 .fill(brick.clone());
180 if s == (storeys - 1) {
181 for dir in LOCALITY {
182 let cone_pos = super_center + (dir * 2);
183 let cone_var =
184 4 + RandomField::new(0).get(cone_pos.with_z(base)) as i32 % 4;
185 painter
186 .cone_with_radius(
187 cone_pos.with_z(floor_level + (2 * height) + 5),
188 (length / 2) as f32,
189 (length + cone_var) as f32,
190 )
191 .fill(brick.clone());
192 }
193 }
194 if n == tubes as i32 {
196 if ground_entries < 1 && floor_level > (base - 6) {
198 for dir in CARDINALS {
199 let entry_pos_inner = plot_center + (dir * (2 * length) - 4);
200 let entry_pos_outer = plot_center + (dir * (3 * length) + 4);
201 painter
202 .line(
203 entry_pos_inner.with_z(floor_level + 6),
204 entry_pos_outer.with_z(base + 35),
205 6.0,
206 )
207 .clear();
208 }
209 let door_start = plot_center + door_dir * ((3 * (length / 2)) + 1);
210 painter
211 .line(
212 door_start.with_z(floor_level + 2),
213 self.door_tile.with_z(base),
214 4.0,
215 )
216 .fill(wood.clone());
217 painter
218 .line(
219 door_start.with_z(floor_level + 7),
220 self.door_tile.with_z(base + 6),
221 7.0,
222 )
223 .clear();
224 ground_entries += 1;
225 }
226 painter
227 .cubic_bezier(
228 plot_center.with_z(floor_level + (height / 2)),
229 (plot_center - x_offset).with_z(floor_level + height),
230 (plot_center - y_offset).with_z(floor_level + (height) + (height / 2)),
231 plot_center.with_z(floor_level + (2 * height)),
232 (length + 2) as f32,
233 )
234 .fill(brick.clone());
235 if s == (storeys - 1) {
237 let limit_up = painter.aabb(Aabb {
238 min: (plot_center - platform_length - 2).with_z(floor_level - 4),
239 max: (plot_center + platform_length + 2).with_z(floor_level + 1),
240 });
241 painter
242 .superquadric(
243 Aabb {
244 min: (plot_center - platform_length - 2)
245 .with_z(floor_level - 4),
246 max: (plot_center + platform_length + 2)
247 .with_z(floor_level + 6),
248 },
249 4.0,
250 )
251 .intersect(limit_up)
252 .fill(wood.clone());
253 for dir in NEIGHBORS {
255 let lantern_pos = plot_center + (dir * (platform_length - 6));
256
257 painter.sprite(
258 lantern_pos.with_z(floor_level + 1),
259 SpriteKind::StreetLamp,
260 );
261 }
262 for dir in DIAGONALS {
263 let cargo_pos = plot_center + (dir * (2 * length));
264
265 for dir in CARDINALS {
266 let sprite_pos = cargo_pos + dir;
267 let rows =
268 (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
269 for r in 0..rows {
270 painter
271 .aabb(Aabb {
272 min: (sprite_pos).with_z(floor_level + 1 + r),
273 max: (sprite_pos + 1).with_z(floor_level + 2 + r),
274 })
275 .fill(Fill::Block(Block::air(
276 match (RandomField::new(0)
277 .get(sprite_pos.with_z(base + r))
278 % 2)
279 as i32
280 {
281 0 => SpriteKind::Barrel,
282 _ => SpriteKind::CrateBlock,
283 },
284 )));
285 if r > 0 {
286 painter.owned_resource_sprite(
287 sprite_pos.with_z(floor_level + 2 + r),
288 SpriteKind::Crate,
289 0,
290 );
291 }
292 }
293 }
294 }
295 for dir in CARDINALS {
296 let dock_pos = plot_center + (dir * platform_length);
298
299 painter
300 .cylinder(Aabb {
301 min: (dock_pos - 8).with_z(floor_level),
302 max: (dock_pos + 8).with_z(floor_level + 1),
303 })
304 .fill(wood.clone());
305 painter
306 .cylinder(Aabb {
307 min: (dock_pos - 7).with_z(floor_level - 1),
308 max: (dock_pos + 7).with_z(floor_level),
309 })
310 .fill(wood.clone());
311 }
312 let campfire_pos =
314 Vec2::new(plot_center.x - platform_length - 2, plot_center.y)
315 .with_z(floor_level);
316 painter.spawn(
317 EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
318 .into_special(SpecialEntity::Waypoint),
319 );
320 }
321
322 if floor_level > (base - 6) {
324 painter
326 .line(
327 Vec2::new(plot_center.x, plot_center.y - length)
328 .with_z(floor_level + 5),
329 Vec2::new(plot_center.x, plot_center.y + length)
330 .with_z(floor_level + 5),
331 4.0,
332 )
333 .fill(color.clone());
334 painter
335 .line(
336 Vec2::new(plot_center.x - length, plot_center.y)
337 .with_z(floor_level + 5),
338 Vec2::new(plot_center.x + length, plot_center.y)
339 .with_z(floor_level + 5),
340 4.0,
341 )
342 .fill(color.clone());
343 painter
345 .line(
346 Vec2::new(plot_center.x, plot_center.y - (2 * length) - 4)
347 .with_z(floor_level + 4),
348 Vec2::new(plot_center.x, plot_center.y + (2 * length) + 4)
349 .with_z(floor_level + 4),
350 4.0,
351 )
352 .clear();
353 painter
354 .line(
355 Vec2::new(plot_center.x - (2 * length) - 4, plot_center.y)
356 .with_z(floor_level + 4),
357 Vec2::new(plot_center.x + (2 * length) + 4, plot_center.y)
358 .with_z(floor_level + 4),
359 4.0,
360 )
361 .clear();
362 painter
363 .superquadric(
364 Aabb {
365 min: (plot_center - length - 1).with_z(floor_level),
366 max: (plot_center + length + 1)
367 .with_z(floor_level + height - 4),
368 },
369 sq_type,
370 )
371 .clear();
372 painter
374 .cylinder(Aabb {
375 min: (plot_center - length - 3).with_z(floor_level),
376 max: (plot_center + length + 3).with_z(floor_level + 1),
377 })
378 .fill(brick.clone());
379 painter
380 .cylinder(Aabb {
381 min: (plot_center - length + 1).with_z(floor_level),
382 max: (plot_center + length - 1).with_z(floor_level + 1),
383 })
384 .fill(color.clone());
385 painter
386 .cylinder(Aabb {
387 min: (plot_center - length + 2).with_z(floor_level),
388 max: (plot_center + length - 2).with_z(floor_level + 1),
389 })
390 .fill(brick.clone());
391 painter
393 .aabb(Aabb {
394 min: Vec2::new(plot_center.x - 3, plot_center.y + length)
395 .with_z(floor_level + 2),
396 max: Vec2::new(plot_center.x + 4, plot_center.y + length + 1)
397 .with_z(floor_level + 7),
398 })
399 .fill(window2.clone());
400 painter
401 .aabb(Aabb {
402 min: Vec2::new(plot_center.x - 2, plot_center.y + length)
403 .with_z(floor_level + 2),
404 max: Vec2::new(plot_center.x + 3, plot_center.y + length + 1)
405 .with_z(floor_level + 7),
406 })
407 .clear();
408
409 painter
410 .aabb(Aabb {
411 min: Vec2::new(plot_center.x - 3, plot_center.y - length - 1)
412 .with_z(floor_level + 2),
413 max: Vec2::new(plot_center.x + 4, plot_center.y - length)
414 .with_z(floor_level + 7),
415 })
416 .fill(window2.clone());
417 painter
418 .aabb(Aabb {
419 min: Vec2::new(plot_center.x - 2, plot_center.y - length - 1)
420 .with_z(floor_level + 2),
421 max: Vec2::new(plot_center.x + 3, plot_center.y - length)
422 .with_z(floor_level + 7),
423 })
424 .clear();
425 painter
426 .aabb(Aabb {
427 min: Vec2::new(plot_center.x + length, plot_center.y - 3)
428 .with_z(floor_level + 2),
429 max: Vec2::new(plot_center.x + length + 1, plot_center.y + 4)
430 .with_z(floor_level + 7),
431 })
432 .fill(window.clone());
433 painter
434 .aabb(Aabb {
435 min: Vec2::new(plot_center.x + length, plot_center.y - 2)
436 .with_z(floor_level + 2),
437 max: Vec2::new(plot_center.x + length + 1, plot_center.y + 3)
438 .with_z(floor_level + 7),
439 })
440 .clear();
441
442 painter
443 .aabb(Aabb {
444 min: Vec2::new(plot_center.x - length - 1, plot_center.y - 3)
445 .with_z(floor_level + 2),
446 max: Vec2::new(plot_center.x - length, plot_center.y + 4)
447 .with_z(floor_level + 7),
448 })
449 .fill(window.clone());
450 painter
451 .aabb(Aabb {
452 min: Vec2::new(plot_center.x - length - 1, plot_center.y - 2)
453 .with_z(floor_level + 2),
454 max: Vec2::new(plot_center.x - length, plot_center.y + 3)
455 .with_z(floor_level + 7),
456 })
457 .clear();
458 for dir in DIAGONALS {
460 let cargo_pos = plot_center + (dir * (length / 2));
461 for dir in CARDINALS {
462 let sprite_pos = cargo_pos + dir;
463 let rows =
464 (RandomField::new(0).get(sprite_pos.with_z(base)) % 4) as i32;
465 for r in 0..rows {
466 painter
467 .aabb(Aabb {
468 min: (sprite_pos).with_z(floor_level + 1 + r),
469 max: (sprite_pos + 1).with_z(floor_level + 2 + r),
470 })
471 .fill(Fill::Block(Block::air(
472 match (RandomField::new(0)
473 .get(sprite_pos.with_z(base + r))
474 % 2)
475 as i32
476 {
477 0 => SpriteKind::Barrel,
478 _ => SpriteKind::CrateBlock,
479 },
480 )));
481 }
482 }
483 }
484
485 let corner_pos_1 = Vec2::new(plot_center.x - length, plot_center.y - 5);
487 let corner_pos_2 = Vec2::new(plot_center.x - 5, plot_center.y - length);
488 for dir in SQUARE_4 {
489 let lamp_pos_1 = Vec2::new(
490 corner_pos_1.x + (dir.x * ((2 * length) - 1)),
491 corner_pos_1.y + (dir.y * 10),
492 )
493 .with_z(floor_level + 7);
494 painter.rotated_sprite(
495 lamp_pos_1,
496 SpriteKind::WallLampMesa,
497 (2 + (4 * dir.x)) as u8,
498 );
499 let lamp_pos_2 = Vec2::new(
500 corner_pos_2.x + (dir.x * 10),
501 corner_pos_2.y + (dir.y * ((2 * length) - 1)),
502 )
503 .with_z(floor_level + 7);
504 painter.rotated_sprite(
505 lamp_pos_2,
506 SpriteKind::WallLampMesa,
507 (4 - (4 * dir.y)) as u8,
508 );
509 }
510 }
511 if floor_level > (base + 8) {
513 let stairs_level = floor_level + 1;
514 let stairs_start = plot_center + door_dir * ((2 * length) - 7);
515 let mid_dir = if door_dir.x != 0 {
516 door_dir.x
517 } else {
518 door_dir.y
519 };
520 let stairs_mid = Vec2::new(
521 plot_center.x + mid_dir * (3 * (length / 2)),
522 plot_center.y + mid_dir * (3 * (length / 2)),
523 );
524 let stairs_end = Vec2::new(
525 plot_center.x + door_dir.y * ((2 * length) - 7),
526 plot_center.y + door_dir.x * ((2 * length) - 7),
527 );
528 let rope_pos = Vec2::new(
529 plot_center.x + mid_dir * ((3 * (length / 2)) + 2),
530 plot_center.y + mid_dir * ((3 * (length / 2)) + 2),
531 );
532
533 painter
534 .cylinder(Aabb {
535 min: (stairs_start - 6).with_z(stairs_level - 1),
536 max: (stairs_start + 6).with_z(stairs_level),
537 })
538 .fill(wood.clone());
539
540 painter
541 .cylinder(Aabb {
542 min: (stairs_mid - 6).with_z(stairs_level - (height / 2) - 1),
543 max: (stairs_mid + 6).with_z(stairs_level - (height / 2)),
544 })
545 .fill(wood.clone());
546
547 painter
548 .cylinder(Aabb {
549 min: (stairs_end - 6).with_z(stairs_level - height - 1),
550 max: (stairs_end + 6).with_z(stairs_level - height),
551 })
552 .fill(wood.clone());
553
554 for n in 0..2 {
555 let stairs = painter
556 .line(
557 stairs_start.with_z(stairs_level + (n * 2)),
558 stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
559 4.0 + (n as f32 / 2.0),
560 )
561 .union(painter.line(
562 stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
563 stairs_end.with_z(stairs_level - height + (n * 2)),
564 4.0 + (n as f32 / 2.0),
565 ));
566 match n {
567 0 => stairs.fill(wood.clone()),
568 _ => stairs.clear(),
569 };
570 }
571 painter
572 .line(
573 rope_pos.with_z(stairs_level + (height / 2) - 3),
574 (plot_center - (length / 2))
575 .with_z(stairs_level + (height / 2) + 2),
576 1.5,
577 )
578 .fill(wood.clone());
579
580 painter
581 .aabb(Aabb {
582 min: rope_pos.with_z(stairs_level - (height / 2) - 1),
583 max: (rope_pos + 1).with_z(stairs_level + (height / 2) - 3),
584 })
585 .fill(rope.clone());
586 }
587 }
588 length += -1;
590 width += -1;
591 height += -1;
592 floor_level += height;
593 mem::swap(&mut length, &mut width);
594 }
595 }
596 }
597}