Skip to main content

veloren_world/site/plot/
vampire_castle.rs

1use super::*;
2use crate::{
3    Land,
4    site::generation::wall_staircase,
5    util::{CARDINALS, DIAGONALS, NEIGHBORS, RandomField, sampler::Sampler, within_distance},
6};
7use common::{generation::EntityInfo, terrain::SpriteKind};
8use rand::prelude::*;
9use std::{f32::consts::TAU, sync::Arc};
10use vek::*;
11
12pub struct CastleData {
13    plot_base: i32,
14    center: Vec2<i32>,
15    side_bldg_pos_1: Vec2<i32>,
16    side_bldg_pos_2: Vec2<i32>,
17    side_bldg_positions: Vec<Vec2<i32>>,
18    tower_positions: Vec<Vec2<i32>>,
19}
20
21pub struct VampireCastle {
22    bounds: Aabr<i32>,
23    pub(crate) alt: i32,
24    pub(crate) castle_data: CastleData,
25}
26impl VampireCastle {
27    pub fn generate(land: &Land, _rng: &mut impl Rng, site: &Site, tile_aabr: Aabr<i32>) -> Self {
28        let bounds = Aabr {
29            min: site.tile_wpos(tile_aabr.min),
30            max: site.tile_wpos(tile_aabr.max),
31        };
32        let center = bounds.center();
33        let plot_base = land.get_alt_approx(center) as i32;
34        let castle_length = 24;
35        let castle_width = 18;
36        let tower_base = plot_base + 1;
37        let mut tower_positions = vec![];
38        let mut side_bldg_positions = vec![];
39        let towers = 12.0;
40        let tower_radius_raw = 105;
41        let tower_phi = TAU / towers;
42        let side_bldg_var = (RandomField::new(0).get(center.with_z(plot_base)) % 2) as i32;
43        let side_bldg_pos_1 = Vec2::new(
44            center.x,
45            center.y - (2 * (castle_length + 4)) + side_bldg_var * (4 * (castle_length + 4)),
46        );
47        let side_bldg_pos_2 = Vec2::new(
48            center.x,
49            center.y + (2 * (castle_length + 4)) - side_bldg_var * (4 * (castle_length + 4)),
50        );
51        side_bldg_positions.push(side_bldg_pos_1);
52        side_bldg_positions.push(side_bldg_pos_2);
53
54        // castle towers
55        for dir in DIAGONALS {
56            let tower_pos = Vec2::new(
57                center.x + dir.x * (castle_length + 10),
58                center.y + dir.y * (castle_width + 10),
59            );
60            tower_positions.push(tower_pos);
61        }
62        // outer towers
63        for n in 1..=towers as i32 {
64            let tower_pos_var = RandomField::new(0).get((center + n).with_z(tower_base)) % 10;
65            let tower_radius = tower_radius_raw + tower_pos_var as i32;
66            let tower_pos = Vec2::new(
67                center.x + (tower_radius as f32 * ((n as f32 * tower_phi).cos())) as i32,
68                center.y + (tower_radius as f32 * ((n as f32 * tower_phi).sin())) as i32,
69            );
70
71            if RandomField::new(0).get((center + n).with_z(tower_base)) % 8 < 3 {
72                tower_positions.push(tower_pos)
73            } else {
74                side_bldg_positions.push(tower_pos);
75            }
76        }
77        let castle_data = CastleData {
78            plot_base,
79            center,
80            side_bldg_pos_1,
81            side_bldg_pos_2,
82            side_bldg_positions,
83            tower_positions,
84        };
85
86        Self {
87            bounds,
88            alt: land.get_alt_approx(site.tile_center_wpos((tile_aabr.max - tile_aabr.min) / 2))
89                as i32,
90            castle_data,
91        }
92    }
93}
94
95impl Structure for VampireCastle {
96    #[cfg(feature = "dyn-lib")]
97    #[unsafe(export_name = "as_dyn_structure_vampirecastle")]
98    fn as_dyn_outer(&self) -> Option<(&dyn Structure, &'static str)> {
99        Some((Self::as_dyn_impl(self), "as_dyn_structure_vampirecastle"))
100    }
101
102    fn spawn_rules_inner(
103        &self,
104        spawn_rules: &mut SpawnRules,
105        _land: &Land,
106        wpos: Vec2<i32>,
107        _weight: f32,
108    ) {
109        spawn_rules.trees &= !within_distance(wpos, self.bounds.center(), 75);
110        spawn_rules.waypoints = false;
111    }
112
113    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
114        let mut rng = rand::rng();
115        let brick = Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24);
116        let roof_color = Fill::Block(Block::new(BlockKind::GlowingRock, Rgb::new(30, 37, 55)));
117        let wood = Fill::Brick(BlockKind::Rock, Rgb::new(71, 33, 11), 12);
118        let chain = Fill::Block(Block::air(SpriteKind::MetalChain));
119        let window_ver = Fill::Block(Block::air(SpriteKind::WitchWindow));
120        let window_ver2 = Fill::Block(Block::air(SpriteKind::WitchWindow));
121        let key_door = Fill::Block(Block::air(SpriteKind::VampireKeyDoor));
122        let key_hole = Fill::Block(Block::air(SpriteKind::VampireKeyhole));
123        let onewaydoor = Fill::Block(Block::air(SpriteKind::OneWayWall).with_ori(2).unwrap());
124        let candles = Fill::Sampling(Arc::new(|wpos| {
125            Some(match (RandomField::new(0).get(wpos)) % 4 {
126                0 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
127                _ => Block::air(SpriteKind::Candle),
128            })
129        }));
130        let candles_lite = Fill::Sampling(Arc::new(|wpos| {
131            Some(match (RandomField::new(0).get(wpos)) % 30 {
132                0 => Block::air(SpriteKind::Candle),
133                _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
134            })
135        }));
136        // castle
137        let center = self.castle_data.center;
138        let plot_base = self.castle_data.plot_base;
139        let side_bldg_pos_1 = self.castle_data.side_bldg_pos_1;
140        let side_bldg_pos_2 = self.castle_data.side_bldg_pos_2;
141        let side_bldg_positions = &self.castle_data.side_bldg_positions;
142        let tower_positions = &self.castle_data.tower_positions;
143
144        let castle_base = plot_base + 1;
145        let castle_length = 24;
146        let castle_width = 18;
147        let castle_height = castle_length;
148        // entry
149        let entry_length = 12;
150        let entry_width = 9;
151        let entry_height = entry_length;
152        let entry_base = castle_base - 2;
153        // towers
154        let tower_base = plot_base + 1;
155        let tower_width = 16;
156        let tower_height_raw = 70;
157        let roof_width = tower_width + 2;
158        let roof_height = 3 * (tower_width / 2);
159        let top_height = 20;
160        // side buildings
161        let side_bldg_length = 12;
162        let side_bldg_width = 9;
163        let side_bldg_height = 12;
164        let side_bldg_roof_height_raw = 32;
165        let side_bldg_base_raw = castle_base;
166        let side_bldg_var = (RandomField::new(0).get(center.with_z(plot_base)) % 2) as i32;
167        let side_bldg_stairs_pos = side_bldg_pos_1;
168        let mut bat_positions = vec![];
169        let mut harlequin_positions = vec![];
170        let mut random_npc_positions = vec![];
171        // castle main entry
172        let entry_pos = Vec2::new(center.x - castle_length - 20, center.y);
173        let entry_pillar_pos = Vec2::new(center.x - castle_length - 24, center.y);
174        painter
175            .aabb(Aabb {
176                min: Vec2::new(
177                    entry_pos.x - entry_length - 10,
178                    entry_pos.y - entry_width - 10,
179                )
180                .with_z(entry_base + (entry_height / 2) + (2 * entry_height)),
181                max: Vec2::new(
182                    entry_pos.x + entry_length + 10,
183                    entry_pos.y + entry_width + 10,
184                )
185                .with_z(entry_base + (entry_height / 2) + (3 * entry_height)),
186            })
187            .fill(brick.clone());
188        painter
189            .aabb(Aabb {
190                min: Vec2::new(
191                    entry_pos.x - entry_length - 9,
192                    entry_pos.y - entry_width - 9,
193                )
194                .with_z(entry_base + (entry_height / 2) + (2 * entry_height)),
195                max: Vec2::new(
196                    entry_pos.x + entry_length + 9,
197                    entry_pos.y + entry_width + 9,
198                )
199                .with_z(entry_base + (entry_height / 2) + (3 * entry_height)),
200            })
201            .fill(roof_color.clone());
202        painter
203            .aabb(Aabb {
204                min: Vec2::new(
205                    entry_pos.x - entry_length - 5,
206                    entry_pos.y - entry_width - 5,
207                )
208                .with_z(entry_base + (entry_height / 2) + (2 * entry_height) - 3),
209                max: Vec2::new(
210                    entry_pos.x + entry_length + 5,
211                    entry_pos.y + entry_width + 5,
212                )
213                .with_z(entry_base + (entry_height / 2) + (2 * entry_height)),
214            })
215            .fill(brick.clone());
216        // entry decor
217        let entry_decor_limiter = painter.aabb(Aabb {
218            min: Vec2::new(
219                entry_pos.x - entry_length - 9,
220                entry_pos.y - entry_width - 9,
221            )
222            .with_z(entry_base + (entry_height / 2) + (2 * entry_height) - 1),
223            max: Vec2::new(
224                entry_pos.x + entry_length + 9,
225                entry_pos.y + entry_width + 9,
226            )
227            .with_z(entry_base + (entry_height / 2) + (2 * entry_height)),
228        });
229        let entry_decor_var = RandomField::new(0).get(center.with_z(tower_base)) % 12;
230        let entry_decors = 12.0 + entry_decor_var as f32;
231        let entry_phi = TAU / entry_decors;
232        let entry_decor_radius = entry_length + 10;
233        for d in 1..=entry_decors as i32 {
234            let entry_decors_pos = Vec2::new(
235                entry_pos.x + (entry_decor_radius as f32 * ((d as f32 * entry_phi).cos())) as i32,
236                entry_pos.y + (entry_decor_radius as f32 * ((d as f32 * entry_phi).sin())) as i32,
237            );
238            painter
239                .line(
240                    entry_pos.with_z(entry_base + (entry_height / 2) + (2 * entry_height) - 1),
241                    entry_decors_pos
242                        .with_z(entry_base + (entry_height / 2) + (2 * entry_height) - 1),
243                    1.0,
244                )
245                .intersect(entry_decor_limiter)
246                .fill(brick.clone());
247        }
248        // entry roof carve
249        for c in 0..2 {
250            let w_carve_pos = Vec2::new(
251                entry_pos.x,
252                entry_pos.y - (2 * entry_width) + (c * (4 * entry_width)),
253            );
254            let l_carve_pos = Vec2::new(
255                entry_pos.x - (4 * (entry_length / 2)) + (c * (8 * (entry_length / 2))),
256                entry_pos.y,
257            );
258            painter
259                .superquadric(
260                    Aabb {
261                        min: Vec2::new(
262                            w_carve_pos.x - entry_length - 20,
263                            w_carve_pos.y - entry_width - 10,
264                        )
265                        .with_z(entry_base + (entry_height / 2) + (2 * entry_height)),
266                        max: Vec2::new(
267                            w_carve_pos.x + entry_length + 20,
268                            w_carve_pos.y + entry_width + 10,
269                        )
270                        .with_z(entry_base + (6 * entry_height)),
271                    },
272                    2.0,
273                )
274                .clear();
275
276            painter
277                .superquadric(
278                    Aabb {
279                        min: Vec2::new(
280                            l_carve_pos.x - entry_length - 10,
281                            l_carve_pos.y - entry_width - 20,
282                        )
283                        .with_z(entry_base + (entry_height / 2) + (2 * entry_height)),
284                        max: Vec2::new(
285                            l_carve_pos.x + entry_length + 10,
286                            l_carve_pos.y + entry_width + 20,
287                        )
288                        .with_z(entry_base + (6 * entry_height)),
289                    },
290                    2.0,
291                )
292                .clear();
293        }
294        for p in 0..2 {
295            let entry_pyramid_pos = Vec2::new(
296                entry_pos.x - (entry_length - 4) + (p * (2 * (entry_length - 4))),
297                entry_pos.y,
298            );
299            painter
300                .pyramid(Aabb {
301                    min: (entry_pyramid_pos - entry_length)
302                        .with_z(entry_base + (entry_height / 2) + (2 * entry_height)),
303                    max: (entry_pyramid_pos + entry_length).with_z(
304                        entry_base
305                            + (entry_height / 2)
306                            + (2 * entry_height)
307                            + (2 * entry_length)
308                            + 1,
309                    ),
310                })
311                .fill(roof_color.clone());
312        }
313
314        // entry pillars
315        for dir in DIAGONALS {
316            let pillar_pos = entry_pillar_pos + dir * (entry_length - 4);
317            painter
318                .cylinder(Aabb {
319                    min: (pillar_pos - 4).with_z(entry_base - 2),
320                    max: (pillar_pos + 4)
321                        .with_z(entry_base + (entry_height / 2) + (2 * entry_height) - 2),
322                })
323                .fill(brick.clone());
324        }
325
326        // castle roof
327        painter
328            .aabb(Aabb {
329                min: Vec2::new(center.x - castle_length - 10, center.y - castle_width - 10)
330                    .with_z(castle_base + (castle_height / 2) + (2 * castle_height)),
331                max: Vec2::new(center.x + castle_length + 10, center.y + castle_width + 10)
332                    .with_z(castle_base + (castle_height / 2) + (3 * castle_height)),
333            })
334            .fill(brick.clone());
335        painter
336            .aabb(Aabb {
337                min: Vec2::new(center.x - castle_length - 9, center.y - castle_width - 9)
338                    .with_z(castle_base + (castle_height / 2) + (2 * castle_height)),
339                max: Vec2::new(center.x + castle_length + 9, center.y + castle_width + 9)
340                    .with_z(castle_base + (castle_height / 2) + (3 * castle_height)),
341            })
342            .fill(roof_color.clone());
343        // roof carve
344        for c in 0..2 {
345            let w_carve_pos = Vec2::new(
346                center.x,
347                center.y - (2 * castle_width) + (c * (4 * castle_width)),
348            );
349            let l_carve_pos = Vec2::new(
350                center.x - (4 * (castle_length / 2)) + (c * (8 * (castle_length / 2))),
351                center.y,
352            );
353            painter
354                .superquadric(
355                    Aabb {
356                        min: Vec2::new(
357                            w_carve_pos.x - castle_length - 40,
358                            w_carve_pos.y - castle_width - 20,
359                        )
360                        .with_z(castle_base + (castle_height / 2) + (2 * castle_height)),
361                        max: Vec2::new(
362                            w_carve_pos.x + castle_length + 40,
363                            w_carve_pos.y + castle_width + 20,
364                        )
365                        .with_z(castle_base + (6 * castle_height)),
366                    },
367                    2.0,
368                )
369                .clear();
370
371            painter
372                .superquadric(
373                    Aabb {
374                        min: Vec2::new(
375                            l_carve_pos.x - castle_length - 20,
376                            l_carve_pos.y - castle_width - 40,
377                        )
378                        .with_z(castle_base + (castle_height / 2) + (2 * castle_height)),
379                        max: Vec2::new(
380                            l_carve_pos.x + castle_length + 20,
381                            l_carve_pos.y + castle_width + 40,
382                        )
383                        .with_z(castle_base + (6 * castle_height)),
384                    },
385                    2.0,
386                )
387                .clear();
388        }
389        // towers
390        let tower_access = RandomField::new(0).get(center.with_z(plot_base)) % 4;
391        let harlequin_0 = (RandomField::new(0).get(center.with_z(tower_base)) % 4) as usize;
392        let harlequin_1 = (RandomField::new(0).get(center.with_z(tower_base + 1)) % 4) as usize;
393        for (t, tower_center) in tower_positions.iter().enumerate() {
394            let height_var = RandomField::new(0).get(tower_center.with_z(tower_base)) % 30;
395            let tower_height = tower_height_raw + height_var as i32;
396            let top_base = tower_base + tower_height;
397            let cone_height = 20;
398            let tower_radius = (tower_width - 6) as f32;
399            let access = t == tower_access as usize;
400            let tower_top_var = if access {
401                0
402            } else if t < 4 {
403                1
404            } else {
405                RandomField::new(0).get((tower_center).with_z(tower_base)) % 3
406            };
407            if t < 4 && access {
408                // tower top chain
409                painter
410                    .line(
411                        (tower_center + ((center - tower_center) / 2))
412                            .with_z(castle_base + (3 * castle_height) + 5),
413                        (tower_center + ((center - tower_center) / 2))
414                            .with_z(top_base + top_height - 1),
415                        1.0,
416                    )
417                    .fill(chain.clone());
418                painter
419                    .line(
420                        tower_center.with_z(top_base + top_height),
421                        (tower_center + ((center - tower_center) / 2))
422                            .with_z(top_base + top_height),
423                        1.0,
424                    )
425                    .fill(wood.clone());
426            };
427            // tower
428            painter
429                .cylinder(Aabb {
430                    min: (tower_center - tower_width + 6).with_z(tower_base - castle_height - 30),
431                    max: (tower_center + 1 + tower_width - 6).with_z(tower_base + tower_height),
432                })
433                .fill(brick.clone());
434            // tower top
435            painter
436                .cylinder(Aabb {
437                    min: (tower_center - tower_width + 1).with_z(top_base),
438                    max: (tower_center + 1 + tower_width - 1).with_z(top_base + top_height),
439                })
440                .fill(brick.clone());
441
442            // carve outs
443            let carve_limiter = painter.aabb(Aabb {
444                min: (tower_center - (tower_width / 2) - 3).with_z(top_base),
445                max: (tower_center + 1 + (tower_width / 2) + 3).with_z(top_base + top_height),
446            });
447            for dir in CARDINALS {
448                let carve_pos = tower_center + dir * (tower_width + (tower_width / 4));
449                painter
450                    .superquadric(
451                        Aabb {
452                            min: (carve_pos - tower_width + 3).with_z(top_base + 1),
453                            max: (carve_pos + tower_width - 3).with_z(top_base + top_height - 1),
454                        },
455                        2.5,
456                    )
457                    .intersect(carve_limiter)
458                    .clear()
459            }
460            // outside decor
461            let decor_var = RandomField::new(0).get(tower_center.with_z(tower_base)) % 6;
462            let decor_radius = (tower_width / 3) * 2;
463            let decors = 4.0 + decor_var as f32;
464            let decor_phi = TAU / decors;
465            for n in 1..=decors as i32 {
466                let pos = Vec2::new(
467                    tower_center.x + (decor_radius as f32 * ((n as f32 * decor_phi).cos())) as i32,
468                    tower_center.y + (decor_radius as f32 * ((n as f32 * decor_phi).sin())) as i32,
469                );
470                painter
471                    .cubic_bezier(
472                        pos.with_z(tower_base + (tower_height / 2) + (tower_height / 6)),
473                        (pos - ((tower_center - pos) / 4))
474                            .with_z(tower_base + (tower_height / 2) + (tower_height / 3)),
475                        (pos - ((tower_center - pos) / 2)).with_z(
476                            tower_base
477                                + (tower_height / 2)
478                                + (tower_height / 3)
479                                + (tower_height / 6),
480                        ),
481                        pos.with_z(top_base + 1),
482                        1.5,
483                    )
484                    .fill(brick.clone());
485            }
486            // top platform outside low
487            painter
488                .cylinder(Aabb {
489                    min: (tower_center - (3 * (tower_width / 2)) + 11).with_z(top_base - 5),
490                    max: (tower_center + 1 + (3 * (tower_width / 2)) - 11).with_z(top_base - 4),
491                })
492                .fill(brick.clone());
493            painter
494                .cylinder(Aabb {
495                    min: (tower_center - (3 * (tower_width / 2)) + 10).with_z(top_base - 4),
496                    max: (tower_center + 1 + (3 * (tower_width / 2)) - 10).with_z(top_base - 2),
497                })
498                .fill(brick.clone());
499            painter
500                .cylinder(Aabb {
501                    min: (tower_center - (3 * (tower_width / 2)) + 8).with_z(top_base - 2),
502                    max: (tower_center + 1 + (3 * (tower_width / 2)) - 8).with_z(top_base),
503                })
504                .fill(brick.clone());
505            painter
506                .cylinder(Aabb {
507                    min: (tower_center - (3 * (tower_width / 2)) + 6).with_z(top_base),
508                    max: (tower_center + 1 + (3 * (tower_width / 2)) - 6).with_z(top_base + 2),
509                })
510                .fill(brick.clone());
511            // top platform outside high
512            painter
513                .cylinder(Aabb {
514                    min: (tower_center - (3 * (tower_width / 2)) + 5).with_z(top_base + top_height),
515                    max: (tower_center + 1 + (3 * (tower_width / 2)) - 5)
516                        .with_z(top_base + top_height + 2),
517                })
518                .fill(brick.clone());
519            // repaint tower room
520            painter
521                .cylinder(Aabb {
522                    min: (tower_center - tower_width + 2).with_z(top_base),
523                    max: (tower_center + 1 + tower_width - 2).with_z(top_base + top_height),
524                })
525                .fill(brick.clone());
526            // top windows
527            for h in 0..2 {
528                // clear windows
529                painter
530                    .line(
531                        Vec2::new(tower_center.x - tower_width, tower_center.y)
532                            .with_z(top_base + (top_height / 2) - 3 + (4 * h)),
533                        Vec2::new(tower_center.x + 1 + tower_width, tower_center.y)
534                            .with_z(top_base + (top_height / 2) - 3 + (4 * h)),
535                        2.5,
536                    )
537                    .clear();
538                painter
539                    .line(
540                        Vec2::new(tower_center.x, tower_center.y - tower_width)
541                            .with_z(top_base + (top_height / 2) - 3 + (4 * h)),
542                        Vec2::new(tower_center.x, tower_center.y + 1 + tower_width)
543                            .with_z(top_base + (top_height / 2) - 3 + (4 * h)),
544                        2.5,
545                    )
546                    .clear();
547            }
548            // top window sprites
549            let top_window_limiter = painter.aabb(Aabb {
550                min: (tower_center - tower_width + 2).with_z(top_base + 1),
551                max: (tower_center + 1 + tower_width - 2).with_z(top_base + tower_height - 1),
552            });
553            for h in 0..2 {
554                painter
555                    .line(
556                        Vec2::new(tower_center.x - tower_width, tower_center.y)
557                            .with_z(top_base + (top_height / 2) - 3 + (4 * h)),
558                        Vec2::new(tower_center.x + 1 + tower_width, tower_center.y)
559                            .with_z(top_base + (top_height / 2) - 3 + (4 * h)),
560                        2.5,
561                    )
562                    .intersect(top_window_limiter)
563                    .fill(window_ver2.clone());
564                painter
565                    .line(
566                        Vec2::new(tower_center.x, tower_center.y - tower_width)
567                            .with_z(top_base + (top_height / 2) - 3 + (4 * h)),
568                        Vec2::new(tower_center.x, tower_center.y + 1 + tower_width)
569                            .with_z(top_base + (top_height / 2) - 3 + (4 * h)),
570                        2.5,
571                    )
572                    .intersect(top_window_limiter)
573                    .fill(window_ver.clone());
574            }
575            // tower top window sills
576            painter
577                .aabb(Aabb {
578                    min: Vec2::new(tower_center.x - 1, tower_center.y - tower_width)
579                        .with_z(top_base + (top_height / 2) - 5),
580                    max: Vec2::new(tower_center.x + 2, tower_center.y + tower_width + 1)
581                        .with_z(top_base + (top_height / 2) - 4),
582                })
583                .fill(wood.clone());
584
585            painter
586                .aabb(Aabb {
587                    min: Vec2::new(tower_center.x - tower_width, tower_center.y - 1)
588                        .with_z(top_base + (top_height / 2) - 5),
589                    max: Vec2::new(tower_center.x + tower_width + 1, tower_center.y + 2)
590                        .with_z(top_base + (top_height / 2) - 4),
591                })
592                .fill(wood.clone());
593            // clear top room
594            painter
595                .cylinder(Aabb {
596                    min: (tower_center - tower_width + 3).with_z(top_base + 1),
597                    max: (tower_center + 1 + tower_width - 3).with_z(top_base + top_height),
598                })
599                .clear();
600            // top room candles and wood ring
601            painter
602                .cylinder(Aabb {
603                    min: (tower_center - tower_width + 3).with_z(top_base + 1),
604                    max: (tower_center + 1 + tower_width - 3).with_z(top_base + 2),
605                })
606                .fill(candles_lite.clone());
607            painter
608                .cylinder(Aabb {
609                    min: (tower_center - tower_width + 3).with_z(top_base + top_height - 1),
610                    max: (tower_center + 1 + tower_width - 3).with_z(top_base + top_height),
611                })
612                .fill(wood.clone());
613            painter
614                .cylinder(Aabb {
615                    min: (tower_center - tower_width + 8).with_z(top_base + 1),
616                    max: (tower_center + 1 + tower_width - 8).with_z(top_base + top_height),
617                })
618                .clear();
619            // tower windows and decor
620            let tower_window_limiter = painter.aabb(Aabb {
621                min: (tower_center + 1 - tower_radius as i32).with_z(tower_base),
622                max: (tower_center + tower_radius as i32).with_z(tower_base + tower_height + 1),
623            });
624            let decor_var = RandomField::new(0).get(tower_center.with_z(tower_base)) % 10;
625            let decors = 10.0 + decor_var as f32;
626            let tower_decor_phi = TAU / decors;
627            let decor_radius = (tower_width / 2) + 4;
628            for h in 0..2 {
629                for n in 0..2 {
630                    // tower decor
631                    for d in 1..=decors as i32 {
632                        let decor_pos = Vec2::new(
633                            tower_center.x
634                                + (decor_radius as f32 * ((d as f32 * tower_decor_phi).cos()))
635                                    as i32,
636                            tower_center.y
637                                + (decor_radius as f32 * ((d as f32 * tower_decor_phi).sin()))
638                                    as i32,
639                        );
640                        painter
641                            .line(
642                                tower_center.with_z(
643                                    tower_base + (tower_height / 3) + ((tower_height / 3) * n),
644                                ),
645                                decor_pos.with_z(
646                                    tower_base + (tower_height / 3) + ((tower_height / 3) * n),
647                                ),
648                                1.0,
649                            )
650                            .fill(brick.clone());
651                    }
652                    // clear windows
653                    painter
654                        .line(
655                            Vec2::new(tower_center.x - tower_width + 5, tower_center.y).with_z(
656                                tower_base
657                                    + (tower_height / 4)
658                                    + (4 * h)
659                                    + ((tower_height / 4) * n),
660                            ),
661                            Vec2::new(tower_center.x + 1 + tower_width - 5, tower_center.y).with_z(
662                                tower_base
663                                    + (tower_height / 4)
664                                    + (4 * h)
665                                    + ((tower_height / 4) * n),
666                            ),
667                            2.5,
668                        )
669                        .clear();
670                    painter
671                        .line(
672                            Vec2::new(tower_center.x, tower_center.y - tower_width + 5).with_z(
673                                tower_base
674                                    + (tower_height / 4)
675                                    + (4 * h)
676                                    + ((tower_height / 4) * n),
677                            ),
678                            Vec2::new(tower_center.x, tower_center.y + 1 + tower_width - 5).with_z(
679                                tower_base
680                                    + (tower_height / 4)
681                                    + (4 * h)
682                                    + ((tower_height / 4) * n),
683                            ),
684                            2.5,
685                        )
686                        .clear();
687                    // tower window sprites
688                    painter
689                        .line(
690                            Vec2::new(tower_center.x - tower_width + 4, tower_center.y).with_z(
691                                tower_base
692                                    + (tower_height / 4)
693                                    + (4 * h)
694                                    + ((tower_height / 4) * n),
695                            ),
696                            Vec2::new(tower_center.x + 1 + tower_width - 4, tower_center.y).with_z(
697                                tower_base
698                                    + (tower_height / 4)
699                                    + (4 * h)
700                                    + ((tower_height / 4) * n),
701                            ),
702                            2.5,
703                        )
704                        .intersect(tower_window_limiter)
705                        .fill(window_ver2.clone());
706                    painter
707                        .line(
708                            Vec2::new(tower_center.x, tower_center.y - tower_width + 4).with_z(
709                                tower_base
710                                    + (tower_height / 4)
711                                    + (4 * h)
712                                    + ((tower_height / 4) * n),
713                            ),
714                            Vec2::new(tower_center.x, tower_center.y + 1 + tower_width - 4).with_z(
715                                tower_base
716                                    + (tower_height / 4)
717                                    + (4 * h)
718                                    + ((tower_height / 4) * n),
719                            ),
720                            2.5,
721                        )
722                        .intersect(tower_window_limiter)
723                        .fill(window_ver.clone());
724                    // tower window sills
725                    painter
726                        .aabb(Aabb {
727                            min: Vec2::new(
728                                tower_center.x - 1,
729                                tower_center.y - (tower_width / 2) - 3,
730                            )
731                            .with_z(tower_base + (tower_height / 4) - 3 + ((tower_height / 4) * n)),
732                            max: Vec2::new(
733                                tower_center.x + 2,
734                                tower_center.y + (tower_width / 2) + 4,
735                            )
736                            .with_z(tower_base + (tower_height / 4) - 2 + ((tower_height / 4) * n)),
737                        })
738                        .fill(wood.clone());
739
740                    painter
741                        .aabb(Aabb {
742                            min: Vec2::new(
743                                tower_center.x - (tower_width / 2) - 3,
744                                tower_center.y - 1,
745                            )
746                            .with_z(tower_base + (tower_height / 4) - 3 + ((tower_height / 4) * n)),
747                            max: Vec2::new(
748                                tower_center.x + (tower_width / 2) + 4,
749                                tower_center.y + 2,
750                            )
751                            .with_z(tower_base + (tower_height / 4) - 2 + ((tower_height / 4) * n)),
752                        })
753                        .fill(wood.clone());
754                }
755            }
756            // roof
757            painter
758                .cylinder(Aabb {
759                    min: (tower_center - roof_width).with_z(top_base + top_height),
760                    max: (tower_center + 1 + roof_width)
761                        .with_z(top_base + top_height + roof_height),
762                })
763                .fill(brick.clone());
764            let tower_roof_filling = painter.cylinder(Aabb {
765                min: (tower_center - roof_width + 1).with_z(top_base + top_height),
766                max: (tower_center + 1 + roof_width - 1)
767                    .with_z(top_base + top_height + roof_height),
768            });
769            if tower_top_var > 0 {
770                tower_roof_filling.fill(roof_color.clone());
771            } else {
772                tower_roof_filling.clear();
773            }
774            // roof carve outs
775            for dir in NEIGHBORS {
776                let carve_pos = tower_center + dir * roof_width;
777                let carve_z_offset = roof_height / 20;
778
779                painter
780                    .superquadric(
781                        Aabb {
782                            min: (carve_pos - 3 * (tower_width / 4))
783                                .with_z(top_base + top_height + carve_z_offset),
784                            max: (carve_pos + 3 * (tower_width / 4))
785                                .with_z(top_base + top_height + (2 * roof_height) + carve_z_offset),
786                        },
787                        2.5,
788                    )
789                    .clear();
790            }
791            if tower_top_var > 0 {
792                // cones
793                painter
794                    .cone(Aabb {
795                        min: (tower_center - (roof_width / 3) - 2).with_z(top_base + top_height),
796                        max: (tower_center + (roof_width / 3) + 2)
797                            .with_z(top_base + cone_height + (4 * roof_height) - 4),
798                    })
799                    .fill(roof_color.clone());
800
801                for dir in DIAGONALS {
802                    let cone_center = tower_center + dir * 6;
803                    let cone_var = RandomField::new(0).get(cone_center.with_z(tower_base)) % 60;
804                    painter
805                        .cone(Aabb {
806                            min: (cone_center - 4).with_z(top_base + top_height + 2),
807                            max: (cone_center + 4)
808                                .with_z(top_base + top_height + 30 + cone_var as i32),
809                        })
810                        .fill(roof_color.clone());
811                }
812            } else {
813                // top platform variant
814                painter
815                    .cylinder(Aabb {
816                        min: (tower_center - (3 * (tower_width / 2)) + 6)
817                            .with_z(top_base + top_height),
818                        max: (tower_center + 1 + (3 * (tower_width / 2)) - 6)
819                            .with_z(top_base + top_height + 2),
820                    })
821                    .fill(brick.clone());
822            }
823
824            // tower clear
825            let tower_radius = (tower_width - 7) as f32;
826            let add_on = if tower_top_var > 0 { 0 } else { top_height + 1 };
827            painter
828                .cylinder(Aabb {
829                    min: (tower_center + 1 - tower_radius as i32)
830                        .with_z(tower_base - castle_height + 1),
831                    max: (tower_center + tower_radius as i32)
832                        .with_z(tower_base + tower_height + 1 + add_on),
833                })
834                .clear();
835            // entries and floor for far towers
836            if t > 3 {
837                for dir in DIAGONALS {
838                    let entry_pos = tower_center + (dir * ((tower_width / 3) + 3));
839                    painter
840                        .line(
841                            tower_center.with_z(tower_base + (top_height / 3) + 5),
842                            entry_pos.with_z(tower_base + (top_height / 3)),
843                            4.5,
844                        )
845                        .clear();
846                    painter
847                        .cylinder(Aabb {
848                            min: (tower_center - tower_width + 7)
849                                .with_z(tower_base - castle_height + 1),
850                            max: (tower_center + 1 + tower_width - 7)
851                                .with_z(tower_base + (top_height / 3)),
852                        })
853                        .fill(brick.clone());
854                }
855                // floor candles
856                painter
857                    .cylinder(Aabb {
858                        min: (tower_center - tower_width + 8).with_z(tower_base + (top_height / 3)),
859                        max: (tower_center + 1 + tower_width - 8)
860                            .with_z(tower_base + (top_height / 3) + 1),
861                    })
862                    .fill(candles_lite.clone());
863            }
864            if tower_top_var < 1 {
865                // center clear
866                let ground_floor = if t < 4 {
867                    -castle_height - 1
868                } else {
869                    top_height / 3
870                };
871                painter
872                    .cylinder(Aabb {
873                        min: (tower_center - 2).with_z(tower_base + ground_floor),
874                        max: (tower_center + 3).with_z(tower_base + tower_height + 1 + add_on),
875                    })
876                    .clear();
877                if t < 4 && access {
878                    // tower top entry door
879                    painter
880                        .cylinder(Aabb {
881                            min: (tower_center + 1 - tower_radius as i32)
882                                .with_z(tower_base + tower_height + add_on),
883                            max: (tower_center + tower_radius as i32)
884                                .with_z(tower_base + tower_height + 1 + add_on),
885                        })
886                        .fill(key_door.clone());
887                    painter
888                        .cylinder(Aabb {
889                            min: (tower_center).with_z(tower_base + tower_height + add_on),
890                            max: (tower_center + 1).with_z(tower_base + tower_height + 1 + add_on),
891                        })
892                        .fill(key_hole.clone());
893                    painter
894                        .cylinder(Aabb {
895                            min: (tower_center + 1 - tower_radius as i32)
896                                .with_z(tower_base + tower_height + 1 + add_on),
897                            max: (tower_center + tower_radius as i32)
898                                .with_z(tower_base + tower_height + 2 + add_on),
899                        })
900                        .fill(onewaydoor.clone());
901                    painter
902                        .cylinder(Aabb {
903                            min: (tower_center + 2 - tower_radius as i32)
904                                .with_z(tower_base + tower_height + 1 + add_on),
905                            max: (tower_center - 1 + tower_radius as i32)
906                                .with_z(tower_base + tower_height + 2 + add_on),
907                        })
908                        .clear();
909                    let top_bat_pos = tower_center.with_z(tower_base + tower_height + 2 + add_on);
910                    bat_positions.push(top_bat_pos);
911                }
912            }
913            let bat_z = if t > 3 {
914                tower_base + (top_height / 3)
915            } else {
916                castle_base - castle_height + 1
917            };
918            let bat_pos_bottom = tower_center.with_z(bat_z);
919            bat_positions.push(bat_pos_bottom);
920            // tower platforms, chests and harlequins
921            for p in 0..4 {
922                painter
923                    .cylinder(Aabb {
924                        min: (tower_center - (tower_width / 4) - 3)
925                            .with_z(tower_base + (p * (tower_height / 3))),
926                        max: (tower_center - (tower_width / 4) + 3)
927                            .with_z(tower_base + (p * (tower_height / 3)) + 1),
928                    })
929                    .fill(roof_color.clone());
930                painter.sprite(
931                    (tower_center - (tower_width / 4) + 1)
932                        .with_z(tower_base + 1 + (p * (tower_height / 3))),
933                    SpriteKind::Candle,
934                );
935            }
936            let chest_pos =
937                (tower_center + (tower_width / 2)).with_z(tower_base + tower_height + 1);
938            let rand_npc_pos =
939                (tower_center + (tower_width / 2) + 1).with_z(tower_base + tower_height + 1);
940            let chest_var = RandomField::new(0).get(chest_pos) % 2;
941            if chest_var > 0 {
942                painter.sprite(chest_pos, SpriteKind::DungeonChest3);
943                random_npc_positions.push(rand_npc_pos);
944            }
945            if t == harlequin_0 {
946                let harlequin_pos_0 =
947                    (tower_center - (tower_width / 2)).with_z(tower_base + tower_height + 1);
948                harlequin_positions.push(harlequin_pos_0);
949            }
950            if t == harlequin_1 {
951                let harlequin_pos_1 =
952                    (tower_center - (tower_width / 2) + 1).with_z(tower_base + tower_height + 1);
953                harlequin_positions.push(harlequin_pos_1);
954            }
955        }
956        let mut harlequin_2_positions = vec![];
957        for pos in side_bldg_positions.iter().skip(2) {
958            harlequin_2_positions.push(*pos)
959        }
960        for pos in tower_positions.iter().skip(4) {
961            harlequin_2_positions.push(*pos)
962        }
963        let harlequin_2 = (RandomField::new(0).get(center.with_z(tower_base))
964            % harlequin_2_positions.len() as u32) as usize;
965        let harlequin_pos_2 = harlequin_2_positions[harlequin_2].with_z(tower_base + 8);
966        harlequin_positions.push(harlequin_pos_2);
967        // castle base
968        painter
969            .aabb(Aabb {
970                min: Vec2::new(center.x - castle_length - 3, center.y - castle_width - 3)
971                    .with_z(castle_base),
972                max: Vec2::new(center.x + castle_length + 3, center.y + castle_width + 3)
973                    .with_z(castle_base + (castle_height / 2)),
974            })
975            .fill(brick.clone());
976        // castle room
977        painter
978            .aabb(Aabb {
979                min: Vec2::new(center.x - castle_length - 5, center.y - castle_width - 5)
980                    .with_z(castle_base + (castle_height / 2)),
981                max: Vec2::new(center.x + castle_length + 5, center.y + castle_width + 5)
982                    .with_z(castle_base + (castle_height / 2) + (2 * castle_height)),
983            })
984            .fill(brick.clone());
985        // castle decor and windows
986        let castle_limiter = painter.aabb(Aabb {
987            min: Vec2::new(center.x - castle_length - 6, center.y - castle_width - 6)
988                .with_z(castle_base),
989            max: Vec2::new(center.x + castle_length + 6, center.y + castle_width + 6)
990                .with_z(castle_base + (castle_height / 2) + (2 * castle_height)),
991        });
992        let castle_window_limiter = painter.aabb(Aabb {
993            min: Vec2::new(center.x - castle_length - 4, center.y - castle_width - 4)
994                .with_z(castle_base + (castle_height / 2) + 1),
995            max: Vec2::new(center.x + castle_length + 4, center.y + castle_width + 4)
996                .with_z(castle_base + (castle_height / 2) + (2 * castle_height) - 1),
997        });
998        // castle decor
999        let castle_decor_var = RandomField::new(0).get(center.with_z(tower_base)) % 25;
1000        let castle_decors = 25.0 + castle_decor_var as f32;
1001        let castle_decor_phi = TAU / castle_decors;
1002        let castle_decor_radius = castle_length + 10;
1003        // tower decor
1004        for d in 1..=castle_decors as i32 {
1005            let castle_decor_pos = Vec2::new(
1006                center.x
1007                    + (castle_decor_radius as f32 * ((d as f32 * castle_decor_phi).cos())) as i32,
1008                center.y
1009                    + (castle_decor_radius as f32 * ((d as f32 * castle_decor_phi).sin())) as i32,
1010            );
1011            for l in 0..2 {
1012                painter
1013                    .line(
1014                        center.with_z(
1015                            castle_base + (castle_height / 2) + ((2 * l) * castle_height) - 1,
1016                        ),
1017                        castle_decor_pos.with_z(
1018                            castle_base + (castle_height / 2) + ((2 * l) * castle_height) - 1,
1019                        ),
1020                        1.0,
1021                    )
1022                    .intersect(castle_limiter)
1023                    .fill(brick.clone());
1024                painter
1025                    .line(
1026                        center.with_z(
1027                            castle_base + 3 + castle_height + (l * ((castle_height / 2) + 5)),
1028                        ),
1029                        castle_decor_pos.with_z(
1030                            castle_base + 3 + castle_height + (l * ((castle_height / 2) + 5)),
1031                        ),
1032                        1.0,
1033                    )
1034                    .intersect(castle_limiter)
1035                    .fill(brick.clone());
1036            }
1037        }
1038        // castle windows
1039        for h in 0..2 {
1040            for s in 1..=2 {
1041                for r in 0..=2 {
1042                    // clear windows
1043                    painter
1044                        .line(
1045                            Vec2::new(
1046                                center.x - castle_length - 5,
1047                                center.y - (castle_width / 2) + (r * (castle_width / 2)),
1048                            )
1049                            .with_z(castle_base + (s * ((castle_height / 2) + 5)) + (4 * h)),
1050                            Vec2::new(
1051                                center.x + 1 + castle_length + 5,
1052                                center.y - (castle_width / 2) + (r * (castle_width / 2)),
1053                            )
1054                            .with_z(castle_base + (s * ((castle_height / 2) + 5)) + (4 * h)),
1055                            2.5,
1056                        )
1057                        .clear();
1058                    painter
1059                        .line(
1060                            Vec2::new(
1061                                center.x - (castle_length / 2) + (r * (castle_length / 2)),
1062                                center.y - castle_width - 5,
1063                            )
1064                            .with_z(castle_base + (s * ((castle_height / 2) + 5)) + (4 * h)),
1065                            Vec2::new(
1066                                center.x - (castle_length / 2) + (r * (castle_length / 2)),
1067                                center.y + 1 + castle_width + 5,
1068                            )
1069                            .with_z(castle_base + (s * ((castle_height / 2) + 5)) + (4 * h)),
1070                            2.5,
1071                        )
1072                        .clear();
1073
1074                    // castle window sills
1075                    painter
1076                        .aabb(Aabb {
1077                            min: Vec2::new(
1078                                center.x - 1 - (castle_length / 2) + (r * (castle_length / 2)),
1079                                center.y - castle_width - 6,
1080                            )
1081                            .with_z(castle_base + (s * ((castle_height / 2) + 2)) + (3 * (s - 1))),
1082                            max: Vec2::new(
1083                                center.x + 2 - (castle_length / 2) + (r * (castle_length / 2)),
1084                                center.y + castle_width + 6,
1085                            )
1086                            .with_z(
1087                                castle_base + 1 + (s * ((castle_height / 2) + 2)) + (3 * (s - 1)),
1088                            ),
1089                        })
1090                        .fill(wood.clone());
1091
1092                    painter
1093                        .aabb(Aabb {
1094                            min: Vec2::new(
1095                                center.x - castle_length - 6,
1096                                center.y - 1 - (castle_width / 2) + (r * (castle_width / 2)),
1097                            )
1098                            .with_z(castle_base + (s * ((castle_height / 2) + 2)) + (3 * (s - 1))),
1099                            max: Vec2::new(
1100                                center.x + castle_length + 6,
1101                                center.y + 2 - (castle_width / 2) + (r * (castle_width / 2)),
1102                            )
1103                            .with_z(
1104                                castle_base + 1 + (s * ((castle_height / 2) + 2)) + (3 * (s - 1)),
1105                            ),
1106                        })
1107                        .fill(wood.clone());
1108                    // castle window sprites
1109                    painter
1110                        .line(
1111                            Vec2::new(
1112                                center.x - castle_length - 5,
1113                                center.y - (castle_width / 2) + (r * (castle_width / 2)),
1114                            )
1115                            .with_z(castle_base + (s * ((castle_height / 2) + 5)) + (4 * h)),
1116                            Vec2::new(
1117                                center.x + 1 + castle_length + 5,
1118                                center.y - (castle_width / 2) + (r * (castle_width / 2)),
1119                            )
1120                            .with_z(castle_base + (s * ((castle_height / 2) + 5)) + (4 * h)),
1121                            2.5,
1122                        )
1123                        .intersect(castle_window_limiter)
1124                        .fill(window_ver2.clone());
1125                    painter
1126                        .line(
1127                            Vec2::new(
1128                                center.x - (castle_length / 2) + (r * (castle_length / 2)),
1129                                center.y - castle_width - 5,
1130                            )
1131                            .with_z(castle_base + (s * ((castle_height / 2) + 5)) + (4 * h)),
1132                            Vec2::new(
1133                                center.x - (castle_length / 2) + (r * (castle_length / 2)),
1134                                center.y + 1 + castle_width + 5,
1135                            )
1136                            .with_z(castle_base + (s * ((castle_height / 2) + 5)) + (4 * h)),
1137                            2.5,
1138                        )
1139                        .intersect(castle_window_limiter)
1140                        .fill(window_ver.clone());
1141                }
1142            }
1143        }
1144        // main entry stairs
1145        painter
1146            .line(
1147                entry_pos.with_z(castle_base - 2),
1148                center.with_z(castle_base + castle_height - 5),
1149                5.0,
1150            )
1151            .fill(brick.clone());
1152        painter
1153            .line(
1154                entry_pos.with_z(castle_base),
1155                center.with_z(castle_base + castle_height - 3),
1156                5.0,
1157            )
1158            .clear();
1159        painter
1160            .aabb(Aabb {
1161                min: (entry_pos - 10).with_z(castle_base - 5),
1162                max: (entry_pos + 10).with_z(castle_base),
1163            })
1164            .fill(brick.clone());
1165        // main entry door
1166        painter
1167            .horizontal_cylinder(
1168                Aabb {
1169                    min: Vec2::new(center.x - castle_length - 3, center.y - 4)
1170                        .with_z(entry_base + 6),
1171                    max: Vec2::new(center.x - castle_length - 2, center.y + 5)
1172                        .with_z(entry_base + 15),
1173                },
1174                Dir2::NegX,
1175            )
1176            .fill(onewaydoor.clone());
1177        // castle cellar
1178        painter
1179            .aabb(Aabb {
1180                min: Vec2::new(center.x - castle_length - 2, center.y - castle_width - 2)
1181                    .with_z(castle_base - castle_height),
1182                max: Vec2::new(center.x + castle_length + 2, center.y + castle_width + 2)
1183                    .with_z(castle_base + (castle_height / 8)),
1184            })
1185            .fill(brick.clone());
1186        // cellar stairs to side_bldgs
1187        painter
1188            .line(
1189                center.with_z(castle_base - castle_height - 3),
1190                side_bldg_stairs_pos.with_z(side_bldg_base_raw),
1191                6.0,
1192            )
1193            .fill(brick.clone());
1194        painter
1195            .line(
1196                center.with_z(castle_base - castle_height - 3),
1197                side_bldg_stairs_pos.with_z(side_bldg_base_raw),
1198                5.0,
1199            )
1200            .clear();
1201        // clear castle cellar
1202        painter
1203            .aabb(Aabb {
1204                min: Vec2::new(center.x - castle_length - 1, center.y - castle_width - 1)
1205                    .with_z(castle_base - castle_height + 1),
1206                max: Vec2::new(center.x + castle_length + 1, center.y + castle_width + 1)
1207                    .with_z(castle_base + (castle_height / 8) - 1),
1208            })
1209            .clear();
1210
1211        random_npc_positions.push(center.with_z(castle_base - castle_height + 1));
1212
1213        // castle cellar tower entries
1214        for dir in DIAGONALS {
1215            let entry_pos = Vec2::new(
1216                center.x + dir.x * (castle_length + 2),
1217                center.y + dir.y * (castle_width + 2),
1218            );
1219            painter
1220                .line(
1221                    entry_pos.with_z(castle_base - castle_height),
1222                    entry_pos.with_z(castle_base - castle_height + 6),
1223                    8.0,
1224                )
1225                .clear();
1226            painter
1227                .cylinder(Aabb {
1228                    min: (entry_pos - 8).with_z(castle_base - castle_height - 5),
1229                    max: (entry_pos + 8).with_z(castle_base - castle_height + 1),
1230                })
1231                .fill(brick.clone());
1232        }
1233        // castle cellar floor
1234        painter
1235            .aabb(Aabb {
1236                min: Vec2::new(center.x - castle_length - 20, center.y - castle_width - 20)
1237                    .with_z(castle_base - castle_height - 8),
1238                max: Vec2::new(center.x + castle_length + 20, center.y + castle_width + 20)
1239                    .with_z(castle_base - castle_height + 1),
1240            })
1241            .fill(brick.clone());
1242        // cellar wood decor and candles
1243        painter
1244            .aabb(Aabb {
1245                min: Vec2::new(center.x - castle_length - 1, center.y - castle_width - 1)
1246                    .with_z(castle_base + (castle_height / 8) - 6),
1247                max: Vec2::new(center.x + castle_length + 1, center.y + castle_width + 1)
1248                    .with_z(castle_base + (castle_height / 8) - 1),
1249            })
1250            .fill(wood.clone());
1251        painter
1252            .aabb(Aabb {
1253                min: Vec2::new(center.x - castle_length + 1, center.y - castle_width + 1)
1254                    .with_z(castle_base + (castle_height / 8) - 6),
1255                max: Vec2::new(center.x + castle_length - 1, center.y + castle_width - 1)
1256                    .with_z(castle_base + (castle_height / 8) - 1),
1257            })
1258            .clear();
1259        let cellar_podium_limiter = painter.aabb(Aabb {
1260            min: Vec2::new(center.x - castle_length - 1, center.y - castle_width - 1)
1261                .with_z(castle_base - castle_height + 1),
1262            max: Vec2::new(center.x + castle_length + 1, center.y + castle_width + 1)
1263                .with_z(castle_base - castle_height + 4),
1264        });
1265        let mut cellar_beam_postions = vec![];
1266        for dir in DIAGONALS {
1267            let beam_pos = Vec2::new(
1268                center.x + (dir.x * (castle_length / 3)),
1269                center.y + (dir.y * (castle_width + 1)),
1270            );
1271            cellar_beam_postions.push(beam_pos);
1272        }
1273        for b in 0..2 {
1274            let beam_pos = Vec2::new(
1275                center.x - castle_length - 2 + (b * ((2 * castle_length) + 3)),
1276                center.y,
1277            );
1278            cellar_beam_postions.push(beam_pos);
1279        }
1280        for beam_pos in cellar_beam_postions {
1281            for n in 0..3 {
1282                painter
1283                    .cylinder_with_radius(
1284                        beam_pos.with_z(castle_base - castle_height + 1 + n),
1285                        (5 - n) as f32,
1286                        1.0,
1287                    )
1288                    .intersect(cellar_podium_limiter)
1289                    .fill(match n {
1290                        2 => candles.clone(),
1291                        _ => wood.clone(),
1292                    });
1293            }
1294            painter
1295                .line(
1296                    beam_pos.with_z(castle_base - castle_height + 1),
1297                    beam_pos.with_z(castle_base + (castle_height / 8) - 5),
1298                    2.0,
1299                )
1300                .fill(wood.clone());
1301        }
1302        // side buildings
1303        for (b, side_bldg_pos) in side_bldg_positions.iter().enumerate() {
1304            let side_bldg_roof_height = side_bldg_roof_height_raw
1305                + (RandomField::new(0).get(side_bldg_pos.with_z(tower_base)) % 12) as i32;
1306            let side_bldg_base = if b > 1 {
1307                side_bldg_base_raw - (side_bldg_height / 2) - side_bldg_roof_height
1308            } else {
1309                side_bldg_base_raw
1310            };
1311            // side_bldg roof
1312            let side_bldg_roof = painter.aabb(Aabb {
1313                min: Vec2::new(
1314                    side_bldg_pos.x - side_bldg_length - 10,
1315                    side_bldg_pos.y - side_bldg_width - 10,
1316                )
1317                .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height)),
1318                max: Vec2::new(
1319                    side_bldg_pos.x + side_bldg_length + 10,
1320                    side_bldg_pos.y + side_bldg_width + 10,
1321                )
1322                .with_z(side_bldg_base + side_bldg_roof_height + (3 * side_bldg_height)),
1323            });
1324            side_bldg_roof.fill(brick.clone());
1325            painter
1326                .aabb(Aabb {
1327                    min: Vec2::new(
1328                        side_bldg_pos.x - side_bldg_length - 9,
1329                        side_bldg_pos.y - side_bldg_width - 9,
1330                    )
1331                    .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height)),
1332                    max: Vec2::new(
1333                        side_bldg_pos.x + side_bldg_length + 9,
1334                        side_bldg_pos.y + side_bldg_width + 9,
1335                    )
1336                    .with_z(side_bldg_base + side_bldg_roof_height + (3 * side_bldg_height)),
1337                })
1338                .fill(roof_color.clone());
1339            // side_bldg room
1340            painter
1341                .aabb(Aabb {
1342                    min: Vec2::new(
1343                        side_bldg_pos.x - side_bldg_length - 5,
1344                        side_bldg_pos.y - side_bldg_width - 5,
1345                    )
1346                    .with_z(side_bldg_base - 2),
1347                    max: Vec2::new(
1348                        side_bldg_pos.x + side_bldg_length + 5,
1349                        side_bldg_pos.y + side_bldg_width + 5,
1350                    )
1351                    .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height)),
1352                })
1353                .fill(brick.clone());
1354            // side_bldg roof carve
1355            for c in 0..2 {
1356                let w_carve_pos = Vec2::new(
1357                    side_bldg_pos.x,
1358                    side_bldg_pos.y - (2 * side_bldg_width) + (c * (4 * side_bldg_width)),
1359                );
1360                let l_carve_pos = Vec2::new(
1361                    side_bldg_pos.x - (4 * (side_bldg_length / 2))
1362                        + (c * (8 * (side_bldg_length / 2))),
1363                    side_bldg_pos.y,
1364                );
1365                painter
1366                    .superquadric(
1367                        Aabb {
1368                            min: Vec2::new(
1369                                w_carve_pos.x - side_bldg_length - 20,
1370                                w_carve_pos.y - side_bldg_width - 10,
1371                            )
1372                            .with_z(
1373                                side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height),
1374                            ),
1375                            max: Vec2::new(
1376                                w_carve_pos.x + side_bldg_length + 20,
1377                                w_carve_pos.y + side_bldg_width + 10,
1378                            )
1379                            .with_z(
1380                                side_bldg_base + side_bldg_roof_height - (side_bldg_height / 2)
1381                                    + (6 * side_bldg_height),
1382                            ),
1383                        },
1384                        2.0,
1385                    )
1386                    .intersect(side_bldg_roof)
1387                    .clear();
1388
1389                painter
1390                    .superquadric(
1391                        Aabb {
1392                            min: Vec2::new(
1393                                l_carve_pos.x - side_bldg_length - 10,
1394                                l_carve_pos.y - side_bldg_width - 20,
1395                            )
1396                            .with_z(
1397                                side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height),
1398                            ),
1399                            max: Vec2::new(
1400                                l_carve_pos.x + side_bldg_length + 10,
1401                                l_carve_pos.y + side_bldg_width + 20,
1402                            )
1403                            .with_z(
1404                                side_bldg_base + side_bldg_roof_height - (side_bldg_height / 2)
1405                                    + (6 * side_bldg_height),
1406                            ),
1407                        },
1408                        2.0,
1409                    )
1410                    .intersect(side_bldg_roof)
1411                    .clear();
1412            }
1413            for p in 0..2 {
1414                let pyramid_pos = Vec2::new(
1415                    side_bldg_pos.x - (side_bldg_length - 4) + (p * (2 * (side_bldg_length - 4))),
1416                    side_bldg_pos.y,
1417                );
1418                painter
1419                    .pyramid(Aabb {
1420                        min: (pyramid_pos - side_bldg_length).with_z(
1421                            side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height),
1422                        ),
1423                        max: (pyramid_pos + side_bldg_length).with_z(
1424                            side_bldg_base
1425                                + side_bldg_roof_height
1426                                + (2 * side_bldg_height)
1427                                + (2 * side_bldg_length)
1428                                + 1,
1429                        ),
1430                    })
1431                    .fill(roof_color.clone());
1432            }
1433
1434            // side_bldg decor
1435            let side_bldg_decor_limiter_1 = painter.aabb(Aabb {
1436                min: Vec2::new(
1437                    side_bldg_pos.x - side_bldg_length - 9,
1438                    side_bldg_pos.y - side_bldg_width - 9,
1439                )
1440                .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height) - 1),
1441                max: Vec2::new(
1442                    side_bldg_pos.x + side_bldg_length + 9,
1443                    side_bldg_pos.y + side_bldg_width + 9,
1444                )
1445                .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height)),
1446            });
1447            let side_bldg_decor_limiter_2 = painter.aabb(Aabb {
1448                min: Vec2::new(
1449                    side_bldg_pos.x - side_bldg_length - 6,
1450                    side_bldg_pos.y - side_bldg_width - 6,
1451                )
1452                .with_z(side_bldg_base + 8),
1453                max: Vec2::new(
1454                    side_bldg_pos.x + side_bldg_length + 6,
1455                    side_bldg_pos.y + side_bldg_width + 6,
1456                )
1457                .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height) - 2),
1458            });
1459            let side_bldg_window_limiter = painter.aabb(Aabb {
1460                min: Vec2::new(
1461                    side_bldg_pos.x - side_bldg_length - 4,
1462                    side_bldg_pos.y - side_bldg_width - 4,
1463                )
1464                .with_z(side_bldg_base),
1465                max: Vec2::new(
1466                    side_bldg_pos.x + side_bldg_length + 4,
1467                    side_bldg_pos.y + side_bldg_width + 4,
1468                )
1469                .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height) - 2),
1470            });
1471            let side_bldg_decor_rows = (side_bldg_roof_height + (2 * side_bldg_height)) / 6;
1472            for r in 0..=6 {
1473                let side_bldg_decor_limiter = if r == 0 {
1474                    side_bldg_decor_limiter_1
1475                } else {
1476                    side_bldg_decor_limiter_2
1477                };
1478                let side_bldg_decor_var =
1479                    RandomField::new(0).get((side_bldg_pos + r).with_z(tower_base)) % 12;
1480                let side_bldg_decors = 12.0 + side_bldg_decor_var as f32;
1481                let side_bldg_phi = TAU / side_bldg_decors;
1482                let side_bldg_decor_radius = side_bldg_length + 10;
1483                for d in 1..=side_bldg_decors as i32 {
1484                    let side_bldg_decors_pos = Vec2::new(
1485                        side_bldg_pos.x
1486                            + (side_bldg_decor_radius as f32 * ((d as f32 * side_bldg_phi).cos()))
1487                                as i32,
1488                        side_bldg_pos.y
1489                            + (side_bldg_decor_radius as f32 * ((d as f32 * side_bldg_phi).sin()))
1490                                as i32,
1491                    );
1492                    painter
1493                        .line(
1494                            side_bldg_pos.with_z(
1495                                side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height)
1496                                    - 1
1497                                    - (r * side_bldg_decor_rows),
1498                            ),
1499                            side_bldg_decors_pos.with_z(
1500                                side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height)
1501                                    - 1
1502                                    - (r * side_bldg_decor_rows),
1503                            ),
1504                            1.0,
1505                        )
1506                        .intersect(side_bldg_decor_limiter)
1507                        .fill(brick.clone());
1508                }
1509            }
1510            // side bldg close, windows, gangway to main room
1511            if b < 2 {
1512                // side bldg windows
1513                for r in 0..2 {
1514                    for t in 0..=2 {
1515                        for h in 0..2 {
1516                            // clear windows
1517                            painter
1518                                .line(
1519                                    Vec2::new(
1520                                        side_bldg_pos.x - side_bldg_length - 7,
1521                                        side_bldg_pos.y - 1 - (side_bldg_width / 2)
1522                                            + (r * ((side_bldg_length) - 2)),
1523                                    )
1524                                    .with_z(
1525                                        side_bldg_base
1526                                            + (side_bldg_roof_height / 4)
1527                                            + (t * (side_bldg_roof_height / 3))
1528                                            + (4 * h),
1529                                    ),
1530                                    Vec2::new(
1531                                        side_bldg_pos.x + 1 + side_bldg_length + 7,
1532                                        side_bldg_pos.y - 1 - (side_bldg_width / 2)
1533                                            + (r * ((side_bldg_length) - 2)),
1534                                    )
1535                                    .with_z(
1536                                        side_bldg_base
1537                                            + (side_bldg_roof_height / 4)
1538                                            + (t * (side_bldg_roof_height / 3))
1539                                            + (4 * h),
1540                                    ),
1541                                    2.5,
1542                                )
1543                                .clear();
1544                            painter
1545                                .line(
1546                                    Vec2::new(
1547                                        side_bldg_pos.x - (side_bldg_length / 2)
1548                                            + (r * side_bldg_width),
1549                                        side_bldg_pos.y - side_bldg_width - 7,
1550                                    )
1551                                    .with_z(
1552                                        side_bldg_base
1553                                            + (side_bldg_roof_height / 4)
1554                                            + (t * (side_bldg_roof_height / 3))
1555                                            + (4 * h),
1556                                    ),
1557                                    Vec2::new(
1558                                        side_bldg_pos.x - (side_bldg_length / 2)
1559                                            + (r * side_bldg_width),
1560                                        side_bldg_pos.y + 1 + side_bldg_width + 7,
1561                                    )
1562                                    .with_z(
1563                                        side_bldg_base
1564                                            + (side_bldg_roof_height / 4)
1565                                            + (t * (side_bldg_roof_height / 3))
1566                                            + (4 * h),
1567                                    ),
1568                                    2.5,
1569                                )
1570                                .clear();
1571
1572                            // side bldg window sprites
1573                            painter
1574                                .line(
1575                                    Vec2::new(
1576                                        side_bldg_pos.x - side_bldg_length - 7,
1577                                        side_bldg_pos.y - 1 - (side_bldg_width / 2)
1578                                            + (r * ((side_bldg_length) - 2)),
1579                                    )
1580                                    .with_z(
1581                                        side_bldg_base
1582                                            + (side_bldg_roof_height / 4)
1583                                            + (t * (side_bldg_roof_height / 3))
1584                                            + (4 * h),
1585                                    ),
1586                                    Vec2::new(
1587                                        side_bldg_pos.x + 1 + side_bldg_length + 7,
1588                                        side_bldg_pos.y - 1 - (side_bldg_width / 2)
1589                                            + (r * ((side_bldg_length) - 2)),
1590                                    )
1591                                    .with_z(
1592                                        side_bldg_base
1593                                            + (side_bldg_roof_height / 4)
1594                                            + (t * (side_bldg_roof_height / 3))
1595                                            + (4 * h),
1596                                    ),
1597                                    2.5,
1598                                )
1599                                .intersect(side_bldg_window_limiter)
1600                                .fill(window_ver2.clone());
1601                            painter
1602                                .line(
1603                                    Vec2::new(
1604                                        side_bldg_pos.x - (side_bldg_length / 2)
1605                                            + (r * side_bldg_width),
1606                                        side_bldg_pos.y - side_bldg_width - 7,
1607                                    )
1608                                    .with_z(
1609                                        side_bldg_base
1610                                            + (side_bldg_roof_height / 4)
1611                                            + (t * (side_bldg_roof_height / 3))
1612                                            + (4 * h),
1613                                    ),
1614                                    Vec2::new(
1615                                        side_bldg_pos.x - (side_bldg_length / 2)
1616                                            + (r * side_bldg_width),
1617                                        side_bldg_pos.y + 1 + side_bldg_width + 7,
1618                                    )
1619                                    .with_z(
1620                                        side_bldg_base
1621                                            + (side_bldg_roof_height / 4)
1622                                            + (t * (side_bldg_roof_height / 3))
1623                                            + (4 * h),
1624                                    ),
1625                                    2.5,
1626                                )
1627                                .intersect(side_bldg_window_limiter)
1628                                .fill(window_ver.clone());
1629                        }
1630                        // side_bldg window sills
1631                        painter
1632                            .aabb(Aabb {
1633                                min: Vec2::new(
1634                                    side_bldg_pos.x - 1 - (side_bldg_length / 2)
1635                                        + (r * ((side_bldg_length) - 3)),
1636                                    side_bldg_pos.y - side_bldg_width - 6,
1637                                )
1638                                .with_z(
1639                                    side_bldg_base - 2
1640                                        + (side_bldg_roof_height / 4)
1641                                        + (t * (side_bldg_roof_height / 3)),
1642                                ),
1643                                max: Vec2::new(
1644                                    side_bldg_pos.x + 2 - (side_bldg_length / 2)
1645                                        + (r * (side_bldg_length - 3)),
1646                                    side_bldg_pos.y + side_bldg_width + 6,
1647                                )
1648                                .with_z(
1649                                    side_bldg_base - 1
1650                                        + (side_bldg_roof_height / 4)
1651                                        + (t * (side_bldg_roof_height / 3)),
1652                                ),
1653                            })
1654                            .fill(wood.clone());
1655
1656                        painter
1657                            .aabb(Aabb {
1658                                min: Vec2::new(
1659                                    side_bldg_pos.x - side_bldg_length - 6,
1660                                    side_bldg_pos.y - 2 - (side_bldg_width / 2)
1661                                        + (r * (side_bldg_width + 1)),
1662                                )
1663                                .with_z(
1664                                    side_bldg_base - 2
1665                                        + (side_bldg_roof_height / 4)
1666                                        + (t * (side_bldg_roof_height / 3)),
1667                                ),
1668                                max: Vec2::new(
1669                                    side_bldg_pos.x + side_bldg_length + 6,
1670                                    side_bldg_pos.y + 1 - (side_bldg_width / 2)
1671                                        + (r * (side_bldg_width + 1)),
1672                                )
1673                                .with_z(
1674                                    side_bldg_base - 1
1675                                        + (side_bldg_roof_height / 4)
1676                                        + (t * (side_bldg_roof_height / 3)),
1677                                ),
1678                            })
1679                            .fill(wood.clone());
1680                    }
1681                }
1682                // top gangway
1683                let gangway_handle = 2;
1684                // mark loot entry with light
1685                painter
1686                    .line(
1687                        center.with_z(side_bldg_base + (2 * castle_height) + gangway_handle),
1688                        side_bldg_pos_2
1689                            .with_z(side_bldg_base + (2 * castle_height) + gangway_handle),
1690                        6.0,
1691                    )
1692                    .fill(roof_color.clone());
1693                painter
1694                    .line(
1695                        center.with_z(side_bldg_base + (2 * castle_height) + gangway_handle),
1696                        side_bldg_pos.with_z(side_bldg_base + (2 * castle_height) + gangway_handle),
1697                        5.0,
1698                    )
1699                    .fill(wood.clone());
1700                painter
1701                    .line(
1702                        center.with_z(side_bldg_base + (2 * castle_height) + gangway_handle),
1703                        side_bldg_pos.with_z(side_bldg_base + (2 * castle_height) + gangway_handle),
1704                        4.0,
1705                    )
1706                    .clear();
1707            }
1708            // clear side_bldg room
1709            painter
1710                .aabb(Aabb {
1711                    min: Vec2::new(
1712                        side_bldg_pos.x - side_bldg_length - 3,
1713                        side_bldg_pos.y - side_bldg_width - 3,
1714                    )
1715                    .with_z(side_bldg_base),
1716                    max: Vec2::new(
1717                        side_bldg_pos.x + side_bldg_length + 3,
1718                        side_bldg_pos.y + side_bldg_width + 3,
1719                    )
1720                    .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height) - 1),
1721                })
1722                .clear();
1723            // side bldg room decor
1724            for r in 0..2 {
1725                let row = r * (side_bldg_roof_height + (2 * side_bldg_height) - 2);
1726                painter
1727                    .aabb(Aabb {
1728                        min: Vec2::new(
1729                            side_bldg_pos.x - side_bldg_length - 3,
1730                            side_bldg_pos.y - side_bldg_width - 3,
1731                        )
1732                        .with_z(side_bldg_base + row),
1733                        max: Vec2::new(
1734                            side_bldg_pos.x + side_bldg_length + 3,
1735                            side_bldg_pos.y + side_bldg_width + 3,
1736                        )
1737                        .with_z(side_bldg_base + row + 1),
1738                    })
1739                    .fill(wood.clone());
1740                painter
1741                    .aabb(Aabb {
1742                        min: Vec2::new(
1743                            side_bldg_pos.x - side_bldg_length - 1,
1744                            side_bldg_pos.y - side_bldg_width - 1,
1745                        )
1746                        .with_z(side_bldg_base + row),
1747                        max: Vec2::new(
1748                            side_bldg_pos.x + side_bldg_length + 1,
1749                            side_bldg_pos.y + side_bldg_width + 1,
1750                        )
1751                        .with_z(side_bldg_base + row + 1),
1752                    })
1753                    .clear();
1754            }
1755            // wood decor and podiums with candle sprites
1756            let podium_limiter_1 = painter.aabb(Aabb {
1757                min: Vec2::new(
1758                    side_bldg_pos.x - side_bldg_length - 3,
1759                    side_bldg_pos.y - side_bldg_width - 3,
1760                )
1761                .with_z(side_bldg_base),
1762                max: Vec2::new(
1763                    side_bldg_pos.x + side_bldg_length + 3,
1764                    side_bldg_pos.y + side_bldg_width + 3,
1765                )
1766                .with_z(side_bldg_base + 3),
1767            });
1768            let podium_limiter_2 = painter.aabb(Aabb {
1769                min: Vec2::new(
1770                    side_bldg_pos.x - side_bldg_length - 3,
1771                    side_bldg_pos.y - side_bldg_width - 3,
1772                )
1773                .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height) - 10),
1774                max: Vec2::new(
1775                    side_bldg_pos.x + side_bldg_length + 3,
1776                    side_bldg_pos.y + side_bldg_width + 3,
1777                )
1778                .with_z(side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height) - 6),
1779            });
1780            let mut side_bldg_beam_pos = vec![];
1781            for dir in DIAGONALS {
1782                let corner_pos = Vec2::new(
1783                    side_bldg_pos.x + (dir.x * (side_bldg_length + 2)),
1784                    side_bldg_pos.y + (dir.y * (side_bldg_width + 2)),
1785                );
1786                side_bldg_beam_pos.push(corner_pos);
1787            }
1788            for corner_pos in &side_bldg_beam_pos {
1789                for n in 0..3 {
1790                    painter
1791                        .cylinder_with_radius(
1792                            corner_pos.with_z(side_bldg_base + n),
1793                            (5 - n) as f32,
1794                            1.0,
1795                        )
1796                        .intersect(podium_limiter_1)
1797                        .fill(match n {
1798                            2 => candles.clone(),
1799                            _ => wood.clone(),
1800                        });
1801                    painter
1802                        .cylinder_with_radius(
1803                            corner_pos.with_z(
1804                                side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height)
1805                                    - 10
1806                                    + n,
1807                            ),
1808                            (2 + n) as f32,
1809                            1.0,
1810                        )
1811                        .intersect(podium_limiter_2)
1812                        .fill(wood.clone());
1813                }
1814                painter
1815                    .cylinder_with_radius(
1816                        corner_pos.with_z(
1817                            side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height) - 7,
1818                        ),
1819                        4.0,
1820                        1.0,
1821                    )
1822                    .intersect(podium_limiter_2)
1823                    .fill(candles.clone());
1824            }
1825            if b < 2 {
1826                // side_bldg stairway
1827                let side_bldg_room_stairs = painter.aabb(Aabb {
1828                    min: Vec2::new(
1829                        side_bldg_pos.x - side_bldg_length - 3,
1830                        side_bldg_pos.y - side_bldg_width - 3,
1831                    )
1832                    .with_z(side_bldg_base),
1833                    max: Vec2::new(
1834                        side_bldg_pos.x + side_bldg_length + 3,
1835                        side_bldg_pos.y + side_bldg_width + 3,
1836                    )
1837                    .with_z(side_bldg_base + (2 * castle_height) + 2),
1838                });
1839                side_bldg_room_stairs
1840                    .sample(wall_staircase(
1841                        side_bldg_pos.with_z(side_bldg_base + 1),
1842                        (side_bldg_width + 3) as f32,
1843                        side_bldg_roof_height as f32,
1844                    ))
1845                    .fill(candles_lite.clone());
1846                side_bldg_room_stairs
1847                    .sample(wall_staircase(
1848                        side_bldg_pos.with_z(side_bldg_base),
1849                        (side_bldg_width + 3) as f32,
1850                        side_bldg_roof_height as f32,
1851                    ))
1852                    .fill(wood.clone());
1853                // platforms
1854                for dir in DIAGONALS {
1855                    let chain_pos = Vec2::new(
1856                        side_bldg_pos.x + dir.x * (side_bldg_length - 4),
1857                        side_bldg_pos.y + dir.y * (side_bldg_width - 4),
1858                    );
1859                    painter
1860                        .aabb(Aabb {
1861                            min: chain_pos.with_z(castle_base + side_bldg_height - 1),
1862                            max: (chain_pos + 1).with_z(
1863                                side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height) - 1,
1864                            ),
1865                        })
1866                        .fill(chain.clone());
1867                }
1868                for p in 1..=4 {
1869                    let npc_var =
1870                        RandomField::new(0).get(side_bldg_pos.with_z(castle_base + p)) % 5;
1871
1872                    painter
1873                        .aabb(Aabb {
1874                            min: Vec2::new(
1875                                side_bldg_pos.x - side_bldg_length + 4,
1876                                side_bldg_pos.y - side_bldg_width + 4,
1877                            )
1878                            .with_z(castle_base + (side_bldg_height * p) - 2),
1879                            max: Vec2::new(
1880                                side_bldg_pos.x + side_bldg_length - 3,
1881                                side_bldg_pos.y + side_bldg_width - 3,
1882                            )
1883                            .with_z(castle_base + (side_bldg_height * p) - 1),
1884                        })
1885                        .fill(wood.clone());
1886                    if npc_var > 0 {
1887                        random_npc_positions
1888                            .push(side_bldg_pos.with_z(castle_base + (side_bldg_height * p) + 2));
1889                    }
1890                }
1891            } else {
1892                // side bldg far, foundation, entries, stairs for surrounding side bldg
1893                // side bldg entries
1894                let entry_limiter = painter.aabb(Aabb {
1895                    min: (side_bldg_pos - (2 * side_bldg_length))
1896                        .with_z(side_bldg_base + side_bldg_roof_height + (side_bldg_height / 2)),
1897                    max: (side_bldg_pos + (2 * side_bldg_length)).with_z(
1898                        side_bldg_base + side_bldg_roof_height + (side_bldg_height / 2) + 7,
1899                    ),
1900                });
1901                for dir in CARDINALS {
1902                    let entry_pos = side_bldg_pos + dir * (side_bldg_length + 4);
1903                    painter
1904                        .line(
1905                            side_bldg_pos.with_z(
1906                                side_bldg_base + side_bldg_roof_height + (side_bldg_height / 2),
1907                            ),
1908                            entry_pos.with_z(
1909                                side_bldg_base + side_bldg_roof_height + (side_bldg_height / 2),
1910                            ),
1911                            6.5,
1912                        )
1913                        .intersect(entry_limiter)
1914                        .clear();
1915                }
1916                let side_bldg_npc_pos = side_bldg_pos
1917                    .with_z(side_bldg_base + side_bldg_roof_height + (side_bldg_height / 2) + 2);
1918                random_npc_positions.push(side_bldg_npc_pos);
1919
1920                // foundation
1921                painter
1922                    .aabb(Aabb {
1923                        min: Vec2::new(
1924                            side_bldg_pos.x - side_bldg_length - 20,
1925                            side_bldg_pos.y - side_bldg_width - 20,
1926                        )
1927                        .with_z(side_bldg_base - 40),
1928                        max: Vec2::new(
1929                            side_bldg_pos.x + side_bldg_length + 20,
1930                            side_bldg_pos.y + side_bldg_width + 20,
1931                        )
1932                        .with_z(side_bldg_base + 2),
1933                    })
1934                    .fill(brick.clone());
1935                painter
1936                    .aabb(Aabb {
1937                        min: Vec2::new(
1938                            side_bldg_pos.x - side_bldg_length - 8,
1939                            side_bldg_pos.y - side_bldg_width - 8,
1940                        )
1941                        .with_z(side_bldg_base + 1),
1942                        max: Vec2::new(
1943                            side_bldg_pos.x + side_bldg_length + 8,
1944                            side_bldg_pos.y + side_bldg_width + 8,
1945                        )
1946                        .with_z(side_bldg_base + side_bldg_roof_height + (side_bldg_height / 2)),
1947                    })
1948                    .fill(brick.clone());
1949                // stairs
1950                painter
1951                    .ramp_inset(
1952                        Aabb {
1953                            min: Vec2::new(
1954                                side_bldg_pos.x - (2 * side_bldg_length) - 4,
1955                                side_bldg_pos.y - side_bldg_width - 12,
1956                            )
1957                            .with_z(side_bldg_base + 2),
1958                            max: Vec2::new(
1959                                side_bldg_pos.x + side_bldg_length + 8,
1960                                side_bldg_pos.y - side_bldg_width - 8,
1961                            )
1962                            .with_z(
1963                                side_bldg_base + side_bldg_roof_height + (side_bldg_height / 2),
1964                            ),
1965                        },
1966                        (2 * side_bldg_length) + 20,
1967                        Dir2::X,
1968                    )
1969                    .fill(brick.clone());
1970                painter
1971                    .ramp_inset(
1972                        Aabb {
1973                            min: Vec2::new(
1974                                side_bldg_pos.x - side_bldg_length - 8,
1975                                side_bldg_pos.y + side_bldg_width + 8,
1976                            )
1977                            .with_z(side_bldg_base + 2),
1978                            max: Vec2::new(
1979                                side_bldg_pos.x + (2 * side_bldg_length) + 4,
1980                                side_bldg_pos.y + side_bldg_width + 12,
1981                            )
1982                            .with_z(
1983                                side_bldg_base + side_bldg_roof_height + (side_bldg_height / 2),
1984                            ),
1985                        },
1986                        (2 * side_bldg_length) + 20,
1987                        Dir2::NegX,
1988                    )
1989                    .fill(brick.clone());
1990            }
1991            // side bldg wood beams
1992            for corner_pos in side_bldg_beam_pos {
1993                painter
1994                    .line(
1995                        corner_pos.with_z(side_bldg_base),
1996                        corner_pos.with_z(
1997                            side_bldg_base + side_bldg_roof_height + (2 * side_bldg_height) - 1,
1998                        ),
1999                        2.0,
2000                    )
2001                    .fill(wood.clone());
2002            }
2003        }
2004        // re clear floor cellar entry
2005        painter
2006            .cylinder(Aabb {
2007                min: (side_bldg_stairs_pos - 6).with_z(side_bldg_base_raw - 2),
2008                max: (side_bldg_stairs_pos + 6).with_z(side_bldg_base_raw - 1),
2009            })
2010            .clear();
2011        // cellar door to side_bldg
2012        painter
2013            .cylinder(Aabb {
2014                min: (side_bldg_stairs_pos - 7).with_z(side_bldg_base_raw - 1),
2015                max: (side_bldg_stairs_pos + 7).with_z(side_bldg_base_raw),
2016            })
2017            .fill(key_door.clone());
2018        painter
2019            .cylinder(Aabb {
2020                min: (side_bldg_stairs_pos - 2).with_z(side_bldg_base_raw - 1),
2021                max: (side_bldg_stairs_pos - 1).with_z(side_bldg_base_raw),
2022            })
2023            .fill(key_hole.clone());
2024        // clear castle room
2025        painter
2026            .aabb(Aabb {
2027                min: Vec2::new(center.x - castle_length - 3, center.y - castle_width - 3)
2028                    .with_z(castle_base + (castle_height / 2) + 1),
2029                max: Vec2::new(center.x + castle_length + 3, center.y + castle_width + 3)
2030                    .with_z(castle_base + (castle_height / 2) + (2 * castle_height) - 1),
2031            })
2032            .clear();
2033        // side_bldg exit to main roon
2034        let entry_side = if side_bldg_var < 1 {
2035            side_bldg_width + 3
2036        } else {
2037            -(side_bldg_width + 4)
2038        };
2039        painter
2040            .horizontal_cylinder(
2041                Aabb {
2042                    min: Vec2::new(side_bldg_pos_1.x - 4, side_bldg_pos_1.y + entry_side)
2043                        .with_z(side_bldg_base_raw + (2 * castle_height) - 2),
2044                    max: Vec2::new(side_bldg_pos_1.x + 5, side_bldg_pos_1.y + entry_side + 1)
2045                        .with_z(side_bldg_base_raw + (2 * castle_height) + 7),
2046                },
2047                Dir2::NegY,
2048            )
2049            .fill(key_door.clone());
2050        painter
2051            .aabb(Aabb {
2052                min: Vec2::new(side_bldg_pos_1.x, side_bldg_pos_1.y + entry_side)
2053                    .with_z(side_bldg_base_raw + (2 * castle_height)),
2054                max: Vec2::new(side_bldg_pos_1.x + 1, side_bldg_pos_1.y + entry_side + 1)
2055                    .with_z(side_bldg_base_raw + (2 * castle_height) + 1),
2056            })
2057            .fill(key_hole.clone());
2058        painter
2059            .aabb(Aabb {
2060                min: Vec2::new(side_bldg_pos_1.x - 2, side_bldg_pos_1.y + entry_side - 7)
2061                    .with_z(side_bldg_base_raw + (2 * castle_height) - 2),
2062                max: Vec2::new(side_bldg_pos_1.x + 3, side_bldg_pos_1.y + entry_side + 8)
2063                    .with_z(side_bldg_base_raw + (2 * castle_height) - 1),
2064            })
2065            .fill(wood.clone());
2066        painter.sprite(
2067            side_bldg_pos_2.with_z(side_bldg_base_raw),
2068            SpriteKind::DungeonChest3,
2069        );
2070        // main room bossfight
2071        painter
2072            .aabb(Aabb {
2073                min: Vec2::new(center.x - castle_length - 3, center.y - castle_width - 3)
2074                    .with_z(castle_base + (2 * castle_height) - 7),
2075                max: Vec2::new(center.x + castle_length + 3, center.y + castle_width + 3)
2076                    .with_z(castle_base + (2 * castle_height) - 6),
2077            })
2078            .fill(candles_lite.clone());
2079        painter
2080            .aabb(Aabb {
2081                min: Vec2::new(center.x - castle_length - 3, center.y - castle_width - 3)
2082                    .with_z(castle_base + (2 * castle_height) - 8),
2083                max: Vec2::new(center.x + castle_length + 3, center.y + castle_width + 3)
2084                    .with_z(castle_base + (2 * castle_height) - 7),
2085            })
2086            .fill(wood.clone());
2087        painter
2088            .aabb(Aabb {
2089                min: Vec2::new(center.x - castle_length + 3, center.y - castle_width + 3)
2090                    .with_z(castle_base + (2 * castle_height) - 8),
2091                max: Vec2::new(center.x + castle_length - 3, center.y + castle_width - 3)
2092                    .with_z(castle_base + (2 * castle_height) - 6),
2093            })
2094            .clear();
2095        // castle room decor
2096        for r in 0..2 {
2097            let row = r * ((2 * castle_height) - 3);
2098            painter
2099                .aabb(Aabb {
2100                    min: Vec2::new(center.x - castle_length - 3, center.y - castle_width - 3)
2101                        .with_z(castle_base + (castle_height / 2) + 1 + row),
2102                    max: Vec2::new(center.x + castle_length + 3, center.y + castle_width + 3)
2103                        .with_z(castle_base + (castle_height / 2) + 2 + row),
2104                })
2105                .fill(wood.clone());
2106            painter
2107                .aabb(Aabb {
2108                    min: Vec2::new(center.x - castle_length - 1, center.y - castle_width - 1)
2109                        .with_z(castle_base + (castle_height / 2) + 1 + row),
2110                    max: Vec2::new(center.x + castle_length + 1, center.y + castle_width + 1)
2111                        .with_z(castle_base + (castle_height / 2) + 2 + row),
2112                })
2113                .clear();
2114        }
2115
2116        // wood decor and podiums with candle sprites
2117        let castle_podium_limiter_1 = painter.aabb(Aabb {
2118            min: Vec2::new(center.x - castle_length - 3, center.y - castle_width - 3)
2119                .with_z(castle_base + (castle_height / 2) + 1),
2120            max: Vec2::new(center.x + castle_length + 3, center.y + castle_width + 3)
2121                .with_z(castle_base + (castle_height / 2) + 4),
2122        });
2123        let castle_podium_limiter_2 = painter.aabb(Aabb {
2124            min: Vec2::new(center.x - castle_length - 3, center.y - castle_width - 3)
2125                .with_z(castle_base + (2 * castle_height) - 10),
2126            max: Vec2::new(center.x + castle_length + 3, center.y + castle_width + 3)
2127                .with_z(castle_base + (2 * castle_height) - 6),
2128        });
2129        let mut wood_beam_postions = vec![];
2130        let mut outer_beam_postions = vec![];
2131        for dir in DIAGONALS {
2132            let beam_pos_outer = Vec2::new(
2133                center.x + (dir.x * (castle_length + 2)),
2134                center.y + (dir.y * (castle_width + 2)),
2135            );
2136            let beam_pos_inner = Vec2::new(
2137                center.x + (dir.x * (castle_length / 4)),
2138                center.y + (dir.y * (castle_width + 2)),
2139            );
2140            wood_beam_postions.push(beam_pos_outer);
2141            outer_beam_postions.push(beam_pos_outer);
2142            wood_beam_postions.push(beam_pos_inner);
2143        }
2144        for beam_pos in wood_beam_postions {
2145            for n in 0..2 {
2146                painter
2147                    .cylinder_with_radius(
2148                        beam_pos.with_z(castle_base + (castle_height / 2) + 1 + n),
2149                        (5 - n) as f32,
2150                        1.0,
2151                    )
2152                    .intersect(castle_podium_limiter_1)
2153                    .fill(wood.clone());
2154            }
2155        }
2156        for beam_pos in outer_beam_postions {
2157            for n in 0..3 {
2158                painter
2159                    .cylinder_with_radius(
2160                        beam_pos.with_z(castle_base + (2 * castle_height) - 10 + n),
2161                        (2 + n) as f32,
2162                        1.0,
2163                    )
2164                    .intersect(castle_podium_limiter_2)
2165                    .fill(wood.clone());
2166            }
2167            painter
2168                .cylinder_with_radius(
2169                    beam_pos.with_z(castle_base + (2 * castle_height) - 7),
2170                    4.0,
2171                    1.0,
2172                )
2173                .intersect(castle_podium_limiter_2)
2174                .fill(candles.clone());
2175            painter
2176                .line(
2177                    beam_pos.with_z(castle_base + (castle_height / 2) + 1),
2178                    beam_pos.with_z(castle_base + (castle_height / 2) + (2 * castle_height) - 1),
2179                    2.0,
2180                )
2181                .fill(wood.clone());
2182        }
2183        // boss
2184        let boss_pos = center.with_z(castle_base + (castle_height / 2) + 2);
2185        painter.spawn(EntityInfo::at(boss_pos.as_()).with_asset_expect(
2186            "common.entity.dungeon.vampire.bloodmoon_bat",
2187            &mut rng,
2188            None,
2189        ));
2190        // bats
2191        for bat_pos in bat_positions {
2192            for _ in 0..2 {
2193                painter.spawn(EntityInfo::at(bat_pos.as_()).with_asset_expect(
2194                    "common.entity.dungeon.vampire.vampire_bat",
2195                    &mut rng,
2196                    None,
2197                ))
2198            }
2199        }
2200        // harlequins
2201        for harlequin_pos in harlequin_positions {
2202            painter.spawn(EntityInfo::at(harlequin_pos.as_()).with_asset_expect(
2203                "common.entity.dungeon.vampire.harlequin",
2204                &mut rng,
2205                None,
2206            ))
2207        }
2208        for npc_pos in random_npc_positions {
2209            spawn_random_entity(npc_pos, painter);
2210        }
2211    }
2212}
2213
2214pub fn spawn_random_entity(pos: Vec3<i32>, painter: &Painter) {
2215    let mut rng = rand::rng();
2216    let entities = [
2217        "common.entity.dungeon.vampire.strigoi",
2218        "common.entity.dungeon.vampire.executioner",
2219        "common.entity.dungeon.vampire.bloodservant",
2220    ];
2221    let random_entity_index = rng.random_range(0..entities.len());
2222    let random_entity = entities[random_entity_index];
2223    painter.spawn(EntityInfo::at(pos.as_()).with_asset_expect(random_entity, &mut rng, None));
2224}