veloren_world/site2/plot/
cliff_town_airship_dock.rs

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
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 + 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                // 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 = 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            // 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 = 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                // CliffTower Hoodoo Overlay
171                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                // center tube with  rooms
195                if n == tubes as i32 {
196                    // ground_entries
197                    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                    // platform
236                    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                        // lanterns & cargo
254                        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                            // docks
297                            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                        // campfire
313                        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                    // clear rooms and entries & decor
323                    if floor_level > (base - 6) {
324                        // decor
325                        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                        // entries
344                        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                        // room floor
373                        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                        // entry sprites
392                        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                        // cargo in rooms
459                        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                        // wall lamps
486                        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                    // stairs
512                    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                // vary next storey
589                length += -1;
590                width += -1;
591                height += -1;
592                floor_level += height;
593                mem::swap(&mut length, &mut width);
594            }
595        }
596    }
597}