diff --git a/server-cli/.~lock.economy.csv# b/server-cli/.~lock.economy.csv# deleted file mode 100644 index 7651efaee5..0000000000 --- a/server-cli/.~lock.economy.csv# +++ /dev/null @@ -1 +0,0 @@ -,joshua,archbox.localdomain,17.06.2020 16:07,file:///home/joshua/.config/libreoffice/4; \ No newline at end of file diff --git a/server/src/cmd.rs b/server/src/cmd.rs index a95997a874..a97b687c46 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -15,7 +15,7 @@ use common::{ sync::{Uid, WorldSyncExt}, terrain::{Block, BlockKind, TerrainChunkSize}, util::Dir, - vol::{RectVolSize, WriteVol}, + vol::RectVolSize, LoadoutBuilder, }; use rand::Rng; diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 77ec0186ef..9a34d83de0 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -127,7 +127,7 @@ impl Civs { let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32); let flatten_radius = match &site.kind { - SiteKind::Settlement => 8.0, + SiteKind::Settlement => 10.0, SiteKind::Dungeon => 2.0, SiteKind::Castle => 5.0, }; @@ -148,9 +148,10 @@ impl Civs { 0.0 }; // Raise the town centre up a little let pos = site.center + offs; - let factor = (1.0 + let factor = ((1.0 - (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius) - * 0.8; + * 1.25) + .min(1.0); ctx.sim .get_mut(pos) // Don't disrupt chunks that are near water @@ -170,9 +171,11 @@ impl Civs { let mut cnt = 0; for sim_site in this.sites.values() { cnt += 1; - let wpos = sim_site.center.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { - e * sz as i32 + sz as i32 / 2 - }); + let wpos = sim_site + .center + .map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { + e * sz as i32 + sz as i32 / 2 + }); let mut rng = ctx.reseed().rng; let site = index.sites.insert(match &sim_site.kind { diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 3c0ba76b9d..d90f07d178 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -26,6 +26,7 @@ pub use self::{ use crate::{ all::ForestKind, civ::Place, + column::{ColumnGen, ColumnSample}, site::Site, util::{seed_expan, FastNoise, RandomField, StructureGen2d, LOCALITY, NEIGHBORS}, Index, CONFIG, @@ -1699,8 +1700,9 @@ impl WorldSim { Some(z0 + z1 + z2 + z3) } - /// Return the distance to the nearest path in blocks, along with the closest point on the - /// path, the path metadata, and the tangent vector of that path. + /// Return the distance to the nearest path in blocks, along with the + /// closest point on the path, the path metadata, and the tangent vector + /// of that path. pub fn get_nearest_path(&self, wpos: Vec2) -> Option<(f32, Vec2, Path, Vec2)> { let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { e.div_euclid(sz as i32) diff --git a/world/src/sim2/mod.rs b/world/src/sim2/mod.rs index 8bcf961b61..ad0d8b4ca9 100644 --- a/world/src/sim2/mod.rs +++ b/world/src/sim2/mod.rs @@ -23,7 +23,7 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) { write!(f, "{:?} Value,", g).unwrap(); } for g in Good::list() { - write!(f, "{:?} Price,", g).unwrap(); + write!(f, "{:?} LaborVal,", g).unwrap(); } for g in Good::list() { write!(f, "{:?} Stock,", g).unwrap(); @@ -37,6 +37,9 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) { for l in Labor::list() { write!(f, "{:?} Productivity,", l).unwrap(); } + for l in Labor::list() { + write!(f, "{:?} Yields,", l).unwrap(); + } writeln!(f, "").unwrap(); for i in 0..(HISTORY_DAYS / TICK_PERIOD) as i32 { @@ -53,7 +56,7 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) { write!(f, "{:?},", site.economy.values[*g].unwrap_or(-1.0)).unwrap(); } for g in Good::list() { - write!(f, "{:?},", site.economy.prices[*g]).unwrap(); + write!(f, "{:?},", site.economy.labor_values[*g]).unwrap(); } for g in Good::list() { write!(f, "{:?},", site.economy.stocks[*g]).unwrap(); @@ -67,6 +70,9 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) { for l in Labor::list() { write!(f, "{:?},", site.economy.productivity[*l]).unwrap(); } + for l in Labor::list() { + write!(f, "{:?},", site.economy.yields[*l]).unwrap(); + } writeln!(f, "").unwrap(); } } @@ -144,11 +150,6 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { }; }); - site.economy.prices = site.economy.stocks.clone().map(|g, stock| { - // Price rationalisation - demand[g] / (supply[g] + stocks[g]) - }); - // Update export targets based on relative values let value_avg = values .iter() @@ -169,7 +170,8 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { // Redistribute workforce according to relative good values let labor_ratios = productivity.clone().map(|labor, (output_good, _)| { - site.economy.values[output_good].unwrap_or(0.0) * site.economy.productivity[labor] + site.economy.values[output_good].unwrap_or(0.0) * site.economy.yields[labor] + //(site.economy.prices[output_good] - site.economy.material_costs[output_good]) * site.economy.yields[labor] //* demand[output_good] / supply[output_good].max(0.001) }); let labor_ratio_sum = labor_ratios.iter().map(|(_, r)| *r).sum::().max(0.01); @@ -182,6 +184,7 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { // Production let stocks_before = site.economy.stocks.clone(); + site.economy.labor_values = Default::default(); for (labor, orders) in orders.iter() { let scale = if let Some(labor) = labor { site.economy.labors[*labor] @@ -207,12 +210,16 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { .min_by(|a, b| a.partial_cmp(b).unwrap()) .unwrap_or_else(|| panic!("Industry {:?} requires at least one input order", labor)); + let mut total_materials_cost = 0.0; for (good, amount) in orders { // What quantity is this order requesting? let quantity = *amount * scale; // What amount gets actually used in production? let used = quantity * labor_productivity; + // Material cost of each factor of production + total_materials_cost += used * site.economy.labor_values[*good]; + // Deplete stocks accordingly site.economy.stocks[*good] = (site.economy.stocks[*good] - used).max(0.0); } @@ -222,10 +229,19 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { let (stock, rate) = productivity[*labor]; let workers = site.economy.labors[*labor] * site.economy.pop; let final_rate = rate; - let yield_per_worker = labor_productivity * final_rate; + let yield_per_worker = labor_productivity * final_rate * (1.0 + workers / 100.0); site.economy.yields[*labor] = yield_per_worker; site.economy.productivity[*labor] = labor_productivity; - site.economy.stocks[stock] += yield_per_worker * workers.powf(1.1); + let total_output = yield_per_worker * workers; + site.economy.stocks[stock] += total_output; + + // Materials cost per unit + let material_cost_per_unit = total_materials_cost / total_output.max(0.001); + site.economy.material_costs[stock] = material_cost_per_unit; + site.economy.labor_values[stock] += material_cost_per_unit; + // Labor costs + let wages = 1.0; + site.economy.labor_values[stock] += workers * wages / total_output.max(0.001); } } diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index 9858a6f568..d3039e11bc 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -69,19 +69,6 @@ impl Castle { 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 { origin: wpos, alt: ctx @@ -157,6 +144,23 @@ impl Castle { this } + pub fn contains_point(&self, wpos: Vec2) -> bool { + let lpos = wpos - self.origin; + for i in 0..self.towers.len() { + let tower0 = &self.towers[i]; + let tower1 = &self.towers[(i + 1) % self.towers.len()]; + + if lpos.determine_side(Vec2::zero(), tower0.offset) > 0 + && lpos.determine_side(Vec2::zero(), tower1.offset) <= 0 + && lpos.determine_side(tower0.offset, tower1.offset) > 0 + { + return true; + } + } + + false + } + pub fn get_origin(&self) -> Vec2 { self.origin } pub fn radius(&self) -> f32 { 1200.0 } @@ -182,13 +186,38 @@ impl Castle { let wpos2d = wpos2d + offs; let rpos = wpos2d - self.origin; - // Apply the dungeon entrance + if rpos.magnitude_squared() > (self.radius + 64).pow(2) { + continue; + } + let col_sample = if let Some(col) = get_column(offs) { col } else { continue; }; + // Inner ground + if self.contains_point(wpos2d) { + let surface_z = col_sample.alt as i32; + for z in -5..3 { + let pos = Vec3::new(offs.x, offs.y, surface_z + z); + + if z >= 0 { + if vol.get(pos).unwrap().kind() != BlockKind::Water { + let _ = vol.set(pos, Block::empty()); + } + } else { + let _ = vol.set( + pos, + Block::new( + BlockKind::Normal, + col_sample.sub_surface_color.map(|e| (e * 255.0) as u8), + ), + ); + } + } + } + let (wall_dist, wall_pos, wall_alt, wall_ori, towers) = (0..self.towers.len()) .map(|i| { let tower0 = &self.towers[i]; diff --git a/world/src/site/economy.rs b/world/src/site/economy.rs index fcbacf85d9..26dae4c831 100644 --- a/world/src/site/economy.rs +++ b/world/src/site/economy.rs @@ -35,7 +35,9 @@ pub struct Economy { pub surplus: MapVec, pub marginal_surplus: MapVec, pub values: MapVec>, - pub prices: MapVec, + + pub labor_values: MapVec, + pub material_costs: MapVec, pub labors: MapVec, pub yields: MapVec, @@ -51,7 +53,9 @@ impl Default for Economy { surplus: Default::default(), marginal_surplus: Default::default(), values: Default::default(), - prices: Default::default(), + + labor_values: Default::default(), + material_costs: Default::default(), labors: Default::default(), yields: Default::default(), diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index ca6c190773..0703058d31 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -12,28 +12,60 @@ use common::{ use rand::prelude::*; use vek::*; -const COLOR_THEMES: [Rgb; 17] = [ - Rgb::new(0x1D, 0x4D, 0x45), - Rgb::new(0xB3, 0x7D, 0x60), - Rgb::new(0xAC, 0x5D, 0x26), - Rgb::new(0x32, 0x46, 0x6B), - Rgb::new(0x2B, 0x19, 0x0F), - Rgb::new(0x93, 0x78, 0x51), - Rgb::new(0x92, 0x57, 0x24), - Rgb::new(0x4A, 0x4E, 0x4E), - Rgb::new(0x2F, 0x32, 0x47), - Rgb::new(0x8F, 0x35, 0x43), - Rgb::new(0x6D, 0x1E, 0x3A), - Rgb::new(0x6D, 0xA7, 0x80), - Rgb::new(0x4F, 0xA0, 0x95), - Rgb::new(0xE2, 0xB9, 0x99), - Rgb::new(0x7A, 0x30, 0x22), - Rgb::new(0x4A, 0x06, 0x08), - Rgb::new(0x8E, 0xB4, 0x57), +pub struct ColorTheme { + roof: Rgb, + wall: Rgb, + support: Rgb, +} + +const ROOF_COLORS: &[Rgb] = &[ + // Rgb::new(0x1D, 0x4D, 0x45), + // Rgb::new(0xB3, 0x7D, 0x60), + // Rgb::new(0xAC, 0x5D, 0x26), + // Rgb::new(0x32, 0x46, 0x6B), + // Rgb::new(0x2B, 0x19, 0x0F), + // Rgb::new(0x93, 0x78, 0x51), + // Rgb::new(0x92, 0x57, 0x24), + // Rgb::new(0x4A, 0x4E, 0x4E), + // Rgb::new(0x2F, 0x32, 0x47), + // Rgb::new(0x8F, 0x35, 0x43), + // Rgb::new(0x6D, 0x1E, 0x3A), + // Rgb::new(0x6D, 0xA7, 0x80), + // Rgb::new(0x4F, 0xA0, 0x95), + // Rgb::new(0xE2, 0xB9, 0x99), + // Rgb::new(0x7A, 0x30, 0x22), + // Rgb::new(0x4A, 0x06, 0x08), + // Rgb::new(0x8E, 0xB4, 0x57), + Rgb::new(0x99, 0x5E, 0x54), + Rgb::new(0x43, 0x63, 0x64), + Rgb::new(0x76, 0x6D, 0x68), + Rgb::new(0x7B, 0x41, 0x61), + Rgb::new(0x52, 0x20, 0x20), + Rgb::new(0x1A, 0x4A, 0x59), + Rgb::new(0xCC, 0x76, 0x4E), +]; + +const WALL_COLORS: &[Rgb] = &[ + Rgb::new(200, 180, 150), + Rgb::new(0xB8, 0xB4, 0xA4), + Rgb::new(0x76, 0x6D, 0x68), + Rgb::new(0xF3, 0xC9, 0x8F), + Rgb::new(0xD3, 0xB7, 0x99), + Rgb::new(0xE1, 0xAB, 0x91), + Rgb::new(0x82, 0x57, 0x4C), + Rgb::new(0xB9, 0x96, 0x77), + Rgb::new(0x6E, 0x4D, 0x3C), +]; + +const SUPPORT_COLORS: &[Rgb] = &[ + Rgb::new(60, 45, 30), + Rgb::new(0x65, 0x55, 0x56), + Rgb::new(0x53, 0x33, 0x13), + Rgb::new(0x58, 0x42, 0x33), ]; pub struct House { - pub roof_color: Rgb, + pub colors: ColorTheme, pub noise: RandomField, pub roof_ribbing: bool, pub roof_ribbing_diagonal: bool, @@ -126,6 +158,7 @@ impl Archetype for House { let len = rng.gen_range(-8, 24).clamped(0, 20); let locus = 6 + rng.gen_range(0, 5); let branches_per_side = 1 + len as usize / 20; + let levels = rng.gen_range(1, 3); let skel = Skeleton { offset: -rng.gen_range(0, len + 7).clamped(0, len), ori: if rng.gen() { Ori::East } else { Ori::North }, @@ -139,7 +172,7 @@ impl Archetype for House { 1 => Pillar::Tower(5 + rng.gen_range(1, 5)), _ => Pillar::None, }, - levels: rng.gen_range(1, 4), + levels, ..Attr::generate(rng, locus) }, locus, @@ -154,7 +187,10 @@ impl Archetype for House { i as i32 * len / (branches_per_side - 1).max(1) as i32, Branch { len: rng.gen_range(8, 16) * flip, - attr: Attr::generate(rng, locus), + attr: Attr { + levels: rng.gen_range(1, 4).min(levels), + ..Attr::generate(rng, locus) + }, locus: (6 + rng.gen_range(0, 3)).min(locus), border: 4, children: Vec::new(), @@ -169,7 +205,11 @@ impl Archetype for House { }; let this = Self { - roof_color: *COLOR_THEMES.choose(rng).unwrap(), + colors: ColorTheme { + roof: *ROOF_COLORS.choose(rng).unwrap(), + wall: *WALL_COLORS.choose(rng).unwrap(), + support: *SUPPORT_COLORS.choose(rng).unwrap(), + }, noise: RandomField::new(rng.gen()), roof_ribbing: rng.gen(), roof_ribbing_diagonal: rng.gen(), @@ -205,7 +245,7 @@ impl Archetype for House { ) }; - let make_block = |r, g, b| { + let make_block = |(r, g, b)| { let nz = self .noise .get(Vec3::new(center_offset.x, center_offset.y, z * 8)); @@ -225,12 +265,11 @@ impl Archetype for House { let foundation_layer = internal_layer + 1; let floor_layer = foundation_layer + 1; - let foundation = make_block(100, 100, 100).with_priority(foundation_layer); - let log = make_block(60, 45, 30); - let floor = make_block(100, 75, 50); - let wall = make_block(200, 180, 150).with_priority(facade_layer); - let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b) - .with_priority(facade_layer - 1); + let foundation = make_block((100, 100, 100)).with_priority(foundation_layer); + let log = make_block(self.colors.support.into_tuple()); + let floor = make_block((100, 75, 50)); + let wall = make_block(self.colors.wall.into_tuple()).with_priority(facade_layer); + let roof = make_block(self.colors.roof.into_tuple()).with_priority(facade_layer - 1); let empty = BlockMask::nothing(); let internal = BlockMask::new(Block::empty(), internal_layer); let end_window = BlockMask::new( diff --git a/world/src/site/settlement/building/mod.rs b/world/src/site/settlement/building/mod.rs index 52fbdd3a7c..0f2e6096cd 100644 --- a/world/src/site/settlement/building/mod.rs +++ b/world/src/site/settlement/building/mod.rs @@ -2,15 +2,15 @@ pub mod archetype; pub mod skeleton; // Reexports -pub use self::{archetype::Archetype, skeleton::*}; +pub use self::{ + archetype::{house::House, keep::Keep, Archetype}, + skeleton::*, +}; use common::terrain::Block; use rand::prelude::*; use vek::*; -pub type HouseBuilding = Building; -pub type KeepBuilding = Building; - pub struct Building { skel: Skeleton, archetype: A, diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index ace88fcf90..99ef6a1fc9 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -2,7 +2,7 @@ pub mod building; mod town; use self::{ - building::{HouseBuilding, KeepBuilding}, + building::{Building, House, Keep}, town::{District, Town}, }; use super::SpawnRules; @@ -86,8 +86,8 @@ const AREA_SIZE: u32 = 32; fn to_tile(e: i32) -> i32 { ((e as f32).div_euclid(AREA_SIZE as f32)).floor() as i32 } pub enum StructureKind { - House(HouseBuilding), - Keep(KeepBuilding), + House(Building), + Keep(Building), } pub struct Structure { @@ -403,12 +403,12 @@ impl Settlement { let structure = Structure { kind: if tile == town_center && i == 0 { - StructureKind::Keep(KeepBuilding::generate( + StructureKind::Keep(Building::::generate( ctx.rng, Vec3::new(house_pos.x, house_pos.y, alt), )) } else { - StructureKind::House(HouseBuilding::generate( + StructureKind::House(Building::::generate( ctx.rng, Vec3::new(house_pos.x, house_pos.y, alt), )) @@ -671,94 +671,79 @@ impl Settlement { })) }, Some(Plot::Field { seed, crop, .. }) => { - if let Some(color) = col_sample.path.and_then(|(dist, _, path, _)| { - if dist < path.width { - Some(path.surface_color( - col_sample.sub_surface_color.map(|e| (e * 255.0) as u8), - )) - } else { - None - } - }) { - 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 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| { - e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) + let dirt = Rgb::new(80, 55, 35).map(|e| { + e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 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 }); - 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 roll(0, 5) == 0 { - surface_block = match crop { - Crop::Corn => Some(BlockKind::Corn), - Crop::Wheat if roll(1, 2) == 0 => { - Some(BlockKind::WheatYellow) - }, - Crop::Wheat => Some(BlockKind::WheatGreen), - Crop::Cabbage if roll(2, 2) == 0 => { - Some(BlockKind::Cabbage) - }, - Crop::Pumpkin if roll(3, 2) == 0 => { - Some(BlockKind::Pumpkin) - }, - Crop::Flax if roll(4, 2) == 0 => Some(BlockKind::Flax), - Crop::Carrot if roll(5, 2) == 0 => { - Some(BlockKind::Carrot) - }, - Crop::Tomato if roll(6, 2) == 0 => { - Some(BlockKind::Tomato) - }, - Crop::Radish if roll(7, 2) == 0 => { - Some(BlockKind::Radish) - }, - Crop::Turnip if roll(8, 2) == 0 => { - Some(BlockKind::Turnip) - }, - Crop::Sunflower => Some(BlockKind::Sunflower), - _ => None, - } - .or_else(|| { - if roll(9, 400) == 0 { - Some(BlockKind::Scarecrow) - } else { - None - } - }) - .map(|kind| Block::new(kind, Rgb::white())); + if in_furrow { + if roll(0, 5) == 0 { + surface_block = match crop { + Crop::Corn => Some(BlockKind::Corn), + Crop::Wheat if roll(1, 2) == 0 => { + Some(BlockKind::WheatYellow) + }, + Crop::Wheat => Some(BlockKind::WheatGreen), + Crop::Cabbage if roll(2, 2) == 0 => { + Some(BlockKind::Cabbage) + }, + Crop::Pumpkin if roll(3, 2) == 0 => { + Some(BlockKind::Pumpkin) + }, + Crop::Flax if roll(4, 2) == 0 => Some(BlockKind::Flax), + Crop::Carrot if roll(5, 2) == 0 => Some(BlockKind::Carrot), + Crop::Tomato if roll(6, 2) == 0 => Some(BlockKind::Tomato), + Crop::Radish if roll(7, 2) == 0 => Some(BlockKind::Radish), + Crop::Turnip if roll(8, 2) == 0 => Some(BlockKind::Turnip), + Crop::Sunflower => Some(BlockKind::Sunflower), + _ => None, } - } 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())); + .or_else(|| { + if roll(9, 400) == 0 { + Some(BlockKind::Scarecrow) + } else { + None + } + }) + .map(|kind| Block::new(kind, Rgb::white())); } - - Some(if in_furrow { dirt } else { mound }) + } 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 }) }, _ => None, }; if let Some(color) = color { - if col_sample.water_dist.map(|dist| dist > 2.0).unwrap_or(true) { + let is_path = col_sample + .path + .map(|(dist, _, path, _)| dist < path.width) + .unwrap_or(false); + + if col_sample.water_dist.map(|dist| dist > 2.0).unwrap_or(true) && !is_path + { let diff = (surface_z - land_surface_z).abs(); + for z in -8 - diff..3 + diff { let pos = Vec3::new(offs.x, offs.y, surface_z + z);