veloren_world/site2/plot/cliff_tower.rs
1use super::*;
2use crate::{
3 Land,
4 site2::util::gradient::WrapMode,
5 util::{DIAGONALS, LOCALITY, RandomField, Sampler},
6};
7use common::{
8 generation::{EntityInfo, SpecialEntity},
9 terrain::{BlockKind, SpriteKind},
10};
11use rand::prelude::*;
12use std::{f32::consts::TAU, mem};
13use vek::*;
14
15/// Represents house data generated by the `generate()` method
16pub struct CliffTower {
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 campfire: bool,
24 door_dir: Vec2<i32>,
25 surface_color: Rgb<f32>,
26 sub_surface_color: Rgb<f32>,
27}
28
29impl CliffTower {
30 pub fn generate(
31 land: &Land,
32 index: IndexRef,
33 _rng: &mut impl Rng,
34 site: &Site,
35 door_tile: Vec2<i32>,
36 door_dir: Vec2<i32>,
37 tile_aabr: Aabr<i32>,
38 campfire: bool,
39 alt: Option<i32>,
40 ) -> Self {
41 let door_tile_pos = site.tile_center_wpos(door_tile);
42 let bounds = Aabr {
43 min: site.tile_wpos(tile_aabr.min),
44 max: site.tile_wpos(tile_aabr.max),
45 };
46 let (surface_color, sub_surface_color) =
47 if let Some(sample) = land.column_sample(bounds.center(), index) {
48 (sample.surface_color, sample.sub_surface_color)
49 } else {
50 (Rgb::new(161.0, 116.0, 86.0), Rgb::new(88.0, 64.0, 64.0))
51 };
52 Self {
53 door_tile: door_tile_pos,
54 bounds,
55 alt: alt.unwrap_or_else(|| land.get_alt_approx(door_tile_pos) as i32),
56 campfire,
57 door_dir,
58 surface_color,
59 sub_surface_color,
60 }
61 }
62}
63
64impl Structure for CliffTower {
65 #[cfg(feature = "use-dyn-lib")]
66 const UPDATE_FN: &'static [u8] = b"render_clifftower\0";
67
68 #[cfg_attr(feature = "be-dyn-lib", export_name = "render_clifftower")]
69 fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
70 let base = self.alt;
71 let plot_center = self.bounds.center();
72 let door_dir = self.door_dir;
73
74 let surface_color = self.surface_color.map(|e| (e * 255.0) as u8);
75 let sub_surface_color = self.sub_surface_color.map(|e| (e * 255.0) as u8);
76 let gradient_center = Vec3::new(
77 plot_center.x as f32,
78 plot_center.y as f32,
79 (base + 1) as f32,
80 );
81 let gradient_var_1 = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 8;
82 let gradient_var_2 = RandomField::new(0).get(plot_center.with_z(base + 1)) as i32 % 10;
83
84 let brick = Fill::Gradient(
85 util::gradient::Gradient::new(
86 gradient_center,
87 8.0 + gradient_var_1 as f32,
88 util::gradient::Shape::Point,
89 (surface_color, sub_surface_color),
90 )
91 .with_repeat(if gradient_var_2 > 5 {
92 WrapMode::Repeat
93 } else {
94 WrapMode::PingPong
95 }),
96 BlockKind::Rock,
97 );
98
99 let wood = Fill::Brick(BlockKind::Wood, Rgb::new(106, 83, 51), 12);
100 let color = Fill::Block(Block::air(SpriteKind::CliffDecorBlock));
101 let window = Fill::Block(Block::air(SpriteKind::WindowArabic));
102 let window2 = Fill::Block(Block::air(SpriteKind::WindowArabic).with_ori(2).unwrap());
103 let rope = Fill::Block(Block::air(SpriteKind::Rope));
104
105 let tube_var = RandomField::new(0).get(plot_center.with_z(base)) as i32 % 6;
106 let radius = 10.0 + tube_var as f32;
107 let tubes = 3.0 + tube_var as f32;
108 let phi = TAU / tubes;
109 for n in 1..=tubes as i32 {
110 let center = Vec2::new(
111 plot_center.x + (radius * ((n as f32 * phi).cos())) as i32,
112 plot_center.y + (radius * ((n as f32 * phi).sin())) as i32,
113 );
114
115 let variant_pos = center.with_z(base);
116 let variant = RandomField::new(0).get(variant_pos) as i32 % 10;
117 // common superquadric degree for rooms
118 let sq_type = 3.5;
119 let storeys = 5 + (variant / 2);
120 let mut length = 16 + (variant / 2);
121 let mut width = 7 * length / 8;
122 let mut height = 18 + variant / 2;
123 let mut floor_level = base - 40;
124 let mut workshops = 0;
125 let mut ground_entries = 0;
126 for s in 0..storeys {
127 let x_offset = RandomField::new(0).get((center - length).with_z(base)) as i32 % 10;
128 let y_offset = RandomField::new(0).get((center + length).with_z(base)) as i32 % 10;
129 let super_center =
130 Vec2::new(center.x - 3 + x_offset / 2, center.y - 3 + y_offset / 2);
131 // CliffTower Hoodoo Overlay
132 painter
133 .cubic_bezier(
134 super_center.with_z(floor_level + (height / 2)),
135 (super_center - x_offset).with_z(floor_level + height),
136 (super_center - y_offset).with_z(floor_level + (height) + (height / 2)),
137 super_center.with_z(floor_level + (2 * height)),
138 (length - 1) as f32,
139 )
140 .fill(brick.clone());
141 if s == (storeys - 1) {
142 for dir in LOCALITY {
143 let cone_pos = super_center + (dir * 2);
144 let cone_var =
145 4 + RandomField::new(0).get(cone_pos.with_z(base)) as i32 % 4;
146 painter
147 .cone_with_radius(
148 cone_pos.with_z(floor_level + (2 * height) + 5),
149 (length / 2) as f32,
150 (length + cone_var) as f32,
151 )
152 .fill(brick.clone());
153 }
154 }
155 // center tube with rooms
156 if n == tubes as i32 {
157 // ground_entries
158 if ground_entries < 1 && floor_level > (base - 6) {
159 for dir in CARDINALS {
160 let entry_pos_inner = plot_center + (dir * (2 * length) - 4);
161 let entry_pos_outer = plot_center + (dir * (3 * length) + 4);
162 painter
163 .line(
164 entry_pos_inner.with_z(floor_level + 6),
165 entry_pos_outer.with_z(base + 35),
166 6.0,
167 )
168 .clear();
169 }
170 let door_start = plot_center + door_dir * ((3 * (length / 2)) + 1);
171 painter
172 .line(
173 door_start.with_z(floor_level + 2),
174 self.door_tile.with_z(base),
175 4.0,
176 )
177 .fill(wood.clone());
178 painter
179 .line(
180 door_start.with_z(floor_level + 7),
181 self.door_tile.with_z(base + 6),
182 7.0,
183 )
184 .clear();
185 ground_entries += 1;
186 }
187 painter
188 .cubic_bezier(
189 plot_center.with_z(floor_level + (height / 2)),
190 (plot_center - x_offset).with_z(floor_level + height),
191 (plot_center - y_offset).with_z(floor_level + (height) + (height / 2)),
192 plot_center.with_z(floor_level + (2 * height)),
193 (length + 2) as f32,
194 )
195 .fill(brick.clone());
196 // platforms on some upper storeys
197 let balcony = RandomField::new(0).get((plot_center - floor_level).with_z(base))
198 as i32
199 % 2;
200 if storeys > 3 && floor_level > base + 25 && balcony == 0 {
201 let limit_up = painter.aabb(Aabb {
202 min: (plot_center - (2 * length) - 2).with_z(floor_level - 4),
203 max: (plot_center + (2 * length) + 2).with_z(floor_level + 1),
204 });
205 painter
206 .superquadric(
207 Aabb {
208 min: (plot_center - (2 * length) - 2).with_z(floor_level - 4),
209 max: (plot_center + (2 * length) + 2).with_z(floor_level + 6),
210 },
211 4.0,
212 )
213 .intersect(limit_up)
214 .fill(wood.clone());
215 // lanterns & random sprites for wood platform corners
216 for dir in DIAGONALS {
217 let sprite_pos = plot_center + (dir * ((2 * length) - 4));
218 painter
219 .aabb(Aabb {
220 min: sprite_pos.with_z(floor_level + 1),
221 max: (sprite_pos + 1).with_z(floor_level + 2),
222 })
223 .clear();
224 painter.owned_resource_sprite(
225 sprite_pos.with_z(floor_level + 1),
226 match (RandomField::new(0).get(sprite_pos.with_z(floor_level + 1)))
227 % 8
228 {
229 0 => SpriteKind::Bowl,
230 1 => SpriteKind::VialEmpty,
231 2 => SpriteKind::Crate,
232 3 => SpriteKind::Pot,
233 _ => SpriteKind::MesaLantern,
234 },
235 0,
236 );
237 }
238 // planters
239 for r in 0..2 {
240 for p in 0..((length / 2) - 2) {
241 let planter_pos_1 = Vec2::new(
242 plot_center.x - (2 * (length / 3)) + (p * (length / 3)),
243 plot_center.y - ((2 * length) + 1) + (r * ((4 * length) + 1)),
244 );
245 painter
246 .aabb(Aabb {
247 min: Vec2::new(planter_pos_1.x - 1, planter_pos_1.y)
248 .with_z(floor_level + 1),
249 max: Vec2::new(planter_pos_1.x + 2, planter_pos_1.y + 1)
250 .with_z(floor_level + 3),
251 })
252 .clear();
253 painter.rotated_sprite(
254 planter_pos_1.with_z(floor_level + 1),
255 SpriteKind::Planter,
256 (4 - (r * 4)) as u8,
257 );
258 let planter_pos_2 = Vec2::new(
259 plot_center.x - ((2 * length) + 1) + (r * ((4 * length) + 1)),
260 plot_center.y - (2 * (length / 3)) + (p * (length / 3)),
261 );
262 painter
263 .aabb(Aabb {
264 min: Vec2::new(planter_pos_2.x, planter_pos_2.y - 1)
265 .with_z(floor_level + 1),
266 max: Vec2::new(planter_pos_2.x + 1, planter_pos_2.y + 2)
267 .with_z(floor_level + 3),
268 })
269 .clear();
270 painter.rotated_sprite(
271 planter_pos_2.with_z(floor_level + 1),
272 SpriteKind::Planter,
273 (6 - (r * 4)) as u8,
274 );
275 }
276 }
277 }
278
279 // clear rooms and entries & decor
280 if floor_level > (base - 6) {
281 // decor
282 painter
283 .line(
284 Vec2::new(plot_center.x, plot_center.y - length)
285 .with_z(floor_level + 5),
286 Vec2::new(plot_center.x, plot_center.y + length)
287 .with_z(floor_level + 5),
288 4.0,
289 )
290 .fill(color.clone());
291 painter
292 .line(
293 Vec2::new(plot_center.x - length, plot_center.y)
294 .with_z(floor_level + 5),
295 Vec2::new(plot_center.x + length, plot_center.y)
296 .with_z(floor_level + 5),
297 4.0,
298 )
299 .fill(color.clone());
300 // entries
301 painter
302 .line(
303 Vec2::new(plot_center.x, plot_center.y - (2 * length) - 4)
304 .with_z(floor_level + 4),
305 Vec2::new(plot_center.x, plot_center.y + (2 * length) + 4)
306 .with_z(floor_level + 4),
307 4.0,
308 )
309 .clear();
310 painter
311 .line(
312 Vec2::new(plot_center.x - (2 * length) - 4, plot_center.y)
313 .with_z(floor_level + 4),
314 Vec2::new(plot_center.x + (2 * length) + 4, plot_center.y)
315 .with_z(floor_level + 4),
316 4.0,
317 )
318 .clear();
319 painter
320 .superquadric(
321 Aabb {
322 min: (plot_center - length - 1).with_z(floor_level),
323 max: (plot_center + length + 1)
324 .with_z(floor_level + height - 4),
325 },
326 sq_type,
327 )
328 .clear();
329 // room floor
330 painter
331 .cylinder(Aabb {
332 min: (plot_center - length - 3).with_z(floor_level),
333 max: (plot_center + length + 3).with_z(floor_level + 1),
334 })
335 .fill(brick.clone());
336 painter
337 .cylinder(Aabb {
338 min: (plot_center - length + 1).with_z(floor_level),
339 max: (plot_center + length - 1).with_z(floor_level + 1),
340 })
341 .fill(color.clone());
342 painter
343 .cylinder(Aabb {
344 min: (plot_center - length + 2).with_z(floor_level),
345 max: (plot_center + length - 2).with_z(floor_level + 1),
346 })
347 .fill(brick.clone());
348 // entry sprites
349 painter
350 .aabb(Aabb {
351 min: Vec2::new(plot_center.x - 3, plot_center.y + length)
352 .with_z(floor_level + 2),
353 max: Vec2::new(plot_center.x + 4, plot_center.y + length + 1)
354 .with_z(floor_level + 7),
355 })
356 .fill(window2.clone());
357 painter
358 .aabb(Aabb {
359 min: Vec2::new(plot_center.x - 2, plot_center.y + length)
360 .with_z(floor_level + 2),
361 max: Vec2::new(plot_center.x + 3, plot_center.y + length + 1)
362 .with_z(floor_level + 7),
363 })
364 .clear();
365
366 painter
367 .aabb(Aabb {
368 min: Vec2::new(plot_center.x - 3, plot_center.y - length - 1)
369 .with_z(floor_level + 2),
370 max: Vec2::new(plot_center.x + 4, plot_center.y - length)
371 .with_z(floor_level + 7),
372 })
373 .fill(window2.clone());
374 painter
375 .aabb(Aabb {
376 min: Vec2::new(plot_center.x - 2, plot_center.y - length - 1)
377 .with_z(floor_level + 2),
378 max: Vec2::new(plot_center.x + 3, plot_center.y - length)
379 .with_z(floor_level + 7),
380 })
381 .clear();
382 painter
383 .aabb(Aabb {
384 min: Vec2::new(plot_center.x + length, plot_center.y - 3)
385 .with_z(floor_level + 2),
386 max: Vec2::new(plot_center.x + length + 1, plot_center.y + 4)
387 .with_z(floor_level + 7),
388 })
389 .fill(window.clone());
390 painter
391 .aabb(Aabb {
392 min: Vec2::new(plot_center.x + length, plot_center.y - 2)
393 .with_z(floor_level + 2),
394 max: Vec2::new(plot_center.x + length + 1, plot_center.y + 3)
395 .with_z(floor_level + 7),
396 })
397 .clear();
398
399 painter
400 .aabb(Aabb {
401 min: Vec2::new(plot_center.x - length - 1, plot_center.y - 3)
402 .with_z(floor_level + 2),
403 max: Vec2::new(plot_center.x - length, plot_center.y + 4)
404 .with_z(floor_level + 7),
405 })
406 .fill(window.clone());
407 painter
408 .aabb(Aabb {
409 min: Vec2::new(plot_center.x - length - 1, plot_center.y - 2)
410 .with_z(floor_level + 2),
411 max: Vec2::new(plot_center.x - length, plot_center.y + 3)
412 .with_z(floor_level + 7),
413 })
414 .clear();
415 // furniture
416 if workshops < 1 {
417 painter
418 .aabb(Aabb {
419 min: (plot_center - 1).with_z(floor_level + 1),
420 max: (plot_center + 2).with_z(floor_level + 2),
421 })
422 .fill(brick.clone());
423 painter
424 .aabb(Aabb {
425 min: plot_center.with_z(floor_level),
426 max: (plot_center + 1).with_z(floor_level + 1),
427 })
428 .fill(Fill::Block(Block::air(SpriteKind::FireBlock)));
429
430 painter
431 .aabb(Aabb {
432 min: plot_center.with_z(floor_level + 1),
433 max: (plot_center + 1).with_z(floor_level + 2),
434 })
435 .clear();
436 let mut stations = vec![
437 SpriteKind::CraftingBench,
438 SpriteKind::Forge,
439 SpriteKind::SpinningWheel,
440 SpriteKind::TanningRack,
441 SpriteKind::CookingPot,
442 SpriteKind::Cauldron,
443 SpriteKind::Loom,
444 SpriteKind::Anvil,
445 SpriteKind::DismantlingBench,
446 SpriteKind::RepairBench,
447 ];
448 'outer: for d in 0..3 {
449 for dir in CARDINALS {
450 if stations.is_empty() {
451 break 'outer;
452 }
453 let position = plot_center + dir * (3 + d * 2);
454 let cr_station = stations.swap_remove(
455 RandomField::new(0).get(position.with_z(base)) as usize
456 % stations.len(),
457 );
458 painter.sprite(position.with_z(floor_level + 1), cr_station);
459 }
460 }
461 workshops += 1;
462 // forge tools
463 for d in 0..2 {
464 let pos = Vec2::new(
465 plot_center.x - 5 + (d * 10),
466 plot_center.y - length + (d * ((2 * length) - 1)),
467 );
468 painter
469 .aabb(Aabb {
470 min: Vec2::new(pos.x - 2, pos.y - (4 * d))
471 .with_z(floor_level + 1),
472 max: Vec2::new(pos.x + 3, pos.y + 5 - (4 * d))
473 .with_z(floor_level + 4),
474 })
475 .clear();
476 painter.rotated_sprite(
477 pos.with_z(floor_level + 1),
478 SpriteKind::Hearth,
479 (4 * d) as u8,
480 );
481 }
482 // forge tools
483 for d in 0..2 {
484 let pos = Vec2::new(
485 plot_center.x + 5 - (d * 10),
486 plot_center.y - length + (d * ((2 * length) - 1)),
487 );
488 painter
489 .aabb(Aabb {
490 min: Vec2::new(pos.x - 2, pos.y - (4 * d))
491 .with_z(floor_level + 1),
492 max: Vec2::new(pos.x + 3, pos.y + 5 - (4 * d))
493 .with_z(floor_level + 4),
494 })
495 .clear();
496 painter.rotated_sprite(
497 pos.with_z(floor_level + 1),
498 SpriteKind::ForgeTools,
499 (4 * d) as u8,
500 );
501 }
502 } else {
503 match (RandomField::new(0).get(plot_center.with_z(floor_level))) % 3 {
504 0 => {
505 // living room
506 // distribute small sprites
507 for dir in LOCALITY {
508 let pos = plot_center + dir * ((length / 3) - 1);
509 painter.owned_resource_sprite(
510 pos.with_z(floor_level + 1),
511 match (RandomField::new(0).get(pos.with_z(floor_level)))
512 % 9
513 {
514 0 => SpriteKind::WardrobeSingleMesa,
515 1 => SpriteKind::CoatRack,
516 2 => SpriteKind::MirrorMesa,
517 3 => SpriteKind::CushionArabic,
518 4 => SpriteKind::JugArabic,
519 5 => SpriteKind::SepareArabic,
520 6 => SpriteKind::Crate,
521 7 => SpriteKind::Bowl,
522 _ => SpriteKind::MesaLantern,
523 },
524 0,
525 );
526 }
527 // beds & wardrobes
528 for d in 0..2 {
529 let pos = Vec2::new(
530 plot_center.x - length + 6 + (d * ((2 * length) - 12)),
531 plot_center.y - length + 5 + (d * ((2 * length) - 10)),
532 );
533 painter
534 .aabb(Aabb {
535 min: Vec2::new(
536 pos.x - 1 - (3 * d),
537 pos.y - 1 - (3 * d),
538 )
539 .with_z(floor_level + 1),
540 max: Vec2::new(
541 pos.x + 5 - (3 * d),
542 pos.y + 5 - (3 * d),
543 )
544 .with_z(floor_level + 5),
545 })
546 .clear();
547 painter.rotated_sprite(
548 pos.with_z(floor_level + 1),
549 match (RandomField::new(0)
550 .get(pos.with_z(floor_level - d)))
551 % 3
552 {
553 0 => SpriteKind::WardrobeDoubleMesa,
554 _ => SpriteKind::BedMesa,
555 },
556 (4 * d) as u8,
557 );
558 }
559 // bookshelfs
560 for d in 0..2 {
561 let pos = Vec2::new(
562 plot_center.x + 5 - (d * 10),
563 plot_center.y - length + (d * ((2 * length) - 1)),
564 );
565 painter.rotated_sprite(
566 pos.with_z(floor_level + 4),
567 SpriteKind::BookshelfArabic,
568 (4 * d) as u8,
569 );
570 }
571 // decor set / separe / table large
572 for d in 0..2 {
573 let pos = Vec2::new(
574 plot_center.x - length + 8 + (d * ((2 * length) - 16)),
575 plot_center.y + length - 8 + (d * ((-2 * length) + 16)),
576 );
577 painter
578 .aabb(Aabb {
579 min: Vec2::new(pos.x - 2, pos.y - 1)
580 .with_z(floor_level + 1),
581 max: Vec2::new(pos.x + 3, pos.y + 2)
582 .with_z(floor_level + 3),
583 })
584 .clear();
585 painter.sprite(
586 pos.with_z(floor_level + 1),
587 match (RandomField::new(0)
588 .get(pos.with_z(floor_level - d)))
589 % 3
590 {
591 0 => SpriteKind::TableArabicLarge,
592 1 => SpriteKind::DecorSetArabic,
593 _ => SpriteKind::SepareArabic,
594 },
595 )
596 }
597 },
598 1 => {
599 // bath
600 // wall tables with varying items
601 for d in 0..2 {
602 let pos = Vec2::new(
603 plot_center.x - 5 + (d * 10),
604 plot_center.y - length + (d * ((2 * length) - 1)),
605 );
606 painter.rotated_sprite(
607 pos.with_z(floor_level + 3),
608 SpriteKind::WallTableMesa,
609 (4 * d) as u8,
610 );
611 painter.owned_resource_sprite(
612 pos.with_z(floor_level + 4),
613 match (RandomField::new(0).get(pos.with_z(floor_level)))
614 % 3
615 {
616 0 => SpriteKind::Bowl,
617 1 => SpriteKind::VialEmpty,
618 _ => SpriteKind::JugArabic,
619 },
620 (4 * d) as u8,
621 );
622 }
623 // distribute small sprites
624 for dir in LOCALITY {
625 let pos = plot_center + dir * ((length / 3) + 1);
626 painter.owned_resource_sprite(
627 pos.with_z(floor_level + 1),
628 match (RandomField::new(0).get(pos.with_z(floor_level)))
629 % 12
630 {
631 0 => SpriteKind::DrawerSmall,
632 1 => SpriteKind::CoatRack,
633 2 => SpriteKind::TableArabicSmall,
634 3 => SpriteKind::CushionArabic,
635 4 => SpriteKind::JugArabic,
636 5 => SpriteKind::WardrobeSingleMesa,
637 6 => SpriteKind::Crate,
638 7 => SpriteKind::DecorSetArabic,
639 8 => SpriteKind::VialEmpty,
640 9 => SpriteKind::SepareArabic,
641 10 => SpriteKind::MesaLantern,
642 _ => SpriteKind::FountainArabic,
643 },
644 0,
645 );
646 }
647 },
648 _ => {
649 // kitchen
650 // cupbooards
651 for d in 0..2 {
652 let pos = Vec2::new(
653 plot_center.x + 5 - (d * 10),
654 plot_center.y - length + (d * ((2 * length) - 1)),
655 );
656 painter.rotated_sprite(
657 pos.with_z(floor_level + 3),
658 SpriteKind::CupboardMesa,
659 (4 * d) as u8,
660 );
661 }
662 // wall tables with varying items
663 for d in 0..2 {
664 let pos = Vec2::new(
665 plot_center.x - 5 + (d * 10),
666 plot_center.y - length + (d * ((2 * length) - 1)),
667 );
668 painter.rotated_sprite(
669 pos.with_z(floor_level + 2),
670 SpriteKind::WallTableMesa,
671 (4 * d) as u8,
672 );
673 painter.owned_resource_sprite(
674 pos.with_z(floor_level + 3),
675 match (RandomField::new(0).get(pos.with_z(floor_level)))
676 % 4
677 {
678 0 => SpriteKind::Melon,
679 1 => SpriteKind::Bowl,
680 2 => SpriteKind::JugArabic,
681 _ => SpriteKind::VialEmpty,
682 },
683 (4 * d) as u8,
684 );
685 }
686 // distribute small sprites
687 for dir in LOCALITY {
688 let pos = plot_center + dir * ((length / 3) + 1);
689 painter.owned_resource_sprite(
690 pos.with_z(floor_level + 1),
691 match (RandomField::new(0).get(pos.with_z(floor_level)))
692 % 11
693 {
694 0 => SpriteKind::WardrobeSingleMesa,
695 1 => SpriteKind::Cauldron,
696 2 => SpriteKind::TableArabicSmall,
697 3 => SpriteKind::CushionArabic,
698 4 => SpriteKind::JugArabic,
699 5 => SpriteKind::Crate,
700 6 => SpriteKind::Bowl,
701 7 => SpriteKind::VialEmpty,
702 8 => SpriteKind::CookingPot,
703 9 => SpriteKind::MesaLantern,
704 10 => SpriteKind::JugAndBowlArabic,
705 _ => SpriteKind::OvenArabic,
706 },
707 0,
708 );
709 }
710 },
711 }
712 }
713 // wall lamps
714
715 let corner_pos_1 = Vec2::new(plot_center.x - length, plot_center.y - 5);
716 let corner_pos_2 = Vec2::new(plot_center.x - 5, plot_center.y - length);
717 for dir in SQUARE_4 {
718 let lamp_pos_1 = Vec2::new(
719 corner_pos_1.x + (dir.x * ((2 * length) - 1)),
720 corner_pos_1.y + (dir.y * 10),
721 )
722 .with_z(floor_level + 7);
723 painter.rotated_sprite(
724 lamp_pos_1,
725 SpriteKind::WallLampMesa,
726 (2 + (4 * dir.x)) as u8,
727 );
728 let lamp_pos_2 = Vec2::new(
729 corner_pos_2.x + (dir.x * 10),
730 corner_pos_2.y + (dir.y * ((2 * length) - 1)),
731 )
732 .with_z(floor_level + 7);
733 painter.rotated_sprite(
734 lamp_pos_2,
735 SpriteKind::WallLampMesa,
736 (4 - (4 * dir.y)) as u8,
737 );
738 }
739 }
740 // stairs
741 if floor_level > (base + 8) {
742 let stairs_level = floor_level + 1;
743 let stairs_start = plot_center + door_dir * ((2 * length) - 7);
744 let mid_dir = if door_dir.x != 0 {
745 door_dir.x
746 } else {
747 door_dir.y
748 };
749 let stairs_mid = Vec2::new(
750 plot_center.x + mid_dir * (3 * (length / 2)),
751 plot_center.y + mid_dir * (3 * (length / 2)),
752 );
753 let stairs_end = Vec2::new(
754 plot_center.x + door_dir.y * ((2 * length) - 7),
755 plot_center.y + door_dir.x * ((2 * length) - 7),
756 );
757 let rope_pos = Vec2::new(
758 plot_center.x + mid_dir * ((3 * (length / 2)) + 2),
759 plot_center.y + mid_dir * ((3 * (length / 2)) + 2),
760 );
761
762 painter
763 .cylinder(Aabb {
764 min: (stairs_start - 6).with_z(stairs_level - 1),
765 max: (stairs_start + 6).with_z(stairs_level),
766 })
767 .fill(wood.clone());
768
769 painter
770 .cylinder(Aabb {
771 min: (stairs_mid - 6).with_z(stairs_level - (height / 2) - 1),
772 max: (stairs_mid + 6).with_z(stairs_level - (height / 2)),
773 })
774 .fill(wood.clone());
775
776 painter
777 .cylinder(Aabb {
778 min: (stairs_end - 6).with_z(stairs_level - height - 1),
779 max: (stairs_end + 6).with_z(stairs_level - height),
780 })
781 .fill(wood.clone());
782
783 for n in 0..2 {
784 let stairs = painter
785 .line(
786 stairs_start.with_z(stairs_level + (n * 2)),
787 stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
788 4.0 + (n as f32 / 2.0),
789 )
790 .union(painter.line(
791 stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
792 stairs_end.with_z(stairs_level - height + (n * 2)),
793 4.0 + (n as f32 / 2.0),
794 ));
795 match n {
796 0 => stairs.fill(wood.clone()),
797 _ => stairs.clear(),
798 };
799 }
800 painter
801 .line(
802 rope_pos.with_z(stairs_level + (height / 2) - 3),
803 (plot_center - (length / 2))
804 .with_z(stairs_level + (height / 2) + 2),
805 1.5,
806 )
807 .fill(wood.clone());
808
809 painter
810 .aabb(Aabb {
811 min: rope_pos.with_z(stairs_level - (height / 2) - 1),
812 max: (rope_pos + 1).with_z(stairs_level + (height / 2) - 3),
813 })
814 .fill(rope.clone());
815 }
816 }
817 // vary next storey
818 length += -1;
819 width += -1;
820 height += -1;
821 floor_level += height;
822 mem::swap(&mut length, &mut width);
823 }
824 }
825 // spawn campfire next to some clifftowers
826 if self.campfire {
827 let campfire_pos = (plot_center - 20).with_z(self.alt + 18);
828 painter.spawn(
829 EntityInfo::at(campfire_pos.map(|e| e as f32))
830 .into_special(SpecialEntity::Waypoint),
831 );
832 }
833 }
834}