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 = "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 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 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 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 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 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 for dir in CARDINALS {
176 let frame_pos = center + dir * (length - 1);
177 let clear_pos = center + dir * (length);
178
179 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 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 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 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 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 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 let random_index = (RandomField::new(0).get(center.with_z(base)) % 4) as usize;
244 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 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}