diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 4587ad2e4f..f2a36ebdeb 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -5,7 +5,7 @@ mod tile; use self::{ gen::{Fill, Primitive, Structure}, plot::{Plot, PlotKind}, - tile::{HazardKind, Ori, Tile, TileGrid, TileKind, TILE_SIZE}, + tile::{HazardKind, KeepKind, Ori, RoofKind, Tile, TileGrid, TileKind, TILE_SIZE}, }; use crate::{ site::SpawnRules, @@ -326,15 +326,15 @@ impl Site { }, // Guard tower 2 => { - if let Some((aabr, entrance_tile)) = attempt(10, || { + if let Some((_aabr, _)) = attempt(10, || { site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2)) }) { + /* let plot = site.create_plot(Plot { kind: PlotKind::Castle(plot::Castle::generate( land, &mut rng, &site, - entrance_tile, aabr, )), root_tile: aabr.center(), @@ -346,6 +346,7 @@ impl Site { kind: TileKind::Castle, plot: Some(plot), }); + */ } }, // Field @@ -381,40 +382,87 @@ impl Site { }, // Castle 4 if castles < 1 => { - if let Some((aabr, entrance_tile)) = attempt(10, || { + if let Some((aabr, _entrance_tile)) = attempt(10, || { site.find_roadside_aabr(&mut rng, 16 * 16..18 * 18, Extent2::new(16, 16)) }) { + let offset = rng.gen_range(5..(aabr.size().w.min(aabr.size().h) - 4)); + let gate_aabr = Aabr { + min: Vec2::new(aabr.min.x + offset - 1, aabr.min.y), + max: Vec2::new(aabr.min.x + offset + 2, aabr.min.y + 1), + }; let plot = site.create_plot(Plot { kind: PlotKind::Castle(plot::Castle::generate( - land, - &mut rng, - &site, - entrance_tile, - aabr, + land, &mut rng, &site, aabr, gate_aabr, )), root_tile: aabr.center(), tiles: aabr_tiles(aabr).collect(), seed: rng.gen(), }); - // Walls - site.blit_aabr(aabr, Tile { + let wall_north = Tile { kind: TileKind::Wall(Ori::North), plot: Some(plot), - }); + }; - let tower = Tile { - kind: TileKind::Tower, + let wall_east = Tile { + kind: TileKind::Wall(Ori::East), plot: Some(plot), }; + for x in 0..aabr.size().w { + site.tiles + .set(aabr.min + Vec2::new(x, 0), wall_east.clone()); + site.tiles.set( + aabr.min + Vec2::new(x, aabr.size().h - 1), + wall_east.clone(), + ); + } + for y in 0..aabr.size().h { + site.tiles + .set(aabr.min + Vec2::new(0, y), wall_north.clone()); + site.tiles.set( + aabr.min + Vec2::new(aabr.size().w - 1, y), + wall_north.clone(), + ); + } + + let gate = Tile { + kind: TileKind::Gate, + plot: Some(plot), + }; + let tower_parapet = Tile { + kind: TileKind::Tower(RoofKind::Parapet), + plot: Some(plot), + }; + let tower_pyramid = Tile { + kind: TileKind::Tower(RoofKind::Pyramid), + plot: Some(plot), + }; + + site.tiles.set( + Vec2::new(aabr.min.x + offset - 2, aabr.min.y), + tower_parapet.clone(), + ); site.tiles - .set(Vec2::new(aabr.min.x, aabr.min.y), tower.clone()); + .set(Vec2::new(aabr.min.x + offset - 1, aabr.min.y), gate.clone()); site.tiles - .set(Vec2::new(aabr.max.x - 1, aabr.min.y), tower.clone()); + .set(Vec2::new(aabr.min.x + offset, aabr.min.y), gate.clone()); site.tiles - .set(Vec2::new(aabr.min.x, aabr.max.y - 1), tower.clone()); + .set(Vec2::new(aabr.min.x + offset + 1, aabr.min.y), gate.clone()); + site.tiles.set( + Vec2::new(aabr.min.x + offset + 2, aabr.min.y), + tower_parapet.clone(), + ); + site.tiles - .set(Vec2::new(aabr.max.x - 1, aabr.max.y - 1), tower.clone()); + .set(Vec2::new(aabr.min.x, aabr.min.y), tower_parapet.clone()); + site.tiles + .set(Vec2::new(aabr.max.x - 1, aabr.min.y), tower_parapet.clone()); + site.tiles + .set(Vec2::new(aabr.min.x, aabr.max.y - 1), tower_parapet.clone()); + site.tiles.set( + Vec2::new(aabr.max.x - 1, aabr.max.y - 1), + tower_parapet.clone(), + ); // Courtyard site.blit_aabr( @@ -441,19 +489,19 @@ impl Site { ); site.tiles.set( Vec2::new(aabr.center().x + 2, aabr.center().y + 2), - tower.clone(), + tower_pyramid.clone(), ); site.tiles.set( Vec2::new(aabr.center().x + 2, aabr.center().y - 3), - tower.clone(), + tower_pyramid.clone(), ); site.tiles.set( Vec2::new(aabr.center().x - 3, aabr.center().y + 2), - tower.clone(), + tower_pyramid.clone(), ); site.tiles.set( Vec2::new(aabr.center().x - 3, aabr.center().y - 3), - tower.clone(), + tower_pyramid.clone(), ); site.blit_aabr( @@ -462,7 +510,7 @@ impl Site { max: aabr.center() + 2, }, Tile { - kind: TileKind::Keep(tile::KeepKind::Middle), + kind: TileKind::Keep(KeepKind::Middle), plot: Some(plot), }, ); diff --git a/world/src/site2/plot/castle.rs b/world/src/site2/plot/castle.rs index 6a185428c5..e4459bda6f 100644 --- a/world/src/site2/plot/castle.rs +++ b/world/src/site2/plot/castle.rs @@ -1,13 +1,15 @@ use super::*; -use crate::Land; +use crate::{util::SQUARE_4, Land}; use common::terrain::{Block, BlockKind}; +use num::Integer; use rand::prelude::*; use vek::*; pub struct Castle { - _entrance_tile: Vec2, tile_aabr: Aabr, _bounds: Aabr, + gate_aabr: Aabr, + gate_alt: i32, alt: i32, } @@ -16,22 +18,31 @@ impl Castle { land: &Land, _rng: &mut impl Rng, site: &Site, - entrance_tile: Vec2, tile_aabr: Aabr, + gate_aabr: Aabr, ) -> Self { + let alt = SQUARE_4 + .iter() + .map(|corner| tile_aabr.min + (tile_aabr.max - tile_aabr.min - 1) * corner) + .map(|pos| land.get_alt_approx(site.tile_center_wpos(pos)) as i32) + .sum::() + / 4; + Self { - _entrance_tile: entrance_tile, tile_aabr, _bounds: Aabr { min: site.tile_wpos(tile_aabr.min), max: site.tile_wpos(tile_aabr.max), }, - alt: land.get_alt_approx(site.tile_center_wpos(entrance_tile)) as i32, + gate_aabr, + gate_alt: land.get_alt_approx(site.tile_center_wpos(gate_aabr.center())) as i32, + alt, } } } impl Structure for Castle { + #[allow(clippy::identity_op)] fn render Id, G: FnMut(Id, Fill)>( &self, site: &Site, @@ -39,177 +50,232 @@ impl Structure for Castle { mut fill: G, ) { let wall_height = 24; - let _thickness = 12; let parapet_height = 2; - let parapet_width = 1; - let _downwards = 40; - - let tower_height = 12; - + let parapet_gap = 2; + let parapet_offset = 2; + let ts = TILE_SIZE as i32; + let tower_height = 16; let keep_levels = 3; let keep_level_height = 8; let _keep_height = wall_height + keep_levels * keep_level_height + 1; + let wall_rgb = Rgb::new(38, 46, 43); + // Flatten inside of the castle + fill( + prim(Primitive::Aabb(Aabb { + min: site.tile_wpos(self.tile_aabr.min).with_z(self.gate_alt), + max: site + .tile_wpos(self.tile_aabr.max) + .with_z(self.alt + tower_height), + })), + Fill::Block(Block::empty()), + ); + fill( + prim(Primitive::Aabb(Aabb { + min: site.tile_wpos(self.tile_aabr.min).with_z(self.gate_alt), + max: site.tile_wpos(self.tile_aabr.max).with_z(self.gate_alt + 1), + })), + Fill::Block(Block::new(BlockKind::Rock, Rgb::new(55, 45, 65))), + ); for x in 0..self.tile_aabr.size().w { for y in 0..self.tile_aabr.size().h { let tile_pos = self.tile_aabr.min + Vec2::new(x, y); - let _wpos_center = site.tile_center_wpos(tile_pos); let wpos = site.tile_wpos(tile_pos); - let ori = if x == 0 || x == self.tile_aabr.size().w - 1 { - Vec2::new(1, 0) - } else { - Vec2::new(0, 1) - }; - let ori_tower_x = if x == 0 { - Vec2::new(1, 0) - } else { - Vec2::new(0, 0) - }; - let ori_tower_y = if y == 0 { - Vec2::new(0, 1) - } else { - Vec2::new(0, 0) - }; - let ori_tower = ori_tower_x + ori_tower_y; match site.tiles.get(tile_pos).kind.clone() { - TileKind::Wall(_ori) => { + TileKind::Wall(ori) => { + let dir = ori.dir(); let wall = prim(Primitive::Aabb(Aabb { - min: wpos.with_z(self.alt), - max: (wpos + 6).with_z(self.alt + wall_height + parapet_height), + min: wpos.with_z(self.alt - 20), + max: (wpos + ts).with_z(self.alt + wall_height), })); - let cut_path = prim(Primitive::Aabb(Aabb { - min: (wpos + (parapet_width * ori) as Vec2) - .with_z(self.alt + wall_height), - max: (wpos - + (6 - parapet_width) * ori as Vec2 - + 6 * ori.yx() as Vec2) + // TODO Figure out logic to choose on on which site wall should be placed + // (inner, outer) + let parapet = prim(Primitive::Aabb(Aabb { + min: (wpos - dir.yx()).with_z(self.alt + wall_height), + max: (wpos + ts * dir).with_z(self.alt + wall_height + parapet_height), + })); + let parapet2 = prim(Primitive::Aabb(Aabb { + min: (wpos + ts * dir.yx()).with_z(self.alt + wall_height), + max: (wpos + (ts + 1) * dir.yx() + ts * dir) .with_z(self.alt + wall_height + parapet_height), })); - let cut_sides1 = prim(Primitive::Aabb(Aabb { - min: Vec3::new(wpos.x, wpos.y, self.alt + wall_height + 1), - max: Vec3::new( - wpos.x + 6 * ori.x + ori.y, - wpos.y + 6 * ori.y + ori.x, - self.alt + wall_height + parapet_height, - ), + let cut_sides = prim(Primitive::Aabb(Aabb { + min: (wpos + parapet_offset * dir - dir.yx()) + .with_z(self.alt + wall_height + parapet_height - 1), + max: (wpos + + (ts + 1) * dir.yx() + + (parapet_offset + parapet_gap) * dir) + .with_z(self.alt + wall_height + parapet_height), })); - let pillar_start = prim(Primitive::Aabb(Aabb { - min: Vec3::new(wpos.x, wpos.y - 1, self.alt), - max: Vec3::new(wpos.x + 1, wpos.y + 7, self.alt + wall_height), - })); - let pillar_end = prim(Primitive::Aabb(Aabb { - min: Vec3::new(wpos.x + 5, wpos.y - 1, self.alt), - max: Vec3::new(wpos.x + 6, wpos.y + 7, self.alt + wall_height), - })); - let pillars = prim(Primitive::Or(pillar_start, pillar_end)); - fill( - prim(Primitive::Or(wall, pillars)), - Fill::Block(Block::new(BlockKind::Rock, Rgb::new(33, 33, 33))), - ); - fill(cut_path, Fill::Block(Block::empty())); - fill(cut_sides1, Fill::Block(Block::empty())); + + fill(wall, Fill::Brick(BlockKind::Rock, wall_rgb, 12)); + let sides = prim(Primitive::Or(parapet, parapet2)); + fill(sides, Fill::Brick(BlockKind::Rock, wall_rgb, 12)); + if (x + y).is_odd() { + fill( + prim(Primitive::Aabb(Aabb { + min: (wpos + 2 * dir - dir.yx()).with_z(self.alt - 20), + max: (wpos + 4 * dir + (ts + 1) * dir.yx()) + .with_z(self.alt + wall_height), + })), + Fill::Brick(BlockKind::Rock, wall_rgb, 12), + ); + } else { + let window_top = prim(Primitive::Aabb(Aabb { + min: (wpos + 2 * dir).with_z(self.alt + wall_height / 4 + 9), + max: (wpos + (ts - 2) * dir + dir.yx()) + .with_z(self.alt + wall_height / 4 + 12), + })); + let window_bottom = prim(Primitive::Aabb(Aabb { + min: (wpos + 1 * dir).with_z(self.alt + wall_height / 4), + max: (wpos + (ts - 1) * dir + dir.yx()) + .with_z(self.alt + wall_height / 4 + 9), + })); + let window_top2 = prim(Primitive::Aabb(Aabb { + min: (wpos + 2 * dir + (ts - 1) * dir.yx()) + .with_z(self.alt + wall_height / 4 + 9), + max: (wpos + (ts - 2) * dir + ts * dir.yx()) + .with_z(self.alt + wall_height / 4 + 12), + })); + let window_bottom2 = prim(Primitive::Aabb(Aabb { + min: (wpos + 1 * dir + (ts - 1) * dir.yx()) + .with_z(self.alt + wall_height / 4), + max: (wpos + (ts - 1) * dir + ts * dir.yx()) + .with_z(self.alt + wall_height / 4 + 9), + })); + + fill(window_bottom, Fill::Block(Block::empty())); + fill(window_top, Fill::Block(Block::empty())); + fill(window_bottom2, Fill::Block(Block::empty())); + fill(window_top2, Fill::Block(Block::empty())); + } + fill(cut_sides, Fill::Block(Block::empty())); }, - TileKind::Tower => { + TileKind::Tower(roof) => { + let tower_total_height = + self.alt + wall_height + parapet_height + tower_height; let tower_lower = prim(Primitive::Aabb(Aabb { - min: wpos.with_z(self.alt), - max: (wpos + 6).with_z(self.alt + wall_height + tower_height), + min: (wpos - 1).with_z(self.alt - 20), + max: (wpos + ts + 1).with_z(tower_total_height), })); - let tower_lower_inner_x = prim(Primitive::Aabb(Aabb { - min: Vec3::new( - wpos.x + ori_tower.x, - wpos.y + parapet_width, - self.alt + wall_height, - ), - max: Vec3::new( - wpos.x + 6 + ori_tower.x - 1, - wpos.y + 6 - parapet_width, - self.alt + wall_height + tower_height / 3, - ), - })); - let tower_lower_inner_y = prim(Primitive::Aabb(Aabb { - min: Vec3::new( - wpos.x + parapet_width, - wpos.y + ori_tower.y, - self.alt + wall_height, - ), - max: Vec3::new( - wpos.x + 6 - parapet_width, - wpos.y + 6 + ori_tower.y - 1, - self.alt + wall_height + tower_height / 3, - ), - })); - let tower_lower_inner = - prim(Primitive::Or(tower_lower_inner_x, tower_lower_inner_y)); - fill( - prim(Primitive::Xor(tower_lower, tower_lower_inner)), - Fill::Block(Block::new(BlockKind::Rock, Rgb::new(33, 33, 33))), - ); + fill(tower_lower, Fill::Brick(BlockKind::Rock, wall_rgb, 12)); let tower_upper = prim(Primitive::Aabb(Aabb { - min: Vec3::new( - wpos.x - 1, - wpos.y - 1, - self.alt + wall_height + tower_height - 3i32, - ), - max: Vec3::new( - wpos.x + 7, - wpos.y + 7, - self.alt + wall_height + tower_height - 1i32, - ), + min: (wpos - 2).with_z(tower_total_height - 4i32), + max: (wpos + ts + 2).with_z(tower_total_height - 2i32), })); let tower_upper2 = prim(Primitive::Aabb(Aabb { - min: Vec3::new( - wpos.x - 2, - wpos.y - 2, - self.alt + wall_height + tower_height - 1i32, - ), - max: Vec3::new( - wpos.x + 8, - wpos.y + 8, - self.alt + wall_height + tower_height, - ), + min: (wpos - 3).with_z(tower_total_height - 2i32), + max: (wpos + ts + 3).with_z(tower_total_height), })); fill( prim(Primitive::Or(tower_upper, tower_upper2)), - Fill::Block(Block::new(BlockKind::Rock, Rgb::new(33, 33, 33))), + Fill::Brick(BlockKind::Rock, wall_rgb, 12), ); - let roof_lip = 1; - let roof_height = 8 / 2 + roof_lip + 1; + match roof { + RoofKind::Pyramid => { + let roof_lip = 1; + let roof_height = (ts + 3) / 2 + roof_lip + 1; - // Roof - fill( - prim(Primitive::Pyramid { - aabb: Aabb { - min: (wpos - 2 - roof_lip) - .with_z(self.alt + wall_height + tower_height), - max: (wpos + 8 + roof_lip).with_z( - self.alt + wall_height + tower_height + roof_height, + fill( + prim(Primitive::Pyramid { + aabb: Aabb { + min: (wpos - 3 - roof_lip).with_z(tower_total_height), + max: (wpos + ts + 3 + roof_lip) + .with_z(tower_total_height + roof_height), + }, + inset: roof_height, + }), + Fill::Brick(BlockKind::Wood, Rgb::new(40, 5, 11), 10), + ); + }, + RoofKind::Parapet => { + let tower_top_outer = prim(Primitive::Aabb(Aabb { + min: (wpos - 3).with_z( + self.alt + wall_height + parapet_height + tower_height, ), - }, - inset: roof_height, - }), - Fill::Block(Block::new(BlockKind::Wood, Rgb::new(116, 20, 20))), - ); + max: (wpos + ts + 3) + .with_z(tower_total_height + parapet_height), + })); + let tower_top_inner = prim(Primitive::Aabb(Aabb { + min: (wpos - 2).with_z(tower_total_height), + max: (wpos + ts + 2) + .with_z(tower_total_height + parapet_height), + })); + + fill( + prim(Primitive::Xor(tower_top_outer, tower_top_inner)), + Fill::Brick(BlockKind::Rock, wall_rgb, 12), + ); + + for x in (wpos.x..wpos.x + ts).step_by(2 * parapet_gap as usize) { + fill( + prim(Primitive::Aabb(Aabb { + min: Vec3::new(x, wpos.y - 3, tower_total_height + 1), + max: Vec3::new( + x + parapet_gap, + wpos.y + ts + 3, + tower_total_height + parapet_height, + ), + })), + Fill::Block(Block::empty()), + ); + } + for y in (wpos.y..wpos.y + ts).step_by(2 * parapet_gap as usize) { + fill( + prim(Primitive::Aabb(Aabb { + min: Vec3::new(wpos.x - 3, y, tower_total_height + 1), + max: Vec3::new( + wpos.x + ts + 3, + y + parapet_gap, + tower_total_height + parapet_height, + ), + })), + Fill::Block(Block::empty()), + ); + } + + for &cpos in SQUARE_4.iter() { + let pos = wpos - 3 + (ts + 6) * cpos - cpos; + let pos2 = wpos - 2 + (ts + 4) * cpos - cpos; + fill( + prim(Primitive::Aabb(Aabb { + min: pos.with_z(tower_total_height - 2), + max: (pos + 1) + .with_z(tower_total_height + parapet_height), + })), + Fill::Block(Block::empty()), + ); + fill( + prim(Primitive::Aabb(Aabb { + min: pos2.with_z(tower_total_height - 4), + max: (pos2 + 1).with_z(tower_total_height - 2), + })), + Fill::Block(Block::empty()), + ); + } + }, + } }, TileKind::Keep(kind) => { match kind { - tile::KeepKind::Middle => { + KeepKind::Middle => { for i in 0..keep_levels + 1 { let height = keep_level_height * i; fill( prim(Primitive::Aabb(Aabb { min: wpos.with_z(self.alt + height), - max: (wpos + 6).with_z(self.alt + height + 1), + max: (wpos + ts).with_z(self.alt + height + 1), })), Fill::Block(Block::new( - BlockKind::Rock, + BlockKind::Wood, Rgb::new(89, 44, 14), )), ); } }, - tile::KeepKind::Corner => {}, - tile::KeepKind::Wall(_ori) => { + KeepKind::Corner => {}, + KeepKind::Wall(_ori) => { for i in 0..keep_levels + 1 { let _height = keep_level_height * i; // TODO clamp value in case of big heights @@ -222,5 +288,57 @@ impl Structure for Castle { } } } + + // Render gate here + // TODO move this into tile loop + let gate_aabb = Aabb { + min: (site.tile_wpos(self.gate_aabr.min) + Vec2::unit_x()).with_z(self.gate_alt - 1), + max: (site.tile_wpos(self.gate_aabr.max) - Vec2::unit_x()) + .with_z(self.alt + wall_height), + }; + fill( + prim(Primitive::Aabb(gate_aabb)), + Fill::Brick(BlockKind::Rock, wall_rgb, 12), + ); + fill( + prim(Primitive::Aabb(Aabb { + min: (gate_aabb.min + Vec3::unit_x() * 2 + Vec3::unit_z() * 2), + max: (gate_aabb.max - Vec3::unit_x() * 2 - Vec3::unit_z() * 16), + })), + Fill::Block(Block::empty()), + ); + let height = self.alt + wall_height - 17; + for i in 1..5 { + fill( + prim(Primitive::Aabb(Aabb { + min: Vec3::new(gate_aabb.min.x + 2 + i, gate_aabb.min.y, height + i as i32), + max: Vec3::new( + gate_aabb.max.x - 2 - i, + gate_aabb.max.y, + height + i as i32 + 1, + ), + })), + Fill::Block(Block::empty()), + ); + } + let height = self.alt + wall_height - 7; + for x in (gate_aabb.min.x + 1..gate_aabb.max.x - 2).step_by(4) { + fill( + prim(Primitive::Aabb(Aabb { + min: Vec3::new(x, gate_aabb.min.y + 1, height - 13), + max: Vec3::new(x + 2, gate_aabb.min.y + 2, height), + })), + Fill::Brick(BlockKind::Rock, Rgb::new(27, 35, 32), 8), + ); + } + for z in (height - 12..height).step_by(4) { + fill( + prim(Primitive::Aabb(Aabb { + min: Vec3::new(gate_aabb.min.x + 2, gate_aabb.min.y + 1, z), + max: Vec3::new(gate_aabb.max.x - 2, gate_aabb.min.y + 2, z + 2), + })), + Fill::Brick(BlockKind::Rock, Rgb::new(27, 35, 32), 8), + ); + } } } diff --git a/world/src/site2/tile.rs b/world/src/site2/tile.rs index ef757f9887..1d0f759deb 100644 --- a/world/src/site2/tile.rs +++ b/world/src/site2/tile.rs @@ -183,8 +183,9 @@ pub enum TileKind { Building, Castle, Wall(Ori), - Tower, + Tower(RoofKind), Keep(KeepKind), + Gate, } #[derive(Clone, PartialEq)] @@ -229,6 +230,12 @@ pub enum KeepKind { Wall(Ori), } +#[derive(Copy, Clone, PartialEq)] +pub enum RoofKind { + Parapet, + Pyramid, +} + #[repr(u8)] #[derive(Copy, Clone, PartialEq)] pub enum Ori {