Skip to main content

veloren_world/site/plot/
coastal_house.rs

1use super::*;
2use crate::{
3    Land,
4    site::{generation::wall_staircase, util::sprites::PainterSpriteExt},
5    util::{CARDINALS, NEIGHBORS, RandomField, Sampler},
6};
7use common::terrain::{BlockKind, SpriteKind};
8use rand::prelude::*;
9use std::sync::Arc;
10use strum::IntoEnumIterator;
11use vek::*;
12
13/// Represents house data generated by the `generate()` method
14pub struct CoastalHouse {
15    /// Tile position of the door tile
16    pub door_tile: Vec2<i32>,
17    /// Axis aligned bounding region for the house
18    bounds: Aabr<i32>,
19    /// Approximate altitude of the door tile
20    pub(crate) alt: i32,
21}
22
23impl CoastalHouse {
24    pub fn generate(
25        land: &Land,
26        _rng: &mut impl Rng,
27        site: &Site,
28        door_tile: Vec2<i32>,
29        door_dir: Vec2<i32>,
30        tile_aabr: Aabr<i32>,
31        alt: Option<i32>,
32    ) -> Self {
33        let door_tile_pos = site.tile_center_wpos(door_tile);
34        let bounds = Aabr {
35            min: site.tile_wpos(tile_aabr.min),
36            max: site.tile_wpos(tile_aabr.max),
37        };
38        Self {
39            door_tile: door_tile_pos,
40            bounds,
41            alt: alt.unwrap_or_else(|| {
42                land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
43            }) + 2,
44        }
45    }
46}
47
48impl Structure for CoastalHouse {
49    #[cfg(feature = "dyn-lib")]
50    #[unsafe(export_name = "as_dyn_structure_coastalhouse")]
51    fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
52        Some((Self::as_dyn_impl(self), "as_dyn_structure_coastalhouse"))
53    }
54
55    fn spawn_rules_inner(
56        &self,
57        spawn_rules: &mut SpawnRules,
58        _land: &Land,
59        _wpos: Vec2<i32>,
60        weight: f32,
61    ) {
62        spawn_rules.prefer_alt(self.alt as f32, weight);
63    }
64
65    fn door_tile(&self) -> Option<Vec2<i32>> { Some(self.door_tile) }
66
67    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
68        let base = self.alt + 1;
69        let center = self.bounds.center();
70        let white = Fill::Sampling(Arc::new(|center| {
71            Some(match (RandomField::new(0).get(center)) % 37 {
72                0..=8 => Block::new(BlockKind::Rock, Rgb::new(251, 251, 227)),
73                9..=17 => Block::new(BlockKind::Rock, Rgb::new(245, 245, 229)),
74                18..=26 => Block::new(BlockKind::Rock, Rgb::new(250, 243, 221)),
75                27..=35 => Block::new(BlockKind::Rock, Rgb::new(240, 240, 230)),
76                _ => Block::new(BlockKind::Rock, Rgb::new(255, 244, 193)),
77            })
78        }));
79        let blue_broken = Fill::Sampling(Arc::new(|center| {
80            Some(match (RandomField::new(0).get(center)) % 20 {
81                0 => Block::new(BlockKind::Rock, Rgb::new(30, 187, 235)),
82                _ => Block::new(BlockKind::Rock, Rgb::new(11, 146, 187)),
83            })
84        }));
85        let length = (14 + RandomField::new(0).get(center.with_z(base)) % 3) as i32;
86        let width = (12 + RandomField::new(0).get((center - 1).with_z(base)) % 3) as i32;
87        let height = (12 + RandomField::new(0).get((center + 1).with_z(base)) % 4) as i32;
88        let storeys = (1 + RandomField::new(0).get(center.with_z(base)) % 2) as i32;
89
90        // fence, blue gates
91        painter
92            .aabb(Aabb {
93                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 2),
94                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
95            })
96            .fill(blue_broken.clone());
97
98        for dir in CARDINALS {
99            let frame_pos = Vec2::new(
100                center.x + dir.x * (length + 5),
101                center.y + dir.y * (width + 5),
102            );
103            painter
104                .line(center.with_z(base - 1), frame_pos.with_z(base - 1), 3.0)
105                .fill(blue_broken.clone());
106        }
107        // foundation
108        painter
109            .aabb(Aabb {
110                min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - height),
111                max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 2),
112            })
113            .fill(white.clone());
114        for f in 0..8 {
115            painter
116                .aabb(Aabb {
117                    min: Vec2::new(center.x - length - 7 - f, center.y - width - 7 - f)
118                        .with_z(base - 3 - f),
119                    max: Vec2::new(center.x + length + 8 + f, center.y + width + 8 + f)
120                        .with_z(base - 2 - f),
121                })
122                .fill(white.clone());
123        }
124        // clear yard
125        painter
126            .aabb(Aabb {
127                min: Vec2::new(center.x - length - 5, center.y - width - 5).with_z(base - 2),
128                max: Vec2::new(center.x + length + 6, center.y + width + 6).with_z(base + height),
129            })
130            .clear();
131        // clear entries
132        for dir in CARDINALS {
133            let clear_pos = Vec2::new(
134                center.x + dir.x * (length + 7),
135                center.y + dir.y * (width + 7),
136            );
137            painter
138                .line(center.with_z(base - 1), clear_pos.with_z(base - 1), 2.0)
139                .clear();
140        }
141        for s in 0..storeys {
142            // roof terrace
143            painter
144                .aabb(Aabb {
145                    min: Vec2::new(
146                        center.x - length - 3 + (2 * s),
147                        center.y - width - 3 + (2 * s),
148                    )
149                    .with_z(base - 3 + height + (s * height)),
150                    max: Vec2::new(
151                        center.x + length + 2 - (2 * s),
152                        center.y + width + 2 - (2 * s),
153                    )
154                    .with_z(base - 2 + height + (s * height)),
155                })
156                .fill(white.clone());
157            painter
158                .aabb(Aabb {
159                    min: Vec2::new(
160                        center.x - length - 3 + (2 * s),
161                        center.y - width - 3 + (2 * s),
162                    )
163                    .with_z(base - 2 + height + (s * height)),
164                    max: Vec2::new(
165                        center.x + length + 2 - (2 * s),
166                        center.y + width + 2 - (2 * s),
167                    )
168                    .with_z(base - 1 + height + (s * height)),
169                })
170                .fill(blue_broken.clone());
171            painter
172                .aabb(Aabb {
173                    min: Vec2::new(
174                        center.x - length - 2 + (2 * s),
175                        center.y - width - 2 + (2 * s),
176                    )
177                    .with_z(base - 2 + height + (s * height)),
178                    max: Vec2::new(
179                        center.x + length + 1 - (2 * s),
180                        center.y + width + 1 - (2 * s),
181                    )
182                    .with_z(base - 1 + height + (s * height)),
183                })
184                .clear();
185            // room
186            painter
187                .aabb(Aabb {
188                    min: Vec2::new(center.x - length + (2 * s), center.y - width + (2 * s))
189                        .with_z(base - 2 + (s * height)),
190                    max: Vec2::new(center.x + length - (2 * s), center.y + width - (2 * s))
191                        .with_z(base - 1 + (s * height)),
192                })
193                .fill(blue_broken.clone());
194            painter
195                .aabb(Aabb {
196                    min: Vec2::new(
197                        center.x - length + 1 + (2 * s),
198                        center.y - width + 1 + (2 * s),
199                    )
200                    .with_z(base - 2 + (s * height)),
201                    max: Vec2::new(
202                        center.x + length - 1 - (2 * s),
203                        center.y + width - 1 - (2 * s),
204                    )
205                    .with_z(base - 1 + height - 1 + (s * height)),
206                })
207                .fill(white.clone());
208
209            // entries
210            painter
211                .line(
212                    Vec2::new(center.x, center.y + 1 - width + (2 * s))
213                        .with_z(base - 1 + (s * height)),
214                    Vec2::new(center.x, center.y - 2 + width - (2 * s))
215                        .with_z(base - 1 + (s * height)),
216                    3.0,
217                )
218                .fill(blue_broken.clone());
219            painter
220                .line(
221                    Vec2::new(center.x, center.y - width + (2 * s)).with_z(base - 1 + (s * height)),
222                    Vec2::new(center.x, center.y + width - (2 * s)).with_z(base - 1 + (s * height)),
223                    2.0,
224                )
225                .clear();
226            painter
227                .line(
228                    Vec2::new(center.x + 1 - length + (2 * s), center.y)
229                        .with_z(base - 1 + (s * height)),
230                    Vec2::new(center.x - 2 + length - (2 * s), center.y)
231                        .with_z(base - 1 + (s * height)),
232                    3.0,
233                )
234                .fill(blue_broken.clone());
235            painter
236                .line(
237                    Vec2::new(center.x - length + (2 * s), center.y)
238                        .with_z(base - 1 + (s * height)),
239                    Vec2::new(center.x + length - (2 * s), center.y)
240                        .with_z(base - 1 + (s * height)),
241                    2.0,
242                )
243                .clear();
244            // windows length
245            painter
246                .line(
247                    Vec2::new(center.x - (length / 3), center.y + 1 - width + (2 * s))
248                        .with_z(base - 1 + (s * height) + (height / 2)),
249                    Vec2::new(center.x - (length / 3), center.y - 2 + width - (2 * s))
250                        .with_z(base - 1 + (s * height) + (height / 2)),
251                    3.0,
252                )
253                .fill(blue_broken.clone());
254            painter
255                .line(
256                    Vec2::new(center.x - (length / 3), center.y - width - 2 + (2 * s))
257                        .with_z(base - 1 + (s * height) + (height / 2)),
258                    Vec2::new(center.x - (length / 3), center.y + width + 2 - (2 * s))
259                        .with_z(base - 1 + (s * height) + (height / 2)),
260                    2.0,
261                )
262                .clear();
263
264            painter
265                .line(
266                    Vec2::new(center.x + (length / 3), center.y + 1 - width + (2 * s))
267                        .with_z(base - 1 + (s * height) + (height / 2)),
268                    Vec2::new(center.x + (length / 3), center.y - 2 + width - (2 * s))
269                        .with_z(base - 1 + (s * height) + (height / 2)),
270                    3.0,
271                )
272                .fill(blue_broken.clone());
273            painter
274                .line(
275                    Vec2::new(center.x + (length / 3), center.y - width - 2 + (2 * s))
276                        .with_z(base - 1 + (s * height) + (height / 2)),
277                    Vec2::new(center.x + (length / 3), center.y + width + 2 - (2 * s))
278                        .with_z(base - 1 + (s * height) + (height / 2)),
279                    2.0,
280                )
281                .clear();
282
283            // windows width
284            painter
285                .line(
286                    Vec2::new(center.x + 1 - length + (2 * s), center.y)
287                        .with_z(base - 1 + (s * height) + (height / 2)),
288                    Vec2::new(center.x - 2 + length - (2 * s), center.y)
289                        .with_z(base - 1 + (s * height) + (height / 2)),
290                    3.0,
291                )
292                .fill(blue_broken.clone());
293            painter
294                .line(
295                    Vec2::new(center.x - length - 2 + (2 * s), center.y)
296                        .with_z(base - 1 + (s * height) + (height / 2)),
297                    Vec2::new(center.x + length + 2 - (2 * s), center.y)
298                        .with_z(base - 1 + (s * height) + (height / 2)),
299                    2.0,
300                )
301                .clear();
302
303            // clear room
304            painter
305                .aabb(Aabb {
306                    min: Vec2::new(
307                        center.x - length + 2 + (2 * s),
308                        center.y - width + 2 + (2 * s),
309                    )
310                    .with_z(base - 2 + (s * height)),
311                    max: Vec2::new(
312                        center.x + length - 2 - (2 * s),
313                        center.y + width - 2 - (2 * s),
314                    )
315                    .with_z(base - 2 + height - 1 + (s * height)),
316                })
317                .clear();
318
319            // room floors
320            painter
321                .aabb(Aabb {
322                    min: Vec2::new(
323                        center.x - length + 5 + (2 * s),
324                        center.y - width + 5 + (2 * s),
325                    )
326                    .with_z(base - 3 + (s * height)),
327                    max: Vec2::new(
328                        center.x + length - 5 - (2 * s),
329                        center.y + width - 5 - (2 * s),
330                    )
331                    .with_z(base - 2 + (s * height)),
332                })
333                .fill(blue_broken.clone());
334            painter
335                .aabb(Aabb {
336                    min: Vec2::new(
337                        center.x - length + 6 + (2 * s),
338                        center.y - width + 6 + (2 * s),
339                    )
340                    .with_z(base - 3 + (s * height)),
341                    max: Vec2::new(
342                        center.x + length - 6 - (2 * s),
343                        center.y + width - 6 - (2 * s),
344                    )
345                    .with_z(base - 2 + (s * height)),
346                })
347                .fill(white.clone());
348            // furniture
349            let mut sprites = vec![
350                SpriteKind::Bowl,
351                SpriteKind::VialEmpty,
352                SpriteKind::FountainArabic,
353                SpriteKind::Crate,
354                SpriteKind::Lantern,
355            ];
356            'outer: for dir in NEIGHBORS {
357                let furniture_pos = Vec2::new(
358                    center.x + dir.x * ((length / 2) + 1),
359                    center.y + dir.y * ((width / 2) + 1),
360                );
361                if sprites.is_empty() {
362                    break 'outer;
363                }
364                let sprite = sprites.swap_remove(
365                    RandomField::new(0).get(furniture_pos.with_z(base)) as usize % sprites.len(),
366                );
367                painter.owned_resource_sprite(
368                    furniture_pos.with_z(base - 2 + (s * height)),
369                    sprite,
370                    0,
371                );
372            }
373
374            // clear floor center if stairs
375            if storeys > 1 {
376                painter
377                    .cylinder(Aabb {
378                        min: (center - 6).with_z(base - 2 + (s * height)),
379                        max: (center + 6).with_z(base + (s * height)),
380                    })
381                    .clear();
382            };
383
384            // draws a random index based of base and currently storey
385            let random_index_1 = (RandomField::new(0).get(center.with_z(base + s)) % 4) as usize;
386            let random_index_2 = 3 - random_index_1;
387            // add beds and tables at random corners
388            for (d, dir) in Dir2::iter().enumerate() {
389                let diagonal = dir.diagonal();
390                let bed_pos = center + diagonal * ((length / 2) - 2);
391                let table_pos = Vec2::new(
392                    center.x + diagonal.x * ((length / 2) - 1),
393                    center.y + diagonal.y * ((width / 2) - 2),
394                );
395                let alt = base - 2 + (s * height);
396                if d == random_index_1 {
397                    painter.bed_coastal(bed_pos.with_z(alt), dir);
398                } else if d == random_index_2 {
399                    painter.rotated_sprite(table_pos.with_z(alt), SpriteKind::TableCoastalLarge, 2);
400                    painter.sprite(table_pos.with_z(alt + 1), SpriteKind::JugAndCupsCoastal);
401
402                    for dir in Dir2::iter() {
403                        let vec = dir.to_vec2();
404                        let bench_pos = Vec2::new(table_pos.x + vec.x * 2, table_pos.y + vec.y);
405                        painter.rotated_sprite(
406                            bench_pos.with_z(alt),
407                            SpriteKind::BenchCoastal,
408                            dir.opposite().sprite_ori(),
409                        );
410                    }
411                }
412            }
413
414            // wall lamps
415            for d in 0..2 {
416                let door_lamp_pos = Vec2::new(
417                    center.x - length + 2 + (2 * s) + (d * ((2 * (length - (2 * s))) - 5)),
418                    center.y,
419                )
420                .with_z(base + 1 + (s * height));
421                painter.rotated_sprite(
422                    door_lamp_pos,
423                    SpriteKind::WallLampSmall,
424                    2 + ((d * 4) as u8),
425                );
426
427                let lamp_pos = Vec2::new(
428                    center.x,
429                    center.y - width + 2 + (2 * s) + (d * ((2 * (width - (2 * s))) - 5)),
430                )
431                .with_z(base + 1 + (s * height));
432                painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 4 - ((d * 4) as u8));
433            }
434            for d in 0..2 {
435                let door_lamp_pos = Vec2::new(
436                    center.x - length - 1 + (2 * s) + (d * ((2 * (length - (2 * s))) + 1)),
437                    center.y,
438                )
439                .with_z(base + 1 + (s * height));
440                painter.rotated_sprite(
441                    door_lamp_pos,
442                    SpriteKind::WallLampSmall,
443                    6 + ((d * 4) as u8),
444                );
445
446                let lamp_pos = Vec2::new(
447                    center.x,
448                    center.y - width - 1 + (2 * s) + (d * ((2 * (width - (2 * s))) + 1)),
449                )
450                .with_z(base + 1 + (s * height));
451                painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 8 - ((d * 4) as u8));
452            }
453        }
454        let top_limit = painter.aabb(Aabb {
455            min: Vec2::new(center.x - length, center.y - width)
456                .with_z(base + (storeys * height) - 2),
457            max: Vec2::new(center.x + length, center.y + width)
458                .with_z(base - 2 + (storeys * height) + (height / 2)),
459        });
460        painter
461            .superquadric(
462                Aabb {
463                    min: Vec2::new(center.x - length - 1, center.y - width - 1)
464                        .with_z(base + (storeys * height) - (height / 2)),
465                    max: Vec2::new(center.x + length, center.y + width)
466                        .with_z(base - 2 + (storeys * height) + (height / 2)),
467                },
468                1.5,
469            )
470            .intersect(top_limit)
471            .fill(white.clone());
472        if storeys > 1 {
473            // stairway1 stairs
474            let stair_radius1 = 3.0;
475            let stairs_clear1 = painter.cylinder(Aabb {
476                min: (center - 1 - stair_radius1 as i32).with_z(base - 2),
477                max: (center + 2 + stair_radius1 as i32)
478                    .with_z(base + ((storeys - 1) * height) - 2),
479            });
480            let stairs_clear2 = painter.cylinder(Aabb {
481                min: (center - 2 - stair_radius1 as i32).with_z(base - 2),
482                max: (center + 3 + stair_radius1 as i32)
483                    .with_z(base + ((storeys - 1) * height) - 2),
484            });
485            stairs_clear1.clear();
486            painter
487                .cylinder(Aabb {
488                    min: (center - 1).with_z(base - 2),
489                    max: (center + 2).with_z(base + ((storeys - 1) * height) - 2),
490                })
491                .fill(white.clone());
492
493            stairs_clear2
494                .sample(wall_staircase(
495                    center.with_z(base + ((storeys - 1) * height) - 2),
496                    stair_radius1,
497                    (height / 2) as f32,
498                ))
499                .fill(white);
500        }
501    }
502}