Better castle variation, improved paths, multi-storey houses

This commit is contained in:
Joshua Barretto 2020-07-03 00:14:07 +01:00
parent 6a41f6aa88
commit bdb39b8e7f
9 changed files with 253 additions and 130 deletions

View File

@ -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 {

View File

@ -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.

View File

@ -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))
}, },
); );

View File

@ -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,

View File

@ -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) }
} }

View File

@ -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,
}, },
)); ));

View File

@ -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) {

View File

@ -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

View File

@ -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,
}; };