use super::*;
use crate::{
    util::{RandomField, Sampler, CARDINALS, DIAGONALS, NEIGHBORS},
    Land,
};
use common::{
    generation::{EntityInfo, SpecialEntity},
    terrain::{BlockKind, SpriteKind},
};
use rand::prelude::*;
use std::sync::Arc;
use vek::*;

/// Represents house data generated by the `generate()` method
pub struct SavannahPit {
    /// Axis aligned bounding region for the house
    bounds: Aabr<i32>,
    /// Approximate altitude of the door tile
    pub(crate) alt: i32,
}

impl SavannahPit {
    pub fn generate(land: &Land, _rng: &mut impl Rng, site: &Site, tile_aabr: Aabr<i32>) -> Self {
        let bounds = Aabr {
            min: site.tile_wpos(tile_aabr.min),
            max: site.tile_wpos(tile_aabr.max),
        };
        Self {
            bounds,
            alt: land.get_alt_approx(site.tile_center_wpos(tile_aabr.center())) as i32 + 2,
        }
    }
}

impl Structure for SavannahPit {
    #[cfg(feature = "use-dyn-lib")]
    const UPDATE_FN: &'static [u8] = b"render_savannahpit\0";

    #[cfg_attr(feature = "be-dyn-lib", export_name = "render_savannahpit")]
    fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) {
        let base = self.alt + 1;
        let center = self.bounds.center();
        let sprite_fill = Fill::Sampling(Arc::new(|wpos| {
            Some(match (RandomField::new(0).get(wpos)) % 50 {
                0 => Block::air(SpriteKind::Bowl),
                1 => Block::air(SpriteKind::VialEmpty),
                2 => Block::air(SpriteKind::Lantern),
                3 => Block::air(SpriteKind::JugArabic),
                4 => Block::air(SpriteKind::Crate),
                _ => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)),
            })
        }));
        let wood_light = Fill::Brick(BlockKind::Misc, Rgb::new(179, 125, 64), 12);
        let wood_dark = Fill::Brick(BlockKind::Misc, Rgb::new(142, 67, 27), 12);
        let reed = Fill::Brick(BlockKind::Misc, Rgb::new(72, 55, 46), 22);
        let clay = Fill::Brick(BlockKind::Misc, Rgb::new(209, 124, 57), 22);
        let color = Fill::Sampling(Arc::new(|center| {
            Some(match (RandomField::new(0).get(center)) % 7 {
                0 => Block::new(BlockKind::GlowingRock, Rgb::new(153, 82, 40)),
                1 => Block::new(BlockKind::GlowingRock, Rgb::new(172, 104, 57)),
                2 => Block::new(BlockKind::GlowingRock, Rgb::new(135, 106, 100)),
                3 => Block::new(BlockKind::GlowingRock, Rgb::new(198, 164, 139)),
                4 => Block::new(BlockKind::GlowingRock, Rgb::new(168, 163, 157)),
                5 => Block::new(BlockKind::GlowingRock, Rgb::new(73, 53, 42)),
                _ => Block::new(BlockKind::GlowingRock, Rgb::new(178, 124, 90)),
            })
        }));
        let snow_fill = Fill::Block(Block::new(BlockKind::Snow, Rgb::new(255, 255, 255)));
        let length = 9;
        // foundation
        painter
            .superquadric(
                Aabb {
                    min: (center - (4 * length)).with_z(base - (3 * length)),
                    max: (center + (4 * length)).with_z(base),
                },
                5.0,
            )
            .fill(clay.clone());
        // outside cylinder and decor
        painter
            .cylinder(Aabb {
                min: (center - (3 * length) - 2).with_z(base - (1 * length)),
                max: (center + (3 * length) + 2).with_z(base),
            })
            .fill(clay.clone());
        painter
            .cylinder(Aabb {
                min: (center - (7 * length)).with_z(base - (6 * length) - 3),
                max: (center + (7 * length)).with_z(base - (1 * length)),
            })
            .fill(clay.clone());
        // decor inlays
        let decor_var = RandomField::new(0).get(center.with_z(base)) % 5;
        for s in 0..3 {
            for dir in NEIGHBORS {
                let sphere_pos = center + dir * ((2 * length) - (s * length));
                let decor1 = Aabb {
                    min: (sphere_pos - length).with_z(base - ((2 * length) - (s * length))),
                    max: (sphere_pos + length).with_z(base - (s * length)),
                };
                let decor2 = Aabb {
                    min: (sphere_pos - length + 1).with_z(base - ((2 * length) - (s * length))),
                    max: (sphere_pos + length - 1).with_z(base - (s * length)),
                };
                match decor_var {
                    0 => {
                        painter.cone(decor1).fill(color.clone());
                        painter.cone(decor2).fill(clay.clone())
                    },
                    1 => {
                        painter.aabb(decor1).fill(color.clone());
                        painter.aabb(decor2).fill(clay.clone())
                    },
                    2 => {
                        painter.cylinder(decor1).fill(color.clone());
                        painter.cylinder(decor2).fill(clay.clone())
                    },
                    _ => {
                        painter.sphere(decor1).fill(color.clone());
                        painter.sphere(decor2).fill(clay.clone())
                    },
                };
            }
            for dir in CARDINALS {
                let sphere_pos = center + dir * ((2 * length) - (s * length));
                let decor1 = Aabb {
                    min: (sphere_pos - (2 * length)).with_z(base - ((4 * length) - (s * length))),
                    max: (sphere_pos + (2 * length)).with_z(base - (s * length)),
                };
                let decor2 = Aabb {
                    min: (sphere_pos - (2 * length) + 1)
                        .with_z(base - ((4 * length) - (s * length))),
                    max: (sphere_pos + (2 * length) - 1).with_z(base - (s * length)),
                };
                match decor_var {
                    0 => {
                        painter.cone(decor1).fill(color.clone());
                        painter.cone(decor2).fill(clay.clone())
                    },
                    1 => {
                        painter.aabb(decor1).fill(color.clone());
                        painter.aabb(decor2).fill(clay.clone())
                    },
                    2 => {
                        painter.cylinder(decor1).fill(color.clone());
                        painter.cylinder(decor2).fill(clay.clone())
                    },
                    _ => {
                        painter.sphere(decor1).fill(color.clone());
                        painter.sphere(decor2).fill(clay.clone())
                    },
                };
            }
        }
        // market hall decor inlays
        for dir in NEIGHBORS {
            let sphere_pos = center + dir * (3 * length);
            let decor1 = Aabb {
                min: (sphere_pos - length).with_z(base - (5 * length)),
                max: (sphere_pos + length).with_z(base - (4 * length)),
            };
            let decor2 = Aabb {
                min: (sphere_pos - length + 1).with_z(base - (5 * length)),
                max: (sphere_pos + length - 1).with_z(base - (4 * length)),
            };
            match decor_var {
                0 => {
                    painter.cone(decor1).fill(color.clone());
                    painter.cone(decor2).fill(clay.clone())
                },
                1 => {
                    painter.aabb(decor1).fill(color.clone());
                    painter.aabb(decor2).fill(clay.clone())
                },
                2 => {
                    painter.cylinder(decor1).fill(color.clone());
                    painter.cylinder(decor2).fill(clay.clone())
                },
                _ => {
                    painter.sphere(decor1).fill(color.clone());
                    painter.sphere(decor2).fill(clay.clone())
                },
            };
        }
        for dir in CARDINALS {
            let sphere_pos = center + dir * (3 * length);
            let decor1 = Aabb {
                min: (sphere_pos - (2 * length)).with_z(base - (5 * length)),
                max: (sphere_pos + (2 * length)).with_z(base - (3 * length)),
            };
            let decor2 = Aabb {
                min: (sphere_pos - (2 * length) + 1).with_z(base - (5 * length)),
                max: (sphere_pos + (2 * length) - 1).with_z(base - (3 * length)),
            };
            match decor_var {
                0 => {
                    painter.cone(decor1).fill(color.clone());
                    painter.cone(decor2).fill(clay.clone())
                },
                1 => {
                    painter.aabb(decor1).fill(color.clone());
                    painter.aabb(decor2).fill(clay.clone())
                },
                2 => {
                    painter.cylinder(decor1).fill(color.clone());
                    painter.cylinder(decor2).fill(clay.clone())
                },
                _ => {
                    painter.sphere(decor1).fill(color.clone());
                    painter.sphere(decor2).fill(clay.clone())
                },
            };
        }
        // top ring
        painter
            .cylinder(Aabb {
                min: (center - (3 * length) - 1).with_z(base - 1),
                max: (center + (3 * length) + 1).with_z(base),
            })
            .fill(wood_light.clone());
        // floor 0 ring
        painter
            .cylinder(Aabb {
                min: (center - (2 * length) - 1).with_z(base - length - 1),
                max: (center + (2 * length) + 1).with_z(base - length),
            })
            .fill(wood_light.clone());
        // floor -1 inner ring
        painter
            .cylinder(Aabb {
                min: (center - length - 1).with_z(base - (2 * length) - 1),
                max: (center + length + 1).with_z(base - (2 * length)),
            })
            .fill(wood_light.clone());
        // floor 0 outer ring
        painter
            .cylinder(Aabb {
                min: (center - (3 * length) - 1).with_z(base - length + (length / 2) - 1),
                max: (center + (3 * length) + 1).with_z(base - length + (length / 2)),
            })
            .fill(wood_light.clone());
        // floor -1 inner ring
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) - 1).with_z(base - (2 * length) + (length / 2) - 1),
                max: (center + (4 * length) + 1).with_z(base - (2 * length) + (length / 2)),
            })
            .fill(wood_light.clone());
        // floor -2 inner ring
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) - 1).with_z(base - (3 * length) + (length / 2) - 1),
                max: (center + (4 * length) + 1).with_z(base - (3 * length) + (length / 2)),
            })
            .fill(wood_light.clone());

        // floor 0
        painter
            .cylinder(Aabb {
                min: (center - (3 * length)).with_z(base - length),
                max: (center + (3 * length)).with_z(base + 25),
            })
            .clear();
        // floor -1
        painter
            .cylinder(Aabb {
                min: (center - (2 * length)).with_z(base - (2 * length)),
                max: (center + (2 * length)).with_z(base - length),
            })
            .clear();
        // floor -2
        painter
            .cylinder(Aabb {
                min: (center - length).with_z(base - (3 * length)),
                max: (center + length).with_z(base - (2 * length)),
            })
            .clear();
        // floor -1 donut room & floor -2 donut room
        let rooms_clear = painter
            .cylinder(Aabb {
                min: (center - (4 * length)).with_z(base - (2 * length)),
                max: (center + (4 * length)).with_z(base - length - 1),
            })
            .union(painter.cylinder(Aabb {
                min: (center - (4 * length)).with_z(base - (3 * length)),
                max: (center + (4 * length)).with_z(base - (2 * length) - 1),
            }));

        let rooms = painter
            .cylinder(Aabb {
                min: (center - (2 * length) - 2).with_z(base - (2 * length)),
                max: (center + (2 * length) + 2).with_z(base - length - 1),
            })
            .union(painter.cylinder(Aabb {
                min: (center - length - 2).with_z(base - (3 * length)),
                max: (center + length + 2).with_z(base - (2 * length) - 1),
            }));
        rooms_clear.without(rooms).clear();

        // floor 0 wood ring
        painter
            .cylinder(Aabb {
                min: (center - (3 * length)).with_z(base - length + (length / 2) - 3),
                max: (center + (3 * length)).with_z(base - length + (length / 2) - 2),
            })
            .fill(wood_light.clone());
        painter
            .cylinder(Aabb {
                min: (center - (3 * length) + 1).with_z(base - length + (length / 2) - 3),
                max: (center + (3 * length) - 1).with_z(base - length + (length / 2) - 2),
            })
            .clear();
        // floor -1 wood ring
        painter
            .cylinder(Aabb {
                min: (center - (4 * length)).with_z(base - (2 * length) + (length / 2) - 3),
                max: (center + (4 * length)).with_z(base - (2 * length) + (length / 2) - 2),
            })
            .fill(wood_light.clone());
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) + 1).with_z(base - (2 * length) + (length / 2) - 3),
                max: (center + (4 * length) - 1).with_z(base - (2 * length) + (length / 2) - 2),
            })
            .clear();
        // floor -1 wood ring sprites
        painter
            .cylinder(Aabb {
                min: (center - (4 * length)).with_z(base - (2 * length) + (length / 2) - 2),
                max: (center + (4 * length)).with_z(base - (2 * length) + (length / 2) - 1),
            })
            .fill(sprite_fill.clone());
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) + 1).with_z(base - (2 * length) + (length / 2) - 2),
                max: (center + (4 * length) - 1).with_z(base - (2 * length) + (length / 2) - 1),
            })
            .clear();
        // floor -1 center decor ring
        painter
            .cylinder(Aabb {
                min: (center - (2 * length) - 2).with_z(base - (2 * length) + (length / 2) - 3),
                max: (center + (2 * length) + 2).with_z(base - (2 * length) + (length / 2) - 1),
            })
            .fill(color.clone());
        painter
            .cylinder(Aabb {
                min: (center - (2 * length)).with_z(base - (2 * length) + (length / 2) - 3),
                max: (center + (2 * length)).with_z(base - (2 * length) + (length / 2) - 1),
            })
            .clear();
        // floor -1 room entry
        let room1_entry_pos = Vec2::new(center.x, center.y - (2 * length));
        painter
            .sphere(Aabb {
                min: (room1_entry_pos - 5).with_z(base - (2 * length)),
                max: (room1_entry_pos + 5).with_z(base - (2 * length) + 5),
            })
            .clear();
        // floor -2 wood ring
        painter
            .cylinder(Aabb {
                min: (center - (4 * length)).with_z(base - (3 * length) + (length / 2) - 3),
                max: (center + (4 * length)).with_z(base - (3 * length) + (length / 2) - 2),
            })
            .fill(wood_light.clone());
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) + 1).with_z(base - (3 * length) + (length / 2) - 3),
                max: (center + (4 * length) - 1).with_z(base - (3 * length) + (length / 2) - 2),
            })
            .clear();
        // floor -2 wood ring sprites
        painter
            .cylinder(Aabb {
                min: (center - (4 * length)).with_z(base - (3 * length) + (length / 2) - 2),
                max: (center + (4 * length)).with_z(base - (3 * length) + (length / 2) - 1),
            })
            .fill(sprite_fill.clone());
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) + 1).with_z(base - (3 * length) + (length / 2) - 2),
                max: (center + (4 * length) - 1).with_z(base - (3 * length) + (length / 2) - 1),
            })
            .clear();
        // floor -2 center decor ring
        painter
            .cylinder(Aabb {
                min: (center - length - 2).with_z(base - (3 * length) + (length / 2) - 3),
                max: (center + length + 2).with_z(base - (3 * length) + (length / 2) - 1),
            })
            .fill(color);
        painter
            .cylinder(Aabb {
                min: (center - length).with_z(base - (3 * length) + (length / 2) - 3),
                max: (center + length).with_z(base - (3 * length) + (length / 2) - 1),
            })
            .clear();
        // floor -2 room entry
        let room2_entry_pos = Vec2::new(center.x, center.y - length);
        painter
            .sphere(Aabb {
                min: (room2_entry_pos - 5).with_z(base - (3 * length)),
                max: (room2_entry_pos + 5).with_z(base - (3 * length) + 5),
            })
            .clear();

        // floor stairs lamps
        for dir in SQUARE_4 {
            let corner_pos1 = Vec2::new(center.x - 1, center.y - (3 * length) + 1);

            let lamp_pos1 = Vec2::new(
                corner_pos1.x + dir.x * 1,
                corner_pos1.y + dir.y * ((6 * length) - 3),
            );
            painter.rotated_sprite(
                lamp_pos1.with_z(base - length + 9),
                SpriteKind::WallLampSmall,
                4 - (4 * dir.y) as u8,
            );
            let corner_pos2 = Vec2::new(center.x - (3 * length) + 1, center.y - 1);
            let lamp_pos2 = Vec2::new(
                corner_pos2.x + dir.x * ((6 * length) - 3),
                corner_pos2.y + dir.y,
            );
            painter.rotated_sprite(
                lamp_pos2.with_z(base - length + 9),
                SpriteKind::WallLampSmall,
                2 + (4 * dir.x) as u8,
            );
        }

        for dir in SQUARE_4 {
            let corner_pos1 = Vec2::new(center.x - 1, center.y - (2 * length));

            let lamp_pos1 = Vec2::new(
                corner_pos1.x + dir.x * 1,
                corner_pos1.y + dir.y * ((4 * length) - 1),
            );
            painter.rotated_sprite(
                lamp_pos1.with_z(base - (2 * length) + 8),
                SpriteKind::WallLampSmall,
                4 - (4 * dir.y) as u8,
            );
            let corner_pos2 = Vec2::new(center.x - (2 * length), center.y - 1);
            let lamp_pos2 = Vec2::new(
                corner_pos2.x + dir.x * ((4 * length) - 1),
                corner_pos2.y + dir.y,
            );
            painter.rotated_sprite(
                lamp_pos2.with_z(base - (2 * length) + 8),
                SpriteKind::WallLampSmall,
                2 + (4 * dir.x) as u8,
            );
        }
        for dir in SQUARE_4 {
            let corner_pos1 = Vec2::new(center.x - 1, center.y - length);

            let lamp_pos1 = Vec2::new(
                corner_pos1.x + dir.x * 1,
                corner_pos1.y + dir.y * ((2 * length) - 1),
            );
            painter.rotated_sprite(
                lamp_pos1.with_z(base - (3 * length) + 8),
                SpriteKind::WallLampSmall,
                4 - (4 * dir.y) as u8,
            );
            let corner_pos2 = Vec2::new(center.x - length, center.y - 1);
            let lamp_pos2 = Vec2::new(
                corner_pos2.x + dir.x * ((2 * length) - 1),
                corner_pos2.y + dir.y,
            );
            painter.rotated_sprite(
                lamp_pos2.with_z(base - (3 * length) + 8),
                SpriteKind::WallLampSmall,
                2 + (4 * dir.x) as u8,
            );
        }

        let stair_radius0 = ((3 * length) + 1) as f32;
        let stairs_clear0 = painter.prim(Primitive::Cylinder(Aabb {
            min: (center - (3 * length)).with_z(base - length),
            max: (center + (3 * length)).with_z(base),
        }));
        painter
            .prim(Primitive::sampling(
                stairs_clear0,
                dungeon::spiral_staircase(
                    center.with_z(base - length),
                    stair_radius0,
                    2.5,
                    (2 * length) as f32,
                ),
            ))
            .fill(wood_dark.clone());
        let stair_radius1 = ((2 * length) + 1) as f32;
        let stairs_clear1 = painter.prim(Primitive::Cylinder(Aabb {
            min: (center - (3 * length)).with_z(base - (2 * length)),
            max: (center + (3 * length)).with_z(base - length),
        }));
        painter
            .prim(Primitive::sampling(
                stairs_clear1,
                dungeon::spiral_staircase(
                    center.with_z(base - (2 * length)),
                    stair_radius1,
                    2.5,
                    (2 * length) as f32,
                ),
            ))
            .fill(wood_dark.clone());
        let stair_radius2 = ((2 * length) + 1) as f32;
        let stairs_clear2 = painter.prim(Primitive::Cylinder(Aabb {
            min: (center - length).with_z(base - (3 * length)),
            max: (center + length).with_z(base - (2 * length)),
        }));
        painter
            .prim(Primitive::sampling(
                stairs_clear2,
                dungeon::spiral_staircase(
                    center.with_z(base - (3 * length)),
                    stair_radius2,
                    2.5,
                    (2 * length) as f32,
                ),
            ))
            .fill(wood_dark.clone());
        // top wood cone
        painter
            .cone(Aabb {
                min: (center - (4 * length)).with_z(base + length),
                max: (center + (4 * length)).with_z(base + (3 * length)),
            })
            .fill(reed.clone());
        painter
            .cone(Aabb {
                min: (center - (4 * length)).with_z(base + length - 1),
                max: (center + (4 * length)).with_z(base + (3 * length) - 1),
            })
            .clear();
        painter
            .cylinder(Aabb {
                min: (center - 2).with_z(base),
                max: (center + 2).with_z(base + (3 * length) - 1),
            })
            .fill(wood_dark.clone());
        for dir in CARDINALS {
            let outer_support = center + dir * ((3 * length) + 2);
            painter
                .cylinder(Aabb {
                    min: (outer_support - 3).with_z(base - length),
                    max: (outer_support + 3).with_z(base + length + 2),
                })
                .fill(wood_dark.clone());
        }
        // wood platform
        painter
            .cylinder(Aabb {
                min: (center - length).with_z(base + length + 1),
                max: (center + length).with_z(base + length + 2),
            })
            .fill(wood_light.clone());
        // small stairs
        let small_stairs_pos = center - 5;
        let small_stair_radius = 4.0;
        let small_stairs_clear = painter.cylinder(Aabb {
            min: (small_stairs_pos + 1 - small_stair_radius as i32).with_z(base),
            max: (small_stairs_pos - 1 + small_stair_radius as i32).with_z(base + length + 3),
        });
        small_stairs_clear.clear();
        painter
            .prim(Primitive::sampling(
                small_stairs_clear,
                dungeon::spiral_staircase(
                    small_stairs_pos.with_z(base),
                    small_stair_radius,
                    0.5,
                    (length - 2) as f32,
                ),
            ))
            .fill(wood_dark.clone());

        // small outer rooms 1
        for dir in CARDINALS {
            let room_center = center + dir * ((4 * length) - (length / 4));
            let entries_clear = center + dir * ((3 * length) - (length / 4));
            for f in 1..3 {
                painter
                    .cylinder(Aabb {
                        min: (room_center - length - 1).with_z(base - ((1 + f) * length)),
                        max: (room_center + length + 1).with_z(base - (length * f) - 1),
                    })
                    .fill(clay.clone());

                painter
                    .cylinder(Aabb {
                        min: (room_center - length).with_z(base - ((1 + f) * length)),
                        max: (room_center + length).with_z(base - (length * f) - 1),
                    })
                    .clear();
                painter
                    .cylinder(Aabb {
                        min: (room_center - length).with_z(base - ((1 + f) * length) + 1),
                        max: (room_center + length).with_z(base - ((1 + f) * length) + 2),
                    })
                    .fill(wood_light.clone());
                painter
                    .cylinder(Aabb {
                        min: (room_center - length).with_z(base - ((1 + f) * length) + 2),
                        max: (room_center + length).with_z(base - ((1 + f) * length) + 3),
                    })
                    .fill(sprite_fill.clone());
                painter
                    .cylinder(Aabb {
                        min: (room_center - length + 1).with_z(base - ((1 + f) * length) + 1),
                        max: (room_center + length - 1).with_z(base - ((1 + f) * length) + 3),
                    })
                    .clear();
                painter
                    .sphere(Aabb {
                        min: (entries_clear - 2).with_z(base - ((1 + f) * length)),
                        max: (entries_clear + 2).with_z(base - ((1 + f) * length) + 4),
                    })
                    .clear();
            }
            // outside platforms
            painter
                .cylinder(Aabb {
                    min: (room_center - length + 1).with_z(base + length),
                    max: (room_center + length - 1).with_z(base + length + 1),
                })
                .fill(wood_light.clone());
            //small top cones
            let top_var = RandomField::new(0).get(room_center.with_z(base)) % 4;
            if let 0..=2 = top_var {
                painter
                    .cone(Aabb {
                        min: (room_center - length)
                            .with_z(base + (2 * length) + (3 * top_var) as i32),
                        max: (room_center + length)
                            .with_z(base + (2 * length) + (length / 2) + 2 + (3 * top_var) as i32),
                    })
                    .fill(reed.clone());
                painter
                    .cone(Aabb {
                        min: (room_center - length)
                            .with_z(base + (2 * length) - 1 + (3 * top_var) as i32),
                        max: (room_center + length)
                            .with_z(base + (2 * length) + (length / 2) + 1 + (3 * top_var) as i32),
                    })
                    .clear();
                painter
                    .aabb(Aabb {
                        min: (room_center - 1).with_z(base + length + 1),
                        max: (room_center + 1)
                            .with_z(base + (2 * length) + 5 + (3 * top_var) as i32),
                    })
                    .fill(wood_dark.clone());
            }
            let lantern_pos = center + dir * ((4 * length) - (length / 4) + 4);
            painter.sprite(lantern_pos.with_z(base + length + 1), SpriteKind::Lantern);
        }

        // small outer rooms 2
        for dir in DIAGONALS {
            let room_center = center + dir * ((3 * length) - (length / 3));
            let entries_clear = center + dir * (2 * length);
            for f in 1..3 {
                painter
                    .cylinder(Aabb {
                        min: (room_center - length - 1).with_z(base - ((1 + f) * length)),
                        max: (room_center + length + 1).with_z(base - (length * f) - 1),
                    })
                    .fill(clay.clone());

                painter
                    .cylinder(Aabb {
                        min: (room_center - length).with_z(base - ((1 + f) * length)),
                        max: (room_center + length).with_z(base - (length * f) - 1),
                    })
                    .clear();
                painter
                    .cylinder(Aabb {
                        min: (room_center - length).with_z(base - ((1 + f) * length) + 1),
                        max: (room_center + length).with_z(base - ((1 + f) * length) + 2),
                    })
                    .fill(wood_light.clone());
                painter
                    .cylinder(Aabb {
                        min: (room_center - length).with_z(base - ((1 + f) * length) + 2),
                        max: (room_center + length).with_z(base - ((1 + f) * length) + 3),
                    })
                    .fill(sprite_fill.clone());
                painter
                    .cylinder(Aabb {
                        min: (room_center - length + 1).with_z(base - ((1 + f) * length) + 1),
                        max: (room_center + length - 1).with_z(base - ((1 + f) * length) + 3),
                    })
                    .clear();
                painter
                    .sphere(Aabb {
                        min: (entries_clear - 3).with_z(base - ((1 + f) * length)),
                        max: (entries_clear + 3).with_z(base - ((1 + f) * length) + 4),
                    })
                    .clear();
                // furniture
                match RandomField::new(0).get(room_center.with_z(base)) % 2 {
                    0 => {
                        // bath rooms
                        let mut sprites = vec![
                            SpriteKind::Bowl,
                            SpriteKind::VialEmpty,
                            SpriteKind::JugArabic,
                            SpriteKind::JugAndBowlArabic,
                            SpriteKind::FountainArabic,
                            SpriteKind::SepareArabic,
                        ];
                        'outer3: for d in 0..2 {
                            for dir in NEIGHBORS {
                                if sprites.is_empty() {
                                    break 'outer3;
                                }
                                let position = room_center + dir * (5 + d);
                                let sprite = sprites.swap_remove(
                                    RandomField::new(0).get(position.with_z(base)) as usize
                                        % sprites.len(),
                                );
                                painter.sprite(position.with_z(base - ((1 + f) * length)), sprite);
                            }
                        }
                    },
                    1 => {
                        // bed rooms
                        let mut sprites = vec![
                            SpriteKind::DrawerSmall,
                            SpriteKind::ChairSingle,
                            SpriteKind::CoatRack,
                            SpriteKind::Bed,
                            SpriteKind::WardrobeSingle,
                            SpriteKind::CushionArabic,
                            SpriteKind::TableArabicSmall,
                            SpriteKind::DecorSetArabic,
                        ];
                        'outer4: for d in 0..2 {
                            for dir in NEIGHBORS {
                                if sprites.is_empty() {
                                    break 'outer4;
                                }
                                let position = room_center + dir * (5 + d);
                                let sprite = sprites.swap_remove(
                                    RandomField::new(0).get(position.with_z(base)) as usize
                                        % sprites.len(),
                                );
                                painter.sprite(position.with_z(base - ((1 + f) * length)), sprite);
                            }
                        }
                    },
                    _ => {},
                }
            }
            // outside platforms connected to rooms
            painter
                .cylinder(Aabb {
                    min: (room_center - length + 1).with_z(base + length),
                    max: (room_center + length - 1).with_z(base + length + 1),
                })
                .fill(wood_light.clone());
            let small_stairs_pos = room_center;
            let small_stair_radius = 4.0;
            let small_stairs_clear = painter.cylinder(Aabb {
                min: (small_stairs_pos + 1 - small_stair_radius as i32).with_z(base - (3 * length)),
                max: (small_stairs_pos - 1 + small_stair_radius as i32).with_z(base + length + 3),
            });
            small_stairs_clear.clear();
            painter
                .prim(Primitive::sampling(
                    small_stairs_clear,
                    dungeon::spiral_staircase(
                        small_stairs_pos.with_z(base - (3 * length)),
                        small_stair_radius,
                        0.5,
                        (length - 2) as f32,
                    ),
                ))
                .fill(wood_dark.clone());
            //small top cones
            let top_var = RandomField::new(0).get(room_center.with_z(base)) % 4;
            if let 0..=2 = top_var {
                painter
                    .cone(Aabb {
                        min: (room_center - length)
                            .with_z(base + (2 * length) + (3 * top_var) as i32),
                        max: (room_center + length)
                            .with_z(base + (2 * length) + (length / 2) + 2 + (3 * top_var) as i32),
                    })
                    .fill(reed.clone());
                painter
                    .cone(Aabb {
                        min: (room_center - length)
                            .with_z(base + (2 * length) - 1 + (3 * top_var) as i32),
                        max: (room_center + length)
                            .with_z(base + (2 * length) + (length / 2) + 1 + (3 * top_var) as i32),
                    })
                    .clear();
                painter
                    .aabb(Aabb {
                        min: (room_center - 1).with_z(base + length + 1),
                        max: (room_center + 1)
                            .with_z(base + (2 * length) + 5 + (3 * top_var) as i32),
                    })
                    .fill(wood_dark.clone());
            }
            let lantern_pos = center + dir * ((3 * length) - (length / 3) + 4);
            painter.sprite(lantern_pos.with_z(base + length + 1), SpriteKind::Lantern);
        }
        // floor room lamps
        for dir in SQUARE_4 {
            for f in 2..4 {
                let corner_pos1 = Vec2::new(center.x - 1, center.y - (2 * length) - 6);

                let lamp_pos1 = Vec2::new(
                    corner_pos1.x + dir.x * 1,
                    corner_pos1.y + dir.y * ((4 * length) + 11),
                );
                painter.rotated_sprite(
                    lamp_pos1.with_z(base - (f * length) + 5),
                    SpriteKind::WallLampSmall,
                    4 - (4 * dir.y) as u8,
                );
                let corner_pos2 = Vec2::new(center.x - (2 * length) - 6, center.y - 1);
                let lamp_pos2 = Vec2::new(
                    corner_pos2.x + dir.x * ((4 * length) + 11),
                    corner_pos2.y + dir.y,
                );
                painter.rotated_sprite(
                    lamp_pos2.with_z(base - (f * length) + 5),
                    SpriteKind::WallLampSmall,
                    2 + (4 * dir.x) as u8,
                );
            }
        }
        // workshop rooms
        for f in 2..4 {
            for n in 0..2 {
                let ws_center = Vec2::new(
                    center.x,
                    center.y - (4 * length) + 2 + (n * ((8 * length) - 4)),
                );
                painter
                    .aabb(Aabb {
                        min: (ws_center - 2).with_z(base - (f * length)),
                        max: (ws_center + 2).with_z(base - (f * length) + 2),
                    })
                    .fill(clay.clone());

                painter
                    .aabb(Aabb {
                        min: (ws_center - 1).with_z(base - (f * length) + 1),
                        max: (ws_center + 1).with_z(base - (f * length) + 2),
                    })
                    .clear();
                painter
                    .aabb(Aabb {
                        min: (ws_center - 1).with_z(base - (f * length)),
                        max: (ws_center + 1).with_z(base - (f * length) + 1),
                    })
                    .fill(Fill::Block(Block::air(SpriteKind::Ember)));
                let mut stations = vec![
                    SpriteKind::CraftingBench,
                    SpriteKind::Forge,
                    SpriteKind::SpinningWheel,
                    SpriteKind::TanningRack,
                    SpriteKind::Loom,
                    SpriteKind::Anvil,
                    SpriteKind::DismantlingBench,
                    SpriteKind::RepairBench,
                ];
                'outer: for d in 0..2 {
                    for dir in NEIGHBORS {
                        if stations.is_empty() {
                            break 'outer;
                        }
                        let position = ws_center + dir * (4 + d * 2);
                        let cr_station = stations.swap_remove(
                            RandomField::new(0).get(position.with_z(base)) as usize
                                % stations.len(),
                        );
                        painter.sprite(position.with_z(base - (f * length)), cr_station);
                    }
                }

                // kitchen rooms
                let k_center = Vec2::new(
                    center.x - (4 * length) + 2 + (n * ((8 * length) - 4)),
                    center.y,
                );
                painter
                    .aabb(Aabb {
                        min: (k_center - 2).with_z(base - (f * length)),
                        max: (k_center + 2).with_z(base - (f * length) + 2),
                    })
                    .fill(clay.clone());

                painter
                    .aabb(Aabb {
                        min: (k_center - 1).with_z(base - (f * length) + 1),
                        max: (k_center + 1).with_z(base - (f * length) + 2),
                    })
                    .clear();
                painter
                    .aabb(Aabb {
                        min: (k_center - 1).with_z(base - (f * length)),
                        max: (k_center + 1).with_z(base - (f * length) + 1),
                    })
                    .fill(Fill::Block(Block::air(SpriteKind::Ember)));
                let mut stations = vec![SpriteKind::CookingPot, SpriteKind::Cauldron];
                'outer2: for d in 0..2 {
                    for dir in NEIGHBORS {
                        if stations.is_empty() {
                            break 'outer2;
                        }
                        let position = k_center + dir * (3 + d);
                        let cr_station = stations.swap_remove(
                            RandomField::new(0).get(position.with_z(base)) as usize
                                % stations.len(),
                        );
                        painter.sprite(position.with_z(base - (f * length)), cr_station);
                    }
                }
            }
        }

        // campfire
        let campfire_pos = (center - 10).with_z(base);

        painter.spawn(
            EntityInfo::at(campfire_pos.map(|e| e as f32)).into_special(SpecialEntity::Waypoint),
        );

        // underground market hall
        let stairs_pos = Vec2::new(center.x, center.y - (2 * length) + 1);
        let cone_pos = Vec2::new(center.x, center.y - (3 * length) + 3);
        let cyl_pos = Vec2::new(center.x, center.y - (4 * length) - 3);
        let crates_pos = Vec2::new(center.x, center.y + (2 * length));
        // entry
        painter
            .cylinder(Aabb {
                min: (stairs_pos - (length / 2) - 1).with_z(base - (5 * length) + 3),
                max: (stairs_pos + (length / 2) + 1).with_z(base - (3 * length)),
            })
            .clear();
        // market hall room
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) + 1).with_z(base - (5 * length) + 3),
                max: (center + (4 * length) - 1).with_z(base - (3 * length) - 2),
            })
            .clear();
        // wood ring with sprites
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) + 1).with_z(base - (5 * length) + 4),
                max: (center + (4 * length) - 1).with_z(base - (5 * length) + 5),
            })
            .fill(wood_light.clone());
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) + 1).with_z(base - (5 * length) + 5),
                max: (center + (4 * length) - 1).with_z(base - (5 * length) + 6),
            })
            .fill(sprite_fill);
        painter
            .cylinder(Aabb {
                min: (center - (4 * length) + 2).with_z(base - (5 * length) + 3),
                max: (center + (4 * length) - 2).with_z(base - (5 * length) + 6),
            })
            .clear();
        // market hall lamps
        for dir in SQUARE_4 {
            let corner_pos1 = Vec2::new(center.x - 1, center.y - (3 * length) - 4);

            let lamp_pos1 = Vec2::new(
                corner_pos1.x + dir.x * 1,
                corner_pos1.y + dir.y * ((6 * length) + 7),
            );
            painter.rotated_sprite(
                lamp_pos1.with_z(base - (5 * length) + 8),
                SpriteKind::WallLampSmall,
                4 - (4 * dir.y) as u8,
            );
            let corner_pos2 = Vec2::new(center.x - (3 * length) - 4, center.y - 1);
            let lamp_pos2 = Vec2::new(
                corner_pos2.x + dir.x * ((6 * length) + 7),
                corner_pos2.y + dir.y,
            );
            painter.rotated_sprite(
                lamp_pos2.with_z(base - (5 * length) + 8),
                SpriteKind::WallLampSmall,
                2 + (4 * dir.x) as u8,
            );
        }
        // market hall entry stairs
        painter
            .cone(Aabb {
                min: (cone_pos - (3 * length)).with_z(base - (5 * length) + 3),
                max: (cone_pos + (3 * length)).with_z(base - (3 * length)),
            })
            .fill(clay.clone());
        painter
            .cylinder(Aabb {
                min: (cyl_pos - (2 * length)).with_z(base - (5 * length) + 3),
                max: (cyl_pos + (2 * length)).with_z(base - (3 * length) - 1),
            })
            .fill(clay.clone());
        // crates
        for dir in CARDINALS {
            let crate_pos = crates_pos + dir * length;
            painter
                .cylinder(Aabb {
                    min: (crate_pos - 3).with_z(base - (5 * length) + 3),
                    max: (crate_pos + 3).with_z(base - (5 * length) + 4),
                })
                .fill(wood_light.clone());
            painter
                .cylinder(Aabb {
                    min: (crate_pos - 3).with_z(base - (5 * length) + 4),
                    max: (crate_pos + 3).with_z(base - (5 * length) + 5),
                })
                // placeholder for market items
                .fill(Fill::Block(Block::air(SpriteKind::Planter)));
            painter
                .cylinder(Aabb {
                    min: (crate_pos - 2).with_z(base - (5 * length) + 3),
                    max: (crate_pos + 2).with_z(base - (5 * length) + 5),
                })
                .clear();
            painter.sprite(
                (crate_pos + 1).with_z(base - (5 * length) + 3),
                SpriteKind::Lantern,
            );
        }
        // fridges
        for c in 0..2 {
            let fridge_pos = Vec2::new(
                crates_pos.x - (2 * length) + (c * (4 * length)),
                crates_pos.y,
            );
            painter
                .aabb(Aabb {
                    min: (fridge_pos - 1).with_z(base - (5 * length) + 3),
                    max: (fridge_pos + 2).with_z(base - (5 * length) + 6),
                })
                .fill(snow_fill.clone());
            painter
                .aabb(Aabb {
                    min: Vec2::new(fridge_pos.x, fridge_pos.y - 1).with_z(base - (5 * length) + 4),
                    max: Vec2::new(fridge_pos.x + 1, fridge_pos.y + 2)
                        .with_z(base - (5 * length) + 5),
                })
                .clear();
            painter
                .aabb(Aabb {
                    min: Vec2::new(fridge_pos.x, fridge_pos.y - 1).with_z(base - (5 * length) + 4),
                    max: Vec2::new(fridge_pos.x + 1, fridge_pos.y + 2).with_z(base - (5 * length) + 5),
                })
                // placeholder for market items
                .fill(Fill::Block(Block::air(SpriteKind::Crate)));
        }
        // supports
        for dir in CARDINALS {
            let outer_support = center + dir * ((4 * length) - 2);
            painter
                .cylinder(Aabb {
                    min: (outer_support - 3).with_z(base - (5 * length) + 3),
                    max: (outer_support + 3).with_z(base - (3 * length) - 2),
                })
                .fill(wood_dark.clone());
        }

        // wall
        let wall_length = length - 1;
        let wall_height = 5;
        let wall_size = 1.5;
        for w in 0..10 {
            // lower wall
            painter
                .line(
                    Vec2::new(center.x, center.y - (7 * wall_length))
                        .with_z(base - ((wall_height / 2) * w)),
                    (center - (6 * wall_length))
                        .with_z(base + (wall_height / 2) - ((wall_height / 2) * w)),
                    wall_size,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    (center - (6 * wall_length)).with_z(base - ((wall_height / 2) * w)),
                    Vec2::new(center.x - (7 * wall_length), center.y)
                        .with_z(base + (wall_height / 2) - ((wall_height / 2) * w)),
                    wall_size,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x - (7 * wall_length), center.y)
                        .with_z(base - ((wall_height / 2) * w)),
                    Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                        .with_z(base + (wall_height / 2) - ((wall_height / 2) * w)),
                    wall_size,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                        .with_z(base - ((wall_height / 2) * w)),
                    Vec2::new(center.x, center.y + (7 * wall_length))
                        .with_z(base + (wall_height / 2) - ((wall_height / 2) * w)),
                    wall_size,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x, center.y + (7 * wall_length))
                        .with_z(base - ((wall_height / 2) * w)),
                    (center + (6 * wall_length)).with_z(base - ((wall_height / 2) * w)),
                    wall_size,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    (center + (6 * wall_length)).with_z(base - ((wall_height / 2) * w)),
                    Vec2::new(center.x + (7 * wall_length), center.y)
                        .with_z(base + (wall_height / 2) - ((wall_height / 2) * w)),
                    wall_size,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x + (7 * wall_length), center.y)
                        .with_z(base - ((wall_height / 2) * w)),
                    Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                        .with_z(base + (wall_height / 2) - ((wall_height / 2) * w)),
                    wall_size,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                        .with_z(base - ((wall_height / 2) * w)),
                    Vec2::new(center.x, center.y - (7 * wall_length))
                        .with_z(base + (wall_height / 2) - ((wall_height / 2) * w)),
                    wall_size,
                )
                .fill(wood_light.clone());
        }
        for w in 0..2 {
            // upper wall
            painter
                .line(
                    Vec2::new(center.x, center.y - (7 * wall_length))
                        .with_z(base + (wall_height * w)),
                    (center - (6 * wall_length)).with_z(base + (wall_height * w)),
                    wall_size + (2 * w) as f32,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    (center - (6 * wall_length)).with_z(base + (wall_height * w)),
                    Vec2::new(center.x - (7 * wall_length), center.y)
                        .with_z(base + (wall_height * w)),
                    wall_size + (2 * w) as f32,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x - (7 * wall_length), center.y)
                        .with_z(base + (wall_height * w)),
                    Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                        .with_z(base + (wall_height * w)),
                    wall_size + (2 * w) as f32,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                        .with_z(base + (wall_height * w)),
                    Vec2::new(center.x, center.y + (7 * wall_length))
                        .with_z(base + (wall_height * w)),
                    wall_size + (2 * w) as f32,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x, center.y + (7 * wall_length))
                        .with_z(base + (wall_height * w)),
                    (center + (6 * wall_length)).with_z(base + (wall_height * w)),
                    wall_size + (2 * w) as f32,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    (center + (6 * wall_length)).with_z(base + (wall_height * w)),
                    Vec2::new(center.x + (7 * wall_length), center.y)
                        .with_z(base + (wall_height * w)),
                    wall_size + (2 * w) as f32,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x + (7 * wall_length), center.y)
                        .with_z(base + (wall_height * w)),
                    Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                        .with_z(base + (wall_height * w)),
                    wall_size + (2 * w) as f32,
                )
                .fill(wood_light.clone());
            painter
                .line(
                    Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                        .with_z(base + (wall_height * w)),
                    Vec2::new(center.x, center.y - (7 * wall_length))
                        .with_z(base + (wall_height * w)),
                    wall_size + (2 * w) as f32,
                )
                .fill(wood_light.clone());
        }
        // cut out for walk
        painter
            .line(
                Vec2::new(center.x, center.y - (7 * wall_length)).with_z(base + 3 + wall_height),
                (center - (6 * wall_length)).with_z(base + 3 + wall_height),
                wall_size + 3_f32,
            )
            .clear();
        painter
            .line(
                (center - (6 * wall_length)).with_z(base + 3 + wall_height),
                Vec2::new(center.x - (7 * wall_length), center.y).with_z(base + 3 + wall_height),
                wall_size + 3_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x - (7 * wall_length), center.y).with_z(base + 3 + wall_height),
                Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                    .with_z(base + 3 + wall_height),
                wall_size + 3_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                    .with_z(base + 3 + wall_height),
                Vec2::new(center.x, center.y + (7 * wall_length)).with_z(base + 3 + wall_height),
                wall_size + 3_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x, center.y + (7 * wall_length)).with_z(base + 3 + wall_height),
                (center + (6 * wall_length)).with_z(base + 3 + wall_height),
                wall_size + 3_f32,
            )
            .clear();
        painter
            .line(
                (center + (6 * wall_length)).with_z(base + 3 + wall_height),
                Vec2::new(center.x + (7 * wall_length), center.y).with_z(base + 3 + wall_height),
                wall_size + 3_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x + (7 * wall_length), center.y).with_z(base + 3 + wall_height),
                Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                    .with_z(base + 3 + wall_height),
                wall_size + 3_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                    .with_z(base + 3 + wall_height),
                Vec2::new(center.x, center.y - (7 * wall_length)).with_z(base + 3 + wall_height),
                wall_size + 3_f32,
            )
            .clear();

        // wall tunnel

        painter
            .line(
                Vec2::new(center.x, center.y - (7 * wall_length)).with_z(base - (2 * length)),
                (center - (6 * wall_length)).with_z(base - (2 * length)),
                wall_size * 3_f32,
            )
            .fill(clay.clone());
        painter
            .line(
                (center - (6 * wall_length)).with_z(base - (2 * length)),
                Vec2::new(center.x - (7 * wall_length), center.y).with_z(base - (2 * length)),
                wall_size * 3_f32,
            )
            .fill(clay.clone());
        painter
            .line(
                Vec2::new(center.x - (7 * wall_length), center.y).with_z(base - (2 * length)),
                Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                    .with_z(base - (2 * length)),
                wall_size * 3_f32,
            )
            .fill(clay.clone());
        painter
            .line(
                Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                    .with_z(base - (2 * length)),
                Vec2::new(center.x, center.y + (7 * wall_length)).with_z(base - (2 * length)),
                wall_size * 3_f32,
            )
            .fill(clay.clone());
        painter
            .line(
                Vec2::new(center.x, center.y + (7 * wall_length)).with_z(base - (2 * length)),
                (center + (6 * wall_length)).with_z(base - (2 * length)),
                wall_size * 3_f32,
            )
            .fill(clay.clone());
        painter
            .line(
                (center + (6 * wall_length)).with_z(base - (2 * length)),
                Vec2::new(center.x + (7 * wall_length), center.y).with_z(base - (2 * length)),
                wall_size * 3_f32,
            )
            .fill(clay.clone());
        painter
            .line(
                Vec2::new(center.x + (7 * wall_length), center.y).with_z(base - (2 * length)),
                Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                    .with_z(base - (2 * length)),
                wall_size * 3_f32,
            )
            .fill(clay.clone());
        painter
            .line(
                Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                    .with_z(base - (2 * length)),
                Vec2::new(center.x, center.y - (7 * wall_length)).with_z(base - (2 * length)),
                wall_size * 3_f32,
            )
            .fill(clay.clone());

        painter
            .line(
                Vec2::new(center.x, center.y - (7 * wall_length)).with_z(base - (2 * length)),
                (center - (6 * wall_length)).with_z(base - (2 * length)),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                (center - (6 * wall_length)).with_z(base - (2 * length)),
                Vec2::new(center.x - (7 * wall_length), center.y).with_z(base - (2 * length)),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x - (7 * wall_length), center.y).with_z(base - (2 * length)),
                Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                    .with_z(base - (2 * length)),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                    .with_z(base - (2 * length)),
                Vec2::new(center.x, center.y + (7 * wall_length)).with_z(base - (2 * length)),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x, center.y + (7 * wall_length)).with_z(base - (2 * length)),
                (center + (6 * wall_length)).with_z(base - (2 * length)),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                (center + (6 * wall_length)).with_z(base - (2 * length)),
                Vec2::new(center.x + (7 * wall_length), center.y).with_z(base - (2 * length)),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x + (7 * wall_length), center.y).with_z(base - (2 * length)),
                Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                    .with_z(base - (2 * length)),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                    .with_z(base - (2 * length)),
                Vec2::new(center.x, center.y - (7 * wall_length)).with_z(base - (2 * length)),
                wall_size * 2_f32,
            )
            .clear();
        // passages to rooms
        painter
            .line(
                Vec2::new(center.x, center.y - (7 * wall_length)).with_z(base - (2 * length)),
                Vec2::new(center.x, center.y - (6 * wall_length) + (length / 2))
                    .with_z(base - (2 * length) + 2),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                (center - (6 * wall_length)).with_z(base - (2 * length)),
                (center - (4 * wall_length) + 1).with_z(base - (2 * length) + 2),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x - (7 * wall_length), center.y).with_z(base - (2 * length)),
                Vec2::new(center.x - (6 * wall_length) + (length / 2), center.y)
                    .with_z(base - (2 * length) + 2),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x - (6 * wall_length), center.y + (6 * wall_length))
                    .with_z(base - (2 * length)),
                Vec2::new(
                    center.x - (4 * wall_length) + 1,
                    center.y + (4 * wall_length) - 1,
                )
                .with_z(base - (2 * length) + 2),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x, center.y + (7 * wall_length)).with_z(base - (2 * length)),
                Vec2::new(center.x, center.y + (6 * wall_length) - (length / 2))
                    .with_z(base - (2 * length) + 2),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                (center + (6 * wall_length)).with_z(base - (2 * length)),
                (center + (4 * wall_length) - 1).with_z(base - (2 * length) + 2),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x + (7 * wall_length), center.y).with_z(base - (2 * length)),
                Vec2::new(center.x + (6 * wall_length) - (length / 2), center.y)
                    .with_z(base - (2 * length) + 2),
                wall_size * 2_f32,
            )
            .clear();
        painter
            .line(
                Vec2::new(center.x + (6 * wall_length), center.y - (6 * wall_length))
                    .with_z(base - (2 * length)),
                Vec2::new(
                    center.x + (4 * wall_length) - 1,
                    center.y - (4 * wall_length) + 1,
                )
                .with_z(base - (2 * length) + 2),
                wall_size * 2_f32,
            )
            .clear();

        // watchtowers
        for dir in DIAGONALS {
            let tower_center = center + dir * ((6 * wall_length) - 3);
            // small stairs
            let small_stairs_pos = tower_center;
            let small_stair_radius = 4.0;
            let small_stairs_clear = painter.cylinder(Aabb {
                min: (small_stairs_pos + 1 - small_stair_radius as i32)
                    .with_z(base - (2 * length) - 2),
                max: (small_stairs_pos - 1 + small_stair_radius as i32)
                    .with_z(base + (length / 2) + 2),
            });
            small_stairs_clear.clear();
            painter
                .prim(Primitive::sampling(
                    small_stairs_clear,
                    dungeon::spiral_staircase(
                        small_stairs_pos.with_z(base - (2 * length) - 2),
                        small_stair_radius,
                        0.5,
                        (length - 2) as f32,
                    ),
                ))
                .fill(wood_dark.clone());
            // top cone
            let top_var = RandomField::new(0).get(tower_center.with_z(base)) % 4;
            if let 0..=2 = top_var {
                painter
                    .cone(Aabb {
                        min: (tower_center - length - 2)
                            .with_z(base + length + (length / 2) + (3 * top_var) as i32),
                        max: (tower_center + length + 2)
                            .with_z(base + (2 * length) + (length / 2) + (3 * top_var) as i32),
                    })
                    .fill(reed.clone());
                painter
                    .cone(Aabb {
                        min: (tower_center - length - 2)
                            .with_z(base + length + (length / 2) - 1 + (3 * top_var) as i32),
                        max: (tower_center + length + 2)
                            .with_z(base + (2 * length) + (length / 2) - 1 + (3 * top_var) as i32),
                    })
                    .clear();
                painter
                    .cylinder(Aabb {
                        min: (tower_center - 1).with_z(base + (length / 2) - 1),
                        max: (tower_center + 1)
                            .with_z(base + (2 * length) + (length / 2) - 2 + (3 * top_var) as i32),
                    })
                    .fill(wood_dark.clone());
            }
            let lantern_pos = tower_center - 3;
            painter.sprite(lantern_pos.with_z(base + (length / 2)), SpriteKind::Lantern);
            // tunnel lanterns
            let tunnel_lantern_pos = tower_center - 3;
            painter.sprite(
                tunnel_lantern_pos.with_z(base - (2 * length) - 2),
                SpriteKind::Lantern,
            );
        }
        // watchtowers with entries
        for dir in CARDINALS {
            let tower_center = center + dir * ((7 * wall_length) - 2);
            // entry foundation
            painter
                .cone(Aabb {
                    min: (tower_center - (2 * length)).with_z(base - length),
                    max: (tower_center + (2 * length)).with_z(base),
                })
                .fill(clay.clone());

            painter
                .cylinder(Aabb {
                    min: (tower_center - (length / 2) - 2).with_z(base - 1),
                    max: (tower_center + (length / 2) + 2).with_z(base + 2),
                })
                .clear();
            painter
                .cylinder(Aabb {
                    min: (tower_center - (length / 2)).with_z(base + 2),
                    max: (tower_center + (length / 2)).with_z(base + 3),
                })
                .clear();
            // top cone
            let top_var = RandomField::new(0).get(tower_center.with_z(base)) % 4;
            if let 0..=2 = top_var {
                painter
                    .cone(Aabb {
                        min: (tower_center - length - 2)
                            .with_z(base + length + (length / 2) + (3 * top_var) as i32),
                        max: (tower_center + length + 2)
                            .with_z(base + (2 * length) + (length / 2) + (3 * top_var) as i32),
                    })
                    .fill(reed.clone());
                painter
                    .cone(Aabb {
                        min: (tower_center - length - 2)
                            .with_z(base + length + (length / 2) - 1 + (3 * top_var) as i32),
                        max: (tower_center + length + 2)
                            .with_z(base + (2 * length) + (length / 2) - 1 + (3 * top_var) as i32),
                    })
                    .clear();
                painter
                    .cylinder(Aabb {
                        min: (tower_center - 1).with_z(base + (length / 2) - 1),
                        max: (tower_center + 1)
                            .with_z(base + (2 * length) + (length / 2) - 2 + (3 * top_var) as i32),
                    })
                    .fill(wood_dark.clone());
            }
            let lantern_pos = center + dir * (7 * wall_length);
            painter.sprite(lantern_pos.with_z(base + (length / 2)), SpriteKind::Lantern);
            // tunnel lanterns
            let tunnel_lantern_pos = tower_center;
            painter.sprite(
                tunnel_lantern_pos.with_z(base - (2 * length) - 2),
                SpriteKind::Lantern,
            );
        }
    }
}
