Skip to main content

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
118impl Structure for DesertCityAirshipDock {
119    #[cfg(feature = "dyn-lib")]
120    #[unsafe(export_name = "as_dyn_structure_desertcityairshipdock")]
121    fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
122        Some((
123            Self::as_dyn_impl(self),
124            "as_dyn_structure_desertcityairshipdock",
125        ))
126    }
127
128    fn spawn_rules_inner(
129        &self,
130        spawn_rules: &mut SpawnRules,
131        _land: &Land,
132        wpos: Vec2<i32>,
133        _weight: f32,
134    ) {
135        // dock is 5 tiles = 30 blocks in radius
136        // airships are 39 blocks wide.
137        // Tree can be up to 20 blocks in radius.
138        // Don't allow trees within 30 + 39 + 20 = 89 blocks of the dock center
139        const AIRSHIP_MIN_TREE_DIST: i32 = 89;
140        spawn_rules.trees &= !within_distance(wpos, self.center, AIRSHIP_MIN_TREE_DIST);
141
142        const MIN_FLAT_DIST: f32 = 12.0;
143        const SLOPE_LENGTH: f32 = 16.0;
144        let dist = wpos.as_::<f32>().distance(self.center.as_());
145        let weight = (1.0 - (dist - MIN_FLAT_DIST).max(0.0) / SLOPE_LENGTH).max(0.0);
146        spawn_rules.prefer_alt(self.alt as f32, weight);
147    }
148
149    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
150        let sandstone = Fill::Sampling(Arc::new(|center| {
151            Some(match (RandomField::new(0).get(center)) % 37 {
152                0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
153                9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
154                18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
155                27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
156                _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
157            })
158        }));
159        let sandstone_broken = Fill::Sampling(Arc::new(|center| {
160            Some(match (RandomField::new(0).get(center)) % 42 {
161                0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
162                9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
163                18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
164                27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
165                36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
166                _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
167            })
168        }));
169        let wood = Fill::Brick(BlockKind::Wood, Rgb::new(71, 33, 11), 12);
170        let base = self.base;
171        let center = self.center;
172        // Fence
173        painter
174            .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.min.x + 2, self.bounds.max.y).with_z(base + 2),
177            })
178            .union(painter.aabb(Aabb {
179                min: Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 1).with_z(base - 20),
180                max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
181            }))
182            .union(painter.aabb(Aabb {
183                min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
184                max: Vec2::new(self.bounds.max.x, self.bounds.min.y + 2).with_z(base + 2),
185            }))
186            .union(painter.aabb(Aabb {
187                min: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 1).with_z(base - 20),
188                max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
189            }))
190            .fill(sandstone_broken);
191        painter
192            .aabb(Aabb {
193                min: Vec2::new(self.bounds.min.x + 1, center.y - 8).with_z(base),
194                max: Vec2::new(self.bounds.max.x, center.y + 8).with_z(base + 7),
195            })
196            .clear();
197        painter
198            .aabb(Aabb {
199                min: Vec2::new(center.x - 7, self.bounds.min.y + 1).with_z(base),
200                max: Vec2::new(center.x + 9, self.bounds.max.y).with_z(base + 7),
201            })
202            .clear();
203        // Foundation
204        painter
205            .aabb(Aabb {
206                min: (self.bounds.min + 1).with_z(base - 20),
207                max: (self.bounds.max).with_z(base),
208            })
209            .fill(sandstone.clone());
210
211        // rooms
212        let length = self.length;
213        let height = self.height;
214        let floors = self.floors;
215        let carve = length / 4;
216
217        for f in 0..=floors {
218            let bldg_base = base + f * (height + 1);
219            let bldg_length = length;
220            if f == floors {
221                // Agent Desk
222                painter
223                    .aabb(Aabb {
224                        min: Vec2::new(center.x - 7, center.y - 5).with_z(bldg_base + height),
225                        max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 6),
226                    })
227                    .fill(sandstone.clone());
228                painter
229                    .aabb(Aabb {
230                        min: Vec2::new(center.x - 8, center.y - 4).with_z(bldg_base + height),
231                        max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 5),
232                    })
233                    .clear();
234                painter
235                    .aabb(Aabb {
236                        min: Vec2::new(center.x - 11, center.y - 5).with_z(bldg_base + height),
237                        max: Vec2::new(center.x - 12, center.y + 5).with_z(bldg_base + height + 5),
238                    })
239                    .clear();
240                painter
241                    .aabb(Aabb {
242                        min: Vec2::new(center.x - 10, center.y - 4).with_z(bldg_base + height),
243                        max: Vec2::new(center.x - 11, center.y + 4).with_z(bldg_base + height + 2),
244                    })
245                    .fill(wood.clone());
246            }
247            // room
248            painter
249                .aabb(Aabb {
250                    min: (center - bldg_length).with_z(bldg_base),
251                    max: (center + bldg_length).with_z(bldg_base + height),
252                })
253                .fill(sandstone.clone());
254            // roof floor
255            painter
256                .aabb(Aabb {
257                    min: (center - bldg_length - 1 - f).with_z(bldg_base + height),
258                    max: (center + bldg_length + 1 + f).with_z(bldg_base + height + 1),
259                })
260                .fill(wood.clone());
261            painter
262                .aabb(Aabb {
263                    min: (center - bldg_length - f).with_z(bldg_base + height),
264                    max: (center + bldg_length + f).with_z(bldg_base + height + 1),
265                })
266                .fill(sandstone.clone());
267            // clear room
268            painter
269                .aabb(Aabb {
270                    min: (center - bldg_length + 1).with_z(bldg_base),
271                    max: (center + bldg_length - 1).with_z(bldg_base + height - 1),
272                })
273                .clear();
274
275            let clear_limit_1 = painter.aabb(Aabb {
276                min: Vec2::new(center.x - bldg_length, center.y - bldg_length + 1)
277                    .with_z(bldg_base),
278                max: Vec2::new(center.x + bldg_length, center.y + bldg_length - 1)
279                    .with_z(bldg_base + height),
280            });
281            let clear_limit_2 = painter.aabb(Aabb {
282                min: Vec2::new(center.x - bldg_length + 1, center.y - bldg_length)
283                    .with_z(bldg_base),
284                max: Vec2::new(center.x + bldg_length - 1, center.y + bldg_length)
285                    .with_z(bldg_base + height),
286            });
287            for c in 0..=4 {
288                let space = c * ((2 * carve) + 1);
289                painter
290                    .vault(
291                        Aabb {
292                            min: Vec2::new(
293                                center.x - bldg_length,
294                                center.y + space - bldg_length - carve + 1,
295                            )
296                            .with_z(bldg_base + (height / 2)),
297                            max: Vec2::new(
298                                center.x + bldg_length,
299                                center.y + space - bldg_length + carve - 1,
300                            )
301                            .with_z(bldg_base + height - 1),
302                        },
303                        Dir2::X,
304                    )
305                    .intersect(clear_limit_1)
306                    .clear();
307                painter
308                    .vault(
309                        Aabb {
310                            min: Vec2::new(
311                                center.x + space - bldg_length - carve + 1,
312                                center.y - bldg_length,
313                            )
314                            .with_z(bldg_base + (height / 2)),
315                            max: Vec2::new(
316                                center.x + space - bldg_length + carve - 1,
317                                center.y + bldg_length,
318                            )
319                            .with_z(bldg_base + height - 1),
320                        },
321                        Dir2::Y,
322                    )
323                    .intersect(clear_limit_2)
324                    .clear();
325
326                painter
327                    .aabb(Aabb {
328                        min: Vec2::new(
329                            center.x - bldg_length,
330                            center.y + space - bldg_length - carve,
331                        )
332                        .with_z(bldg_base),
333                        max: Vec2::new(
334                            center.x + bldg_length,
335                            center.y + space - bldg_length + carve,
336                        )
337                        .with_z(bldg_base + (height / 2)),
338                    })
339                    .intersect(clear_limit_1)
340                    .clear();
341                painter
342                    .aabb(Aabb {
343                        min: Vec2::new(
344                            center.x + space - bldg_length - carve,
345                            center.y - bldg_length,
346                        )
347                        .with_z(bldg_base),
348                        max: Vec2::new(
349                            center.x + space - bldg_length + carve,
350                            center.y + bldg_length,
351                        )
352                        .with_z(bldg_base + (height / 2)),
353                    })
354                    .intersect(clear_limit_2)
355                    .clear();
356            }
357            for dir in DIAGONALS {
358                let clear_pos = center + dir * bldg_length;
359                painter
360                    .aabb(Aabb {
361                        min: (clear_pos - 1).with_z(bldg_base),
362                        max: (clear_pos + 1).with_z(bldg_base + height - 1),
363                    })
364                    .clear();
365            }
366            for dir in DIAGONALS {
367                let lamp_pos = center + dir * (bldg_length - 1);
368                painter.sprite(lamp_pos.with_z(bldg_base), SpriteKind::StreetLamp);
369                if f == 4 {
370                    let lamp_pos = center + dir * bldg_length;
371                    painter.sprite(
372                        lamp_pos.with_z(bldg_base + height + 1),
373                        SpriteKind::StreetLamp,
374                    );
375
376                    let cargo_pos = center + (dir * ((bldg_length / 2) + 1));
377                    for dir in CARDINALS {
378                        let sprite_pos = cargo_pos + dir;
379                        let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
380                        for r in 0..rows {
381                            painter
382                                .aabb(Aabb {
383                                    min: (sprite_pos).with_z(bldg_base + height + 1 + r),
384                                    max: (sprite_pos + 1).with_z(bldg_base + height + 2 + r),
385                                })
386                                .fill(Fill::Block(Block::air(
387                                    match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
388                                        as i32
389                                    {
390                                        0 => SpriteKind::Barrel,
391                                        _ => SpriteKind::CrateBlock,
392                                    },
393                                )));
394                            if r > 0 {
395                                painter.owned_resource_sprite(
396                                    sprite_pos.with_z(bldg_base + height + 2 + r),
397                                    SpriteKind::Crate,
398                                    0,
399                                );
400                            }
401                        }
402                    }
403                }
404            }
405            // docks
406            if f == 4 {
407                for dir in CARDINALS {
408                    let gangway_pos_1 = center + dir * (3 * (bldg_length / 2));
409                    let gangway_pos_2 = center + dir * ((3 * (bldg_length / 2)) - 4);
410                    let dock_pos = center + dir * 27;
411                    painter
412                        .aabb(Aabb {
413                            min: (gangway_pos_2 - 3).with_z(bldg_base + height - 1),
414                            max: (gangway_pos_2 + 3).with_z(bldg_base + height),
415                        })
416                        .fill(wood.clone());
417                    painter
418                        .aabb(Aabb {
419                            min: (gangway_pos_1 - 3).with_z(bldg_base + height),
420                            max: (gangway_pos_1 + 3).with_z(bldg_base + height + 1),
421                        })
422                        .fill(wood.clone());
423                    painter
424                        .cylinder(Aabb {
425                            min: (dock_pos - 4).with_z(bldg_base + height),
426                            max: (dock_pos + 4).with_z(bldg_base + height + 1),
427                        })
428                        .fill(wood.clone());
429                    painter
430                        .cylinder(Aabb {
431                            min: (dock_pos - 3).with_z(bldg_base + height - 1),
432                            max: (dock_pos + 3).with_z(bldg_base + height),
433                        })
434                        .fill(wood.clone());
435                }
436                // campfire
437                painter.spawn(
438                    EntityInfo::at(
439                        Vec2::new(center.x + bldg_length - 2, center.y)
440                            .with_z(bldg_base + height + 1)
441                            .map(|e| e as f32 + 0.5),
442                    )
443                    .into_special(SpecialEntity::Waypoint),
444                );
445            }
446            // stairs
447            painter
448                .aabb(Aabb {
449                    min: (center - (bldg_length / 2) - 1).with_z(bldg_base + height - 1),
450                    max: (center + (bldg_length / 2) + 1).with_z(bldg_base + height + 1),
451                })
452                .fill(wood.clone());
453            painter
454                .aabb(Aabb {
455                    min: (center - (bldg_length / 2)).with_z(bldg_base + height - 1),
456                    max: (center + (bldg_length / 2)).with_z(bldg_base + height + 1),
457                })
458                .clear();
459
460            for w in 0..((bldg_length / 2) + 2) {
461                painter
462                    .aabb(Aabb {
463                        min: Vec2::new(
464                            center.x - (bldg_length / 2) - 2 + (2 * w),
465                            center.y - (bldg_length / 2),
466                        )
467                        .with_z(bldg_base + w),
468                        max: Vec2::new(
469                            center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
470                            center.y + (bldg_length / 2),
471                        )
472                        .with_z(bldg_base + 1 + w),
473                    })
474                    .fill(wood.clone());
475                painter
476                    .aabb(Aabb {
477                        min: Vec2::new(
478                            center.x - (bldg_length / 2) - 2 + (2 * w),
479                            center.y - (bldg_length / 2),
480                        )
481                        .with_z(bldg_base - 1 + w),
482                        max: Vec2::new(
483                            center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
484                            center.y + (bldg_length / 2),
485                        )
486                        .with_z(bldg_base + w),
487                    })
488                    .fill(sandstone.clone());
489            }
490        }
491    }
492}