Skip to main content

veloren_world/site/plot/
cliff_tower.rs

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