veloren_world/site/plot/
cliff_town_airship_dock.rs

1use super::*;
2use crate::{
3    Land,
4    site::{gen::PrimitiveTransform, util::gradient::WrapMode},
5    util::{DIAGONALS, LOCALITY, NEIGHBORS, RandomField, Sampler, within_distance},
6};
7use common::{
8    generation::SpecialEntity,
9    terrain::{BlockKind, SpriteKind},
10};
11use rand::prelude::*;
12use std::{f32::consts::TAU, mem};
13use vek::*;
14
15/// Represents house data generated by the `generate()` method
16pub struct CliffTownAirshipDock {
17    /// Tile position of the door tile
18    pub door_tile: Vec2<i32>,
19    /// Approximate altitude of the door tile
20    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 + 9);
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                // dock is 3 tiles = 18 blocks in radius
90                // airships are 20 blocks wide.
91                // Leave extra space for tree width (at lease 15 extra).
92                // Don't allow trees within 18 + 20 + 15 = 53 blocks of the dock center
93                const AIRSHIP_MIN_TREE_DIST2: i32 = 53;
94                !within_distance(wpos, 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)) % 8) as i32;
123        let gradient_var_2 = (RandomField::new(0).get(plot_center.with_z(base + 1)) % 10) as i32;
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)) % 6) as i32;
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            // common superquadric degree for rooms
156            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 =
167                    (RandomField::new(0).get((center - length).with_z(base)) % 10) as i32;
168                let y_offset =
169                    (RandomField::new(0).get((center + length).with_z(base)) % 10) as i32;
170                let super_center =
171                    Vec2::new(center.x - 3 + x_offset / 2, center.y - 3 + y_offset / 2);
172                // CliffTower Hoodoo Overlay
173                painter
174                    .cubic_bezier(
175                        super_center.with_z(floor_level + (height / 2)),
176                        (super_center - x_offset).with_z(floor_level + height),
177                        (super_center - y_offset).with_z(floor_level + (height) + (height / 2)),
178                        super_center.with_z(floor_level + (2 * height)),
179                        (length - 1) as f32,
180                    )
181                    .fill(brick.clone());
182                if s == (storeys - 1) {
183                    for dir in LOCALITY {
184                        let cone_pos = super_center + (dir * 2);
185                        let cone_var =
186                            4 + (RandomField::new(0).get(cone_pos.with_z(base)) % 4) as i32;
187                        painter
188                            .cone_with_radius(
189                                cone_pos.with_z(floor_level + (2 * height) + 5),
190                                (length / 2) as f32,
191                                (length + cone_var) as f32,
192                            )
193                            .fill(brick.clone());
194                    }
195                }
196                // center tube with  rooms
197                if n == tubes as i32 {
198                    // ground_entries
199                    if ground_entries < 1 && floor_level > (base - 6) {
200                        for dir in CARDINALS {
201                            let entry_pos_inner = plot_center + (dir * (2 * length) - 4);
202                            let entry_pos_outer = plot_center + (dir * (3 * length) + 4);
203                            painter
204                                .line(
205                                    entry_pos_inner.with_z(floor_level + 6),
206                                    entry_pos_outer.with_z(base + 35),
207                                    6.0,
208                                )
209                                .clear();
210                        }
211                        let door_start = plot_center + door_dir * ((3 * (length / 2)) + 1);
212                        painter
213                            .line(
214                                door_start.with_z(floor_level + 2),
215                                self.door_tile.with_z(base),
216                                4.0,
217                            )
218                            .fill(wood.clone());
219                        painter
220                            .line(
221                                door_start.with_z(floor_level + 7),
222                                self.door_tile.with_z(base + 6),
223                                7.0,
224                            )
225                            .clear();
226                        ground_entries += 1;
227                    }
228                    painter
229                        .cubic_bezier(
230                            plot_center.with_z(floor_level + (height / 2)),
231                            (plot_center - x_offset).with_z(floor_level + height),
232                            (plot_center - y_offset).with_z(floor_level + (height) + (height / 2)),
233                            plot_center.with_z(floor_level + (2 * height)),
234                            (length + 2) as f32,
235                        )
236                        .fill(brick.clone());
237                    // platform
238                    if s == (storeys - 1) {
239                        let limit_up = painter.aabb(Aabb {
240                            min: (plot_center - platform_length - 2).with_z(floor_level - 4),
241                            max: (plot_center + platform_length + 2).with_z(floor_level + 1),
242                        });
243                        painter
244                            .superquadric(
245                                Aabb {
246                                    min: (plot_center - platform_length - 2)
247                                        .with_z(floor_level - 4),
248                                    max: (plot_center + platform_length + 2)
249                                        .with_z(floor_level + 6),
250                                },
251                                4.0,
252                            )
253                            .intersect(limit_up)
254                            .fill(wood.clone());
255
256                        // Travel Agent desk
257                        let rotation = -f32::atan2(door_dir.x as f32, door_dir.y as f32);
258                        painter
259                            .aabb(Aabb {
260                                min: Vec2::new(plot_center.x - 5, plot_center.y - 5)
261                                    .with_z(floor_level + 1),
262                                max: Vec2::new(plot_center.x - 10, plot_center.y - 10)
263                                    .with_z(floor_level + 6),
264                            })
265                            .rotate_about(
266                                Mat3::rotation_z(rotation).as_(),
267                                plot_center.with_z(base),
268                            )
269                            .clear();
270                        painter
271                            .line(
272                                Vec2::new(plot_center.x - 6, plot_center.y - 9)
273                                    .with_z(floor_level + 1),
274                                Vec2::new(plot_center.x - 9, plot_center.y - 6)
275                                    .with_z(floor_level + 1),
276                                0.5,
277                            )
278                            .rotate_about(
279                                Mat3::rotation_z(rotation).as_(),
280                                plot_center.with_z(base),
281                            )
282                            .fill(brick.clone());
283                        painter
284                            .line(
285                                Vec2::new(plot_center.x - 10, plot_center.y - 10)
286                                    .with_z(floor_level + 1),
287                                Vec2::new(plot_center.x - 10, plot_center.y - 10)
288                                    .with_z(floor_level + 6),
289                                0.5,
290                            )
291                            .rotate_about(
292                                Mat3::rotation_z(rotation).as_(),
293                                plot_center.with_z(base),
294                            )
295                            .fill(brick.clone());
296                        let agent_lantern_pos = Vec2::new(plot_center.x - 9, plot_center.y - 9);
297                        let agent_lantern_pos_rotated = (plot_center.map(|i| i as f32)
298                            + Mat2::rotation_z(rotation)
299                                * (agent_lantern_pos - plot_center).map(|i| i as f32))
300                        .map(|f| f.round() as i32);
301                        painter.sprite(
302                            agent_lantern_pos_rotated.with_z(floor_level + 5),
303                            SpriteKind::WallLampMesa,
304                        );
305
306                        // lanterns & cargo
307                        for dir in NEIGHBORS {
308                            let lantern_pos = plot_center + (dir * (platform_length - 6));
309
310                            painter.sprite(
311                                lantern_pos.with_z(floor_level + 1),
312                                SpriteKind::StreetLamp,
313                            );
314                        }
315                        for dir in DIAGONALS {
316                            let cargo_pos = plot_center + (dir * (2 * length));
317
318                            for dir in CARDINALS {
319                                let sprite_pos = cargo_pos + dir;
320                                let rows =
321                                    (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
322                                for r in 0..rows {
323                                    painter
324                                        .aabb(Aabb {
325                                            min: (sprite_pos).with_z(floor_level + 1 + r),
326                                            max: (sprite_pos + 1).with_z(floor_level + 2 + r),
327                                        })
328                                        .fill(Fill::Block(Block::air(
329                                            match (RandomField::new(0)
330                                                .get(sprite_pos.with_z(base + r))
331                                                % 2)
332                                                as i32
333                                            {
334                                                0 => SpriteKind::Barrel,
335                                                _ => SpriteKind::CrateBlock,
336                                            },
337                                        )));
338                                    if r > 0 {
339                                        painter.owned_resource_sprite(
340                                            sprite_pos.with_z(floor_level + 2 + r),
341                                            SpriteKind::Crate,
342                                            0,
343                                        );
344                                    }
345                                }
346                            }
347                        }
348                        for dir in CARDINALS {
349                            // docks
350                            let dock_pos = plot_center + (dir * platform_length);
351
352                            painter
353                                .cylinder(Aabb {
354                                    min: (dock_pos - 8).with_z(floor_level),
355                                    max: (dock_pos + 8).with_z(floor_level + 1),
356                                })
357                                .fill(wood.clone());
358                            painter
359                                .cylinder(Aabb {
360                                    min: (dock_pos - 7).with_z(floor_level - 1),
361                                    max: (dock_pos + 7).with_z(floor_level),
362                                })
363                                .fill(wood.clone());
364                        }
365                        // campfire
366                        let campfire_pos =
367                            Vec2::new(plot_center.x - platform_length - 2, plot_center.y)
368                                .with_z(floor_level);
369                        painter.spawn(
370                            EntityInfo::at(campfire_pos.map(|e| e as f32 + 0.5))
371                                .into_special(SpecialEntity::Waypoint),
372                        );
373                    }
374
375                    // clear rooms and entries & decor
376                    if floor_level > (base - 6) {
377                        // decor
378                        painter
379                            .line(
380                                Vec2::new(plot_center.x, plot_center.y - length)
381                                    .with_z(floor_level + 5),
382                                Vec2::new(plot_center.x, plot_center.y + length)
383                                    .with_z(floor_level + 5),
384                                4.0,
385                            )
386                            .fill(color.clone());
387                        painter
388                            .line(
389                                Vec2::new(plot_center.x - length, plot_center.y)
390                                    .with_z(floor_level + 5),
391                                Vec2::new(plot_center.x + length, plot_center.y)
392                                    .with_z(floor_level + 5),
393                                4.0,
394                            )
395                            .fill(color.clone());
396                        // entries
397                        painter
398                            .line(
399                                Vec2::new(plot_center.x, plot_center.y - (2 * length) - 4)
400                                    .with_z(floor_level + 4),
401                                Vec2::new(plot_center.x, plot_center.y + (2 * length) + 4)
402                                    .with_z(floor_level + 4),
403                                4.0,
404                            )
405                            .clear();
406                        painter
407                            .line(
408                                Vec2::new(plot_center.x - (2 * length) - 4, plot_center.y)
409                                    .with_z(floor_level + 4),
410                                Vec2::new(plot_center.x + (2 * length) + 4, plot_center.y)
411                                    .with_z(floor_level + 4),
412                                4.0,
413                            )
414                            .clear();
415                        painter
416                            .superquadric(
417                                Aabb {
418                                    min: (plot_center - length - 1).with_z(floor_level),
419                                    max: (plot_center + length + 1)
420                                        .with_z(floor_level + height - 4),
421                                },
422                                sq_type,
423                            )
424                            .clear();
425                        // room floor
426                        painter
427                            .cylinder(Aabb {
428                                min: (plot_center - length - 3).with_z(floor_level),
429                                max: (plot_center + length + 3).with_z(floor_level + 1),
430                            })
431                            .fill(brick.clone());
432                        painter
433                            .cylinder(Aabb {
434                                min: (plot_center - length + 1).with_z(floor_level),
435                                max: (plot_center + length - 1).with_z(floor_level + 1),
436                            })
437                            .fill(color.clone());
438                        painter
439                            .cylinder(Aabb {
440                                min: (plot_center - length + 2).with_z(floor_level),
441                                max: (plot_center + length - 2).with_z(floor_level + 1),
442                            })
443                            .fill(brick.clone());
444                        // entry sprites
445                        painter
446                            .aabb(Aabb {
447                                min: Vec2::new(plot_center.x - 3, plot_center.y + length)
448                                    .with_z(floor_level + 2),
449                                max: Vec2::new(plot_center.x + 4, plot_center.y + length + 1)
450                                    .with_z(floor_level + 7),
451                            })
452                            .fill(window2.clone());
453                        painter
454                            .aabb(Aabb {
455                                min: Vec2::new(plot_center.x - 2, plot_center.y + length)
456                                    .with_z(floor_level + 2),
457                                max: Vec2::new(plot_center.x + 3, plot_center.y + length + 1)
458                                    .with_z(floor_level + 7),
459                            })
460                            .clear();
461
462                        painter
463                            .aabb(Aabb {
464                                min: Vec2::new(plot_center.x - 3, plot_center.y - length - 1)
465                                    .with_z(floor_level + 2),
466                                max: Vec2::new(plot_center.x + 4, plot_center.y - length)
467                                    .with_z(floor_level + 7),
468                            })
469                            .fill(window2.clone());
470                        painter
471                            .aabb(Aabb {
472                                min: Vec2::new(plot_center.x - 2, plot_center.y - length - 1)
473                                    .with_z(floor_level + 2),
474                                max: Vec2::new(plot_center.x + 3, plot_center.y - length)
475                                    .with_z(floor_level + 7),
476                            })
477                            .clear();
478                        painter
479                            .aabb(Aabb {
480                                min: Vec2::new(plot_center.x + length, plot_center.y - 3)
481                                    .with_z(floor_level + 2),
482                                max: Vec2::new(plot_center.x + length + 1, plot_center.y + 4)
483                                    .with_z(floor_level + 7),
484                            })
485                            .fill(window.clone());
486                        painter
487                            .aabb(Aabb {
488                                min: Vec2::new(plot_center.x + length, plot_center.y - 2)
489                                    .with_z(floor_level + 2),
490                                max: Vec2::new(plot_center.x + length + 1, plot_center.y + 3)
491                                    .with_z(floor_level + 7),
492                            })
493                            .clear();
494
495                        painter
496                            .aabb(Aabb {
497                                min: Vec2::new(plot_center.x - length - 1, plot_center.y - 3)
498                                    .with_z(floor_level + 2),
499                                max: Vec2::new(plot_center.x - length, plot_center.y + 4)
500                                    .with_z(floor_level + 7),
501                            })
502                            .fill(window.clone());
503                        painter
504                            .aabb(Aabb {
505                                min: Vec2::new(plot_center.x - length - 1, plot_center.y - 2)
506                                    .with_z(floor_level + 2),
507                                max: Vec2::new(plot_center.x - length, plot_center.y + 3)
508                                    .with_z(floor_level + 7),
509                            })
510                            .clear();
511                        // cargo in rooms
512                        for dir in DIAGONALS {
513                            let cargo_pos = plot_center + (dir * (length / 2));
514                            for dir in CARDINALS {
515                                let sprite_pos = cargo_pos + dir;
516                                let rows =
517                                    (RandomField::new(0).get(sprite_pos.with_z(base)) % 4) as i32;
518                                for r in 0..rows {
519                                    painter
520                                        .aabb(Aabb {
521                                            min: (sprite_pos).with_z(floor_level + 1 + r),
522                                            max: (sprite_pos + 1).with_z(floor_level + 2 + r),
523                                        })
524                                        .fill(Fill::Block(Block::air(
525                                            match (RandomField::new(0)
526                                                .get(sprite_pos.with_z(base + r))
527                                                % 2)
528                                                as i32
529                                            {
530                                                0 => SpriteKind::Barrel,
531                                                _ => SpriteKind::CrateBlock,
532                                            },
533                                        )));
534                                }
535                            }
536                        }
537
538                        // wall lamps
539                        let corner_pos_1 = Vec2::new(plot_center.x - length, plot_center.y - 5);
540                        let corner_pos_2 = Vec2::new(plot_center.x - 5, plot_center.y - length);
541                        for dir in SQUARE_4 {
542                            let lamp_pos_1 = Vec2::new(
543                                corner_pos_1.x + (dir.x * ((2 * length) - 1)),
544                                corner_pos_1.y + (dir.y * 10),
545                            )
546                            .with_z(floor_level + 7);
547                            painter.rotated_sprite(
548                                lamp_pos_1,
549                                SpriteKind::WallLampMesa,
550                                (2 + (4 * dir.x)) as u8,
551                            );
552                            let lamp_pos_2 = Vec2::new(
553                                corner_pos_2.x + (dir.x * 10),
554                                corner_pos_2.y + (dir.y * ((2 * length) - 1)),
555                            )
556                            .with_z(floor_level + 7);
557                            painter.rotated_sprite(
558                                lamp_pos_2,
559                                SpriteKind::WallLampMesa,
560                                (4 - (4 * dir.y)) as u8,
561                            );
562                        }
563                    }
564                    // stairs
565                    if floor_level > (base + 8) {
566                        let stairs_level = floor_level + 1;
567                        let stairs_start = plot_center + door_dir * ((2 * length) - 7);
568                        let mid_dir = if door_dir.x != 0 {
569                            door_dir.x
570                        } else {
571                            door_dir.y
572                        };
573                        let stairs_mid = Vec2::new(
574                            plot_center.x + mid_dir * (3 * (length / 2)),
575                            plot_center.y + mid_dir * (3 * (length / 2)),
576                        );
577                        let stairs_end = Vec2::new(
578                            plot_center.x + door_dir.y * ((2 * length) - 7),
579                            plot_center.y + door_dir.x * ((2 * length) - 7),
580                        );
581                        let rope_pos = Vec2::new(
582                            plot_center.x + mid_dir * ((3 * (length / 2)) + 2),
583                            plot_center.y + mid_dir * ((3 * (length / 2)) + 2),
584                        );
585
586                        painter
587                            .cylinder(Aabb {
588                                min: (stairs_start - 6).with_z(stairs_level - 1),
589                                max: (stairs_start + 6).with_z(stairs_level),
590                            })
591                            .fill(wood.clone());
592
593                        painter
594                            .cylinder(Aabb {
595                                min: (stairs_mid - 6).with_z(stairs_level - (height / 2) - 1),
596                                max: (stairs_mid + 6).with_z(stairs_level - (height / 2)),
597                            })
598                            .fill(wood.clone());
599
600                        painter
601                            .cylinder(Aabb {
602                                min: (stairs_end - 6).with_z(stairs_level - height - 1),
603                                max: (stairs_end + 6).with_z(stairs_level - height),
604                            })
605                            .fill(wood.clone());
606
607                        for n in 0..2 {
608                            let stairs = painter
609                                .line(
610                                    stairs_start.with_z(stairs_level + (n * 2)),
611                                    stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
612                                    4.0 + (n as f32 / 2.0),
613                                )
614                                .union(painter.line(
615                                    stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
616                                    stairs_end.with_z(stairs_level - height + (n * 2)),
617                                    4.0 + (n as f32 / 2.0),
618                                ));
619                            match n {
620                                0 => stairs.fill(wood.clone()),
621                                _ => stairs.clear(),
622                            };
623                        }
624                        painter
625                            .line(
626                                rope_pos.with_z(stairs_level + (height / 2) - 3),
627                                (plot_center - (length / 2))
628                                    .with_z(stairs_level + (height / 2) + 2),
629                                1.5,
630                            )
631                            .fill(wood.clone());
632
633                        painter
634                            .aabb(Aabb {
635                                min: rope_pos.with_z(stairs_level - (height / 2) - 1),
636                                max: (rope_pos + 1).with_z(stairs_level + (height / 2) - 3),
637                            })
638                            .fill(rope.clone());
639                    }
640                }
641                // vary next storey
642                length += -1;
643                width += -1;
644                height += -1;
645                floor_level += height;
646                mem::swap(&mut length, &mut width);
647            }
648        }
649    }
650}