veloren_world/site2/plot/
cliff_tower.rs

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