Castle improvements

This commit is contained in:
Joshua Barretto 2020-06-28 16:09:31 +01:00
parent d6cdb0c433
commit b27a65fd0f
7 changed files with 178 additions and 121 deletions

View File

@ -5,7 +5,7 @@ mod econ;
use self::{Occupation::*, Stock::*};
use crate::{
sim::WorldSim,
site::{Dungeon, Settlement, Castle, Site as WorldSite},
site::{Castle, Dungeon, Settlement, Site as WorldSite},
util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS},
Index,
};
@ -376,7 +376,7 @@ impl Civs {
loc: Vec2<i32>,
site_fn: impl FnOnce(Id<Place>) -> Site,
) -> Option<Id<Site>> {
const SITE_AREA: Range<usize> = 1..4;//64..256;
const SITE_AREA: Range<usize> = 1..4; //64..256;
let place = match ctx.sim.get(loc).and_then(|site| site.place) {
Some(place) => place,
@ -612,7 +612,11 @@ fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2<i32>) -> bool {
/// Attempt to search for a location that's suitable for site construction
#[allow(clippy::useless_conversion)] // TODO: Pending review in #587
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn find_site_loc(ctx: &mut GenCtx<impl Rng>, near: Option<(Vec2<i32>, f32)>, size: i32) -> Option<Vec2<i32>> {
fn find_site_loc(
ctx: &mut GenCtx<impl Rng>,
near: Option<(Vec2<i32>, f32)>,
size: i32,
) -> Option<Vec2<i32>> {
const MAX_ATTEMPTS: usize = 100;
let mut loc = None;
for _ in 0..MAX_ATTEMPTS {

View File

@ -4,8 +4,11 @@ use crate::{
column::ColumnSample,
sim::WorldSim,
site::{
settlement::building::{
archetype::keep::{Attr, Keep},
Archetype, Branch, Ori,
},
BlockMask,
settlement::building::{Archetype, Ori, Branch, archetype::keep::{Keep, Attr}},
},
util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS},
};
@ -42,6 +45,7 @@ pub struct Castle {
origin: Vec2<i32>,
alt: i32,
seed: u32,
radius: i32,
towers: Vec<Tower>,
segments: Vec<Segment>,
}
@ -57,6 +61,9 @@ impl Castle {
let mut ctx = GenCtx { sim, rng };
let boundary_towers = ctx.rng.gen_range(5, 10);
let boundary_noise = ctx.rng.gen_range(-2i32, 8).max(1) as f32;
let radius = 150;
let this = Self {
origin: wpos,
@ -66,43 +73,53 @@ impl Castle {
.unwrap_or(0.0) as i32
+ 6,
seed: ctx.rng.gen(),
radius,
towers: (0..boundary_towers)
.map(|i| {
let angle = (i as f32 / boundary_towers as f32) * f32::consts::PI * 2.0;
let dir = Vec2::new(
angle.cos(),
angle.sin(),
);
let dist = ctx.rng.gen_range(45.0, 190.0).clamped(75.0, 135.0);
let dir = Vec2::new(angle.cos(), angle.sin());
let dist = radius as f32 + ((angle * boundary_noise).sin() - 1.0) * 40.0;
let offset = (dir * dist).map(|e| e as i32);
let mut offset = (dir * dist).map(|e| e as i32);
// Try to move the tower around until it's not intersecting a path
for i in (1..80).step_by(5) {
if ctx
.sim
.and_then(|sim| sim.get_nearest_path(wpos + offset))
.map(|(dist, _)| dist > 24.0)
.unwrap_or(true)
{
break;
}
offset = (dir * dist)
.map(|e| (e + ctx.rng.gen_range(-1.0, 1.0) * i as f32) as i32);
}
Tower {
offset,
alt: ctx
.sim
.and_then(|sim| sim.get_alt_approx(wpos + offset))
.unwrap_or(0.0) as i32 + 2,
.unwrap_or(0.0) as i32
+ 2,
}
})
.collect(),
segments: (0..0)//rng.gen_range(18, 24))
segments: (0..0) //rng.gen_range(18, 24))
.map(|_| {
let dir = Vec2::new(
rng.gen_range(-1.0, 1.0),
rng.gen_range(-1.0, 1.0),
).normalized();
let dist = 16.0 + rng.gen_range(0.0f32, 1.0).powf(0.5) * 64.0;
let dir = Vec2::new(ctx.rng.gen_range(-1.0, 1.0), ctx.rng.gen_range(-1.0, 1.0))
.normalized();
let dist = 16.0 + ctx.rng.gen_range(0.0f32, 1.0).powf(0.5) * 64.0;
let height = 48.0 - (dist / 64.0).powf(2.0) * 32.0;
Segment {
offset: (dir * dist).map(|e| e as i32),
locus: rng.gen_range(6, 26),
height: height as i32,
is_tower: height > 36.0,
}
offset: (dir * dist).map(|e| e as i32),
locus: ctx.rng.gen_range(6, 26),
height: height as i32,
is_tower: height > 36.0,
}
})
.collect(),
};
@ -117,7 +134,7 @@ impl Castle {
#[allow(clippy::needless_update)] // TODO: Pending review in #587
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
SpawnRules {
trees: wpos.distance_squared(self.origin) > 64i32.pow(2),
trees: wpos.distance_squared(self.origin) > self.radius.pow(2),
..SpawnRules::default()
}
}
@ -142,7 +159,7 @@ impl Castle {
continue;
};
let (wall_dist, wall_pos, wall_alt) = (0..self.towers.len())
let (wall_dist, wall_pos, wall_alt, wall_ori) = (0..self.towers.len())
.map(|i| {
let tower0 = &self.towers[i];
let tower1 = &self.towers[(i + 1) % self.towers.len()];
@ -152,47 +169,64 @@ impl Castle {
end: tower1.offset.map(|e| e as f32),
};
let projected = wall.projected_point(rpos.map(|e| e as f32)).map(|e| e as i32);
let projected = wall
.projected_point(rpos.map(|e| e as f32))
.map(|e| e as i32);
let tower0_dist = tower0.offset.map(|e| e as f32).distance(projected.map(|e| e as f32));
let tower1_dist = tower1.offset.map(|e| e as f32).distance(projected.map(|e| e as f32));
let tower0_dist = tower0
.offset
.map(|e| e as f32)
.distance(projected.map(|e| e as f32));
let tower1_dist = tower1
.offset
.map(|e| e as f32)
.distance(projected.map(|e| e as f32));
let tower_lerp = tower0_dist / (tower0_dist + tower1_dist);
let wall_ori = if (tower0.offset.x - tower1.offset.x).abs()
< (tower0.offset.y - tower1.offset.y).abs()
{
Ori::East
} else {
Ori::North
};
(
wall.distance_to_point(rpos.map(|e| e as f32)) as i32,
projected,
Lerp::lerp(tower0.alt as f32, tower1.alt as f32, tower_lerp) as i32,
wall_ori,
)
})
.min_by_key(|x| x.0)
.unwrap();
for z in -10..64 {
let wpos = Vec3::new(
wpos2d.x,
wpos2d.y,
col_sample.alt as i32 + z,
);
let wpos = Vec3::new(wpos2d.x, wpos2d.y, col_sample.alt as i32 + z);
let keep = Keep {
flag_color: Rgb::new(200, 80, 40),
};
// Boundary
let border_pos = (wall_pos - rpos).map(|e| e.abs());
let mut mask = Keep.draw(
Vec3::from(rpos) + Vec3::unit_z() * wpos.z - wall_alt,
let wall_rpos = if wall_ori == Ori::East {
rpos
} else {
rpos.yx()
};
let mut mask = keep.draw(
Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt,
wall_dist,
Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()),
rpos - wall_pos,
wpos.z - wall_alt,
Ori::North,
&Branch {
len: 0,
attr: Attr {
height: 16,
is_tower: false,
},
locus: 4,
border: 0,
children: Vec::new(),
}
wall_ori,
4,
0,
&Attr {
height: 16,
is_tower: false,
},
);
for tower in &self.towers {
let tower_wpos = Vec3::new(
@ -203,23 +237,27 @@ impl Castle {
let tower_locus = 10;
let border_pos = (tower_wpos - wpos).xy().map(|e| e.abs());
mask = mask.resolve_with(Keep.draw(
wpos - tower_wpos,
mask = mask.resolve_with(keep.draw(
if (tower_wpos.x - wpos.x).abs() < (tower_wpos.y - wpos.y).abs() {
wpos - tower_wpos
} else {
Vec3::new(
wpos.y - tower_wpos.y,
wpos.x - tower_wpos.x,
wpos.z - tower_wpos.z,
)
},
border_pos.reduce_max() - tower_locus,
Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()),
(wpos - tower_wpos).xy(),
wpos.z - tower.alt,
Ori::North,
&Branch {
len: 0,
attr: Attr {
height: 28,
is_tower: true,
},
locus: tower_locus,
border: 0,
children: Vec::new(),
}
Ori::East,
tower_locus,
0,
&Attr {
height: 28,
is_tower: true,
},
));
}

View File

@ -1,16 +1,13 @@
mod block_mask;
mod dungeon;
mod castle;
mod dungeon;
pub mod economy;
mod settlement;
// Reexports
pub use self::{
block_mask::BlockMask,
dungeon::Dungeon,
economy::Economy,
block_mask::BlockMask, castle::Castle, dungeon::Dungeon, economy::Economy,
settlement::Settlement,
castle::Castle,
};
use crate::column::ColumnSample;

View File

@ -33,25 +33,28 @@ const COLOR_THEMES: [Rgb<u8>; 17] = [
];
pub struct House {
roof_color: Rgb<u8>,
noise: RandomField,
roof_ribbing: bool,
roof_ribbing_diagonal: bool,
pub roof_color: Rgb<u8>,
pub noise: RandomField,
pub roof_ribbing: bool,
pub roof_ribbing_diagonal: bool,
}
enum Pillar {
#[derive(Copy, Clone)]
pub enum Pillar {
None,
Chimney(i32),
Tower(i32),
}
enum RoofStyle {
#[derive(Copy, Clone)]
pub enum RoofStyle {
Hip,
Gable,
Rounded,
}
enum StoreyFill {
#[derive(Copy, Clone)]
pub enum StoreyFill {
None,
Upper,
All,
@ -75,16 +78,17 @@ impl StoreyFill {
}
}
#[derive(Copy, Clone)]
pub struct Attr {
central_supports: bool,
storey_fill: StoreyFill,
roof_style: RoofStyle,
mansard: i32,
pillar: Pillar,
pub central_supports: bool,
pub storey_fill: StoreyFill,
pub roof_style: RoofStyle,
pub mansard: i32,
pub pillar: Pillar,
}
impl Attr {
fn generate<R: Rng>(rng: &mut R, locus: i32) -> Self {
pub fn generate<R: Rng>(rng: &mut R, locus: i32) -> Self {
Self {
central_supports: rng.gen(),
storey_fill: match rng.gen_range(0, 2) {
@ -174,7 +178,9 @@ impl Archetype for House {
center_offset: Vec2<i32>,
z: i32,
ori: Ori,
branch: &Branch<Self::Attr>,
locus: i32,
len: i32,
attr: &Self::Attr,
) -> BlockMask {
let profile = Vec2::new(bound_offset.x, z);
@ -224,8 +230,8 @@ impl Archetype for House {
let fire = BlockMask::new(Block::new(BlockKind::Ember, Rgb::white()), foundation_layer);
let ceil_height = 6;
let lower_width = branch.locus - 1;
let upper_width = branch.locus;
let lower_width = locus - 1;
let upper_width = locus;
let width = if profile.y >= ceil_height {
upper_width
} else {
@ -234,7 +240,7 @@ impl Archetype for House {
let foundation_height = 0 - (dist - width - 1).max(0);
let roof_top = 8 + width;
if let Pillar::Chimney(chimney_top) = branch.attr.pillar {
if let Pillar::Chimney(chimney_top) = attr.pillar {
// Chimney shaft
if center_offset.map(|e| e.abs()).reduce_max() == 0
&& profile.y >= foundation_height + 1
@ -262,7 +268,7 @@ impl Archetype for House {
if profile.y <= foundation_height && dist < width + 3 {
// Foundations
if branch.attr.storey_fill.has_lower() {
if attr.storey_fill.has_lower() {
if dist == width - 1 {
// Floor lining
return log.with_priority(floor_layer);
@ -285,7 +291,7 @@ impl Archetype for House {
|profile: Vec2<i32>, width, dist, bound_offset: Vec2<i32>, roof_top, mansard| {
// Roof
let (roof_profile, roof_dist) = match &branch.attr.roof_style {
let (roof_profile, roof_dist) = match &attr.roof_style {
RoofStyle::Hip => (Vec2::new(dist, profile.y), dist),
RoofStyle::Gable => (profile, dist),
RoofStyle::Rounded => {
@ -324,7 +330,7 @@ impl Archetype for House {
&& bound_offset.x > 0
&& bound_offset.x < width
&& profile.y < ceil_height
&& branch.attr.storey_fill.has_lower()
&& attr.storey_fill.has_lower()
{
return Some(
if (bound_offset.x == (width - 1) / 2
@ -355,9 +361,9 @@ impl Archetype for House {
if bound_offset.x == bound_offset.y || profile.y == ceil_height {
// Support beams
return Some(log);
} else if !branch.attr.storey_fill.has_lower() && profile.y < ceil_height {
} else if !attr.storey_fill.has_lower() && profile.y < ceil_height {
return Some(empty);
} else if !branch.attr.storey_fill.has_upper() {
} else if !attr.storey_fill.has_upper() {
return Some(empty);
} else {
let (frame_bounds, frame_borders) = if profile.y >= ceil_height {
@ -396,7 +402,7 @@ impl Archetype for House {
}
// Wall
return Some(if branch.attr.central_supports && profile.x == 0 {
return Some(if attr.central_supports && profile.x == 0 {
// Support beams
log.with_priority(structural_layer)
} else {
@ -411,12 +417,12 @@ impl Archetype for House {
if profile.x == 0 {
// Rafters
return Some(log);
} else if branch.attr.storey_fill.has_upper() {
} else if attr.storey_fill.has_upper() {
// Ceiling
return Some(floor);
}
} else if (!branch.attr.storey_fill.has_lower() && profile.y < ceil_height)
|| (!branch.attr.storey_fill.has_upper() && profile.y >= ceil_height)
} else if (!attr.storey_fill.has_lower() && profile.y < ceil_height)
|| (!attr.storey_fill.has_upper() && profile.y >= ceil_height)
{
return Some(empty);
} else {
@ -429,18 +435,13 @@ impl Archetype for House {
let mut cblock = empty;
if let Some(block) = do_roof_wall(
profile,
width,
dist,
bound_offset,
roof_top,
branch.attr.mansard,
) {
if let Some(block) =
do_roof_wall(profile, width, dist, bound_offset, roof_top, attr.mansard)
{
cblock = cblock.resolve_with(block);
}
if let Pillar::Tower(tower_top) = branch.attr.pillar {
if let Pillar::Tower(tower_top) = attr.pillar {
let profile = Vec2::new(center_offset.x.abs(), profile.y);
let dist = center_offset.map(|e| e.abs()).reduce_max();
@ -450,7 +451,7 @@ impl Archetype for House {
dist,
center_offset.map(|e| e.abs()),
tower_top,
branch.attr.mansard,
attr.mansard,
) {
cblock = cblock.resolve_with(block);
}

View File

@ -7,7 +7,9 @@ use common::{
use rand::prelude::*;
use vek::*;
pub struct Keep;
pub struct Keep {
pub flag_color: Rgb<u8>,
}
pub struct Attr {
pub height: i32,
@ -50,7 +52,12 @@ impl Archetype for Keep {
},
};
(Self, skel)
(
Self {
flag_color: Rgb::new(200, 80, 40),
},
skel,
)
}
#[allow(clippy::if_same_then_else)] // TODO: Pending review in #587
@ -62,7 +69,9 @@ impl Archetype for Keep {
center_offset: Vec2<i32>,
z: i32,
ori: Ori,
branch: &Branch<Self::Attr>,
locus: i32,
len: i32,
attr: &Self::Attr,
) -> BlockMask {
let profile = Vec2::new(bound_offset.x, z);
@ -82,24 +91,25 @@ impl Archetype for Keep {
let wall = make_block(100, 100, 110);
let floor = make_block(120, 80, 50).with_priority(important_layer);
let pole = make_block(90, 70, 50).with_priority(important_layer);
let flag = make_block(50, 170, 100).with_priority(important_layer);
let flag = make_block(self.flag_color.r, self.flag_color.g, self.flag_color.b)
.with_priority(important_layer);
let internal = BlockMask::new(Block::empty(), internal_layer);
let empty = BlockMask::nothing();
let width = branch.locus;
let rampart_width = 2 + branch.locus;
let ceil_height = branch.attr.height;
let width = locus;
let rampart_width = 2 + locus;
let ceil_height = attr.height;
let door_height = 6;
let edge_pos = if (bound_offset.x == rampart_width) ^ (ori == Ori::East) {
pos.y + pos.x
pos.y
} else {
pos.x + pos.y
pos.x
};
let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 };
let inner = Clamp::clamp(
center_offset,
Vec2::new(-5, -branch.len / 2 - 5),
Vec2::new(5, branch.len / 2 + 5),
Vec2::new(-5, -len / 2 - 5),
Vec2::new(5, len / 2 + 5),
);
let min_dist = bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32; //(bound_offset.distance_squared(inner) as f32).sqrt() as i32 + 5;//bound_offset.reduce_max();
@ -108,7 +118,7 @@ impl Archetype for Keep {
foundation
} else if profile.y == ceil_height && min_dist < rampart_width {
if min_dist < width { floor } else { wall }
} else if !branch.attr.is_tower
} else if !attr.is_tower
&& bound_offset.x.abs() == 4
&& min_dist == width + 1
&& profile.y < ceil_height
@ -123,12 +133,9 @@ impl Archetype for Keep {
wall
} else if profile.y >= ceil_height {
if profile.y > ceil_height && min_dist < rampart_width {
if branch.attr.is_tower
&& center_offset == Vec2::zero()
&& profile.y < ceil_height + 16
{
if attr.is_tower && center_offset == Vec2::zero() && profile.y < ceil_height + 16 {
pole
} else if branch.attr.is_tower
} else if attr.is_tower
&& center_offset.x == 0
&& center_offset.y > 0
&& center_offset.y < 8

View File

@ -20,6 +20,8 @@ pub trait Archetype {
center_offset: Vec2<i32>,
z: i32,
ori: Ori,
branch: &Branch<Self::Attr>,
locus: i32,
len: i32,
attr: &Self::Attr,
) -> BlockMask;
}

View File

@ -2,8 +2,7 @@ pub mod archetype;
pub mod skeleton;
// Reexports
pub use self::archetype::Archetype;
pub use self::skeleton::*;
pub use self::{archetype::Archetype, skeleton::*};
use common::terrain::Block;
use rand::prelude::*;
@ -53,8 +52,17 @@ impl<A: Archetype> Building<A> {
.sample_closest(
rpos,
|pos, dist, bound_offset, center_offset, ori, branch| {
self.archetype
.draw(pos, dist, bound_offset, center_offset, rpos.z, ori, branch)
self.archetype.draw(
pos,
dist,
bound_offset,
center_offset,
rpos.z,
ori,
branch.locus,
branch.len,
&branch.attr,
)
},
)
.finish()