From 485b057d60105debf243046fcb969367de8bb895 Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Thu, 16 Sep 2021 21:26:35 -0700 Subject: [PATCH] Prettier town houses --- world/src/site2/gen.rs | 60 +- world/src/site2/plot/dungeon.rs | 38 +- world/src/site2/plot/house.rs | 1993 +++++++++++++++++++++++++++++-- 3 files changed, 1969 insertions(+), 122 deletions(-) diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index 1717dadb99..33e9e0f44c 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -24,6 +24,11 @@ pub enum Primitive { aabb: Aabb, inset: i32, }, + Ramp { + aabb: Aabb, + inset: i32, + dir: u8, + }, Gable { aabb: Aabb, inset: i32, @@ -34,6 +39,8 @@ pub enum Primitive { Cone(Aabb), Sphere(Aabb), Plane(Aabr, Vec3, Vec2), + /// A line segment from start to finish point + Segment(LineSegment3), /// A sampling function is always a subset of another primitive to avoid /// needing infinite bounds Sampling(Id, Box) -> bool>), @@ -75,6 +82,34 @@ impl Fill { Primitive::Empty => false, Primitive::Aabb(aabb) => aabb_contains(*aabb, pos), + Primitive::Ramp { aabb, inset, dir } => { + let inset = (*inset).max(aabb.size().reduce_min()); + let inner = match dir { + 0 => Aabr { + min: Vec2::new(aabb.min.x - 1 + inset, aabb.min.y), + max: Vec2::new(aabb.max.x, aabb.max.y), + }, + 1 => Aabr { + min: Vec2::new(aabb.min.x, aabb.min.y), + max: Vec2::new(aabb.max.x - inset, aabb.max.y), + }, + 2 => Aabr { + min: Vec2::new(aabb.min.x, aabb.min.y - 1 + inset), + max: Vec2::new(aabb.max.x, aabb.max.y), + }, + _ => Aabr { + min: Vec2::new(aabb.min.x, aabb.min.y), + max: Vec2::new(aabb.max.x, aabb.max.y - inset), + }, + }; + aabb_contains(*aabb, pos) + && (inner.projected_point(pos.xy()) - pos.xy()) + .map(|e| e.abs()) + .reduce_max() as f32 + / (inset as f32) + < 1.0 + - ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32 + }, Primitive::Pyramid { aabb, inset } => { let inset = (*inset).max(aabb.size().reduce_min()); let inner = Aabr { @@ -102,14 +137,14 @@ impl Fill { max: Vec2::new(aabb.max.x, aabb.max.y - inset), } }; - aabb_contains(*aabb, pos) && - (inner.projected_point(pos.xy()) - pos.xy()) - .map(|e| e.abs()) - .reduce_max() as f32 - / (inset as f32) - < 1.0 + aabb_contains(*aabb, pos) + && (inner.projected_point(pos.xy()) - pos.xy()) + .map(|e| e.abs()) + .reduce_max() as f32 + / (inset as f32) + < 1.0 - ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32 - } + }, Primitive::Cylinder(aabb) => { (aabb.min.z..aabb.max.z).contains(&pos.z) && (pos @@ -145,6 +180,12 @@ impl Fill { .as_() .dot(*gradient) as i32) }, + Primitive::Segment(segment) => { + (segment.start.x..segment.end.x).contains(&pos.x) + && (segment.start.y..segment.end.y).contains(&pos.y) + && (segment.start.z..segment.end.z).contains(&pos.z) + && segment.as_().distance_to_point(pos.map(|e| e as f32)) < 0.75 + }, Primitive::Sampling(a, f) => self.contains_at(tree, *a, pos) && f(pos), Primitive::Prefab(p) => !matches!(p.get(pos), Err(_) | Ok(StructureBlock::None)), Primitive::And(a, b) => { @@ -228,6 +269,7 @@ impl Fill { Primitive::Aabb(aabb) => *aabb, Primitive::Pyramid { aabb, .. } => *aabb, Primitive::Gable { aabb, .. } => *aabb, + Primitive::Ramp { aabb, .. } => *aabb, Primitive::Cylinder(aabb) => *aabb, Primitive::Cone(aabb) => *aabb, Primitive::Sphere(aabb) => *aabb, @@ -248,6 +290,10 @@ impl Fill { }; aabb.made_valid() }, + Primitive::Segment(segment) => Aabb { + min: segment.start, + max: segment.end, + }, Primitive::Sampling(a, _) => self.get_bounds_inner(tree, *a)?, Primitive::Prefab(p) => p.get_bounds(), Primitive::And(a, b) => or_zip_with( diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 36a2a4f421..0ac03f4b92 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -192,6 +192,7 @@ enum RoomKind { Fight, Boss, Miniboss, + #[allow(dead_code)] LavaPlatforming, } @@ -202,7 +203,7 @@ pub struct Room { area: Rect, height: i32, pillars: Option, // Pillars with the given separation - pits: Option, // Pits filled with lava + pits: Option, // Pits filled with lava difficulty: u32, } @@ -514,17 +515,17 @@ impl Floor { pits: None, difficulty: self.difficulty, }), - // Lava platforming room - 1 => self.create_room(Room { - seed: ctx.rng.gen(), - loot_density: 0.0, - kind: RoomKind::LavaPlatforming, - area, - height: ctx.rng.gen_range(10..15), - pillars: None, - pits: Some(1), - difficulty: self.difficulty, - }), + //// Lava platforming room + //1 => self.create_room(Room { + // seed: ctx.rng.gen(), + // loot_density: 0.0, + // kind: RoomKind::LavaPlatforming, + // area, + // height: ctx.rng.gen_range(10..15), + // pillars: None, + // pits: Some(1), + // difficulty: self.difficulty, + //}), // Fight room with enemies in it _ => self.create_room(Room { seed: ctx.rng.gen(), @@ -1259,6 +1260,7 @@ impl Floor { })); chests = Some((chest_sprite, chest_sprite_fill)); + // If a room has pits, place them if room.pits.is_some() { // Make an air pit let tile_pit = prim(Primitive::Aabb(aabr_with_z( @@ -1281,20 +1283,18 @@ impl Floor { if room .pits .map(|pit_space| { - tile_pos - .map(|e| e.rem_euclid(pit_space) == 0) - .reduce_and() + tile_pos.map(|e| e.rem_euclid(pit_space) == 0).reduce_and() }) - .unwrap_or(false) { + .unwrap_or(false) + { let platform = prim(Primitive::Aabb(Aabb { min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) .with_z(floor_z - 7), - max: (tile_center + Vec2::broadcast(pillar_thickness)) - .with_z(floor_z), + max: (tile_center + Vec2::broadcast(pillar_thickness)).with_z(floor_z), })); fill(platform, Fill::Block(stone)); } - + // If a room has pillars, the current tile aligns with the pillar spacing, and // we're not too close to a wall (i.e. the adjacent tiles are rooms and not // hallways/solid), place a pillar diff --git a/world/src/site2/plot/house.rs b/world/src/site2/plot/house.rs index a04fca267f..e83e3247ab 100644 --- a/world/src/site2/plot/house.rs +++ b/world/src/site2/plot/house.rs @@ -4,13 +4,23 @@ use common::terrain::{Block, BlockKind, SpriteKind}; use rand::prelude::*; use vek::*; +/// Represents house data generated by the `generate()` method pub struct House { - _door_tile: Vec2, + /// Tile position of the door tile + door_tile: Vec2, + /// Axis aligned bounding region of tiles tile_aabr: Aabr, + /// Axis aligned bounding region for the house bounds: Aabr, + /// Approximate altitude of the door tile alt: i32, + /// Number of floors levels: u32, + /// Difference between a level and the floor above + overhang: i32, + /// Color of the roof roof_color: Rgb, + front: u8, } impl House { @@ -21,15 +31,52 @@ impl House { door_tile: Vec2, tile_aabr: Aabr, ) -> Self { + let levels = rng.gen_range(1..2 + (tile_aabr.max - tile_aabr.min).product() / 6) as u32; + let door_tile_pos = site.tile_center_wpos(door_tile); + let bounds = Aabr { + min: site.tile_wpos(tile_aabr.min), + max: site.tile_wpos(tile_aabr.max), + }; + let max_x_door_offset = (door_tile_pos.x - bounds.max.x).abs(); + let max_y_door_offset = (door_tile_pos.y - bounds.max.y).abs(); + let min_x_door_offset = (door_tile_pos.x - bounds.min.x).abs(); + let min_y_door_offset = (door_tile_pos.y - bounds.min.y).abs(); + let front = if max_y_door_offset < max_x_door_offset + && max_y_door_offset < min_x_door_offset + && max_y_door_offset < min_y_door_offset + { + 0 + } else if max_x_door_offset < max_y_door_offset + && max_x_door_offset < min_x_door_offset + && max_x_door_offset < min_y_door_offset + { + 1 + } else if min_y_door_offset < max_y_door_offset + && min_y_door_offset < min_x_door_offset + && min_y_door_offset < max_x_door_offset + { + 2 + } else { + 3 + }; + Self { - _door_tile: door_tile, + door_tile: door_tile_pos, tile_aabr, - bounds: Aabr { - min: site.tile_wpos(tile_aabr.min), - max: site.tile_wpos(tile_aabr.max), - }, + bounds, alt: land.get_alt_approx(site.tile_center_wpos(door_tile)) as i32 + 2, - levels: rng.gen_range(1..2 + (tile_aabr.max - tile_aabr.min).product() / 6) as u32, + levels, + overhang: if levels > 3 { + // Overhangs of 3 at this building height are ill-advised. + // Failure to comply with Veloren building code will result + // in a fine and a revoked building permit. + *[-5, 1, 2].choose(rng).unwrap_or(&-5) + } else if levels > 1 { + *[-5, 1, 2, 3].choose(rng).unwrap_or(&2) + } else { + // Single story buildings require no overhangs + 0 + }, roof_color: { let colors = [ Rgb::new(21, 43, 48), @@ -40,8 +87,9 @@ impl House { Rgb::new(40, 5, 11), Rgb::new(55, 45, 11), ]; - *colors.choose(rng).unwrap() + *colors.choose(rng).unwrap_or(&Rgb::new(21, 43, 48)) }, + front, } } } @@ -57,70 +105,753 @@ impl Structure for House { let storey = 5; let roof = storey * self.levels as i32; let foundations = 12; + let alt = self.alt + 1; - // Walls - let inner = prim(Primitive::Aabb(Aabb { - min: (self.bounds.min + 1).with_z(self.alt), - max: self.bounds.max.with_z(self.alt + roof), - })); - let outer = prim(Primitive::Aabb(Aabb { - min: self.bounds.min.with_z(self.alt - foundations), - max: (self.bounds.max + 1).with_z(self.alt + roof), - })); + // Roof + let roof_lip = 1; + let roof_height = (self.bounds.min - self.bounds.max) + .map(|e| e.abs()) + .reduce_min() + / 2 + + roof_lip + + 1; + + let (roof_primitive, roof_empty) = match self.front { + 0 => { + ( + prim(Primitive::Gable { + aabb: Aabb { + min: (self.bounds.min - roof_lip).with_z(alt + roof), + max: (Vec2::new( + self.bounds.max.x + 1 + roof_lip, + self.bounds.max.y + + 1 + + roof_lip + + (self.levels as i32 - 1) * self.overhang, + )) + .with_z(alt + roof + roof_height), + }, + inset: roof_height, + dir: true, + }), + prim(Primitive::Gable { + aabb: Aabb { + min: (Vec2::new(self.bounds.min.x, self.bounds.min.y - 1)) + .with_z(alt + roof), /* self.bounds.min - roof_lip).with_z(alt + + * roof), */ + max: (Vec2::new( + self.bounds.max.x + 1, + self.bounds.max.y + + 1 + + (self.levels as i32 - 1) * self.overhang + + 1, + )) + .with_z(alt + roof + roof_height - 1), + }, + inset: roof_height - 1, + dir: true, + }), + ) + }, + 1 => { + ( + prim(Primitive::Gable { + aabb: Aabb { + min: (self.bounds.min - roof_lip).with_z(alt + roof), + max: Vec2::new( + self.bounds.max.x + + 1 + + roof_lip + + (self.levels as i32 - 1) * self.overhang, + self.bounds.max.y + 1 + roof_lip, + ) + .with_z(alt + roof + roof_height), + }, + inset: roof_height, + dir: false, + }), + prim(Primitive::Gable { + aabb: Aabb { + min: (Vec2::new(self.bounds.min.x - 1, self.bounds.min.y)) + .with_z(alt + roof), /* self.bounds.min - roof_lip).with_z(alt + + * roof), */ + max: Vec2::new( + self.bounds.max.x + + 1 + + (self.levels as i32 - 1) * self.overhang + + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + roof + roof_height - 1), + }, + inset: roof_height - 1, + dir: false, + }), + ) + }, + 2 => ( + prim(Primitive::Gable { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x - roof_lip, + self.bounds.min.y - roof_lip - (self.levels as i32 - 1) * self.overhang, + ) + .with_z(alt + roof), + max: Vec2::new( + self.bounds.max.x + 1 + roof_lip, + self.bounds.max.y + roof_lip + 1, + ) + .with_z(alt + roof + roof_height), + }, + inset: roof_height, + dir: true, + }), + prim(Primitive::Gable { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x, + self.bounds.min.y - 1 - (self.levels as i32 - 1) * self.overhang - 1, + ) + .with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + roof_lip + 1) + .with_z(alt + roof + roof_height - 1), + }, + inset: roof_height - 1, + dir: true, + }), + ), + _ => ( + prim(Primitive::Gable { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x - roof_lip - (self.levels as i32 - 1) * self.overhang, + self.bounds.min.y - roof_lip, + ) + .with_z(alt + roof), + max: Vec2::new( + self.bounds.max.x + 1 + roof_lip, + self.bounds.max.y + 1 + roof_lip, + ) + .with_z(alt + roof + roof_height), + }, + inset: roof_height, + dir: false, + }), + prim(Primitive::Gable { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x + - roof_lip + - 1 + - (self.levels as i32 - 1) * self.overhang, + self.bounds.min.y, + ) + .with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1 + roof_lip, self.bounds.max.y + 1) + .with_z(alt + roof + roof_height - 1), + }, + inset: roof_height - 1, + dir: false, + }), + ), + }; + + let (roof_front_wall, roof_rear_wall) = match self.front { + 0 => ( + prim(Primitive::Aabb(Aabb { + min: (Vec2::new( + self.bounds.min.x, + self.bounds.max.y + (self.levels as i32 - 1) * self.overhang, + )) + .with_z(alt + roof), + max: (Vec2::new( + self.bounds.max.x + 1, + self.bounds.max.y + (self.levels as i32 - 1) * self.overhang + 1, + )) + .with_z(alt + roof + roof_height), + })), + prim(Primitive::Aabb(Aabb { + min: (Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof)), + max: (Vec2::new(self.bounds.max.x + 1, self.bounds.min.y + 1) + .with_z(alt + roof + roof_height)), + })), + ), + 1 => ( + prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.max.x + (self.levels as i32 - 1) * self.overhang, + self.bounds.min.y, + ) + .with_z(alt + roof), + max: Vec2::new( + self.bounds.max.x + (self.levels as i32 - 1) * self.overhang + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + roof + roof_height), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof), + max: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y + 1) + .with_z(alt + roof + roof_height), + })), + ), + 2 => ( + prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x, + self.bounds.min.y - (self.levels as i32 - 1) * self.overhang, + ) + .with_z(alt + roof), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.min.y - (self.levels as i32 - 1) * self.overhang + 1, + ) + .with_z(alt + roof + roof_height), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.max.y).with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + roof + roof_height), + })), + ), + _ => ( + prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - (self.levels as i32 - 1) * self.overhang, + self.bounds.min.y, + ) + .with_z(alt + roof), + max: Vec2::new( + self.bounds.min.x - (self.levels as i32 - 1) * self.overhang + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + roof + roof_height), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.max.x, self.bounds.min.y).with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + roof + roof_height), + })), + ), + }; + let roof_front = prim(Primitive::And(roof_empty, roof_front_wall)); + let roof_rear = prim(Primitive::And(roof_empty, roof_rear_wall)); fill( - outer, - Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24), + roof_primitive, + Fill::Block(Block::new(BlockKind::Wood, self.roof_color)), ); - fill(inner, Fill::Block(Block::empty())); - let walls = prim(Primitive::Xor(outer, inner)); - - // wall pillars - let mut pillars_y = prim(Primitive::Empty); - for x in self.tile_aabr.min.x..self.tile_aabr.max.x + 2 { - let pillar = prim(Primitive::Aabb(Aabb { - min: site - .tile_wpos(Vec2::new(x, self.tile_aabr.min.y)) - .with_z(self.alt), - max: (site.tile_wpos(Vec2::new(x, self.tile_aabr.max.y + 1)) + Vec2::unit_x()) - .with_z(self.alt + roof), - })); - pillars_y = prim(Primitive::Or(pillars_y, pillar)); - } - let mut pillars_x = prim(Primitive::Empty); - for y in self.tile_aabr.min.y..self.tile_aabr.max.y + 2 { - let pillar = prim(Primitive::Aabb(Aabb { - min: site - .tile_wpos(Vec2::new(self.tile_aabr.min.x, y)) - .with_z(self.alt), - max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x + 1, y)) + Vec2::unit_y()) - .with_z(self.alt + roof), - })); - pillars_x = prim(Primitive::Or(pillars_x, pillar)); - } - let pillars = prim(Primitive::And(pillars_x, pillars_y)); + fill(roof_empty, Fill::Block(Block::empty())); + let roof_walls = prim(Primitive::Or(roof_front, roof_rear)); fill( - pillars, + roof_walls, + Fill::Brick(BlockKind::Wood, Rgb::new(200, 180, 150), 24), + ); + let max_overhang = (self.levels as i32 - 1) * self.overhang; + let (roof_beam, roof_beam_right, roof_beam_left) = match self.front { + 0 => ( + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1 + max_overhang) + .with_z(alt + roof + 1), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof), + max: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y + 1 + max_overhang) + .with_z(alt + roof + 1), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.max.x, self.bounds.min.y).with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1 + max_overhang) + .with_z(alt + roof + 1), + })), + ), + 1 => ( + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + max_overhang + 1, self.bounds.max.y + 1) + .with_z(alt + roof + 1), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.min.y).with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + max_overhang + 1, self.bounds.min.y + 1) + .with_z(alt + roof + 1), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.max.y).with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + max_overhang + 1, self.bounds.max.y + 1) + .with_z(alt + roof + 1), + })), + ), + 2 => ( + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.min.y - max_overhang) + .with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + roof + 1), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.max.x, self.bounds.min.y - max_overhang) + .with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + roof + 1), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.min.y - max_overhang) + .with_z(alt + roof), + max: Vec2::new(self.bounds.min.x + 1, self.bounds.max.y + 1) + .with_z(alt + roof + 1), + })), + ), + _ => ( + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x - max_overhang - 1, self.bounds.min.y) + .with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + roof + 1), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x - max_overhang, self.bounds.min.y) + .with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.min.y + 1) + .with_z(alt + roof + 1), + })), + prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x - max_overhang, self.bounds.max.y) + .with_z(alt + roof), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + roof + 1), + })), + ), + }; + let quarter_x = self.bounds.min.x + (self.bounds.max.x - self.bounds.min.x) / 4; + let quarter_y = self.bounds.min.y + (self.bounds.max.y - self.bounds.min.y) / 4; + let half_x = self.bounds.min.x + (self.bounds.max.x - self.bounds.min.x) / 2; + let half_y = self.bounds.min.y + (self.bounds.max.y - self.bounds.min.y) / 2; + let three_quarter_x = self.bounds.min.x + 3 * (self.bounds.max.x - self.bounds.min.x) / 4; + let three_quarter_y = self.bounds.min.y + 3 * (self.bounds.max.y - self.bounds.min.y) / 4; + let top_rafter = if self.front % 2 == 0 { + prim(Primitive::Aabb(Aabb { + min: (Vec2::new(half_x, self.bounds.min.y - 2 - max_overhang.abs()) + .with_z(alt + roof)), + max: (Vec2::new(half_x + 1, self.bounds.max.y + 2 + max_overhang.abs())) + .with_z(alt + roof + roof_height), + })) + } else { + prim(Primitive::Aabb(Aabb { + min: (Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), half_y) + .with_z(alt + roof)), + max: (Vec2::new(self.bounds.max.x + 1 + max_overhang.abs(), half_y + 1)) + .with_z(alt + roof + roof_height), + })) + }; + let left_rafter = if self.front % 2 == 0 { + prim(Primitive::Plane( + Aabr { + min: Vec2::new(half_x, self.bounds.min.y - 1 - max_overhang.abs()), + max: Vec2::new( + three_quarter_x + 1, + self.bounds.max.y + 1 + max_overhang.abs(), + ), + }, + Vec2::new(half_x, self.bounds.min.y - 1 - max_overhang.abs()).with_z(alt + roof), + Vec2::new(1.0, 0.0), + )) + } else { + prim(Primitive::Plane( + Aabr { + min: Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), half_y), + max: Vec2::new( + self.bounds.max.x + 1 + max_overhang.abs(), + three_quarter_y + 1, + ), + }, + Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), half_y).with_z(alt + roof), + Vec2::new(0.0, 1.0), + )) + }; + let right_rafter = if self.front % 2 == 0 { + prim(Primitive::Plane( + Aabr { + min: Vec2::new(quarter_x, self.bounds.min.y - 1 - max_overhang.abs()), + max: Vec2::new(half_x + 1, self.bounds.max.y + 1 + max_overhang.abs()), + }, + Vec2::new(half_x, self.bounds.min.y - 1 - max_overhang.abs()).with_z(alt + roof), + Vec2::new(1.0, 0.0), + )) + } else { + prim(Primitive::Plane( + Aabr { + min: Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), quarter_y), + max: Vec2::new(self.bounds.max.x + 1 + max_overhang.abs(), half_y + 1), + }, + Vec2::new(self.bounds.min.x - 1 - max_overhang.abs(), half_y).with_z(alt + roof), + Vec2::new(0.0, 1.0), + )) + }; + let rafters1 = prim(Primitive::Or(left_rafter, right_rafter)); + let rafters2 = prim(Primitive::Or(rafters1, top_rafter)); + + fill( + prim(Primitive::And(roof_beam, roof_walls)), + Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))), + ); + fill( + prim(Primitive::Or(roof_beam_left, roof_beam_right)), + Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))), + ); + fill( + prim(Primitive::And(rafters2, roof_walls)), Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))), ); + // Walls // For each storey... - for i in 0..self.levels + 1 { + for i in 1..self.levels + 1 { + let previous_height = (storey * (i as i32 - 1)).max(0); let height = storey * i as i32; let window_height = storey - 3; + let storey_increase = (i as i32 - 1) * self.overhang; + + // Walls + let inner_level = if self.overhang < -4 && i > 1 { + match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: (self.bounds.min + 1).with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x, self.bounds.max.y + storey_increase + 1) + .with_z(alt + height), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: (self.bounds.min + 1).with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x + storey_increase + 1, self.bounds.max.y) + .with_z(alt + height), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x + 1, + self.bounds.min.y - storey_increase + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(alt + height), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase + 1, + self.bounds.min.y + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(alt + height), + })), + } + } else { + match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: (self.bounds.min + 1).with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x, self.bounds.max.y + storey_increase) + .with_z(alt + height), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: (self.bounds.min + 1).with_z(alt + previous_height), + max: (Vec2::new(self.bounds.max.x + storey_increase, self.bounds.max.y)) + .with_z(alt + height), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x + 1, + self.bounds.min.y - storey_increase + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(alt + height), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase + 1, + self.bounds.min.y + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x, self.bounds.max.y).with_z(alt + height), + })), + } + }; + let outer_level = match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: self.bounds.min.with_z(alt + previous_height), + max: (Vec2::new( + self.bounds.max.x + 1, + self.bounds.max.y + storey_increase + 1, + )) + .with_z(alt + height), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: self.bounds.min.with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + height), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.min.y - storey_increase) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + height), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x - storey_increase, self.bounds.min.y) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + height), + })), + }; + + // Ground floor has rock walls + let wall_block_fill = if i < 2 { + Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24) + } else { + Fill::Brick(BlockKind::Wood, Rgb::new(200, 180, 150), 24) + }; + fill(outer_level, wall_block_fill); + fill(inner_level, Fill::Block(Block::empty())); + + let walls = prim(Primitive::Xor(outer_level, inner_level)); + + // Wall Pillars + // Only upper non-stone floors have wooden beams in the walls + if i > 1 { + let mut pillars_y = prim(Primitive::Empty); + let mut overhang_supports = prim(Primitive::Empty); + + for x in self.tile_aabr.min.x - 2..self.tile_aabr.max.x + 2 { + if self.overhang >= 2 && self.front % 2 == 0 { + let temp = match self.front { + 0 => site.tile_wpos(Vec2::new(x, self.tile_aabr.max.y)), + //2 => site.tile_wpos(Vec2::new(x, self.tile_aabr.min.y)), + _ => Vec2::zero(), + }; + // NOTE: Orientation 2 doesn't work for some reason. I believe it is + // something to do with AABBs with min and max not smaller in the right + // order. The same thing is true for orientation 3. + let support = match self.front { + 0 => prim(Primitive::Segment(LineSegment3 { + start: Vec2::new( + temp.x, + self.bounds.max.y + storey_increase - self.overhang + 1, + ) + .with_z(alt + previous_height - 3), + end: Vec2::new( + temp.x + 1, + self.bounds.max.y + storey_increase - self.overhang + 3, + ) + .with_z(alt + previous_height), + })), + //2 => { + // prim(Primitive::Segment(LineSegment3 { + // start: Vec2::new(temp.x, self.bounds.min.y - storey_increase - + // 6).with_z(alt + previous_height + 30), + // end: Vec2::new(temp.x + 1, self.bounds.min.y - storey_increase + // - 3).with_z(alt + previous_height - 3), })) + //}, + _ => prim(Primitive::Empty), + }; + if temp.x <= self.bounds.max.x && temp.x >= self.bounds.min.x { + overhang_supports = prim(Primitive::Or(overhang_supports, support)); + } + } + let pillar = prim(Primitive::Aabb(Aabb { + min: site + .tile_wpos(Vec2::new(x, self.tile_aabr.min.y - 4)) + .with_z(alt + previous_height), + max: (site.tile_wpos(Vec2::new(x, self.tile_aabr.max.y + 4)) + + Vec2::unit_x()) + .with_z(alt + height), + })); + pillars_y = prim(Primitive::Or(pillars_y, pillar)); + } + let mut pillars_x = prim(Primitive::Empty); + for y in self.tile_aabr.min.y - 2..self.tile_aabr.max.y + 2 { + if self.overhang >= 2 && self.front % 2 != 0 { + let temp = match self.front { + 0 => Vec2::zero(), + 1 => site.tile_wpos(Vec2::new(self.tile_aabr.max.x, y)), + 2 => Vec2::zero(), + _ => site.tile_wpos(Vec2::new(self.tile_aabr.min.x, y)), + }; + let support = match self.front { + 0 => prim(Primitive::Empty), + 1 => prim(Primitive::Segment(LineSegment3 { + start: Vec2::new( + self.bounds.max.x + storey_increase - self.overhang + 1, + temp.y, + ) + .with_z(alt + previous_height - 3), + end: Vec2::new( + self.bounds.max.x + storey_increase - self.overhang + 3, + temp.y + 1, + ) + .with_z(alt + previous_height), + })), + 2 => prim(Primitive::Empty), + _ => prim(Primitive::Segment(LineSegment3 { + start: Vec2::new( + self.bounds.min.x - storey_increase + self.overhang - 1, + temp.y, + ) + .with_z(alt + previous_height - 3), + end: Vec2::new( + self.bounds.min.x - storey_increase + self.overhang - 3, + temp.y + 1, + ) + .with_z(alt + previous_height), + })), + }; + if temp.y <= self.bounds.max.y && temp.y >= self.bounds.min.y { + overhang_supports = prim(Primitive::Or(overhang_supports, support)); + } + } + let pillar = prim(Primitive::Aabb(Aabb { + min: site + .tile_wpos(Vec2::new(self.tile_aabr.min.x - 4, y)) + .with_z(alt + previous_height), + max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x + 4, y)) + + Vec2::unit_y()) + .with_z(alt + height), + })); + pillars_x = prim(Primitive::Or(pillars_x, pillar)); + } + let front_wall = if self.overhang < -4 && i > 1 { + prim(Primitive::Empty) + } else { + match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - 1, + self.bounds.max.y + storey_increase, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.max.y + storey_increase + 1, + ) + .with_z(alt + height), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.max.x + storey_increase, + self.bounds.min.y - 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + height), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - 1, + self.bounds.min.y - storey_increase, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.min.y - storey_increase + 1, + ) + .with_z(alt + height), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase, + self.bounds.min.y - 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.min.x - storey_increase + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + height), + })), + } + }; + let pillars1 = if self.front % 2 == 0 { + prim(Primitive::And(pillars_y, front_wall)) + } else { + prim(Primitive::And(pillars_x, front_wall)) + }; + let pillars2 = prim(Primitive::And(pillars_x, pillars_y)); + let pillars3 = prim(Primitive::Or(pillars1, pillars2)); + let pillars4 = match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x - 1, self.bounds.min.y - 1) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.max.y + storey_increase + 1, + ) + .with_z(alt + previous_height + 1), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x - 1, self.bounds.min.y - 1) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + previous_height + 1), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - 1, + self.bounds.min.y - storey_increase - 1, + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + previous_height + 1), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase - 1, + self.bounds.min.y - 1, + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x + 1, self.bounds.max.y + 1) + .with_z(alt + previous_height + 1), + })), + }; + let pillars = prim(Primitive::Or(pillars3, pillars4)); + fill( + prim(Primitive::And(walls, pillars)), + Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))), + ); + + fill( + overhang_supports, + Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))), + ); + } // Windows x axis { let mut windows = prim(Primitive::Empty); - for y in self.tile_aabr.min.y..self.tile_aabr.max.y { - let window = prim(Primitive::Aabb(Aabb { - min: (site.tile_wpos(Vec2::new(self.tile_aabr.min.x, y)) - + Vec2::unit_y() * 2) - .with_z(self.alt + height + 2), - max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x, y + 1)) - + Vec2::new(1, -1)) - .with_z(self.alt + height + 2 + window_height), - })); - windows = prim(Primitive::Or(windows, window)); + for y in self.tile_aabr.min.y - 2..self.tile_aabr.max.y + 2 { + let min = (site.tile_wpos(Vec2::new(self.tile_aabr.min.x - 4, y)) + + Vec2::unit_y() * 2) + .with_z(alt + previous_height + 2); + let max = (site.tile_wpos(Vec2::new(self.tile_aabr.max.x + 4, y + 1)) + + Vec2::new(1, -1)) + .with_z(alt + previous_height + 2 + window_height); + let window = prim(Primitive::Aabb(Aabb { min, max })); + let add_windows = match self.front { + 0 => { + max.y < self.bounds.max.y + storey_increase && min.y > self.bounds.min.y + }, + 1 => max.y < self.bounds.max.y && min.y > self.bounds.min.y, + 2 => { + max.y < self.bounds.max.y && min.y > self.bounds.min.y - storey_increase + }, + _ => max.y < self.bounds.max.y && min.y > self.bounds.min.y, + }; + if add_windows { + windows = prim(Primitive::Or(windows, window)); + } } fill( prim(Primitive::And(walls, windows)), @@ -130,16 +861,27 @@ impl Structure for House { // Windows y axis { let mut windows = prim(Primitive::Empty); - for x in self.tile_aabr.min.x..self.tile_aabr.max.x { - let window = prim(Primitive::Aabb(Aabb { - min: (site.tile_wpos(Vec2::new(x, self.tile_aabr.min.y)) - + Vec2::unit_x() * 2) - .with_z(self.alt + height + 2), - max: (site.tile_wpos(Vec2::new(x + 1, self.tile_aabr.max.y)) - + Vec2::new(-1, 1)) - .with_z(self.alt + height + 2 + window_height), - })); - windows = prim(Primitive::Or(windows, window)); + for x in self.tile_aabr.min.x - 2..self.tile_aabr.max.x + 2 { + let min = (site.tile_wpos(Vec2::new(x, self.tile_aabr.min.y - 4)) + + Vec2::unit_x() * 2) + .with_z(alt + previous_height + 2); + let max = (site.tile_wpos(Vec2::new(x + 1, self.tile_aabr.max.y + 4)) + + Vec2::new(-1, 1)) + .with_z(alt + previous_height + 2 + window_height); + let window = prim(Primitive::Aabb(Aabb { min, max })); + let add_windows = match self.front { + 0 => max.x < self.bounds.max.x && min.x > self.bounds.min.x, + 1 => { + max.x < self.bounds.max.x + storey_increase && min.x > self.bounds.min.x + }, + 2 => max.x < self.bounds.max.x && min.x > self.bounds.min.x, + _ => { + max.x < self.bounds.max.x && min.x > self.bounds.min.x - storey_increase + }, + }; + if add_windows { + windows = prim(Primitive::Or(windows, window)); + }; } fill( prim(Primitive::And(walls, windows)), @@ -147,37 +889,862 @@ impl Structure for House { ); } + // Shed roof on negative overhangs + if self.overhang < -4 && i > 1 { + let shed = match self.front { + 0 => prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x - 1, + self.bounds.max.y + storey_increase + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.max.y + storey_increase + self.overhang.abs() + 1, + ) + .with_z(alt + height), + }, + inset: storey, + dir: 3, + }), + 1 => prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new( + self.bounds.max.x + storey_increase + 1, + self.bounds.min.y - 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs() + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + height), + }, + inset: storey, + dir: 1, + }), + 2 => prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x - 1, + self.bounds.min.y - storey_increase - self.overhang.abs(), + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 2, + self.bounds.min.y - storey_increase, + ) + .with_z(alt + height), + }, + inset: storey, + dir: 2, + }), + _ => prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs(), + self.bounds.min.y - 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.min.x - storey_increase + 1, + self.bounds.max.y + 2, + ) + .with_z(alt + height), + }, + inset: storey, + dir: 0, + }), + }; + let shed_empty = match self.front { + 0 => prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x - 1, + self.bounds.max.y + storey_increase + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.max.y + storey_increase + self.overhang.abs(), + ) + .with_z(alt + height - 1), + }, + inset: storey - 1, + dir: 3, + }), + 1 => prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new( + self.bounds.max.x + storey_increase + 1, + self.bounds.min.y - 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs(), + self.bounds.max.y + 1, + ) + .with_z(alt + height - 1), + }, + inset: storey - 1, + dir: 1, + }), + 2 => prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x - 1, + self.bounds.min.y - storey_increase - self.overhang.abs() + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 2, + self.bounds.min.y - storey_increase + 1, + ) + .with_z(alt + height), + }, + inset: storey - 1, + dir: 2, + }), + _ => prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs() + 1, + self.bounds.min.y - 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.min.x - storey_increase + 1, + self.bounds.max.y + 2, + ) + .with_z(alt + height), + }, + inset: storey - 1, + dir: 0, + }), + }; + fill( + shed, + Fill::Block(Block::new(BlockKind::Wood, self.roof_color)), + ); + fill(shed_empty, Fill::Block(Block::empty())); + let shed_left_wall = match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.max.y + storey_increase + 1) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.min.x + 1, + self.bounds.max.y + storey_increase + self.overhang.abs(), + ) + .with_z(alt + height - 1), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.max.x + storey_increase + 1, self.bounds.min.y) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs(), + self.bounds.min.y + 1, + ) + .with_z(alt + height - 1), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.max.x, + self.bounds.min.y - storey_increase - self.overhang.abs() + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.min.y - storey_increase + 1, + ) + .with_z(alt + height), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs() + 1, + self.bounds.max.y, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.min.x - storey_increase + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + height), + })), + }; + let shed_right_wall = match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.max.x, self.bounds.max.y + storey_increase + 1) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.max.y + storey_increase + self.overhang.abs(), + ) + .with_z(alt + height - 1), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.max.x + storey_increase + 1, self.bounds.max.y) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs(), + self.bounds.max.y + 1, + ) + .with_z(alt + height - 1), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x, + self.bounds.min.y - storey_increase - self.overhang.abs() + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.min.x + 1, + self.bounds.min.y - storey_increase + 1, + ) + .with_z(alt + height), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs() + 1, + self.bounds.min.y, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.min.x - storey_increase + 1, + self.bounds.min.y + 1, + ) + .with_z(alt + height), + })), + }; + let shed_wall_beams = match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x, self.bounds.max.y + storey_increase + 1) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.max.y + storey_increase + self.overhang.abs(), + ) + .with_z(alt + previous_height + 1), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.max.x + storey_increase + 1, self.bounds.min.y) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs(), + self.bounds.max.y + 1, + ) + .with_z(alt + previous_height + 1), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x, + self.bounds.min.y - storey_increase - self.overhang.abs() + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + 1, + self.bounds.min.y - storey_increase + 1, + ) + .with_z(alt + previous_height + 1), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs() + 1, + self.bounds.min.y, + ) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.min.x - storey_increase + 1, + self.bounds.max.y + 1, + ) + .with_z(alt + previous_height + 1), + })), + }; + let shed_walls = prim(Primitive::Or(shed_left_wall, shed_right_wall)); + fill( + prim(Primitive::And(shed_walls, shed_empty)), + Fill::Brick(BlockKind::Wood, Rgb::new(200, 180, 150), 24), + ); + fill( + prim(Primitive::And(shed_wall_beams, shed_walls)), + Fill::Block(Block::new(BlockKind::Wood, Rgb::new(55, 25, 8))), + ); + + // Dormers + let range = if self.front % 2 == 0 { + self.tile_aabr.min.x - 3..self.tile_aabr.max.x + 3 + } else { + self.tile_aabr.min.y - 3..self.tile_aabr.max.y + 3 + }; + for n in range { + let temp = match self.front { + 0 => site.tile_wpos(Vec2::new(n, self.tile_aabr.max.y)) - 4, + 1 => site.tile_wpos(Vec2::new(self.tile_aabr.max.x, n)) - 4, + 2 => site.tile_wpos(Vec2::new(n, self.tile_aabr.min.y)) - 4, + _ => site.tile_wpos(Vec2::new(self.tile_aabr.min.x, n)) - 4, + }; + let dormer_box = match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(temp.x - 1, self.bounds.max.y + storey_increase + 1) + .with_z(alt + previous_height), + max: Vec2::new( + temp.x + 4, + self.bounds.max.y + storey_increase + self.overhang.abs(), + ) + .with_z(alt + height - 1), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.max.x + storey_increase + 1, temp.y - 1) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs(), + temp.y + 4, + ) + .with_z(alt + height - 1), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + temp.x - 1, + self.bounds.min.y - storey_increase - self.overhang.abs() + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new(temp.x + 4, self.bounds.min.y - storey_increase - 1) + .with_z(alt + height - 1), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs() + 1, + temp.y - 1, + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.min.x - storey_increase - 1, temp.y + 4) + .with_z(alt + height - 1), + })), + }; + let dormer_roof = match self.front { + 0 => prim(Primitive::Gable { + aabb: Aabb { + min: Vec2::new(temp.x - 1, self.bounds.max.y + storey_increase + 1) + .with_z(alt + height - 2), + max: Vec2::new( + temp.x + 4, + self.bounds.max.y + storey_increase + self.overhang.abs(), + ) + .with_z(alt + height + 1), + }, + inset: 3, + dir: true, + }), + 1 => prim(Primitive::Gable { + aabb: Aabb { + min: Vec2::new(self.bounds.max.x + storey_increase + 1, temp.y - 1) + .with_z(alt + height - 2), + max: Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs(), + temp.y + 4, + ) + .with_z(alt + height + 1), + }, + inset: 3, + dir: false, + }), + 2 => prim(Primitive::Gable { + aabb: Aabb { + min: Vec2::new( + temp.x - 1, + self.bounds.min.y - storey_increase - self.overhang.abs() + 1, + ) + .with_z(alt + height - 2), + max: Vec2::new(temp.x + 4, self.bounds.min.y - storey_increase) + .with_z(alt + height + 1), + }, + inset: 3, + dir: true, + }), + _ => prim(Primitive::Gable { + aabb: Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs() + 1, + temp.y - 1, + ) + .with_z(alt + height - 2), + max: Vec2::new(self.bounds.min.x - storey_increase, temp.y + 4) + .with_z(alt + height + 1), + }, + inset: 3, + dir: false, + }), + }; + let window_min = match self.front { + 0 => Vec2::new( + temp.x, + self.bounds.max.y + storey_increase + self.overhang.abs() - 1, + ) + .with_z(alt + previous_height + 2), + 1 => Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs() - 1, + temp.y, + ) + .with_z(alt + previous_height + 2), + 2 => Vec2::new( + temp.x, + self.bounds.min.y - storey_increase - self.overhang.abs() + 1, + ) + .with_z(alt + previous_height + 2), + _ => Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs() + 1, + temp.y, + ) + .with_z(alt + previous_height + 2), + }; + let window_max = match self.front { + 0 => Vec2::new( + temp.x + 3, + self.bounds.max.y + storey_increase + self.overhang.abs(), + ) + .with_z(alt + previous_height + 2 + window_height), + 1 => Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs(), + temp.y + 3, + ) + .with_z(alt + previous_height + 2 + window_height), + 2 => Vec2::new( + temp.x + 3, + self.bounds.min.y - storey_increase - self.overhang.abs() + 2, + ) + .with_z(alt + previous_height + 2 + window_height), + _ => Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs() + 2, + temp.y + 3, + ) + .with_z(alt + previous_height + 2 + window_height), + }; + let window = prim(Primitive::Aabb(Aabb { + min: window_min, + max: window_max, + })); + let window_cavity = match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(temp.x, self.bounds.max.y + storey_increase) + .with_z(alt + previous_height), + max: Vec2::new( + temp.x + 3, + self.bounds.max.y + storey_increase + self.overhang.abs() - 1, + ) + .with_z(alt + previous_height + 2 + window_height), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.max.x + storey_increase, temp.y) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs() - 1, + temp.y + 3, + ) + .with_z(alt + previous_height + 2 + window_height), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + temp.x, + self.bounds.min.y - storey_increase - self.overhang.abs() + 2, + ) + .with_z(alt + previous_height), + max: Vec2::new(temp.x + 3, self.bounds.min.y - storey_increase + 1) + .with_z(alt + previous_height + 2 + window_height), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x - storey_increase - self.overhang.abs() + 2, + temp.y, + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.min.x - storey_increase + 1, temp.y + 3) + .with_z(alt + previous_height + 2 + window_height), + })), + }; + let valid_dormer = if self.front % 2 == 0 { + window_min.x > self.bounds.min.x && window_max.x < self.bounds.max.x + } else { + window_min.y > self.bounds.min.y && window_max.y < self.bounds.max.y + }; + let window_ori = if self.front % 2 == 0 { 0 } else { 2 }; + if valid_dormer { + fill( + prim(Primitive::Diff(dormer_box, shed)), + Fill::Brick(BlockKind::Wood, Rgb::new(200, 180, 150), 24), + ); + fill( + prim(Primitive::Diff(dormer_roof, shed)), + Fill::Block(Block::new(BlockKind::Wood, self.roof_color)), + ); + fill(window_cavity, Fill::Block(Block::empty())); + fill( + window, + Fill::Block( + Block::air(SpriteKind::Window1) + .with_ori(window_ori) + .unwrap(), + ), + ); + } + } + } + // Floor - fill( - prim(Primitive::Aabb(Aabb { - min: (self.bounds.min + 1).with_z(self.alt + height), - max: self.bounds.max.with_z(self.alt + height + 1), - })), - Fill::Block(Block::new(BlockKind::Rock, Rgb::new(89, 44, 14))), - ); + // No extra floor needed for the ground floor + if i > 1 { + let floor = if self.overhang < -1 && i > 1 { + match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: (self.bounds.min + 1).with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x, + self.bounds.max.y + storey_increase + self.overhang.abs(), + ) + .with_z(alt + previous_height + 1), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(self.bounds.min.x + 1, self.bounds.min.y + 1) + .with_z(alt + previous_height), + max: Vec2::new( + self.bounds.max.x + storey_increase + self.overhang.abs(), + self.bounds.max.y, + ) + .with_z(alt + previous_height + 1), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x + 1, + self.bounds.min.y + 1 - storey_increase - self.overhang.abs(), + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x, self.bounds.max.y) + .with_z(alt + previous_height + 1), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x + 1 - storey_increase - self.overhang.abs(), + self.bounds.min.y + 1, + ) + .with_z(alt + previous_height), + max: Vec2::new(self.bounds.max.x, self.bounds.max.y) + .with_z(alt + previous_height + 1), + })), + } + } else { + match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: (self.bounds.min + 1).with_z(alt + previous_height), + max: (Vec2::new( + self.bounds.max.x, + self.bounds.max.y + storey_increase, + )) + .with_z(alt + previous_height + 1), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: (self.bounds.min + 1).with_z(alt + previous_height), + max: (Vec2::new( + self.bounds.max.x + storey_increase, + self.bounds.max.y, + )) + .with_z(alt + previous_height + 1), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x + 1, + self.bounds.min.y + 1 - storey_increase, + ) + .with_z(alt + previous_height), + max: (Vec2::new(self.bounds.max.x, self.bounds.max.y)) + .with_z(alt + previous_height + 1), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new( + self.bounds.min.x + 1 - storey_increase, + self.bounds.min.y + 1, + ) + .with_z(alt + previous_height), + max: (Vec2::new(self.bounds.max.x, self.bounds.max.y)) + .with_z(alt + previous_height + 1), + })), + } + }; + fill( + floor, + Fill::Block(Block::new(BlockKind::Rock, Rgb::new(89, 44, 14))), + ); + } + + // Stairs + if i > 1 { + let stair_width = 3; + let previous_floor_height = (storey * (i as i32 - 2)).max(0); + let stair_origin = match self.front { + 0 => self.bounds.min + 1, + 1 => self.bounds.min + 1, + 2 => Vec2::new(self.bounds.max.x - 12, self.bounds.max.y - stair_width * 2), + _ => Vec2::new(self.bounds.max.x - 12, self.bounds.min.y + 1), + }; + let staircase = if i < 2 { + prim(Primitive::Empty) + } else if i % 2 == 0 { + let ramp = /*match self.front */{ + //0 => { + prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new(stair_origin.x + 3, stair_origin.y).with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + 10, stair_origin.y + stair_width).with_z(alt + previous_height + 1), + }, + inset: storey + 1, + dir: 0, + }) + /*}, + 1 => { + prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new(stair_origin.x, stair_origin.y + 3).with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + stair_width, stair_origin.y + 10).with_z(alt + previous_height + 1), + }, + inset: storey + 1, + dir: 0, + }) + }, + 2 => { + prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new(stair_origin.x + 3, stair_origin.y).with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + 10, stair_origin.y + stair_width).with_z(alt + previous_height + 1), + }, + inset: storey + 1, + dir: 0, + }) + }, + _ => { + prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new(stair_origin.x, stair_origin.y + 3).with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + stair_width, stair_origin.y + 10).with_z(alt + previous_height + 1), + }, + inset: storey + 1, + dir: 0, + }) + }*/ + }; + let support = { + //match self.front { + //0 => { + prim(Primitive::Aabb(Aabb { + min: Vec2::new(stair_origin.x + 10, stair_origin.y) + .with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + 12, stair_origin.y + stair_width) + .with_z(alt + previous_height + 1), + })) + //}, + //1 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x, stair_origin.y + // + 10).with_z(alt + previous_floor_height), + // max: Vec2::new(stair_origin.x + stair_width, + // stair_origin.y + 12).with_z(alt + previous_height + + // 1), })) + //}, + //2 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x + 10, + // stair_origin.y).with_z(alt + previous_floor_height), + // max: Vec2::new(stair_origin.x + 12, + // stair_origin.y + stair_width).with_z(alt + + // previous_height + 1), })) + //}, + //_ => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x, stair_origin.y + // + 10).with_z(alt + previous_floor_height), + // max: Vec2::new(stair_origin.x + stair_width, + // stair_origin.y + 12).with_z(alt + previous_height + + // 1), })) + //}, + }; + prim(Primitive::Or(ramp, support)) + } else { + let ramp = /*match self.front */{ + //0 => { + prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new(stair_origin.x + 1, stair_origin.y + stair_width).with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + 8, stair_origin.y + 2 * stair_width).with_z(alt + previous_height + 1), + }, + inset: storey + 1, + dir: 1, + }) + /*}, + 1 => { + prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new(stair_origin.x + stair_width, stair_origin.y + 1).with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + 2 * stair_width, stair_origin.y + 8).with_z(alt + previous_height + 1), + }, + inset: storey + 1, + dir: 1, + }) + }, + 2 => { + prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new(stair_origin.x + 1, stair_origin.y + stair_width).with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + 8, stair_origin.y + 2 * stair_width).with_z(alt + previous_height + 1), + }, + inset: storey + 1, + dir: 1, + }) + }, + _ => { + prim(Primitive::Ramp { + aabb: Aabb { + min: Vec2::new(stair_origin.x + stair_width, stair_origin.y + 1).with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + 2 * stair_width, stair_origin.y + 8).with_z(alt + previous_height + 1), + }, + inset: storey + 1, + dir: 1, + }) + }, + */ + }; + let support = { + //match self.front { + //0 => { + prim(Primitive::Aabb(Aabb { + min: Vec2::new(stair_origin.x, stair_origin.y + stair_width) + .with_z(alt + previous_floor_height), + max: Vec2::new(stair_origin.x + 2, stair_origin.y + 2 * stair_width) + .with_z(alt + previous_height + 1), + })) + //}, + //1 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x + stair_width, + // stair_origin.y).with_z(alt + previous_floor_height), + // max: Vec2::new(stair_origin.x + 2 * + // stair_width, stair_origin.y + 2).with_z(alt + + // previous_height + 1), })) + //}, + //2 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x, stair_origin.y + // + stair_width).with_z(alt + previous_floor_height), + // max: Vec2::new(stair_origin.x + 2, + // stair_origin.y + 2 * stair_width).with_z(alt + + // previous_height + 1), })) + //}, + //_ => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x + stair_width, + // stair_origin.y).with_z(alt + previous_floor_height), + // max: Vec2::new(stair_origin.x + 2 * + // stair_width, stair_origin.y + 2).with_z(alt + + // previous_height + 1), })) + //}, + }; + prim(Primitive::Or(ramp, support)) + }; + let stairwell = if i < 2 { + prim(Primitive::Empty) + } else if i % 2 == 0 { + prim(Primitive::Aabb(Aabb { + min: Vec2::new(stair_origin.x, stair_origin.y) + .with_z(alt + previous_floor_height + 1), + max: Vec2::new(stair_origin.x + 9, stair_origin.y + stair_width) + .with_z(alt + previous_height + 1), + })) + //match self.front { + // 0 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x, + // stair_origin.y).with_z(alt + previous_floor_height + 1), + // max: Vec2::new(stair_origin.x + 9, + // stair_origin.y + stair_width).with_z(alt + + // previous_height + 1), })) + // }, + // 1 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x, + // stair_origin.y).with_z(alt + previous_floor_height + 1), + // max: Vec2::new(stair_origin.x + + // stair_width, stair_origin.y + 9).with_z(alt + + // previous_height + 1), })) + // }, + // 2 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x, + // stair_origin.y).with_z(alt + previous_floor_height + 1), + // max: Vec2::new(stair_origin.x + 9, + // stair_origin.y + stair_width).with_z(alt + + // previous_height + 1), })) + // }, + // _ => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x, + // stair_origin.y).with_z(alt + previous_floor_height + 1), + // max: Vec2::new(stair_origin.x + + // stair_width, stair_origin.y + 9).with_z(alt + + // previous_height + 1), })) + // }, + //} + } else { + prim(Primitive::Aabb(Aabb { + min: Vec2::new(stair_origin.x + 2, stair_origin.y + stair_width) + .with_z(alt + previous_floor_height + 1), + max: Vec2::new(stair_origin.x + 11, stair_origin.y + 2 * stair_width) + .with_z(alt + previous_height + 1), + })) + //match self.front { + // 0 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x + 2, + // stair_origin.y + stair_width).with_z(alt + + // previous_floor_height + 1), + // max: Vec2::new(stair_origin.x + 11, + // stair_origin.y + 2 * stair_width).with_z(alt + + // previous_height + 1), })) + // }, + // 1 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x + + // stair_width, stair_origin.y + 2).with_z(alt + + // previous_floor_height + 1), + // max: Vec2::new(stair_origin.x + 2 * + // stair_width, stair_origin.y + 11).with_z(alt + + // previous_height + 1), })) + // }, + // 2 => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x + 2, + // stair_origin.y + stair_width).with_z(alt + + // previous_floor_height + 1), + // max: Vec2::new(stair_origin.x + 11, + // stair_origin.y + 2 * stair_width).with_z(alt + + // previous_height + 1), })) + // }, + // _ => { + // prim(Primitive::Aabb(Aabb { + // min: Vec2::new(stair_origin.x + + // stair_width, stair_origin.y + 2).with_z(alt + + // previous_floor_height + 1), + // max: Vec2::new(stair_origin.x + 2 * + // stair_width, stair_origin.y + 11).with_z(alt + + // previous_height + 1), })) + // }, + //} + }; + + fill(stairwell, Fill::Block(Block::empty())); + fill( + staircase, + Fill::Block(Block::new(BlockKind::Rock, Rgb::new(89, 44, 14))), + ); + } } - let roof_lip = 2; - let roof_height = (self.bounds.min - self.bounds.max) - .map(|e| e.abs()) - .reduce_min() - / 2 - + roof_lip - + 1; - - // Roof - fill( - prim(Primitive::Pyramid { - aabb: Aabb { - min: (self.bounds.min - roof_lip).with_z(self.alt + roof), - max: (self.bounds.max + 1 + roof_lip).with_z(self.alt + roof + roof_height), - }, - inset: roof_height, - }), - Fill::Block(Block::new(BlockKind::Wood, self.roof_color)), - ); - - // Foundations + // Foundation fill( prim(Primitive::Aabb(Aabb { min: (self.bounds.min - 1).with_z(self.alt - foundations), @@ -185,5 +1752,239 @@ impl Structure for House { })), Fill::Block(Block::new(BlockKind::Rock, Rgb::new(31, 33, 32))), ); + + // Fireplace and chimney + let fireplace_origin = /*if self.levels > 1 { + match self.front { + 0 => { + Vec2::new(self.bounds.min.x + 12, self.bounds.min.y + 1) + }, + 1 => { + Vec2::new(self.bounds.min.x + 1, self.bounds.max.y - 12) + }, + 2 => { + Vec2::new(self.bounds.max.x - 12, self.bounds.max.y - 1) + }, + _ => { + Vec2::new(self.bounds.max.x - 1, self.bounds.min.y + 12) + }, + } + } else */{ + match self.front { + 0 => { + Vec2::new(half_x, self.bounds.min.y + 1) + }, + 1 => { + Vec2::new(self.bounds.min.x + 1, half_y) + }, + 2 => { + Vec2::new(half_x - 4, self.bounds.max.y - 3) + }, + _ => { + Vec2::new(self.bounds.max.x - 3, half_y) + }, + } + }; + let chimney = match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(fireplace_origin.x, fireplace_origin.y).with_z(alt), + max: Vec2::new(fireplace_origin.x + 4, fireplace_origin.y + 3) + .with_z(alt + roof + roof_height + 2), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(fireplace_origin.x, fireplace_origin.y).with_z(alt), + max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 4) + .with_z(alt + roof + roof_height + 2), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(fireplace_origin.x, fireplace_origin.y).with_z(alt), + max: Vec2::new(fireplace_origin.x + 4, fireplace_origin.y + 3) + .with_z(alt + roof + roof_height + 2), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new(fireplace_origin.x, fireplace_origin.y).with_z(alt), + max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 4) + .with_z(alt + roof + roof_height + 2), + })), + }; + + let chimney_cavity = match self.front { + 0 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2) + .with_z(alt + roof + roof_height + 2), + })), + 1 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3) + .with_z(alt + roof + roof_height + 2), + })), + 2 => prim(Primitive::Aabb(Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2) + .with_z(alt + roof + roof_height + 2), + })), + _ => prim(Primitive::Aabb(Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3) + .with_z(alt + roof + roof_height + 2), + })), + }; + let fire_embers = match self.front { + 0 => Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2).with_z(alt + 1), + }, + 1 => Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3).with_z(alt + 1), + }, + 2 => Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2).with_z(alt + 1), + }, + _ => Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3).with_z(alt + 1), + }, + }; + let fireplace_cavity = match self.front { + 0 => Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 3).with_z(alt + 2), + }, + 1 => Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 3).with_z(alt + 2), + }, + 2 => Aabb { + min: Vec2::new(fireplace_origin.x + 1, fireplace_origin.y).with_z(alt), + max: Vec2::new(fireplace_origin.x + 3, fireplace_origin.y + 2).with_z(alt + 2), + }, + _ => Aabb { + min: Vec2::new(fireplace_origin.x, fireplace_origin.y + 1).with_z(alt), + max: Vec2::new(fireplace_origin.x + 2, fireplace_origin.y + 3).with_z(alt + 2), + }, + }; + + fill( + chimney, + Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24), + ); + fill(chimney_cavity, Fill::Block(Block::empty())); + fill( + prim(Primitive::Aabb(fireplace_cavity)), + Fill::Block(Block::empty()), + ); + fill( + prim(Primitive::Aabb(fire_embers)), + Fill::Block(Block::air(SpriteKind::Ember)), + ); + + // Door + // Fill around the door with wall + let doorway1 = match self.front { + 0 => Aabb { + min: Vec2::new(self.door_tile.x - 1, self.bounds.max.y).with_z(alt), + max: Vec2::new(self.door_tile.x + 3, self.bounds.max.y + 1).with_z(alt + 4), + }, + 1 => Aabb { + min: Vec2::new(self.bounds.max.x, self.door_tile.y - 1).with_z(alt), + max: Vec2::new(self.bounds.max.x + 1, self.door_tile.y + 3).with_z(alt + 4), + }, + 2 => Aabb { + min: Vec2::new(self.door_tile.x - 1, self.bounds.min.y).with_z(alt), + max: Vec2::new(self.door_tile.x + 3, self.bounds.min.y + 1).with_z(alt + 4), + }, + _ => Aabb { + min: Vec2::new(self.bounds.min.x, self.door_tile.y - 1).with_z(alt), + max: Vec2::new(self.bounds.min.x + 1, self.door_tile.y + 3).with_z(alt + 4), + }, + }; + fill( + prim(Primitive::Aabb(doorway1)), + Fill::Brick(BlockKind::Rock, Rgb::new(80, 75, 85), 24), + ); + + // Carve out the doorway with air + let doorway2 = match self.front { + 0 => Aabb { + min: Vec2::new(self.door_tile.x, self.bounds.max.y).with_z(alt), + max: Vec2::new(self.door_tile.x + 2, self.bounds.max.y + 1).with_z(alt + 3), + }, + 1 => Aabb { + min: Vec2::new(self.bounds.max.x, self.door_tile.y).with_z(alt), + max: Vec2::new(self.bounds.max.x + 1, self.door_tile.y + 2).with_z(alt + 3), + }, + 2 => Aabb { + min: Vec2::new(self.door_tile.x, self.bounds.min.y).with_z(alt), + max: Vec2::new(self.door_tile.x + 2, self.bounds.min.y + 1).with_z(alt + 3), + }, + _ => Aabb { + min: Vec2::new(self.bounds.min.x, self.door_tile.y).with_z(alt), + max: Vec2::new(self.bounds.min.x + 1, self.door_tile.y + 2).with_z(alt + 3), + }, + }; + fill(prim(Primitive::Aabb(doorway2)), Fill::Block(Block::empty())); + + // Fill in the right and left side doors + let (door1, door1_ori, door2, door2_ori) = match self.front { + 0 => ( + Aabb { + min: Vec2::new(self.door_tile.x, self.bounds.max.y).with_z(alt), + max: Vec2::new(self.door_tile.x + 1, self.bounds.max.y + 1).with_z(alt + 1), + }, + 0, + Aabb { + min: Vec2::new(self.door_tile.x + 1, self.bounds.max.y).with_z(alt), + max: Vec2::new(self.door_tile.x + 2, self.bounds.max.y + 1).with_z(alt + 1), + }, + 4, + ), + 1 => ( + Aabb { + min: Vec2::new(self.bounds.max.x, self.door_tile.y).with_z(alt), + max: Vec2::new(self.bounds.max.x + 1, self.door_tile.y + 1).with_z(alt + 1), + }, + 2, + Aabb { + min: Vec2::new(self.bounds.max.x, self.door_tile.y + 1).with_z(alt), + max: Vec2::new(self.bounds.max.x + 1, self.door_tile.y + 2).with_z(alt + 1), + }, + 6, + ), + 2 => ( + Aabb { + min: Vec2::new(self.door_tile.x, self.bounds.min.y).with_z(alt), + max: Vec2::new(self.door_tile.x + 1, self.bounds.min.y + 1).with_z(alt + 1), + }, + 0, + Aabb { + min: Vec2::new(self.door_tile.x + 1, self.bounds.min.y).with_z(alt), + max: Vec2::new(self.door_tile.x + 2, self.bounds.min.y + 1).with_z(alt + 1), + }, + 4, + ), + _ => ( + Aabb { + min: Vec2::new(self.bounds.min.x, self.door_tile.y).with_z(alt), + max: Vec2::new(self.bounds.min.x + 1, self.door_tile.y + 1).with_z(alt + 1), + }, + 2, + Aabb { + min: Vec2::new(self.bounds.min.x, self.door_tile.y + 1).with_z(alt), + max: Vec2::new(self.bounds.min.x + 1, self.door_tile.y + 2).with_z(alt + 1), + }, + 6, + ), + }; + fill( + prim(Primitive::Aabb(door1)), + Fill::Block(Block::air(SpriteKind::Door).with_ori(door1_ori).unwrap()), + ); + fill( + prim(Primitive::Aabb(door2)), + Fill::Block(Block::air(SpriteKind::Door).with_ori(door2_ori).unwrap()), + ); } }