Skip to main content

veloren_world/site/plot/
savannah_hut.rs

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