veloren_world/site2/plot/
desert_city_airship_dock.rs

1use super::*;
2use crate::{
3    Land,
4    util::{DIAGONALS, RandomField, Sampler},
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        let length = 14;
51        let height = 2 * (length / 3);
52        let floors = 4;
53        let mut docking_positions = vec![];
54        let base = alt + 1;
55        let top_floor = base + 5 + (height * (floors + 1));
56        for dir in CARDINALS {
57            let docking_pos = center + dir * (length * 2);
58            docking_positions.push(docking_pos.with_z(top_floor));
59        }
60        Self {
61            bounds,
62            door_tile: door_tile_pos,
63            alt,
64            docking_positions,
65            center,
66            base,
67            length,
68            height,
69            floors,
70        }
71    }
72
73    pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
74        SpawnRules {
75            trees: {
76                // dock is 3 tiles = 18 blocks in radius
77                // airships are 20 blocks wide.
78                // Leave extra space for tree width (at lease 15 extra).
79                // Don't allow trees within 18 + 20 + 15 = 53 blocks of the dock center
80                const AIRSHIP_MIN_TREE_DIST2: i32 = 53i32.pow(2);
81                wpos.distance_squared(self.center) > AIRSHIP_MIN_TREE_DIST2
82            },
83            waypoints: false,
84            ..SpawnRules::default()
85        }
86    }
87}
88
89impl Structure for DesertCityAirshipDock {
90    #[cfg(feature = "use-dyn-lib")]
91    const UPDATE_FN: &'static [u8] = b"render_desertcityairshipdock\0";
92
93    #[cfg_attr(
94        feature = "be-dyn-lib",
95        unsafe(export_name = "render_desertcityairshipdock")
96    )]
97    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
98        let sandstone = Fill::Sampling(Arc::new(|center| {
99            Some(match (RandomField::new(0).get(center)) % 37 {
100                0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
101                9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
102                18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
103                27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
104                _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
105            })
106        }));
107        let sandstone_broken = Fill::Sampling(Arc::new(|center| {
108            Some(match (RandomField::new(0).get(center)) % 42 {
109                0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
110                9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
111                18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
112                27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
113                36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
114                _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
115            })
116        }));
117        let wood = Fill::Brick(BlockKind::Wood, Rgb::new(71, 33, 11), 12);
118        let base = self.base;
119        let center = self.center;
120        // Fence
121        painter
122            .aabb(Aabb {
123                min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
124                max: Vec2::new(self.bounds.min.x + 2, self.bounds.max.y).with_z(base + 2),
125            })
126            .union(painter.aabb(Aabb {
127                min: Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 1).with_z(base - 20),
128                max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
129            }))
130            .union(painter.aabb(Aabb {
131                min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1).with_z(base - 20),
132                max: Vec2::new(self.bounds.max.x, self.bounds.min.y + 2).with_z(base + 2),
133            }))
134            .union(painter.aabb(Aabb {
135                min: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 1).with_z(base - 20),
136                max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(base + 2),
137            }))
138            .fill(sandstone_broken);
139        painter
140            .aabb(Aabb {
141                min: Vec2::new(self.bounds.min.x + 1, center.y - 8).with_z(base),
142                max: Vec2::new(self.bounds.max.x, center.y + 8).with_z(base + 7),
143            })
144            .clear();
145        painter
146            .aabb(Aabb {
147                min: Vec2::new(center.x - 7, self.bounds.min.y + 1).with_z(base),
148                max: Vec2::new(center.x + 9, self.bounds.max.y).with_z(base + 7),
149            })
150            .clear();
151        // Foundation
152        painter
153            .aabb(Aabb {
154                min: (self.bounds.min + 1).with_z(base - 20),
155                max: (self.bounds.max).with_z(base),
156            })
157            .fill(sandstone.clone());
158
159        // rooms
160        let length = self.length;
161        let height = self.height;
162        let floors = self.floors;
163        let carve = length / 4;
164
165        for f in 0..=floors {
166            let bldg_base = base + f * (height + 1);
167            let bldg_length = length;
168            // room
169            painter
170                .aabb(Aabb {
171                    min: (center - bldg_length).with_z(bldg_base),
172                    max: (center + bldg_length).with_z(bldg_base + height),
173                })
174                .fill(sandstone.clone());
175            // roof floor
176            painter
177                .aabb(Aabb {
178                    min: (center - bldg_length - 1 - f).with_z(bldg_base + height),
179                    max: (center + bldg_length + 1 + f).with_z(bldg_base + height + 1),
180                })
181                .fill(wood.clone());
182            painter
183                .aabb(Aabb {
184                    min: (center - bldg_length - f).with_z(bldg_base + height),
185                    max: (center + bldg_length + f).with_z(bldg_base + height + 1),
186                })
187                .fill(sandstone.clone());
188            // clear room
189            painter
190                .aabb(Aabb {
191                    min: (center - bldg_length + 1).with_z(bldg_base),
192                    max: (center + bldg_length - 1).with_z(bldg_base + height - 1),
193                })
194                .clear();
195
196            let clear_limit_1 = painter.aabb(Aabb {
197                min: Vec2::new(center.x - bldg_length, center.y - bldg_length + 1)
198                    .with_z(bldg_base),
199                max: Vec2::new(center.x + bldg_length, center.y + bldg_length - 1)
200                    .with_z(bldg_base + height),
201            });
202            let clear_limit_2 = painter.aabb(Aabb {
203                min: Vec2::new(center.x - bldg_length + 1, center.y - bldg_length)
204                    .with_z(bldg_base),
205                max: Vec2::new(center.x + bldg_length - 1, center.y + bldg_length)
206                    .with_z(bldg_base + height),
207            });
208            for c in 0..=4 {
209                let space = c * ((2 * carve) + 1);
210                painter
211                    .vault(
212                        Aabb {
213                            min: Vec2::new(
214                                center.x - bldg_length,
215                                center.y + space - bldg_length - carve + 1,
216                            )
217                            .with_z(bldg_base + (height / 2)),
218                            max: Vec2::new(
219                                center.x + bldg_length,
220                                center.y + space - bldg_length + carve - 1,
221                            )
222                            .with_z(bldg_base + height - 1),
223                        },
224                        Dir::X,
225                    )
226                    .intersect(clear_limit_1)
227                    .clear();
228                painter
229                    .vault(
230                        Aabb {
231                            min: Vec2::new(
232                                center.x + space - bldg_length - carve + 1,
233                                center.y - bldg_length,
234                            )
235                            .with_z(bldg_base + (height / 2)),
236                            max: Vec2::new(
237                                center.x + space - bldg_length + carve - 1,
238                                center.y + bldg_length,
239                            )
240                            .with_z(bldg_base + height - 1),
241                        },
242                        Dir::Y,
243                    )
244                    .intersect(clear_limit_2)
245                    .clear();
246
247                painter
248                    .aabb(Aabb {
249                        min: Vec2::new(
250                            center.x - bldg_length,
251                            center.y + space - bldg_length - carve,
252                        )
253                        .with_z(bldg_base),
254                        max: Vec2::new(
255                            center.x + bldg_length,
256                            center.y + space - bldg_length + carve,
257                        )
258                        .with_z(bldg_base + (height / 2)),
259                    })
260                    .intersect(clear_limit_1)
261                    .clear();
262                painter
263                    .aabb(Aabb {
264                        min: Vec2::new(
265                            center.x + space - bldg_length - carve,
266                            center.y - bldg_length,
267                        )
268                        .with_z(bldg_base),
269                        max: Vec2::new(
270                            center.x + space - bldg_length + carve,
271                            center.y + bldg_length,
272                        )
273                        .with_z(bldg_base + (height / 2)),
274                    })
275                    .intersect(clear_limit_2)
276                    .clear();
277            }
278            for dir in DIAGONALS {
279                let clear_pos = center + dir * bldg_length;
280                painter
281                    .aabb(Aabb {
282                        min: (clear_pos - 1).with_z(bldg_base),
283                        max: (clear_pos + 1).with_z(bldg_base + height - 1),
284                    })
285                    .clear();
286            }
287            for dir in DIAGONALS {
288                let lamp_pos = center + dir * (bldg_length - 1);
289                painter.sprite(lamp_pos.with_z(bldg_base), SpriteKind::StreetLamp);
290                if f == 4 {
291                    let lamp_pos = center + dir * bldg_length;
292                    painter.sprite(
293                        lamp_pos.with_z(bldg_base + height + 1),
294                        SpriteKind::StreetLamp,
295                    );
296
297                    let cargo_pos = center + (dir * ((bldg_length / 2) + 1));
298                    for dir in CARDINALS {
299                        let sprite_pos = cargo_pos + dir;
300                        let rows = (RandomField::new(0).get(sprite_pos.with_z(base)) % 3) as i32;
301                        for r in 0..rows {
302                            painter
303                                .aabb(Aabb {
304                                    min: (sprite_pos).with_z(bldg_base + height + 1 + r),
305                                    max: (sprite_pos + 1).with_z(bldg_base + height + 2 + r),
306                                })
307                                .fill(Fill::Block(Block::air(
308                                    match (RandomField::new(0).get(sprite_pos.with_z(base + r)) % 2)
309                                        as i32
310                                    {
311                                        0 => SpriteKind::Barrel,
312                                        _ => SpriteKind::CrateBlock,
313                                    },
314                                )));
315                            if r > 0 {
316                                painter.owned_resource_sprite(
317                                    sprite_pos.with_z(bldg_base + height + 2 + r),
318                                    SpriteKind::Crate,
319                                    0,
320                                );
321                            }
322                        }
323                    }
324                }
325            }
326            // docks
327            if f == 4 {
328                for dir in CARDINALS {
329                    let gangway_pos_1 = center + dir * (3 * (bldg_length / 2));
330                    let gangway_pos_2 = center + dir * ((3 * (bldg_length / 2)) - 4);
331                    let dock_pos = center + dir * ((bldg_length * 2) - 3);
332                    painter
333                        .aabb(Aabb {
334                            min: (gangway_pos_2 - 3).with_z(bldg_base + height - 1),
335                            max: (gangway_pos_2 + 3).with_z(bldg_base + height),
336                        })
337                        .fill(wood.clone());
338                    painter
339                        .aabb(Aabb {
340                            min: (gangway_pos_1 - 3).with_z(bldg_base + height),
341                            max: (gangway_pos_1 + 3).with_z(bldg_base + height + 1),
342                        })
343                        .fill(wood.clone());
344                    painter
345                        .cylinder(Aabb {
346                            min: (dock_pos - 4).with_z(bldg_base + height),
347                            max: (dock_pos + 4).with_z(bldg_base + height + 1),
348                        })
349                        .fill(wood.clone());
350                    painter
351                        .cylinder(Aabb {
352                            min: (dock_pos - 3).with_z(bldg_base + height - 1),
353                            max: (dock_pos + 3).with_z(bldg_base + height),
354                        })
355                        .fill(wood.clone());
356                }
357                // campfire
358                painter.spawn(
359                    EntityInfo::at(
360                        Vec2::new(center.x + bldg_length - 2, center.y)
361                            .with_z(bldg_base + height + 1)
362                            .map(|e| e as f32 + 0.5),
363                    )
364                    .into_special(SpecialEntity::Waypoint),
365                );
366            }
367            // stairs
368            painter
369                .aabb(Aabb {
370                    min: (center - (bldg_length / 2) - 1).with_z(bldg_base + height - 1),
371                    max: (center + (bldg_length / 2) + 1).with_z(bldg_base + height + 1),
372                })
373                .fill(wood.clone());
374            painter
375                .aabb(Aabb {
376                    min: (center - (bldg_length / 2)).with_z(bldg_base + height - 1),
377                    max: (center + (bldg_length / 2)).with_z(bldg_base + height + 1),
378                })
379                .clear();
380
381            for w in 0..((bldg_length / 2) + 2) {
382                painter
383                    .aabb(Aabb {
384                        min: Vec2::new(
385                            center.x - (bldg_length / 2) - 2 + (2 * w),
386                            center.y - (bldg_length / 2),
387                        )
388                        .with_z(bldg_base + w),
389                        max: Vec2::new(
390                            center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
391                            center.y + (bldg_length / 2),
392                        )
393                        .with_z(bldg_base + 1 + w),
394                    })
395                    .fill(wood.clone());
396                painter
397                    .aabb(Aabb {
398                        min: Vec2::new(
399                            center.x - (bldg_length / 2) - 2 + (2 * w),
400                            center.y - (bldg_length / 2),
401                        )
402                        .with_z(bldg_base - 1 + w),
403                        max: Vec2::new(
404                            center.x - (bldg_length / 2) - 2 + (2 * w) + 2,
405                            center.y + (bldg_length / 2),
406                        )
407                        .with_z(bldg_base + w),
408                    })
409                    .fill(sandstone.clone());
410            }
411        }
412    }
413}