veloren_world/site/plot/
savannah_guard_hut.rs

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