1use super::*;
2use crate::{
3 Land,
4 site::{generation::wall_staircase, util::sprites::PainterSpriteExt},
5 util::{CARDINALS, NEIGHBORS, RandomField, Sampler},
6};
7use common::terrain::{BlockKind, SpriteKind};
8use rand::prelude::*;
9use std::sync::Arc;
10use strum::IntoEnumIterator;
11use vek::*;
12
13pub struct CoastalHouse {
15 pub door_tile: Vec2<i32>,
17 bounds: Aabr<i32>,
19 pub(crate) alt: i32,
21}
22
23impl CoastalHouse {
24 pub fn generate(
25 land: &Land,
26 _rng: &mut impl Rng,
27 site: &Site,
28 door_tile: Vec2<i32>,
29 door_dir: Vec2<i32>,
30 tile_aabr: Aabr<i32>,
31 alt: Option<i32>,
32 ) -> Self {
33 let door_tile_pos = site.tile_center_wpos(door_tile);
34 let bounds = Aabr {
35 min: site.tile_wpos(tile_aabr.min),
36 max: site.tile_wpos(tile_aabr.max),
37 };
38 Self {
39 door_tile: door_tile_pos,
40 bounds,
41 alt: alt.unwrap_or_else(|| {
42 land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32
43 }) + 2,
44 }
45 }
46}
47
48impl Structure for CoastalHouse {
49 #[cfg(feature = "dyn-lib")]
50 #[unsafe(export_name = "as_dyn_structure_coastalhouse")]
51 fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
52 Some((Self::as_dyn_impl(self), "as_dyn_structure_coastalhouse"))
53 }
54
55 fn spawn_rules_inner(
56 &self,
57 spawn_rules: &mut SpawnRules,
58 _land: &Land,
59 _wpos: Vec2<i32>,
60 weight: f32,
61 ) {
62 spawn_rules.prefer_alt(self.alt as f32, weight);
63 }
64
65 fn door_tile(&self) -> Option<Vec2<i32>> { Some(self.door_tile) }
66
67 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
68 let base = self.alt + 1;
69 let center = self.bounds.center();
70 let white = Fill::Sampling(Arc::new(|center| {
71 Some(match (RandomField::new(0).get(center)) % 37 {
72 0..=8 => Block::new(BlockKind::Rock, Rgb::new(251, 251, 227)),
73 9..=17 => Block::new(BlockKind::Rock, Rgb::new(245, 245, 229)),
74 18..=26 => Block::new(BlockKind::Rock, Rgb::new(250, 243, 221)),
75 27..=35 => Block::new(BlockKind::Rock, Rgb::new(240, 240, 230)),
76 _ => Block::new(BlockKind::Rock, Rgb::new(255, 244, 193)),
77 })
78 }));
79 let blue_broken = Fill::Sampling(Arc::new(|center| {
80 Some(match (RandomField::new(0).get(center)) % 20 {
81 0 => Block::new(BlockKind::Rock, Rgb::new(30, 187, 235)),
82 _ => Block::new(BlockKind::Rock, Rgb::new(11, 146, 187)),
83 })
84 }));
85 let length = (14 + RandomField::new(0).get(center.with_z(base)) % 3) as i32;
86 let width = (12 + RandomField::new(0).get((center - 1).with_z(base)) % 3) as i32;
87 let height = (12 + RandomField::new(0).get((center + 1).with_z(base)) % 4) as i32;
88 let storeys = (1 + RandomField::new(0).get(center.with_z(base)) % 2) as i32;
89
90 painter
92 .aabb(Aabb {
93 min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - 2),
94 max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 1),
95 })
96 .fill(blue_broken.clone());
97
98 for dir in CARDINALS {
99 let frame_pos = Vec2::new(
100 center.x + dir.x * (length + 5),
101 center.y + dir.y * (width + 5),
102 );
103 painter
104 .line(center.with_z(base - 1), frame_pos.with_z(base - 1), 3.0)
105 .fill(blue_broken.clone());
106 }
107 painter
109 .aabb(Aabb {
110 min: Vec2::new(center.x - length - 6, center.y - width - 6).with_z(base - height),
111 max: Vec2::new(center.x + length + 7, center.y + width + 7).with_z(base - 2),
112 })
113 .fill(white.clone());
114 for f in 0..8 {
115 painter
116 .aabb(Aabb {
117 min: Vec2::new(center.x - length - 7 - f, center.y - width - 7 - f)
118 .with_z(base - 3 - f),
119 max: Vec2::new(center.x + length + 8 + f, center.y + width + 8 + f)
120 .with_z(base - 2 - f),
121 })
122 .fill(white.clone());
123 }
124 painter
126 .aabb(Aabb {
127 min: Vec2::new(center.x - length - 5, center.y - width - 5).with_z(base - 2),
128 max: Vec2::new(center.x + length + 6, center.y + width + 6).with_z(base + height),
129 })
130 .clear();
131 for dir in CARDINALS {
133 let clear_pos = Vec2::new(
134 center.x + dir.x * (length + 7),
135 center.y + dir.y * (width + 7),
136 );
137 painter
138 .line(center.with_z(base - 1), clear_pos.with_z(base - 1), 2.0)
139 .clear();
140 }
141 for s in 0..storeys {
142 painter
144 .aabb(Aabb {
145 min: Vec2::new(
146 center.x - length - 3 + (2 * s),
147 center.y - width - 3 + (2 * s),
148 )
149 .with_z(base - 3 + height + (s * height)),
150 max: Vec2::new(
151 center.x + length + 2 - (2 * s),
152 center.y + width + 2 - (2 * s),
153 )
154 .with_z(base - 2 + height + (s * height)),
155 })
156 .fill(white.clone());
157 painter
158 .aabb(Aabb {
159 min: Vec2::new(
160 center.x - length - 3 + (2 * s),
161 center.y - width - 3 + (2 * s),
162 )
163 .with_z(base - 2 + height + (s * height)),
164 max: Vec2::new(
165 center.x + length + 2 - (2 * s),
166 center.y + width + 2 - (2 * s),
167 )
168 .with_z(base - 1 + height + (s * height)),
169 })
170 .fill(blue_broken.clone());
171 painter
172 .aabb(Aabb {
173 min: Vec2::new(
174 center.x - length - 2 + (2 * s),
175 center.y - width - 2 + (2 * s),
176 )
177 .with_z(base - 2 + height + (s * height)),
178 max: Vec2::new(
179 center.x + length + 1 - (2 * s),
180 center.y + width + 1 - (2 * s),
181 )
182 .with_z(base - 1 + height + (s * height)),
183 })
184 .clear();
185 painter
187 .aabb(Aabb {
188 min: Vec2::new(center.x - length + (2 * s), center.y - width + (2 * s))
189 .with_z(base - 2 + (s * height)),
190 max: Vec2::new(center.x + length - (2 * s), center.y + width - (2 * s))
191 .with_z(base - 1 + (s * height)),
192 })
193 .fill(blue_broken.clone());
194 painter
195 .aabb(Aabb {
196 min: Vec2::new(
197 center.x - length + 1 + (2 * s),
198 center.y - width + 1 + (2 * s),
199 )
200 .with_z(base - 2 + (s * height)),
201 max: Vec2::new(
202 center.x + length - 1 - (2 * s),
203 center.y + width - 1 - (2 * s),
204 )
205 .with_z(base - 1 + height - 1 + (s * height)),
206 })
207 .fill(white.clone());
208
209 painter
211 .line(
212 Vec2::new(center.x, center.y + 1 - width + (2 * s))
213 .with_z(base - 1 + (s * height)),
214 Vec2::new(center.x, center.y - 2 + width - (2 * s))
215 .with_z(base - 1 + (s * height)),
216 3.0,
217 )
218 .fill(blue_broken.clone());
219 painter
220 .line(
221 Vec2::new(center.x, center.y - width + (2 * s)).with_z(base - 1 + (s * height)),
222 Vec2::new(center.x, center.y + width - (2 * s)).with_z(base - 1 + (s * height)),
223 2.0,
224 )
225 .clear();
226 painter
227 .line(
228 Vec2::new(center.x + 1 - length + (2 * s), center.y)
229 .with_z(base - 1 + (s * height)),
230 Vec2::new(center.x - 2 + length - (2 * s), center.y)
231 .with_z(base - 1 + (s * height)),
232 3.0,
233 )
234 .fill(blue_broken.clone());
235 painter
236 .line(
237 Vec2::new(center.x - length + (2 * s), center.y)
238 .with_z(base - 1 + (s * height)),
239 Vec2::new(center.x + length - (2 * s), center.y)
240 .with_z(base - 1 + (s * height)),
241 2.0,
242 )
243 .clear();
244 painter
246 .line(
247 Vec2::new(center.x - (length / 3), center.y + 1 - width + (2 * s))
248 .with_z(base - 1 + (s * height) + (height / 2)),
249 Vec2::new(center.x - (length / 3), center.y - 2 + width - (2 * s))
250 .with_z(base - 1 + (s * height) + (height / 2)),
251 3.0,
252 )
253 .fill(blue_broken.clone());
254 painter
255 .line(
256 Vec2::new(center.x - (length / 3), center.y - width - 2 + (2 * s))
257 .with_z(base - 1 + (s * height) + (height / 2)),
258 Vec2::new(center.x - (length / 3), center.y + width + 2 - (2 * s))
259 .with_z(base - 1 + (s * height) + (height / 2)),
260 2.0,
261 )
262 .clear();
263
264 painter
265 .line(
266 Vec2::new(center.x + (length / 3), center.y + 1 - width + (2 * s))
267 .with_z(base - 1 + (s * height) + (height / 2)),
268 Vec2::new(center.x + (length / 3), center.y - 2 + width - (2 * s))
269 .with_z(base - 1 + (s * height) + (height / 2)),
270 3.0,
271 )
272 .fill(blue_broken.clone());
273 painter
274 .line(
275 Vec2::new(center.x + (length / 3), center.y - width - 2 + (2 * s))
276 .with_z(base - 1 + (s * height) + (height / 2)),
277 Vec2::new(center.x + (length / 3), center.y + width + 2 - (2 * s))
278 .with_z(base - 1 + (s * height) + (height / 2)),
279 2.0,
280 )
281 .clear();
282
283 painter
285 .line(
286 Vec2::new(center.x + 1 - length + (2 * s), center.y)
287 .with_z(base - 1 + (s * height) + (height / 2)),
288 Vec2::new(center.x - 2 + length - (2 * s), center.y)
289 .with_z(base - 1 + (s * height) + (height / 2)),
290 3.0,
291 )
292 .fill(blue_broken.clone());
293 painter
294 .line(
295 Vec2::new(center.x - length - 2 + (2 * s), center.y)
296 .with_z(base - 1 + (s * height) + (height / 2)),
297 Vec2::new(center.x + length + 2 - (2 * s), center.y)
298 .with_z(base - 1 + (s * height) + (height / 2)),
299 2.0,
300 )
301 .clear();
302
303 painter
305 .aabb(Aabb {
306 min: Vec2::new(
307 center.x - length + 2 + (2 * s),
308 center.y - width + 2 + (2 * s),
309 )
310 .with_z(base - 2 + (s * height)),
311 max: Vec2::new(
312 center.x + length - 2 - (2 * s),
313 center.y + width - 2 - (2 * s),
314 )
315 .with_z(base - 2 + height - 1 + (s * height)),
316 })
317 .clear();
318
319 painter
321 .aabb(Aabb {
322 min: Vec2::new(
323 center.x - length + 5 + (2 * s),
324 center.y - width + 5 + (2 * s),
325 )
326 .with_z(base - 3 + (s * height)),
327 max: Vec2::new(
328 center.x + length - 5 - (2 * s),
329 center.y + width - 5 - (2 * s),
330 )
331 .with_z(base - 2 + (s * height)),
332 })
333 .fill(blue_broken.clone());
334 painter
335 .aabb(Aabb {
336 min: Vec2::new(
337 center.x - length + 6 + (2 * s),
338 center.y - width + 6 + (2 * s),
339 )
340 .with_z(base - 3 + (s * height)),
341 max: Vec2::new(
342 center.x + length - 6 - (2 * s),
343 center.y + width - 6 - (2 * s),
344 )
345 .with_z(base - 2 + (s * height)),
346 })
347 .fill(white.clone());
348 let mut sprites = vec![
350 SpriteKind::Bowl,
351 SpriteKind::VialEmpty,
352 SpriteKind::FountainArabic,
353 SpriteKind::Crate,
354 SpriteKind::Lantern,
355 ];
356 'outer: for dir in NEIGHBORS {
357 let furniture_pos = Vec2::new(
358 center.x + dir.x * ((length / 2) + 1),
359 center.y + dir.y * ((width / 2) + 1),
360 );
361 if sprites.is_empty() {
362 break 'outer;
363 }
364 let sprite = sprites.swap_remove(
365 RandomField::new(0).get(furniture_pos.with_z(base)) as usize % sprites.len(),
366 );
367 painter.owned_resource_sprite(
368 furniture_pos.with_z(base - 2 + (s * height)),
369 sprite,
370 0,
371 );
372 }
373
374 if storeys > 1 {
376 painter
377 .cylinder(Aabb {
378 min: (center - 6).with_z(base - 2 + (s * height)),
379 max: (center + 6).with_z(base + (s * height)),
380 })
381 .clear();
382 };
383
384 let random_index_1 = (RandomField::new(0).get(center.with_z(base + s)) % 4) as usize;
386 let random_index_2 = 3 - random_index_1;
387 for (d, dir) in Dir2::iter().enumerate() {
389 let diagonal = dir.diagonal();
390 let bed_pos = center + diagonal * ((length / 2) - 2);
391 let table_pos = Vec2::new(
392 center.x + diagonal.x * ((length / 2) - 1),
393 center.y + diagonal.y * ((width / 2) - 2),
394 );
395 let alt = base - 2 + (s * height);
396 if d == random_index_1 {
397 painter.bed_coastal(bed_pos.with_z(alt), dir);
398 } else if d == random_index_2 {
399 painter.rotated_sprite(table_pos.with_z(alt), SpriteKind::TableCoastalLarge, 2);
400 painter.sprite(table_pos.with_z(alt + 1), SpriteKind::JugAndCupsCoastal);
401
402 for dir in Dir2::iter() {
403 let vec = dir.to_vec2();
404 let bench_pos = Vec2::new(table_pos.x + vec.x * 2, table_pos.y + vec.y);
405 painter.rotated_sprite(
406 bench_pos.with_z(alt),
407 SpriteKind::BenchCoastal,
408 dir.opposite().sprite_ori(),
409 );
410 }
411 }
412 }
413
414 for d in 0..2 {
416 let door_lamp_pos = Vec2::new(
417 center.x - length + 2 + (2 * s) + (d * ((2 * (length - (2 * s))) - 5)),
418 center.y,
419 )
420 .with_z(base + 1 + (s * height));
421 painter.rotated_sprite(
422 door_lamp_pos,
423 SpriteKind::WallLampSmall,
424 2 + ((d * 4) as u8),
425 );
426
427 let lamp_pos = Vec2::new(
428 center.x,
429 center.y - width + 2 + (2 * s) + (d * ((2 * (width - (2 * s))) - 5)),
430 )
431 .with_z(base + 1 + (s * height));
432 painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 4 - ((d * 4) as u8));
433 }
434 for d in 0..2 {
435 let door_lamp_pos = Vec2::new(
436 center.x - length - 1 + (2 * s) + (d * ((2 * (length - (2 * s))) + 1)),
437 center.y,
438 )
439 .with_z(base + 1 + (s * height));
440 painter.rotated_sprite(
441 door_lamp_pos,
442 SpriteKind::WallLampSmall,
443 6 + ((d * 4) as u8),
444 );
445
446 let lamp_pos = Vec2::new(
447 center.x,
448 center.y - width - 1 + (2 * s) + (d * ((2 * (width - (2 * s))) + 1)),
449 )
450 .with_z(base + 1 + (s * height));
451 painter.rotated_sprite(lamp_pos, SpriteKind::WallLampSmall, 8 - ((d * 4) as u8));
452 }
453 }
454 let top_limit = painter.aabb(Aabb {
455 min: Vec2::new(center.x - length, center.y - width)
456 .with_z(base + (storeys * height) - 2),
457 max: Vec2::new(center.x + length, center.y + width)
458 .with_z(base - 2 + (storeys * height) + (height / 2)),
459 });
460 painter
461 .superquadric(
462 Aabb {
463 min: Vec2::new(center.x - length - 1, center.y - width - 1)
464 .with_z(base + (storeys * height) - (height / 2)),
465 max: Vec2::new(center.x + length, center.y + width)
466 .with_z(base - 2 + (storeys * height) + (height / 2)),
467 },
468 1.5,
469 )
470 .intersect(top_limit)
471 .fill(white.clone());
472 if storeys > 1 {
473 let stair_radius1 = 3.0;
475 let stairs_clear1 = painter.cylinder(Aabb {
476 min: (center - 1 - stair_radius1 as i32).with_z(base - 2),
477 max: (center + 2 + stair_radius1 as i32)
478 .with_z(base + ((storeys - 1) * height) - 2),
479 });
480 let stairs_clear2 = painter.cylinder(Aabb {
481 min: (center - 2 - stair_radius1 as i32).with_z(base - 2),
482 max: (center + 3 + stair_radius1 as i32)
483 .with_z(base + ((storeys - 1) * height) - 2),
484 });
485 stairs_clear1.clear();
486 painter
487 .cylinder(Aabb {
488 min: (center - 1).with_z(base - 2),
489 max: (center + 2).with_z(base + ((storeys - 1) * height) - 2),
490 })
491 .fill(white.clone());
492
493 stairs_clear2
494 .sample(wall_staircase(
495 center.with_z(base + ((storeys - 1) * height) - 2),
496 stair_radius1,
497 (height / 2) as f32,
498 ))
499 .fill(white);
500 }
501 }
502}