veloren_world/site2/plot/
savannah_hut.rs

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