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(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            // common superquadric degree for rooms
153            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                // CliffTower Hoodoo Overlay
168                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                // center tube with  rooms
192                if n == tubes as i32 {
193                    // ground_entries
194                    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                    // platform
233                    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                        // lanterns & cargo
251                        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                            // docks
294                            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                        // campfire
310                        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                    // clear rooms and entries & decor
320                    if floor_level > (base - 6) {
321                        // decor
322                        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                        // entries
341                        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                        // room floor
370                        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                        // entry sprites
389                        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                        // cargo in rooms
456                        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                        // wall lamps
483                        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                    // stairs
509                    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                // vary next storey
586                length += -1;
587                width += -1;
588                height += -1;
589                floor_level += height;
590                mem::swap(&mut length, &mut width);
591            }
592        }
593    }
594}