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(feature = "be-dyn-lib", export_name = "render_cliff_town_airship_dock")]
107 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
108 let base = self.alt;
109 let plot_center = self.center;
110 let door_dir = self.door_dir;
111
112 let surface_color = self.surface_color.map(|e| (e * 255.0) as u8);
113 let sub_surface_color = self.sub_surface_color.map(|e| (e * 255.0) as u8);
114 let gradient_center = Vec3::new(
115 plot_center.x as f32,
116 plot_center.y as f32,
117 (base + 1) as f32,
118 );
119 let gradient_var_1 = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 8;
120 let gradient_var_2 = RandomField::new(0).get(plot_center.with_z(base + 1)) as i32 % 10;
121
122 let brick = Fill::Gradient(
123 util::gradient::Gradient::new(
124 gradient_center,
125 8.0 + gradient_var_1 as f32,
126 util::gradient::Shape::Point,
127 (surface_color, sub_surface_color),
128 )
129 .with_repeat(if gradient_var_2 > 5 {
130 WrapMode::Repeat
131 } else {
132 WrapMode::PingPong
133 }),
134 BlockKind::Rock,
135 );
136
137 let wood = Fill::Brick(BlockKind::Wood, Rgb::new(106, 83, 51), 12);
138 let color = Fill::Block(Block::air(SpriteKind::CliffDecorBlock));
139 let window = Fill::Block(Block::air(SpriteKind::WindowArabic));
140 let window2 = Fill::Block(Block::air(SpriteKind::WindowArabic).with_ori(2).unwrap());
141 let rope = Fill::Block(Block::air(SpriteKind::Rope));
142
143 let tube_var = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 6;
144 let radius = 10.0 + tube_var as f32;
145 let tubes = 3.0 + tube_var as f32;
146 let phi = TAU / tubes;
147 for n in 1..=tubes as i32 {
148 let center = Vec2::new(
149 plot_center.x + (radius * ((n as f32 * phi).cos())) as i32,
150 plot_center.y + (radius * ((n as f32 * phi).sin())) as i32,
151 );
152 let sq_type = 3.5;
154 let storeys = self.storeys;
155 let variant = self.variant;
156 let mut length = 16 + (variant / 2);
157 let mut width = 7 * length / 8;
158 let mut height = 18 + variant / 2;
159 let mut floor_level = self.alt - 40;
160 let platform_length = self.platform_length;
161 let mut ground_entries = 0;
162 for s in 0..storeys {
163 let x_offset = RandomField::new(0).get((center - length).with_z(base)) as i32 % 10;
164 let y_offset = RandomField::new(0).get((center + length).with_z(base)) as i32 % 10;
165 let super_center =
166 Vec2::new(center.x - 3 + x_offset / 2, center.y - 3 + y_offset / 2);
167 painter
169 .cubic_bezier(
170 super_center.with_z(floor_level + (height / 2)),
171 (super_center - x_offset).with_z(floor_level + height),
172 (super_center - y_offset).with_z(floor_level + (height) + (height / 2)),
173 super_center.with_z(floor_level + (2 * height)),
174 (length - 1) as f32,
175 )
176 .fill(brick.clone());
177 if s == (storeys - 1) {
178 for dir in LOCALITY {
179 let cone_pos = super_center + (dir * 2);
180 let cone_var =
181 4 + RandomField::new(0).get(cone_pos.with_z(base)) as i32 % 4;
182 painter
183 .cone_with_radius(
184 cone_pos.with_z(floor_level + (2 * height) + 5),
185 (length / 2) as f32,
186 (length + cone_var) as f32,
187 )
188 .fill(brick.clone());
189 }
190 }
191 if n == tubes as i32 {
193 if ground_entries < 1 && floor_level > (base - 6) {
195 for dir in CARDINALS {
196 let entry_pos_inner = plot_center + (dir * (2 * length) - 4);
197 let entry_pos_outer = plot_center + (dir * (3 * length) + 4);
198 painter
199 .line(
200 entry_pos_inner.with_z(floor_level + 6),
201 entry_pos_outer.with_z(base + 35),
202 6.0,
203 )
204 .clear();
205 }
206 let door_start = plot_center + door_dir * ((3 * (length / 2)) + 1);
207 painter
208 .line(
209 door_start.with_z(floor_level + 2),
210 self.door_tile.with_z(base),
211 4.0,
212 )
213 .fill(wood.clone());
214 painter
215 .line(
216 door_start.with_z(floor_level + 7),
217 self.door_tile.with_z(base + 6),
218 7.0,
219 )
220 .clear();
221 ground_entries += 1;
222 }
223 painter
224 .cubic_bezier(
225 plot_center.with_z(floor_level + (height / 2)),
226 (plot_center - x_offset).with_z(floor_level + height),
227 (plot_center - y_offset).with_z(floor_level + (height) + (height / 2)),
228 plot_center.with_z(floor_level + (2 * height)),
229 (length + 2) as f32,
230 )
231 .fill(brick.clone());
232 if s == (storeys - 1) {
234 let limit_up = painter.aabb(Aabb {
235 min: (plot_center - platform_length - 2).with_z(floor_level - 4),
236 max: (plot_center + platform_length + 2).with_z(floor_level + 1),
237 });
238 painter
239 .superquadric(
240 Aabb {
241 min: (plot_center - platform_length - 2)
242 .with_z(floor_level - 4),
243 max: (plot_center + platform_length + 2)
244 .with_z(floor_level + 6),
245 },
246 4.0,
247 )
248 .intersect(limit_up)
249 .fill(wood.clone());
250 for dir in NEIGHBORS {
252 let lantern_pos = plot_center + (dir * (platform_length - 6));
253
254 painter.sprite(
255 lantern_pos.with_z(floor_level + 1),
256 SpriteKind::StreetLamp,
257 );
258 }
259 for dir in DIAGONALS {
260 let cargo_pos = plot_center + (dir * (2 * length));
261
262 for dir in CARDINALS {
263 let sprite_pos = cargo_pos + dir;
264 let rows =
265 (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
266 for r in 0..rows {
267 painter
268 .aabb(Aabb {
269 min: (sprite_pos).with_z(floor_level + 1 + r),
270 max: (sprite_pos + 1).with_z(floor_level + 2 + r),
271 })
272 .fill(Fill::Block(Block::air(
273 match (RandomField::new(0)
274 .get(sprite_pos.with_z(base + r))
275 % 2)
276 as i32
277 {
278 0 => SpriteKind::Barrel,
279 _ => SpriteKind::CrateBlock,
280 },
281 )));
282 if r > 0 {
283 painter.owned_resource_sprite(
284 sprite_pos.with_z(floor_level + 2 + r),
285 SpriteKind::Crate,
286 0,
287 );
288 }
289 }
290 }
291 }
292 for dir in CARDINALS {
293 let dock_pos = plot_center + (dir * platform_length);
295
296 painter
297 .cylinder(Aabb {
298 min: (dock_pos - 8).with_z(floor_level),
299 max: (dock_pos + 8).with_z(floor_level + 1),
300 })
301 .fill(wood.clone());
302 painter
303 .cylinder(Aabb {
304 min: (dock_pos - 7).with_z(floor_level - 1),
305 max: (dock_pos + 7).with_z(floor_level),
306 })
307 .fill(wood.clone());
308 }
309 let campfire_pos =
311 Vec2::new(plot_center.x - platform_length - 2, plot_center.y)
312 .with_z(floor_level);
313 painter.spawn(
314 EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
315 .into_special(SpecialEntity::Waypoint),
316 );
317 }
318
319 if floor_level > (base - 6) {
321 painter
323 .line(
324 Vec2::new(plot_center.x, plot_center.y - length)
325 .with_z(floor_level + 5),
326 Vec2::new(plot_center.x, plot_center.y + length)
327 .with_z(floor_level + 5),
328 4.0,
329 )
330 .fill(color.clone());
331 painter
332 .line(
333 Vec2::new(plot_center.x - length, plot_center.y)
334 .with_z(floor_level + 5),
335 Vec2::new(plot_center.x + length, plot_center.y)
336 .with_z(floor_level + 5),
337 4.0,
338 )
339 .fill(color.clone());
340 painter
342 .line(
343 Vec2::new(plot_center.x, plot_center.y - (2 * length) - 4)
344 .with_z(floor_level + 4),
345 Vec2::new(plot_center.x, plot_center.y + (2 * length) + 4)
346 .with_z(floor_level + 4),
347 4.0,
348 )
349 .clear();
350 painter
351 .line(
352 Vec2::new(plot_center.x - (2 * length) - 4, plot_center.y)
353 .with_z(floor_level + 4),
354 Vec2::new(plot_center.x + (2 * length) + 4, plot_center.y)
355 .with_z(floor_level + 4),
356 4.0,
357 )
358 .clear();
359 painter
360 .superquadric(
361 Aabb {
362 min: (plot_center - length - 1).with_z(floor_level),
363 max: (plot_center + length + 1)
364 .with_z(floor_level + height - 4),
365 },
366 sq_type,
367 )
368 .clear();
369 painter
371 .cylinder(Aabb {
372 min: (plot_center - length - 3).with_z(floor_level),
373 max: (plot_center + length + 3).with_z(floor_level + 1),
374 })
375 .fill(brick.clone());
376 painter
377 .cylinder(Aabb {
378 min: (plot_center - length + 1).with_z(floor_level),
379 max: (plot_center + length - 1).with_z(floor_level + 1),
380 })
381 .fill(color.clone());
382 painter
383 .cylinder(Aabb {
384 min: (plot_center - length + 2).with_z(floor_level),
385 max: (plot_center + length - 2).with_z(floor_level + 1),
386 })
387 .fill(brick.clone());
388 painter
390 .aabb(Aabb {
391 min: Vec2::new(plot_center.x - 3, plot_center.y + length)
392 .with_z(floor_level + 2),
393 max: Vec2::new(plot_center.x + 4, plot_center.y + length + 1)
394 .with_z(floor_level + 7),
395 })
396 .fill(window2.clone());
397 painter
398 .aabb(Aabb {
399 min: Vec2::new(plot_center.x - 2, plot_center.y + length)
400 .with_z(floor_level + 2),
401 max: Vec2::new(plot_center.x + 3, plot_center.y + length + 1)
402 .with_z(floor_level + 7),
403 })
404 .clear();
405
406 painter
407 .aabb(Aabb {
408 min: Vec2::new(plot_center.x - 3, plot_center.y - length - 1)
409 .with_z(floor_level + 2),
410 max: Vec2::new(plot_center.x + 4, plot_center.y - length)
411 .with_z(floor_level + 7),
412 })
413 .fill(window2.clone());
414 painter
415 .aabb(Aabb {
416 min: Vec2::new(plot_center.x - 2, plot_center.y - length - 1)
417 .with_z(floor_level + 2),
418 max: Vec2::new(plot_center.x + 3, plot_center.y - length)
419 .with_z(floor_level + 7),
420 })
421 .clear();
422 painter
423 .aabb(Aabb {
424 min: Vec2::new(plot_center.x + length, plot_center.y - 3)
425 .with_z(floor_level + 2),
426 max: Vec2::new(plot_center.x + length + 1, plot_center.y + 4)
427 .with_z(floor_level + 7),
428 })
429 .fill(window.clone());
430 painter
431 .aabb(Aabb {
432 min: Vec2::new(plot_center.x + length, plot_center.y - 2)
433 .with_z(floor_level + 2),
434 max: Vec2::new(plot_center.x + length + 1, plot_center.y + 3)
435 .with_z(floor_level + 7),
436 })
437 .clear();
438
439 painter
440 .aabb(Aabb {
441 min: Vec2::new(plot_center.x - length - 1, plot_center.y - 3)
442 .with_z(floor_level + 2),
443 max: Vec2::new(plot_center.x - length, plot_center.y + 4)
444 .with_z(floor_level + 7),
445 })
446 .fill(window.clone());
447 painter
448 .aabb(Aabb {
449 min: Vec2::new(plot_center.x - length - 1, plot_center.y - 2)
450 .with_z(floor_level + 2),
451 max: Vec2::new(plot_center.x - length, plot_center.y + 3)
452 .with_z(floor_level + 7),
453 })
454 .clear();
455 for dir in DIAGONALS {
457 let cargo_pos = plot_center + (dir * (length / 2));
458 for dir in CARDINALS {
459 let sprite_pos = cargo_pos + dir;
460 let rows =
461 (RandomField::new(0).get(sprite_pos.with_z(base)) % 4) as i32;
462 for r in 0..rows {
463 painter
464 .aabb(Aabb {
465 min: (sprite_pos).with_z(floor_level + 1 + r),
466 max: (sprite_pos + 1).with_z(floor_level + 2 + r),
467 })
468 .fill(Fill::Block(Block::air(
469 match (RandomField::new(0)
470 .get(sprite_pos.with_z(base + r))
471 % 2)
472 as i32
473 {
474 0 => SpriteKind::Barrel,
475 _ => SpriteKind::CrateBlock,
476 },
477 )));
478 }
479 }
480 }
481
482 let corner_pos_1 = Vec2::new(plot_center.x - length, plot_center.y - 5);
484 let corner_pos_2 = Vec2::new(plot_center.x - 5, plot_center.y - length);
485 for dir in SQUARE_4 {
486 let lamp_pos_1 = Vec2::new(
487 corner_pos_1.x + (dir.x * ((2 * length) - 1)),
488 corner_pos_1.y + (dir.y * 10),
489 )
490 .with_z(floor_level + 7);
491 painter.rotated_sprite(
492 lamp_pos_1,
493 SpriteKind::WallLampMesa,
494 (2 + (4 * dir.x)) as u8,
495 );
496 let lamp_pos_2 = Vec2::new(
497 corner_pos_2.x + (dir.x * 10),
498 corner_pos_2.y + (dir.y * ((2 * length) - 1)),
499 )
500 .with_z(floor_level + 7);
501 painter.rotated_sprite(
502 lamp_pos_2,
503 SpriteKind::WallLampMesa,
504 (4 - (4 * dir.y)) as u8,
505 );
506 }
507 }
508 if floor_level > (base + 8) {
510 let stairs_level = floor_level + 1;
511 let stairs_start = plot_center + door_dir * ((2 * length) - 7);
512 let mid_dir = if door_dir.x != 0 {
513 door_dir.x
514 } else {
515 door_dir.y
516 };
517 let stairs_mid = Vec2::new(
518 plot_center.x + mid_dir * (3 * (length / 2)),
519 plot_center.y + mid_dir * (3 * (length / 2)),
520 );
521 let stairs_end = Vec2::new(
522 plot_center.x + door_dir.y * ((2 * length) - 7),
523 plot_center.y + door_dir.x * ((2 * length) - 7),
524 );
525 let rope_pos = Vec2::new(
526 plot_center.x + mid_dir * ((3 * (length / 2)) + 2),
527 plot_center.y + mid_dir * ((3 * (length / 2)) + 2),
528 );
529
530 painter
531 .cylinder(Aabb {
532 min: (stairs_start - 6).with_z(stairs_level - 1),
533 max: (stairs_start + 6).with_z(stairs_level),
534 })
535 .fill(wood.clone());
536
537 painter
538 .cylinder(Aabb {
539 min: (stairs_mid - 6).with_z(stairs_level - (height / 2) - 1),
540 max: (stairs_mid + 6).with_z(stairs_level - (height / 2)),
541 })
542 .fill(wood.clone());
543
544 painter
545 .cylinder(Aabb {
546 min: (stairs_end - 6).with_z(stairs_level - height - 1),
547 max: (stairs_end + 6).with_z(stairs_level - height),
548 })
549 .fill(wood.clone());
550
551 for n in 0..2 {
552 let stairs = painter
553 .line(
554 stairs_start.with_z(stairs_level + (n * 2)),
555 stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
556 4.0 + (n as f32 / 2.0),
557 )
558 .union(painter.line(
559 stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
560 stairs_end.with_z(stairs_level - height + (n * 2)),
561 4.0 + (n as f32 / 2.0),
562 ));
563 match n {
564 0 => stairs.fill(wood.clone()),
565 _ => stairs.clear(),
566 };
567 }
568 painter
569 .line(
570 rope_pos.with_z(stairs_level + (height / 2) - 3),
571 (plot_center - (length / 2))
572 .with_z(stairs_level + (height / 2) + 2),
573 1.5,
574 )
575 .fill(wood.clone());
576
577 painter
578 .aabb(Aabb {
579 min: rope_pos.with_z(stairs_level - (height / 2) - 1),
580 max: (rope_pos + 1).with_z(stairs_level + (height / 2) - 3),
581 })
582 .fill(rope.clone());
583 }
584 }
585 length += -1;
587 width += -1;
588 height += -1;
589 floor_level += height;
590 mem::swap(&mut length, &mut width);
591 }
592 }
593 }
594}