veloren_world/site2/plot/
cliff_tower.rs

1use super::*;
2use crate::{
3    Land,
4    site2::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 = "use-dyn-lib")]
66    const UPDATE_FN: &'static [u8] = b"render_clifftower\0";
67
68    #[cfg_attr(feature = "be-dyn-lib", unsafe(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::FlowerpotWoodWoodlandS,
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::CoatrackMetalWoodland,
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                                        let dir = Dir::from_vec2(plot_center - pos);
548                                        match (RandomField::new(0).get(pos.with_z(floor_level - d)))
549                                            % 3
550                                        {
551                                            0 => {
552                                                painter.rotated_sprite(
553                                                    pos.with_z(floor_level + 1),
554                                                    SpriteKind::WardrobeDoubleMesa,
555                                                    (4 * d) as u8,
556                                                );
557                                            },
558                                            _ => {
559                                                painter.bed_cliff(pos.with_z(floor_level + 1), dir);
560                                            },
561                                        }
562                                    }
563                                    // bookshelfs
564                                    for d in 0..2 {
565                                        let pos = Vec2::new(
566                                            plot_center.x + 5 - (d * 10),
567                                            plot_center.y - length + (d * ((2 * length) - 1)),
568                                        );
569                                        painter.rotated_sprite(
570                                            pos.with_z(floor_level + 4),
571                                            SpriteKind::BookshelfArabic,
572                                            (4 * d) as u8,
573                                        );
574                                    }
575                                    // decor set / separe / table large
576                                    for d in 0..2 {
577                                        let pos = Vec2::new(
578                                            plot_center.x - length + 8 + (d * ((2 * length) - 16)),
579                                            plot_center.y + length - 8 + (d * ((-2 * length) + 16)),
580                                        );
581                                        painter
582                                            .aabb(Aabb {
583                                                min: Vec2::new(pos.x - 2, pos.y - 1)
584                                                    .with_z(floor_level + 1),
585                                                max: Vec2::new(pos.x + 3, pos.y + 2)
586                                                    .with_z(floor_level + 3),
587                                            })
588                                            .clear();
589                                        painter.sprite(
590                                            pos.with_z(floor_level + 1),
591                                            match (RandomField::new(0)
592                                                .get(pos.with_z(floor_level - d)))
593                                                % 3
594                                            {
595                                                0 => SpriteKind::TableArabicLarge,
596                                                1 => SpriteKind::DecorSetArabic,
597                                                _ => SpriteKind::SepareArabic,
598                                            },
599                                        )
600                                    }
601                                },
602                                1 => {
603                                    // bath
604                                    // wall tables with varying items
605                                    for d in 0..2 {
606                                        let pos = Vec2::new(
607                                            plot_center.x - 5 + (d * 10),
608                                            plot_center.y - length + (d * ((2 * length) - 1)),
609                                        );
610                                        painter.rotated_sprite(
611                                            pos.with_z(floor_level + 3),
612                                            SpriteKind::WallTableMesa,
613                                            (4 * d) as u8,
614                                        );
615                                        painter.owned_resource_sprite(
616                                            pos.with_z(floor_level + 4),
617                                            match (RandomField::new(0).get(pos.with_z(floor_level)))
618                                                % 3
619                                            {
620                                                0 => SpriteKind::Bowl,
621                                                1 => SpriteKind::VialEmpty,
622                                                _ => SpriteKind::JugArabic,
623                                            },
624                                            (4 * d) as u8,
625                                        );
626                                    }
627                                    // distribute small sprites
628                                    for dir in LOCALITY {
629                                        let pos = plot_center + dir * ((length / 3) + 1);
630                                        painter.owned_resource_sprite(
631                                            pos.with_z(floor_level + 1),
632                                            match (RandomField::new(0).get(pos.with_z(floor_level)))
633                                                % 12
634                                            {
635                                                0 => SpriteKind::DrawerWoodWoodlandS,
636                                                1 => SpriteKind::CoatrackWoodWoodland,
637                                                2 => SpriteKind::TableArabicSmall,
638                                                3 => SpriteKind::CushionArabic,
639                                                4 => SpriteKind::JugArabic,
640                                                5 => SpriteKind::WardrobeSingleMesa,
641                                                6 => SpriteKind::Crate,
642                                                7 => SpriteKind::DecorSetArabic,
643                                                8 => SpriteKind::VialEmpty,
644                                                9 => SpriteKind::SepareArabic,
645                                                10 => SpriteKind::MesaLantern,
646                                                _ => SpriteKind::FountainArabic,
647                                            },
648                                            0,
649                                        );
650                                    }
651                                },
652                                _ => {
653                                    // kitchen
654                                    // cupbooards
655                                    for d in 0..2 {
656                                        let pos = Vec2::new(
657                                            plot_center.x + 5 - (d * 10),
658                                            plot_center.y - length + (d * ((2 * length) - 1)),
659                                        );
660                                        painter.rotated_sprite(
661                                            pos.with_z(floor_level + 3),
662                                            SpriteKind::CupboardMesa,
663                                            (4 * d) as u8,
664                                        );
665                                    }
666                                    // wall tables with varying items
667                                    for d in 0..2 {
668                                        let pos = Vec2::new(
669                                            plot_center.x - 5 + (d * 10),
670                                            plot_center.y - length + (d * ((2 * length) - 1)),
671                                        );
672                                        painter.rotated_sprite(
673                                            pos.with_z(floor_level + 2),
674                                            SpriteKind::WallTableMesa,
675                                            (4 * d) as u8,
676                                        );
677                                        painter.owned_resource_sprite(
678                                            pos.with_z(floor_level + 3),
679                                            match (RandomField::new(0).get(pos.with_z(floor_level)))
680                                                % 4
681                                            {
682                                                0 => SpriteKind::Melon,
683                                                1 => SpriteKind::Bowl,
684                                                2 => SpriteKind::JugArabic,
685                                                _ => SpriteKind::VialEmpty,
686                                            },
687                                            (4 * d) as u8,
688                                        );
689                                    }
690                                    // distribute small sprites
691                                    for dir in LOCALITY {
692                                        let pos = plot_center + dir * ((length / 3) + 1);
693                                        painter.owned_resource_sprite(
694                                            pos.with_z(floor_level + 1),
695                                            match (RandomField::new(0).get(pos.with_z(floor_level)))
696                                                % 11
697                                            {
698                                                0 => SpriteKind::WardrobeSingleMesa,
699                                                1 => SpriteKind::Cauldron,
700                                                2 => SpriteKind::TableArabicSmall,
701                                                3 => SpriteKind::CushionArabic,
702                                                4 => SpriteKind::JugArabic,
703                                                5 => SpriteKind::Crate,
704                                                6 => SpriteKind::Bowl,
705                                                7 => SpriteKind::VialEmpty,
706                                                8 => SpriteKind::CookingPot,
707                                                9 => SpriteKind::MesaLantern,
708                                                10 => SpriteKind::JugAndBowlArabic,
709                                                _ => SpriteKind::OvenArabic,
710                                            },
711                                            0,
712                                        );
713                                    }
714                                },
715                            }
716                        }
717                        // wall lamps
718
719                        let corner_pos_1 = Vec2::new(plot_center.x - length, plot_center.y - 5);
720                        let corner_pos_2 = Vec2::new(plot_center.x - 5, plot_center.y - length);
721                        for dir in SQUARE_4 {
722                            let lamp_pos_1 = Vec2::new(
723                                corner_pos_1.x + (dir.x * ((2 * length) - 1)),
724                                corner_pos_1.y + (dir.y * 10),
725                            )
726                            .with_z(floor_level + 7);
727                            painter.rotated_sprite(
728                                lamp_pos_1,
729                                SpriteKind::WallLampMesa,
730                                (2 + (4 * dir.x)) as u8,
731                            );
732                            let lamp_pos_2 = Vec2::new(
733                                corner_pos_2.x + (dir.x * 10),
734                                corner_pos_2.y + (dir.y * ((2 * length) - 1)),
735                            )
736                            .with_z(floor_level + 7);
737                            painter.rotated_sprite(
738                                lamp_pos_2,
739                                SpriteKind::WallLampMesa,
740                                (4 - (4 * dir.y)) as u8,
741                            );
742                        }
743                    }
744                    // stairs
745                    if floor_level > (base + 8) {
746                        let stairs_level = floor_level + 1;
747                        let stairs_start = plot_center + door_dir * ((2 * length) - 7);
748                        let mid_dir = if door_dir.x != 0 {
749                            door_dir.x
750                        } else {
751                            door_dir.y
752                        };
753                        let stairs_mid = Vec2::new(
754                            plot_center.x + mid_dir * (3 * (length / 2)),
755                            plot_center.y + mid_dir * (3 * (length / 2)),
756                        );
757                        let stairs_end = Vec2::new(
758                            plot_center.x + door_dir.y * ((2 * length) - 7),
759                            plot_center.y + door_dir.x * ((2 * length) - 7),
760                        );
761                        let rope_pos = Vec2::new(
762                            plot_center.x + mid_dir * ((3 * (length / 2)) + 2),
763                            plot_center.y + mid_dir * ((3 * (length / 2)) + 2),
764                        );
765
766                        painter
767                            .cylinder(Aabb {
768                                min: (stairs_start - 6).with_z(stairs_level - 1),
769                                max: (stairs_start + 6).with_z(stairs_level),
770                            })
771                            .fill(wood.clone());
772
773                        painter
774                            .cylinder(Aabb {
775                                min: (stairs_mid - 6).with_z(stairs_level - (height / 2) - 1),
776                                max: (stairs_mid + 6).with_z(stairs_level - (height / 2)),
777                            })
778                            .fill(wood.clone());
779
780                        painter
781                            .cylinder(Aabb {
782                                min: (stairs_end - 6).with_z(stairs_level - height - 1),
783                                max: (stairs_end + 6).with_z(stairs_level - height),
784                            })
785                            .fill(wood.clone());
786
787                        for n in 0..2 {
788                            let stairs = painter
789                                .line(
790                                    stairs_start.with_z(stairs_level + (n * 2)),
791                                    stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
792                                    4.0 + (n as f32 / 2.0),
793                                )
794                                .union(painter.line(
795                                    stairs_mid.with_z(stairs_level - (height / 2) + (n * 2)),
796                                    stairs_end.with_z(stairs_level - height + (n * 2)),
797                                    4.0 + (n as f32 / 2.0),
798                                ));
799                            match n {
800                                0 => stairs.fill(wood.clone()),
801                                _ => stairs.clear(),
802                            };
803                        }
804                        painter
805                            .line(
806                                rope_pos.with_z(stairs_level + (height / 2) - 3),
807                                (plot_center - (length / 2))
808                                    .with_z(stairs_level + (height / 2) + 2),
809                                1.5,
810                            )
811                            .fill(wood.clone());
812
813                        painter
814                            .aabb(Aabb {
815                                min: rope_pos.with_z(stairs_level - (height / 2) - 1),
816                                max: (rope_pos + 1).with_z(stairs_level + (height / 2) - 3),
817                            })
818                            .fill(rope.clone());
819                    }
820                }
821                // vary next storey
822                length += -1;
823                width += -1;
824                height += -1;
825                floor_level += height;
826                mem::swap(&mut length, &mut width);
827            }
828        }
829        // spawn campfire next to some clifftowers
830        if self.campfire {
831            let campfire_pos = (plot_center - 20).with_z(self.alt + 18);
832            painter.spawn(
833                EntityInfo::at(campfire_pos.map(|e| e as f32))
834                    .into_special(SpecialEntity::Waypoint),
835            );
836        }
837    }
838}