Basic town house generation

This commit is contained in:
Joshua Barretto 2020-04-11 01:09:01 +01:00
parent fd3ffdf28b
commit 8499143f77
6 changed files with 111 additions and 36 deletions

View File

@ -14,8 +14,12 @@ use common::{
store::{Id, Store}, store::{Id, Store},
path::Path, path::Path,
astar::Astar, astar::Astar,
spiral::Spiral2d,
};
use crate::{
sim::{WorldSim, SimChunk},
site::{Site as WorldSite, Settlement},
}; };
use crate::sim::{WorldSim, SimChunk};
const CARDINALS: [Vec2<i32>; 4] = [ const CARDINALS: [Vec2<i32>; 4] = [
Vec2::new(1, 0), Vec2::new(1, 0),
@ -79,10 +83,22 @@ impl Civs {
// Temporary! // Temporary!
for track in this.tracks.iter() { for track in this.tracks.iter() {
for loc in track.path.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.display_info();
this this

View File

@ -1451,6 +1451,7 @@ impl WorldSim {
*/ */
// Stage 2 - towns! // Stage 2 - towns!
/*
let chunk_idx_center = |e: Vec2<i32>| { let chunk_idx_center = |e: Vec2<i32>| {
e.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { e.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| {
e * sz as i32 + sz as i32 / 2 e * sz as i32 + sz as i32 / 2
@ -1464,12 +1465,9 @@ impl WorldSim {
chunk_idx_center(WORLD_SIZE.map(|e| e as i32)), chunk_idx_center(WORLD_SIZE.map(|e| e as i32)),
) )
.map_init( .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)); 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, pos,
Site::from(Settlement::generate(pos, Some(self), &mut rng)), Site::from(Settlement::generate(pos, Some(self), &mut rng)),
@ -1496,6 +1494,7 @@ impl WorldSim {
chunk.sites.push(site.clone()); chunk.sites.push(site.clone());
} }
}); });
*/
// Create waypoints // Create waypoints
const WAYPOINT_EVERY: usize = 16; const WAYPOINT_EVERY: usize = 16;

View File

@ -52,7 +52,7 @@ impl Archetype for House {
let empty = Some(Some(Block::empty())); let empty = Some(Some(Block::empty()));
let ceil_height = 6; 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 foundation_height = 0 - (dist - width - 1).max(0);
let roof_height = 8 + width; let roof_height = 8 + width;
@ -115,7 +115,7 @@ impl Archetype for House {
}; };
// Window // 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); let surface_pos = Vec2::new(bound_offset.x, profile.y);
if window_bounds.contains_point(surface_pos) { if window_bounds.contains_point(surface_pos) {
return empty; return empty;

View File

@ -30,12 +30,12 @@ impl<A: Archetype> Building<A> {
root: Branch { root: Branch {
len, len,
attr: A::Attr::default(), 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)) 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 }, len: rng.gen_range(5, 12) * if rng.gen() { 1 } else { -1 },
attr: A::Attr::default(), attr: A::Attr::default(),
locus: 1 + rng.gen_range(0, 3), locus: 8 + rng.gen_range(0, 3),
children: Vec::new(), children: Vec::new(),
})) }))
.collect(), .collect(),
@ -49,8 +49,8 @@ impl<A: Archetype> Building<A> {
pub fn bounds_2d(&self) -> Aabr<i32> { pub fn bounds_2d(&self) -> Aabr<i32> {
let b = self.skel.bounds(); let b = self.skel.bounds();
Aabr { Aabr {
min: Vec2::from(self.origin) + b.min - 14, min: Vec2::from(self.origin) + b.min,
max: Vec2::from(self.origin) + b.max + 14, max: Vec2::from(self.origin) + b.max,
} }
} }

View File

@ -52,8 +52,12 @@ impl<T> Skeleton<T> {
pub fn bounds(&self) -> Aabr<i32> { pub fn bounds(&self) -> Aabr<i32> {
let mut bounds = Aabr::new_empty(self.ori.dir() * self.offset); let mut bounds = Aabr::new_empty(self.ori.dir() * self.offset);
self.for_each(|node, ori, branch| { self.for_each(|node, ori, branch| {
bounds.expand_to_contain(Aabr::new_empty(node - ori.flip().dir() * branch.locus) let node2 = node + ori.dir() * branch.len;
.expanded_to_contain_point(node + ori.dir() * branch.len + ori.flip().dir() * branch.locus));
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 bounds
} }

View File

@ -89,6 +89,14 @@ pub struct Structure {
kind: StructureKind, kind: StructureKind,
} }
impl Structure {
pub fn bounds_2d(&self) -> Aabr<i32> {
match &self.kind {
StructureKind::House(house) => house.bounds_2d(),
}
}
}
pub struct Settlement { pub struct Settlement {
origin: Vec2<i32>, origin: Vec2<i32>,
land: Land, land: Land,
@ -128,7 +136,7 @@ impl Settlement {
//this.place_river(rng); //this.place_river(rng);
this.place_farms(&mut ctx); this.place_farms(&mut ctx);
this.place_town(ctx.rng); this.place_town(&mut ctx);
this.place_paths(ctx.rng); this.place_paths(ctx.rng);
this 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<impl Rng>) {
const PLOT_COUNT: usize = 2; 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 { for i in 0..PLOT_COUNT {
if let Some(base_tile) = self.land.find_tile_near(origin, |plot| match plot { 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) .plot_at_mut(base_tile)
.map(|plot| *plot = Plot::Town); .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::<i32>::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 { if i == 0 {
/* /*
for dir in CARDINALS.iter() { for dir in CARDINALS.iter() {
@ -315,21 +360,21 @@ impl Settlement {
self.land.set(base_tile, farmhouse); self.land.set(base_tile, farmhouse);
// Farmhouses // Farmhouses
for _ in 0..ctx.rng.gen_range(1, 3) { // 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) // 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)); // + Vec2::new(ctx.rng.gen_range(-16, 16), ctx.rng.gen_range(-16, 16));
self.structures.push(Structure { // self.structures.push(Structure {
kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new( // kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new(
house_pos.x, // house_pos.x,
house_pos.y, // house_pos.y,
ctx.sim // ctx.sim
.and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) // .and_then(|sim| sim.get_alt_approx(self.origin + house_pos))
.unwrap_or(0.0) // .unwrap_or(0.0)
.ceil() as i32, // .ceil() as i32,
))), // ))),
}); // });
} // }
// Fields // Fields
let farmland = self.farms.insert(Farm { base_tile }); let farmland = self.farms.insert(Farm { base_tile });
@ -463,13 +508,24 @@ impl Settlement {
// Apply structures // Apply structures
for structure in &self.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 { match &structure.kind {
StructureKind::House(b) => { StructureKind::House(b) => {
let centre = b.bounds_2d().center(); let centre = b.bounds_2d().center();
let bounds = b.bounds(); let bounds = b.bounds();
for x in bounds.min.x..bounds.max.x {
for y in bounds.min.y..bounds.max.y { for x in bounds.min.x..bounds.max.x + 1 {
for z in bounds.min.z..bounds.max.z { 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 rpos = Vec3::new(x, y, z);
let wpos = Vec3::from(self.origin) + rpos; let wpos = Vec3::from(self.origin) + rpos;
let coffs = wpos - Vec3::from(wpos2d); let coffs = wpos - Vec3::from(wpos2d);