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
12pub struct SavannahHut {
14 pub door_tile: Vec2<i32>,
16 bounds: Aabr<i32>,
18 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 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 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 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 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 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 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 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 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 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 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 let random_index = (RandomField::new(0).get(center.with_z(base)) % 4) as usize;
261 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 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}