veloren_world/site/plot/
desert_city_airship_dock.rs

1use super::*;
2use crate::{
3    Land,
4    util::{DIAGONALS, RandomField, Sampler, within_distance},
5};
6use common::{
7    generation::SpecialEntity,
8    terrain::{BlockKind, SpriteKind},
9};
10
11use rand::prelude::*;
12use std::sync::Arc;
13use vek::*;
14
15/// Represents house data generated by the `generate()` method
16pub struct DesertCityAirshipDock {
17    /// Tile position of the door tile
18    pub door_tile: Vec2<i32>,
19    /// Axis aligned bounding region for the house
20    bounds: Aabr<i32>,
21    /// Approximate altitude of the door tile
22    pub(crate) alt: i32,
23    pub docking_positions: Vec<Vec3<i32>>,
24    pub center: Vec2<i32>,
25    base: i32,
26    length: i32,
27    height: i32,
28    floors: i32,
29}
30
31impl DesertCityAirshipDock {
32    pub fn generate(
33        land: &Land,
34        _rng: &mut impl Rng,
35        site: &Site,
36        door_tile: Vec2<i32>,
37        door_dir: Vec2<i32>,
38        tile_aabr: Aabr<i32>,
39        alt: Option<i32>,
40    ) -> Self {
41        let door_tile_pos = site.tile_center_wpos(door_tile);
42        let bounds = Aabr {
43            min: site.tile_wpos(tile_aabr.min),
44            max: site.tile_wpos(tile_aabr.max),
45        };
46        let alt = alt.unwrap_or_else(|| {
47            land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
48        });
49        let center = bounds.center();
50
51        // dock is 5 tiles = 30 blocks in radius
52        // airships are 37 blocks wide = 6 tiles.
53        // distance from the center to the outside edge of the airship when docked is 11
54        // tiles. The area covered by all four airships is a square 22 tiles on
55        // a side.
56
57        // Sample the surface altitude every 4 tiles (= 24 blocks) around the dock
58        // center to find the highest point of terrain surrounding the dock.
59        let mut max_surface_alt = i32::MIN;
60        // -12 -8 -4 0 +4 +8 +12
61        for dx in -3..=3 {
62            for dy in -3..=3 {
63                let pos = center + Vec2::new(dx * 24, dy * 24);
64                let alt = land.get_surface_alt_approx(pos) as i32;
65                if alt > max_surface_alt {
66                    max_surface_alt = alt;
67                }
68            }
69        }
70
71        // The foundation is the size of the bounds.
72        // Sample the surface altitude over the foundation area to get the
73        // foundation max alt.
74        let foundation_qtr = bounds.size().w / 4;
75        let mut max_foundation_alt = i32::MIN;
76        for dx in -2..=2 {
77            for dy in -2..=2 {
78                let pos = center + Vec2::new(dx * foundation_qtr, dy * foundation_qtr);
79                let alt = land.get_surface_alt_approx(pos) as i32;
80                if alt > max_foundation_alt {
81                    max_foundation_alt = alt;
82                }
83            }
84        }
85
86        let length = 14;
87        let height = 2 * (length / 3);
88        let floors = 4;
89
90        // The desired base alt is max_foundation_alt + 1.
91        let mut base = max_foundation_alt + 1;
92        let mut top_floor = base + 5 + (height * (floors + 1));
93        // The docking platforms will be at top_floor.
94        let clearance = top_floor - (max_surface_alt + 6);
95        if clearance < 0 {
96            base += -clearance;
97            top_floor += -clearance;
98        }
99
100        let docking_positions = CARDINALS
101            .iter()
102            .map(|dir| (center + dir * 31).with_z(top_floor))
103            .collect::<Vec<_>>();
104        Self {
105            bounds,
106            door_tile: door_tile_pos,
107            alt,
108            docking_positions,
109            center,
110            base,
111            length,
112            height,
113            floors,
114        }
115    }
116
117    pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
118        SpawnRules {
119            trees: {
120                // dock is 5 tiles = 30 blocks in radius
121                // airships are 39 blocks wide.
122                // Tree can be up to 20 blocks in radius.
123                // Don't allow trees within 30 + 39 + 20 = 89 blocks of the dock center
124                const AIRSHIP_MIN_TREE_DIST: i32 = 89;
125                !within_distance(wpos, self.center, AIRSHIP_MIN_TREE_DIST)
126            },
127            waypoints: false,
128            ..SpawnRules::default()
129        }
130    }
131}
132
133impl Structure for DesertCityAirshipDock {
134    #[cfg(feature = "use-dyn-lib")]
135    const UPDATE_FN: &'static [u8] = b"render_desertcityairshipdock\0";
136
137    #[cfg_attr(
138        feature = "be-dyn-lib",
139        unsafe(export_name = "render_desertcityairshipdock")
140    )]
141    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
142        let sandstone = Fill::Sampling(Arc::new(|center| {
143            Some(match (RandomField::new(0).get(center)) % 37 {
144                0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
145                9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
146                18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
147                27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
148                _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
149            })
150        }));
151        let sandstone_broken = Fill::Sampling(Arc::new(|center| {
152            Some(match (RandomField::new(0).get(center)) % 42 {
153                0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
154                9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
155                18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
156                27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
157                36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
158                _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
159            })
160        }));
161        let wood = Fill::Brick(BlockKind::Wood, Rgb::new(71, 33, 11), 12);
162        let base = self.base;
163        let center = self.center;
164        // Fence
165        painter
166            .aabb(Aabb {
167                min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
168                max: Vec2::new(self.bounds.min.x + 2, self.bounds.max.y).with_z(base + 2),
169            })
170            .union(painter.aabb(Aabb {
171                min: Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 1).with_z(base - 20),
172                max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
173            }))
174            .union(painter.aabb(Aabb {
175                min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
176                max: Vec2::new(self.bounds.max.x, self.bounds.min.y + 2).with_z(base + 2),
177            }))
178            .union(painter.aabb(Aabb {
179                min: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 1).with_z(base - 20),
180                max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
181            }))
182            .fill(sandstone_broken);
183        painter
184            .aabb(Aabb {
185                min: Vec2::new(self.bounds.min.x + 1, center.y - 8).with_z(base),
186                max: Vec2::new(self.bounds.max.x, center.y + 8).with_z(base + 7),
187            })
188            .clear();
189        painter
190            .aabb(Aabb {
191                min: Vec2::new(center.x - 7, self.bounds.min.y + 1).with_z(base),
192                max: Vec2::new(center.x + 9, self.bounds.max.y).with_z(base + 7),
193            })
194            .clear();
195        // Foundation
196        painter
197            .aabb(Aabb {
198                min: (self.bounds.min + 1).with_z(base - 20),
199                max: (self.bounds.max).with_z(base),
200            })
201            .fill(sandstone.clone());
202
203        // rooms
204        let length = self.length;
205        let height = self.height;
206        let floors = self.floors;
207        let carve = length / 4;
208
209        for f in 0..=floors {
210            let bldg_base = base + f * (height + 1);
211            let bldg_length = length;
212            if f == floors {
213                // Agent Desk
214                painter
215                    .aabb(Aabb {
216                        min: Vec2::new(center.x - 7, center.y - 5).with_z(bldg_base + height),
217                        max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 6),
218                    })
219                    .fill(sandstone.clone());
220                painter
221                    .aabb(Aabb {
222                        min: Vec2::new(center.x - 8, center.y - 4).with_z(bldg_base + height),
223                        max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 5),
224                    })
225                    .clear();
226                painter
227                    .aabb(Aabb {
228                        min: Vec2::new(center.x - 11, center.y - 5).with_z(bldg_base + height),
229                        max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 5),
230                    })
231                    .clear();
232                painter
233                    .aabb(Aabb {
234                        min: Vec2::new(center.x - 10, center.y - 4).with_z(bldg_base + height),
235                        max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 2),
236                    })
237                    .fill(wood.clone());
238            }
239            // room
240            painter
241                .aabb(Aabb {
242                    min: (center - bldg_length).with_z(bldg_base),
243                    max: (center + bldg_length).with_z(bldg_base + height),
244                })
245                .fill(sandstone.clone());
246            // roof floor
247            painter
248                .aabb(Aabb {
249                    min: (center - bldg_length - 1 - f).with_z(bldg_base + height),
250                    max: (center + bldg_length + 1 + f).with_z(bldg_base + height + 1),
251                })
252                .fill(wood.clone());
253            painter
254                .aabb(Aabb {
255                    min: (center - bldg_length - f).with_z(bldg_base + height),
256                    max: (center + bldg_length + f).with_z(bldg_base + height + 1),
257                })
258                .fill(sandstone.clone());
259            // clear room
260            painter
261                .aabb(Aabb {
262                    min: (center - bldg_length + 1).with_z(bldg_base),
263                    max: (center + bldg_length - 1).with_z(bldg_base + height - 1),
264                })
265                .clear();
266
267            let clear_limit_1 = painter.aabb(Aabb {
268                min: Vec2::new(center.x - bldg_length, center.y - bldg_length + 1)
269                    .with_z(bldg_base),
270                max: Vec2::new(center.x + bldg_length, center.y + bldg_length - 1)
271                    .with_z(bldg_base + height),
272            });
273            let clear_limit_2 = painter.aabb(Aabb {
274                min: Vec2::new(center.x - bldg_length + 1, center.y - bldg_length)
275                    .with_z(bldg_base),
276                max: Vec2::new(center.x + bldg_length - 1, center.y + bldg_length)
277                    .with_z(bldg_base + height),
278            });
279            for c in 0..=4 {
280                let space = c * ((2 * carve) + 1);
281                painter
282                    .vault(
283                        Aabb {
284                            min: Vec2::new(
285                                center.x - bldg_length,
286                                center.y + space - bldg_length - carve + 1,
287                            )
288                            .with_z(bldg_base + (height / 2)),
289                            max: Vec2::new(
290                                center.x + bldg_length,
291                                center.y + space - bldg_length + carve - 1,
292                            )
293                            .with_z(bldg_base + height - 1),
294                        },
295                        Dir::X,
296                    )
297                    .intersect(clear_limit_1)
298                    .clear();
299                painter
300                    .vault(
301                        Aabb {
302                            min: Vec2::new(
303                                center.x + space - bldg_length - carve + 1,
304                                center.y - bldg_length,
305                            )
306                            .with_z(bldg_base + (height / 2)),
307                            max: Vec2::new(
308                                center.x + space - bldg_length + carve - 1,
309                                center.y + bldg_length,
310                            )
311                            .with_z(bldg_base + height - 1),
312                        },
313                        Dir::Y,
314                    )
315                    .intersect(clear_limit_2)
316                    .clear();
317
318                painter
319                    .aabb(Aabb {
320                        min: Vec2::new(
321                            center.x - bldg_length,
322                            center.y + space - bldg_length - carve,
323                        )
324                        .with_z(bldg_base),
325                        max: Vec2::new(
326                            center.x + bldg_length,
327                            center.y + space - bldg_length + carve,
328                        )
329                        .with_z(bldg_base + (height / 2)),
330                    })
331                    .intersect(clear_limit_1)
332                    .clear();
333                painter
334                    .aabb(Aabb {
335                        min: Vec2::new(
336                            center.x + space - bldg_length - carve,
337                            center.y - bldg_length,
338                        )
339                        .with_z(bldg_base),
340                        max: Vec2::new(
341                            center.x + space - bldg_length + carve,
342                            center.y + bldg_length,
343                        )
344                        .with_z(bldg_base + (height / 2)),
345                    })
346                    .intersect(clear_limit_2)
347                    .clear();
348            }
349            for dir in DIAGONALS {
350                let clear_pos = center + dir * bldg_length;
351                painter
352                    .aabb(Aabb {
353                        min: (clear_pos - 1).with_z(bldg_base),
354                        max: (clear_pos + 1).with_z(bldg_base + height - 1),
355                    })
356                    .clear();
357            }
358            for dir in DIAGONALS {
359                let lamp_pos = center + dir * (bldg_length - 1);
360                painter.sprite(lamp_pos.with_z(bldg_base), SpriteKind::StreetLamp);
361                if f == 4 {
362                    let lamp_pos = center + dir * bldg_length;
363                    painter.sprite(
364                        lamp_pos.with_z(bldg_base + height + 1),
365                        SpriteKind::StreetLamp,
366                    );
367
368                    let cargo_pos = center + (dir * ((bldg_length / 2) + 1));
369                    for dir in CARDINALS {
370                        let sprite_pos = cargo_pos + dir;
371                        let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
372                        for r in 0..rows {
373                            painter
374                                .aabb(Aabb {
375                                    min: (sprite_pos).with_z(bldg_base + height + 1 + r),
376                                    max: (sprite_pos + 1).with_z(bldg_base + height + 2 + r),
377                                })
378                                .fill(Fill::Block(Block::air(
379                                    match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
380                                        as i32
381                                    {
382                                        0 => SpriteKind::Barrel,
383                                        _ => SpriteKind::CrateBlock,
384                                    },
385                                )));
386                            if r > 0 {
387                                painter.owned_resource_sprite(
388                                    sprite_pos.with_z(bldg_base + height + 2 + r),
389                                    SpriteKind::Crate,
390                                    0,
391                                );
392                            }
393                        }
394                    }
395                }
396            }
397            // docks
398            if f == 4 {
399                for dir in CARDINALS {
400                    let gangway_pos_1 = center + dir * (3 * (bldg_length / 2));
401                    let gangway_pos_2 = center + dir * ((3 * (bldg_length / 2)) - 4);
402                    let dock_pos = center + dir * 27;
403                    painter
404                        .aabb(Aabb {
405                            min: (gangway_pos_2 - 3).with_z(bldg_base + height - 1),
406                            max: (gangway_pos_2 + 3).with_z(bldg_base + height),
407                        })
408                        .fill(wood.clone());
409                    painter
410                        .aabb(Aabb {
411                            min: (gangway_pos_1 - 3).with_z(bldg_base + height),
412                            max: (gangway_pos_1 + 3).with_z(bldg_base + height + 1),
413                        })
414                        .fill(wood.clone());
415                    painter
416                        .cylinder(Aabb {
417                            min: (dock_pos - 4).with_z(bldg_base + height),
418                            max: (dock_pos + 4).with_z(bldg_base + height + 1),
419                        })
420                        .fill(wood.clone());
421                    painter
422                        .cylinder(Aabb {
423                            min: (dock_pos - 3).with_z(bldg_base + height - 1),
424                            max: (dock_pos + 3).with_z(bldg_base + height),
425                        })
426                        .fill(wood.clone());
427                }
428                // campfire
429                painter.spawn(
430                    EntityInfo::at(
431                        Vec2::new(center.x + bldg_length - 2, center.y)
432                            .with_z(bldg_base + height + 1)
433                            .map(|e| e as f32 + 0.5),
434                    )
435                    .into_special(SpecialEntity::Waypoint),
436                );
437            }
438            // stairs
439            painter
440                .aabb(Aabb {
441                    min: (center - (bldg_length / 2) - 1).with_z(bldg_base + height - 1),
442                    max: (center + (bldg_length / 2) + 1).with_z(bldg_base + height + 1),
443                })
444                .fill(wood.clone());
445            painter
446                .aabb(Aabb {
447                    min: (center - (bldg_length / 2)).with_z(bldg_base + height - 1),
448                    max: (center + (bldg_length / 2)).with_z(bldg_base + height + 1),
449                })
450                .clear();
451
452            for w in 0..((bldg_length / 2) + 2) {
453                painter
454                    .aabb(Aabb {
455                        min: Vec2::new(
456                            center.x - (bldg_length / 2) - 2 + (2 * w),
457                            center.y - (bldg_length / 2),
458                        )
459                        .with_z(bldg_base + w),
460                        max: Vec2::new(
461                            center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
462                            center.y + (bldg_length / 2),
463                        )
464                        .with_z(bldg_base + 1 + w),
465                    })
466                    .fill(wood.clone());
467                painter
468                    .aabb(Aabb {
469                        min: Vec2::new(
470                            center.x - (bldg_length / 2) - 2 + (2 * w),
471                            center.y - (bldg_length / 2),
472                        )
473                        .with_z(bldg_base - 1 + w),
474                        max: Vec2::new(
475                            center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
476                            center.y + (bldg_length / 2),
477                        )
478                        .with_z(bldg_base + w),
479                    })
480                    .fill(sandstone.clone());
481            }
482        }
483    }
484}