veloren_world/site2/plot/
savannah_hut.rs

1use super::*;
2use crate::{
3    Land,
4    site2::util::sprites::PainterSpriteExt,
5    util::{CARDINALS, DIAGONALS, RandomField, Sampler},
6};
7use common::terrain::{BlockKind, SpriteKind, sprite::Owned};
8use rand::prelude::*;
9use std::{f32::consts::TAU, sync::Arc};
10use vek::*;
11
12/// Represents house data generated by the `generate()` method
13pub struct SavannahHut {
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 SavannahHut {
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            bounds,
39            door_tile: door_tile_pos,
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 SavannahHut {
48    #[cfg(feature = "use-dyn-lib")]
49    const UPDATE_FN: &'static [u8] = b"render_savannahhut\0";
50
51    #[cfg_attr(feature = "be-dyn-lib", unsafe(export_name = "render_savannahhut"))]
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 sprite_fill = Fill::Sampling(Arc::new(|wpos| {
56            Some(match (RandomField::new(0).get(wpos)) % 25 {
57                0 => Block::air(SpriteKind::Bowl).with_attr(Owned(true)).unwrap(),
58                1 => Block::air(SpriteKind::VialEmpty)
59                    .with_attr(Owned(true))
60                    .unwrap(),
61                2 => Block::air(SpriteKind::Lantern),
62                3 => Block::air(SpriteKind::JugArabic),
63                4 => Block::air(SpriteKind::Crate)
64                    .with_attr(Owned(true))
65                    .unwrap(),
66                _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
67            })
68        }));
69        let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
70        let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
71        let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
72        let color = Fill::Sampling(Arc::new(|center| {
73            Some(match (RandomField::new(0).get(center)) % 7 {
74                0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
75                1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
76                2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
77                3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
78                4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
79                5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
80                _ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
81            })
82        }));
83        let length = (10 + RandomField::new(0).get(center.with_z(base)) % 6) as i32;
84        let height = 2 * length / 3;
85        let storeys = (1 + RandomField::new(0).get(center.with_z(base)) % 2) as i32;
86        let radius = length + (length / 3);
87        let reed_var = (1 + RandomField::new(0).get(center.with_z(base)) % 4) as f32;
88        let reed_parts = 36_f32 + reed_var;
89        let phi = TAU / reed_parts;
90        // roof cone
91        painter
92            .cone(Aabb {
93                min: (center - radius).with_z(base + (storeys * height) - (height / 2) + 1),
94                max: (center + radius)
95                    .with_z(base + (storeys * height) + (height / 2) - 1 + reed_var as i32),
96            })
97            .fill(reed.clone());
98        painter
99            .cone(Aabb {
100                min: (center - radius).with_z(base + (storeys * height) - (height / 2)),
101                max: (center + radius)
102                    .with_z(base + (storeys * height) + (height / 2) - 2 + reed_var as i32),
103            })
104            .clear();
105        // foundation
106        painter
107            .cylinder(Aabb {
108                min: (center - length).with_z(base - 3),
109                max: (center + length + 1).with_z(base - 2),
110            })
111            .fill(clay.clone());
112        painter
113            .cylinder(Aabb {
114                min: (center - length - 1).with_z(base - 4),
115                max: (center + length + 2).with_z(base - 3),
116            })
117            .fill(clay.clone());
118        painter
119            .cylinder(Aabb {
120                min: (center - length - 2).with_z(base - 5),
121                max: (center + length + 3).with_z(base - 4),
122            })
123            .fill(clay.clone());
124        painter
125            .cylinder(Aabb {
126                min: (center - length - 3).with_z(base - height),
127                max: (center + length + 4).with_z(base - 5),
128            })
129            .fill(clay.clone());
130        // room
131        for s in 0..storeys {
132            let room = painter.cylinder(Aabb {
133                min: (center - length + 2 + s).with_z(base - 2 + (s * height)),
134                max: (center + 1 + length - 2 - s).with_z(base + height + (s * height)),
135            });
136            room.fill(clay.clone());
137            // decor inlays
138            for dir in DIAGONALS {
139                let decor_pos = center + dir * (length - 2 - s);
140                let decor = painter
141                    .line(
142                        center.with_z(base - 1 + (s * (height + 2))),
143                        decor_pos.with_z(base - 1 + (s * (height + 2))),
144                        5.0,
145                    )
146                    .intersect(room);
147                decor.fill(color.clone());
148                painter
149                    .line(
150                        center.with_z(base - 1 + (s * (height + 2))),
151                        decor_pos.with_z(base - 1 + (s * (height + 2))),
152                        4.0,
153                    )
154                    .intersect(decor)
155                    .fill(clay.clone());
156            }
157        }
158        // clear rooms
159        painter
160            .cylinder(Aabb {
161                min: (center - length + 4).with_z(base - 2),
162                max: (center + 1 + length - 4).with_z(base + (storeys * height)),
163            })
164            .clear();
165        // wood decor
166        painter
167            .cylinder(Aabb {
168                min: (center - length + 4).with_z(base - 1),
169                max: (center + 1 + length - 4).with_z(base),
170            })
171            .fill(wood_dark.clone());
172        painter
173            .cylinder(Aabb {
174                min: (center - length + 4).with_z(base),
175                max: (center + 1 + length - 4).with_z(base + 1),
176            })
177            .fill(sprite_fill);
178
179        painter
180            .cylinder(Aabb {
181                min: (center - length + 4).with_z(base + (storeys * height) - 1),
182                max: (center + 1 + length - 4).with_z(base + (storeys * height) + 1),
183            })
184            .fill(wood_dark);
185
186        for s in 0..storeys {
187            // entries, windows
188            for dir in CARDINALS {
189                let frame_pos = center + dir * (length - 2 - s);
190                let clear_pos = center + dir * (length + 2 - s);
191
192                painter
193                    .line(
194                        center.with_z(base - 1 + (s * (height + 2))),
195                        frame_pos.with_z(base - 1 + (s * (height + 2))),
196                        3.0,
197                    )
198                    .fill(color.clone());
199                painter
200                    .line(
201                        center.with_z(base - 1 + (s * (height + 2))),
202                        clear_pos.with_z(base - 1 + (s * (height + 2))),
203                        2.0,
204                    )
205                    .clear();
206            }
207        }
208        // re clear room
209        painter
210            .cylinder(Aabb {
211                min: (center - length + 5).with_z(base - 2),
212                max: (center + 1 + length - 5).with_z(base + (storeys * height) + 1),
213            })
214            .clear();
215        // floor
216        painter
217            .cylinder(Aabb {
218                min: (center - (length / 2) - 1).with_z(base - 3),
219                max: (center + (length / 2) + 1).with_z(base - 2),
220            })
221            .fill(color);
222        painter
223            .cylinder(Aabb {
224                min: (center - (length / 2) + 1).with_z(base - 3),
225                max: (center + (length / 2) - 1).with_z(base - 2),
226            })
227            .fill(clay);
228
229        // furniture
230        let mut sprites = vec![
231            SpriteKind::CushionArabic,
232            SpriteKind::TableArabicSmall,
233            SpriteKind::DecorSetArabic,
234            SpriteKind::Bowl,
235            SpriteKind::VialEmpty,
236            SpriteKind::JugArabic,
237            SpriteKind::JugAndBowlArabic,
238            SpriteKind::SepareArabic,
239        ];
240
241        let rows = if length > 12 { 2 } else { 1 };
242        'outer: for d in 0..rows {
243            for dir in DIAGONALS {
244                if sprites.is_empty() {
245                    break 'outer;
246                }
247                let position = center + dir * (length - (9 + (d * 3)));
248                let sprite = sprites.swap_remove(
249                    RandomField::new(0).get(position.with_z(base)) as usize % sprites.len(),
250                );
251                painter.owned_resource_sprite(position.with_z(base - 2), sprite, 0);
252            }
253        }
254
255        // draws a random index
256        let random_index = (RandomField::new(0).get(center.with_z(base)) % 4) as usize;
257        // add bed at random diagonal
258        let dir = *Dir::ALL.get(random_index).unwrap();
259        let diagonal = dir.diagonal();
260        let bed_pos = center + diagonal * (length - 9);
261        painter.bed_savannah(bed_pos.with_z(base - 2), dir);
262
263        // reed roof lines
264        for n in 1..=reed_parts as i32 {
265            let pos = Vec2::new(
266                center.x + ((radius as f32) * ((n as f32 * phi).cos())) as i32,
267                center.y + ((radius as f32) * ((n as f32 * phi).sin())) as i32,
268            );
269            painter
270                .line(
271                    pos.with_z(base + (storeys * height) - (height / 2)),
272                    center.with_z(base + (storeys * height) + (height / 2) + reed_var as i32),
273                    1.0,
274                )
275                .fill(reed.clone());
276        }
277    }
278}