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