mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Better castle variation, improved paths, multi-storey houses
This commit is contained in:
parent
6a41f6aa88
commit
bdb39b8e7f
@ -11,6 +11,11 @@ pub struct Spiral2d {
|
|||||||
impl Spiral2d {
|
impl Spiral2d {
|
||||||
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
|
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
|
||||||
pub fn new() -> Self { Self { layer: 0, i: 0 } }
|
pub fn new() -> Self { Self { layer: 0, i: 0 } }
|
||||||
|
|
||||||
|
pub fn radius(self, radius: i32) -> impl Iterator<Item = Vec2<i32>> {
|
||||||
|
self.take((radius * 2 + 1).pow(2) as usize)
|
||||||
|
.filter(move |pos| pos.magnitude_squared() < (radius + 1).pow(2))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for Spiral2d {
|
impl Iterator for Spiral2d {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
all::ForestKind,
|
all::ForestKind,
|
||||||
block::StructureMeta,
|
block::StructureMeta,
|
||||||
sim::{local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk, WorldSim, Path},
|
sim::{
|
||||||
|
local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, Path, RiverKind, SimChunk, WorldSim,
|
||||||
|
},
|
||||||
util::Sampler,
|
util::Sampler,
|
||||||
Index, CONFIG,
|
Index, CONFIG,
|
||||||
};
|
};
|
||||||
@ -196,6 +198,7 @@ where
|
|||||||
let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?;
|
let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?;
|
||||||
let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?;
|
let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?;
|
||||||
let alt = sim.get_interpolated_monotone(wpos, |chunk| chunk.alt)?;
|
let alt = sim.get_interpolated_monotone(wpos, |chunk| chunk.alt)?;
|
||||||
|
let surface_veg = sim.get_interpolated_monotone(wpos, |chunk| chunk.surface_veg)?;
|
||||||
let chunk_warp_factor = sim.get_interpolated_monotone(wpos, |chunk| chunk.warp_factor)?;
|
let chunk_warp_factor = sim.get_interpolated_monotone(wpos, |chunk| chunk.warp_factor)?;
|
||||||
let sim_chunk = sim.get(chunk_pos)?;
|
let sim_chunk = sim.get(chunk_pos)?;
|
||||||
let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
let neighbor_coef = TerrainChunkSize::RECT_SIZE.map(|e| e as f64);
|
||||||
@ -860,8 +863,8 @@ where
|
|||||||
let cold_stone = Rgb::new(0.57, 0.67, 0.8);
|
let cold_stone = Rgb::new(0.57, 0.67, 0.8);
|
||||||
let hot_stone = Rgb::new(0.07, 0.07, 0.06);
|
let hot_stone = Rgb::new(0.07, 0.07, 0.06);
|
||||||
let warm_stone = Rgb::new(0.77, 0.77, 0.64);
|
let warm_stone = Rgb::new(0.77, 0.77, 0.64);
|
||||||
let beach_sand = Rgb::new(0.9, 0.82, 0.6);
|
let beach_sand = Rgb::new(0.8, 0.75, 0.5);
|
||||||
let desert_sand = Rgb::new(0.95, 0.75, 0.5);
|
let desert_sand = Rgb::new(0.7, 0.4, 0.25);
|
||||||
let snow = Rgb::new(0.8, 0.85, 1.0);
|
let snow = Rgb::new(0.8, 0.85, 1.0);
|
||||||
|
|
||||||
let stone_col = Rgb::new(195, 187, 201);
|
let stone_col = Rgb::new(195, 187, 201);
|
||||||
@ -1088,11 +1091,15 @@ where
|
|||||||
water_level,
|
water_level,
|
||||||
warp_factor,
|
warp_factor,
|
||||||
surface_color: Rgb::lerp(
|
surface_color: Rgb::lerp(
|
||||||
Rgb::lerp(cliff, sand, alt.sub(basement).mul(0.25)),
|
sub_surface_color,
|
||||||
// Land
|
Rgb::lerp(
|
||||||
ground,
|
Rgb::lerp(cliff, sand, alt.sub(basement).mul(0.25)),
|
||||||
// Beach
|
// Land
|
||||||
((ocean_level - 1.0) / 2.0).max(0.0),
|
ground,
|
||||||
|
// Beach
|
||||||
|
((ocean_level - 1.0) / 2.0).max(0.0),
|
||||||
|
),
|
||||||
|
surface_veg,
|
||||||
),
|
),
|
||||||
sub_surface_color,
|
sub_surface_color,
|
||||||
// No growing directly on bedrock.
|
// No growing directly on bedrock.
|
||||||
|
@ -37,7 +37,9 @@ pub fn apply_paths_to<'a>(
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((path_dist, path_nearest, path, _)) = col_sample.path.filter(|(dist, _, path, _)| *dist < path.width)
|
if let Some((path_dist, path_nearest, path, _)) = col_sample
|
||||||
|
.path
|
||||||
|
.filter(|(dist, _, path, _)| *dist < path.width)
|
||||||
{
|
{
|
||||||
let inset = 0;
|
let inset = 0;
|
||||||
|
|
||||||
@ -75,9 +77,9 @@ pub fn apply_paths_to<'a>(
|
|||||||
if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 {
|
if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 {
|
||||||
Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8))
|
Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8))
|
||||||
} else {
|
} else {
|
||||||
let path_color = col_sample
|
let path_color = path.surface_color(
|
||||||
.sub_surface_color
|
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
||||||
.map(|e| (e * 255.0 * 0.7) as u8);
|
);
|
||||||
Block::new(BlockKind::Normal, noisy_color(path_color, 8))
|
Block::new(BlockKind::Normal, noisy_color(path_color, 8))
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1699,8 +1699,8 @@ impl WorldSim {
|
|||||||
Some(z0 + z1 + z2 + z3)
|
Some(z0 + z1 + z2 + z3)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the distance to the nearest path in blocks, along with the closest point on the path
|
/// Return the distance to the nearest path in blocks, along with the closest point on the
|
||||||
/// and the tangent vector of that path.
|
/// path, the path metadata, and the tangent vector of that path.
|
||||||
pub fn get_nearest_path(&self, wpos: Vec2<i32>) -> Option<(f32, Vec2<f32>, Path, Vec2<f32>)> {
|
pub fn get_nearest_path(&self, wpos: Vec2<i32>) -> Option<(f32, Vec2<f32>, Path, Vec2<f32>)> {
|
||||||
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| {
|
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| {
|
||||||
e.div_euclid(sz as i32)
|
e.div_euclid(sz as i32)
|
||||||
@ -1761,7 +1761,9 @@ impl WorldSim {
|
|||||||
.clamped(0.0, 1.0);
|
.clamped(0.0, 1.0);
|
||||||
let pos = bez.evaluate(nearest_interval);
|
let pos = bez.evaluate(nearest_interval);
|
||||||
let dist_sqrd = pos.distance_squared(wpos.map(|e| e as f32));
|
let dist_sqrd = pos.distance_squared(wpos.map(|e| e as f32));
|
||||||
Some((dist_sqrd, pos, chunk.path.path, move || bez.evaluate_derivative(nearest_interval).normalized()))
|
Some((dist_sqrd, pos, chunk.path.path, move || {
|
||||||
|
bez.evaluate_derivative(nearest_interval).normalized()
|
||||||
|
}))
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -1789,6 +1791,7 @@ pub struct SimChunk {
|
|||||||
pub spawn_rate: f32,
|
pub spawn_rate: f32,
|
||||||
pub river: RiverData,
|
pub river: RiverData,
|
||||||
pub warp_factor: f32,
|
pub warp_factor: f32,
|
||||||
|
pub surface_veg: f32,
|
||||||
|
|
||||||
pub sites: Vec<Id<Site>>,
|
pub sites: Vec<Id<Site>>,
|
||||||
pub place: Option<Id<Place>>,
|
pub place: Option<Id<Place>>,
|
||||||
@ -2024,6 +2027,7 @@ impl SimChunk {
|
|||||||
spawn_rate: 1.0,
|
spawn_rate: 1.0,
|
||||||
river,
|
river,
|
||||||
warp_factor: 1.0,
|
warp_factor: 1.0,
|
||||||
|
surface_veg: 1.0,
|
||||||
|
|
||||||
sites: Vec::new(),
|
sites: Vec::new(),
|
||||||
place: None,
|
place: None,
|
||||||
|
@ -2,7 +2,7 @@ use vek::*;
|
|||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Path {
|
pub struct Path {
|
||||||
pub width: f32,
|
pub width: f32, // Actually radius
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -15,23 +15,27 @@ pub struct PathData {
|
|||||||
|
|
||||||
impl PathData {
|
impl PathData {
|
||||||
pub fn is_path(&self) -> bool { self.neighbors != 0 }
|
pub fn is_path(&self) -> bool { self.neighbors != 0 }
|
||||||
|
|
||||||
|
pub fn clear(&mut self) { self.neighbors = 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PathData {
|
impl Default for PathData {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
offset: Vec2::zero(),
|
offset: Vec2::zero(),
|
||||||
path: Path {
|
path: Path { width: 5.0 },
|
||||||
width: 5.0,
|
|
||||||
},
|
|
||||||
neighbors: 0,
|
neighbors: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Path {
|
impl Path {
|
||||||
/// Return the number of blocks of headspace required at the given path distance
|
/// Return the number of blocks of headspace required at the given path
|
||||||
|
/// distance
|
||||||
pub fn head_space(&self, dist: f32) -> i32 {
|
pub fn head_space(&self, dist: f32) -> i32 {
|
||||||
(8 - (dist * 0.25).powf(6.0).round() as i32).max(1)
|
(8 - (dist * 0.25).powf(6.0).round() as i32).max(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the surface colour of a path given the surrounding surface color
|
||||||
|
pub fn surface_color(&self, col: Rgb<u8>) -> Rgb<u8> { col.map(|e| (e as f32 * 0.7) as u8) }
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
sim::WorldSim,
|
sim::WorldSim,
|
||||||
site::{
|
site::{
|
||||||
settlement::building::{
|
settlement::building::{
|
||||||
archetype::keep::{Attr, Keep},
|
archetype::keep::{Attr, Keep as KeepArchetype},
|
||||||
Archetype, Branch, Ori,
|
Archetype, Branch, Ori,
|
||||||
},
|
},
|
||||||
BlockMask,
|
BlockMask,
|
||||||
@ -18,6 +18,7 @@ use common::{
|
|||||||
comp,
|
comp,
|
||||||
generation::{ChunkSupplement, EntityInfo},
|
generation::{ChunkSupplement, EntityInfo},
|
||||||
npc,
|
npc,
|
||||||
|
spiral::Spiral2d,
|
||||||
store::{Id, Store},
|
store::{Id, Store},
|
||||||
terrain::{Block, BlockKind, Structure, TerrainChunkSize},
|
terrain::{Block, BlockKind, Structure, TerrainChunkSize},
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||||
@ -29,11 +30,12 @@ use rand::prelude::*;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
struct Segment {
|
struct Keep {
|
||||||
offset: Vec2<i32>,
|
offset: Vec2<i32>,
|
||||||
locus: i32,
|
locus: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
is_tower: bool,
|
is_tower: bool,
|
||||||
|
alt: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Tower {
|
struct Tower {
|
||||||
@ -47,29 +49,44 @@ pub struct Castle {
|
|||||||
seed: u32,
|
seed: u32,
|
||||||
radius: i32,
|
radius: i32,
|
||||||
towers: Vec<Tower>,
|
towers: Vec<Tower>,
|
||||||
segments: Vec<Segment>,
|
keeps: Vec<Keep>,
|
||||||
rounded_towers: bool,
|
rounded_towers: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GenCtx<'a, R: Rng> {
|
pub struct GenCtx<'a, R: Rng> {
|
||||||
sim: Option<&'a WorldSim>,
|
sim: Option<&'a mut WorldSim>,
|
||||||
rng: &'a mut R,
|
rng: &'a mut R,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Castle {
|
impl Castle {
|
||||||
#[allow(clippy::let_and_return)] // TODO: Pending review in #587
|
#[allow(clippy::let_and_return)] // TODO: Pending review in #587
|
||||||
pub fn generate(wpos: Vec2<i32>, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self {
|
pub fn generate(wpos: Vec2<i32>, sim: Option<&mut WorldSim>, rng: &mut impl Rng) -> Self {
|
||||||
let mut ctx = GenCtx { sim, rng };
|
let mut ctx = GenCtx { sim, rng };
|
||||||
|
|
||||||
let boundary_towers = ctx.rng.gen_range(5, 10);
|
let boundary_towers = ctx.rng.gen_range(5, 10);
|
||||||
|
let keep_count = ctx.rng.gen_range(1, 4);
|
||||||
let boundary_noise = ctx.rng.gen_range(-2i32, 8).max(1) as f32;
|
let boundary_noise = ctx.rng.gen_range(-2i32, 8).max(1) as f32;
|
||||||
|
|
||||||
let radius = 150;
|
let radius = 150;
|
||||||
|
|
||||||
|
// Adjust ground
|
||||||
|
if let Some(sim) = ctx.sim.as_mut() {
|
||||||
|
for rpos in Spiral2d::new()
|
||||||
|
.radius((radius as f32 * 0.7) as i32 / TerrainChunkSize::RECT_SIZE.x as i32)
|
||||||
|
{
|
||||||
|
sim.get_mut(wpos / TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + rpos)
|
||||||
|
.map(|chunk| {
|
||||||
|
chunk.surface_veg = 0.0;
|
||||||
|
chunk.path.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let this = Self {
|
let this = Self {
|
||||||
origin: wpos,
|
origin: wpos,
|
||||||
alt: ctx
|
alt: ctx
|
||||||
.sim
|
.sim
|
||||||
|
.as_ref()
|
||||||
.and_then(|sim| sim.get_alt_approx(wpos))
|
.and_then(|sim| sim.get_alt_approx(wpos))
|
||||||
.unwrap_or(0.0) as i32
|
.unwrap_or(0.0) as i32
|
||||||
+ 6,
|
+ 6,
|
||||||
@ -87,6 +104,7 @@ impl Castle {
|
|||||||
for i in (1..80).step_by(5) {
|
for i in (1..80).step_by(5) {
|
||||||
if ctx
|
if ctx
|
||||||
.sim
|
.sim
|
||||||
|
.as_ref()
|
||||||
.and_then(|sim| sim.get_nearest_path(wpos + offset))
|
.and_then(|sim| sim.get_nearest_path(wpos + offset))
|
||||||
.map(|(dist, _, _, _)| dist > 24.0)
|
.map(|(dist, _, _, _)| dist > 24.0)
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
@ -101,6 +119,7 @@ impl Castle {
|
|||||||
offset,
|
offset,
|
||||||
alt: ctx
|
alt: ctx
|
||||||
.sim
|
.sim
|
||||||
|
.as_ref()
|
||||||
.and_then(|sim| sim.get_alt_approx(wpos + offset))
|
.and_then(|sim| sim.get_alt_approx(wpos + offset))
|
||||||
.unwrap_or(0.0) as i32
|
.unwrap_or(0.0) as i32
|
||||||
+ 2,
|
+ 2,
|
||||||
@ -108,19 +127,28 @@ impl Castle {
|
|||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
rounded_towers: ctx.rng.gen(),
|
rounded_towers: ctx.rng.gen(),
|
||||||
|
keeps: (0..keep_count)
|
||||||
|
.map(|i| {
|
||||||
|
let angle = (i as f32 / keep_count as f32) * f32::consts::PI * 2.0;
|
||||||
|
let dir = Vec2::new(angle.cos(), angle.sin());
|
||||||
|
let dist =
|
||||||
|
(radius as f32 + ((angle * boundary_noise).sin() - 1.0) * 40.0) * 0.3;
|
||||||
|
|
||||||
segments: (0..0) //rng.gen_range(18, 24))
|
let locus = ctx.rng.gen_range(20, 26);
|
||||||
.map(|_| {
|
let offset = (dir * dist).map(|e| e as i32);
|
||||||
let dir = Vec2::new(ctx.rng.gen_range(-1.0, 1.0), ctx.rng.gen_range(-1.0, 1.0))
|
let height = ctx.rng.gen_range(25, 70).clamped(30, 48);
|
||||||
.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 {
|
Keep {
|
||||||
offset: (dir * dist).map(|e| e as i32),
|
offset,
|
||||||
locus: ctx.rng.gen_range(6, 26),
|
locus,
|
||||||
height: height as i32,
|
height,
|
||||||
is_tower: height > 36.0,
|
is_tower: true,
|
||||||
|
alt: ctx
|
||||||
|
.sim
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|sim| sim.get_alt_approx(wpos + offset))
|
||||||
|
.unwrap_or(0.0) as i32
|
||||||
|
+ 2,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
@ -209,7 +237,8 @@ impl Castle {
|
|||||||
} else {
|
} else {
|
||||||
rpos.yx()
|
rpos.yx()
|
||||||
};
|
};
|
||||||
let head_space = col_sample.path
|
let head_space = col_sample
|
||||||
|
.path
|
||||||
.map(|(dist, _, path, _)| path.head_space(dist))
|
.map(|(dist, _, path, _)| path.head_space(dist))
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
@ -220,34 +249,37 @@ impl Castle {
|
|||||||
col_sample
|
col_sample
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let keep_archetype = KeepArchetype {
|
||||||
|
flag_color: Rgb::new(200, 80, 40),
|
||||||
|
};
|
||||||
|
|
||||||
for z in -10..64 {
|
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 {
|
// Boundary wall
|
||||||
flag_color: Rgb::new(200, 80, 40),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Boundary
|
|
||||||
let wall_z = wpos.z - wall_alt;
|
let wall_z = wpos.z - wall_alt;
|
||||||
let mut mask = if z < head_space {
|
if z < head_space {
|
||||||
BlockMask::nothing()
|
continue;
|
||||||
} else {
|
}
|
||||||
keep.draw(
|
|
||||||
Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt,
|
let mut mask = keep_archetype.draw(
|
||||||
wall_dist,
|
Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt,
|
||||||
Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()),
|
wall_dist,
|
||||||
rpos - wall_pos,
|
Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()),
|
||||||
wall_z,
|
rpos - wall_pos,
|
||||||
wall_ori,
|
wall_z,
|
||||||
4,
|
wall_ori,
|
||||||
0,
|
4,
|
||||||
&Attr {
|
0,
|
||||||
height: 16,
|
&Attr {
|
||||||
is_tower: false,
|
height: 16,
|
||||||
rounded: true,
|
is_tower: false,
|
||||||
},
|
ridged: false,
|
||||||
)
|
rounded: true,
|
||||||
};
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Boundary towers
|
||||||
for tower in &self.towers {
|
for tower in &self.towers {
|
||||||
let tower_wpos = Vec3::new(
|
let tower_wpos = Vec3::new(
|
||||||
self.origin.x + tower.offset.x,
|
self.origin.x + tower.offset.x,
|
||||||
@ -257,7 +289,7 @@ impl Castle {
|
|||||||
let tower_locus = 10;
|
let tower_locus = 10;
|
||||||
|
|
||||||
let border_pos = (tower_wpos - wpos).xy().map(|e| e.abs());
|
let border_pos = (tower_wpos - wpos).xy().map(|e| e.abs());
|
||||||
mask = mask.resolve_with(keep.draw(
|
mask = mask.resolve_with(keep_archetype.draw(
|
||||||
if (tower_wpos.x - wpos.x).abs() < (tower_wpos.y - wpos.y).abs() {
|
if (tower_wpos.x - wpos.x).abs() < (tower_wpos.y - wpos.y).abs() {
|
||||||
wpos - tower_wpos
|
wpos - tower_wpos
|
||||||
} else {
|
} else {
|
||||||
@ -277,6 +309,42 @@ impl Castle {
|
|||||||
&Attr {
|
&Attr {
|
||||||
height: 28,
|
height: 28,
|
||||||
is_tower: true,
|
is_tower: true,
|
||||||
|
ridged: false,
|
||||||
|
rounded: self.rounded_towers,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keeps
|
||||||
|
for keep in &self.keeps {
|
||||||
|
let keep_wpos = Vec3::new(
|
||||||
|
self.origin.x + keep.offset.x,
|
||||||
|
self.origin.y + keep.offset.y,
|
||||||
|
keep.alt,
|
||||||
|
);
|
||||||
|
|
||||||
|
let border_pos = (keep_wpos - wpos).xy().map(|e| e.abs());
|
||||||
|
mask = mask.resolve_with(keep_archetype.draw(
|
||||||
|
if (keep_wpos.x - wpos.x).abs() < (keep_wpos.y - wpos.y).abs() {
|
||||||
|
wpos - keep_wpos
|
||||||
|
} else {
|
||||||
|
Vec3::new(
|
||||||
|
wpos.y - keep_wpos.y,
|
||||||
|
wpos.x - keep_wpos.x,
|
||||||
|
wpos.z - keep_wpos.z,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
border_pos.reduce_max() - keep.locus,
|
||||||
|
Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()),
|
||||||
|
(wpos - keep_wpos).xy(),
|
||||||
|
wpos.z - keep.alt,
|
||||||
|
Ori::East,
|
||||||
|
keep.locus,
|
||||||
|
0,
|
||||||
|
&Attr {
|
||||||
|
height: keep.height,
|
||||||
|
is_tower: keep.is_tower,
|
||||||
|
ridged: true,
|
||||||
rounded: self.rounded_towers,
|
rounded: self.rounded_towers,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
@ -105,7 +105,7 @@ impl Attr {
|
|||||||
},
|
},
|
||||||
mansard: rng.gen_range(-7, 4).max(0),
|
mansard: rng.gen_range(-7, 4).max(0),
|
||||||
pillar: match rng.gen_range(0, 4) {
|
pillar: match rng.gen_range(0, 4) {
|
||||||
0 => Pillar::Chimney(rng.gen_range(1, 5)),
|
0 => Pillar::Chimney(rng.gen_range(2, 6)),
|
||||||
_ => Pillar::None,
|
_ => Pillar::None,
|
||||||
},
|
},
|
||||||
levels: rng.gen_range(1, 3),
|
levels: rng.gen_range(1, 3),
|
||||||
@ -135,7 +135,7 @@ impl Archetype for House {
|
|||||||
storey_fill: StoreyFill::All,
|
storey_fill: StoreyFill::All,
|
||||||
mansard: 0,
|
mansard: 0,
|
||||||
pillar: match rng.gen_range(0, 3) {
|
pillar: match rng.gen_range(0, 3) {
|
||||||
0 => Pillar::Chimney(rng.gen_range(1, 5)),
|
0 => Pillar::Chimney(rng.gen_range(2, 6)),
|
||||||
1 => Pillar::Tower(5 + rng.gen_range(1, 5)),
|
1 => Pillar::Tower(5 + rng.gen_range(1, 5)),
|
||||||
_ => Pillar::None,
|
_ => Pillar::None,
|
||||||
},
|
},
|
||||||
@ -409,7 +409,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
|
// Window frame is large enough for a window
|
||||||
let surface_pos = Vec2::new(bound_offset.x, profile.y - floor_height);
|
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 Some(end_window);
|
return Some(end_window);
|
||||||
} else if frame_bounds.contains_point(surface_pos) {
|
} else if frame_bounds.contains_point(surface_pos) {
|
||||||
|
@ -14,6 +14,7 @@ pub struct Keep {
|
|||||||
pub struct Attr {
|
pub struct Attr {
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
pub is_tower: bool,
|
pub is_tower: bool,
|
||||||
|
pub ridged: bool,
|
||||||
pub rounded: bool,
|
pub rounded: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ impl Archetype for Keep {
|
|||||||
attr: Attr {
|
attr: Attr {
|
||||||
height: rng.gen_range(12, 16),
|
height: rng.gen_range(12, 16),
|
||||||
is_tower: false,
|
is_tower: false,
|
||||||
|
ridged: false,
|
||||||
rounded: true,
|
rounded: true,
|
||||||
},
|
},
|
||||||
locus: 10 + rng.gen_range(0, 5),
|
locus: 10 + rng.gen_range(0, 5),
|
||||||
@ -43,6 +45,7 @@ impl Archetype for Keep {
|
|||||||
attr: Attr {
|
attr: Attr {
|
||||||
height: rng.gen_range(20, 28),
|
height: rng.gen_range(20, 28),
|
||||||
is_tower: true,
|
is_tower: true,
|
||||||
|
ridged: false,
|
||||||
rounded: true,
|
rounded: true,
|
||||||
},
|
},
|
||||||
locus: 4 + rng.gen_range(0, 5),
|
locus: 4 + rng.gen_range(0, 5),
|
||||||
@ -104,15 +107,21 @@ impl Archetype for Keep {
|
|||||||
let internal = BlockMask::new(Block::empty(), internal_layer);
|
let internal = BlockMask::new(Block::empty(), internal_layer);
|
||||||
let empty = BlockMask::nothing();
|
let empty = BlockMask::nothing();
|
||||||
|
|
||||||
let width = locus;
|
let edge_pos = if (bound_offset.x.abs() > bound_offset.y.abs()) ^ (ori == Ori::East) {
|
||||||
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.y
|
||||||
} else {
|
} else {
|
||||||
pos.x
|
pos.x
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let width = locus
|
||||||
|
+ if edge_pos % 4 == 0 && attr.ridged && !attr.rounded {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let rampart_width = 2 + width;
|
||||||
|
let ceil_height = attr.height;
|
||||||
|
let door_height = 6;
|
||||||
let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 };
|
let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 };
|
||||||
let min_dist = if attr.rounded {
|
let min_dist = if attr.rounded {
|
||||||
bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32
|
bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32
|
||||||
|
@ -593,14 +593,15 @@ impl Settlement {
|
|||||||
// if let Some((WayKind::Path, dist, nearest)) = sample.way {
|
// if let Some((WayKind::Path, dist, nearest)) = sample.way {
|
||||||
// let inset = -1;
|
// let inset = -1;
|
||||||
|
|
||||||
// // Try to use the column at the centre of the path for sampling to make them
|
// // Try to use the column at the centre of the path for sampling to make
|
||||||
// // flatter
|
// them // flatter
|
||||||
// let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos))
|
// let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos))
|
||||||
// .unwrap_or(col_sample);
|
// .unwrap_or(col_sample);
|
||||||
// let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist {
|
// let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist {
|
||||||
// (
|
// (
|
||||||
// ((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) * 5.0,
|
// ((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) *
|
||||||
// ((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs())
|
// 5.0, ((1.0 - ((water_dist + 2.0) *
|
||||||
|
// 0.3).min(0.0).cos().abs())
|
||||||
// * (col.riverless_alt + 5.0 - col.alt).max(0.0)
|
// * (col.riverless_alt + 5.0 - col.alt).max(0.0)
|
||||||
// * 1.75
|
// * 1.75
|
||||||
// + 3.0) as i32,
|
// + 3.0) as i32,
|
||||||
@ -614,10 +615,10 @@ impl Settlement {
|
|||||||
// let _ = vol.set(
|
// let _ = vol.set(
|
||||||
// Vec3::new(offs.x, offs.y, surface_z + z),
|
// Vec3::new(offs.x, offs.y, surface_z + z),
|
||||||
// if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 {
|
// if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 {
|
||||||
// Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8))
|
// Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80,
|
||||||
// } else {
|
// 100), 8)) } else {
|
||||||
// Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 50, 30), 8))
|
// Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 50,
|
||||||
// },
|
// 30), 8)) },
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
// let head_space = (8 - (dist * 0.25).powf(6.0).round() as i32).max(1);
|
// let head_space = (8 - (dist * 0.25).powf(6.0).round() as i32).max(1);
|
||||||
@ -639,6 +640,7 @@ impl Settlement {
|
|||||||
Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)),
|
Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)),
|
||||||
Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)),
|
Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)),
|
||||||
Some(Plot::Water) => Some(Rgb::new(100, 150, 250)),
|
Some(Plot::Water) => Some(Rgb::new(100, 150, 250)),
|
||||||
|
Some(Plot::Town { district }) => None,
|
||||||
Some(Plot::Town { district }) => {
|
Some(Plot::Town { district }) => {
|
||||||
if let Some((_, path_nearest, _, _)) = col_sample.path {
|
if let Some((_, path_nearest, _, _)) = col_sample.path {
|
||||||
let path_dir = (path_nearest - wpos2d.map(|e| e as f32))
|
let path_dir = (path_nearest - wpos2d.map(|e| e as f32))
|
||||||
@ -669,65 +671,87 @@ impl Settlement {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Some(Plot::Field { seed, crop, .. }) => {
|
Some(Plot::Field { seed, crop, .. }) => {
|
||||||
let furrow_dirs = [
|
if let Some(color) = col_sample.path.and_then(|(dist, _, path, _)| {
|
||||||
Vec2::new(1, 0),
|
if dist < path.width {
|
||||||
Vec2::new(0, 1),
|
Some(path.surface_color(
|
||||||
Vec2::new(1, 1),
|
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
||||||
Vec2::new(-1, 1),
|
))
|
||||||
];
|
} else {
|
||||||
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
|
None
|
||||||
let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2;
|
}
|
||||||
|
}) {
|
||||||
|
Some(color)
|
||||||
|
} else {
|
||||||
|
let furrow_dirs = [
|
||||||
|
Vec2::new(1, 0),
|
||||||
|
Vec2::new(0, 1),
|
||||||
|
Vec2::new(1, 1),
|
||||||
|
Vec2::new(-1, 1),
|
||||||
|
];
|
||||||
|
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
|
||||||
|
let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2;
|
||||||
|
|
||||||
let dirt = Rgb::new(80, 55, 35).map(|e| {
|
let dirt = Rgb::new(80, 55, 35).map(|e| {
|
||||||
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32)
|
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32))
|
||||||
as u8
|
|
||||||
});
|
|
||||||
let mound =
|
|
||||||
Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| {
|
|
||||||
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32))
|
|
||||||
% 32) as u8
|
% 32) as u8
|
||||||
});
|
});
|
||||||
|
let mound =
|
||||||
|
Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| {
|
||||||
|
e + (self
|
||||||
|
.noise
|
||||||
|
.get(Vec3::broadcast((seed % 4096 + 1) as i32))
|
||||||
|
% 32) as u8
|
||||||
|
});
|
||||||
|
|
||||||
if in_furrow {
|
if in_furrow {
|
||||||
if roll(0, 5) == 0 {
|
if roll(0, 5) == 0 {
|
||||||
surface_block = match crop {
|
surface_block = match crop {
|
||||||
Crop::Corn => Some(BlockKind::Corn),
|
Crop::Corn => Some(BlockKind::Corn),
|
||||||
Crop::Wheat if roll(1, 2) == 0 => {
|
Crop::Wheat if roll(1, 2) == 0 => {
|
||||||
Some(BlockKind::WheatYellow)
|
Some(BlockKind::WheatYellow)
|
||||||
},
|
},
|
||||||
Crop::Wheat => Some(BlockKind::WheatGreen),
|
Crop::Wheat => Some(BlockKind::WheatGreen),
|
||||||
Crop::Cabbage if roll(2, 2) == 0 => {
|
Crop::Cabbage if roll(2, 2) == 0 => {
|
||||||
Some(BlockKind::Cabbage)
|
Some(BlockKind::Cabbage)
|
||||||
},
|
},
|
||||||
Crop::Pumpkin if roll(3, 2) == 0 => {
|
Crop::Pumpkin if roll(3, 2) == 0 => {
|
||||||
Some(BlockKind::Pumpkin)
|
Some(BlockKind::Pumpkin)
|
||||||
},
|
},
|
||||||
Crop::Flax if roll(4, 2) == 0 => Some(BlockKind::Flax),
|
Crop::Flax if roll(4, 2) == 0 => Some(BlockKind::Flax),
|
||||||
Crop::Carrot if roll(5, 2) == 0 => Some(BlockKind::Carrot),
|
Crop::Carrot if roll(5, 2) == 0 => {
|
||||||
Crop::Tomato if roll(6, 2) == 0 => Some(BlockKind::Tomato),
|
Some(BlockKind::Carrot)
|
||||||
Crop::Radish if roll(7, 2) == 0 => Some(BlockKind::Radish),
|
},
|
||||||
Crop::Turnip if roll(8, 2) == 0 => Some(BlockKind::Turnip),
|
Crop::Tomato if roll(6, 2) == 0 => {
|
||||||
Crop::Sunflower => Some(BlockKind::Sunflower),
|
Some(BlockKind::Tomato)
|
||||||
_ => None,
|
},
|
||||||
}
|
Crop::Radish if roll(7, 2) == 0 => {
|
||||||
.or_else(|| {
|
Some(BlockKind::Radish)
|
||||||
if roll(9, 400) == 0 {
|
},
|
||||||
Some(BlockKind::Scarecrow)
|
Crop::Turnip if roll(8, 2) == 0 => {
|
||||||
} else {
|
Some(BlockKind::Turnip)
|
||||||
None
|
},
|
||||||
|
Crop::Sunflower => Some(BlockKind::Sunflower),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
})
|
.or_else(|| {
|
||||||
.map(|kind| Block::new(kind, Rgb::white()));
|
if roll(9, 400) == 0 {
|
||||||
|
Some(BlockKind::Scarecrow)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|kind| Block::new(kind, Rgb::white()));
|
||||||
|
}
|
||||||
|
} else if roll(0, 20) == 0 {
|
||||||
|
surface_block =
|
||||||
|
Some(Block::new(BlockKind::ShortGrass, Rgb::white()));
|
||||||
|
} else if roll(1, 30) == 0 {
|
||||||
|
surface_block =
|
||||||
|
Some(Block::new(BlockKind::MediumGrass, Rgb::white()));
|
||||||
}
|
}
|
||||||
} else if roll(0, 20) == 0 {
|
|
||||||
surface_block =
|
|
||||||
Some(Block::new(BlockKind::ShortGrass, Rgb::white()));
|
|
||||||
} else if roll(1, 30) == 0 {
|
|
||||||
surface_block =
|
|
||||||
Some(Block::new(BlockKind::MediumGrass, Rgb::white()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(if in_furrow { dirt } else { mound })
|
Some(if in_furrow { dirt } else { mound })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user