veloren_world/site2/plot/
coastal_house.rs

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