veloren_world/site2/plot/
coastal_house.rs

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