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
15pub struct SavannahGuardHut {
17 pub door_tile: Vec2<i32>,
19 bounds: Aabr<i32>,
21 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 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 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 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 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 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 for dir in CARDINALS {
170 let frame_pos = center + dir * (length - 1);
171 let clear_pos = center + dir * (length);
172
173 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 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 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 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 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 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 let random_index = (RandomField::new(0).get(center.with_z(base)) % 4) as usize;
238 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 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}