Skip to main content

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