diff --git a/assets/voxygen/voxel/sprite_manifest.ron b/assets/voxygen/voxel/sprite_manifest.ron index ea021ba90c..c4439fba25 100644 --- a/assets/voxygen/voxel/sprite_manifest.ron +++ b/assets/voxygen/voxel/sprite_manifest.ron @@ -3329,7 +3329,7 @@ Lantern: Some(( ], wind_sway: 0.0, )), -// CliffTown Decor +// CliffTown & DesertCity Decor WindowArabic: Some(( variations: [ ( diff --git a/server/src/rtsim/mod.rs b/server/src/rtsim/mod.rs index fdbfdbee9c..690c9780f3 100644 --- a/server/src/rtsim/mod.rs +++ b/server/src/rtsim/mod.rs @@ -275,7 +275,6 @@ pub fn init( }); } }, - SiteKind::CliffTown(site2) => { for _ in 0..(site2.plazas().len() as f32 * 1.5) as usize { rtsim.entities.insert(Entity { @@ -300,6 +299,75 @@ pub fn init( }); } }, + SiteKind::DesertCity(site2) => { + // villagers + for _ in 0..(site2.plazas().len() as f32 * 1.5) as usize { + rtsim.entities.insert(Entity { + is_loaded: false, + pos: site2 + .plots() + .choose(&mut thread_rng()) + .map_or(site.get_origin(), |plot| { + site2.tile_center_wpos(plot.root_tile()) + }) + .with_z(0) + .map(|e| e as f32), + seed: thread_rng().gen(), + controller: RtSimController::default(), + last_time_ticked: 0.0, + kind: RtSimEntityKind::Villager, + brain: Brain::villager(site_id, &mut thread_rng()), + }); + } + + // guards + for _ in 0..site2.plazas().len() as usize { + rtsim.entities.insert(Entity { + is_loaded: false, + pos: site2 + .plazas() + .choose(&mut thread_rng()) + .map_or(site.get_origin(), |p| { + site2.tile_center_wpos(site2.plot(p).root_tile()) + + Vec2::new( + thread_rng().gen_range(-8..9), + thread_rng().gen_range(-8..9), + ) + }) + .with_z(0) + .map(|e| e as f32), + seed: thread_rng().gen(), + controller: RtSimController::default(), + last_time_ticked: 0.0, + kind: RtSimEntityKind::TownGuard, + brain: Brain::town_guard(site_id, &mut thread_rng()), + }); + } + + // merchants + for _ in 0..site2.plazas().len() as usize { + rtsim.entities.insert(Entity { + is_loaded: false, + pos: site2 + .plazas() + .choose(&mut thread_rng()) + .map_or(site.get_origin(), |p| { + site2.tile_center_wpos(site2.plot(p).root_tile()) + + Vec2::new( + thread_rng().gen_range(-8..9), + thread_rng().gen_range(-8..9), + ) + }) + .with_z(0) + .map(|e| e as f32), + seed: thread_rng().gen(), + controller: RtSimController::default(), + last_time_ticked: 0.0, + kind: RtSimEntityKind::Merchant, + brain: Brain::merchant(site_id, &mut thread_rng()), + }); + } + }, _ => {}, } } diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 0ff8f76d7d..1aeb62eb53 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -170,6 +170,7 @@ impl Civs { SiteKind::Castle => (16i32, 5.0), SiteKind::Refactor => (32i32, 10.0), SiteKind::CliffTown => (32i32, 10.0), + SiteKind::DesertCity => (64i32, 25.0), SiteKind::Tree => (12i32, 8.0), SiteKind::GiantTree => (12i32, 8.0), SiteKind::Gnarling => (16i32, 10.0), @@ -231,42 +232,46 @@ impl Civs { }); let mut rng = ctx.reseed().rng; - let site = index.sites.insert(match &sim_site.kind { - SiteKind::Settlement => { - WorldSite::settlement(Settlement::generate(wpos, Some(ctx.sim), &mut rng)) - }, - SiteKind::Dungeon => WorldSite::dungeon(site2::Site::generate_dungeon( - &Land::from_sim(ctx.sim), - &mut rng, - wpos, - )), - SiteKind::Castle => { - WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng)) - }, - SiteKind::Refactor => WorldSite::refactor(site2::Site::generate_city( - &Land::from_sim(ctx.sim), - &mut rng, - wpos, - )), - SiteKind::CliffTown => WorldSite::cliff_town(site2::Site::generate_cliff_town( - &Land::from_sim(ctx.sim), - &mut rng, - wpos, - )), - SiteKind::Tree => { - WorldSite::tree(Tree::generate(wpos, &Land::from_sim(ctx.sim), &mut rng)) - }, - SiteKind::GiantTree => WorldSite::giant_tree(site2::Site::generate_giant_tree( - &Land::from_sim(ctx.sim), - &mut rng, - wpos, - )), - SiteKind::Gnarling => WorldSite::gnarling(site2::Site::generate_gnarling( - &Land::from_sim(ctx.sim), - &mut rng, - wpos, - )), - }); + let site = + index.sites.insert(match &sim_site.kind { + SiteKind::Settlement => { + WorldSite::settlement(Settlement::generate(wpos, Some(ctx.sim), &mut rng)) + }, + SiteKind::Dungeon => WorldSite::dungeon(site2::Site::generate_dungeon( + &Land::from_sim(ctx.sim), + &mut rng, + wpos, + )), + SiteKind::Castle => { + WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng)) + }, + SiteKind::Refactor => WorldSite::refactor(site2::Site::generate_city( + &Land::from_sim(ctx.sim), + &mut rng, + wpos, + )), + SiteKind::CliffTown => WorldSite::cliff_town(site2::Site::generate_cliff_town( + &Land::from_sim(ctx.sim), + &mut rng, + wpos, + )), + SiteKind::DesertCity => WorldSite::desert_city( + site2::Site::generate_desert_city(&Land::from_sim(ctx.sim), &mut rng, wpos), + ), + SiteKind::Tree => { + WorldSite::tree(Tree::generate(wpos, &Land::from_sim(ctx.sim), &mut rng)) + }, + SiteKind::GiantTree => WorldSite::giant_tree(site2::Site::generate_giant_tree( + &Land::from_sim(ctx.sim), + &mut rng, + wpos, + )), + SiteKind::Gnarling => WorldSite::gnarling(site2::Site::generate_gnarling( + &Land::from_sim(ctx.sim), + &mut rng, + wpos, + )), + }); sim_site.site_tmp = Some(site); let site_ref = &index.sites[site]; @@ -549,6 +554,7 @@ impl Civs { // TODO: specify SiteKind based on where a suitable location is found let kind = match ctx.rng.gen_range(0..64) { 0..=10 => SiteKind::CliffTown, + 11..=12 => SiteKind::DesertCity, _ => SiteKind::Refactor, }; let site = attempt(100, || { @@ -968,6 +974,7 @@ impl Civs { SiteKind::Refactor | SiteKind::Settlement | SiteKind::CliffTown + | SiteKind::DesertCity | SiteKind::Castle ) }) @@ -976,8 +983,11 @@ impl Civs { .collect::>(); nearby.sort_by_key(|(_, dist)| *dist as i32); - if let SiteKind::Refactor | SiteKind::Settlement | SiteKind::CliffTown | SiteKind::Castle = - self.sites[site].kind + if let SiteKind::Refactor + | SiteKind::Settlement + | SiteKind::CliffTown + | SiteKind::DesertCity + | SiteKind::Castle = self.sites[site].kind { for (nearby, _) in nearby.into_iter().take(5) { // Find a novel path @@ -1229,6 +1239,7 @@ pub enum SiteKind { Castle, Refactor, CliffTown, + DesertCity, Tree, GiantTree, Gnarling, @@ -1344,6 +1355,11 @@ impl SiteKind { && chunk.near_cliffs() && suitable_for_town(4.0) }, + SiteKind::DesertCity => { + (0.9..1.0).contains(&chunk.temp) + && !chunk.near_cliffs() + && suitable_for_town(4.0) + }, SiteKind::Castle => { if chunk.tree_density > 0.4 || chunk.river.near_water() || chunk.near_cliffs() { return false; @@ -1392,7 +1408,10 @@ impl Site { pub fn is_dungeon(&self) -> bool { matches!(self.kind, SiteKind::Dungeon) } pub fn is_settlement(&self) -> bool { - matches!(self.kind, SiteKind::Settlement | SiteKind::Refactor) + matches!( + self.kind, + SiteKind::Settlement | SiteKind::Refactor | SiteKind::CliffTown | SiteKind::DesertCity + ) } pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) } diff --git a/world/src/lib.rs b/world/src/lib.rs index 9e43d3b5c1..717f04dcee 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -149,7 +149,7 @@ impl World { name: site.site_tmp.map(|id| index.sites[id].name().to_string()), // TODO: Probably unify these, at some point kind: match &site.kind { - civ::SiteKind::Settlement | civ::SiteKind::Refactor | civ::SiteKind::CliffTown => world_msg::SiteKind::Town, + civ::SiteKind::Settlement | civ::SiteKind::Refactor | civ::SiteKind::CliffTown | civ::SiteKind::DesertCity => world_msg::SiteKind::Town, civ::SiteKind::Dungeon => world_msg::SiteKind::Dungeon { difficulty: match site.site_tmp.map(|id| &index.sites[id].kind) { Some(SiteKind::Dungeon(d)) => d.dungeon_difficulty().unwrap_or(0), diff --git a/world/src/site/economy/context.rs b/world/src/site/economy/context.rs index 5c1ebe321e..8c8b3d2206 100644 --- a/world/src/site/economy/context.rs +++ b/world/src/site/economy/context.rs @@ -119,9 +119,10 @@ impl Environment { for site in index.sites.ids() { let site = &index.sites[site]; match site.kind { - SiteKind::Settlement(_) | SiteKind::Refactor(_) | SiteKind::CliffTown(_) => { - towns += site.economy.pop - }, + SiteKind::Settlement(_) + | SiteKind::Refactor(_) + | SiteKind::CliffTown(_) + | SiteKind::DesertCity(_) => towns += site.economy.pop, SiteKind::Dungeon(_) => dungeons += site.economy.pop, SiteKind::Castle(_) => castles += site.economy.pop, SiteKind::Tree(_) => (), @@ -383,7 +384,8 @@ mod tests { kind: match i.kind { crate::site::SiteKind::Settlement(_) | crate::site::SiteKind::Refactor(_) - | crate::site::SiteKind::CliffTown(_) => { + | crate::site::SiteKind::CliffTown(_) + | crate::site::SiteKind::DesertCity(_) => { common::terrain::site::SitesKind::Settlement }, crate::site::SiteKind::Dungeon(_) => { diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index 4cb4b43894..559130790f 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -66,6 +66,7 @@ pub enum SiteKind { Refactor(site2::Site), CliffTown(site2::Site), Tree(Tree), + DesertCity(site2::Site), GiantTree(site2::Site), Gnarling(site2::Site), } @@ -113,6 +114,13 @@ impl Site { } } + pub fn desert_city(dc: site2::Site) -> Self { + Self { + kind: SiteKind::DesertCity(dc), + economy: Economy::default(), + } + } + pub fn tree(t: Tree) -> Self { Self { kind: SiteKind::Tree(t), @@ -134,6 +142,7 @@ impl Site { SiteKind::Castle(c) => c.radius(), SiteKind::Refactor(s) => s.radius(), SiteKind::CliffTown(ct) => ct.radius(), + SiteKind::DesertCity(dc) => dc.radius(), SiteKind::Tree(t) => t.radius(), SiteKind::GiantTree(gt) => gt.radius(), SiteKind::Gnarling(g) => g.radius(), @@ -147,6 +156,7 @@ impl Site { SiteKind::Castle(c) => c.get_origin(), SiteKind::Refactor(s) => s.origin, SiteKind::CliffTown(ct) => ct.origin, + SiteKind::DesertCity(dc) => dc.origin, SiteKind::Tree(t) => t.origin, SiteKind::GiantTree(gt) => gt.origin, SiteKind::Gnarling(g) => g.origin, @@ -160,6 +170,7 @@ impl Site { SiteKind::Castle(c) => c.spawn_rules(wpos), SiteKind::Refactor(s) => s.spawn_rules(wpos), SiteKind::CliffTown(ct) => ct.spawn_rules(wpos), + SiteKind::DesertCity(dc) => dc.spawn_rules(wpos), SiteKind::Tree(t) => t.spawn_rules(wpos), SiteKind::GiantTree(gt) => gt.spawn_rules(wpos), SiteKind::Gnarling(g) => g.spawn_rules(wpos), @@ -173,6 +184,7 @@ impl Site { SiteKind::Castle(c) => c.name(), SiteKind::Refactor(s) => s.name(), SiteKind::CliffTown(ct) => ct.name(), + SiteKind::DesertCity(dc) => dc.name(), SiteKind::Tree(_) => "Giant Tree", SiteKind::GiantTree(gt) => gt.name(), SiteKind::Gnarling(g) => g.name(), @@ -184,12 +196,13 @@ impl Site { site_id: common::trade::SiteId, ) -> Option { match &self.kind { - SiteKind::Settlement(_) | SiteKind::Refactor(_) | SiteKind::CliffTown(_) => { - Some(common::trade::SiteInformation { - id: site_id, - unconsumed_stock: self.economy.get_available_stock(), - }) - }, + SiteKind::Settlement(_) + | SiteKind::Refactor(_) + | SiteKind::CliffTown(_) + | SiteKind::DesertCity(_) => Some(common::trade::SiteInformation { + id: site_id, + unconsumed_stock: self.economy.get_available_stock(), + }), _ => None, } } @@ -203,6 +216,7 @@ impl Site { SiteKind::Castle(c) => c.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk), SiteKind::Refactor(s) => s.render(canvas, dynamic_rng), SiteKind::CliffTown(ct) => ct.render(canvas, dynamic_rng), + SiteKind::DesertCity(dc) => dc.render(canvas, dynamic_rng), SiteKind::Tree(t) => t.render(canvas, dynamic_rng), SiteKind::GiantTree(gt) => gt.render(canvas, dynamic_rng), SiteKind::Gnarling(g) => g.render(canvas, dynamic_rng), @@ -229,6 +243,7 @@ impl Site { SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), SiteKind::Refactor(_) => {}, SiteKind::CliffTown(_) => {}, + SiteKind::DesertCity(_) => {}, SiteKind::Tree(_) => {}, SiteKind::GiantTree(gt) => gt.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::Gnarling(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement), @@ -238,7 +253,10 @@ impl Site { pub fn do_economic_simulation(&self) -> bool { matches!( self.kind, - SiteKind::Refactor(_) | SiteKind::CliffTown(_) | SiteKind::Settlement(_) + SiteKind::Refactor(_) + | SiteKind::CliffTown(_) + | SiteKind::DesertCity(_) + | SiteKind::Settlement(_) ) } } diff --git a/world/src/site/namegen.rs b/world/src/site/namegen.rs index 1b0dda89f1..e043a390ce 100644 --- a/world/src/site/namegen.rs +++ b/world/src/site/namegen.rs @@ -623,8 +623,8 @@ impl<'a, R: Rng> NameGen<'a, R> { self.generate_theme_from_parts(&start, &middle, &vowel, &end) } - // arabic inspired location names for cliff towns - pub fn generate_cliff_town(mut self) -> String { + // arabic inspired location names for clifftown and desertcity + pub fn generate_arabic(mut self) -> String { let start = [ "zor", "el", "mas", "yaz", "ra", "boh", "mah", "ah", "lam", "mak", "mol", "wa", "bisk", "moj", "bis", "ay", "sha", "rez", "bakh", "ta", "je", "ki", "mos", "asj", "meh", diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index eb45e6f126..9ed7badcf8 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -747,7 +747,7 @@ impl Site { let mut site = Site { origin, - name: NameGen::location(&mut rng).generate_cliff_town(), + name: NameGen::location(&mut rng).generate_arabic(), ..Site::default() }; @@ -787,6 +787,100 @@ impl Site { site } + pub fn generate_desert_city(land: &Land, rng: &mut impl Rng, origin: Vec2) -> Self { + let mut rng = reseed(rng); + + let mut site = Site { + origin, + name: NameGen::location(&mut rng).generate_arabic(), + ..Site::default() + }; + + site.demarcate_obstacles(land); + + site.make_plaza(land, &mut rng); + + let build_chance = Lottery::from(vec![(20.0, 1), (10.0, 2)]); + + let mut temples = 0; + + for _ in 0..30 { + match *build_chance.choose_seeded(rng.gen()) { + // DesertCityMultiplot + 1 => { + let size = (9.0 + rng.gen::().powf(5.0) * 1.5).round() as u32; + if let Some((aabr, door_tile, door_dir)) = attempt(32, || { + site.find_roadside_aabr( + &mut rng, + 8..(size + 1).pow(2), + Extent2::broadcast(size), + ) + }) { + let desert_city_multi_plot = plot::DesertCityMultiPlot::generate( + land, + &mut reseed(&mut rng), + &site, + door_tile, + door_dir, + aabr, + ); + let desert_city_multi_plot_alt = desert_city_multi_plot.alt; + let plot = site.create_plot(Plot { + kind: PlotKind::DesertCityMultiPlot(desert_city_multi_plot), + root_tile: aabr.center(), + tiles: aabr_tiles(aabr).collect(), + seed: rng.gen(), + }); + + site.blit_aabr(aabr, Tile { + kind: TileKind::Building, + plot: Some(plot), + hard_alt: Some(desert_city_multi_plot_alt), + }); + } else { + site.make_plaza(land, &mut rng); + } + }, + // DesertCityTemple + 2 if temples < 1 => { + let size = (9.0 + rng.gen::().powf(5.0) * 1.5).round() as u32; + if let Some((aabr, door_tile, door_dir)) = attempt(32, || { + site.find_roadside_aabr( + &mut rng, + 8..(size + 1).pow(2), + Extent2::broadcast(size), + ) + }) { + let desert_city_temple = plot::DesertCityTemple::generate( + land, + &mut reseed(&mut rng), + &site, + door_tile, + door_dir, + aabr, + ); + let desert_city_temple_alt = desert_city_temple.alt; + let plot = site.create_plot(Plot { + kind: PlotKind::DesertCityTemple(desert_city_temple), + root_tile: aabr.center(), + tiles: aabr_tiles(aabr).collect(), + seed: rng.gen(), + }); + + site.blit_aabr(aabr, Tile { + kind: TileKind::Building, + plot: Some(plot), + hard_alt: Some(desert_city_temple_alt), + }); + temples += 1; + } + }, + _ => {}, + } + } + site + } + pub fn wpos_tile_pos(&self, wpos2d: Vec2) -> Vec2 { (wpos2d - self.origin).map(|e| e.div_euclid(TILE_SIZE as i32)) } @@ -1035,6 +1129,12 @@ impl Site { PlotKind::Gnarling(gnarling) => gnarling.render_collect(self, canvas), PlotKind::GiantTree(giant_tree) => giant_tree.render_collect(self, canvas), PlotKind::CliffTower(cliff_tower) => cliff_tower.render_collect(self, canvas), + PlotKind::DesertCityMultiPlot(desert_city_multi_plot) => { + desert_city_multi_plot.render_collect(self, canvas) + }, + PlotKind::DesertCityTemple(desert_city_temple) => { + desert_city_temple.render_collect(self, canvas) + }, _ => continue, }; diff --git a/world/src/site2/plot.rs b/world/src/site2/plot.rs index 9b6c2d4c4f..df70e74ac4 100644 --- a/world/src/site2/plot.rs +++ b/world/src/site2/plot.rs @@ -1,5 +1,7 @@ mod castle; mod cliff_tower; +mod desert_city_multiplot; +mod desert_city_temple; pub mod dungeon; mod giant_tree; mod gnarling; @@ -7,7 +9,8 @@ mod house; mod workshop; pub use self::{ - castle::Castle, cliff_tower::CliffTower, dungeon::Dungeon, giant_tree::GiantTree, + castle::Castle, cliff_tower::CliffTower, desert_city_multiplot::DesertCityMultiPlot, + desert_city_temple::DesertCityTemple, dungeon::Dungeon, giant_tree::GiantTree, gnarling::GnarlingFortification, house::House, workshop::Workshop, }; @@ -47,6 +50,8 @@ impl Plot { pub enum PlotKind { House(House), Workshop(Workshop), + DesertCityMultiPlot(DesertCityMultiPlot), + DesertCityTemple(DesertCityTemple), Plaza, Castle(Castle), Road(Path>), diff --git a/world/src/site2/plot/desert_city_multiplot.rs b/world/src/site2/plot/desert_city_multiplot.rs new file mode 100644 index 0000000000..7d5b36d07a --- /dev/null +++ b/world/src/site2/plot/desert_city_multiplot.rs @@ -0,0 +1,2413 @@ +use super::*; +use crate::{ + assets::AssetHandle, + site2::{gen::PrimitiveTransform, plot::dungeon::spiral_staircase}, + util::{RandomField, Sampler, DIAGONALS, NEIGHBORS}, + Land, +}; +use common::terrain::{ + Block, BlockKind, SpriteKind, Structure as PrefabStructure, StructuresGroup, +}; +use lazy_static::lazy_static; +use rand::prelude::*; +use std::sync::Arc; +use vek::*; + +enum PlotKind { + MarketHall { + floors: i32, + towers: [(WatchTower, Vec2); 4], + }, + Multiple { + subplots: Vec<(SubPlotKind, Vec2)>, + }, +} + +enum SubPlotKind { + WorkshopHouse { floors: i32 }, + Library, + WatchTower(WatchTower), + PalmTree, + AnimalShed, +} + +struct WatchTower { + length: i32, + height: i32, +} + +/// Represents house data generated by the `generate()` method +pub struct DesertCityMultiPlot { + /// Axis aligned bounding region for the house + bounds: Aabr, + /// Approximate altitude of the door tile + pub(crate) alt: i32, + diameter: i32, + plot_kind: PlotKind, +} + +impl DesertCityMultiPlot { + pub fn generate( + land: &Land, + rng: &mut impl Rng, + site: &Site, + door_tile: Vec2, + door_dir: Vec2, + tile_aabr: Aabr, + ) -> Self { + let bounds = Aabr { + min: site.tile_wpos(tile_aabr.min), + max: site.tile_wpos(tile_aabr.max), + }; + let diameter = 10 + (bounds.max.x - bounds.min.x).min(bounds.max.y - bounds.min.y); + let plot_kind = match rng.gen_range(0..5) { + 0 => { + let floors = rng.gen_range(2..5); + let towers = { + let center = bounds.center(); + let room_length = diameter / 4; + let tower_length = diameter / 14; + let mut tower = |i| { + let tower_center = center + + DIAGONALS[i] + * (Vec2::new( + room_length + tower_length + 1, + room_length + 2 * tower_length, + )); + let height = rng.gen_range(25..35); + ( + WatchTower { + length: tower_length, + height, + }, + tower_center, + ) + }; + [tower(0), tower(1), tower(2), tower(3)] + }; + PlotKind::MarketHall { floors, towers } + }, + _ => { + let mut subplot_kind = || match rng.gen_range(0..15) { + 0..=5 => SubPlotKind::WorkshopHouse { + floors: rng.gen_range(1..4), + }, + 6..=7 => SubPlotKind::Library, + 8..=9 => SubPlotKind::AnimalShed, + 10..=11 => SubPlotKind::WatchTower(WatchTower { + length: diameter / 14, + height: rng.gen_range(25..35), + }), + _ => SubPlotKind::PalmTree, + }; + let subplot_center = |i| { + let corner_pos = bounds.min + diameter / 5; + corner_pos + SQUARE_4[i] * (diameter / 2 - 5) + }; + + let sw_plot = (subplot_kind(), subplot_center(0)); + let se_plot = (subplot_kind(), subplot_center(1)); + let nw_plot = (subplot_kind(), subplot_center(2)); + let ne_plot = (subplot_kind(), subplot_center(3)); + + PlotKind::Multiple { + subplots: vec![ne_plot, se_plot, sw_plot, nw_plot], + } + }, + }; + Self { + bounds, + alt: land.get_alt_approx(site.tile_center_wpos(door_tile + door_dir)) as i32, + diameter, + plot_kind, + } + } +} + +impl Structure for DesertCityMultiPlot { + fn render(&self, _site: &Site, _land: &Land, painter: &Painter) { + let sandstone = Fill::Sampling(Arc::new(|center| { + Some(match (RandomField::new(0).get(center)) % 37 { + 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)), + 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)), + 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)), + 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)), + _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)), + }) + })); + let sandstone_broken = Fill::Sampling(Arc::new(|center| { + Some(match (RandomField::new(0).get(center)) % 42 { + 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)), + 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)), + 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)), + 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)), + 36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)), + _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)), + }) + })); + let wood = Fill::Brick(BlockKind::Wood, Rgb::new(71, 33, 11), 12); + let base = self.alt + 1; + let center = self.bounds.center(); + // Fence + painter + .aabb(Aabb { + min: (self.bounds.min + 1).with_z(base - 20), + max: (self.bounds.max).with_z(base + 2), + }) + .without(painter.aabb(Aabb { + min: (self.bounds.min + 2).with_z(base - 20), + max: (self.bounds.max - 1).with_z(base + 2), + })) + .fill(sandstone_broken.clone()); + // Gate1 + painter + .aabb(Aabb { + min: Vec2::new(self.bounds.min.x + 1, center.y - 3).with_z(base - 20), + max: Vec2::new(self.bounds.max.x, center.y + 3).with_z(base + 10), + }) + .fill(sandstone.clone()); + + painter + .aabb(Aabb { + min: Vec2::new(self.bounds.min.x + 1, center.y - 3).with_z(base + 9), + max: Vec2::new(self.bounds.max.x, center.y - 1).with_z(base + 10), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(self.bounds.min.x + 1, center.y + 1).with_z(base + 9), + max: Vec2::new(self.bounds.max.x, center.y + 3).with_z(base + 10), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(self.bounds.min.x + 2, center.y - 3).with_z(base + 8), + max: Vec2::new(self.bounds.max.x - 1, center.y + 3).with_z(base + 10), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(self.bounds.min.x + 3, center.y - 3).with_z(base - 20), + max: Vec2::new(self.bounds.max.x - 2, center.y + 3).with_z(base + 10), + }) + .clear(); + // Gate2 + painter + .aabb(Aabb { + min: Vec2::new(center.x - 2, self.bounds.min.y + 1).with_z(base - 20), + max: Vec2::new(center.x + 4, self.bounds.max.y).with_z(base + 10), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x + 2, self.bounds.min.y + 1).with_z(base + 9), + max: Vec2::new(center.x + 4, self.bounds.max.y).with_z(base + 10), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 2, self.bounds.min.y + 1).with_z(base + 9), + max: Vec2::new(center.x, self.bounds.max.y).with_z(base + 10), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 2, self.bounds.min.y + 2).with_z(base + 8), + max: Vec2::new(center.x + 4, self.bounds.max.y - 1).with_z(base + 10), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 2, self.bounds.min.y + 3).with_z(base - 20), + max: Vec2::new(center.x + 4, self.bounds.max.y - 2).with_z(base + 10), + }) + .clear(); + // gate clear + painter + .aabb(Aabb { + min: Vec2::new(self.bounds.min.x + 1, center.y - 2).with_z(base), + max: Vec2::new(self.bounds.max.x, center.y + 2).with_z(base + 7), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 1, self.bounds.min.y + 1).with_z(base), + max: Vec2::new(center.x + 3, self.bounds.max.y).with_z(base + 7), + }) + .clear(); + // Foundation + painter + .aabb(Aabb { + min: (self.bounds.min + 1).with_z(base - 20), + max: (self.bounds.max).with_z(base), + }) + .fill(sandstone.clone()); + + // buildings + match &self.plot_kind { + // Market Hall with Watchtowers + PlotKind::MarketHall { floors, towers } => { + for floor in 0..*floors { + let room_height = self.diameter / 6; + let floor_level = base + (room_height * floor); + let room_length = (self.diameter / 4) - (2 * floor); + // Windowsills + painter + .aabb(Aabb { + min: Vec2::new(center.x - room_length - 1, center.y) + .with_z(floor_level + 1), + max: Vec2::new(center.x + room_length + 1, center.y + 2) + .with_z(floor_level + 2), + }) + .fill(wood.clone()); + // Room + painter + .aabb(Aabb { + min: Vec2::new(center.x - room_length, center.y - room_length + 3) + .with_z(floor_level), + max: Vec2::new(center.x + room_length, center.y + room_length - 1) + .with_z(floor_level + room_height), + }) + .fill(sandstone.clone()); + // Window Sprites + painter + .aabb(Aabb { + min: Vec2::new(center.x - room_length, center.y) + .with_z(floor_level + 2), + max: Vec2::new(center.x + room_length, center.y + 2) + .with_z(floor_level + 5), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WindowArabic).with_ori(4).unwrap(), + )); + // Clear Room + painter + .aabb(Aabb { + min: Vec2::new(center.x - room_length + 1, center.y - room_length + 4) + .with_z(floor_level), + max: Vec2::new(center.x + room_length - 1, center.y + room_length - 2) + .with_z(floor_level + room_height - 2), + }) + .clear(); + // Overhang1 + painter + .aabb(Aabb { + min: Vec2::new(center.x - room_length + 1, center.y + room_length - 1) + .with_z(floor_level + room_height - 1), + max: Vec2::new(center.x + room_length - 1, center.y + room_length) + .with_z(floor_level + room_height), + }) + .fill(wood.clone()); + // Overhang2 + painter + .aabb(Aabb { + min: Vec2::new(center.x + room_length, center.y - room_length + 2) + .with_z(floor_level + room_height - 1), + max: Vec2::new(center.x + room_length + 1, center.y + room_length - 2) + .with_z(floor_level + room_height), + }) + .fill(wood.clone()); + // Overhang3 + painter + .aabb(Aabb { + min: Vec2::new(center.x - room_length - 1, center.y - room_length + 4) + .with_z(floor_level + room_height - 1), + max: Vec2::new(center.x - room_length, center.y + room_length - 2) + .with_z(floor_level + room_height), + }) + .fill(wood.clone()); + // Door Frame + painter + .aabb(Aabb { + min: Vec2::new(center.x - 2, center.y + room_length - 1) + .with_z(floor_level), + max: Vec2::new(center.x + 2, center.y + room_length) + .with_z(floor_level + 5), + }) + .fill(sandstone.clone()); + // Clear Door + painter + .aabb(Aabb { + min: Vec2::new(center.x - 1, center.y + room_length - 2) + .with_z(floor_level), + max: Vec2::new(center.x + 1, center.y + room_length) + .with_z(floor_level + 4), + }) + .clear(); + // Remove Terrain in front of doors + painter + .aabb(Aabb { + min: Vec2::new(center.x - 2, center.y + room_length) + .with_z(floor_level), + max: Vec2::new(center.x + 2, center.y + room_length + 4) + .with_z(floor_level + 5), + }) + .clear(); + // Stairs for each storey + painter + .ramp( + Aabb { + min: Vec2::new(center.x - room_length, center.y - room_length + 1) + .with_z(floor_level), + max: Vec2::new(center.x + room_length, center.y - room_length + 3) + .with_z(floor_level + room_height), + }, + 2 * room_length, + Dir::X, + ) + .fill(sandstone.clone()); + //interior room compartment with entries + painter + .aabb(Aabb { + min: Vec2::new( + center.x - (2 * room_length / 3) + 1, + center.y - (2 * room_length / 3) + 4, + ) + .with_z(floor_level), + max: Vec2::new( + center.x + (2 * room_length / 3) - 1, + center.y + (2 * room_length / 3) - 2, + ) + .with_z(floor_level + room_height - 2), + }) + .fill(sandstone.clone()); + + painter + .aabb(Aabb { + min: Vec2::new( + center.x - (2 * room_length / 3) + 2, + center.y - (2 * room_length / 3) + 5, + ) + .with_z(floor_level), + max: Vec2::new( + center.x + (2 * room_length / 3) - 2, + center.y + (2 * room_length / 3) - 3, + ) + .with_z(floor_level + room_height - 2), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new( + center.x - (room_length / 6), + center.y - (2 * room_length / 3) + 4, + ) + .with_z(floor_level), + max: Vec2::new( + center.x + (room_length / 6), + center.y + (2 * room_length / 3) - 2, + ) + .with_z(floor_level + 3), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + center.x - (2 * room_length / 3) + 1, + center.y + 1 + (room_length / 6), + ) + .with_z(floor_level), + max: Vec2::new( + center.x + (2 * room_length / 3) - 1, + center.y + 1 - (room_length / 6), + ) + .with_z(floor_level + 3), + }) + .clear(); + + // interior room Wall Lamps + painter + .aabb(Aabb { + min: Vec2::new(center.x - 1, center.y - (2 * room_length / 3) + 5) + .with_z(floor_level + 4), + max: Vec2::new(center.x + 1, center.y + (2 * room_length / 3) - 1) + .with_z(floor_level + 5), + }) + .without( + painter.aabb(Aabb { + min: Vec2::new(center.x - 1, center.y - (2 * room_length / 3) + 6) + .with_z(floor_level + 4), + max: Vec2::new(center.x + 1, center.y + (2 * room_length / 3) - 2) + .with_z(floor_level + 5), + }), + ) + .fill(Fill::Block( + Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap(), + )); + + // Wall Lamps + painter + .aabb(Aabb { + min: Vec2::new(center.x - 1, center.y - room_length + 4) + .with_z(floor_level + 4), + max: Vec2::new(center.x + 1, center.y + room_length + 1) + .with_z(floor_level + 5), + }) + .without( + painter.aabb(Aabb { + min: Vec2::new(center.x - 1, center.y - room_length + 5) + .with_z(floor_level + 4), + max: Vec2::new(center.x + 1, center.y + room_length) + .with_z(floor_level + 5), + }), + ) + .fill(Fill::Block( + Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap(), + )); + + // Floor specific stuff + match floor { + // Ground level room furniture - bank + 0 => { + for dir in NEIGHBORS { + let pos = center + dir * 4; + painter.sprite(pos.with_z(floor_level), SpriteKind::Crate); + } + for dir in NEIGHBORS { + let pos = center + dir * 8; + painter.sprite(pos.with_z(floor_level), SpriteKind::DrawerSmall); + } + + for dir in SQUARE_4 { + let corner_pos = + Vec2::new(center.x - 4, center.y - room_length + 4); + let planter_pos = Vec2::new( + corner_pos.x + dir.x * 7, + corner_pos.y + dir.y * ((2 * room_length) - 7), + ); + painter.rotated_sprite( + planter_pos.with_z(floor_level), + SpriteKind::DrawerMedium, + 4 - (4 * dir.y) as u8, + ); + } + }, + // First floor room furniture + 1 => { + for dir in NEIGHBORS { + let pos = center + dir * 7; + painter.sprite(pos.with_z(floor_level), SpriteKind::Planter); + } + + for dir in SQUARE_4 { + let corner_pos = + Vec2::new(center.x - 4, center.y - room_length + 4); + let planter_pos = Vec2::new( + corner_pos.x + dir.x * 7, + corner_pos.y + dir.y * ((2 * room_length) - 7), + ); + painter.rotated_sprite( + planter_pos.with_z(floor_level), + SpriteKind::DrawerSmall, + 4 - (4 * dir.y) as u8, + ); + } + }, + _ => {}, + } + // On top floor, carve the roof terrace + if floor == (*floors - 1) { + painter + .aabb(Aabb { + min: Vec2::new( + center.x - room_length + 1, + center.y - room_length + 4, + ) + .with_z(floor_level + room_height - 1), + max: Vec2::new( + center.x + room_length - 1, + center.y + room_length - 2, + ) + .with_z(floor_level + room_height), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + center.x + room_length - 3, + center.y - room_length + 1, + ) + .with_z(floor_level + room_height - 1), + max: Vec2::new( + center.x + room_length - 1, + center.y - room_length + 4, + ) + .with_z(floor_level + room_height), + }) + .clear(); + // Roof Ornament + painter + .aabb(Aabb { + min: Vec2::new(center.x - room_length, center.y + room_length - 2) + .with_z(floor_level + room_height), + max: Vec2::new(center.x + room_length, center.y + room_length - 1) + .with_z(floor_level + room_height + 2), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + center.x - room_length + 2, + center.y + room_length - 2, + ) + .with_z(floor_level + room_height + 2), + max: Vec2::new( + center.x + room_length - 2, + center.y + room_length - 1, + ) + .with_z(floor_level + room_height + 3), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 2, center.y + room_length - 2) + .with_z(floor_level + room_height + 3), + max: Vec2::new(center.x + 2, center.y + room_length - 1) + .with_z(floor_level + room_height + 4), + }) + .fill(sandstone.clone()); + // Wood Beams + for dir in SQUARE_4 { + let corner_pos = + Vec2::new(center.x - room_length + 3, center.y - room_length + 7); + let pos = Vec2::new( + corner_pos.x + dir.x * ((2 * room_length) - 8), + corner_pos.y + dir.y * ((2 * room_length) - 12), + ); + painter + .aabb(Aabb { + min: pos.with_z(floor_level + room_height - 1), + max: (pos + 1) + .with_z(floor_level + room_height + (room_height / 2)), + }) + .fill(wood.clone()); + } + painter + .aabb(Aabb { + min: Vec2::new( + center.x - room_length + 2, + center.y - room_length + 6, + ) + .with_z(floor_level + room_height + (room_height / 2) - 1), + max: Vec2::new( + center.x + room_length - 3, + center.y + room_length - 3, + ) + .with_z(floor_level + room_height + (room_height / 2)), + }) + .fill(wood.clone()); + + painter + .aabb(Aabb { + min: Vec2::new( + center.x - room_length + 3, + center.y - room_length + 7, + ) + .with_z(floor_level + room_height + (room_height / 2) - 1), + max: Vec2::new( + center.x + room_length - 4, + center.y + room_length - 4, + ) + .with_z(floor_level + room_height + (room_height / 2)), + }) + .clear(); + + for b in 0..(room_length - 3) { + painter + .aabb(Aabb { + min: Vec2::new( + center.x - room_length + 3 + (b * 2), + center.y - room_length + 5, + ) + .with_z(floor_level + room_height + (room_height / 2)), + max: Vec2::new( + center.x - room_length + 4 + (b * 2), + center.y + room_length - 2, + ) + .with_z(floor_level + room_height + (room_height / 2) + 1), + }) + .fill(wood.clone()); + } + // roof furniture + for d in 0..2 { + for dir in NEIGHBORS { + let pos = center + dir * (3 + d * 3); + painter.sprite( + pos.with_z(floor_level + room_height - 1), + SpriteKind::Planter, + ); + } + } + } + } + // WatchTowers + for (tower, tower_center) in towers { + // Tower Windowsills + for h in 0..4 { + painter + .aabb(Aabb { + min: Vec2::new( + tower_center.x - 1, + tower_center.y - tower.length - 1, + ) + .with_z(base + 8 + (h * (tower.height / 5))), + max: Vec2::new( + tower_center.x + 1, + tower_center.y + tower.length + 1, + ) + .with_z(base + 9 + (h * (tower.height / 5))), + }) + .fill(wood.clone()); + } + for h in 0..4 { + painter + .aabb(Aabb { + min: Vec2::new( + tower_center.x - tower.length - 1, + tower_center.y - 1, + ) + .with_z(base + 8 + (h * (tower.height / 5))), + max: Vec2::new( + tower_center.x + tower.length + 1, + tower_center.y + 1, + ) + .with_z(base + 9 + (h * (tower.height / 5))), + }) + .fill(wood.clone()); + } + // Tower base + painter + .aabb(Aabb { + min: (tower_center - tower.length - 1).with_z(base), + max: (tower_center + tower.length + 1).with_z(base + 6), + }) + .fill(sandstone.clone()); + // Tower + painter + .aabb(Aabb { + min: (tower_center - tower.length).with_z(base), + max: (tower_center + tower.length).with_z(base + tower.height), + }) + .fill(sandstone.clone()); + // Tower Windows + for h in 0..4 { + painter + .aabb(Aabb { + min: Vec2::new(tower_center.x - 1, tower_center.y - tower.length) + .with_z(base + 9 + (h * (tower.height / 5))), + max: Vec2::new(tower_center.x + 1, tower_center.y + tower.length) + .with_z(base + 12 + (h * (tower.height / 5))), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WindowArabic).with_ori(2).unwrap(), + )); + } + for h in 0..4 { + painter + .aabb(Aabb { + min: Vec2::new(tower_center.x - tower.length, tower_center.y - 1) + .with_z(base + 9 + (h * (tower.height / 5))), + max: Vec2::new(tower_center.x + tower.length, tower_center.y + 1) + .with_z(base + 12 + (h * (tower.height / 5))), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WindowArabic).with_ori(4).unwrap(), + )); + } + // Tower entries1 + painter + .aabb(Aabb { + min: Vec2::new(tower_center.x - tower.length - 2, tower_center.y - 2) + .with_z(base - 20), + max: Vec2::new(tower_center.x + tower.length + 2, tower_center.y + 2) + .with_z(base + 5), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(tower_center.x - tower.length - 4, tower_center.y - 1) + .with_z(base), + max: Vec2::new(tower_center.x + tower.length + 4, tower_center.y + 1) + .with_z(base + 4), + }) + .clear(); + // Tower entries2 + painter + .aabb(Aabb { + min: Vec2::new(tower_center.x - 2, tower_center.y - tower.length - 2) + .with_z(base - 20), + max: Vec2::new(tower_center.x + 2, tower_center.y + tower.length + 2) + .with_z(base + 5), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(tower_center.x - 1, tower_center.y - tower.length - 4) + .with_z(base), + max: Vec2::new(tower_center.x + 1, tower_center.y + tower.length + 4) + .with_z(base + 4), + }) + .clear(); + // Tower Entry Lamps + for d in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + tower_center.x - 1, + tower_center.y - tower.length - 3 + + (d * ((2 * tower.length) + 6)), + ) + .with_z(base + 4), + max: Vec2::new( + tower_center.x + 1, + tower_center.y - tower.length - 2 + + (d * ((2 * tower.length) + 4)), + ) + .with_z(base + 5), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WallLampSmall) + .with_ori(0 + (4 * d) as u8) + .unwrap(), + )); + } + // Platform + painter + .aabb(Aabb { + min: (tower_center - tower.length - 2).with_z(base + tower.height), + max: (tower_center + tower.length + 2).with_z(base + tower.height + 4), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: (tower_center - tower.length - 1).with_z(base + tower.height + 2), + max: (tower_center + tower.length + 1).with_z(base + tower.height + 4), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new(tower_center.x - tower.length - 2, tower_center.y - 2) + .with_z(base + tower.height + 3), + max: Vec2::new(tower_center.x + tower.length + 2, tower_center.y + 2) + .with_z(base + tower.height + 4), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(tower_center.x - 2, tower_center.y - tower.length - 2) + .with_z(base + tower.height + 3), + max: Vec2::new(tower_center.x + 2, tower_center.y + tower.length + 2) + .with_z(base + tower.height + 4), + }) + .clear(); + + // clear Tower + let tower_clear = painter.aabb(Aabb { + min: (tower_center - tower.length + 1).with_z(base), + max: (tower_center + tower.length - 1).with_z(base + tower.height + 3), + }); + tower_clear.clear(); + // stairway + let stair_radius = tower.length + 1; + let stairs_clear = painter.aabb(Aabb { + min: (tower_center - stair_radius as i32).with_z(base), + max: (tower_center + stair_radius as i32).with_z(base + tower.height + 2), + }); + stairs_clear + .sample(spiral_staircase( + tower_center.with_z(base + tower.height + 2), + stair_radius as f32, + 0.5, + (2 * tower.length) as f32, + )) + .intersect(tower_clear) + .fill(sandstone.clone()); + //make some room for base entries + painter + .aabb(Aabb { + min: (tower_center - tower.length).with_z(base), + max: (tower_center + tower.length).with_z(base + 5), + }) + .without(painter.aabb(Aabb { + min: (tower_center - tower.length + 1).with_z(base), + max: (tower_center + tower.length - 1).with_z(base + 5), + })) + .clear(); + // clear some steps at the bottom for tower entries + painter + .aabb(Aabb { + min: (tower_center - tower.length + 1).with_z(base + 1), + max: (tower_center + tower.length - 1).with_z(base + 2), + }) + .without(painter.aabb(Aabb { + min: (tower_center - tower.length + 2).with_z(base + 1), + max: (tower_center + tower.length - 2).with_z(base + 2), + })) + .clear(); + } + }, + PlotKind::Multiple { subplots } => { + // House, Workshop, Library, Palm, WatchTower + for (subplot_kind, subplot_center) in subplots { + match subplot_kind { + // House or Workshop (one storey house) + SubPlotKind::WorkshopHouse { floors } => { + for floor in 0..*floors { + let room_height = self.diameter / 6; + let floor_level = base + (room_height * floor); + let room_length = (self.diameter / 7) - (2 * floor); + // Windowsills + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - room_length - 1, + subplot_center.y, + ) + .with_z(floor_level + 1), + max: Vec2::new( + subplot_center.x + room_length + 1, + subplot_center.y + 2, + ) + .with_z(floor_level + 2), + }) + .fill(wood.clone()); + // Room + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - room_length, + subplot_center.y - room_length + 3, + ) + .with_z(floor_level), + max: Vec2::new( + subplot_center.x + room_length, + subplot_center.y + room_length - 1, + ) + .with_z(floor_level + room_height), + }) + .fill(sandstone.clone()); + // Window Sprites + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - room_length, + subplot_center.y, + ) + .with_z(floor_level + 2), + max: Vec2::new( + subplot_center.x + room_length, + subplot_center.y + 2, + ) + .with_z(floor_level + 5), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WindowArabic).with_ori(4).unwrap(), + )); + // Clear Room + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - room_length + 1, + subplot_center.y - room_length + 4, + ) + .with_z(floor_level), + max: Vec2::new( + subplot_center.x + room_length - 1, + subplot_center.y + room_length - 2, + ) + .with_z(floor_level + room_height - 2), + }) + .clear(); + // Overhang1 + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - room_length + 1, + subplot_center.y + room_length - 1, + ) + .with_z(floor_level + room_height - 1), + max: Vec2::new( + subplot_center.x + room_length - 1, + subplot_center.y + room_length, + ) + .with_z(floor_level + room_height), + }) + .fill(wood.clone()); + // Overhang2 + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x + room_length, + subplot_center.y - room_length + 2, + ) + .with_z(floor_level + room_height - 1), + max: Vec2::new( + subplot_center.x + room_length + 1, + subplot_center.y + room_length - 2, + ) + .with_z(floor_level + room_height), + }) + .fill(wood.clone()); + // Overhang3 + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - room_length - 1, + subplot_center.y - room_length + 4, + ) + .with_z(floor_level + room_height - 1), + max: Vec2::new( + subplot_center.x - room_length, + subplot_center.y + room_length - 2, + ) + .with_z(floor_level + room_height), + }) + .fill(wood.clone()); + // Door Frame + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 2, + subplot_center.y + room_length - 1, + ) + .with_z(floor_level), + max: Vec2::new( + subplot_center.x + 2, + subplot_center.y + room_length, + ) + .with_z(floor_level + 5), + }) + .fill(sandstone.clone()); + // Clear Door + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 1, + subplot_center.y + room_length - 2, + ) + .with_z(floor_level), + max: Vec2::new( + subplot_center.x + 1, + subplot_center.y + room_length, + ) + .with_z(floor_level + 4), + }) + .clear(); + // Remove Terrain in front of doors + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 2, + subplot_center.y + room_length, + ) + .with_z(floor_level), + max: Vec2::new( + subplot_center.x + 2, + subplot_center.y + room_length + 4, + ) + .with_z(floor_level + 5), + }) + .clear(); + // Stairs for each storey + painter + .ramp( + Aabb { + min: Vec2::new( + subplot_center.x - room_length, + subplot_center.y - room_length + 1, + ) + .with_z(floor_level), + max: Vec2::new( + subplot_center.x + room_length, + subplot_center.y - room_length + 3, + ) + .with_z(floor_level + room_height), + }, + 2 * room_length, + Dir::X, + ) + .fill(sandstone.clone()); + // Carve Roof Terrace + if floor == floors - 1 { + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - room_length + 1, + subplot_center.y - room_length + 4, + ) + .with_z(floor_level + room_height - 1), + max: Vec2::new( + subplot_center.x + room_length - 1, + subplot_center.y + room_length - 2, + ) + .with_z(floor_level + room_height), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x + room_length - 3, + subplot_center.y - room_length + 1, + ) + .with_z(floor_level + room_height - 1), + max: Vec2::new( + subplot_center.x + room_length - 1, + subplot_center.y - room_length + 4, + ) + .with_z(floor_level + room_height), + }) + .clear(); + // Roof Ornament + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - room_length, + subplot_center.y + room_length - 2, + ) + .with_z(floor_level + room_height), + max: Vec2::new( + subplot_center.x + room_length, + subplot_center.y + room_length - 1, + ) + .with_z(floor_level + room_height + 2), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - room_length + 2, + subplot_center.y + room_length - 2, + ) + .with_z(floor_level + room_height + 2), + max: Vec2::new( + subplot_center.x + room_length - 2, + subplot_center.y + room_length - 1, + ) + .with_z(floor_level + room_height + 3), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 2, + subplot_center.y + room_length - 2, + ) + .with_z(floor_level + room_height + 3), + max: Vec2::new( + subplot_center.x + 2, + subplot_center.y + room_length - 1, + ) + .with_z(floor_level + room_height + 4), + }) + .fill(sandstone.clone()); + }; + // furniture + if *floors == 1 { + // Workshop in one-storey buildings + painter + .aabb(Aabb { + min: (subplot_center - 2).with_z(floor_level), + max: (subplot_center + 2) + .with_z(floor_level + room_height + 1), + }) + .fill(sandstone.clone()); + + painter + .aabb(Aabb { + min: (subplot_center - 2).with_z(floor_level + 1), + max: (subplot_center + 2) + .with_z(floor_level + room_height - 2), + }) + .clear(); + painter + .aabb(Aabb { + min: (subplot_center - 1).with_z(floor_level), + max: (subplot_center + 1) + .with_z(floor_level + room_height + 1), + }) + .clear(); + painter + .aabb(Aabb { + min: (subplot_center - 1).with_z(floor_level - 1), + max: (subplot_center + 1).with_z(floor_level), + }) + .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, + ]; + 'outer: for d in 0..2 { + for dir in NEIGHBORS { + if stations.is_empty() { + break 'outer; + } + let position = subplot_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(floor_level), cr_station); + } + } + } else { + // kitchen, bath, living room in buildings with 2-3 storeys + match floor { + 0 => { + // kitchen + // cupboards / ovens / cushions / jugs + for d in 0..2 { + let a_pos = Vec2::new( + subplot_center.x + 3 - (d * 6), + subplot_center.y - room_length + 4, + ); + painter.rotated_sprite( + a_pos.with_z(floor_level), + match (RandomField::new(0) + .get(a_pos.with_z(floor_level))) + % 2 + { + 0 => SpriteKind::CupboardArabic, + _ => SpriteKind::OvenArabic, + }, + 4, + ); + let b_pos = Vec2::new( + subplot_center.x + 3 - (d * 6), + subplot_center.y + room_length - 3, + ); + painter.rotated_sprite( + b_pos.with_z(floor_level), + match (RandomField::new(0) + .get(b_pos.with_z(floor_level))) + % 4 + { + 0 => SpriteKind::CupboardArabic, + 1 => SpriteKind::OvenArabic, + 2 => SpriteKind::CushionArabic, + _ => SpriteKind::JugArabic, + }, + 0, + ); + // wall tables with varying items + let c_pos = Vec2::new( + subplot_center.x - room_length + + 1 + + (d * ((2 * room_length) - 3)), + subplot_center.y, + ); + painter.rotated_sprite( + c_pos.with_z(floor_level), + SpriteKind::WallTableArabic, + 6 - (4 * d) as u8, + ); + painter.rotated_sprite( + c_pos.with_z(floor_level + 1), + match (RandomField::new(0) + .get(c_pos.with_z(floor_level))) + % 5 + { + 0 => SpriteKind::MelonCut, + 1 => SpriteKind::JugAndBowlArabic, + 2 => SpriteKind::Bowl, + 3 => SpriteKind::JugArabic, + _ => SpriteKind::VialEmpty, + }, + 6 - (4 * d) as u8, + ); + } + // CookingPot, Cauldron + painter.sprite( + (subplot_center - 2).with_z(floor_level), + SpriteKind::CookingPot, + ); + painter.sprite( + (subplot_center + 2).with_z(floor_level), + SpriteKind::Cauldron, + ); + }, + 1 => { + // living room + // canapes + for d in 0..2 { + let a_pos = Vec2::new( + subplot_center.x - 4 + (d * 7), + subplot_center.y - room_length + + 5 + + (d * ((2 * room_length) - 9)), + ); + painter.rotated_sprite( + a_pos.with_z(floor_level), + SpriteKind::CanapeArabic, + (4 * d) as u8, + ); + + // other sprites + let b_pos = Vec2::new( + subplot_center.x + 3 - (d * 7), + subplot_center.y - room_length + + 5 + + (d * ((2 * room_length) - 9)), + ); + painter.sprite( + b_pos.with_z(floor_level), + match (RandomField::new(0) + .get(b_pos.with_z(floor_level - d))) + % 6 + { + 0 => SpriteKind::TableArabicLarge, + 1 => SpriteKind::DecorSetArabic, + 2 => SpriteKind::DrawerSmall, + 3 => SpriteKind::CoatRack, + 4 => SpriteKind::TableArabicSmall, + _ => SpriteKind::SepareArabic, + }, + ) + } + }, + _ => { + // bath + // wall tables with varying items + for d in 0..2 { + // wall tables with varying items + let a_pos = Vec2::new( + subplot_center.x - room_length + + 1 + + (d * ((2 * room_length) - 3)), + subplot_center.y, + ); + painter.rotated_sprite( + a_pos.with_z(floor_level), + SpriteKind::WallTableArabic, + 6 - (4 * d) as u8, + ); + painter.rotated_sprite( + a_pos.with_z(floor_level + 1), + match (RandomField::new(0) + .get(a_pos.with_z(floor_level))) + % 4 + { + 0 => SpriteKind::Bowl, + 1 => SpriteKind::VialEmpty, + 2 => SpriteKind::JugArabic, + _ => SpriteKind::JugAndBowlArabic, + }, + 6 - (4 * d) as u8, + ); + // fountain + painter.sprite( + subplot_center.with_z(floor_level), + SpriteKind::FountainArabic, + ) + } + }, + } + } + // Wall Lamps + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 1, + subplot_center.y - room_length + 4, + ) + .with_z(floor_level + 4), + max: Vec2::new( + subplot_center.x + 1, + subplot_center.y + room_length + 1, + ) + .with_z(floor_level + 5), + }) + .without( + painter.aabb(Aabb { + min: Vec2::new( + subplot_center.x - 1, + subplot_center.y - room_length + 5, + ) + .with_z(floor_level + 4), + max: Vec2::new( + subplot_center.x + 1, + subplot_center.y + room_length, + ) + .with_z(floor_level + 5), + }), + ) + .fill(Fill::Block( + Block::air(SpriteKind::WallLampSmall).with_ori(4).unwrap(), + )); + } + }, + SubPlotKind::Library => { + // Library with stairway + let tower_length = self.diameter / 14; + let tower_height = 23; + let bldg_a_center = Vec2::new( + subplot_center.x - (2 * tower_length) + 1, + subplot_center.y, + ); + let bldg_b_center = + Vec2::new(subplot_center.x + tower_length + 1, subplot_center.y); + // Library windowsills + let sq_corner = Vec2::new( + bldg_b_center.x - 3, + bldg_b_center.y - (2 * tower_length) + 1, + ); + for dir in SQUARE_4 { + for h in 0..2 { + for n in 0..2 { + let window_sill_pos = Vec2::new( + sq_corner.x - 1 + (dir.x * 7), + sq_corner.y - 2 + (dir.y * ((4 * tower_length) + 1)), + ); + painter + .aabb(Aabb { + min: Vec2::new( + window_sill_pos.x - 1, + window_sill_pos.y, + ) + .with_z( + base + 1 + (n * (tower_height / 2)) + (h * 4), + ), + max: Vec2::new( + window_sill_pos.x + 1, + window_sill_pos.y + 1, + ) + .with_z( + base + 2 + (n * (tower_height / 2)) + (h * 4), + ), + }) + .fill(wood.clone()); + } + } + } + for h in 0..2 { + for n in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + (2 * tower_length), + bldg_b_center.y - 4, + ) + .with_z(base + 1 + (n * (tower_height / 2)) + (h * 4)), + max: Vec2::new( + bldg_b_center.x + (2 * tower_length) + 1, + bldg_b_center.y + 4, + ) + .with_z(base + 2 + (n * (tower_height / 2)) + (h * 4)), + }) + .fill(wood.clone()); + } + } + // Library + painter + .aabb(Aabb { + min: (bldg_b_center - (2 * tower_length)).with_z(base), + max: (bldg_b_center + (2 * tower_length)) + .with_z(base + tower_height), + }) + .fill(sandstone.clone()); + // Library windows + for dir in SQUARE_4 { + for h in 0..2 { + for n in 0..2 { + let window_pos = Vec2::new( + sq_corner.x - 1 + (dir.x * 7), + sq_corner.y - 1 + (dir.y * ((4 * tower_length) - 1)), + ); + painter + .aabb(Aabb { + min: Vec2::new(window_pos.x - 1, window_pos.y) + .with_z( + base + 2 + + (n * (tower_height / 2)) + + (h * 4), + ), + max: Vec2::new(window_pos.x + 1, window_pos.y + 1) + .with_z( + base + 5 + + (n * (tower_height / 2)) + + (h * 4), + ), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WindowArabic) + .with_ori(2) + .unwrap(), + )); + } + } + } + for h in 0..2 { + for n in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + (2 * tower_length) - 1, + bldg_b_center.y - 4, + ) + .with_z(base + 2 + (n * (tower_height / 2)) + (h * 4)), + max: Vec2::new( + bldg_b_center.x + (2 * tower_length), + bldg_b_center.y + 4, + ) + .with_z(base + 5 + (n * (tower_height / 2)) + (h * 4)), + }) + .without( + painter.aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + (2 * tower_length) - 1, + bldg_b_center.y - 2, + ) + .with_z( + base + 2 + (n * (tower_height / 2)) + (h * 4), + ), + max: Vec2::new( + bldg_b_center.x + (2 * tower_length), + bldg_b_center.y + 2, + ) + .with_z( + base + 5 + (n * (tower_height / 2)) + (h * 4), + ), + }), + ) + .fill(Fill::Block( + Block::air(SpriteKind::WindowArabic) + .with_ori(4) + .unwrap(), + )); + } + } + // roof carve out + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x - (2 * tower_length) + 2, + bldg_b_center.y - (2 * tower_length) + 1, + ) + .with_z(base + tower_height - 1), + max: Vec2::new( + bldg_b_center.x + (2 * tower_length) - 1, + bldg_b_center.y + (2 * tower_length) - 1, + ) + .with_z(base + tower_height), + }) + .clear(); + + // Clear Library + painter + .aabb(Aabb { + min: (bldg_b_center - (2 * tower_length) + 1).with_z(base), + max: (bldg_b_center + (2 * tower_length) - 1) + .with_z(base + tower_height - 2), + }) + .clear(); + // Library Floor + painter + .aabb(Aabb { + min: (bldg_b_center - (2 * tower_length)) + .with_z(base + (tower_height / 2) - 1), + max: (bldg_b_center + (2 * tower_length)) + .with_z(base + (tower_height / 2)), + }) + .fill(sandstone.clone()); + // furniture + for h in 0..2 { + for dir in NEIGHBORS { + let sprite_pos = (bldg_b_center + + (dir * ((2 * tower_length) - 4))) + .with_z(base + ((tower_height / 2) * h)); + painter.sprite( + sprite_pos, + match (RandomField::new(0).get(sprite_pos)) % 6 { + 0 => SpriteKind::DrawerSmall, + 1 => SpriteKind::ChairSingle, + 2 => SpriteKind::CoatRack, + 3 => SpriteKind::Bench, + 4 => SpriteKind::ChairDouble, + _ => SpriteKind::TableDining, + }, + ); + } + + for d in 0..2 { + for e in 0..2 { + // bookshelfs + let a_pos = Vec2::new( + bldg_b_center.x - 3 + (e * 6), + bldg_b_center.y - (2 * tower_length) + + 1 + + (d * ((2 * (2 * tower_length)) - 3)), + ); + painter.rotated_sprite( + a_pos.with_z(base + 1 + ((tower_height / 2) * h)), + SpriteKind::BookshelfArabic, + 4 - (4 * d) as u8, + ); + } + } + } + // Library Wall Lamps + for h in 0..2 { + for n in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + (2 * tower_length) - 2, + bldg_b_center.y - 1, + ) + .with_z(base + 4 + (n * (tower_height / 2)) + (h * 4)), + max: Vec2::new( + bldg_b_center.x + (2 * tower_length) - 1, + bldg_b_center.y + 1, + ) + .with_z(base + 5 + (n * (tower_height / 2)) + (h * 4)), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WallLampSmall) + .with_ori(6) + .unwrap(), + )); + } + } + // Library Roof Overhangs + for d in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x - (2 * tower_length) + 1, + bldg_b_center.y + (2 * tower_length) + - (d * ((4 * tower_length) + 1)), + ) + .with_z(base + tower_height - 1), + max: Vec2::new( + bldg_b_center.x + (2 * tower_length) - 1, + bldg_b_center.y + (2 * tower_length) + 1 + - (d * ((4 * tower_length) + 1)), + ) + .with_z(base + tower_height), + }) + .fill(wood.clone()); + } + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + (2 * tower_length), + bldg_b_center.y - (2 * tower_length) + 1, + ) + .with_z(base + tower_height - 1), + max: Vec2::new( + bldg_b_center.x + (2 * tower_length) + 1, + bldg_b_center.y + (2 * tower_length) - 1, + ) + .with_z(base + tower_height), + }) + .fill(wood.clone()); + // Stairs to Tower Top + painter + .ramp( + Aabb { + min: Vec2::new( + bldg_b_center.x - (2 * tower_length) + 2, + bldg_b_center.y - 1, + ) + .with_z(base + tower_height - 1), + max: Vec2::new( + bldg_b_center.x - (2 * tower_length) + 4, + bldg_b_center.y + 1, + ) + .with_z(base + tower_height + 1), + }, + 2, + Dir::NegX, + ) + .fill(sandstone.clone()); + // Library Top Room + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x - tower_length + 2, + bldg_b_center.y - tower_length, + ) + .with_z(base + tower_height - 1), + max: Vec2::new( + bldg_b_center.x + tower_length + 2, + bldg_b_center.y + tower_length, + ) + .with_z(base + tower_height + tower_length), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x - tower_length + 3, + bldg_b_center.y - tower_length + 1, + ) + .with_z(base + tower_height - 1), + max: Vec2::new( + bldg_b_center.x + tower_length + 1, + bldg_b_center.y + tower_length - 1, + ) + .with_z(base + tower_height + tower_length - 2), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x - tower_length + 3, + bldg_b_center.y - tower_length + 1, + ) + .with_z(base + tower_height + tower_length - 1), + max: Vec2::new( + bldg_b_center.x + tower_length + 1, + bldg_b_center.y + tower_length - 1, + ) + .with_z(base + tower_height + tower_length), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + 1, + bldg_b_center.y - tower_length - 1, + ) + .with_z(base + tower_height - 1), + max: Vec2::new( + bldg_b_center.x + 3, + bldg_b_center.y + tower_length + 1, + ) + .with_z(base + tower_height + 2), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x - tower_length + 1, + bldg_b_center.y - 1, + ) + .with_z(base + tower_height - 1), + max: Vec2::new( + bldg_b_center.x + tower_length + 3, + bldg_b_center.y + 1, + ) + .with_z(base + tower_height + 2), + }) + .clear(); + + // Library Top Room Overhangs + for d in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x - tower_length + 3, + bldg_b_center.y + tower_length + - (d * ((2 * tower_length) + 1)), + ) + .with_z(base + tower_height + tower_length - 1), + max: Vec2::new( + bldg_b_center.x + tower_length + 1, + bldg_b_center.y + tower_length + 1 + - (d * ((2 * tower_length) + 1)), + ) + .with_z(base + tower_height + tower_length), + }) + .fill(wood.clone()); + } + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + tower_length + 2, + bldg_b_center.y - tower_length + 1, + ) + .with_z(base + tower_height + tower_length - 1), + max: Vec2::new( + bldg_b_center.x + tower_length + 3, + bldg_b_center.y + tower_length - 1, + ) + .with_z(base + tower_height + tower_length), + }) + .fill(wood.clone()); + // Library Top Room Roof Ornament + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + tower_length + 1, + bldg_b_center.y - tower_length, + ) + .with_z(base + tower_height + tower_length), + max: Vec2::new( + bldg_b_center.x + tower_length + 2, + bldg_b_center.y + tower_length, + ) + .with_z(base + tower_height + tower_length + 2), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + tower_length + 1, + bldg_b_center.y - tower_length + 2, + ) + .with_z(base + tower_height + tower_length + 2), + max: Vec2::new( + bldg_b_center.x + tower_length + 2, + bldg_b_center.y + tower_length - 2, + ) + .with_z(base + tower_height + tower_length + 3), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + bldg_b_center.x + tower_length + 1, + bldg_b_center.y - 1, + ) + .with_z(base + tower_height + tower_length + 3), + max: Vec2::new( + bldg_b_center.x + tower_length + 2, + bldg_b_center.y + 1, + ) + .with_z(base + tower_height + tower_length + 4), + }) + .fill(sandstone.clone()); + // Stairway Tower windowsills + for h in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + bldg_a_center.x - 1, + bldg_a_center.y - tower_length - 1, + ) + .with_z(base + (tower_height / 2) - 4 + (h * 6)), + max: Vec2::new( + bldg_a_center.x + 1, + bldg_a_center.y + tower_length + 1, + ) + .with_z(base + (tower_height / 2) - 3 + (h * 6)), + }) + .fill(wood.clone()); + } + // Stairway Tower base + painter + .aabb(Aabb { + min: (bldg_a_center - tower_length - 1).with_z(base), + max: (bldg_a_center + tower_length + 1).with_z(base + 6), + }) + .fill(sandstone.clone()); + // Tower + painter + .aabb(Aabb { + min: (bldg_a_center - tower_length).with_z(base), + max: (bldg_a_center + tower_length).with_z(base + tower_height), + }) + .fill(sandstone.clone()); + // Stairway Tower windows + for h in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + bldg_a_center.x - 1, + bldg_a_center.y - tower_length, + ) + .with_z(base + (tower_height / 2) - 3 + (h * 6)), + max: Vec2::new( + bldg_a_center.x + 1, + bldg_a_center.y + tower_length, + ) + .with_z(base + (tower_height / 2) + (h * 6)), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WindowArabic).with_ori(2).unwrap(), + )); + } + // Stairway Tower entry + painter + .aabb(Aabb { + min: Vec2::new( + bldg_a_center.x - 2, + bldg_a_center.y - tower_length - 2, + ) + .with_z(base), + max: Vec2::new( + bldg_a_center.x + 2, + bldg_a_center.y + tower_length + 2, + ) + .with_z(base + 5), + }) + .fill(sandstone.clone()); + // clear Stairway Tower entry + painter + .aabb(Aabb { + min: Vec2::new( + bldg_a_center.x - 1, + bldg_a_center.y - tower_length - 5, + ) + .with_z(base), + max: Vec2::new( + bldg_a_center.x + 1, + bldg_a_center.y + tower_length + 5, + ) + .with_z(base + 4), + }) + .clear(); + // Stairway Tower Entry Lamps + for d in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + bldg_a_center.x - 1, + bldg_a_center.y - tower_length - 3 + + (d * ((2 * tower_length) + 6)), + ) + .with_z(base + 4), + max: Vec2::new( + bldg_a_center.x + 1, + bldg_a_center.y - tower_length - 2 + + (d * ((2 * tower_length) + 4)), + ) + .with_z(base + 5), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WallLampSmall) + .with_ori(0 + (4 * d) as u8) + .unwrap(), + )); + } + // Library entries from Stairway Tower + for h in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + bldg_a_center.x + tower_length - 1, + bldg_a_center.y - 1, + ) + .with_z(base + (h * (tower_height / 2))), + max: Vec2::new( + bldg_a_center.x + tower_length + 1, + bldg_a_center.y + 1, + ) + .with_z(base + (h * (tower_height / 2)) + 3), + }) + .clear(); + } + // Stairway Tower Platform + painter + .aabb(Aabb { + min: (bldg_a_center - tower_length - 2) + .with_z(base + tower_height), + max: (bldg_a_center + tower_length + 2) + .with_z(base + tower_height + 3), + }) + .fill(sandstone.clone()); + + painter + .aabb(Aabb { + min: (bldg_a_center - tower_length - 1) + .with_z(base + tower_height + 2), + max: (bldg_a_center + tower_length + 1) + .with_z(base + tower_height + 3), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new( + bldg_a_center.x + tower_length + 1, + bldg_a_center.y - 1, + ) + .with_z(base + tower_height + 2), + max: Vec2::new( + bldg_a_center.x + tower_length + 2, + bldg_a_center.y + 1, + ) + .with_z(base + tower_height + 3), + }) + .clear(); + // clear Tower + let tower_clear = painter.aabb(Aabb { + min: (bldg_a_center - tower_length + 1).with_z(base), + max: (bldg_a_center + tower_length - 1) + .with_z(base + tower_height + 3), + }); + tower_clear.clear(); + // stairway + let stair_radius = tower_length + 1; + let stairs_clear = painter.aabb(Aabb { + min: (bldg_a_center - stair_radius as i32).with_z(base), + max: (bldg_a_center + stair_radius as i32) + .with_z(base + tower_height + 2), + }); + stairs_clear + .sample(spiral_staircase( + bldg_a_center.with_z(base + tower_height + 2), + stair_radius as f32, + 0.5, + ((2 * tower_length) - 1) as f32, + )) + .intersect(tower_clear) + .fill(sandstone.clone()); + // make some room for base entries + painter + .aabb(Aabb { + min: (bldg_a_center - tower_length).with_z(base), + max: (bldg_a_center + tower_length).with_z(base + 5), + }) + .without(painter.aabb(Aabb { + min: (bldg_a_center - tower_length + 1).with_z(base), + max: (bldg_a_center + tower_length - 1).with_z(base + 5), + })) + .clear(); + }, + SubPlotKind::WatchTower(tower) => { + // WatchTower Windowsills + for h in 0..4 { + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 1, + subplot_center.y - tower.length - 1, + ) + .with_z(base + 8 + (h * (tower.height / 5))), + max: Vec2::new( + subplot_center.x + 1, + subplot_center.y + tower.length + 1, + ) + .with_z(base + 9 + (h * (tower.height / 5))), + }) + .fill(wood.clone()); + } + for h in 0..4 { + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - tower.length - 1, + subplot_center.y - 1, + ) + .with_z(base + 8 + (h * (tower.height / 5))), + max: Vec2::new( + subplot_center.x + tower.length + 1, + subplot_center.y + 1, + ) + .with_z(base + 9 + (h * (tower.height / 5))), + }) + .fill(wood.clone()); + } + // Watch Tower base + painter + .aabb(Aabb { + min: (subplot_center - tower.length - 1).with_z(base), + max: (subplot_center + tower.length + 1).with_z(base + 6), + }) + .fill(sandstone.clone()); + // WatchTower + painter + .aabb(Aabb { + min: (subplot_center - tower.length).with_z(base), + max: (subplot_center + tower.length) + .with_z(base + tower.height), + }) + .fill(sandstone.clone()); + // WatchTower Windows + for h in 0..4 { + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 1, + subplot_center.y - tower.length, + ) + .with_z(base + 9 + (h * (tower.height / 5))), + max: Vec2::new( + subplot_center.x + 1, + subplot_center.y + tower.length, + ) + .with_z(base + 12 + (h * (tower.height / 5))), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WindowArabic).with_ori(2).unwrap(), + )); + } + for h in 0..4 { + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - tower.length, + subplot_center.y - 1, + ) + .with_z(base + 9 + (h * (tower.height / 5))), + max: Vec2::new( + subplot_center.x + tower.length, + subplot_center.y + 1, + ) + .with_z(base + 12 + (h * (tower.height / 5))), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WindowArabic).with_ori(4).unwrap(), + )); + } + // Watch Tower entries + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 2, + subplot_center.y - tower.length - 2, + ) + .with_z(base), + max: Vec2::new( + subplot_center.x + 2, + subplot_center.y + tower.length + 2, + ) + .with_z(base + 5), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 1, + subplot_center.y - tower.length - 2, + ) + .with_z(base), + max: Vec2::new( + subplot_center.x + 1, + subplot_center.y + tower.length + 2, + ) + .with_z(base + 4), + }) + .clear(); + // WatchTower Entry Lamps + for d in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 1, + subplot_center.y - tower.length - 3 + + (d * ((2 * tower.length) + 6)), + ) + .with_z(base + 4), + max: Vec2::new( + subplot_center.x + 1, + subplot_center.y - tower.length - 2 + + (d * ((2 * tower.length) + 4)), + ) + .with_z(base + 5), + }) + .fill(Fill::Block( + Block::air(SpriteKind::WallLampSmall) + .with_ori(0 + (4 * d) as u8) + .unwrap(), + )); + } + // Watchtower Platform + painter + .aabb(Aabb { + min: (subplot_center - tower.length - 2) + .with_z(base + tower.height), + max: (subplot_center + tower.length + 2) + .with_z(base + tower.height + 4), + }) + .fill(sandstone.clone()); + + painter + .aabb(Aabb { + min: (subplot_center - tower.length - 1) + .with_z(base + tower.height + 2), + max: (subplot_center + tower.length + 1) + .with_z(base + tower.height + 4), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - tower.length - 2, + subplot_center.y - 2, + ) + .with_z(base + tower.height + 3), + max: Vec2::new( + subplot_center.x + tower.length + 2, + subplot_center.y + 2, + ) + .with_z(base + tower.height + 4), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - 2, + subplot_center.y - tower.length - 2, + ) + .with_z(base + tower.height + 3), + max: Vec2::new( + subplot_center.x + 2, + subplot_center.y + tower.length + 2, + ) + .with_z(base + tower.height + 4), + }) + .clear(); + + // clear Tower + let tower_clear = painter.aabb(Aabb { + min: (subplot_center - tower.length + 1).with_z(base), + max: (subplot_center + tower.length - 1) + .with_z(base + tower.height + 3), + }); + tower_clear.clear(); + // stairway + let stair_radius = tower.length + 1; + let stairs_clear = painter.aabb(Aabb { + min: (subplot_center - stair_radius as i32).with_z(base), + max: (subplot_center + stair_radius as i32) + .with_z(base + tower.height + 2), + }); + stairs_clear + .sample(spiral_staircase( + subplot_center.with_z(base + tower.height + 2), + stair_radius as f32, + 0.5, + (2 * tower.length) as f32, + )) + .intersect(tower_clear) + .fill(sandstone.clone()); + //make some room for base entries + painter + .aabb(Aabb { + min: (subplot_center - tower.length).with_z(base), + max: (subplot_center + tower.length).with_z(base + 5), + }) + .without(painter.aabb(Aabb { + min: (subplot_center - tower.length + 1).with_z(base), + max: (subplot_center + tower.length - 1).with_z(base + 5), + })) + .clear(); + // clear some steps at the bottom for tower entries + painter + .aabb(Aabb { + min: (subplot_center - tower.length + 1).with_z(base + 1), + max: (subplot_center + tower.length - 1).with_z(base + 2), + }) + .without(painter.aabb(Aabb { + min: (subplot_center - tower.length + 2).with_z(base + 1), + max: (subplot_center + tower.length - 2).with_z(base + 2), + })) + .clear(); + }, + SubPlotKind::AnimalShed => { + // fenced animal shed + let shed_length = self.diameter / 14; + // small fence + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - (2 * shed_length), + subplot_center.y - (2 * shed_length) - 1, + ) + .with_z(base), + max: Vec2::new( + subplot_center.x + (2 * shed_length) + 2, + subplot_center.y + (2 * shed_length) + 1, + ) + .with_z(base + 2), + }) + .without( + painter.aabb(Aabb { + min: Vec2::new( + subplot_center.x - (2 * shed_length) + 1, + subplot_center.y - (2 * shed_length), + ) + .with_z(base), + max: Vec2::new( + subplot_center.x + (2 * shed_length) + 1, + subplot_center.y + (2 * shed_length), + ) + .with_z(base + 2), + }), + ) + .fill(sandstone_broken.clone()); + // shed + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - shed_length + 2, + subplot_center.y - shed_length, + ) + .with_z(base), + max: Vec2::new( + subplot_center.x + shed_length + 2, + subplot_center.y + shed_length, + ) + .with_z(base + shed_length + 1), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - shed_length + 3, + subplot_center.y - shed_length + 1, + ) + .with_z(base), + max: Vec2::new( + subplot_center.x + shed_length + 1, + subplot_center.y + shed_length - 1, + ) + .with_z(base + shed_length - 1), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - shed_length + 3, + subplot_center.y - shed_length + 1, + ) + .with_z(base + shed_length), + max: Vec2::new( + subplot_center.x + shed_length + 1, + subplot_center.y + shed_length - 1, + ) + .with_z(base + shed_length + 1), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x + 1, + subplot_center.y - shed_length - 1, + ) + .with_z(base), + max: Vec2::new( + subplot_center.x + 3, + subplot_center.y + shed_length + 1, + ) + .with_z(base + 3), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - shed_length + 1, + subplot_center.y - 1, + ) + .with_z(base), + max: Vec2::new( + subplot_center.x + shed_length + 3, + subplot_center.y + 1, + ) + .with_z(base + 3), + }) + .clear(); + // Shed Overhangs + for d in 0..2 { + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x - shed_length + 3, + subplot_center.y + shed_length + - (d * ((2 * shed_length) + 1)), + ) + .with_z(base + shed_length - 1), + max: Vec2::new( + subplot_center.x + shed_length + 1, + subplot_center.y + shed_length + 1 + - (d * ((2 * shed_length) + 1)), + ) + .with_z(base + shed_length), + }) + .fill(wood.clone()); + } + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x + shed_length + 2, + subplot_center.y - shed_length + 1, + ) + .with_z(base + shed_length - 1), + max: Vec2::new( + subplot_center.x + shed_length + 3, + subplot_center.y + shed_length - 1, + ) + .with_z(base + shed_length), + }) + .fill(wood.clone()); + // Shed Roof Ornament + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x + shed_length + 1, + subplot_center.y - shed_length, + ) + .with_z(base + shed_length), + max: Vec2::new( + subplot_center.x + shed_length + 2, + subplot_center.y + shed_length, + ) + .with_z(base + shed_length + 2), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x + shed_length + 1, + subplot_center.y - shed_length + 2, + ) + .with_z(base + shed_length + 2), + max: Vec2::new( + subplot_center.x + shed_length + 2, + subplot_center.y + shed_length - 2, + ) + .with_z(base + shed_length + 3), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new( + subplot_center.x + shed_length + 1, + subplot_center.y - 1, + ) + .with_z(base + shed_length + 3), + max: Vec2::new( + subplot_center.x + shed_length + 2, + subplot_center.y + 1, + ) + .with_z(base + shed_length + 4), + }) + .fill(sandstone.clone()); + }, + SubPlotKind::PalmTree => { + // Palm + painter + .cylinder(Aabb { + min: (subplot_center - 8).with_z(base), + max: (subplot_center + 14).with_z(base + 1), + }) + .fill(sandstone.clone()); + painter + .cylinder(Aabb { + min: (subplot_center - 7).with_z(base), + max: (subplot_center + 13).with_z(base + 1), + }) + .fill(Fill::Brick(BlockKind::Rock, Rgb::new(235, 178, 99), 12)); + for p in 0..2 { + let palm_pos = (subplot_center + 3 + (2 * p)).with_z(base); + lazy_static! { + pub static ref PALM: AssetHandle = + PrefabStructure::load_group("trees.palms"); + } + let rng = RandomField::new(0).get(palm_pos) % 10; + let palm = PALM.read(); + let palm = palm[rng as usize % palm.len()].clone(); + painter + .prim(Primitive::Prefab(Box::new(palm.clone()))) + .translate(palm_pos) + .fill(Fill::Prefab(Box::new(palm), palm_pos, rng)); + } + }, + } + } + }, + } + } +} diff --git a/world/src/site2/plot/desert_city_temple.rs b/world/src/site2/plot/desert_city_temple.rs new file mode 100644 index 0000000000..c30e516808 --- /dev/null +++ b/world/src/site2/plot/desert_city_temple.rs @@ -0,0 +1,679 @@ +use super::*; +use crate::{ + util::{RandomField, Sampler}, + Land, +}; +use common::terrain::{Block, BlockKind}; +use rand::prelude::*; +use std::sync::Arc; +use vek::*; + +/// Represents house data generated by the `generate()` method +pub struct DesertCityTemple { + /// Axis aligned bounding region for the house + bounds: Aabr, + /// Approximate altitude of the door tile + pub(crate) alt: i32, +} + +impl DesertCityTemple { + pub fn generate( + land: &Land, + _rng: &mut impl Rng, + site: &Site, + door_tile: Vec2, + door_dir: Vec2, + tile_aabr: Aabr, + ) -> 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(door_tile + door_dir)) as i32, + } + } +} + +impl Structure for DesertCityTemple { + fn render(&self, _site: &Site, _land: &Land, painter: &Painter) { + let sandstone = Fill::Sampling(Arc::new(|center| { + Some(match (RandomField::new(0).get(center)) % 37 { + 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)), + 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)), + 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)), + 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)), + _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)), + }) + })); + let sandstone_broken = Fill::Sampling(Arc::new(|center| { + Some(match (RandomField::new(0).get(center)) % 42 { + 0..=8 => Block::new(BlockKind::Rock, Rgb::new(245, 212, 129)), + 9..=17 => Block::new(BlockKind::Rock, Rgb::new(246, 214, 133)), + 18..=26 => Block::new(BlockKind::Rock, Rgb::new(247, 216, 136)), + 27..=35 => Block::new(BlockKind::Rock, Rgb::new(248, 219, 142)), + 36..=40 => Block::new(BlockKind::Air, Rgb::new(0, 0, 0)), + _ => Block::new(BlockKind::Rock, Rgb::new(235, 178, 99)), + }) + })); + let base = self.alt + 1; + let center = self.bounds.center(); + let diameter = + 10 + (self.bounds.max.x - self.bounds.min.x).min(self.bounds.max.y - self.bounds.min.y); + // Fence + painter + .aabb(Aabb { + min: (self.bounds.min + 1).with_z(base - 20), + max: (self.bounds.max).with_z(base + 2), + }) + .without(painter.aabb(Aabb { + min: (self.bounds.min + 2).with_z(base - 20), + max: (self.bounds.max - 1).with_z(base + 2), + })) + .fill(sandstone_broken); + painter + .aabb(Aabb { + min: Vec2::new(self.bounds.min.x + 1, center.y - 8).with_z(base), + max: Vec2::new(self.bounds.max.x, center.y + 8).with_z(base + 7), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 7, self.bounds.min.y + 1).with_z(base), + max: Vec2::new(center.x + 9, self.bounds.max.y).with_z(base + 7), + }) + .clear(); + // Foundation + painter + .aabb(Aabb { + min: (self.bounds.min + 1).with_z(base - 20), + max: (self.bounds.max).with_z(base), + }) + .fill(sandstone.clone()); + + // Temple + let temple_color = match (RandomField::new(0).get((center - 3).with_z(base))) % 2 { + 0 => Fill::Brick(BlockKind::Rock, Rgb::new(100, 101, 250), 3), + _ => Fill::Brick(BlockKind::Rock, Rgb::new(63, 49, 63), 24), + }; + let temple_size = diameter / 3; + let floaty_block = Fill::Brick(BlockKind::GlowingWeakRock, Rgb::new(253, 240, 149), 2); + let temple = Aabb { + min: (center - temple_size).with_z(base), + max: (center + temple_size - 1).with_z(base + temple_size + 1), + }; + + // temple with roof carve out + painter.aabb(temple).fill(sandstone.clone()); + // roof decoration + painter + .aabb(Aabb { + min: (center - temple_size - 1).with_z(base + temple_size + 1), + max: (center + temple_size).with_z(base + temple_size + 4), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: (center - temple_size).with_z(base + temple_size + 2), + max: (center + temple_size - 1).with_z(base + temple_size + 4), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size - 1, center.y - (temple_size / 4) - 2) + .with_z(base + temple_size + 3), + max: Vec2::new(center.x + temple_size, center.y + (temple_size / 4) + 1) + .with_z(base + temple_size + 4), + }) + .clear(); + painter + .aabb(Aabb { + min: Vec2::new(center.x - (temple_size / 4) - 2, center.y - temple_size - 1) + .with_z(base + temple_size + 3), + max: Vec2::new(center.x + (temple_size / 4) + 1, center.y + temple_size) + .with_z(base + temple_size + 4), + }) + .clear(); + + // roof carve out + painter + .aabb(Aabb { + min: (center - temple_size + 1).with_z(base + temple_size), + max: (center + temple_size - 2).with_z(base + temple_size + 3), + }) + .clear(); + //corner pillars + for dir in SQUARE_4 { + let corner_pos = center - temple_size; + let pillar_center = corner_pos + (dir * ((2 * temple_size) - 1)); + // Pillar foot + painter + .aabb(Aabb { + min: (pillar_center - 4).with_z(base), + max: (pillar_center + 4).with_z(base + 4), + }) + .fill(sandstone.clone()); + + // Pillar + painter + .aabb(Aabb { + min: (pillar_center - 3).with_z(base + 4), + max: (pillar_center + 3).with_z(base + temple_size + 3), + }) + .fill(sandstone.clone()); + // Pillar Top + painter + .aabb(Aabb { + min: (pillar_center - 4).with_z(base + temple_size + 3), + max: (pillar_center + 4).with_z(base + temple_size + 6), + }) + .fill(sandstone.clone()); + + painter + .aabb(Aabb { + min: (pillar_center - 3).with_z(base + temple_size + 4), + max: (pillar_center + 3).with_z(base + temple_size + 6), + }) + .clear(); + + // clear parts of pillar top for roof carve out + painter + .aabb(Aabb { + min: (center - temple_size).with_z(base + temple_size + 3), + max: (center + temple_size - 1).with_z(base + temple_size + 6), + }) + .clear() + } + + // temple decor carve out + // apply three carve options (horizontal, vertical, none) with two spread + // options (for horizontal and vertical) + for s in 0..2 { + // decor carve out style + let spread_select = ((RandomField::new(0).get((center - 1).with_z(base))) % 2) as i32; + let spread = 3 * spread_select; + let carve_style = ((RandomField::new(0).get((center - 2 - s).with_z(base))) % 3) as i32; + match carve_style { + //vertical + 0 => { + // decor carve outs1 + for c in 0..(temple_size - 5) { + painter + .aabb(Aabb { + min: Vec2::new( + center.x - temple_size + 5 + (2 * c), + center.y - temple_size, + ) + .with_z(base + 2), + max: Vec2::new( + center.x - temple_size + 6 + (2 * c), + center.y + temple_size - 1, + ) + .with_z(base + temple_size - 1), + }) + .clear(); + } + for c in 0..(temple_size - 5) { + painter + .aabb(Aabb { + min: Vec2::new( + center.x - temple_size, + center.y - temple_size + 5 + (2 * c), + ) + .with_z(base + 2), + max: Vec2::new( + center.x + temple_size - 1, + center.y - temple_size + 6 + (2 * c), + ) + .with_z(base + temple_size - 1), + }) + .clear(); + } + }, + 1 => { + // horizontal + for c in 0..((temple_size / 2) - 2) { + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size + 5, center.y - temple_size) + .with_z(base + 2 + ((spread + 2) * c)), + max: Vec2::new( + center.x + temple_size - 6, + center.y + temple_size - 1, + ) + .with_z(base + 3 + ((spread + 2) * c) + spread_select), + }) + .clear(); + } + for c in 0..((temple_size / 2) - 2) { + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - temple_size + 5) + .with_z(base + 2 + ((spread + 2) * c)), + max: Vec2::new( + center.x + temple_size - 1, + center.y + temple_size - 6, + ) + .with_z(base + 3 + ((spread + 2) * c) + spread_select), + }) + .clear(); + } + }, + _ => {}, + } + } + // color inlays1 + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size + 2, center.y - temple_size + 2) + .with_z(base + 2), + max: Vec2::new(center.x + temple_size - 3, center.y + temple_size - 3) + .with_z(base + temple_size - 1), + }) + .without( + painter.aabb(Aabb { + min: Vec2::new(center.x - temple_size + 2, center.y - temple_size + 3) + .with_z(base + 2), + max: Vec2::new(center.x + temple_size - 3, center.y + temple_size - 4) + .with_z(base + temple_size - 1), + }), + ) + .fill(temple_color.clone()); + // color inlays2 + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size + 2, center.y - temple_size + 2) + .with_z(base + 2), + max: Vec2::new(center.x + temple_size - 3, center.y + temple_size - 3) + .with_z(base + temple_size - 1), + }) + .without( + painter.aabb(Aabb { + min: Vec2::new(center.x - temple_size + 3, center.y - temple_size + 2) + .with_z(base + 2), + max: Vec2::new(center.x + temple_size - 4, center.y + temple_size - 3) + .with_z(base + temple_size - 1), + }), + ) + .fill(temple_color.clone()); + // carve outside plane1 + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size + 6, center.y - temple_size).with_z(base + 3), + max: Vec2::new(center.x + temple_size - 7, center.y + temple_size - 1) + .with_z(base + temple_size - 2), + }) + .without( + painter.aabb(Aabb { + min: Vec2::new(center.x - temple_size + 6, center.y - temple_size + 1) + .with_z(base + 3), + max: Vec2::new(center.x + temple_size - 7, center.y + temple_size - 2) + .with_z(base + temple_size - 2), + }), + ) + .clear(); + // carve outside plane2 + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - temple_size + 6).with_z(base + 3), + max: Vec2::new(center.x + temple_size - 1, center.y + temple_size - 7) + .with_z(base + temple_size - 2), + }) + .without( + painter.aabb(Aabb { + min: Vec2::new(center.x - temple_size + 1, center.y - temple_size + 6) + .with_z(base + 3), + max: Vec2::new(center.x + temple_size - 2, center.y + temple_size - 7) + .with_z(base + temple_size - 2), + }), + ) + .clear(); + // Temple top + painter + .sphere(Aabb { + min: (center - temple_size + 3).with_z(base), + max: (center + temple_size - 4).with_z(base + (2 * temple_size) - 6), + }) + .without(painter.aabb(Aabb { + min: (center - temple_size + 2).with_z(base + 1), + max: (center + temple_size - 3).with_z(base + temple_size - 3), + })) + .without(painter.aabb(Aabb { + min: (center - temple_size + 1).with_z(base), + max: (center + temple_size - 1).with_z(base + temple_size), + })) + // carve temple top + .without(painter.sphere(Aabb { + min: (center - temple_size + 5).with_z(base + 2), + max: (center + temple_size - 6).with_z(base + (2 * temple_size) - 8), + })) + .fill(sandstone.clone()); + + // temple top decoration + // apply two decoration options (horizontal, vertical, none) with two spread + // options for hor/ver options + for s in 0..2 { + let top = painter + .sphere(Aabb { + min: (center - temple_size + 3).with_z(base), + max: (center + temple_size - 4).with_z(base + (2 * temple_size) - 6), + }) + .without(painter.sphere(Aabb { + min: (center - temple_size + 5).with_z(base + 2), + max: (center + temple_size - 6).with_z(base + (2 * temple_size) - 8), + })) + .without(painter.aabb(Aabb { + min: (center - temple_size + 1).with_z(base), + max: (center + temple_size - 2).with_z(base + temple_size + 1), + })); + // decor carve out style + let spread_select = + ((RandomField::new(0).get((center - 1 - s).with_z(base))) % 2) as i32; + let spread = 3 * spread_select; + let carve_style = ((RandomField::new(0).get((center - 2 - s).with_z(base))) % 3) as i32; + match carve_style { + //vertical + 0 => { + // decor carve outs1 + for c in 0..(temple_size - 2) { + painter + .aabb(Aabb { + min: Vec2::new( + center.x - temple_size + 2 + ((spread + 2) * c), + center.y - temple_size, + ) + .with_z(base + temple_size), + max: Vec2::new( + center.x - temple_size + 3 + ((spread + 2) * c) + spread_select, + center.y + temple_size - 1, + ) + .with_z(base + (2 * temple_size)), + }) + .intersect(top) + .fill(temple_color.clone()); + } + }, + 1 => { + for c in 0..(temple_size - 2) { + painter + .aabb(Aabb { + min: Vec2::new( + center.x - temple_size, + center.y - temple_size + 2 + ((spread + 2) * c), + ) + .with_z(base + temple_size), + max: Vec2::new( + center.x + temple_size - 1, + center.y - temple_size + 3 + ((spread + 2) * c) + spread_select, + ) + .with_z(base + (2 * temple_size)), + }) + .intersect(top) + .fill(temple_color.clone()); + } + }, + _ => {}, + } + } + + // round or rectangle entries + let entry_select = ((RandomField::new(0).get((center + 1).with_z(base))) % 2) as i32; + match entry_select { + 0 => { + //round Temple entries1 + painter + .aabb(Aabb { + min: Vec2::new(center.x - 5, center.y - temple_size).with_z(base), + max: Vec2::new(center.x + 4, center.y + temple_size - 1).with_z(base + 4), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 4, center.y - temple_size).with_z(base), + max: Vec2::new(center.x + 3, center.y + temple_size - 1).with_z(base + 4), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new(center.x - 4, center.y - temple_size).with_z(base + 4), + max: Vec2::new(center.x + 3, center.y + temple_size - 1).with_z(base + 7), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 3, center.y - temple_size).with_z(base + 4), + max: Vec2::new(center.x + 2, center.y + temple_size - 1).with_z(base + 7), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new(center.x - 3, center.y - temple_size).with_z(base + 7), + max: Vec2::new(center.x + 2, center.y + temple_size - 1).with_z(base + 9), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 2, center.y - temple_size).with_z(base + 7), + max: Vec2::new(center.x + 1, center.y + temple_size - 1).with_z(base + 9), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new(center.x - 2, center.y - temple_size).with_z(base + 9), + max: Vec2::new(center.x + 1, center.y + temple_size - 1).with_z(base + 10), + }) + .fill(sandstone.clone()); + + // round Temple entries2 + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - 5).with_z(base), + max: Vec2::new(center.x + temple_size - 1, center.y + 4).with_z(base + 4), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - 4).with_z(base), + max: Vec2::new(center.x + temple_size - 1, center.y + 3).with_z(base + 4), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - 4).with_z(base + 4), + max: Vec2::new(center.x + temple_size - 1, center.y + 3).with_z(base + 7), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - 3).with_z(base + 4), + max: Vec2::new(center.x + temple_size - 1, center.y + 2).with_z(base + 7), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - 3).with_z(base + 7), + max: Vec2::new(center.x + temple_size - 1, center.y + 2).with_z(base + 9), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - 2).with_z(base + 7), + max: Vec2::new(center.x + temple_size - 1, center.y + 1).with_z(base + 9), + }) + .clear(); + + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - 2).with_z(base + 9), + max: Vec2::new(center.x + temple_size - 1, center.y + 1).with_z(base + 10), + }) + .fill(sandstone.clone()); + }, + _ => { + //rectangle Temple entries1 + painter + .aabb(Aabb { + min: Vec2::new(center.x - 5, center.y - temple_size).with_z(base), + max: Vec2::new(center.x + 4, center.y + temple_size - 1).with_z(base + 8), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x - 3, center.y - temple_size).with_z(base), + max: Vec2::new(center.x + 2, center.y + temple_size - 1).with_z(base + 6), + }) + .clear(); + + //rectangle Temple entries1 + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - 5).with_z(base), + max: Vec2::new(center.x + temple_size - 1, center.y + 4).with_z(base + 8), + }) + .fill(sandstone.clone()); + painter + .aabb(Aabb { + min: Vec2::new(center.x - temple_size, center.y - 3).with_z(base), + max: Vec2::new(center.x + temple_size - 1, center.y + 2).with_z(base + 6), + }) + .clear(); + }, + } + // Temple Top Socket + painter + .cylinder(Aabb { + min: (center - temple_size + 3).with_z(base + temple_size), + max: (center + temple_size - 4).with_z(base + temple_size + 1), + }) + .without(painter.cylinder(Aabb { + min: (center - temple_size + 5).with_z(base + temple_size), + max: (center + temple_size - 6).with_z(base + temple_size + 1), + })) + .fill(sandstone.clone()); + + // carve temple + painter + .aabb(Aabb { + min: (center - temple_size + 4).with_z(base), + max: (center + temple_size - 5).with_z(base + temple_size - 1), + }) + .clear(); + // carve round opening to dome + painter + .cylinder(Aabb { + min: (center - temple_size + 6).with_z(base + temple_size - 1), + max: (center + temple_size - 7).with_z(base + temple_size), + }) + .clear(); + + // floating sculpture, placeholder for sun god + + painter + .sphere(Aabb { + min: (center - 3).with_z(base), + max: (center + 2).with_z(base + 5), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (center - 3).with_z(base + 4), + max: (center + 2).with_z(base + 9), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (center - 4).with_z(base + 8), + max: (center + 3).with_z(base + 13), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (center - 4).with_z(base + 14), + max: (center + 3).with_z(base + 15), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (center - 3).with_z(base + 16), + max: (center + 2).with_z(base + 21), + }) + .fill(floaty_block.clone()); + + // floating sculptures + for dir in SQUARE_4 { + let corner_pos = center - temple_size / 2; + let sclpt_center = corner_pos + (dir * (temple_size)); + + painter + .sphere(Aabb { + min: (sclpt_center - 2).with_z(base), + max: (sclpt_center + 1).with_z(base + 3), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (sclpt_center - 2).with_z(base + 2), + max: (sclpt_center + 1).with_z(base + 5), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (sclpt_center - 3).with_z(base + 5), + max: (sclpt_center + 2).with_z(base + 6), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (sclpt_center - 3).with_z(base + 7), + max: (sclpt_center + 2).with_z(base + 8), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (sclpt_center - 2).with_z(base + 9), + max: (sclpt_center + 1).with_z(base + 12), + }) + .fill(floaty_block.clone()); + } + + // floating sculpture, placeholder for sun god + + painter + .sphere(Aabb { + min: (center - 3).with_z(base), + max: (center + 2).with_z(base + 5), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (center - 3).with_z(base + 4), + max: (center + 2).with_z(base + 9), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (center - 4).with_z(base + 8), + max: (center + 3).with_z(base + 13), + }) + .fill(sandstone.clone()); + painter + .sphere(Aabb { + min: (center - 4).with_z(base + 14), + max: (center + 3).with_z(base + 15), + }) + .fill(sandstone); + painter + .sphere(Aabb { + min: (center - 3).with_z(base + 16), + max: (center + 2).with_z(base + 21), + }) + .fill(floaty_block); + } +} diff --git a/world/src/util/mod.rs b/world/src/util/mod.rs index 7c3c741bcc..aa5bdd79a9 100644 --- a/world/src/util/mod.rs +++ b/world/src/util/mod.rs @@ -54,6 +54,13 @@ pub const DIRS: [Vec2; 8] = [ Vec2::new(1, -1), ]; +pub const DIAGONALS: [Vec2; 4] = [ + Vec2::new(-1, -1), + Vec2::new(1, -1), + Vec2::new(-1, 1), + Vec2::new(1, 1), +]; + pub const NEIGHBORS: [Vec2; 8] = [ Vec2::new(1, 0), Vec2::new(1, 1),