From 8499143f777684adbce58304c2f3fe008e236e38 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 11 Apr 2020 01:09:01 +0100 Subject: [PATCH] Basic town house generation --- world/src/civ/mod.rs | 20 +++- world/src/sim/mod.rs | 9 +- .../settlement/building/archetype/house.rs | 4 +- world/src/site/settlement/building/mod.rs | 10 +- .../src/site/settlement/building/skeleton.rs | 8 +- world/src/site/settlement/mod.rs | 96 +++++++++++++++---- 6 files changed, 111 insertions(+), 36 deletions(-) diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 298f3050d9..35b20b2bbf 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -14,8 +14,12 @@ use common::{ store::{Id, Store}, path::Path, astar::Astar, + spiral::Spiral2d, +}; +use crate::{ + sim::{WorldSim, SimChunk}, + site::{Site as WorldSite, Settlement}, }; -use crate::sim::{WorldSim, SimChunk}; const CARDINALS: [Vec2; 4] = [ Vec2::new(1, 0), @@ -79,10 +83,22 @@ impl Civs { // Temporary! for track in this.tracks.iter() { for loc in track.path.iter() { - sim.get_mut(*loc).unwrap().place = Some(this.civs.iter().next().unwrap().homeland); + ctx.sim.get_mut(*loc).unwrap().place = Some(this.civs.iter().next().unwrap().homeland); } } + // Place sites in world + for site in this.sites.iter() { + let wpos = site.center * Vec2::from(TerrainChunkSize::RECT_SIZE).map(|e: u32| e as i32); + let settlement = WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), ctx.rng)); + for pos in Spiral2d::new().map(|offs| site.center + offs).take(32usize.pow(2)) { + ctx.sim + .get_mut(pos) + .map(|chunk| chunk.sites.push(settlement.clone())); + } + println!("Placed site at {:?}", site.center); + } + this.display_info(); this diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index b210f52766..1f1b820c85 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -1451,6 +1451,7 @@ impl WorldSim { */ // Stage 2 - towns! + /* let chunk_idx_center = |e: Vec2| { e.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { e * sz as i32 + sz as i32 / 2 @@ -1464,12 +1465,9 @@ impl WorldSim { chunk_idx_center(WORLD_SIZE.map(|e| e as i32)), ) .map_init( - || Box::new(BlockGen::new(ColumnGen::new(self))), - |mut block_gen, (pos, seed)| { + || (), + |_, (pos, seed)| { let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); - // println!("Town: {:?}", town); - //TownState::generate(pos, &mut block_gen, &mut rng).map(|t| (pos, - // Arc::new(t))) ( pos, Site::from(Settlement::generate(pos, Some(self), &mut rng)), @@ -1496,6 +1494,7 @@ impl WorldSim { chunk.sites.push(site.clone()); } }); + */ // Create waypoints const WAYPOINT_EVERY: usize = 16; diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index 681138f16d..b97c8a755f 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -52,7 +52,7 @@ impl Archetype for House { let empty = Some(Some(Block::empty())); let ceil_height = 6; - let width = 3 + branch.locus + if profile.y >= ceil_height { 1 } else { 0 }; + let width = -3 + branch.locus + if profile.y >= ceil_height { 1 } else { 0 }; let foundation_height = 0 - (dist - width - 1).max(0); let roof_height = 8 + width; @@ -115,7 +115,7 @@ impl Archetype for House { }; // Window - if (frame_bounds.size() + 1).reduce_min() > 2 { + if (frame_bounds.size() + 1).reduce_min() > 2 { // Window frame is large enough for a window let surface_pos = Vec2::new(bound_offset.x, profile.y); if window_bounds.contains_point(surface_pos) { return empty; diff --git a/world/src/site/settlement/building/mod.rs b/world/src/site/settlement/building/mod.rs index 5363467bfa..70ded7e16b 100644 --- a/world/src/site/settlement/building/mod.rs +++ b/world/src/site/settlement/building/mod.rs @@ -30,12 +30,12 @@ impl Building { root: Branch { len, attr: A::Attr::default(), - locus: 2 + rng.gen_range(0, 5), + locus: 8 + rng.gen_range(0, 5), children: (0..rng.gen_range(0, 4)) - .map(|_| (rng.gen_range(0, len + 1), Branch { + .map(|_| (rng.gen_range(-5, len + 5).clamped(0, len.max(1) - 1), Branch { len: rng.gen_range(5, 12) * if rng.gen() { 1 } else { -1 }, attr: A::Attr::default(), - locus: 1 + rng.gen_range(0, 3), + locus: 8 + rng.gen_range(0, 3), children: Vec::new(), })) .collect(), @@ -49,8 +49,8 @@ impl Building { pub fn bounds_2d(&self) -> Aabr { let b = self.skel.bounds(); Aabr { - min: Vec2::from(self.origin) + b.min - 14, - max: Vec2::from(self.origin) + b.max + 14, + min: Vec2::from(self.origin) + b.min, + max: Vec2::from(self.origin) + b.max, } } diff --git a/world/src/site/settlement/building/skeleton.rs b/world/src/site/settlement/building/skeleton.rs index 0df56d9b50..b8c6219c01 100644 --- a/world/src/site/settlement/building/skeleton.rs +++ b/world/src/site/settlement/building/skeleton.rs @@ -52,8 +52,12 @@ impl Skeleton { pub fn bounds(&self) -> Aabr { let mut bounds = Aabr::new_empty(self.ori.dir() * self.offset); self.for_each(|node, ori, branch| { - bounds.expand_to_contain(Aabr::new_empty(node - ori.flip().dir() * branch.locus) - .expanded_to_contain_point(node + ori.dir() * branch.len + ori.flip().dir() * branch.locus)); + let node2 = node + ori.dir() * branch.len; + + let a = node.map2(node2, |a, b| a.min(b)) - branch.locus; + let b = node.map2(node2, |a, b| a.max(b)) + branch.locus; + bounds.expand_to_contain_point(a); + bounds.expand_to_contain_point(b); }); bounds } diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 48075fbb0e..6d47f2f23d 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -89,6 +89,14 @@ pub struct Structure { kind: StructureKind, } +impl Structure { + pub fn bounds_2d(&self) -> Aabr { + match &self.kind { + StructureKind::House(house) => house.bounds_2d(), + } + } +} + pub struct Settlement { origin: Vec2, land: Land, @@ -128,7 +136,7 @@ impl Settlement { //this.place_river(rng); this.place_farms(&mut ctx); - this.place_town(ctx.rng); + this.place_town(&mut ctx); this.place_paths(ctx.rng); this @@ -229,10 +237,10 @@ impl Settlement { } } - pub fn place_town(&mut self, rng: &mut impl Rng) { + pub fn place_town(&mut self, ctx: &mut GenCtx) { const PLOT_COUNT: usize = 2; - let mut origin = Vec2::new(rng.gen_range(-2, 3), rng.gen_range(-2, 3)); + let mut origin = Vec2::new(ctx.rng.gen_range(-2, 3), ctx.rng.gen_range(-2, 3)); for i in 0..PLOT_COUNT { if let Some(base_tile) = self.land.find_tile_near(origin, |plot| match plot { @@ -244,6 +252,43 @@ impl Settlement { .plot_at_mut(base_tile) .map(|plot| *plot = Plot::Town); + for _ in 0..ctx.rng.gen_range(10, 30) { + for _ in 0..10 { + let house_pos = base_tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2) + + Vec2::::zero().map(|_| ctx.rng.gen_range(-(AREA_SIZE as i32) * 3, AREA_SIZE as i32 * 3)); + + if let Some(Plot::Town) = self.land + .plot_at(house_pos.map(|e| e.div_euclid(AREA_SIZE as i32))) + {} else { + continue; + } + + let structure = Structure { + kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new( + house_pos.x, + house_pos.y, + ctx.sim + .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) + .unwrap_or(0.0) + .ceil() as i32, + ))), + }; + + let bounds = structure.bounds_2d(); + + // Check for collision with other structures + if self.structures + .iter() + .any(|s| s.bounds_2d().collides_with_aabr(bounds)) + { + continue; + } + + self.structures.push(structure); + break; + } + } + if i == 0 { /* for dir in CARDINALS.iter() { @@ -315,21 +360,21 @@ impl Settlement { self.land.set(base_tile, farmhouse); // Farmhouses - for _ in 0..ctx.rng.gen_range(1, 3) { - let house_pos = base_tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2) - + Vec2::new(ctx.rng.gen_range(-16, 16), ctx.rng.gen_range(-16, 16)); + // for _ in 0..ctx.rng.gen_range(1, 3) { + // let house_pos = base_tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2) + // + Vec2::new(ctx.rng.gen_range(-16, 16), ctx.rng.gen_range(-16, 16)); - self.structures.push(Structure { - kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new( - house_pos.x, - house_pos.y, - ctx.sim - .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) - .unwrap_or(0.0) - .ceil() as i32, - ))), - }); - } + // self.structures.push(Structure { + // kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new( + // house_pos.x, + // house_pos.y, + // ctx.sim + // .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) + // .unwrap_or(0.0) + // .ceil() as i32, + // ))), + // }); + // } // Fields let farmland = self.farms.insert(Farm { base_tile }); @@ -463,13 +508,24 @@ impl Settlement { // Apply structures for structure in &self.structures { + let bounds = structure.bounds_2d(); + + // Skip this structure if it's not near this chunk + if !bounds.collides_with_aabr(Aabr { + min: wpos2d - self.origin, + max: wpos2d - self.origin + vol.size_xy().map(|e| e as i32), + }) { + continue; + } + match &structure.kind { StructureKind::House(b) => { let centre = b.bounds_2d().center(); let bounds = b.bounds(); - for x in bounds.min.x..bounds.max.x { - for y in bounds.min.y..bounds.max.y { - for z in bounds.min.z..bounds.max.z { + + for x in bounds.min.x..bounds.max.x + 1 { + for y in bounds.min.y..bounds.max.y + 1 { + for z in bounds.min.z..bounds.max.z + 1 { let rpos = Vec3::new(x, y, z); let wpos = Vec3::from(self.origin) + rpos; let coffs = wpos - Vec3::from(wpos2d);