Added house overhangs, better per-wing generation options

This commit is contained in:
Joshua Barretto 2020-04-12 22:44:15 +01:00
parent a754b34105
commit c1945a1445
7 changed files with 134 additions and 70 deletions

View File

@ -89,17 +89,15 @@ impl Civs {
}
}
// Place sites in world
// Flatten ground around sites
for site in this.sites.iter() {
let radius = 48i32;
let wpos = site.center * Vec2::from(TerrainChunkSize::RECT_SIZE).map(|e: u32| e as i32);
let nearby_chunks = Spiral2d::new().map(|offs| site.center + offs).take(radius.pow(2) as usize);
// Flatten ground
let flatten_radius = 12.0;
if let Some(center_alt) = ctx.sim.get_alt_approx(wpos) {
for pos in nearby_chunks.clone() {
for pos in Spiral2d::new().map(|offs| site.center + offs).take(radius.pow(2) as usize) {
let factor = (1.0 - (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius) * 1.15;
ctx.sim
.get_mut(pos)
@ -113,10 +111,16 @@ impl Civs {
});
}
}
}
// Place sites in world
for site in this.sites.iter() {
let radius = 48i32;
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 nearby_chunks {
for pos in Spiral2d::new().map(|offs| site.center + offs).take(radius.pow(2) as usize) {
ctx.sim
.get_mut(pos)
.map(|chunk| chunk.sites.push(settlement.clone()));

View File

@ -13,26 +13,70 @@ use super::{
pub struct House {
roof_color: Rgb<u8>,
noise: RandomField,
roof_ribbing: bool,
central_supports: bool,
chimney: Option<i32>,
roof_ribbing: bool,
}
pub struct Attr {
central_supports: bool,
lower_walls: bool,
}
impl Attr {
fn generate<R: Rng>(rng: &mut R) -> Self {
Self {
central_supports: rng.gen(),
lower_walls: rng.gen(),
}
}
}
impl Archetype for House {
type Attr = ();
type Attr = Attr;
fn generate<R: Rng>(rng: &mut R) -> Self {
Self {
fn generate<R: Rng>(rng: &mut R) -> (Self, Skeleton<Self::Attr>) {
let this = Self {
roof_color: Rgb::new(
rng.gen_range(50, 200),
rng.gen_range(50, 200),
rng.gen_range(50, 200),
),
noise: RandomField::new(rng.gen()),
roof_ribbing: rng.gen(),
central_supports: rng.gen(),
chimney: if rng.gen() { Some(rng.gen_range(1, 6)) } else { None },
}
roof_ribbing: rng.gen(),
};
let len = rng.gen_range(-8, 20).clamped(0, 16);
let branches_per_side = 1 + len as usize / 16;
let skel = Skeleton {
offset: -rng.gen_range(0, len + 7).clamped(0, len),
ori: if rng.gen() { Ori::East } else { Ori::North },
root: Branch {
len,
attr: Attr {
central_supports: rng.gen(),
lower_walls: true,
},
locus: 8 + rng.gen_range(0, 5),
children: [1, -1]
.iter()
.map(|flip| (0..branches_per_side).map(move |i| (i, *flip)))
.flatten()
.filter_map(move |(i, flip)| if rng.gen() {
Some((i as i32 * len / (branches_per_side - 1).max(1) as i32, Branch {
len: rng.gen_range(0, 12) * flip,
attr: Attr::generate(rng),
locus: 8 + rng.gen_range(0, 3),
children: Vec::new(),
}))
} else {
None
})
.collect(),
},
};
(this, skel)
}
fn draw(
@ -59,7 +103,9 @@ impl Archetype for House {
let fire = Some(Some(Block::new(BlockKind::Ember, Rgb::white())));
let ceil_height = 6;
let width = -3 + branch.locus + if profile.y >= ceil_height { 1 } else { 0 };
let lower_width = -3 + branch.locus;
let upper_width = -2 + branch.locus;
let width = if profile.y >= ceil_height { upper_width } else { lower_width };
let foundation_height = 0 - (dist - width - 1).max(0);
let roof_height = 8 + width;
@ -67,7 +113,7 @@ impl Archetype for House {
// Chimney shaft
if center_offset.map(|e| e.abs()).reduce_max() == 0 && profile.y >= foundation_height + 1 {
return if profile.y == foundation_height + 1 {
empty//fire
fire
} else {
empty
};
@ -85,36 +131,55 @@ impl Archetype for House {
}
if profile.y <= foundation_height && dist < width + 3 { // Foundations
if dist == width - 1 { // Floor lining
return log;
} else if dist < width - 1 && profile.y == foundation_height { // Floor
return floor;
} else if dist < width && profile.y >= foundation_height - 3 { // Basement
if branch.attr.lower_walls {
if dist == width - 1 { // Floor lining
return log;
} else if dist < width - 1 && profile.y == foundation_height { // Floor
return floor;
}
}
if dist < width && profile.y < foundation_height && profile.y >= foundation_height - 3 { // Basement
return empty;
} else {
return foundation;
}
}
if profile.y > roof_height - profile.x { // Air above roof
return Some(None);
}
// Roof
if profile.y == roof_height - profile.x
&& profile.y >= ceil_height
&& dist <= width + 2
{
let is_ribbing = (roof_height - profile.y) % 3 == 0 && self.roof_ribbing;
if profile.x == 0 || dist == width + 2 || is_ribbing { // Eaves
return log;
} else {
return roof;
let do_roof = |profile: Vec2<i32>, dist, roof_height, roof_width| {
if profile.y > roof_height - profile.x { // Air above roof
return Some(Some(None));
}
// Roof
if profile.y == roof_height - profile.x
&& dist <= roof_width
{
let is_ribbing = (roof_height - profile.y) % 3 == 0 && self.roof_ribbing;
if profile.x == 0 || dist == roof_width|| is_ribbing { // Eaves
return Some(log);
} else {
return Some(roof);
}
}
None
};
if let Some(block) = do_roof(profile, dist, roof_height, width + 2) {
return block;
}
// Walls
if dist == width {
if dist == width && (
bound_offset.x == bound_offset.y ||
(profile.x == 0 && branch.attr.central_supports) ||
profile.y == ceil_height
) { // Support beams
return log;
} else if !branch.attr.lower_walls && profile.y < ceil_height {
return None;
} else if dist == width {
let frame_bounds = if profile.y >= ceil_height {
Aabr {
min: Vec2::new(-1, ceil_height + 2),
@ -142,15 +207,7 @@ impl Archetype for House {
}
// Wall
return if
bound_offset.x == bound_offset.y ||
(profile.x == 0 && self.central_supports) ||
profile.y == ceil_height
{ // Support beams
log
} else {
wall
};
return wall;
}
if dist < width { // Internals

View File

@ -14,8 +14,27 @@ pub struct Keep;
impl Archetype for Keep {
type Attr = ();
fn generate<R: Rng>(rng: &mut R) -> Self {
Self
fn generate<R: Rng>(rng: &mut R) -> (Self, Skeleton<Self::Attr>) {
let len = rng.gen_range(-8, 12).max(0);
let skel = Skeleton {
offset: -rng.gen_range(0, len + 7).clamped(0, len),
ori: if rng.gen() { Ori::East } else { Ori::North },
root: Branch {
len,
attr: Self::Attr::default(),
locus: 8 + rng.gen_range(0, 5),
children: (0..rng.gen_range(0, 4))
.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: Self::Attr::default(),
locus: 8 + rng.gen_range(0, 3),
children: Vec::new(),
}))
.collect(),
},
};
(Self, skel)
}
fn draw(

View File

@ -7,9 +7,9 @@ use common::terrain::Block;
use super::skeleton::*;
pub trait Archetype {
type Attr: Default;
type Attr;
fn generate<R: Rng>(rng: &mut R) -> Self where Self: Sized;
fn generate<R: Rng>(rng: &mut R) -> (Self, Skeleton<Self::Attr>) where Self: Sized;
fn draw(
&self,
dist: i32,

View File

@ -22,25 +22,9 @@ impl<A: Archetype> Building<A> {
where A: Sized
{
let len = rng.gen_range(-8, 12).max(0);
let archetype = A::generate(rng);
let (archetype, skel) = A::generate(rng);
Self {
skel: Skeleton {
offset: -rng.gen_range(0, len + 7).clamped(0, len),
ori: if rng.gen() { Ori::East } else { Ori::North },
root: Branch {
len,
attr: A::Attr::default(),
locus: 8 + rng.gen_range(0, 5),
children: (0..rng.gen_range(0, 4))
.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: 8 + rng.gen_range(0, 3),
children: Vec::new(),
}))
.collect(),
},
},
skel,
archetype,
origin,
}

View File

@ -62,7 +62,7 @@ impl<T> Skeleton<T> {
bounds
}
pub fn closest<R>(&self, pos: Vec2<i32>, mut f: impl FnMut(i32, Vec2<i32>, Vec2<i32>, &Branch<T>) -> Option<R>) -> Option<R> {
pub fn closest<R: Clone>(&self, pos: Vec2<i32>, mut f: impl FnMut(i32, Vec2<i32>, Vec2<i32>, &Branch<T>) -> Option<R>) -> Option<R> {
let mut min = None;
self.for_each(|node, ori, branch| {
let node2 = node + ori.dir() * branch.len;
@ -87,7 +87,7 @@ impl<T> Skeleton<T> {
let dist = bound_offset.reduce_max();
let dist_locus = dist - branch.locus;
if min.as_ref().map(|(min_dist_locus, _)| dist_locus < *min_dist_locus).unwrap_or(true) {
min = f(dist, bound_offset, center_offset, branch).map(|r| (dist_locus, r));
min = f(dist, bound_offset, center_offset, branch).map(|r| (dist_locus, r)).or(min.clone());
}
});
min.map(|(_, r)| r)

View File

@ -376,8 +376,8 @@ impl Settlement {
.find_tile_near(Vec2::zero(), |plot| plot.is_none())
{
// Farm
let farmhouse = self.land.new_plot(Plot::Dirt);
self.land.set(base_tile, farmhouse);
//let farmhouse = self.land.new_plot(Plot::Dirt);
//self.land.set(base_tile, farmhouse);
// Farmhouses
// for _ in 0..ctx.rng.gen_range(1, 3) {