veloren_world/site/plot/
desert_city_arena.rs

1use std::{f32::consts::TAU, sync::Arc};
2
3use crate::{
4    Land,
5    site::{Dir, Fill, Painter, Site, Structure, generation::spiral_staircase},
6    util::{CARDINALS, DIAGONALS, RandomField, Sampler},
7};
8use common::{
9    generation::{EntityInfo, SpecialEntity},
10    terrain::{Block, BlockKind, SpriteKind},
11};
12use rand::RngExt;
13use vek::*;
14
15pub struct DesertCityArena {
16    /// Approximate altitude of the door tile
17    pub(crate) alt: i32,
18    pub base: i32,
19    pub center: Vec2<i32>,
20    // arena
21    // config
22    length: i32,
23    width: i32,
24    height: i32,
25    corner: i32,
26    wall_th: i32,
27    pillar_size: i32,
28    top_height: i32,
29    pub stand_dist: i32,
30    pub stand_length: i32,
31    pub stand_width: i32,
32}
33
34impl DesertCityArena {
35    pub fn generate(
36        land: &Land,
37        _rng: &mut impl RngExt,
38        site: &Site,
39        tile_aabr: Aabr<i32>,
40    ) -> Self {
41        let bounds = Aabr {
42            min: site.tile_wpos(tile_aabr.min),
43            max: site.tile_wpos(tile_aabr.max),
44        };
45        let alt = land.get_alt_approx(site.tile_center_wpos((tile_aabr.max - tile_aabr.min) / 2))
46            as i32
47            + 2;
48        let base = alt + 1;
49        let center = bounds.center();
50        // arena
51        // config
52        let length = 160;
53        let width = length / 2;
54        let height = length / 6;
55        let corner = length / 5;
56        let wall_th = 3;
57        let pillar_size = (length / 15) - wall_th;
58        let top_height = 3;
59        let stand_dist = length / 3;
60        let stand_length = length / 6;
61        let stand_width = length / 16;
62
63        Self {
64            alt,
65            base,
66            center,
67            length,
68            width,
69            height,
70            corner,
71            wall_th,
72            pillar_size,
73            top_height,
74            stand_dist,
75            stand_length,
76            stand_width,
77        }
78    }
79
80    pub fn radius(&self) -> f32 { 100.0 }
81
82    pub fn entity_at(
83        &self,
84        _pos: Vec3<i32>,
85        _above_block: &Block,
86        _dynamic_rng: &mut impl RngExt,
87    ) -> Option<EntityInfo> {
88        None
89    }
90}
91
92impl Structure for DesertCityArena {
93    #[cfg(feature = "use-dyn-lib")]
94    const UPDATE_FN: &'static [u8] = b"render_arena\0";
95
96    #[cfg_attr(feature = "be-dyn-lib", unsafe(export_name = "render_arena"))]
97    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
98        let base = self.base;
99        let center = self.center;
100
101        // arena
102        // config
103        let length = self.length;
104        let width = self.width;
105        let height = self.height;
106        let corner = self.corner;
107        let wall_th = self.wall_th;
108        let pillar_size = self.pillar_size;
109        let top_height = self.top_height;
110
111        let sandstone = Fill::Sampling(Arc::new(|center| {
112            Some(match (RandomField::new(0).get(center)) % 37 {
113                0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)),
114                9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)),
115                18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)),
116                27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)),
117                _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)),
118            })
119        }));
120        let color = Fill::Block(Block::new(BlockKind::Rock, Rgb::new(19, 48, 76)));
121        let chain = Fill::Block(Block::air(SpriteKind::SeaDecorChain));
122        let lantern = Fill::Block(Block::air(SpriteKind::Lantern));
123
124        // clear area for entries
125        for l in 0..8 {
126            painter
127                .aabb(Aabb {
128                    min: (center - (length / 2) - l).with_z(base + l),
129                    max: (center + (length / 2) + l).with_z(base + 1 + l),
130                })
131                .clear();
132        }
133        // top
134        let top_1 = painter.aabb(Aabb {
135            min: Vec2::new(
136                center.x - (length / 2) - (2 * wall_th),
137                center.y - (width / 2) - (2 * wall_th),
138            )
139            .with_z(base + height + wall_th),
140            max: Vec2::new(
141                center.x + (length / 2) + (2 * wall_th),
142                center.y + (width / 2) + (2 * wall_th),
143            )
144            .with_z(base + height + wall_th + top_height),
145        });
146        let top_2 = painter.aabb(Aabb {
147            min: Vec2::new(
148                center.x - (length / 2) - (2 * wall_th) + corner,
149                center.y - (width / 2) - corner - (2 * wall_th),
150            )
151            .with_z(base + height + wall_th),
152            max: Vec2::new(
153                center.x + (length / 2) + (2 * wall_th) - corner,
154                center.y + (width / 2) + corner + (2 * wall_th),
155            )
156            .with_z(base + height + wall_th + top_height),
157        });
158        top_1.union(top_2).fill(sandstone.clone());
159        let top_carve_1 = painter.aabb(Aabb {
160            min: Vec2::new(
161                center.x - (length / 2) - (2 * wall_th) + 1,
162                center.y - (width / 2) - (2 * wall_th) + 1,
163            )
164            .with_z(base + height + wall_th + top_height - 2),
165            max: Vec2::new(
166                center.x + (length / 2) + (2 * wall_th) - 1,
167                center.y + (width / 2) + (2 * wall_th) - 1,
168            )
169            .with_z(base + height + wall_th + top_height),
170        });
171        let top_carve_2 = painter.aabb(Aabb {
172            min: Vec2::new(
173                center.x - (length / 2) - (2 * wall_th) + corner + 1,
174                center.y - (width / 2) - corner - (2 * wall_th) + 1,
175            )
176            .with_z(base + height + wall_th + top_height - 2),
177            max: Vec2::new(
178                center.x + (length / 2) + (2 * wall_th) - corner - 1,
179                center.y + (width / 2) + corner + (2 * wall_th) - 1,
180            )
181            .with_z(base + height + wall_th + 3),
182        });
183        top_carve_1.union(top_carve_2).clear();
184
185        let carve_dots = 80.0_f32;
186        let carve_dots_radius = length + (length / 2);
187        let phi_carve_dots = TAU / carve_dots;
188        for n in 1..=carve_dots as i32 {
189            let dot_pos = Vec2::new(
190                center.x + (carve_dots_radius as f32 * ((n as f32 * phi_carve_dots).cos())) as i32,
191                center.y + (carve_dots_radius as f32 * ((n as f32 * phi_carve_dots).sin())) as i32,
192            );
193            // top decor carve
194            painter
195                .line(
196                    center.with_z(base + height + wall_th + 2),
197                    dot_pos.with_z(base + height + wall_th + 2),
198                    1.5,
199                )
200                .clear();
201        }
202
203        // pillars and spires
204        let mut pillar_positions = vec![];
205        let mut pillars = vec![];
206        let mut spire_positions = vec![];
207
208        for dir in DIAGONALS {
209            let inner_square_pos = center + (dir * ((length / 2) - corner - wall_th));
210            let outer_square_pos_1 = Vec2::new(
211                center.x + (dir.x * ((length / 2) + (corner / 8) - wall_th)),
212                center.y + (dir.y * ((width / 2) - wall_th)),
213            );
214            let outer_square_pos_2 = Vec2::new(
215                center.x + (dir.x * ((length / 2) - corner - wall_th)),
216                center.y + (dir.y * ((width / 2) + (5 * (corner / 4)) - wall_th)),
217            );
218            pillar_positions.push(inner_square_pos);
219            pillar_positions.push(outer_square_pos_1);
220            pillar_positions.push(outer_square_pos_2);
221
222            let spire_pos_1 = Vec2::new(
223                center.x + (dir.x * (length / 10)),
224                center.y + (dir.y * (2 * (length / 5))),
225            );
226
227            let spire_pos_2 = Vec2::new(
228                center.x + (dir.y * (2 * (length / 5))),
229                center.y + (dir.x * (length / 11)),
230            );
231            spire_positions.push(spire_pos_1);
232            spire_positions.push(spire_pos_2);
233        }
234        for pillar_pos in pillar_positions {
235            let height_var = (RandomField::new(0).get(pillar_pos.with_z(base)) % 20) as i32;
236            let pillar_height = height + 8 + height_var;
237            pillars.push((pillar_pos, pillar_height));
238        }
239        for (pillar_pos, pillar_height) in &pillars {
240            // pillar
241            painter
242                .aabb(Aabb {
243                    min: (pillar_pos - pillar_size - wall_th).with_z(base - 10),
244                    max: (pillar_pos + pillar_size + wall_th)
245                        .with_z(base + pillar_height + wall_th),
246                })
247                .fill(sandstone.clone());
248            // carve large
249            painter
250                .aabb(Aabb {
251                    min: Vec2::new(
252                        pillar_pos.x - pillar_size - wall_th,
253                        pillar_pos.y - pillar_size,
254                    )
255                    .with_z(base),
256                    max: Vec2::new(
257                        pillar_pos.x + pillar_size + wall_th,
258                        pillar_pos.y + pillar_size,
259                    )
260                    .with_z(base + pillar_height),
261                })
262                .clear();
263            painter
264                .aabb(Aabb {
265                    min: Vec2::new(
266                        pillar_pos.x - pillar_size,
267                        pillar_pos.y - pillar_size - wall_th,
268                    )
269                    .with_z(base),
270                    max: Vec2::new(
271                        pillar_pos.x + pillar_size,
272                        pillar_pos.y + pillar_size + wall_th,
273                    )
274                    .with_z(base + pillar_height),
275                })
276                .clear();
277            // carve small
278            for dir in DIAGONALS {
279                for c in 0..((pillar_height / 2) - 1) {
280                    let carve_pos = pillar_pos + (dir * (pillar_size + wall_th));
281                    painter
282                        .aabb(Aabb {
283                            min: (carve_pos - 1).with_z(base + wall_th + (c * 2)),
284                            max: (carve_pos + 1).with_z(base + 1 + wall_th + (c * 2)),
285                        })
286                        .clear();
287                }
288            }
289            // upper decor
290            for d in 0..3 {
291                // d1
292                painter
293                    .horizontal_cylinder(
294                        Aabb {
295                            min: Vec2::new(
296                                pillar_pos.x - pillar_size - 1,
297                                pillar_pos.y - 7 + (d * 5),
298                            )
299                            .with_z(base + pillar_height - 6),
300                            max: Vec2::new(
301                                pillar_pos.x + pillar_size + 1,
302                                pillar_pos.y - 3 + (d * 5),
303                            )
304                            .with_z(base + pillar_height),
305                        },
306                        Dir::X,
307                    )
308                    .fill(sandstone.clone());
309                painter
310                    .horizontal_cylinder(
311                        Aabb {
312                            min: Vec2::new(
313                                pillar_pos.x - pillar_size - 1,
314                                pillar_pos.y - 6 + (d * 5),
315                            )
316                            .with_z(base + pillar_height - 5),
317                            max: Vec2::new(
318                                pillar_pos.x + pillar_size + 1,
319                                pillar_pos.y - 4 + (d * 5),
320                            )
321                            .with_z(base + pillar_height - 1),
322                        },
323                        Dir::X,
324                    )
325                    .clear();
326
327                // d2
328                painter
329                    .horizontal_cylinder(
330                        Aabb {
331                            min: Vec2::new(
332                                pillar_pos.x - 7 + (d * 5),
333                                pillar_pos.y - pillar_size - 1,
334                            )
335                            .with_z(base + pillar_height - 6),
336                            max: Vec2::new(
337                                pillar_pos.x - 3 + (d * 5),
338                                pillar_pos.y + pillar_size + 1,
339                            )
340                            .with_z(base + pillar_height),
341                        },
342                        Dir::Y,
343                    )
344                    .fill(sandstone.clone());
345                painter
346                    .horizontal_cylinder(
347                        Aabb {
348                            min: Vec2::new(
349                                pillar_pos.x - 6 + (d * 5),
350                                pillar_pos.y - pillar_size - 1,
351                            )
352                            .with_z(base + pillar_height - 5),
353                            max: Vec2::new(
354                                pillar_pos.x - 4 + (d * 5),
355                                pillar_pos.y + pillar_size + 1,
356                            )
357                            .with_z(base + pillar_height - 1),
358                        },
359                        Dir::Y,
360                    )
361                    .clear();
362            }
363            // arches
364            // a1
365            painter
366                .vault(
367                    Aabb {
368                        min: Vec2::new(
369                            pillar_pos.x - pillar_size,
370                            pillar_pos.y - pillar_size - wall_th + 1,
371                        )
372                        .with_z(base),
373                        max: Vec2::new(
374                            pillar_pos.x + pillar_size,
375                            pillar_pos.y + pillar_size + wall_th - 1,
376                        )
377                        .with_z(base + (4 * pillar_size) + 2),
378                    },
379                    Dir::Y,
380                )
381                .fill(sandstone.clone());
382            painter
383                .vault(
384                    Aabb {
385                        min: Vec2::new(
386                            pillar_pos.x - pillar_size + 2,
387                            pillar_pos.y - pillar_size - wall_th + 1,
388                        )
389                        .with_z(base),
390                        max: Vec2::new(
391                            pillar_pos.x + pillar_size - 2,
392                            pillar_pos.y + pillar_size + wall_th - 1,
393                        )
394                        .with_z(base + (4 * pillar_size)),
395                    },
396                    Dir::Y,
397                )
398                .clear();
399            // a2
400            painter
401                .vault(
402                    Aabb {
403                        min: Vec2::new(
404                            pillar_pos.x - pillar_size + 2,
405                            pillar_pos.y - pillar_size - wall_th + 2,
406                        )
407                        .with_z(base),
408                        max: Vec2::new(
409                            pillar_pos.x + pillar_size - 2,
410                            pillar_pos.y + pillar_size + wall_th - 2,
411                        )
412                        .with_z(base + (4 * pillar_size)),
413                    },
414                    Dir::Y,
415                )
416                .fill(sandstone.clone());
417            painter
418                .vault(
419                    Aabb {
420                        min: Vec2::new(
421                            pillar_pos.x - pillar_size + 4,
422                            pillar_pos.y - pillar_size - wall_th + 2,
423                        )
424                        .with_z(base),
425                        max: Vec2::new(
426                            pillar_pos.x + pillar_size - 4,
427                            pillar_pos.y + pillar_size + wall_th - 2,
428                        )
429                        .with_z(base + (4 * pillar_size) - 2),
430                    },
431                    Dir::Y,
432                )
433                .clear();
434            // b1
435            painter
436                .vault(
437                    Aabb {
438                        min: Vec2::new(
439                            pillar_pos.x - pillar_size - wall_th + 1,
440                            pillar_pos.y - pillar_size,
441                        )
442                        .with_z(base),
443                        max: Vec2::new(
444                            pillar_pos.x + pillar_size + wall_th - 1,
445                            pillar_pos.y + pillar_size,
446                        )
447                        .with_z(base + (4 * pillar_size) + 2),
448                    },
449                    Dir::X,
450                )
451                .fill(sandstone.clone());
452            painter
453                .vault(
454                    Aabb {
455                        min: Vec2::new(
456                            pillar_pos.x - pillar_size - wall_th + 1,
457                            pillar_pos.y - pillar_size + 2,
458                        )
459                        .with_z(base),
460                        max: Vec2::new(
461                            pillar_pos.x + pillar_size + wall_th - 1,
462                            pillar_pos.y + pillar_size - 2,
463                        )
464                        .with_z(base + (4 * pillar_size)),
465                    },
466                    Dir::X,
467                )
468                .clear();
469            // b2
470            painter
471                .vault(
472                    Aabb {
473                        min: Vec2::new(
474                            pillar_pos.x - pillar_size - wall_th + 2,
475                            pillar_pos.y - pillar_size + 2,
476                        )
477                        .with_z(base),
478                        max: Vec2::new(
479                            pillar_pos.x + pillar_size + wall_th - 2,
480                            pillar_pos.y + pillar_size - 2,
481                        )
482                        .with_z(base + (4 * pillar_size)),
483                    },
484                    Dir::X,
485                )
486                .fill(sandstone.clone());
487            painter
488                .vault(
489                    Aabb {
490                        min: Vec2::new(
491                            pillar_pos.x - pillar_size - wall_th + 2,
492                            pillar_pos.y - pillar_size + 4,
493                        )
494                        .with_z(base),
495                        max: Vec2::new(
496                            pillar_pos.x + pillar_size + wall_th - 2,
497                            pillar_pos.y + pillar_size - 4,
498                        )
499                        .with_z(base + (4 * pillar_size) - 2),
500                    },
501                    Dir::X,
502                )
503                .clear();
504            // top
505            painter
506                .aabb(Aabb {
507                    min: (pillar_pos - pillar_size - (2 * wall_th))
508                        .with_z(base + pillar_height + wall_th),
509                    max: (pillar_pos + pillar_size + (2 * wall_th))
510                        .with_z(base + pillar_height + wall_th + top_height),
511                })
512                .fill(sandstone.clone());
513            painter
514                .aabb(Aabb {
515                    min: (pillar_pos - pillar_size - (2 * wall_th) + 1)
516                        .with_z(base + pillar_height + wall_th + top_height - 2),
517                    max: (pillar_pos + pillar_size + (2 * wall_th) - 1)
518                        .with_z(base + pillar_height + wall_th + top_height),
519                })
520                .clear();
521
522            let pillar_inlay = painter.aabb(Aabb {
523                min: (pillar_pos - pillar_size).with_z(base),
524                max: (pillar_pos + pillar_size).with_z(base + pillar_height),
525            });
526            pillar_inlay.fill(color.clone());
527            // decor
528            for r in 0..(pillar_height - 3) {
529                let dots = 8.0_f32 + (r / 3) as f32;
530                let dots_radius = 2 * pillar_size;
531                let phi_dots = TAU / dots;
532                for n in 1..=dots as i32 {
533                    let dot_pos = Vec2::new(
534                        pillar_pos.x + (dots_radius as f32 * ((n as f32 * phi_dots).cos())) as i32,
535                        pillar_pos.y + (dots_radius as f32 * ((n as f32 * phi_dots).sin())) as i32,
536                    );
537                    if dots == 16.0_f32 {
538                        // top decor carve
539                        painter
540                            .line(
541                                pillar_pos.with_z(base + pillar_height + wall_th + 2),
542                                dot_pos.with_z(base + pillar_height + wall_th + 2),
543                                1.5,
544                            )
545                            .clear();
546                    }
547                    // dots
548                    painter
549                        .line(
550                            pillar_pos.with_z(base + (r * 2)),
551                            dot_pos.with_z(base + (r * 2)),
552                            0.5,
553                        )
554                        .intersect(pillar_inlay)
555                        .fill(sandstone.clone());
556                }
557            }
558        }
559
560        // arena
561        let outer_aabb_1 = painter.aabb(Aabb {
562            min: Vec2::new(
563                center.x - (length / 2) - wall_th,
564                center.y - (width / 2) - wall_th,
565            )
566            .with_z(base - 10),
567            max: Vec2::new(
568                center.x + (length / 2) + wall_th,
569                center.y + (width / 2) + wall_th,
570            )
571            .with_z(base + height + wall_th),
572        });
573        let outer_aabb_2 = painter.aabb(Aabb {
574            min: Vec2::new(
575                center.x - (length / 2) - wall_th + corner,
576                center.y - (width / 2) - corner - wall_th,
577            )
578            .with_z(base - 10),
579            max: Vec2::new(
580                center.x + (length / 2) + wall_th - corner,
581                center.y + (width / 2) + corner + wall_th,
582            )
583            .with_z(base + height + wall_th),
584        });
585        outer_aabb_1.union(outer_aabb_2).fill(sandstone.clone());
586        // decor carve
587        let clear_aabb_1 = painter.aabb(Aabb {
588            min: Vec2::new(center.x - (length / 2) - wall_th, center.y - (width / 2)).with_z(base),
589            max: Vec2::new(center.x + (length / 2) + wall_th, center.y + (width / 2))
590                .with_z(base + height),
591        });
592        let clear_aabb_2 = painter.aabb(Aabb {
593            min: Vec2::new(center.x - (length / 2), center.y - (width / 2) - wall_th).with_z(base),
594            max: Vec2::new(center.x + (length / 2), center.y + (width / 2) + wall_th)
595                .with_z(base + height),
596        });
597        clear_aabb_1.union(clear_aabb_2).clear();
598        let clear_aabb_3 = painter.aabb(Aabb {
599            min: Vec2::new(
600                center.x - (length / 2) - wall_th + corner,
601                center.y - (width / 2) - corner,
602            )
603            .with_z(base),
604            max: Vec2::new(
605                center.x + (length / 2) + wall_th - corner,
606                center.y + (width / 2) + corner,
607            )
608            .with_z(base + height),
609        });
610        let clear_aabb_4 = painter.aabb(Aabb {
611            min: Vec2::new(
612                center.x - (length / 2) + corner,
613                center.y - (width / 2) - wall_th - corner,
614            )
615            .with_z(base),
616            max: Vec2::new(
617                center.x + (length / 2) - corner,
618                center.y + (width / 2) + wall_th + corner,
619            )
620            .with_z(base + height),
621        });
622        clear_aabb_3.union(clear_aabb_4).clear();
623        // color inlay
624        let inlay_aabb_1 = painter.aabb(Aabb {
625            min: Vec2::new(center.x - (length / 2), center.y - (width / 2)).with_z(base),
626            max: Vec2::new(center.x + (length / 2), center.y + (width / 2)).with_z(base + height),
627        });
628        let inlay_aabb_2 = painter.aabb(Aabb {
629            min: Vec2::new(
630                center.x - (length / 2) + corner,
631                center.y - (width / 2) - corner,
632            )
633            .with_z(base),
634            max: Vec2::new(
635                center.x + (length / 2) - corner,
636                center.y + (width / 2) + corner,
637            )
638            .with_z(base + height),
639        });
640        inlay_aabb_1.union(inlay_aabb_2).fill(color.clone());
641        let inlay = inlay_aabb_1.union(inlay_aabb_2);
642        for r in 0..(height - 3) {
643            let dots = 50.0_f32 + (3 * r) as f32;
644            let dots_radius = length + (length / 2);
645            let phi_dots = TAU / dots;
646            for n in 1..=dots as i32 {
647                let dot_pos = Vec2::new(
648                    center.x + (dots_radius as f32 * ((n as f32 * phi_dots).cos())) as i32,
649                    center.y + (dots_radius as f32 * ((n as f32 * phi_dots).sin())) as i32,
650                );
651                // color decor
652                painter
653                    .line(
654                        center.with_z(base + (r * 2)),
655                        dot_pos.with_z(base + (r * 2)),
656                        1.0,
657                    )
658                    .intersect(inlay)
659                    .fill(sandstone.clone());
660            }
661        }
662        // entries
663        painter
664            .vault(
665                Aabb {
666                    min: Vec2::new(center.x - (length / 2) - wall_th, center.y - 10).with_z(base),
667                    max: Vec2::new(center.x + (length / 2) + wall_th, center.y + 10)
668                        .with_z(base + (height / 2) + 8),
669                },
670                Dir::X,
671            )
672            .fill(sandstone.clone());
673        painter
674            .vault(
675                Aabb {
676                    min: Vec2::new(center.x - 10, center.y - (width / 2) - corner - wall_th)
677                        .with_z(base),
678                    max: Vec2::new(center.x + 10, center.y + (width / 2) + corner + wall_th)
679                        .with_z(base + (height / 2) + 8),
680                },
681                Dir::Y,
682            )
683            .fill(sandstone.clone());
684        painter
685            .vault(
686                Aabb {
687                    min: Vec2::new(center.x - (length / 2) - wall_th, center.y - 10 + wall_th)
688                        .with_z(base),
689                    max: Vec2::new(center.x + (length / 2) + wall_th, center.y + 10 - wall_th)
690                        .with_z(base + (height / 2) + 8 - wall_th),
691                },
692                Dir::X,
693            )
694            .clear();
695        painter
696            .vault(
697                Aabb {
698                    min: Vec2::new(
699                        center.x - 10 + wall_th,
700                        center.y - (width / 2) - corner - wall_th,
701                    )
702                    .with_z(base),
703                    max: Vec2::new(
704                        center.x + 10 - wall_th,
705                        center.y + (width / 2) + corner + wall_th,
706                    )
707                    .with_z(base + (height / 2) + 8 - wall_th),
708                },
709                Dir::Y,
710            )
711            .clear();
712        // center clear
713        painter
714            .aabb(Aabb {
715                min: Vec2::new(
716                    center.x - (length / 2) + corner + (2 * wall_th) + pillar_size,
717                    center.y - (length / 2) + corner + (2 * wall_th) + pillar_size,
718                )
719                .with_z(base + height),
720                max: Vec2::new(
721                    center.x + (length / 2) - corner - (2 * wall_th) - pillar_size,
722                    center.y + (length / 2) - corner - (2 * wall_th) - pillar_size,
723                )
724                .with_z(base + height + wall_th + top_height - 2),
725            })
726            .clear();
727        painter
728            .aabb(Aabb {
729                min: Vec2::new(
730                    center.x - (length / 2) + corner + wall_th,
731                    center.y - (length / 2) + corner + wall_th,
732                )
733                .with_z(base - 1),
734                max: Vec2::new(
735                    center.x + (length / 2) - corner - wall_th,
736                    center.y + (length / 2) - corner - wall_th,
737                )
738                .with_z(base + height),
739            })
740            .clear();
741        // center decor
742        for d in 0..((length / 12) - 2) {
743            // d1
744            painter
745                .horizontal_cylinder(
746                    Aabb {
747                        min: Vec2::new(
748                            center.x - 3 - (length / 2)
749                                + corner
750                                + (3 * wall_th)
751                                + pillar_size
752                                + (6 * d)
753                                + 2,
754                            center.y - (length / 2) + corner + (2 * wall_th) + pillar_size - 1,
755                        )
756                        .with_z(base + height + wall_th + top_height - 7),
757                        max: Vec2::new(
758                            center.x + 3 - (length / 2)
759                                + corner
760                                + (3 * wall_th)
761                                + pillar_size
762                                + (6 * d)
763                                + 2,
764                            center.y + (length / 2) - corner - (2 * wall_th) - pillar_size + 1,
765                        )
766                        .with_z(base + height + wall_th + top_height - 1),
767                    },
768                    Dir::Y,
769                )
770                .fill(sandstone.clone());
771            painter
772                .horizontal_cylinder(
773                    Aabb {
774                        min: Vec2::new(
775                            center.x - 3 - (length / 2)
776                                + corner
777                                + (3 * wall_th)
778                                + pillar_size
779                                + (6 * d)
780                                + 2,
781                            center.y - (length / 2) + corner + (2 * wall_th) + pillar_size,
782                        )
783                        .with_z(base + height + wall_th + top_height - 7),
784                        max: Vec2::new(
785                            center.x + 3 - (length / 2)
786                                + corner
787                                + (3 * wall_th)
788                                + pillar_size
789                                + (6 * d)
790                                + 2,
791                            center.y + (length / 2) - corner - (2 * wall_th) - pillar_size,
792                        )
793                        .with_z(base + height + wall_th + top_height - 1),
794                    },
795                    Dir::Y,
796                )
797                .clear();
798            painter
799                .horizontal_cylinder(
800                    Aabb {
801                        min: Vec2::new(
802                            center.x - 2 - (length / 2)
803                                + corner
804                                + (3 * wall_th)
805                                + pillar_size
806                                + (6 * d)
807                                + 2,
808                            center.y - (length / 2) + corner + (2 * wall_th) + pillar_size - 2,
809                        )
810                        .with_z(base + height + wall_th + top_height - 6),
811                        max: Vec2::new(
812                            center.x + 2 - (length / 2)
813                                + corner
814                                + (3 * wall_th)
815                                + pillar_size
816                                + (6 * d)
817                                + 2,
818                            center.y + (length / 2) - corner - (2 * wall_th) - pillar_size + 2,
819                        )
820                        .with_z(base + height + wall_th + top_height - 2),
821                    },
822                    Dir::Y,
823                )
824                .fill(color.clone());
825            painter
826                .horizontal_cylinder(
827                    Aabb {
828                        min: Vec2::new(
829                            center.x - 2 - (length / 2)
830                                + corner
831                                + (3 * wall_th)
832                                + pillar_size
833                                + (6 * d)
834                                + 2,
835                            center.y - (length / 2) + corner + (2 * wall_th) + pillar_size - 1,
836                        )
837                        .with_z(base + height + wall_th + top_height - 6),
838                        max: Vec2::new(
839                            center.x + 2 - (length / 2)
840                                + corner
841                                + (3 * wall_th)
842                                + pillar_size
843                                + (6 * d)
844                                + 2,
845                            center.y + (length / 2) - corner - (2 * wall_th) - pillar_size + 1,
846                        )
847                        .with_z(base + height + wall_th + top_height - 2),
848                    },
849                    Dir::Y,
850                )
851                .clear();
852        }
853        for e in 0..(length / 14) {
854            // d2
855            painter
856                .horizontal_cylinder(
857                    Aabb {
858                        min: Vec2::new(
859                            center.x - (length / 2) + corner + (2 * wall_th) + pillar_size - 1,
860                            center.y - 3 - (length / 2)
861                                + corner
862                                + (2 * wall_th)
863                                + pillar_size
864                                + (6 * e)
865                                + 5,
866                        )
867                        .with_z(base + height + wall_th + top_height - 7),
868                        max: Vec2::new(
869                            center.x + (length / 2) - corner - (2 * wall_th) - pillar_size + 1,
870                            center.y + 3 - (length / 2)
871                                + corner
872                                + (2 * wall_th)
873                                + pillar_size
874                                + (6 * e)
875                                + 5,
876                        )
877                        .with_z(base + height + wall_th + top_height - 1),
878                    },
879                    Dir::X,
880                )
881                .fill(sandstone.clone());
882            painter
883                .horizontal_cylinder(
884                    Aabb {
885                        min: Vec2::new(
886                            center.x - (length / 2) + corner + (2 * wall_th) + pillar_size,
887                            center.y - 3 - (length / 2)
888                                + corner
889                                + (2 * wall_th)
890                                + pillar_size
891                                + (6 * e)
892                                + 5,
893                        )
894                        .with_z(base + height + wall_th + top_height - 7),
895                        max: Vec2::new(
896                            center.x + (length / 2) - corner - (2 * wall_th) - pillar_size,
897                            center.y + 3 - (length / 2)
898                                + corner
899                                + (2 * wall_th)
900                                + pillar_size
901                                + (6 * e)
902                                + 5,
903                        )
904                        .with_z(base + height + wall_th + top_height - 1),
905                    },
906                    Dir::X,
907                )
908                .clear();
909            painter
910                .horizontal_cylinder(
911                    Aabb {
912                        min: Vec2::new(
913                            center.x - (length / 2) + corner + (2 * wall_th) + pillar_size - 2,
914                            center.y - 2 - (length / 2)
915                                + corner
916                                + (2 * wall_th)
917                                + pillar_size
918                                + (6 * e)
919                                + 5,
920                        )
921                        .with_z(base + height + wall_th + top_height - 6),
922                        max: Vec2::new(
923                            center.x + (length / 2) - corner - (2 * wall_th) - pillar_size + 2,
924                            center.y + 2 - (length / 2)
925                                + corner
926                                + (2 * wall_th)
927                                + pillar_size
928                                + (6 * e)
929                                + 5,
930                        )
931                        .with_z(base + height + wall_th + top_height - 2),
932                    },
933                    Dir::X,
934                )
935                .fill(color.clone());
936            painter
937                .horizontal_cylinder(
938                    Aabb {
939                        min: Vec2::new(
940                            center.x - (length / 2) + corner + (2 * wall_th) + pillar_size - 1,
941                            center.y - 2 - (length / 2)
942                                + corner
943                                + (2 * wall_th)
944                                + pillar_size
945                                + (6 * e)
946                                + 5,
947                        )
948                        .with_z(base + height + wall_th + top_height - 6),
949                        max: Vec2::new(
950                            center.x + (length / 2) - corner - (2 * wall_th) - pillar_size + 1,
951                            center.y + 2 - (length / 2)
952                                + corner
953                                + (2 * wall_th)
954                                + pillar_size
955                                + (6 * e)
956                                + 5,
957                        )
958                        .with_z(base + height + wall_th + top_height - 2),
959                    },
960                    Dir::X,
961                )
962                .clear();
963        }
964
965        // entry steps
966        for dir in CARDINALS {
967            let step_pos = Vec2::new(
968                center.x + (dir.x * (length / 2)),
969                center.y + (dir.y * ((width / 2) + corner)),
970            );
971            for s in 0..9 {
972                painter
973                    .aabb(Aabb {
974                        min: (step_pos - 7 - s).with_z(base - 2 - s),
975                        max: (step_pos + 7 + s).with_z(base - 1 - s),
976                    })
977                    .fill(sandstone.clone());
978            }
979        }
980        // clear rooms
981        for r in 0..2 {
982            let room_pos_1 = Vec2::new(
983                center.x - (length / 2) + (length / 8) + (r * (length - (length / 4))),
984                center.y,
985            );
986            let room_pos_2 = Vec2::new(
987                center.x,
988                center.y - (width / 2) - (corner / 2) + (r * (width + corner)),
989            );
990
991            painter
992                .aabb(Aabb {
993                    min: Vec2::new(
994                        room_pos_1.x - (length / 8) + wall_th,
995                        room_pos_1.y - (width / 2) + wall_th,
996                    )
997                    .with_z(base - 1),
998                    max: Vec2::new(
999                        room_pos_1.x + (length / 8) - wall_th,
1000                        room_pos_1.y + (width / 2) - wall_th,
1001                    )
1002                    .with_z(base + height),
1003                })
1004                .clear();
1005            painter
1006                .aabb(Aabb {
1007                    min: Vec2::new(
1008                        room_pos_2.x - (length / 2) + corner + wall_th,
1009                        room_pos_2.y - (corner / 2) + wall_th,
1010                    )
1011                    .with_z(base - 1),
1012                    max: Vec2::new(
1013                        room_pos_2.x + (length / 2) - corner - wall_th,
1014                        room_pos_2.y + (corner / 2) - wall_th,
1015                    )
1016                    .with_z(base + height),
1017                })
1018                .clear();
1019            // stands
1020            for s in 0..1 {
1021                // distance from center
1022                let stand_dist = self.stand_dist;
1023                let stand_length = self.stand_length;
1024                let stand_width = self.stand_width;
1025                let floor = s * (height + wall_th + top_height - 1);
1026
1027                painter
1028                    .ramp_inset(
1029                        Aabb {
1030                            min: Vec2::new(center.x - stand_length - 1, center.y - stand_dist)
1031                                .with_z(base - 1 + floor),
1032                            max: Vec2::new(
1033                                center.x + stand_length + 1,
1034                                center.y - stand_dist + stand_width,
1035                            )
1036                            .with_z(base + (length / 16) - 1 + floor),
1037                        },
1038                        length / 16,
1039                        Dir::NegY,
1040                    )
1041                    .fill(color.clone());
1042                painter
1043                    .ramp_inset(
1044                        Aabb {
1045                            min: Vec2::new(center.x - stand_length, center.y - stand_dist)
1046                                .with_z(base - 1 + floor),
1047                            max: Vec2::new(
1048                                center.x + stand_length,
1049                                center.y - stand_dist + stand_width,
1050                            )
1051                            .with_z(base + (length / 16) - 1 + floor),
1052                        },
1053                        length / 16,
1054                        Dir::NegY,
1055                    )
1056                    .fill(sandstone.clone());
1057                painter
1058                    .ramp_inset(
1059                        Aabb {
1060                            min: Vec2::new(
1061                                center.x - stand_length - 1,
1062                                center.y + stand_dist - stand_width,
1063                            )
1064                            .with_z(base - 1 + floor),
1065                            max: Vec2::new(center.x + stand_length + 1, center.y + stand_dist)
1066                                .with_z(base + (length / 16) - 1 + floor),
1067                        },
1068                        length / 16,
1069                        Dir::Y,
1070                    )
1071                    .fill(color.clone());
1072
1073                painter
1074                    .ramp_inset(
1075                        Aabb {
1076                            min: Vec2::new(
1077                                center.x - stand_length,
1078                                center.y + stand_dist - stand_width,
1079                            )
1080                            .with_z(base - 1 + floor),
1081                            max: Vec2::new(center.x + stand_length, center.y + stand_dist)
1082                                .with_z(base + (length / 16) - 1 + floor),
1083                        },
1084                        length / 16,
1085                        Dir::Y,
1086                    )
1087                    .fill(sandstone.clone());
1088                painter
1089                    .ramp_inset(
1090                        Aabb {
1091                            min: Vec2::new(center.x - stand_dist, center.y - stand_length - 1)
1092                                .with_z(base - 1 + floor),
1093                            max: Vec2::new(
1094                                center.x - stand_dist + stand_width,
1095                                center.y + stand_length + 1,
1096                            )
1097                            .with_z(base + (length / 16) - 1 + floor),
1098                        },
1099                        length / 16,
1100                        Dir::NegX,
1101                    )
1102                    .fill(color.clone());
1103                painter
1104                    .ramp_inset(
1105                        Aabb {
1106                            min: Vec2::new(center.x - stand_dist, center.y - stand_length)
1107                                .with_z(base - 1 + floor),
1108                            max: Vec2::new(
1109                                center.x - stand_dist + stand_width,
1110                                center.y + stand_length,
1111                            )
1112                            .with_z(base + (length / 16) - 1 + floor),
1113                        },
1114                        length / 16,
1115                        Dir::NegX,
1116                    )
1117                    .fill(sandstone.clone());
1118                painter
1119                    .ramp_inset(
1120                        Aabb {
1121                            min: Vec2::new(
1122                                center.x + stand_dist - stand_width,
1123                                center.y - stand_length - 1,
1124                            )
1125                            .with_z(base - 1 + floor),
1126                            max: Vec2::new(center.x + stand_dist, center.y + stand_length + 1)
1127                                .with_z(base + (length / 16) - 1 + floor),
1128                        },
1129                        length / 16,
1130                        Dir::X,
1131                    )
1132                    .fill(color.clone());
1133                painter
1134                    .ramp_inset(
1135                        Aabb {
1136                            min: Vec2::new(
1137                                center.x + stand_dist - stand_width,
1138                                center.y - stand_length,
1139                            )
1140                            .with_z(base - 1 + floor),
1141                            max: Vec2::new(center.x + stand_dist, center.y + stand_length)
1142                                .with_z(base + (length / 16) - 1 + floor),
1143                        },
1144                        length / 16,
1145                        Dir::X,
1146                    )
1147                    .fill(sandstone.clone());
1148            }
1149        }
1150        for (pillar_pos, pillar_height) in pillars {
1151            let stairs_radius = pillar_size - 1;
1152            let stairs = painter.aabb(Aabb {
1153                min: (pillar_pos - stairs_radius).with_z(base - 1),
1154                max: (pillar_pos + stairs_radius).with_z(base + pillar_height + wall_th + 1),
1155            });
1156            stairs.clear();
1157            stairs
1158                .sample(spiral_staircase(
1159                    pillar_pos.with_z(base + pillar_height + wall_th + 1),
1160                    (stairs_radius + 2) as f32,
1161                    0.5,
1162                    ((pillar_height + wall_th + top_height) / 3) as f32,
1163                ))
1164                .fill(sandstone.clone());
1165        }
1166        for spire_pos in &spire_positions {
1167            let spire_height =
1168                (height / 3) + (RandomField::new(0).get(spire_pos.with_z(base)) % 6) as i32;
1169            painter
1170                .cylinder(Aabb {
1171                    min: (spire_pos - pillar_size - 1)
1172                        .with_z(base + height + wall_th + top_height - 2),
1173                    max: (spire_pos + pillar_size + 2)
1174                        .with_z(base + height + wall_th + top_height + 6),
1175                })
1176                .fill(sandstone.clone());
1177            painter
1178                .cylinder(Aabb {
1179                    min: (spire_pos - pillar_size).with_z(base + height + wall_th + top_height + 6),
1180                    max: (spire_pos + pillar_size + 1)
1181                        .with_z(base + height + wall_th + top_height + spire_height),
1182                })
1183                .fill(color.clone());
1184            for r in 0..((spire_height / 2) - 3) {
1185                let spire_dots = 16.0_f32 + (2 * r) as f32;
1186                let spire_dots_radius = pillar_size as f32 + 0.5;
1187                let phi_spire_dots = TAU / spire_dots;
1188
1189                for n in 1..=spire_dots as i32 {
1190                    let spire_dot_pos = Vec2::new(
1191                        spire_pos.x
1192                            + (spire_dots_radius * ((n as f32 * phi_spire_dots).cos())) as i32,
1193                        spire_pos.y
1194                            + (spire_dots_radius * ((n as f32 * phi_spire_dots).sin())) as i32,
1195                    );
1196                    // color decor
1197                    painter
1198                        .line(
1199                            spire_pos.with_z(base + height + wall_th + top_height + 6 + (r * 2)),
1200                            spire_dot_pos
1201                                .with_z(base + height + wall_th + top_height + 6 + (r * 2)),
1202                            1.0,
1203                        )
1204                        .fill(sandstone.clone());
1205                }
1206            }
1207
1208            painter
1209                .cylinder(Aabb {
1210                    min: (spire_pos - pillar_size - 1)
1211                        .with_z(base + height + wall_th + top_height + spire_height),
1212                    max: (spire_pos + pillar_size + 2)
1213                        .with_z(base + height + wall_th + top_height + spire_height + 1),
1214                })
1215                .fill(sandstone.clone());
1216            painter
1217                .sphere(Aabb {
1218                    min: (spire_pos - pillar_size)
1219                        .with_z(base + height + wall_th + top_height + spire_height - pillar_size),
1220                    max: (spire_pos + pillar_size + 1)
1221                        .with_z(base + height + wall_th + top_height + spire_height + pillar_size),
1222                })
1223                .fill(color.clone());
1224            painter
1225                .cone(Aabb {
1226                    min: (spire_pos - 2)
1227                        .with_z(base + height + wall_th + top_height + spire_height + pillar_size),
1228                    max: (spire_pos + 3).with_z(
1229                        base + height
1230                            + wall_th
1231                            + top_height
1232                            + spire_height
1233                            + pillar_size
1234                            + (spire_height / 3),
1235                    ),
1236                })
1237                .fill(sandstone.clone());
1238            // campfires & repair benches
1239            painter.spawn(
1240                EntityInfo::at((spire_pos - 2).with_z(base - 1).as_())
1241                    .into_special(SpecialEntity::Waypoint),
1242            );
1243            painter.spawn(EntityInfo::at(center.with_z(base).as_()).into_special(
1244                SpecialEntity::ArenaTotem {
1245                    range: length as f32,
1246                },
1247            ));
1248            painter.sprite((spire_pos + 2).with_z(base - 1), SpriteKind::RepairBench);
1249
1250            // lamps
1251            let lamps = 8.0_f32;
1252            let lamps_radius = 3;
1253            let phi_lamps = TAU / lamps;
1254            for n in 1..=lamps as i32 {
1255                let lamp_pos = Vec2::new(
1256                    spire_pos.x + (lamps_radius as f32 * ((n as f32 * phi_lamps).cos())) as i32,
1257                    spire_pos.y + (lamps_radius as f32 * ((n as f32 * phi_lamps).sin())) as i32,
1258                );
1259                let lamp_var = (RandomField::new(0).get(lamp_pos.with_z(base)) % 8) as i32;
1260                painter
1261                    .aabb(Aabb {
1262                        min: lamp_pos.with_z(base + height - 8 - lamp_var),
1263                        max: (lamp_pos + 1).with_z(base + height),
1264                    })
1265                    .fill(chain.clone());
1266                painter
1267                    .aabb(Aabb {
1268                        min: lamp_pos.with_z(base + height - 9 - lamp_var),
1269                        max: (lamp_pos + 1).with_z(base + height - 8 - lamp_var),
1270                    })
1271                    .fill(lantern.clone());
1272            }
1273        }
1274    }
1275}