From ae8195fac93525d251b665ca865097aa4df0bd18 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 14 Jun 2020 14:03:48 +0100 Subject: [PATCH 01/33] Added simple keeps --- .../settlement/building/archetype/house.rs | 1 + .../settlement/building/archetype/keep.rs | 65 +++++++++--- .../site/settlement/building/archetype/mod.rs | 1 + world/src/site/settlement/building/mod.rs | 8 +- .../src/site/settlement/building/skeleton.rs | 6 +- world/src/site/settlement/mod.rs | 98 ++++++++++++------- 6 files changed, 123 insertions(+), 56 deletions(-) diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index a462551a9c..e9a4d2129c 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -165,6 +165,7 @@ impl Archetype for House { #[allow(clippy::int_plus_one)] // TODO: Pending review in #587 fn draw( &self, + pos: Vec3, dist: i32, bound_offset: Vec2, center_offset: Vec2, diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 0d3d87dbe8..5d957a66cb 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -13,16 +13,16 @@ impl Archetype for Keep { type Attr = (); fn generate(rng: &mut R) -> (Self, Skeleton) { - let len = rng.gen_range(-8, 12).max(0); + let len = rng.gen_range(-8, 20).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: 5 + rng.gen_range(0, 5), + locus: 6 + rng.gen_range(0, 5), border: 3, - children: (0..rng.gen_range(0, 4)) + children: (0..1) .map(|_| { ( rng.gen_range(-5, len + 5).clamped(0, len.max(1) - 1), @@ -45,36 +45,71 @@ impl Archetype for Keep { #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 fn draw( &self, + pos: Vec3, dist: i32, bound_offset: Vec2, _center_offset: Vec2, z: i32, - _ori: Ori, + ori: Ori, branch: &Branch, ) -> BlockMask { let profile = Vec2::new(bound_offset.x, z); + let weak_layer = 1; + let normal_layer = weak_layer + 1; + let important_layer = normal_layer + 1; + let internal_layer = important_layer + 1; + let make_block = - |r, g, b| BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(r, g, b)), 2); + |r, g, b| BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(r, g, b)), normal_layer); let foundation = make_block(100, 100, 100); - let wall = make_block(75, 100, 125); - let roof = make_block(150, 120, 50); - let empty = BlockMask::new(Block::empty(), 2); + let wall = make_block(100, 100, 110); + let floor = make_block(120, 80, 50).with_priority(important_layer); + let internal = BlockMask::new(Block::empty(), internal_layer); + let empty = BlockMask::nothing(); let width = branch.locus; - let rampart_width = 5 + branch.locus; - let ceil_height = 16; + let rampart_width = 2 + branch.locus; + let ceil_height = 12; + let door_height = 6; + let edge_pos = if (bound_offset.x == rampart_width) ^ (ori == Ori::East) { + pos.y + } else { + pos.x + }; + let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 }; + let min_dist = bound_offset.reduce_max(); - if profile.y <= 1 - (dist - width - 1).max(0) && dist < width + 3 { + if profile.y <= 0 - (min_dist - width - 1).max(0) && min_dist < width + 3 { // Foundations foundation - } else if profile.y == ceil_height && dist < rampart_width { - roof - } else if dist == rampart_width && profile.y >= ceil_height && profile.y < ceil_height + 4 { + } else if profile.y == ceil_height && min_dist < rampart_width { + if min_dist < width { + floor + } else { + wall + } + } else if bound_offset.x.abs() == 4 && min_dist == width + 1 && profile.y < ceil_height { wall - } else if dist == width && profile.y <= ceil_height { + } else if bound_offset.x.abs() < 3 && profile.y < door_height - bound_offset.x.abs() && profile.y > 0 { + internal + } else if min_dist == width && profile.y <= ceil_height { wall + } else if profile.y >= ceil_height { + if profile.y > ceil_height && min_dist < rampart_width { + internal + } else if min_dist == rampart_width { + if profile.y < rampart_height { + wall + } else { + internal + } + } else { + empty + } + } else if profile.y < ceil_height && min_dist < width { + internal } else { empty } diff --git a/world/src/site/settlement/building/archetype/mod.rs b/world/src/site/settlement/building/archetype/mod.rs index 89ca5dca24..6da93ab50d 100644 --- a/world/src/site/settlement/building/archetype/mod.rs +++ b/world/src/site/settlement/building/archetype/mod.rs @@ -14,6 +14,7 @@ pub trait Archetype { Self: Sized; fn draw( &self, + pos: Vec3, dist: i32, bound_offset: Vec2, center_offset: Vec2, diff --git a/world/src/site/settlement/building/mod.rs b/world/src/site/settlement/building/mod.rs index 7687bd4bf9..6c578e94e6 100644 --- a/world/src/site/settlement/building/mod.rs +++ b/world/src/site/settlement/building/mod.rs @@ -10,6 +10,8 @@ use rand::prelude::*; use vek::*; pub type HouseBuilding = Building; +pub type KeepBuilding = Building; + pub struct Building { skel: Skeleton, @@ -50,10 +52,10 @@ impl Building { let rpos = pos - self.origin; self.skel .sample_closest( - rpos.into(), - |dist, bound_offset, center_offset, ori, branch| { + rpos, + |pos, dist, bound_offset, center_offset, ori, branch| { self.archetype - .draw(dist, bound_offset, center_offset, rpos.z, ori, branch) + .draw(pos, dist, bound_offset, center_offset, rpos.z, ori, branch) }, ) .finish() diff --git a/world/src/site/settlement/building/skeleton.rs b/world/src/site/settlement/building/skeleton.rs index 2014810b8f..01827a958d 100644 --- a/world/src/site/settlement/building/skeleton.rs +++ b/world/src/site/settlement/building/skeleton.rs @@ -76,8 +76,8 @@ impl Skeleton { #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 pub fn sample_closest( &self, - pos: Vec2, - mut f: impl FnMut(i32, Vec2, Vec2, Ori, &Branch) -> BlockMask, + pos: Vec3, + mut f: impl FnMut(Vec3, i32, Vec2, Vec2, Ori, &Branch) -> BlockMask, ) -> BlockMask { let mut min = None::<(_, BlockMask)>; self.for_each(|node, ori, branch, is_child, parent_locus| { @@ -117,7 +117,7 @@ impl Skeleton { } || true { - let new_bm = f(dist, bound_offset, center_offset, ori, branch); + let new_bm = f(pos, dist, bound_offset, center_offset, ori, branch); min = min .map(|(_, bm)| (dist_locus, bm.resolve_with(new_bm))) .or(Some((dist_locus, new_bm))); diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 7571df7aab..5fd57221e2 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -1,6 +1,6 @@ mod building; -use self::building::HouseBuilding; +use self::building::{HouseBuilding, KeepBuilding}; use super::SpawnRules; use crate::{ column::ColumnSample, @@ -83,6 +83,8 @@ fn to_tile(e: i32) -> i32 { ((e as f32).div_euclid(AREA_SIZE as f32)).floor() as pub enum StructureKind { House(HouseBuilding), + Keep(KeepBuilding), + } pub struct Structure { @@ -93,6 +95,24 @@ impl Structure { pub fn bounds_2d(&self) -> Aabr { match &self.kind { StructureKind::House(house) => house.bounds_2d(), + StructureKind::Keep(keep) => keep.bounds_2d(), + + } + } + + pub fn bounds(&self) -> Aabb { + match &self.kind { + StructureKind::House(house) => house.bounds(), + StructureKind::Keep(keep) => keep.bounds(), + + } + } + + pub fn sample(&self, rpos: Vec3) -> Option { + match &self.kind { + StructureKind::House(house) => house.sample(rpos), + StructureKind::Keep(keep) => keep.sample(rpos), + } } } @@ -326,9 +346,10 @@ impl Settlement { return; }; - for tile in Spiral2d::new() + for (i, tile) in Spiral2d::new() .map(|offs| town_center + offs) .take(16usize.pow(2)) + .enumerate() { // This is a stupid way to decide how to place buildings for _ in 0..ctx.rng.gen_range(2, 5) { @@ -356,17 +377,31 @@ impl Settlement { } let structure = Structure { - kind: StructureKind::House(HouseBuilding::generate( - ctx.rng, - Vec3::new( - house_pos.x, - house_pos.y, - ctx.sim - .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) - .unwrap_or(0.0) - .ceil() as i32, - ), - )), + kind: if i == 0 { + StructureKind::Keep(KeepBuilding::generate( + ctx.rng, + Vec3::new( + house_pos.x, + house_pos.y, + ctx.sim + .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) + .unwrap_or(0.0) + .ceil() as i32, + ), + )) + } else { + StructureKind::House(HouseBuilding::generate( + ctx.rng, + Vec3::new( + house_pos.x, + house_pos.y, + ctx.sim + .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) + .unwrap_or(0.0) + .ceil() as i32, + ), + )) + }, }; let bounds = structure.bounds_2d(); @@ -728,33 +763,26 @@ impl Settlement { continue; } - match &structure.kind { - StructureKind::House(b) => { - let bounds = b.bounds(); + let bounds = structure.bounds(); - for x in bounds.min.x..bounds.max.x + 1 { - for y in bounds.min.y..bounds.max.y + 1 { - let col = if let Some(col) = - get_column(self.origin + Vec2::new(x, y) - wpos2d) - { - col - } else { - continue; - }; + for x in bounds.min.x..bounds.max.x + 1 { + for y in bounds.min.y..bounds.max.y + 1 { + let col = if let Some(col) = get_column(self.origin + Vec2::new(x, y) - wpos2d) { + col + } else { + continue; + }; - for z in bounds.min.z.min(col.alt.floor() as i32 - 1)..bounds.max.z + 1 - { - let rpos = Vec3::new(x, y, z); - let wpos = Vec3::from(self.origin) + rpos; - let coffs = wpos - Vec3::from(wpos2d); + for z in bounds.min.z.min(col.alt.floor() as i32 - 1)..bounds.max.z + 1 { + let rpos = Vec3::new(x, y, z); + let wpos = Vec3::from(self.origin) + rpos; + let coffs = wpos - Vec3::from(wpos2d); - if let Some(block) = b.sample(rpos) { - let _ = vol.set(coffs, block); - } - } + if let Some(block) = structure.sample(rpos) { + let _ = vol.set(coffs, block); } } - }, + } } } } From f21a50e393b0405ca1d91a32f0a9ca04415d9a7c Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 17 Jun 2020 19:05:47 +0100 Subject: [PATCH 02/33] Added forts to towns, began better economy sim --- common/src/store.rs | 17 +- server-cli/.gitignore | 2 +- server-cli/.~lock.economy.csv# | 1 + server/src/cmd.rs | 2 +- server/src/lib.rs | 7 +- world/src/block/mod.rs | 26 +- world/src/block/natural.rs | 4 +- world/src/civ/mod.rs | 126 ++------- world/src/column/mod.rs | 13 +- world/src/index.rs | 8 + world/src/lib.rs | 39 ++- world/src/sim/map.rs | 6 +- world/src/sim/mod.rs | 8 +- world/src/sim2/mod.rs | 250 ++++++++++++++++++ world/src/site/block_mask.rs | 39 +++ world/src/site/dungeon/mod.rs | 4 +- world/src/site/economy.rs | 145 ++++++++++ world/src/site/mod.rs | 117 +++----- .../settlement/building/archetype/house.rs | 13 +- .../settlement/building/archetype/keep.rs | 80 ++++-- world/src/site/settlement/building/mod.rs | 3 +- world/src/site/settlement/mod.rs | 150 +++++++---- world/src/site/settlement/town.rs | 82 ++++++ world/src/util/map_vec.rs | 79 ++++++ world/src/util/mod.rs | 9 + 25 files changed, 929 insertions(+), 301 deletions(-) create mode 100644 server-cli/.~lock.economy.csv# create mode 100644 world/src/index.rs create mode 100644 world/src/sim2/mod.rs create mode 100644 world/src/site/block_mask.rs create mode 100644 world/src/site/economy.rs create mode 100644 world/src/site/settlement/town.rs create mode 100644 world/src/util/map_vec.rs diff --git a/common/src/store.rs b/common/src/store.rs index 8942e4a722..e3ededbd25 100644 --- a/common/src/store.rs +++ b/common/src/store.rs @@ -50,20 +50,17 @@ impl Store { } pub fn ids(&self) -> impl Iterator> { - // NOTE: Assumes usize fits into 8 bytes. - (0..self.items.len() as u64).map(|i| Id(i, PhantomData)) + (0..self.items.len()).map(|i| Id(i as u64, PhantomData)) } - pub fn iter(&self) -> impl Iterator { self.items.iter() } + pub fn values(&self) -> impl Iterator { self.items.iter() } - pub fn iter_mut(&mut self) -> impl Iterator { self.items.iter_mut() } + pub fn values_mut(&mut self) -> impl Iterator { self.items.iter_mut() } - pub fn iter_ids(&self) -> impl Iterator, &T)> { - self.items - .iter() - .enumerate() - // NOTE: Assumes usize fits into 8 bytes. - .map(|(i, item)| (Id(i as u64, PhantomData), item)) + pub fn iter(&self) -> impl Iterator, &T)> { self.ids().zip(self.values()) } + + pub fn iter_mut(&mut self) -> impl Iterator, &mut T)> { + self.ids().zip(self.values_mut()) } pub fn insert(&mut self, item: T) -> Id { diff --git a/server-cli/.gitignore b/server-cli/.gitignore index 8b13789179..d9bcc8922a 100644 --- a/server-cli/.gitignore +++ b/server-cli/.gitignore @@ -1 +1 @@ - +economy.csv diff --git a/server-cli/.~lock.economy.csv# b/server-cli/.~lock.economy.csv# new file mode 100644 index 0000000000..7651efaee5 --- /dev/null +++ b/server-cli/.~lock.economy.csv# @@ -0,0 +1 @@ +,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 5a282766fd..5ee23d8328 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1437,7 +1437,7 @@ fn handle_debug_column( let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?; let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32); let chunk = sim.get(chunk_pos)?; - let col = sampler.get(wpos)?; + let col = sampler.get((wpos, server.world.index()))?; let downhill = chunk.downhill; let river = &chunk.river; let flux = chunk.flux; diff --git a/server/src/lib.rs b/server/src/lib.rs index 1447793c98..e958ec53a3 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -184,7 +184,7 @@ impl Server { ..WorldOpts::default() }); #[cfg(feature = "worldgen")] - let map = world.sim().get_map(); + let map = world.get_map_data(); #[cfg(not(feature = "worldgen"))] let world = World::generate(settings.world_seed); @@ -219,11 +219,11 @@ impl Server { // get a z cache for the collumn in which we want to spawn let mut block_sampler = world.sample_blocks(); let z_cache = block_sampler - .get_z_cache(spawn_location) + .get_z_cache(spawn_location, world.index()) .expect(&format!("no z_cache found for chunk: {}", spawn_chunk)); // get the minimum and maximum z values at which there could be soild blocks - let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler); + let (min_z, _, max_z) = z_cache.get_z_limits(&mut block_sampler, world.index()); // round range outwards, so no potential air block is missed let min_z = min_z.floor() as i32; let max_z = max_z.ceil() as i32; @@ -239,6 +239,7 @@ impl Server { Vec3::new(spawn_location.x, spawn_location.y, *z), Some(&z_cache), false, + world.index(), ) .map(|b| b.is_air()) .unwrap_or(false) diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 7d3558e066..4edef6b8d4 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -3,7 +3,7 @@ mod natural; use crate::{ column::{ColumnGen, ColumnSample}, util::{RandomField, Sampler, SmallCache}, - CONFIG, + Index, CONFIG, }; use common::{ terrain::{structure::StructureBlock, Block, BlockKind, Structure}, @@ -29,8 +29,9 @@ impl<'a> BlockGen<'a> { column_gen: &ColumnGen<'a>, cache: &'b mut SmallCache>>, wpos: Vec2, + index: &Index, ) -> Option<&'b ColumnSample<'a>> { - cache.get(wpos, |wpos| column_gen.get(wpos)).as_ref() + cache.get(wpos, |wpos| column_gen.get((wpos, index))).as_ref() } fn get_cliff_height( @@ -40,11 +41,16 @@ impl<'a> BlockGen<'a> { close_cliffs: &[(Vec2, u32); 9], cliff_hill: f32, tolerance: f32, + index: &Index, ) -> f32 { close_cliffs.iter().fold( 0.0f32, - |max_height, (cliff_pos, seed)| match Self::sample_column(column_gen, cache, *cliff_pos) - { + |max_height, (cliff_pos, seed)| match Self::sample_column( + column_gen, + cache, + Vec2::from(*cliff_pos), + index, + ) { Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => { let cliff_pos3d = Vec3::from(*cliff_pos); @@ -84,14 +90,14 @@ impl<'a> BlockGen<'a> { ) } - pub fn get_z_cache(&mut self, wpos: Vec2) -> Option> { + pub fn get_z_cache(&mut self, wpos: Vec2, index: &'a Index) -> Option> { let BlockGen { column_cache, column_gen, } = self; // Main sample - let sample = column_gen.get(wpos)?; + let sample = column_gen.get((wpos, index))?; // Tree samples let mut structures = [None, None, None, None, None, None, None, None, None]; @@ -101,7 +107,7 @@ impl<'a> BlockGen<'a> { .zip(structures.iter_mut()) .for_each(|(close_structure, structure)| { if let Some(st) = *close_structure { - let st_sample = Self::sample_column(column_gen, column_cache, st.pos); + let st_sample = Self::sample_column(column_gen, column_cache, st.pos, index); if let Some(st_sample) = st_sample { let st_sample = st_sample.clone(); let st_info = match st.meta { @@ -111,6 +117,7 @@ impl<'a> BlockGen<'a> { st.pos, st.seed, &st_sample, + index, ), Some(meta) => Some(StructureInfo { pos: Vec3::from(st.pos) + Vec3::unit_z() * st_sample.alt as i32, @@ -137,6 +144,7 @@ impl<'a> BlockGen<'a> { wpos: Vec3, z_cache: Option<&ZCache>, only_structures: bool, + index: &Index, ) -> Option { let BlockGen { column_cache, @@ -208,6 +216,7 @@ impl<'a> BlockGen<'a> { &close_cliffs, cliff_hill, 0.0, + index, ); ( @@ -412,7 +421,7 @@ pub struct ZCache<'a> { } impl<'a> ZCache<'a> { - pub fn get_z_limits(&self, block_gen: &mut BlockGen) -> (f32, f32, f32) { + pub fn get_z_limits(&self, block_gen: &mut BlockGen, index: &Index) -> (f32, f32, f32) { let cave_depth = if self.sample.cave_xy.abs() > 0.9 && self.sample.water_level <= self.sample.alt { (self.sample.alt - self.sample.cave_alt + 8.0).max(0.0) @@ -430,6 +439,7 @@ impl<'a> ZCache<'a> { &self.sample.close_cliffs, self.sample.cliff_hill, 32.0, + index, ); let rocks = if self.sample.rock > 0.0 { 12.0 } else { 0.0 }; diff --git a/world/src/block/natural.rs b/world/src/block/natural.rs index 1ea1103ae9..845a033982 100644 --- a/world/src/block/natural.rs +++ b/world/src/block/natural.rs @@ -3,7 +3,7 @@ use crate::{ all::ForestKind, column::{ColumnGen, ColumnSample}, util::{RandomPerm, Sampler, SmallCache, UnitChooser}, - CONFIG, + Index, CONFIG, }; use common::terrain::Structure; use lazy_static::lazy_static; @@ -20,6 +20,7 @@ pub fn structure_gen<'a>( st_pos: Vec2, st_seed: u32, st_sample: &ColumnSample, + index: &'a Index, ) -> Option { // Assuming it's a tree... figure out when it SHOULDN'T spawn let random_seed = (st_seed as f64) / (u32::MAX as f64); @@ -39,6 +40,7 @@ pub fn structure_gen<'a>( &st_sample.close_cliffs, st_sample.cliff_hill, 0.0, + index, ); let wheight = st_sample.alt.max(cliff_height); diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 326ec2b0ae..958f334ee8 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -6,7 +6,8 @@ use self::{Occupation::*, Stock::*}; use crate::{ sim::WorldSim, site::{Dungeon, Settlement, Site as WorldSite}, - util::{attempt, seed_expan, CARDINALS, NEIGHBORS}, + util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS}, + Index, }; use common::{ astar::Astar, @@ -70,7 +71,7 @@ impl<'a, R: Rng> GenCtx<'a, R> { } impl Civs { - pub fn generate(seed: u32, sim: &mut WorldSim) -> Self { + pub fn generate(seed: u32, sim: &mut WorldSim, index: &mut Index) -> Self { let mut this = Self::default(); let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); let mut ctx = GenCtx { sim, rng }; @@ -103,7 +104,7 @@ impl Civs { last_exports: Stocks::from_default(0.0), export_targets: Stocks::from_default(0.0), - trade_states: Stocks::default(), + //trade_states: Stocks::default(), coin: 1000.0, }) }); @@ -116,13 +117,13 @@ impl Civs { } // Flatten ground around sites - for site in this.sites.iter() { + for site in this.sites.values() { let radius = 48i32; let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32); let flatten_radius = match &site.kind { - SiteKind::Settlement => 10.0, + SiteKind::Settlement => 8.0, SiteKind::Dungeon => 2.0, }; @@ -143,7 +144,7 @@ impl Civs { let pos = site.center + offs; let factor = (1.0 - (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius) - * 1.15; + * 0.8; ctx.sim .get_mut(pos) // Don't disrupt chunks that are near water @@ -161,33 +162,32 @@ impl Civs { // Place sites in world let mut cnt = 0; - for site in this.sites.iter() { + for sim_site in this.sites.values() { cnt += 1; - let wpos = site.center.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { + 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 world_site = match &site.kind { + let site = index.sites.insert(match &sim_site.kind { SiteKind::Settlement => { - WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), &mut rng)) + WorldSite::settlement(Settlement::generate(wpos, Some(ctx.sim), &mut rng)) }, SiteKind::Dungeon => { - WorldSite::from(Dungeon::generate(wpos, Some(ctx.sim), &mut rng)) + WorldSite::dungeon(Dungeon::generate(wpos, Some(ctx.sim), &mut rng)) }, - }; + }); + let site_ref = &index.sites[site]; let radius_chunks = - (world_site.radius() / TerrainChunkSize::RECT_SIZE.x as f32).ceil() as usize; + (site_ref.radius() / TerrainChunkSize::RECT_SIZE.x as f32).ceil() as usize; for pos in Spiral2d::new() - .map(|offs| site.center + offs) + .map(|offs| sim_site.center + offs) .take((radius_chunks * 2).pow(2)) { - ctx.sim - .get_mut(pos) - .map(|chunk| chunk.sites.push(world_site.clone())); + ctx.sim.get_mut(pos).map(|chunk| chunk.sites.push(site)); } - debug!(?site.center, "Placed site at location"); + debug!(?sim_site.center, "Placed site at location"); } info!(?cnt, "all sites placed"); @@ -198,18 +198,18 @@ impl Civs { pub fn place(&self, id: Id) -> &Place { self.places.get(id) } - pub fn sites(&self) -> impl Iterator + '_ { self.sites.iter() } + pub fn sites(&self) -> impl Iterator + '_ { self.sites.values() } #[allow(dead_code)] #[allow(clippy::print_literal)] // TODO: Pending review in #587 fn display_info(&self) { - for (id, civ) in self.civs.iter_ids() { + for (id, civ) in self.civs.iter() { println!("# Civilisation {:?}", id); println!("Name: {}", ""); println!("Homeland: {:#?}", self.places.get(civ.homeland)); } - for (id, site) in self.sites.iter_ids() { + for (id, site) in self.sites.iter() { println!("# Site {:?}", id); println!("{:#?}", site); } @@ -290,7 +290,7 @@ impl Civs { last_exports: Stocks::from_default(0.0), export_targets: Stocks::from_default(0.0), - trade_states: Stocks::default(), + //trade_states: Stocks::default(), coin: 1000.0, }) })?; @@ -380,7 +380,7 @@ impl Civs { const MAX_NEIGHBOR_DISTANCE: f32 = 500.0; let mut nearby = self .sites - .iter_ids() + .iter() .map(|(id, p)| (id, (p.center.distance_squared(loc) as f32).sqrt())) .filter(|(_, dist)| *dist < MAX_NEIGHBOR_DISTANCE) .collect::>(); @@ -440,7 +440,7 @@ impl Civs { } fn tick(&mut self, _ctx: &mut GenCtx, years: f32) { - for site in self.sites.iter_mut() { + for site in self.sites.values_mut() { site.simulate(years, &self.places.get(site.place).nat_res); } @@ -717,7 +717,7 @@ pub struct Site { last_exports: Stocks, export_targets: Stocks, - trade_states: Stocks, + //trade_states: Stocks, coin: f32, } @@ -996,79 +996,3 @@ impl Default for TradeState { } pub type Stocks = MapVec; - -#[derive(Clone, Debug)] -pub struct MapVec { - /// We use this hasher (FxHasher32) because - /// (1) we don't care about DDOS attacks (ruling out SipHash); - /// (2) we care about determinism across computers (ruling out AAHash); - /// (3) we have 1-byte keys (for which FxHash is supposedly fastest). - entries: HashMap>, - default: T, -} - -/// Need manual implementation of Default since K doesn't need that bound. -impl Default for MapVec { - fn default() -> Self { - Self { - entries: Default::default(), - default: Default::default(), - } - } -} - -impl MapVec { - pub fn from_list<'a>(i: impl IntoIterator, default: T) -> Self - where - K: 'a, - T: 'a, - { - Self { - entries: i.into_iter().cloned().collect(), - default, - } - } - - pub fn from_default(default: T) -> Self { - Self { - entries: HashMap::default(), - default, - } - } - - pub fn get_mut(&mut self, entry: K) -> &mut T { - let default = &self.default; - self.entries.entry(entry).or_insert_with(|| default.clone()) - } - - pub fn get(&self, entry: K) -> &T { self.entries.get(&entry).unwrap_or(&self.default) } - - pub fn map(self, mut f: impl FnMut(K, T) -> U) -> MapVec { - MapVec { - entries: self - .entries - .into_iter() - .map(|(s, v)| (s, f(s, v))) - .collect(), - default: U::default(), - } - } - - pub fn iter(&self) -> impl Iterator + '_ { - self.entries.iter().map(|(s, v)| (*s, v)) - } - - pub fn iter_mut(&mut self) -> impl Iterator + '_ { - self.entries.iter_mut().map(|(s, v)| (*s, v)) - } -} - -impl std::ops::Index for MapVec { - type Output = T; - - fn index(&self, entry: K) -> &Self::Output { self.get(entry) } -} - -impl std::ops::IndexMut for MapVec { - fn index_mut(&mut self, entry: K) -> &mut Self::Output { self.get_mut(entry) } -} diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 7a0619b51f..bbcc762428 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -3,7 +3,7 @@ use crate::{ block::StructureMeta, sim::{local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk, WorldSim}, util::Sampler, - CONFIG, + Index, CONFIG, }; use common::{terrain::TerrainChunkSize, vol::RectVolSize}; use noise::NoiseFn; @@ -165,8 +165,11 @@ pub fn quadratic_nearest_point( min_root } -impl<'a> Sampler<'a> for ColumnGen<'a> { - type Index = Vec2; +impl<'a, 'b> Sampler<'b> for ColumnGen<'a> +where + 'a: 'b, +{ + type Index = (Vec2, &'b Index); type Sample = Option>; #[allow(clippy::float_cmp)] // TODO: Pending review in #587 @@ -174,7 +177,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { #[allow(clippy::nonminimal_bool)] // TODO: Pending review in #587 #[allow(clippy::single_match)] // TODO: Pending review in #587 #[allow(clippy::bind_instead_of_map)] // TODO: Pending review in #587 - fn get(&self, wpos: Vec2) -> Option> { + fn get(&self, (wpos, index): Self::Index) -> Option> { let wposf = wpos.map(|e| e as f64); let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32); @@ -1098,7 +1101,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { tree_density: if sim_chunk .sites .iter() - .all(|site| site.spawn_rules(wpos).trees) + .all(|site| index.sites[*site].spawn_rules(wpos).trees) { Lerp::lerp(0.0, tree_density, alt.sub(2.0).sub(basement).mul(0.5)) } else { diff --git a/world/src/index.rs b/world/src/index.rs new file mode 100644 index 0000000000..242cbf3dcc --- /dev/null +++ b/world/src/index.rs @@ -0,0 +1,8 @@ +use crate::site::Site; +use common::store::{Id, Store}; + +#[derive(Default)] +pub struct Index { + pub time: f32, + pub sites: Store, +} diff --git a/world/src/lib.rs b/world/src/lib.rs index 12e6112947..67769a1d67 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -8,8 +8,10 @@ mod block; pub mod civ; mod column; pub mod config; +pub mod index; pub mod layer; pub mod sim; +pub mod sim2; pub mod site; pub mod util; @@ -19,6 +21,7 @@ pub use crate::config::CONFIG; use crate::{ block::BlockGen, column::{ColumnGen, ColumnSample}, + index::Index, util::{Grid, Sampler}, }; use common::{ @@ -39,26 +42,35 @@ pub enum Error { pub struct World { sim: sim::WorldSim, civs: civ::Civs, + index: Index, } impl World { pub fn generate(seed: u32, opts: sim::WorldOpts) -> Self { let mut sim = sim::WorldSim::generate(seed, opts); - let civs = civ::Civs::generate(seed, &mut sim); - Self { sim, civs } + let mut index = Index::default(); + let civs = civ::Civs::generate(seed, &mut sim, &mut index); + + sim2::simulate(&mut index, &mut sim); + + Self { sim, civs, index } } pub fn sim(&self) -> &sim::WorldSim { &self.sim } pub fn civs(&self) -> &civ::Civs { &self.civs } + pub fn index(&self) -> &Index { &self.index } + pub fn tick(&self, _dt: Duration) { // TODO } + pub fn get_map_data(&self) -> Vec { self.sim.get_map(&self.index) } + pub fn sample_columns( &self, - ) -> impl Sampler, Sample = Option> + '_ { + ) -> impl Sampler, &Index), Sample = Option> + '_ { ColumnGen::new(&self.sim) } @@ -77,7 +89,7 @@ impl World { let grid_border = 4; let zcache_grid = Grid::populate_from( TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, - |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs), + |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, &self.index), ); let air = Block::empty(); @@ -132,7 +144,8 @@ impl World { _ => continue, }; - let (min_z, only_structures_min_z, max_z) = z_cache.get_z_limits(&mut sampler); + let (min_z, only_structures_min_z, max_z) = + z_cache.get_z_limits(&mut sampler, &self.index); (base_z..min_z as i32).for_each(|z| { let _ = chunk.set(Vec3::new(x, y, z), stone); @@ -144,7 +157,7 @@ impl World { let only_structures = lpos.z >= only_structures_min_z as i32; if let Some(block) = - sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures) + sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures, &self.index) { let _ = chunk.set(lpos, block); } @@ -163,10 +176,9 @@ impl World { let mut rng = rand::thread_rng(); // Apply site generation - sim_chunk - .sites - .iter() - .for_each(|site| site.apply_to(chunk_wpos2d, sample_get, &mut chunk)); + sim_chunk.sites.iter().for_each(|site| { + self.index.sites[*site].apply_to(chunk_wpos2d, sample_get, &mut chunk) + }); // Apply paths layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk); @@ -217,7 +229,12 @@ impl World { // Apply site supplementary information sim_chunk.sites.iter().for_each(|site| { - site.apply_supplement(&mut rng, chunk_wpos2d, sample_get, &mut supplement) + self.index.sites[*site].apply_supplement( + &mut rng, + chunk_wpos2d, + sample_get, + &mut supplement, + ) }); Ok((chunk, supplement)) diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index 5365fba293..ebed7fae80 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -1,6 +1,6 @@ use crate::{ sim::{RiverKind, WorldSim, WORLD_SIZE}, - CONFIG, + Index, CONFIG, }; use common::{terrain::TerrainChunkSize, vol::RectVolSize}; use std::{f32, f64}; @@ -114,6 +114,7 @@ impl MapConfig { pub fn generate( &self, sampler: &WorldSim, + index: &Index, mut write_pixel: impl FnMut(Vec2, (u8, u8, u8, u8)), ) -> MapDebug { let MapConfig { @@ -170,7 +171,8 @@ impl MapConfig { sample.river.river_kind, sample.path.is_path(), sample.sites.iter().any(|site| { - site.get_origin() + index.sites[*site] + .get_origin() .distance_squared(pos * TerrainChunkSize::RECT_SIZE.x as i32) < 64i32.pow(2) }), diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index fc53e3a06f..fc5ca00208 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -28,7 +28,7 @@ use crate::{ civ::Place, site::Site, util::{seed_expan, FastNoise, RandomField, StructureGen2d, LOCALITY, NEIGHBORS}, - CONFIG, + Index, CONFIG, }; use common::{ assets, @@ -1305,14 +1305,14 @@ impl WorldSim { /// Draw a map of the world based on chunk information. Returns a buffer of /// u32s. - pub fn get_map(&self) -> Vec { + pub fn get_map(&self, index: &Index) -> Vec { let mut v = vec![0u32; WORLD_SIZE.x * WORLD_SIZE.y]; // TODO: Parallelize again. MapConfig { gain: self.max_height, ..MapConfig::default() } - .generate(&self, |pos, (r, g, b, a)| { + .generate(&self, index, |pos, (r, g, b, a)| { v[pos.y * WORLD_SIZE.x + pos.x] = u32::from_le_bytes([r, g, b, a]); }); v @@ -1788,7 +1788,7 @@ pub struct SimChunk { pub river: RiverData, pub warp_factor: f32, - pub sites: Vec, + pub sites: Vec>, pub place: Option>, pub path: PathData, pub contains_waypoint: bool, diff --git a/world/src/sim2/mod.rs b/world/src/sim2/mod.rs new file mode 100644 index 0000000000..8bcf961b61 --- /dev/null +++ b/world/src/sim2/mod.rs @@ -0,0 +1,250 @@ +use crate::{ + sim::WorldSim, + site::{ + economy::{Good, Labor}, + Site, + }, + util::MapVec, + Index, +}; +use common::store::Id; +use tracing::{debug, info, warn}; + +const MONTH: f32 = 30.0; +const YEAR: f32 = 12.0 * MONTH; +const TICK_PERIOD: f32 = 3.0 * MONTH; // 3 months +const HISTORY_DAYS: f32 = 500.0 * YEAR; // 500 years + +pub fn simulate(index: &mut Index, world: &mut WorldSim) { + use std::io::Write; + let mut f = std::fs::File::create("economy.csv").unwrap(); + write!(f, "Population,").unwrap(); + for g in Good::list() { + write!(f, "{:?} Value,", g).unwrap(); + } + for g in Good::list() { + write!(f, "{:?} Price,", g).unwrap(); + } + for g in Good::list() { + write!(f, "{:?} Stock,", g).unwrap(); + } + for g in Good::list() { + write!(f, "{:?} Surplus,", g).unwrap(); + } + for l in Labor::list() { + write!(f, "{:?} Labor,", l).unwrap(); + } + for l in Labor::list() { + write!(f, "{:?} Productivity,", l).unwrap(); + } + writeln!(f, "").unwrap(); + + for i in 0..(HISTORY_DAYS / TICK_PERIOD) as i32 { + if (index.time / YEAR) as i32 % 50 == 0 && (index.time % YEAR) as i32 == 0 { + debug!("Year {}", (index.time / YEAR) as i32); + } + + tick(index, world, TICK_PERIOD); + + if i % 5 == 0 { + let site = index.sites.values().next().unwrap(); + write!(f, "{},", site.economy.pop).unwrap(); + for g in Good::list() { + write!(f, "{:?},", site.economy.values[*g].unwrap_or(-1.0)).unwrap(); + } + for g in Good::list() { + write!(f, "{:?},", site.economy.prices[*g]).unwrap(); + } + for g in Good::list() { + write!(f, "{:?},", site.economy.stocks[*g]).unwrap(); + } + for g in Good::list() { + write!(f, "{:?},", site.economy.marginal_surplus[*g]).unwrap(); + } + for l in Labor::list() { + write!(f, "{:?},", site.economy.labors[*l] * site.economy.pop).unwrap(); + } + for l in Labor::list() { + write!(f, "{:?},", site.economy.productivity[*l]).unwrap(); + } + writeln!(f, "").unwrap(); + } + } +} + +pub fn tick(index: &mut Index, world: &mut WorldSim, dt: f32) { + for site in index.sites.ids() { + tick_site_economy(index, site, dt); + } + + index.time += dt; +} + +/// Simulate a site's economy. This simulation is roughly equivalent to the +/// Lange-Lerner model's solution to the socialist calculation problem. The +/// simulation begins by assigning arbitrary values to each commodity and then +/// incrementally updates them according to the final scarcity of the commodity +/// at the end of the tick. This results in the formulation of values that are +/// roughly analgous to prices for each commodity. The workforce is then +/// reassigned according to the respective commodity values. The simulation also +/// includes damping terms that prevent cyclical inconsistencies in value +/// rationalisation magnifying enough to crash the economy. We also ensure that +/// a small number of workers are allocated to every industry (even inactive +/// ones) each tick. This is not an accident: a small amount of productive +/// capacity in one industry allows the economy to quickly pivot to a different +/// prodution configuration should an additional commodity that acts as +/// production input become available. This means that the economy will +/// dynamically react to environmental changes. If a product becomes available +/// through a mechanism such as trade, an entire arm of the economy may +/// materialise to take advantage of this. +pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { + let site = &mut index.sites[site]; + + let orders = site.economy.get_orders(); + let productivity = site.economy.get_productivity(); + + let mut demand = MapVec::from_default(0.0); + for (labor, orders) in &orders { + let scale = if let Some(labor) = labor { + site.economy.labors[*labor] + } else { + 1.0 + } * site.economy.pop; + for (good, amount) in orders { + demand[*good] += *amount * scale; + } + } + + let mut supply = MapVec::from_default(0.0); + for (labor, (output_good, _)) in productivity.iter() { + supply[*output_good] += + site.economy.yields[labor] * site.economy.labors[labor] * site.economy.pop; + } + + let stocks = &site.economy.stocks; + site.economy.surplus = demand + .clone() + .map(|g, demand| supply[g] + stocks[g] - demand); + site.economy.marginal_surplus = demand.clone().map(|g, demand| supply[g] - demand); + + // Update values according to the surplus of each stock + // Note that values are used for workforce allocation and are not the same thing + // as price + let values = &mut site.economy.values; + let marginal_surplus = &site.economy.marginal_surplus; + let stocks = &site.economy.stocks; + site.economy.surplus.iter().for_each(|(good, surplus)| { + // Value rationalisation + let val = 2.0f32.powf(1.0 - *surplus / demand[good]); + let smooth = 0.8; + values[good] = if val > 0.001 && val < 1000.0 { + Some(smooth * values[good].unwrap_or(val) + (1.0 - smooth) * val) + } else { + None + }; + }); + + 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() + .map(|(_, v)| (*v).unwrap_or(0.0)) + .sum::() + .max(0.01) + / values.iter().filter(|(_, v)| v.is_some()).count() as f32; + //let export_targets = &mut site.economy.export_targets; + //let last_exports = &self.last_exports; + // site.economy.values.iter().for_each(|(stock, value)| { + // let rvalue = (*value).map(|v| v - value_avg).unwrap_or(0.0); + // //let factor = if export_targets[stock] > 0.0 { 1.0 / rvalue } else { + // rvalue }; //export_targets[stock] = last_exports[stock] - rvalue * + // 0.1; // + (trade_states[stock].sell_belief.price - + // trade_states[stock].buy_belief.price) * 0.025; }); + + //let pop = site.economy.pop; + + // 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] + //* demand[output_good] / supply[output_good].max(0.001) + }); + let labor_ratio_sum = labor_ratios.iter().map(|(_, r)| *r).sum::().max(0.01); + productivity.iter().for_each(|(labor, _)| { + let smooth = 0.8; + site.economy.labors[labor] = smooth * site.economy.labors[labor] + + (1.0 - smooth) + * (labor_ratios[labor].max(labor_ratio_sum / 1000.0) / labor_ratio_sum); + }); + + // Production + let stocks_before = site.economy.stocks.clone(); + for (labor, orders) in orders.iter() { + let scale = if let Some(labor) = labor { + site.economy.labors[*labor] + } else { + 1.0 + } * site.economy.pop; + + // For each order, we try to find the minimum satisfaction rate - this limits + // how much we can produce! For example, if we need 0.25 fish and + // 0.75 oats to make 1 unit of food, but only 0.5 units of oats are + // available then we only need to consume 2/3rds + // of other ingredients and leave the rest in stock + // In effect, this is the productivity + let labor_productivity = orders + .iter() + .map(|(good, amount)| { + // What quantity is this order requesting? + let _quantity = *amount * scale; + // What proportion of this order is the economy able to satisfy? + let satisfaction = (stocks_before[*good] / demand[*good]).min(1.0); + satisfaction + }) + .min_by(|a, b| a.partial_cmp(b).unwrap()) + .unwrap_or_else(|| panic!("Industry {:?} requires at least one input order", labor)); + + 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; + + // Deplete stocks accordingly + site.economy.stocks[*good] = (site.economy.stocks[*good] - used).max(0.0); + } + + // Industries produce things + if let Some(labor) = labor { + 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; + site.economy.yields[*labor] = yield_per_worker; + site.economy.productivity[*labor] = labor_productivity; + site.economy.stocks[stock] += yield_per_worker * workers.powf(1.1); + } + } + + // Decay stocks + site.economy + .stocks + .iter_mut() + .for_each(|(c, v)| *v *= 1.0 - c.decay_rate()); + + // Decay stocks + site.economy.replenish(index.time); + + // Births/deaths + const NATURAL_BIRTH_RATE: f32 = 0.05; + const DEATH_RATE: f32 = 0.005; + let birth_rate = if site.economy.surplus[Good::Food] > 0.0 { + NATURAL_BIRTH_RATE + } else { + 0.0 + }; + site.economy.pop += dt / YEAR * site.economy.pop * (birth_rate - DEATH_RATE); +} diff --git a/world/src/site/block_mask.rs b/world/src/site/block_mask.rs new file mode 100644 index 0000000000..d94afcb6eb --- /dev/null +++ b/world/src/site/block_mask.rs @@ -0,0 +1,39 @@ +use common::{terrain::Block, vol::Vox}; + +#[derive(Copy, Clone)] +pub struct BlockMask { + block: Block, + priority: i32, +} + +impl BlockMask { + pub fn new(block: Block, priority: i32) -> Self { Self { block, priority } } + + pub fn nothing() -> Self { + Self { + block: Block::empty(), + priority: 0, + } + } + + pub fn with_priority(mut self, priority: i32) -> Self { + self.priority = priority; + self + } + + pub fn resolve_with(self, other: Self) -> Self { + if self.priority >= other.priority { + self + } else { + other + } + } + + pub fn finish(self) -> Option { + if self.priority > 0 { + Some(self.block) + } else { + None + } + } +} diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 10cecd7807..1d20e97733 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -305,7 +305,7 @@ impl Floor { this.create_rooms(ctx, level, 7); // Create routes between all rooms - let room_areas = this.rooms.iter().map(|r| r.area).collect::>(); + let room_areas = this.rooms.values().map(|r| r.area).collect::>(); for a in room_areas.iter() { for b in room_areas.iter() { this.create_route(ctx, a.center(), b.center()); @@ -342,7 +342,7 @@ impl Floor { // Ensure no overlap if self .rooms - .iter() + .values() .any(|r| r.area.collides_with_rect(area_border)) { return None; diff --git a/world/src/site/economy.rs b/world/src/site/economy.rs new file mode 100644 index 0000000000..fcbacf85d9 --- /dev/null +++ b/world/src/site/economy.rs @@ -0,0 +1,145 @@ +use crate::util::{DHashMap, MapVec}; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Good { + Wheat = 0, + Flour = 1, + Meat = 2, + Fish = 3, + Game = 4, + Food = 5, + Logs = 6, + Wood = 7, + Rock = 8, + Stone = 9, +} +use Good::*; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Labor { + Farmer = 0, + Lumberjack = 1, + Miner = 2, + Fisher = 3, + Hunter = 4, + Cook = 5, +} +use Labor::*; + +pub struct Economy { + pub pop: f32, + + pub stocks: MapVec, + pub surplus: MapVec, + pub marginal_surplus: MapVec, + pub values: MapVec>, + pub prices: MapVec, + + pub labors: MapVec, + pub yields: MapVec, + pub productivity: MapVec, +} + +impl Default for Economy { + fn default() -> Self { + Self { + pop: 32.0, + + stocks: Default::default(), + surplus: Default::default(), + marginal_surplus: Default::default(), + values: Default::default(), + prices: Default::default(), + + labors: Default::default(), + yields: Default::default(), + productivity: Default::default(), + } + } +} + +impl Economy { + pub fn get_orders(&self) -> DHashMap, Vec<(Good, f32)>> { + vec![ + (None, vec![(Food, 0.5)]), + (Some(Cook), vec![ + (Flour, 12.0), + (Meat, 4.0), + (Wood, 1.5), + (Stone, 1.0), + ]), + (Some(Lumberjack), vec![(Logs, 0.5)]), + (Some(Miner), vec![(Rock, 0.5)]), + (Some(Fisher), vec![(Fish, 4.0)]), + (Some(Hunter), vec![(Game, 1.0)]), + (Some(Farmer), vec![(Wheat, 2.0)]), + ] + .into_iter() + .collect() + } + + pub fn get_productivity(&self) -> MapVec { + // Per labourer, per year + MapVec::from_list( + &[ + (Farmer, (Flour, 2.0)), + (Lumberjack, (Wood, 0.5)), + (Miner, (Stone, 0.5)), + (Fisher, (Meat, 3.0)), + (Hunter, (Meat, 1.0)), + (Cook, (Food, 16.0)), + ], + (Rock, 0.0), + ) + .map(|l, (good, v)| (good, v * (1.0 + self.labors[l]))) + } + + pub fn replenish(&mut self, time: f32) { + use rand::Rng; + for (i, (g, v)) in [(Wheat, 195.0), (Logs, 120.0), (Rock, 120.0), (Game, 20.0)] + .iter() + .enumerate() + { + self.stocks[*g] = (*v + * (1.25 + (((time * 0.0001 + i as f32).sin() + 1.0) % 1.0) * 0.5) + - self.stocks[*g]) + * 0.075; //rand::thread_rng().gen_range(0.05, 0.1); + } + } +} + +impl Default for Good { + fn default() -> Self { + Good::Rock // Arbitrary + } +} + +impl Good { + pub fn list() -> &'static [Self] { + static GOODS: [Good; 10] = [ + Wheat, Flour, Meat, Fish, Game, Food, Logs, Wood, Rock, Stone, + ]; + + &GOODS + } + + pub fn decay_rate(&self) -> f32 { + match self { + Food => 0.2, + Wheat => 0.1, + Meat => 0.25, + Fish => 0.2, + _ => 0.0, + } + } +} + +impl Labor { + pub fn list() -> &'static [Self] { + static LABORS: [Labor; 6] = [Farmer, Lumberjack, Miner, Fisher, Hunter, Cook]; + + &LABORS + } +} diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index b0fab2d161..fbfd8196db 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -1,8 +1,10 @@ +mod block_mask; mod dungeon; +pub mod economy; mod settlement; // Reexports -pub use self::{dungeon::Dungeon, settlement::Settlement}; +pub use self::{block_mask::BlockMask, dungeon::Dungeon, economy::Economy, settlement::Settlement}; use crate::column::ColumnSample; use common::{ @@ -14,44 +16,6 @@ use rand::Rng; use std::{fmt, sync::Arc}; use vek::*; -#[derive(Copy, Clone)] -pub struct BlockMask { - block: Block, - priority: i32, -} - -impl BlockMask { - pub fn new(block: Block, priority: i32) -> Self { Self { block, priority } } - - pub fn nothing() -> Self { - Self { - block: Block::empty(), - priority: 0, - } - } - - pub fn with_priority(mut self, priority: i32) -> Self { - self.priority = priority; - self - } - - pub fn resolve_with(self, other: Self) -> Self { - if self.priority >= other.priority { - self - } else { - other - } - } - - pub fn finish(self) -> Option { - if self.priority > 0 { - Some(self.block) - } else { - None - } - } -} - pub struct SpawnRules { pub trees: bool, } @@ -60,31 +24,49 @@ impl Default for SpawnRules { fn default() -> Self { Self { trees: true } } } -#[derive(Clone)] -pub enum Site { - Settlement(Arc), - Dungeon(Arc), +pub struct Site { + pub kind: SiteKind, + pub economy: Economy, +} + +pub enum SiteKind { + Settlement(Settlement), + Dungeon(Dungeon), } impl Site { + pub fn settlement(s: Settlement) -> Self { + Self { + kind: SiteKind::Settlement(s), + economy: Economy::default(), + } + } + + pub fn dungeon(d: Dungeon) -> Self { + Self { + kind: SiteKind::Dungeon(d), + economy: Economy::default(), + } + } + pub fn radius(&self) -> f32 { - match self { - Site::Settlement(settlement) => settlement.radius(), - Site::Dungeon(dungeon) => dungeon.radius(), + match &self.kind { + SiteKind::Settlement(settlement) => settlement.radius(), + SiteKind::Dungeon(dungeon) => dungeon.radius(), } } pub fn get_origin(&self) -> Vec2 { - match self { - Site::Settlement(s) => s.get_origin(), - Site::Dungeon(d) => d.get_origin(), + match &self.kind { + SiteKind::Settlement(s) => s.get_origin(), + SiteKind::Dungeon(d) => d.get_origin(), } } pub fn spawn_rules(&self, wpos: Vec2) -> SpawnRules { - match self { - Site::Settlement(s) => s.spawn_rules(wpos), - Site::Dungeon(d) => d.spawn_rules(wpos), + match &self.kind { + SiteKind::Settlement(s) => s.spawn_rules(wpos), + SiteKind::Dungeon(d) => d.spawn_rules(wpos), } } @@ -94,9 +76,9 @@ impl Site { get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), ) { - match self { - Site::Settlement(settlement) => settlement.apply_to(wpos2d, get_column, vol), - Site::Dungeon(dungeon) => dungeon.apply_to(wpos2d, get_column, vol), + match &self.kind { + SiteKind::Settlement(settlement) => settlement.apply_to(wpos2d, get_column, vol), + SiteKind::Dungeon(dungeon) => dungeon.apply_to(wpos2d, get_column, vol), } } @@ -107,28 +89,13 @@ impl Site { get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, supplement: &mut ChunkSupplement, ) { - match self { - Site::Settlement(settlement) => { + match &self.kind { + SiteKind::Settlement(settlement) => { settlement.apply_supplement(rng, wpos2d, get_column, supplement) }, - Site::Dungeon(dungeon) => dungeon.apply_supplement(rng, wpos2d, get_column, supplement), - } - } -} - -impl From for Site { - fn from(settlement: Settlement) -> Self { Site::Settlement(Arc::new(settlement)) } -} - -impl From for Site { - fn from(dungeon: Dungeon) -> Self { Site::Dungeon(Arc::new(dungeon)) } -} - -impl fmt::Debug for Site { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Site::Settlement(_) => write!(f, "Settlement"), - Site::Dungeon(_) => write!(f, "Dungeon"), + SiteKind::Dungeon(dungeon) => { + dungeon.apply_supplement(rng, wpos2d, get_column, supplement) + }, } } } diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index e9a4d2129c..fd25aff1a6 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -12,7 +12,7 @@ use common::{ use rand::prelude::*; use vek::*; -const COLOR_THEMES: [Rgb; 11] = [ +const COLOR_THEMES: [Rgb; 17] = [ Rgb::new(0x1D, 0x4D, 0x45), Rgb::new(0xB3, 0x7D, 0x60), Rgb::new(0xAC, 0x5D, 0x26), @@ -24,6 +24,12 @@ const COLOR_THEMES: [Rgb; 11] = [ 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 House { @@ -149,10 +155,7 @@ impl Archetype for House { }; let this = Self { - roof_color: COLOR_THEMES - .choose(rng) - .unwrap() - .map(|e| e.saturating_add(rng.gen_range(0, 20)) - 10), + roof_color: *COLOR_THEMES.choose(rng).unwrap(), noise: RandomField::new(rng.gen()), roof_ribbing: rng.gen(), roof_ribbing_diagonal: rng.gen(), diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 5d957a66cb..0016ca05a7 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -9,27 +9,38 @@ use vek::*; pub struct Keep; +pub struct Attr { + height: i32, + is_tower: bool, +} + impl Archetype for Keep { - type Attr = (); + type Attr = Attr; fn generate(rng: &mut R) -> (Self, Skeleton) { - let len = rng.gen_range(-8, 20).max(0); + let len = rng.gen_range(-8, 24).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: 6 + rng.gen_range(0, 5), + attr: Attr { + height: rng.gen_range(12, 16), + is_tower: false, + }, + locus: 10 + rng.gen_range(0, 5), border: 3, children: (0..1) .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: 5 + rng.gen_range(0, 3), + len: 0, + attr: Attr { + height: rng.gen_range(20, 28), + is_tower: true, + }, + locus: 4 + rng.gen_range(0, 5), border: 3, children: Vec::new(), }, @@ -48,7 +59,7 @@ impl Archetype for Keep { pos: Vec3, dist: i32, bound_offset: Vec2, - _center_offset: Vec2, + center_offset: Vec2, z: i32, ori: Ori, branch: &Branch, @@ -60,18 +71,24 @@ impl Archetype for Keep { let important_layer = normal_layer + 1; let internal_layer = important_layer + 1; - let make_block = - |r, g, b| BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(r, g, b)), normal_layer); + let make_block = |r, g, b| { + BlockMask::new( + Block::new(BlockKind::Normal, Rgb::new(r, g, b)), + normal_layer, + ) + }; let foundation = make_block(100, 100, 100); 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 internal = BlockMask::new(Block::empty(), internal_layer); let empty = BlockMask::nothing(); let width = branch.locus; let rampart_width = 2 + branch.locus; - let ceil_height = 12; + let ceil_height = branch.attr.height; let door_height = 6; let edge_pos = if (bound_offset.x == rampart_width) ^ (ori == Ori::East) { pos.y @@ -79,26 +96,49 @@ impl Archetype for Keep { pos.x }; let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 }; - let min_dist = bound_offset.reduce_max(); + let inner = Clamp::clamp( + center_offset, + Vec2::new(-5, -branch.len / 2 - 5), + Vec2::new(5, branch.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(); if profile.y <= 0 - (min_dist - width - 1).max(0) && min_dist < width + 3 { // Foundations foundation } else if profile.y == ceil_height && min_dist < rampart_width { - if min_dist < width { - floor - } else { - wall - } - } else if bound_offset.x.abs() == 4 && min_dist == width + 1 && profile.y < ceil_height { + if min_dist < width { floor } else { wall } + } else if !branch.attr.is_tower + && bound_offset.x.abs() == 4 + && min_dist == width + 1 + && profile.y < ceil_height + { wall - } else if bound_offset.x.abs() < 3 && profile.y < door_height - bound_offset.x.abs() && profile.y > 0 { + } else if bound_offset.x.abs() < 3 + && profile.y < door_height - bound_offset.x.abs() + && profile.y > 0 + { internal } else if min_dist == width && profile.y <= ceil_height { wall } else if profile.y >= ceil_height { if profile.y > ceil_height && min_dist < rampart_width { - internal + if branch.attr.is_tower + && center_offset == Vec2::zero() + && profile.y < ceil_height + 16 + { + pole + } else if branch.attr.is_tower + && center_offset.x == 0 + && center_offset.y > 0 + && center_offset.y < 8 + && profile.y > ceil_height + 8 + && profile.y < ceil_height + 14 + { + flag + } else { + empty + } } else if min_dist == rampart_width { if profile.y < rampart_height { wall diff --git a/world/src/site/settlement/building/mod.rs b/world/src/site/settlement/building/mod.rs index 6c578e94e6..7f6d2915e3 100644 --- a/world/src/site/settlement/building/mod.rs +++ b/world/src/site/settlement/building/mod.rs @@ -12,7 +12,6 @@ use vek::*; pub type HouseBuilding = Building; pub type KeepBuilding = Building; - pub struct Building { skel: Skeleton, archetype: A, @@ -44,7 +43,7 @@ impl Building { let aabr = self.bounds_2d(); Aabb { min: Vec3::from(aabr.min) + Vec3::unit_z() * (self.origin.z - 8), - max: Vec3::from(aabr.max) + Vec3::unit_z() * (self.origin.z + 32), + max: Vec3::from(aabr.max) + Vec3::unit_z() * (self.origin.z + 48), } } diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 5fd57221e2..5704b69902 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -1,6 +1,10 @@ mod building; +mod town; -use self::building::{HouseBuilding, KeepBuilding}; +use self::{ + building::{HouseBuilding, KeepBuilding}, + town::{District, Town}, +}; use super::SpawnRules; use crate::{ column::ColumnSample, @@ -84,7 +88,6 @@ fn to_tile(e: i32) -> i32 { ((e as f32).div_euclid(AREA_SIZE as f32)).floor() as pub enum StructureKind { House(HouseBuilding), Keep(KeepBuilding), - } pub struct Structure { @@ -96,23 +99,20 @@ impl Structure { match &self.kind { StructureKind::House(house) => house.bounds_2d(), StructureKind::Keep(keep) => keep.bounds_2d(), - } } - + pub fn bounds(&self) -> Aabb { match &self.kind { StructureKind::House(house) => house.bounds(), StructureKind::Keep(keep) => keep.bounds(), - } } - + pub fn sample(&self, rpos: Vec3) -> Option { match &self.kind { StructureKind::House(house) => house.sample(rpos), StructureKind::Keep(keep) => keep.sample(rpos), - } } } @@ -127,10 +127,6 @@ pub struct Settlement { noise: RandomField, } -pub struct Town { - base_tile: Vec2, -} - pub struct Farm { #[allow(dead_code)] base_tile: Vec2, @@ -280,12 +276,28 @@ impl Settlement { Some(Plot::Dirt) => true, _ => false, }) { - self.land - .plot_at_mut(base_tile) - .map(|plot| *plot = Plot::Town); + // self.land + // .plot_at_mut(base_tile) + // .map(|plot| *plot = Plot::Town { district: None }); if i == 0 { - self.town = Some(Town { base_tile }); + let town = Town::generate(self.origin, base_tile, ctx); + + for (id, district) in town.districts().iter() { + let district_plot = + self.land.plots.insert(Plot::Town { district: Some(id) }); + + for x in district.aabr.min.x..district.aabr.max.x { + for y in district.aabr.min.y..district.aabr.max.y { + if !matches!(self.land.plot_at(Vec2::new(x, y)), Some(Plot::Hazard)) + { + self.land.set(Vec2::new(x, y), district_plot); + } + } + } + } + + self.town = Some(town); origin = base_tile; } } @@ -346,27 +358,25 @@ impl Settlement { return; }; - for (i, tile) in Spiral2d::new() + for tile in Spiral2d::new() .map(|offs| town_center + offs) .take(16usize.pow(2)) - .enumerate() { // This is a stupid way to decide how to place buildings - for _ in 0..ctx.rng.gen_range(2, 5) { + for i in 0..ctx.rng.gen_range(2, 5) { for _ in 0..25 { let house_pos = tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2) + Vec2::::zero().map(|_| { ctx.rng - .gen_range(-(AREA_SIZE as i32) / 2, AREA_SIZE as i32 / 2) + .gen_range(-(AREA_SIZE as i32) / 4, AREA_SIZE as i32 / 4) }); let tile_pos = house_pos.map(|e| e.div_euclid(AREA_SIZE as i32)); - if !matches!(self.land.plot_at(tile_pos), Some(Plot::Town)) - || self - .land - .tile_at(tile_pos) - .map(|t| t.contains(WayKind::Path)) - .unwrap_or(true) + if self + .land + .tile_at(tile_pos) + .map(|t| t.contains(WayKind::Path)) + .unwrap_or(true) || ctx .sim .and_then(|sim| sim.get_nearest_path(self.origin + house_pos)) @@ -376,30 +386,30 @@ impl Settlement { continue; } + let alt = if let Some(Plot::Town { district }) = self.land.plot_at(tile_pos) { + district + .and_then(|d| self.town.as_ref().map(|t| t.districts().get(d))) + .map(|d| d.alt) + .unwrap_or_else(|| { + ctx.sim + .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) + .unwrap_or(0.0) + .ceil() as i32 + }) + } else { + continue; + }; + let structure = Structure { - kind: if i == 0 { + kind: if tile == town_center && i == 0 { StructureKind::Keep(KeepBuilding::generate( ctx.rng, - Vec3::new( - house_pos.x, - house_pos.y, - ctx.sim - .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) - .unwrap_or(0.0) - .ceil() as i32, - ), + Vec3::new(house_pos.x, house_pos.y, alt), )) } else { StructureKind::House(HouseBuilding::generate( ctx.rng, - Vec3::new( - house_pos.x, - house_pos.y, - ctx.sim - .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) - .unwrap_or(0.0) - .ceil() as i32, - ), + Vec3::new(house_pos.x, house_pos.y, alt), )) }, }; @@ -536,12 +546,13 @@ impl Settlement { } else { continue; }; - let surface_z = col_sample.riverless_alt.floor() as i32; + let land_surface_z = col_sample.riverless_alt.floor() as i32; + let mut surface_z = land_surface_z; // Sample settlement let sample = self.land.get_at_block(rpos); - let noisy_color = |col: Rgb, factor: u32| { + let noisy_color = move |col: Rgb, factor: u32| { let nz = self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, surface_z)); col.map(|e| { (e as u32 + nz % (factor * 2)) @@ -550,6 +561,30 @@ impl Settlement { }) }; + // District alt + if let Some(Plot::Town { district }) = sample.plot { + if let Some(d) = + district.and_then(|d| self.town.as_ref().map(|t| t.districts().get(d))) + { + let other = self + .land + .plot_at(sample.second_closest) + .and_then(|p| match p { + Plot::Town { district } => *district, + _ => None, + }) + .and_then(|d| { + self.town.as_ref().map(|t| t.districts().get(d).alt as f32) + }) + .unwrap_or(surface_z as f32); + surface_z = Lerp::lerp( + (other + d.alt as f32) / 2.0, + d.alt as f32, + (1.25 * sample.edge_dist / (d.alt as f32 - other).abs()).min(1.0), + ) as i32; + } + } + // Paths if let Some((WayKind::Path, dist, nearest)) = sample.way { let inset = -1; @@ -599,7 +634,7 @@ impl Settlement { Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)), Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)), Some(Plot::Water) => Some(Rgb::new(100, 150, 250)), - Some(Plot::Town) => { + Some(Plot::Town { district }) => { if let Some((_, path_nearest)) = col_sample.path { let path_dir = (path_nearest - wpos2d.map(|e| e as f32)) .rotated_z(f32::consts::PI / 2.0) @@ -693,7 +728,8 @@ impl Settlement { if let Some(color) = color { if col_sample.water_dist.map(|dist| dist > 2.0).unwrap_or(true) { - for z in -8..3 { + 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); if let (0, Some(block)) = (z, surface_block) { @@ -767,7 +803,8 @@ impl Settlement { for x in bounds.min.x..bounds.max.x + 1 { for y in bounds.min.y..bounds.max.y + 1 { - let col = if let Some(col) = get_column(self.origin + Vec2::new(x, y) - wpos2d) { + let col = if let Some(col) = get_column(self.origin + Vec2::new(x, y) - wpos2d) + { col } else { continue; @@ -813,7 +850,7 @@ impl Settlement { let entity_wpos = Vec3::new(wpos2d.x as f32, wpos2d.y as f32, col_sample.alt + 3.0); - if matches!(sample.plot, Some(Plot::Town)) + if matches!(sample.plot, Some(Plot::Town { .. })) && RandomField::new(self.seed).chance(Vec3::from(wpos2d), 1.0 / (50.0 * 50.0)) { let is_human: bool; @@ -903,7 +940,7 @@ impl Settlement { Some(Plot::Dirt) => return Some(Rgb::new(90, 70, 50)), Some(Plot::Grass) => return Some(Rgb::new(100, 200, 0)), Some(Plot::Water) => return Some(Rgb::new(100, 150, 250)), - Some(Plot::Town) => { + Some(Plot::Town { .. }) => { return Some(Rgb::new(150, 110, 60).map2(Rgb::iota(), |e: u8, i: i32| { e.saturating_add((self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8) .saturating_sub(8) @@ -955,7 +992,9 @@ pub enum Plot { Dirt, Grass, Water, - Town, + Town { + district: Option>, + }, Field { farm: Id, seed: u32, @@ -1015,6 +1054,8 @@ pub struct Sample<'a> { plot: Option<&'a Plot>, way: Option<(&'a WayKind, f32, Vec2)>, tower: Option<(&'a Tower, Vec2)>, + edge_dist: f32, + second_closest: Vec2, } pub struct Land { @@ -1049,6 +1090,15 @@ impl Land { .min_by_key(|(center, _)| center.distance_squared(pos)) .unwrap() .0; + let second_closest = neighbors + .iter() + .filter(|(center, _)| *center != closest) + .min_by_key(|(center, _)| center.distance_squared(pos)) + .unwrap() + .0; + sample.second_closest = second_closest.map(to_tile); + sample.edge_dist = (second_closest - pos).map(|e| e as f32).magnitude() + - (closest - pos).map(|e| e as f32).magnitude(); let center_tile = self.tile_at(neighbors[4].0.map(to_tile)); diff --git a/world/src/site/settlement/town.rs b/world/src/site/settlement/town.rs new file mode 100644 index 0000000000..6d6a082cd9 --- /dev/null +++ b/world/src/site/settlement/town.rs @@ -0,0 +1,82 @@ +use super::{GenCtx, AREA_SIZE}; +use common::store::{Id, Store}; +use rand::prelude::*; +use vek::*; + +pub struct Town { + pub base_tile: Vec2, + radius: i32, + districts: Store, +} + +impl Town { + pub fn districts(&self) -> &Store { &self.districts } + + pub fn generate(origin: Vec2, base_tile: Vec2, ctx: &mut GenCtx) -> Self { + let mut this = Self { + base_tile, + radius: 4, + districts: Store::default(), + }; + + this.generate_districts(origin, ctx); + + this + } + + fn generate_districts(&mut self, origin: Vec2, ctx: &mut GenCtx) { + let base_aabr = Aabr { + min: self.base_tile - self.radius, + max: self.base_tile + self.radius, + }; + + gen_plot(base_aabr, ctx).for_each(base_aabr, &mut |aabr| { + if aabr.center().distance_squared(self.base_tile) < self.radius.pow(2) { + self.districts.insert(District { + seed: ctx.rng.gen(), + aabr, + alt: ctx + .sim + .and_then(|sim| { + sim.get_alt_approx( + origin + aabr.center() * AREA_SIZE as i32 + AREA_SIZE as i32 / 2, + ) + }) + .unwrap_or(0.0) as i32, + }); + } + }); + } +} + +pub struct District { + pub seed: u32, + pub aabr: Aabr, + pub alt: i32, +} + +enum Plot { + District, + Parent(Vec<(Aabr, Plot)>), +} + +impl Plot { + fn for_each(&self, aabr: Aabr, f: &mut impl FnMut(Aabr)) { + match self { + Plot::District => f(aabr), + Plot::Parent(children) => children.iter().for_each(|(aabr, p)| p.for_each(*aabr, f)), + } + } +} + +fn gen_plot(aabr: Aabr, ctx: &mut GenCtx) -> Plot { + if aabr.size().product() <= 9 { + Plot::District + } else if aabr.size().w < aabr.size().h { + let [a, b] = aabr.split_at_y(aabr.min.y + ctx.rng.gen_range(1, aabr.size().h)); + Plot::Parent(vec![(a, gen_plot(a, ctx)), (b, gen_plot(b, ctx))]) + } else { + let [a, b] = aabr.split_at_x(aabr.min.x + ctx.rng.gen_range(1, aabr.size().w)); + Plot::Parent(vec![(a, gen_plot(a, ctx)), (b, gen_plot(b, ctx))]) + } +} diff --git a/world/src/util/map_vec.rs b/world/src/util/map_vec.rs new file mode 100644 index 0000000000..47efe67d0a --- /dev/null +++ b/world/src/util/map_vec.rs @@ -0,0 +1,79 @@ +use crate::util::DHashMap; +use std::hash::Hash; + +#[derive(Clone, Debug)] +pub struct MapVec { + /// We use this hasher (FxHasher32) because + /// (1) we don't care about DDOS attacks (ruling out SipHash); + /// (2) we care about determinism across computers (ruling out AAHash); + /// (3) we have 1-byte keys (for which FxHash is supposedly fastest). + entries: DHashMap, + default: T, +} + +/// Need manual implementation of Default since K doesn't need that bound. +impl Default for MapVec { + fn default() -> Self { + Self { + entries: Default::default(), + default: Default::default(), + } + } +} + +impl MapVec { + pub fn from_list<'a>(i: impl IntoIterator, default: T) -> Self + where + K: 'a, + T: 'a, + { + Self { + entries: i.into_iter().cloned().collect(), + default, + } + } + + pub fn from_default(default: T) -> Self { + Self { + entries: DHashMap::default(), + default, + } + } + + pub fn get_mut(&mut self, entry: K) -> &mut T { + let default = &self.default; + self.entries.entry(entry).or_insert_with(|| default.clone()) + } + + pub fn get(&self, entry: K) -> &T { self.entries.get(&entry).unwrap_or(&self.default) } + + #[allow(clippy::clone_on_copy)] // TODO: Pending review in #587 + pub fn map(self, mut f: impl FnMut(K, T) -> U) -> MapVec { + MapVec { + entries: self + .entries + .into_iter() + .map(|(s, v)| (s.clone(), f(s, v))) + .collect(), + default: U::default(), + } + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.entries.iter().map(|(s, v)| (*s, v)) + } + + pub fn iter_mut(&mut self) -> impl Iterator + '_ { + self.entries.iter_mut().map(|(s, v)| (*s, v)) + } +} + +impl std::ops::Index for MapVec { + type Output = T; + + fn index(&self, entry: K) -> &Self::Output { self.get(entry) } +} + +impl std::ops::IndexMut for MapVec { + fn index_mut(&mut self, entry: K) -> &mut Self::Output { self.get_mut(entry) } +} diff --git a/world/src/util/mod.rs b/world/src/util/mod.rs index 9ab6f91dce..7ddc7e85e0 100644 --- a/world/src/util/mod.rs +++ b/world/src/util/mod.rs @@ -1,5 +1,6 @@ pub mod fast_noise; pub mod grid; +pub mod map_vec; pub mod random; pub mod sampler; pub mod seed_expan; @@ -11,6 +12,7 @@ pub mod unit_chooser; pub use self::{ fast_noise::FastNoise, grid::Grid, + map_vec::MapVec, random::{RandomField, RandomPerm}, sampler::{Sampler, SamplerMut}, small_cache::SmallCache, @@ -18,8 +20,15 @@ pub use self::{ unit_chooser::UnitChooser, }; +use fxhash::{FxHasher32, FxHasher64}; +use hashbrown::{HashMap, HashSet}; +use std::hash::BuildHasherDefault; use vek::*; +// Deterministic HashMap and HashSet +pub type DHashMap = HashMap>; +pub type DHashSet = HashSet>; + pub fn attempt(max_iters: usize, mut f: impl FnMut() -> Option) -> Option { (0..max_iters).find_map(|_| f()) } From 1f4cec773b78adf9be490b1513c1e60a43573177 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 27 Jun 2020 19:39:16 +0100 Subject: [PATCH 03/33] Added make_block command --- Cargo.lock | 21 ++++++++++++++++++++ common/Cargo.toml | 1 + common/src/cmd.rs | 15 +++++++++++++- common/src/terrain/block.rs | 31 +++++++++++++++++++++++++++-- server/src/cmd.rs | 39 +++++++++++++++++++++++++++++++++++-- 5 files changed, 102 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index afe3d80245..11aba082cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1189,6 +1189,26 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +[[package]] +name = "enum-iterator" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79a6321a1197d7730510c7e3f6cb80432dfefecb32426de8cea0aa19b4bb8d7" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06" +dependencies = [ + "proc-macro2 1.0.18", + "quote 1.0.7", + "syn 1.0.31", +] + [[package]] name = "error-chain" version = "0.12.2" @@ -4627,6 +4647,7 @@ dependencies = [ "criterion", "crossbeam", "dot_vox", + "enum-iterator", "find_folder", "hashbrown", "image", diff --git a/common/Cargo.toml b/common/Cargo.toml index 92d63c8045..60e7bc534c 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -31,6 +31,7 @@ indexmap = "1.3.0" sum_type = "0.2.0" authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60cd18c34c73097640162bfe" } slab = "0.4.2" +enum-iterator = "0.6" [dev-dependencies] criterion = "0.3" diff --git a/common/src/cmd.rs b/common/src/cmd.rs index e128901f46..7e3194c4a6 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -1,4 +1,4 @@ -use crate::{assets, comp, npc}; +use crate::{assets, comp, npc, terrain}; use lazy_static::lazy_static; use std::{ collections::HashMap, @@ -56,6 +56,7 @@ pub enum ChatCommand { KillNpcs, Lantern, Light, + MakeBlock, Motd, Object, Players, @@ -98,6 +99,7 @@ pub static CHAT_COMMANDS: &[ChatCommand] = &[ ChatCommand::KillNpcs, ChatCommand::Lantern, ChatCommand::Light, + ChatCommand::MakeBlock, ChatCommand::Motd, ChatCommand::Object, ChatCommand::Players, @@ -149,6 +151,11 @@ lazy_static! { .map(|s| s.to_string()) .collect(); + static ref BLOCK_KINDS: Vec = terrain::block::BLOCK_KINDS + .keys() + .cloned() + .collect(); + /// List of item specifiers. Useful for tab completing static ref ITEM_SPECS: Vec = { let path = assets::ASSETS_PATH.join("common").join("items"); @@ -281,6 +288,11 @@ impl ChatCommand { "Spawn entity with light", Admin, ), + ChatCommand::MakeBlock => cmd( + vec![Enum("block", BLOCK_KINDS.clone(), Required)], + "Make a block", + Admin, + ), ChatCommand::Motd => cmd( vec![Message(Optional)], "View the server description", @@ -386,6 +398,7 @@ impl ChatCommand { ChatCommand::KillNpcs => "kill_npcs", ChatCommand::Lantern => "lantern", ChatCommand::Light => "light", + ChatCommand::MakeBlock => "make_block", ChatCommand::Motd => "motd", ChatCommand::Object => "object", ChatCommand::Players => "players", diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index bf6886c0ca..383b4475fc 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -1,9 +1,16 @@ use crate::vol::Vox; use serde::{Deserialize, Serialize}; -use std::ops::Deref; +use std::{ + ops::Deref, + convert::TryFrom, + collections::HashMap, + fmt, +}; +use lazy_static::lazy_static; +use enum_iterator::IntoEnumIterator; use vek::*; -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, IntoEnumIterator)] #[repr(u8)] pub enum BlockKind { Air, @@ -88,6 +95,26 @@ pub enum BlockKind { ShinyGem, } +impl fmt::Display for BlockKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +lazy_static! { + pub static ref BLOCK_KINDS: HashMap = BlockKind::into_enum_iter() + .map(|bk| (bk.to_string(), bk)) + .collect(); +} + +impl<'a> TryFrom<&'a str> for BlockKind { + type Error = (); + + fn try_from(s: &'a str) -> Result { + BLOCK_KINDS.get(s).copied().ok_or(()) + } +} + impl BlockKind { pub const MAX_HEIGHT: f32 = 3.0; diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 5ee23d8328..f1460ce1f1 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -13,15 +13,16 @@ use common::{ npc::{self, get_npc_name}, state::TimeOfDay, sync::{Uid, WorldSyncExt}, - terrain::TerrainChunkSize, + terrain::{TerrainChunkSize, Block, BlockKind}, util::Dir, - vol::RectVolSize, + vol::{RectVolSize, WriteVol}, LoadoutBuilder, }; use rand::Rng; use specs::{Builder, Entity as EcsEntity, Join, WorldExt}; use vek::*; use world::util::Sampler; +use std::convert::TryFrom; use scan_fmt::{scan_fmt, scan_fmt_some}; use tracing::error; @@ -83,6 +84,7 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler { ChatCommand::KillNpcs => handle_kill_npcs, ChatCommand::Lantern => handle_lantern, ChatCommand::Light => handle_light, + ChatCommand::MakeBlock => handle_make_block, ChatCommand::Motd => handle_motd, ChatCommand::Object => handle_object, ChatCommand::Players => handle_players, @@ -179,6 +181,39 @@ fn handle_give_item( } } +fn handle_make_block( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: String, + action: &ChatCommand, +) { + if let Some(block_name) = scan_fmt_some!(&args, &action.arg_fmt(), String) { + if let Ok(bk) = BlockKind::try_from(block_name.as_str()) { + match server.state.read_component_cloned::(target) { + Some(pos) => server.state.set_block( + pos.0.map(|e| e.floor() as i32), + Block::new(bk, Rgb::broadcast(255)), + ), + None => server.notify_client( + client, + ChatType::CommandError.server_msg(String::from("You have no position.")), + ), + } + } else { + server.notify_client( + client, + ChatType::CommandError.server_msg(format!("Invalid block kind: {}", block_name)), + ); + } + } else { + server.notify_client( + client, + ChatType::CommandError.server_msg(String::from(action.help_string())), + ); + } +} + fn handle_motd( server: &mut Server, client: EcsEntity, From d6cdb0c4339aec4c8f3b5ba5771a8d76d2ec64b0 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 28 Jun 2020 00:37:14 +0100 Subject: [PATCH 04/33] Began adding castles --- common/src/cmd.rs | 5 + common/src/terrain/block.rs | 19 +- server/src/cmd.rs | 4 +- world/src/civ/mod.rs | 37 ++- world/src/site/castle/mod.rs | 244 ++++++++++++++++++ world/src/site/dungeon/mod.rs | 13 - world/src/site/mod.rs | 38 ++- .../settlement/building/archetype/keep.rs | 10 +- world/src/site/settlement/building/mod.rs | 6 +- world/src/site/settlement/mod.rs | 2 +- 10 files changed, 316 insertions(+), 62 deletions(-) create mode 100644 world/src/site/castle/mod.rs diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 7e3194c4a6..4c800c3f93 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -298,6 +298,11 @@ impl ChatCommand { "View the server description", NoAdmin, ), + ChatCommand::MakeBlock => cmd( + vec![Enum("block", BLOCK_KINDS.clone(), Required)], + "Make a block", + Admin, + ), ChatCommand::Object => cmd( vec![Enum("object", OBJECTS.clone(), Required)], "Spawn an object", diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 383b4475fc..4025ff8a47 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -1,13 +1,8 @@ use crate::vol::Vox; -use serde::{Deserialize, Serialize}; -use std::{ - ops::Deref, - convert::TryFrom, - collections::HashMap, - fmt, -}; -use lazy_static::lazy_static; use enum_iterator::IntoEnumIterator; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, convert::TryFrom, fmt, ops::Deref}; use vek::*; #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, IntoEnumIterator)] @@ -96,9 +91,7 @@ pub enum BlockKind { } impl fmt::Display for BlockKind { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) } } lazy_static! { @@ -110,9 +103,7 @@ lazy_static! { impl<'a> TryFrom<&'a str> for BlockKind { type Error = (); - fn try_from(s: &'a str) -> Result { - BLOCK_KINDS.get(s).copied().ok_or(()) - } + fn try_from(s: &'a str) -> Result { BLOCK_KINDS.get(s).copied().ok_or(()) } } impl BlockKind { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index f1460ce1f1..a95997a874 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -13,16 +13,16 @@ use common::{ npc::{self, get_npc_name}, state::TimeOfDay, sync::{Uid, WorldSyncExt}, - terrain::{TerrainChunkSize, Block, BlockKind}, + terrain::{Block, BlockKind, TerrainChunkSize}, util::Dir, vol::{RectVolSize, WriteVol}, LoadoutBuilder, }; use rand::Rng; use specs::{Builder, Entity as EcsEntity, Join, WorldExt}; +use std::convert::TryFrom; use vek::*; use world::util::Sampler; -use std::convert::TryFrom; use scan_fmt::{scan_fmt, scan_fmt_some}; use tracing::error; diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 958f334ee8..3f219c4b7d 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -5,7 +5,7 @@ mod econ; use self::{Occupation::*, Stock::*}; use crate::{ sim::WorldSim, - site::{Dungeon, Settlement, Site as WorldSite}, + site::{Dungeon, Settlement, Castle, Site as WorldSite}, util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS}, Index, }; @@ -86,9 +86,13 @@ impl Civs { for _ in 0..INITIAL_CIV_COUNT * 3 { attempt(5, || { - let loc = find_site_loc(&mut ctx, None)?; + let (kind, size) = match ctx.rng.gen_range(0, 2) { + 0 => (SiteKind::Dungeon, 0), + _ => (SiteKind::Castle, 3), + }; + let loc = find_site_loc(&mut ctx, None, size)?; this.establish_site(&mut ctx.reseed(), loc, |place| Site { - kind: SiteKind::Dungeon, + kind, center: loc, place, @@ -125,10 +129,12 @@ impl Civs { let flatten_radius = match &site.kind { SiteKind::Settlement => 8.0, SiteKind::Dungeon => 2.0, + SiteKind::Castle => 5.0, }; let (raise, raise_dist): (f32, i32) = match &site.kind { SiteKind::Settlement => (10.0, 6), + SiteKind::Castle => (0.0, 6), _ => (0.0, 0), }; @@ -176,6 +182,9 @@ impl Civs { SiteKind::Dungeon => { WorldSite::dungeon(Dungeon::generate(wpos, Some(ctx.sim), &mut rng)) }, + SiteKind::Castle => { + WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng)) + }, }); let site_ref = &index.sites[site]; @@ -272,7 +281,7 @@ impl Civs { fn birth_civ(&mut self, ctx: &mut GenCtx) -> Option> { let site = attempt(5, || { - let loc = find_site_loc(ctx, None)?; + let loc = find_site_loc(ctx, None, 1)?; self.establish_site(ctx, loc, |place| Site { kind: SiteKind::Settlement, center: loc, @@ -367,7 +376,7 @@ impl Civs { loc: Vec2, site_fn: impl FnOnce(Id) -> Site, ) -> Option> { - const SITE_AREA: Range = 64..256; + const SITE_AREA: Range = 1..4;//64..256; let place = match ctx.sim.get(loc).and_then(|site| site.place) { Some(place) => place, @@ -381,12 +390,13 @@ impl Civs { let mut nearby = self .sites .iter() + .filter(|(_, p)| matches!(p.kind, SiteKind::Settlement | SiteKind::Castle)) .map(|(id, p)| (id, (p.center.distance_squared(loc) as f32).sqrt())) .filter(|(_, dist)| *dist < MAX_NEIGHBOR_DISTANCE) .collect::>(); nearby.sort_by_key(|(_, dist)| *dist as i32); - if let SiteKind::Settlement = self.sites[site].kind { + if let SiteKind::Settlement | SiteKind::Castle = self.sites[site].kind { for (nearby, _) in nearby.into_iter().take(5) { // Find a novel path if let Some((path, cost)) = find_path(ctx, loc, self.sites.get(nearby).center) { @@ -589,6 +599,7 @@ fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2) -> bool { if let Some(chunk) = sim.get(loc) { !chunk.river.is_ocean() && !chunk.river.is_lake() + && !chunk.river.is_river() && sim .get_gradient_approx(loc) .map(|grad| grad < 1.0) @@ -601,7 +612,7 @@ fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2) -> 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, near: Option<(Vec2, f32)>) -> Option> { +fn find_site_loc(ctx: &mut GenCtx, near: Option<(Vec2, f32)>, size: i32) -> Option> { const MAX_ATTEMPTS: usize = 100; let mut loc = None; for _ in 0..MAX_ATTEMPTS { @@ -621,8 +632,10 @@ fn find_site_loc(ctx: &mut GenCtx, near: Option<(Vec2, f32)>) -> ), }); - if loc_suitable_for_site(&ctx.sim, test_loc) { - return Some(test_loc); + for offset in Spiral2d::new().take((size * 2 + 1).pow(2) as usize) { + if loc_suitable_for_site(&ctx.sim, test_loc + offset) { + return Some(test_loc); + } } loc = ctx.sim.get(test_loc).and_then(|c| { @@ -723,10 +736,7 @@ pub struct Site { impl fmt::Display for Site { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.kind { - SiteKind::Settlement => writeln!(f, "Settlement")?, - SiteKind::Dungeon => writeln!(f, "Dungeon")?, - } + writeln!(f, "{:?}", self.kind)?; writeln!(f, "- population: {}", self.population.floor() as u32)?; writeln!(f, "- coin: {}", self.coin.floor() as u32)?; writeln!(f, "Stocks")?; @@ -766,6 +776,7 @@ impl fmt::Display for Site { pub enum SiteKind { Settlement, Dungeon, + Castle, } impl Site { diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs new file mode 100644 index 0000000000..e10291fa0c --- /dev/null +++ b/world/src/site/castle/mod.rs @@ -0,0 +1,244 @@ +use super::SpawnRules; +use crate::{ + block::block_from_structure, + column::ColumnSample, + sim::WorldSim, + site::{ + BlockMask, + settlement::building::{Archetype, Ori, Branch, archetype::keep::{Keep, Attr}}, + }, + util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, +}; +use common::{ + assets, + astar::Astar, + comp, + generation::{ChunkSupplement, EntityInfo}, + npc, + store::{Id, Store}, + terrain::{Block, BlockKind, Structure, TerrainChunkSize}, + vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol}, +}; +use core::{f32, hash::BuildHasherDefault}; +use fxhash::FxHasher64; +use lazy_static::lazy_static; +use rand::prelude::*; +use std::sync::Arc; +use vek::*; + +struct Segment { + offset: Vec2, + locus: i32, + height: i32, + is_tower: bool, +} + +struct Tower { + offset: Vec2, + alt: i32, +} + +pub struct Castle { + origin: Vec2, + alt: i32, + seed: u32, + towers: Vec, + segments: Vec, +} + +pub struct GenCtx<'a, R: Rng> { + sim: Option<&'a WorldSim>, + rng: &'a mut R, +} + +impl Castle { + #[allow(clippy::let_and_return)] // TODO: Pending review in #587 + pub fn generate(wpos: Vec2, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self { + let mut ctx = GenCtx { sim, rng }; + + let boundary_towers = ctx.rng.gen_range(5, 10); + + let this = Self { + origin: wpos, + alt: ctx + .sim + .and_then(|sim| sim.get_alt_approx(wpos)) + .unwrap_or(0.0) as i32 + + 6, + seed: ctx.rng.gen(), + + 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 offset = (dir * dist).map(|e| e as i32); + + Tower { + offset, + alt: ctx + .sim + .and_then(|sim| sim.get_alt_approx(wpos + offset)) + .unwrap_or(0.0) as i32 + 2, + } + }) + .collect(), + + 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 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, + } + }) + .collect(), + }; + + this + } + + pub fn get_origin(&self) -> Vec2 { self.origin } + + pub fn radius(&self) -> f32 { 1200.0 } + + #[allow(clippy::needless_update)] // TODO: Pending review in #587 + pub fn spawn_rules(&self, wpos: Vec2) -> SpawnRules { + SpawnRules { + trees: wpos.distance_squared(self.origin) > 64i32.pow(2), + ..SpawnRules::default() + } + } + + pub fn apply_to<'a>( + &'a self, + wpos2d: Vec2, + mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, + vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), + ) { + for y in 0..vol.size_xy().y as i32 { + for x in 0..vol.size_xy().x as i32 { + let offs = Vec2::new(x, y); + + let wpos2d = wpos2d + offs; + let rpos = wpos2d - self.origin; + + // Apply the dungeon entrance + let col_sample = if let Some(col) = get_column(offs) { + col + } else { + continue; + }; + + let (wall_dist, wall_pos, wall_alt) = (0..self.towers.len()) + .map(|i| { + let tower0 = &self.towers[i]; + let tower1 = &self.towers[(i + 1) % self.towers.len()]; + + let wall = LineSegment2 { + start: tower0.offset.map(|e| e as f32), + 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 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); + + ( + 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, + ) + }) + .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, + ); + + // 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, + 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(), + } + ); + for tower in &self.towers { + let tower_wpos = Vec3::new( + self.origin.x + tower.offset.x, + self.origin.y + tower.offset.y, + tower.alt, + ); + let tower_locus = 10; + + let border_pos = (tower_wpos - wpos).xy().map(|e| e.abs()); + mask = mask.resolve_with(Keep.draw( + wpos - tower_wpos, + 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(), + } + )); + } + + if let Some(block) = mask.finish() { + let _ = vol.set(Vec3::new(offs.x, offs.y, wpos.z), block); + } + } + } + } + } + + #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 + pub fn apply_supplement<'a>( + &'a self, + rng: &mut impl Rng, + wpos2d: Vec2, + _get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, + supplement: &mut ChunkSupplement, + ) { + // TODO + } +} diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 1d20e97733..aafe012415 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -23,19 +23,6 @@ use rand::prelude::*; use std::sync::Arc; use vek::*; -impl WorldSim { - #[allow(dead_code)] - fn can_host_dungeon(&self, pos: Vec2) -> bool { - self.get(pos) - .map(|chunk| !chunk.near_cliffs && !chunk.river.is_river() && !chunk.river.is_lake()) - .unwrap_or(false) - && self - .get_gradient_approx(pos) - .map(|grad| grad > 0.25 && grad < 1.5) - .unwrap_or(false) - } -} - pub struct Dungeon { origin: Vec2, alt: i32, diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index fbfd8196db..f081ada67d 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -1,10 +1,17 @@ mod block_mask; mod dungeon; +mod castle; pub mod economy; mod settlement; // Reexports -pub use self::{block_mask::BlockMask, dungeon::Dungeon, economy::Economy, settlement::Settlement}; +pub use self::{ + block_mask::BlockMask, + dungeon::Dungeon, + economy::Economy, + settlement::Settlement, + castle::Castle, +}; use crate::column::ColumnSample; use common::{ @@ -32,6 +39,7 @@ pub struct Site { pub enum SiteKind { Settlement(Settlement), Dungeon(Dungeon), + Castle(Castle), } impl Site { @@ -49,10 +57,18 @@ impl Site { } } + pub fn castle(c: Castle) -> Self { + Self { + kind: SiteKind::Castle(c), + economy: Economy::default(), + } + } + pub fn radius(&self) -> f32 { match &self.kind { - SiteKind::Settlement(settlement) => settlement.radius(), - SiteKind::Dungeon(dungeon) => dungeon.radius(), + SiteKind::Settlement(s) => s.radius(), + SiteKind::Dungeon(d) => d.radius(), + SiteKind::Castle(c) => c.radius(), } } @@ -60,6 +76,7 @@ impl Site { match &self.kind { SiteKind::Settlement(s) => s.get_origin(), SiteKind::Dungeon(d) => d.get_origin(), + SiteKind::Castle(c) => c.get_origin(), } } @@ -67,6 +84,7 @@ impl Site { match &self.kind { SiteKind::Settlement(s) => s.spawn_rules(wpos), SiteKind::Dungeon(d) => d.spawn_rules(wpos), + SiteKind::Castle(c) => c.spawn_rules(wpos), } } @@ -77,8 +95,9 @@ impl Site { vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), ) { match &self.kind { - SiteKind::Settlement(settlement) => settlement.apply_to(wpos2d, get_column, vol), - SiteKind::Dungeon(dungeon) => dungeon.apply_to(wpos2d, get_column, vol), + SiteKind::Settlement(s) => s.apply_to(wpos2d, get_column, vol), + SiteKind::Dungeon(d) => d.apply_to(wpos2d, get_column, vol), + SiteKind::Castle(c) => c.apply_to(wpos2d, get_column, vol), } } @@ -90,12 +109,9 @@ impl Site { supplement: &mut ChunkSupplement, ) { match &self.kind { - SiteKind::Settlement(settlement) => { - settlement.apply_supplement(rng, wpos2d, get_column, supplement) - }, - SiteKind::Dungeon(dungeon) => { - dungeon.apply_supplement(rng, wpos2d, get_column, supplement) - }, + SiteKind::Settlement(s) => s.apply_supplement(rng, wpos2d, get_column, supplement), + SiteKind::Dungeon(d) => d.apply_supplement(rng, wpos2d, get_column, supplement), + SiteKind::Castle(c) => c.apply_supplement(rng, wpos2d, get_column, supplement), } } } diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 0016ca05a7..c250719720 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -10,8 +10,8 @@ use vek::*; pub struct Keep; pub struct Attr { - height: i32, - is_tower: bool, + pub height: i32, + pub is_tower: bool, } impl Archetype for Keep { @@ -91,9 +91,9 @@ impl Archetype for Keep { let ceil_height = branch.attr.height; let door_height = 6; let edge_pos = if (bound_offset.x == rampart_width) ^ (ori == Ori::East) { - pos.y + pos.y + pos.x } else { - pos.x + pos.x + pos.y }; let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 }; let inner = Clamp::clamp( @@ -143,7 +143,7 @@ impl Archetype for Keep { if profile.y < rampart_height { wall } else { - internal + empty } } else { empty diff --git a/world/src/site/settlement/building/mod.rs b/world/src/site/settlement/building/mod.rs index 7f6d2915e3..91f686b20e 100644 --- a/world/src/site/settlement/building/mod.rs +++ b/world/src/site/settlement/building/mod.rs @@ -1,10 +1,10 @@ -mod archetype; -mod skeleton; +pub mod archetype; +pub mod skeleton; // Reexports pub use self::archetype::Archetype; +pub use self::skeleton::*; -use self::skeleton::*; use common::terrain::Block; use rand::prelude::*; use vek::*; diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 5704b69902..07a46f7c44 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -1,4 +1,4 @@ -mod building; +pub mod building; mod town; use self::{ From b27a65fd0f462158ec0eea763238de86e7fa43d0 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 28 Jun 2020 16:09:31 +0100 Subject: [PATCH 05/33] Castle improvements --- world/src/civ/mod.rs | 10 +- world/src/site/castle/mod.rs | 148 +++++++++++------- world/src/site/mod.rs | 7 +- .../settlement/building/archetype/house.rs | 73 ++++----- .../settlement/building/archetype/keep.rs | 41 +++-- .../site/settlement/building/archetype/mod.rs | 4 +- world/src/site/settlement/building/mod.rs | 16 +- 7 files changed, 178 insertions(+), 121 deletions(-) diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 3f219c4b7d..77ec0186ef 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -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, site_fn: impl FnOnce(Id) -> Site, ) -> Option> { - const SITE_AREA: Range = 1..4;//64..256; + const SITE_AREA: Range = 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) -> 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, near: Option<(Vec2, f32)>, size: i32) -> Option> { +fn find_site_loc( + ctx: &mut GenCtx, + near: Option<(Vec2, f32)>, + size: i32, +) -> Option> { const MAX_ATTEMPTS: usize = 100; let mut loc = None; for _ in 0..MAX_ATTEMPTS { diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index e10291fa0c..33d83d24c2 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -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, alt: i32, seed: u32, + radius: i32, towers: Vec, segments: Vec, } @@ -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) -> 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, + }, )); } diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index f081ada67d..d32b8028b1 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -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; diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index fd25aff1a6..ef2fd417c9 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -33,25 +33,28 @@ const COLOR_THEMES: [Rgb; 17] = [ ]; pub struct House { - roof_color: Rgb, - noise: RandomField, - roof_ribbing: bool, - roof_ribbing_diagonal: bool, + pub roof_color: Rgb, + 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(rng: &mut R, locus: i32) -> Self { + pub fn generate(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, z: i32, ori: Ori, - branch: &Branch, + 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, width, dist, bound_offset: Vec2, 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); } diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index c250719720..333c208b88 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -7,7 +7,9 @@ use common::{ use rand::prelude::*; use vek::*; -pub struct Keep; +pub struct Keep { + pub flag_color: Rgb, +} 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, z: i32, ori: Ori, - branch: &Branch, + 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 diff --git a/world/src/site/settlement/building/archetype/mod.rs b/world/src/site/settlement/building/archetype/mod.rs index 6da93ab50d..d6b858ff78 100644 --- a/world/src/site/settlement/building/archetype/mod.rs +++ b/world/src/site/settlement/building/archetype/mod.rs @@ -20,6 +20,8 @@ pub trait Archetype { center_offset: Vec2, z: i32, ori: Ori, - branch: &Branch, + locus: i32, + len: i32, + attr: &Self::Attr, ) -> BlockMask; } diff --git a/world/src/site/settlement/building/mod.rs b/world/src/site/settlement/building/mod.rs index 91f686b20e..52fbdd3a7c 100644 --- a/world/src/site/settlement/building/mod.rs +++ b/world/src/site/settlement/building/mod.rs @@ -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 Building { .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() From 6c3c941a49ebac8e7fbe5a938bcc4a5d1fb9790a Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 28 Jun 2020 16:22:22 +0100 Subject: [PATCH 06/33] Fixed client API --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 11aba082cb..85c184d99b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1206,7 +1206,7 @@ checksum = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06" dependencies = [ "proc-macro2 1.0.18", "quote 1.0.7", - "syn 1.0.31", + "syn 1.0.33", ] [[package]] From 568a3e375e64fb341dc03e2bba9236fff5c6df51 Mon Sep 17 00:00:00 2001 From: WelshPixie Date: Sun, 28 Jun 2020 20:42:35 +0200 Subject: [PATCH 07/33] fixed sprite rotations --- .../voxel/sprite/furniture/drawer_large-0.vox | 2 +- .../voxel/sprite/furniture/drawer_large-1.vox | 2 +- .../voxel/sprite/furniture/lamp_wall-1.vox | 2 +- .../sprite/furniture/wardrobe_double-0.vox | 2 +- .../sprite/furniture/wardrobe_double-1.vox | 2 +- .../sprite/furniture/wardrobe_single-0.vox | 2 +- .../sprite/furniture/wardrobe_single-1.vox | 2 +- common/src/terrain/block.rs | 27 ++++++++++++++++--- voxygen/src/scene/terrain.rs | 18 ++++++------- 9 files changed, 40 insertions(+), 19 deletions(-) diff --git a/assets/voxygen/voxel/sprite/furniture/drawer_large-0.vox b/assets/voxygen/voxel/sprite/furniture/drawer_large-0.vox index 3d8a19576f..d37bea3842 100644 --- a/assets/voxygen/voxel/sprite/furniture/drawer_large-0.vox +++ b/assets/voxygen/voxel/sprite/furniture/drawer_large-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6d88a3846ec7643c9903c22f1bf0ac472126db60dea3bf1a46fdc985b88f50c +oid sha256:4b288a9470d9e3b89959035b855508d43454c1a57d2dd96deb6f71e087688629 size 10352 diff --git a/assets/voxygen/voxel/sprite/furniture/drawer_large-1.vox b/assets/voxygen/voxel/sprite/furniture/drawer_large-1.vox index fa4a5d37b4..4bef736a28 100644 --- a/assets/voxygen/voxel/sprite/furniture/drawer_large-1.vox +++ b/assets/voxygen/voxel/sprite/furniture/drawer_large-1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d20a65fb839dfe38bbab6f67b783b17b6ed78e6cd4714feec51bf1ce6132b84 +oid sha256:2c79030afd2d1aa29424e7204a494ecfb5c8d4c45141ee4d0330d76b5ca019b1 size 10120 diff --git a/assets/voxygen/voxel/sprite/furniture/lamp_wall-1.vox b/assets/voxygen/voxel/sprite/furniture/lamp_wall-1.vox index 23a7a3605c..783a0f1e1a 100644 --- a/assets/voxygen/voxel/sprite/furniture/lamp_wall-1.vox +++ b/assets/voxygen/voxel/sprite/furniture/lamp_wall-1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55a879347c38c7755b829aebb954d866cb6ba3427927f9953421fcd500b914c2 +oid sha256:621d9e108e98a2e1364427bff831abf32080e96e0afe0666a6d8dc03d24aa613 size 2040 diff --git a/assets/voxygen/voxel/sprite/furniture/wardrobe_double-0.vox b/assets/voxygen/voxel/sprite/furniture/wardrobe_double-0.vox index 2c335e16d2..1bf1793559 100644 --- a/assets/voxygen/voxel/sprite/furniture/wardrobe_double-0.vox +++ b/assets/voxygen/voxel/sprite/furniture/wardrobe_double-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae7ac53ac0950b319d500046024e182d4a861a91d2a250af752935c55bf1547f +oid sha256:963f9ef3bfe6c265886f3dc49c7ac04efa3e996bd0a4fd5c7670a6011fea8831 size 23868 diff --git a/assets/voxygen/voxel/sprite/furniture/wardrobe_double-1.vox b/assets/voxygen/voxel/sprite/furniture/wardrobe_double-1.vox index f2db8bbb4d..33870384bb 100644 --- a/assets/voxygen/voxel/sprite/furniture/wardrobe_double-1.vox +++ b/assets/voxygen/voxel/sprite/furniture/wardrobe_double-1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:914db732f7cf6bda66b3a76e72b80adf0e1f75656f264ca36edbd8461c6864ce +oid sha256:a564f82caa29a9dd2e7d8a9345a5544825d5aa41fa3f9e4812b5b35166fb2411 size 24180 diff --git a/assets/voxygen/voxel/sprite/furniture/wardrobe_single-0.vox b/assets/voxygen/voxel/sprite/furniture/wardrobe_single-0.vox index c9c8c31568..3fda9eca89 100644 --- a/assets/voxygen/voxel/sprite/furniture/wardrobe_single-0.vox +++ b/assets/voxygen/voxel/sprite/furniture/wardrobe_single-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20a1e1584decf69caff7e9ae5ba281083dacbe95f9885acdc8c9fc4c68ab5fa9 +oid sha256:931ae880000e0e775ceb5b8b00f54979f06ef94f2088f435681beb02292cd804 size 13328 diff --git a/assets/voxygen/voxel/sprite/furniture/wardrobe_single-1.vox b/assets/voxygen/voxel/sprite/furniture/wardrobe_single-1.vox index 6a68423697..dd8a7e3615 100644 --- a/assets/voxygen/voxel/sprite/furniture/wardrobe_single-1.vox +++ b/assets/voxygen/voxel/sprite/furniture/wardrobe_single-1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9867ff78442e130f90f0900bb8e6c9a2307b64e27b291855fbc935164d5e8fe0 +oid sha256:9d6b5366dbb1c8c451c2e832ef6d61e9dbd2b6db84eb7e9e9760bc3624736466 size 13048 diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 4025ff8a47..3cdd6c986e 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -381,9 +381,9 @@ impl BlockKind { BlockKind::Radish => 0.18, BlockKind::Door => 3.0, BlockKind::Bed => 1.54, - BlockKind::Bench => 1.45, - BlockKind::ChairSingle => 1.36, - BlockKind::ChairDouble => 1.36, + BlockKind::Bench => 0.5, + BlockKind::ChairSingle => 0.5, + BlockKind::ChairDouble => 0.5, BlockKind::CoatRack => 2.36, BlockKind::Crate => 0.90, BlockKind::DrawerSmall => 1.0, @@ -456,6 +456,27 @@ impl Block { | BlockKind::Window2 | BlockKind::Window3 | BlockKind::Window4 + | BlockKind::Bed + | BlockKind::Bench + | BlockKind::ChairSingle + | BlockKind::ChairDouble + | BlockKind::CoatRack + | BlockKind::Crate + | BlockKind::DrawerLarge + | BlockKind::DrawerMedium + | BlockKind::DrawerSmall + | BlockKind::DungeonWallDecor + | BlockKind::HangingBasket + | BlockKind::HangingSign + | BlockKind::WallLamp + | BlockKind::Planter + | BlockKind::Shelf + | BlockKind::TableSide + | BlockKind::TableDining + | BlockKind::TableDouble + | BlockKind::WardrobeSingle + | BlockKind::WardrobeDouble + | BlockKind::Pot | BlockKind::Door => Some(self.color[0] & 0b111), _ => None, } diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 76bd95e974..0573816606 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -2195,7 +2195,7 @@ impl Terrain { (BlockKind::Bed, 0), make_models( "voxygen.voxel.sprite.furniture.bed-0", - Vec3::new(-9.5, -6.0, 0.0), + Vec3::new(-9.5, -14.5, 0.0), Vec3::one(), ), ), @@ -2452,7 +2452,7 @@ impl Terrain { (BlockKind::HangingBasket, 0), make_models( "voxygen.voxel.sprite.furniture.hanging_basket-0", - Vec3::new(-6.5, -4.5, 0.0), + Vec3::new(-6.5, -3.5, 0.0), Vec3::one(), ), ), @@ -2486,7 +2486,7 @@ impl Terrain { (BlockKind::WallLamp, 1), make_models( "voxygen.voxel.sprite.furniture.lamp_wall-1", - Vec3::new(-9.0, -10.5, 0.0), + Vec3::new(-10.5, -9.0, 0.0), Vec3::one(), ), ), @@ -2603,7 +2603,7 @@ impl Terrain { (BlockKind::TableDining, 0), make_models( "voxygen.voxel.sprite.furniture.table_dining-0", - Vec3::new(-13.5, -13.5, 0.0), + Vec3::new(-8.5, -8.5, 0.0), Vec3::one(), ), ), @@ -2611,7 +2611,7 @@ impl Terrain { (BlockKind::TableDining, 1), make_models( "voxygen.voxel.sprite.furniture.table_dining-1", - Vec3::new(-13.5, -13.5, 0.0), + Vec3::new(-8.5, -8.5, 0.0), Vec3::one(), ), ), @@ -2629,7 +2629,7 @@ impl Terrain { (BlockKind::WardrobeSingle, 0), make_models( "voxygen.voxel.sprite.furniture.wardrobe_single-0", - Vec3::new(-6.0, -5.5, 0.0), + Vec3::new(-5.5, -6.0, 0.0), Vec3::one(), ), ), @@ -2637,7 +2637,7 @@ impl Terrain { (BlockKind::WardrobeSingle, 1), make_models( "voxygen.voxel.sprite.furniture.wardrobe_single-1", - Vec3::new(-6.5, -5.5, 0.0), + Vec3::new(-5.5, -6.5, 0.0), Vec3::one(), ), ), @@ -2646,7 +2646,7 @@ impl Terrain { (BlockKind::WardrobeDouble, 0), make_models( "voxygen.voxel.sprite.furniture.wardrobe_double-0", - Vec3::new(-6.5, -10.5, 0.0), + Vec3::new(-10.5, -6.5, 0.0), Vec3::one(), ), ), @@ -2654,7 +2654,7 @@ impl Terrain { (BlockKind::WardrobeDouble, 1), make_models( "voxygen.voxel.sprite.furniture.wardrobe_double-1", - Vec3::new(-6.0, -10.5, 0.0), + Vec3::new(-10.5, -6.0, 0.0), Vec3::one(), ), ), From 41916d45944711ec0aa76eb98a05e7c2c29cc8c5 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 29 Jun 2020 13:43:16 +0100 Subject: [PATCH 08/33] Better castles, initial light prop for lit objects --- common/src/cmd.rs | 5 -- common/src/terrain/block.rs | 8 +++ voxygen/src/mesh/terrain.rs | 23 ++++++-- voxygen/src/scene/terrain.rs | 54 ++++++------------- world/src/site/castle/mod.rs | 16 +++++- .../settlement/building/archetype/house.rs | 37 +++++++++---- .../settlement/building/archetype/keep.rs | 21 +++++--- world/src/site/settlement/mod.rs | 15 ++++-- 8 files changed, 108 insertions(+), 71 deletions(-) diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 4c800c3f93..7e3194c4a6 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -298,11 +298,6 @@ impl ChatCommand { "View the server description", NoAdmin, ), - ChatCommand::MakeBlock => cmd( - vec![Enum("block", BLOCK_KINDS.clone(), Required)], - "Make a block", - Admin, - ), ChatCommand::Object => cmd( vec![Enum("object", OBJECTS.clone(), Required)], "Spawn an object", diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 3cdd6c986e..3a87f8f767 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -202,6 +202,14 @@ impl BlockKind { } } + pub fn get_glow(&self) -> Option { + match self { + BlockKind::StreetLamp | BlockKind::StreetLampTall => Some(20), + BlockKind::Velorite | BlockKind::VeloriteFrag => Some(10), + _ => None, + } + } + pub fn is_opaque(&self) -> bool { match self { BlockKind::Air => false, diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index a25d174313..b465565b71 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -4,7 +4,7 @@ use crate::{ }; use common::{ terrain::{Block, BlockKind}, - vol::{ReadVol, RectRasterableVol, Vox}, + vol::{DefaultVolIterator, ReadVol, RectRasterableVol, Vox}, volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d}, }; use std::{collections::VecDeque, fmt::Debug}; @@ -26,13 +26,16 @@ impl Blendable for BlockKind { } } +const SUNLIGHT: u8 = 24; +const MAX_LIGHT_DIST: i32 = SUNLIGHT as i32; + fn calc_light + ReadVol + Debug>( bounds: Aabb, vol: &VolGrid2d, + lit_blocks: impl Iterator, u8)>, ) -> impl FnMut(Vec3) -> f32 + '_ { const UNKNOWN: u8 = 255; const OPAQUE: u8 = 254; - const SUNLIGHT: u8 = 24; let outer = Aabb { min: bounds.min - Vec3::new(SUNLIGHT as i32 - 1, SUNLIGHT as i32 - 1, 1), @@ -47,7 +50,13 @@ fn calc_light + ReadVol + Debug>( move |x, y, z| (z * h * w + x * h + y) as usize }; // Light propagation queue - let mut prop_que = VecDeque::new(); + let mut prop_que = lit_blocks + .map(|(pos, light)| { + let rpos = pos - outer.min; + light_map[lm_idx(rpos.x, rpos.y, rpos.z)] = light; + (rpos.x as u8, rpos.y as u8, rpos.z as u16) + }) + .collect::>(); // Start sun rays for x in 0..outer.size().w { for y in 0..outer.size().h { @@ -216,7 +225,13 @@ impl<'a, V: RectRasterableVol + ReadVol + Debug> &'a self, range: Self::Supplement, ) -> (Mesh, Mesh) { - let mut light = calc_light(range, self); + // Find blocks that should glow + let lit_blocks = + DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST) + .filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); + + // Calculate chunk lighting + let mut light = calc_light(range, self, lit_blocks); let mut lowest_opaque = range.size().d; let mut highest_opaque = 0; diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 0573816606..6cffd77fd8 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -2820,26 +2820,6 @@ impl Terrain { .iter() .map(|(p, _)| *p) { - let chunk_pos = scene_data.state.terrain().pos_key(pos); - // Only mesh if this chunk has all its neighbors - let mut neighbours = true; - for i in -1..2 { - for j in -1..2 { - neighbours &= scene_data - .state - .terrain() - .get_key(chunk_pos + Vec2::new(i, j)) - .is_some(); - } - } - if neighbours { - self.mesh_todo.insert(chunk_pos, ChunkMeshState { - pos: chunk_pos, - started_tick: current_tick, - active_worker: None, - }); - } - // Handle block changes on chunk borders // Remesh all neighbours because we have complex lighting now // TODO: if lighting is on the server this can be updated to only remesh when @@ -2850,26 +2830,24 @@ impl Terrain { let neighbour_pos = pos + Vec3::new(x, y, 0); let neighbour_chunk_pos = scene_data.state.terrain().pos_key(neighbour_pos); - if neighbour_chunk_pos != chunk_pos { - // Only remesh if this chunk has all its neighbors - let mut neighbours = true; - for i in -1..2 { - for j in -1..2 { - neighbours &= scene_data - .state - .terrain() - .get_key(neighbour_chunk_pos + Vec2::new(i, j)) - .is_some(); - } - } - if neighbours { - self.mesh_todo.insert(neighbour_chunk_pos, ChunkMeshState { - pos: neighbour_chunk_pos, - started_tick: current_tick, - active_worker: None, - }); + // Only remesh if this chunk has all its neighbors + let mut neighbours = true; + for i in -1..2 { + for j in -1..2 { + neighbours &= scene_data + .state + .terrain() + .get_key(neighbour_chunk_pos + Vec2::new(i, j)) + .is_some(); } } + if neighbours { + self.mesh_todo.insert(neighbour_chunk_pos, ChunkMeshState { + pos: neighbour_chunk_pos, + started_tick: current_tick, + active_worker: None, + }); + } } } } diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index 33d83d24c2..319ffc6873 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -48,6 +48,7 @@ pub struct Castle { radius: i32, towers: Vec, segments: Vec, + rounded_towers: bool, } pub struct GenCtx<'a, R: Rng> { @@ -106,6 +107,7 @@ impl Castle { } }) .collect(), + rounded_towers: ctx.rng.gen(), segments: (0..0) //rng.gen_range(18, 24)) .map(|_| { @@ -159,7 +161,7 @@ impl Castle { continue; }; - let (wall_dist, wall_pos, wall_alt, wall_ori) = (0..self.towers.len()) + let (wall_dist, wall_pos, wall_alt, wall_ori, towers) = (0..self.towers.len()) .map(|i| { let tower0 = &self.towers[i]; let tower1 = &self.towers[(i + 1) % self.towers.len()]; @@ -171,7 +173,7 @@ impl Castle { let projected = wall .projected_point(rpos.map(|e| e as f32)) - .map(|e| e as i32); + .map(|e| e.floor() as i32); let tower0_dist = tower0 .offset @@ -195,11 +197,19 @@ impl Castle { projected, Lerp::lerp(tower0.alt as f32, tower1.alt as f32, tower_lerp) as i32, wall_ori, + [tower0, tower1], ) }) .min_by_key(|x| x.0) .unwrap(); + // Apply the dungeon entrance + let wall_sample = if let Some(col) = get_column(offs + wall_pos - rpos) { + col + } else { + col_sample + }; + for z in -10..64 { let wpos = Vec3::new(wpos2d.x, wpos2d.y, col_sample.alt as i32 + z); @@ -226,6 +236,7 @@ impl Castle { &Attr { height: 16, is_tower: false, + rounded: true, }, ); for tower in &self.towers { @@ -257,6 +268,7 @@ impl Castle { &Attr { height: 28, is_tower: true, + rounded: self.rounded_towers, }, )); } diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index ef2fd417c9..4c9a2492ef 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -85,6 +85,8 @@ pub struct Attr { pub roof_style: RoofStyle, pub mansard: i32, pub pillar: Pillar, + pub levels: i32, + pub window: BlockKind, } impl Attr { @@ -103,9 +105,16 @@ impl Attr { }, mansard: rng.gen_range(-7, 4).max(0), pillar: match rng.gen_range(0, 4) { - 0 => Pillar::Chimney(9 + locus + rng.gen_range(0, 4)), + 0 => Pillar::Chimney(rng.gen_range(1, 5)), _ => Pillar::None, }, + levels: rng.gen_range(1, 3), + window: match rng.gen_range(0, 4) { + 0 => BlockKind::Window1, + 1 => BlockKind::Window2, + 2 => BlockKind::Window3, + _ => BlockKind::Window4, + }, } } } @@ -126,10 +135,11 @@ impl Archetype for House { storey_fill: StoreyFill::All, mansard: 0, pillar: match rng.gen_range(0, 3) { - 0 => Pillar::Chimney(10 + locus + rng.gen_range(0, 4)), - 1 => Pillar::Tower(15 + locus + rng.gen_range(0, 4)), + 0 => Pillar::Chimney(rng.gen_range(1, 5)), + 1 => Pillar::Tower(5 + rng.gen_range(1, 5)), _ => Pillar::None, }, + levels: rng.gen_range(1, 4), ..Attr::generate(rng, locus) }, locus, @@ -224,12 +234,15 @@ impl Archetype for House { let empty = BlockMask::nothing(); let internal = BlockMask::new(Block::empty(), internal_layer); let end_window = BlockMask::new( - Block::new(BlockKind::Window1, make_meta(ori.flip())), + Block::new(attr.window, make_meta(ori.flip())), structural_layer, ); let fire = BlockMask::new(Block::new(BlockKind::Ember, Rgb::white()), foundation_layer); - let ceil_height = 6; + let storey_height = 6; + let storey = ((z - 1) / storey_height).min(attr.levels - 1); + let floor_height = storey_height * storey; + let ceil_height = storey_height * (storey + 1); let lower_width = locus - 1; let upper_width = locus; let width = if profile.y >= ceil_height { @@ -238,9 +251,10 @@ impl Archetype for House { lower_width }; let foundation_height = 0 - (dist - width - 1).max(0); - let roof_top = 8 + width; + let roof_top = storey_height * attr.levels + 2 + width; - if let Pillar::Chimney(chimney_top) = attr.pillar { + if let Pillar::Chimney(chimney_height) = attr.pillar { + let chimney_top = roof_top + chimney_height; // Chimney shaft if center_offset.map(|e| e.abs()).reduce_max() == 0 && profile.y >= foundation_height + 1 @@ -331,12 +345,14 @@ impl Archetype for House { && bound_offset.x < width && profile.y < ceil_height && attr.storey_fill.has_lower() + && storey == 0 { return Some( if (bound_offset.x == (width - 1) / 2 || bound_offset.x == (width - 1) / 2 + 1) && profile.y <= foundation_height + 3 { + // Doors on first floor only if profile.y == foundation_height + 1 { BlockMask::new( Block::new( @@ -377,7 +393,7 @@ impl Archetype for House { } else { ( Aabr { - min: Vec2::new(2, foundation_height + 2), + min: Vec2::new(2, floor_height + 2), max: Vec2::new(width - 2, ceil_height - 2), }, Vec2::new(1, 0), @@ -393,7 +409,7 @@ impl Archetype for House { // Window if (frame_bounds.size() + 1).reduce_min() > 2 { // Window frame is large enough for a window - let surface_pos = Vec2::new(bound_offset.x, profile.y); + let surface_pos = Vec2::new(bound_offset.x, profile.y - floor_height); if window_bounds.contains_point(surface_pos) { return Some(end_window); } else if frame_bounds.contains_point(surface_pos) { @@ -441,7 +457,8 @@ impl Archetype for House { cblock = cblock.resolve_with(block); } - if let Pillar::Tower(tower_top) = attr.pillar { + if let Pillar::Tower(tower_height) = attr.pillar { + let tower_top = roof_top + tower_height; let profile = Vec2::new(center_offset.x.abs(), profile.y); let dist = center_offset.map(|e| e.abs()).reduce_max(); diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 333c208b88..51a5c47c15 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -14,6 +14,7 @@ pub struct Keep { pub struct Attr { pub height: i32, pub is_tower: bool, + pub rounded: bool, } impl Archetype for Keep { @@ -29,6 +30,7 @@ impl Archetype for Keep { attr: Attr { height: rng.gen_range(12, 16), is_tower: false, + rounded: true, }, locus: 10 + rng.gen_range(0, 5), border: 3, @@ -41,6 +43,7 @@ impl Archetype for Keep { attr: Attr { height: rng.gen_range(20, 28), is_tower: true, + rounded: true, }, locus: 4 + rng.gen_range(0, 5), border: 3, @@ -89,7 +92,12 @@ impl Archetype for Keep { let foundation = make_block(100, 100, 100); let wall = make_block(100, 100, 110); - let floor = make_block(120, 80, 50).with_priority(important_layer); + let floor = make_block( + 80 + (pos.y.abs() % 2) as u8 * 15, + 60 + (pos.y.abs() % 2) as u8 * 15, + 10 + (pos.y.abs() % 2) as u8 * 15, + ) + .with_priority(important_layer); let pole = make_block(90, 70, 50).with_priority(important_layer); let flag = make_block(self.flag_color.r, self.flag_color.g, self.flag_color.b) .with_priority(important_layer); @@ -106,12 +114,11 @@ impl Archetype for Keep { pos.x }; let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 }; - let inner = Clamp::clamp( - center_offset, - 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(); + let min_dist = if attr.rounded { + bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32 + } else { + bound_offset.map(|e| e.abs()).reduce_max() + }; if profile.y <= 0 - (min_dist - width - 1).max(0) && min_dist < width + 3 { // Foundations diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 07a46f7c44..0c563e5ad8 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -390,6 +390,7 @@ impl Settlement { district .and_then(|d| self.town.as_ref().map(|t| t.districts().get(d))) .map(|d| d.alt) + .filter(|_| false) // Temporary .unwrap_or_else(|| { ctx.sim .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) @@ -563,8 +564,10 @@ impl Settlement { // District alt if let Some(Plot::Town { district }) = sample.plot { - if let Some(d) = - district.and_then(|d| self.town.as_ref().map(|t| t.districts().get(d))) + if let Some(d) = district + .and_then(|d| self.town.as_ref().map(|t| t.districts().get(d))) + .filter(|_| false) + // Temporary { let other = self .land @@ -576,6 +579,7 @@ impl Settlement { .and_then(|d| { self.town.as_ref().map(|t| t.districts().get(d).alt as f32) }) + .filter(|_| false) .unwrap_or(surface_z as f32); surface_z = Lerp::lerp( (other + d.alt as f32) / 2.0, @@ -640,14 +644,15 @@ impl Settlement { .rotated_z(f32::consts::PI / 2.0) .normalized(); let is_lamp = if path_dir.x.abs() > path_dir.y.abs() { - wpos2d.x as f32 % 20.0 / path_dir.dot(Vec2::unit_y()).abs() + wpos2d.x as f32 % 30.0 / path_dir.dot(Vec2::unit_y()).abs() <= 1.0 } else { - wpos2d.y as f32 % 20.0 / path_dir.dot(Vec2::unit_x()).abs() + (wpos2d.y as f32 + 10.0) % 30.0 + / path_dir.dot(Vec2::unit_x()).abs() <= 1.0 }; if (col_sample.path.map(|(dist, _)| dist > 6.0 && dist < 7.0).unwrap_or(false) && is_lamp) //roll(0, 50) == 0) - || roll(0, 2000) == 0 + || (roll(0, 2000) == 0 && col_sample.path.map(|(dist, _)| dist > 20.0).unwrap_or(true)) { surface_block = Some(Block::new(BlockKind::StreetLamp, Rgb::white())); From 6a41f6aa884df9bf9417533bf2e0461ec31689a1 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 30 Jun 2020 12:28:25 +0100 Subject: [PATCH 09/33] Lowered path priority --- world/src/block/natural.rs | 2 +- world/src/column/mod.rs | 4 +- world/src/layer/mod.rs | 4 +- world/src/lib.rs | 6 +-- world/src/sim/mod.rs | 12 +++-- world/src/sim/path.rs | 16 ++++++ world/src/site/castle/mod.rs | 49 +++++++++++-------- world/src/site/settlement/mod.rs | 83 ++++++++++++++++---------------- 8 files changed, 102 insertions(+), 74 deletions(-) diff --git a/world/src/block/natural.rs b/world/src/block/natural.rs index 845a033982..e09ca2158a 100644 --- a/world/src/block/natural.rs +++ b/world/src/block/natural.rs @@ -28,7 +28,7 @@ pub fn structure_gen<'a>( || st_sample.alt < st_sample.water_level || st_sample.spawn_rate < 0.5 || st_sample.water_dist.map(|d| d < 8.0).unwrap_or(false) - || st_sample.path.map(|(d, _)| d < 12.0).unwrap_or(false) + || st_sample.path.map(|(d, _, _, _)| d < 12.0).unwrap_or(false) { return None; } diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index bbcc762428..e0df5e832b 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,7 +1,7 @@ use crate::{ all::ForestKind, block::StructureMeta, - sim::{local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk, WorldSim}, + sim::{local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk, WorldSim, Path}, util::Sampler, Index, CONFIG, }; @@ -1157,7 +1157,7 @@ pub struct ColumnSample<'a> { pub spawn_rate: f32, pub stone_col: Rgb, pub water_dist: Option, - pub path: Option<(f32, Vec2)>, + pub path: Option<(f32, Vec2, Path, Vec2)>, pub chunk: &'a SimChunk, } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 6b3ebfbb50..d8f6b9228d 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -37,7 +37,7 @@ pub fn apply_paths_to<'a>( }) }; - if let Some((path_dist, path_nearest)) = col_sample.path.filter(|(dist, _)| *dist < 5.0) + if let Some((path_dist, path_nearest, path, _)) = col_sample.path.filter(|(dist, _, path, _)| *dist < path.width) { let inset = 0; @@ -82,7 +82,7 @@ pub fn apply_paths_to<'a>( }, ); } - let head_space = (8 - (path_dist * 0.25).powf(6.0).round() as i32).max(1); + let head_space = path.head_space(path_dist); for z in inset..inset + head_space { let pos = Vec3::new(offs.x, offs.y, surface_z + z); if vol.get(pos).unwrap().kind() != BlockKind::Water { diff --git a/world/src/lib.rs b/world/src/lib.rs index 67769a1d67..9aabf4dff8 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -175,14 +175,14 @@ impl World { let mut rng = rand::thread_rng(); + // Apply paths + layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk); + // Apply site generation sim_chunk.sites.iter().for_each(|site| { self.index.sites[*site].apply_to(chunk_wpos2d, sample_get, &mut chunk) }); - // Apply paths - layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk); - let gen_entity_pos = || { let lpos2d = TerrainChunkSize::RECT_SIZE .map(|sz| rand::thread_rng().gen::().rem_euclid(sz) as i32); diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index fc5ca00208..00c95058e3 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -15,7 +15,7 @@ pub use self::{ }, location::Location, map::{MapConfig, MapDebug}, - path::PathData, + path::{Path, PathData}, util::{ cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors, uniform_idx_as_vec2, uniform_noise, uphill, vec2_as_uniform_idx, InverseCdf, ScaleBias, @@ -1699,7 +1699,9 @@ impl WorldSim { Some(z0 + z1 + z2 + z3) } - pub fn get_nearest_path(&self, wpos: Vec2) -> Option<(f32, Vec2)> { + /// Return the distance to the nearest path in blocks, along with the closest point on the path + /// 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) }); @@ -1759,13 +1761,13 @@ impl WorldSim { .clamped(0.0, 1.0); let pos = bez.evaluate(nearest_interval); let dist_sqrd = pos.distance_squared(wpos.map(|e| e as f32)); - Some((dist_sqrd, pos)) + Some((dist_sqrd, pos, chunk.path.path, move || bez.evaluate_derivative(nearest_interval).normalized())) }), ) }) .flatten() - .min_by_key(|(dist_sqrd, _)| (dist_sqrd * 1024.0) as i32) - .map(|(dist, pos)| (dist.sqrt(), pos)) + .min_by_key(|(dist_sqrd, _, _, _)| (dist_sqrd * 1024.0) as i32) + .map(|(dist, pos, path, calc_tangent)| (dist.sqrt(), pos, path, calc_tangent())) } } diff --git a/world/src/sim/path.rs b/world/src/sim/path.rs index 525069a1d8..c8ac115b38 100644 --- a/world/src/sim/path.rs +++ b/world/src/sim/path.rs @@ -1,9 +1,15 @@ use vek::*; +#[derive(Copy, Clone, Debug)] +pub struct Path { + pub width: f32, +} + #[derive(Debug)] pub struct PathData { pub offset: Vec2, /* Offset from centre of chunk: must not be more than half chunk * width in any direction */ + pub path: Path, pub neighbors: u8, // One bit for each neighbor } @@ -15,7 +21,17 @@ impl Default for PathData { fn default() -> Self { Self { offset: Vec2::zero(), + path: Path { + width: 5.0, + }, neighbors: 0, } } } + +impl Path { + /// Return the number of blocks of headspace required at the given path distance + pub fn head_space(&self, dist: f32) -> i32 { + (8 - (dist * 0.25).powf(6.0).round() as i32).max(1) + } +} diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index 319ffc6873..12952c6d52 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -88,7 +88,7 @@ impl Castle { if ctx .sim .and_then(|sim| sim.get_nearest_path(wpos + offset)) - .map(|(dist, _)| dist > 24.0) + .map(|(dist, _, _, _)| dist > 24.0) .unwrap_or(true) { break; @@ -202,6 +202,16 @@ impl Castle { }) .min_by_key(|x| x.0) .unwrap(); + let border_pos = (wall_pos - rpos).map(|e| e.abs()); + let wall_normal = (rpos - wall_pos).map(|e| e as f32); + let wall_rpos = if wall_ori == Ori::East { + rpos + } else { + rpos.yx() + }; + let head_space = col_sample.path + .map(|(dist, _, path, _)| path.head_space(dist)) + .unwrap_or(0); // Apply the dungeon entrance let wall_sample = if let Some(col) = get_column(offs + wall_pos - rpos) { @@ -218,27 +228,26 @@ impl Castle { }; // Boundary - let border_pos = (wall_pos - rpos).map(|e| e.abs()); - let wall_rpos = if wall_ori == Ori::East { - rpos + let wall_z = wpos.z - wall_alt; + let mut mask = if z < head_space { + BlockMask::nothing() } else { - rpos.yx() + 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, + wall_z, + wall_ori, + 4, + 0, + &Attr { + height: 16, + is_tower: false, + rounded: true, + }, + ) }; - 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, - wall_ori, - 4, - 0, - &Attr { - height: 16, - is_tower: false, - rounded: true, - }, - ); for tower in &self.towers { let tower_wpos = Vec3::new( self.origin.x + tower.offset.x, diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 0c563e5ad8..4bba7a17f7 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -380,7 +380,7 @@ impl Settlement { || ctx .sim .and_then(|sim| sim.get_nearest_path(self.origin + house_pos)) - .map(|(dist, _)| dist < 28.0) + .map(|(dist, _, _, _)| dist < 28.0) .unwrap_or(false) { continue; @@ -590,45 +590,46 @@ impl Settlement { } // Paths - if let Some((WayKind::Path, dist, nearest)) = sample.way { - let inset = -1; + // if let Some((WayKind::Path, dist, nearest)) = sample.way { + // let inset = -1; - // Try to use the column at the centre of the path for sampling to make them - // flatter - let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)) - .unwrap_or(col_sample); - 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, - ((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs()) - * (col.riverless_alt + 5.0 - col.alt).max(0.0) - * 1.75 - + 3.0) as i32, - ) - } else { - (0.0, 3) - }; - let surface_z = (col.riverless_alt + bridge_offset).floor() as i32; + // // Try to use the column at the centre of the path for sampling to make them + // // flatter + // let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)) + // .unwrap_or(col_sample); + // 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, + // ((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs()) + // * (col.riverless_alt + 5.0 - col.alt).max(0.0) + // * 1.75 + // + 3.0) as i32, + // ) + // } else { + // (0.0, 3) + // }; + // let surface_z = (col.riverless_alt + bridge_offset).floor() as i32; - for z in inset - depth..inset { - let _ = vol.set( - Vec3::new(offs.x, offs.y, surface_z + z), - if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 { - Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8)) - } else { - 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); - for z in inset..inset + head_space { - let pos = Vec3::new(offs.x, offs.y, surface_z + z); - if vol.get(pos).unwrap().kind() != BlockKind::Water { - let _ = vol.set(pos, Block::empty()); - } - } - // Ground colour - } else { + // for z in inset - depth..inset { + // let _ = vol.set( + // Vec3::new(offs.x, offs.y, surface_z + z), + // if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 { + // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8)) + // } else { + // 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); + // for z in inset..inset + head_space { + // let pos = Vec3::new(offs.x, offs.y, surface_z + z); + // if vol.get(pos).unwrap().kind() != BlockKind::Water { + // let _ = vol.set(pos, Block::empty()); + // } + // } + // // Ground colour + // } else + { let mut surface_block = None; let roll = @@ -639,7 +640,7 @@ impl Settlement { Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)), Some(Plot::Water) => Some(Rgb::new(100, 150, 250)), 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)) .rotated_z(f32::consts::PI / 2.0) .normalized(); @@ -651,8 +652,8 @@ impl Settlement { / path_dir.dot(Vec2::unit_x()).abs() <= 1.0 }; - if (col_sample.path.map(|(dist, _)| dist > 6.0 && dist < 7.0).unwrap_or(false) && is_lamp) //roll(0, 50) == 0) - || (roll(0, 2000) == 0 && col_sample.path.map(|(dist, _)| dist > 20.0).unwrap_or(true)) + if (col_sample.path.map(|(dist, _, _, _)| dist > 6.0 && dist < 7.0).unwrap_or(false) && is_lamp) //roll(0, 50) == 0) + || (roll(0, 2000) == 0 && col_sample.path.map(|(dist, _, _, _)| dist > 20.0).unwrap_or(true)) { surface_block = Some(Block::new(BlockKind::StreetLamp, Rgb::white())); From bdb39b8e7f38f5bd06646396bed012ebb354a815 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 3 Jul 2020 00:14:07 +0100 Subject: [PATCH 10/33] Better castle variation, improved paths, multi-storey houses --- common/src/spiral.rs | 5 + world/src/column/mod.rs | 23 ++- world/src/layer/mod.rs | 10 +- world/src/sim/mod.rs | 10 +- world/src/sim/path.rs | 14 +- world/src/site/castle/mod.rs | 152 +++++++++++++----- .../settlement/building/archetype/house.rs | 6 +- .../settlement/building/archetype/keep.rs | 19 ++- world/src/site/settlement/mod.rs | 144 ++++++++++------- 9 files changed, 253 insertions(+), 130 deletions(-) diff --git a/common/src/spiral.rs b/common/src/spiral.rs index 2a76a3484d..93c59fef11 100644 --- a/common/src/spiral.rs +++ b/common/src/spiral.rs @@ -11,6 +11,11 @@ pub struct Spiral2d { impl Spiral2d { #[allow(clippy::new_without_default)] // TODO: Pending review in #587 pub fn new() -> Self { Self { layer: 0, i: 0 } } + + pub fn radius(self, radius: i32) -> impl Iterator> { + self.take((radius * 2 + 1).pow(2) as usize) + .filter(move |pos| pos.magnitude_squared() < (radius + 1).pow(2)) + } } impl Iterator for Spiral2d { diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index e0df5e832b..129b37545c 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,7 +1,9 @@ use crate::{ all::ForestKind, 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, Index, CONFIG, }; @@ -196,6 +198,7 @@ where let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?; let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?; 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 sim_chunk = sim.get(chunk_pos)?; 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 hot_stone = Rgb::new(0.07, 0.07, 0.06); let warm_stone = Rgb::new(0.77, 0.77, 0.64); - let beach_sand = Rgb::new(0.9, 0.82, 0.6); - let desert_sand = Rgb::new(0.95, 0.75, 0.5); + let beach_sand = Rgb::new(0.8, 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 stone_col = Rgb::new(195, 187, 201); @@ -1088,11 +1091,15 @@ where water_level, warp_factor, surface_color: Rgb::lerp( - Rgb::lerp(cliff, sand, alt.sub(basement).mul(0.25)), - // Land - ground, - // Beach - ((ocean_level - 1.0) / 2.0).max(0.0), + sub_surface_color, + Rgb::lerp( + Rgb::lerp(cliff, sand, alt.sub(basement).mul(0.25)), + // Land + ground, + // Beach + ((ocean_level - 1.0) / 2.0).max(0.0), + ), + surface_veg, ), sub_surface_color, // No growing directly on bedrock. diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index d8f6b9228d..7cb81635e6 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -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; @@ -75,9 +77,9 @@ pub fn apply_paths_to<'a>( if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 { Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8)) } else { - let path_color = col_sample - .sub_surface_color - .map(|e| (e * 255.0 * 0.7) as u8); + let path_color = path.surface_color( + col_sample.sub_surface_color.map(|e| (e * 255.0) as u8), + ); Block::new(BlockKind::Normal, noisy_color(path_color, 8)) }, ); diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 00c95058e3..3c0ba76b9d 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -1699,8 +1699,8 @@ impl WorldSim { Some(z0 + z1 + z2 + z3) } - /// Return the distance to the nearest path in blocks, along with the closest point on the path - /// 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) @@ -1761,7 +1761,9 @@ impl WorldSim { .clamped(0.0, 1.0); let pos = bez.evaluate(nearest_interval); 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 river: RiverData, pub warp_factor: f32, + pub surface_veg: f32, pub sites: Vec>, pub place: Option>, @@ -2024,6 +2027,7 @@ impl SimChunk { spawn_rate: 1.0, river, warp_factor: 1.0, + surface_veg: 1.0, sites: Vec::new(), place: None, diff --git a/world/src/sim/path.rs b/world/src/sim/path.rs index c8ac115b38..3756b14dc6 100644 --- a/world/src/sim/path.rs +++ b/world/src/sim/path.rs @@ -2,7 +2,7 @@ use vek::*; #[derive(Copy, Clone, Debug)] pub struct Path { - pub width: f32, + pub width: f32, // Actually radius } #[derive(Debug)] @@ -15,23 +15,27 @@ pub struct PathData { impl PathData { pub fn is_path(&self) -> bool { self.neighbors != 0 } + + pub fn clear(&mut self) { self.neighbors = 0; } } impl Default for PathData { fn default() -> Self { Self { offset: Vec2::zero(), - path: Path { - width: 5.0, - }, + path: Path { width: 5.0 }, neighbors: 0, } } } 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 { (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) -> Rgb { col.map(|e| (e as f32 * 0.7) as u8) } } diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index 12952c6d52..9858a6f568 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -5,7 +5,7 @@ use crate::{ sim::WorldSim, site::{ settlement::building::{ - archetype::keep::{Attr, Keep}, + archetype::keep::{Attr, Keep as KeepArchetype}, Archetype, Branch, Ori, }, BlockMask, @@ -18,6 +18,7 @@ use common::{ comp, generation::{ChunkSupplement, EntityInfo}, npc, + spiral::Spiral2d, store::{Id, Store}, terrain::{Block, BlockKind, Structure, TerrainChunkSize}, vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol}, @@ -29,11 +30,12 @@ use rand::prelude::*; use std::sync::Arc; use vek::*; -struct Segment { +struct Keep { offset: Vec2, locus: i32, height: i32, is_tower: bool, + alt: i32, } struct Tower { @@ -47,29 +49,44 @@ pub struct Castle { seed: u32, radius: i32, towers: Vec, - segments: Vec, + keeps: Vec, rounded_towers: bool, } pub struct GenCtx<'a, R: Rng> { - sim: Option<&'a WorldSim>, + sim: Option<&'a mut WorldSim>, rng: &'a mut R, } impl Castle { #[allow(clippy::let_and_return)] // TODO: Pending review in #587 - pub fn generate(wpos: Vec2, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self { + pub fn generate(wpos: Vec2, sim: Option<&mut WorldSim>, rng: &mut impl Rng) -> Self { let mut ctx = GenCtx { sim, rng }; 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 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 .sim + .as_ref() .and_then(|sim| sim.get_alt_approx(wpos)) .unwrap_or(0.0) as i32 + 6, @@ -87,6 +104,7 @@ impl Castle { for i in (1..80).step_by(5) { if ctx .sim + .as_ref() .and_then(|sim| sim.get_nearest_path(wpos + offset)) .map(|(dist, _, _, _)| dist > 24.0) .unwrap_or(true) @@ -101,6 +119,7 @@ impl Castle { offset, alt: ctx .sim + .as_ref() .and_then(|sim| sim.get_alt_approx(wpos + offset)) .unwrap_or(0.0) as i32 + 2, @@ -108,19 +127,28 @@ impl Castle { }) .collect(), 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)) - .map(|_| { - 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; + let locus = ctx.rng.gen_range(20, 26); + let offset = (dir * dist).map(|e| e as i32); + let height = ctx.rng.gen_range(25, 70).clamped(30, 48); - Segment { - offset: (dir * dist).map(|e| e as i32), - locus: ctx.rng.gen_range(6, 26), - height: height as i32, - is_tower: height > 36.0, + Keep { + offset, + locus, + height, + 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(), @@ -209,7 +237,8 @@ impl Castle { } else { rpos.yx() }; - let head_space = col_sample.path + let head_space = col_sample + .path .map(|(dist, _, path, _)| path.head_space(dist)) .unwrap_or(0); @@ -220,34 +249,37 @@ impl Castle { col_sample }; + let keep_archetype = KeepArchetype { + flag_color: Rgb::new(200, 80, 40), + }; + for z in -10..64 { 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 + // Boundary wall let wall_z = wpos.z - wall_alt; - let mut mask = if z < head_space { - BlockMask::nothing() - } else { - 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, - wall_z, - wall_ori, - 4, - 0, - &Attr { - height: 16, - is_tower: false, - rounded: true, - }, - ) - }; + if z < head_space { + continue; + } + + let mut mask = keep_archetype.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, + wall_z, + wall_ori, + 4, + 0, + &Attr { + height: 16, + is_tower: false, + ridged: false, + rounded: true, + }, + ); + + // Boundary towers for tower in &self.towers { let tower_wpos = Vec3::new( self.origin.x + tower.offset.x, @@ -257,7 +289,7 @@ impl Castle { let tower_locus = 10; 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() { wpos - tower_wpos } else { @@ -277,6 +309,42 @@ impl Castle { &Attr { height: 28, 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, }, )); diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index 4c9a2492ef..ca6c190773 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -105,7 +105,7 @@ impl Attr { }, mansard: rng.gen_range(-7, 4).max(0), 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, }, levels: rng.gen_range(1, 3), @@ -135,7 +135,7 @@ impl Archetype for House { storey_fill: StoreyFill::All, mansard: 0, 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)), _ => Pillar::None, }, @@ -409,7 +409,7 @@ impl Archetype for House { // Window if (frame_bounds.size() + 1).reduce_min() > 2 { // 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) { return Some(end_window); } else if frame_bounds.contains_point(surface_pos) { diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 51a5c47c15..3f234dc737 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -14,6 +14,7 @@ pub struct Keep { pub struct Attr { pub height: i32, pub is_tower: bool, + pub ridged: bool, pub rounded: bool, } @@ -30,6 +31,7 @@ impl Archetype for Keep { attr: Attr { height: rng.gen_range(12, 16), is_tower: false, + ridged: false, rounded: true, }, locus: 10 + rng.gen_range(0, 5), @@ -43,6 +45,7 @@ impl Archetype for Keep { attr: Attr { height: rng.gen_range(20, 28), is_tower: true, + ridged: false, rounded: true, }, locus: 4 + rng.gen_range(0, 5), @@ -104,15 +107,21 @@ impl Archetype for Keep { let internal = BlockMask::new(Block::empty(), internal_layer); let empty = BlockMask::nothing(); - 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) { + let edge_pos = if (bound_offset.x.abs() > bound_offset.y.abs()) ^ (ori == Ori::East) { pos.y } else { 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 min_dist = if attr.rounded { bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32 diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 4bba7a17f7..ace88fcf90 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -593,14 +593,15 @@ impl Settlement { // if let Some((WayKind::Path, dist, nearest)) = sample.way { // let inset = -1; - // // Try to use the column at the centre of the path for sampling to make them - // // flatter + // // Try to use the column at the centre of the path for sampling to make + // them // flatter // let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)) // .unwrap_or(col_sample); // 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, - // ((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs()) + // ((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) * + // 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) // * 1.75 // + 3.0) as i32, @@ -614,10 +615,10 @@ impl Settlement { // let _ = vol.set( // Vec3::new(offs.x, offs.y, surface_z + z), // if bridge_offset >= 2.0 && dist >= 3.0 || z < inset - 1 { - // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, 100), 8)) - // } else { - // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 50, 30), 8)) - // }, + // Block::new(BlockKind::Normal, noisy_color(Rgb::new(80, 80, + // 100), 8)) } else { + // 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); @@ -639,6 +640,7 @@ impl Settlement { Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)), Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)), Some(Plot::Water) => Some(Rgb::new(100, 150, 250)), + Some(Plot::Town { district }) => None, Some(Plot::Town { district }) => { if let Some((_, path_nearest, _, _)) = col_sample.path { let path_dir = (path_nearest - wpos2d.map(|e| e as f32)) @@ -669,65 +671,87 @@ impl Settlement { })) }, Some(Plot::Field { seed, crop, .. }) => { - 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; + 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 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)) + 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 + }); - 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 + 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, } - }) - .map(|kind| Block::new(kind, Rgb::white())); + .or_else(|| { + 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, }; From 1763693f5f430e0467e2bb4aad1629c774ecf090 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 14 Jul 2020 16:21:57 +0100 Subject: [PATCH 11/33] Added labour value to economy, castle improvements --- server-cli/.~lock.economy.csv# | 1 - server/src/cmd.rs | 2 +- world/src/civ/mod.rs | 15 +- world/src/sim/mod.rs | 6 +- world/src/sim2/mod.rs | 36 +++-- world/src/site/castle/mod.rs | 57 +++++-- world/src/site/economy.rs | 8 +- .../settlement/building/archetype/house.rs | 97 ++++++++---- world/src/site/settlement/building/mod.rs | 8 +- world/src/site/settlement/mod.rs | 145 ++++++++---------- 10 files changed, 226 insertions(+), 149 deletions(-) delete mode 100644 server-cli/.~lock.economy.csv# 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); From cb660f47bba6cd2a501944fff605975bf9501824 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 14 Jul 2020 22:59:35 +0100 Subject: [PATCH 12/33] Added waypoint to dungeon staircases --- world/src/site/dungeon/mod.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index aafe012415..201b06d371 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -151,20 +151,6 @@ impl Dungeon { max: rpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32), }; - if area.contains_point(Vec2::zero()) { - let offs = Vec2::new(rng.gen_range(-1.0, 1.0), rng.gen_range(-1.0, 1.0)) - .try_normalized() - .unwrap_or(Vec2::unit_y()) - * 12.0; - supplement.add_entity( - EntityInfo::at( - Vec3::new(self.origin.x, self.origin.y, self.alt + 16).map(|e| e as f32) - + Vec3::from(offs), - ) - .into_waypoint(), - ); - } - let mut z = self.alt + ALT_OFFSET; for floor in &self.floors { z -= floor.total_depth(); @@ -410,6 +396,20 @@ impl Floor { origin: Vec3, supplement: &mut ChunkSupplement, ) { + let stair_rcenter = Vec3::from((self.stair_tile + self.tile_offset) + .map(|e| e * TILE_SIZE + TILE_SIZE / 2)); + + if area.contains_point(stair_rcenter.xy()) { + let offs = Vec2::new(rng.gen_range(-1.0, 1.0), rng.gen_range(-1.0, 1.0)) + .try_normalized() + .unwrap_or(Vec2::unit_y()) + * FLOOR_SIZE.x as f32 / 2.0 - 8.0; + supplement.add_entity( + EntityInfo::at((origin + stair_rcenter).map(|e| e as f32) + Vec3::from(offs)) + .into_waypoint() + ); + } + for x in area.min.x..area.max.x { for y in area.min.y..area.max.y { let tile_pos = Vec2::new(x, y).map(|e| e.div_euclid(TILE_SIZE)) - self.tile_offset; From a9126f7e25886f8195eabf7c56cd8a8291b194ea Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 14 Jul 2020 23:32:25 +0100 Subject: [PATCH 13/33] Simulation improvements, removed block glows --- common/src/terrain/block.rs | 5 +++-- world/src/sim2/mod.rs | 33 +++++++++++++++++++++++---------- world/src/site/dungeon/mod.rs | 10 ++++++---- world/src/site/economy.rs | 6 +++--- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 3a87f8f767..cc0188d266 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -204,8 +204,9 @@ impl BlockKind { pub fn get_glow(&self) -> Option { match self { - BlockKind::StreetLamp | BlockKind::StreetLampTall => Some(20), - BlockKind::Velorite | BlockKind::VeloriteFrag => Some(10), + // TODO: When we have proper volumetric lighting + //BlockKind::StreetLamp | BlockKind::StreetLampTall => Some(20), + //BlockKind::Velorite | BlockKind::VeloriteFrag => Some(10), _ => None, } } diff --git a/world/src/sim2/mod.rs b/world/src/sim2/mod.rs index ad0d8b4ca9..1a45d48511 100644 --- a/world/src/sim2/mod.rs +++ b/world/src/sim2/mod.rs @@ -56,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.labor_values[*g]).unwrap(); + write!(f, "{:?},", site.economy.labor_values[*g].unwrap_or(-1.0)).unwrap(); } for g in Good::list() { write!(f, "{:?},", site.economy.stocks[*g]).unwrap(); @@ -121,7 +121,7 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { } } - let mut supply = MapVec::from_default(0.0); + let mut supply = site.economy.stocks.clone();//MapVec::from_default(0.0); for (labor, (output_good, _)) in productivity.iter() { supply[*output_good] += site.economy.yields[labor] * site.economy.labors[labor] * site.economy.pop; @@ -170,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.yields[labor] + site.economy.values[output_good].unwrap_or(0.0) + * site.economy.productivity[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) }); @@ -184,7 +185,8 @@ 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(); + let mut total_labor_values = MapVec::<_, f32>::default(); + let mut total_outputs = MapVec::<_, f32>::default(); for (labor, orders) in orders.iter() { let scale = if let Some(labor) = labor { site.economy.labors[*labor] @@ -218,7 +220,7 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { let used = quantity * labor_productivity; // Material cost of each factor of production - total_materials_cost += used * site.economy.labor_values[*good]; + total_materials_cost += used * site.economy.labor_values[*good].unwrap_or(0.0); // Deplete stocks accordingly site.economy.stocks[*good] = (site.economy.stocks[*good] - used).max(0.0); @@ -229,22 +231,33 @@ 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 * (1.0 + workers / 100.0); + let yield_per_worker = labor_productivity * final_rate * (1.0 + workers / 100.0).min(3.0); site.economy.yields[*labor] = yield_per_worker; site.economy.productivity[*labor] = labor_productivity; 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; + site.economy.material_costs[stock] = total_materials_cost / total_output.max(0.001); // Labor costs let wages = 1.0; - site.economy.labor_values[stock] += workers * wages / total_output.max(0.001); + let total_labor_cost = workers * wages; + + total_labor_values[stock] += total_materials_cost + total_labor_cost; + total_outputs[stock] += total_output; } } + // Update labour values per unit + site.economy.labor_values = total_labor_values.map(|stock, tlv| { + let total_output = total_outputs[stock]; + if total_output > 0.01 { + Some(tlv / total_outputs[stock]) + } else { + None + } + }); + // Decay stocks site.economy .stocks diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 201b06d371..3e8d7ecc5e 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -396,17 +396,19 @@ impl Floor { origin: Vec3, supplement: &mut ChunkSupplement, ) { - let stair_rcenter = Vec3::from((self.stair_tile + self.tile_offset) - .map(|e| e * TILE_SIZE + TILE_SIZE / 2)); + let stair_rcenter = + Vec3::from((self.stair_tile + self.tile_offset).map(|e| e * TILE_SIZE + TILE_SIZE / 2)); if area.contains_point(stair_rcenter.xy()) { let offs = Vec2::new(rng.gen_range(-1.0, 1.0), rng.gen_range(-1.0, 1.0)) .try_normalized() .unwrap_or(Vec2::unit_y()) - * FLOOR_SIZE.x as f32 / 2.0 - 8.0; + * FLOOR_SIZE.x as f32 + / 2.0 + - 8.0; supplement.add_entity( EntityInfo::at((origin + stair_rcenter).map(|e| e as f32) + Vec3::from(offs)) - .into_waypoint() + .into_waypoint(), ); } diff --git a/world/src/site/economy.rs b/world/src/site/economy.rs index 26dae4c831..2c59d5c31b 100644 --- a/world/src/site/economy.rs +++ b/world/src/site/economy.rs @@ -36,7 +36,7 @@ pub struct Economy { pub marginal_surplus: MapVec, pub values: MapVec>, - pub labor_values: MapVec, + pub labor_values: MapVec>, pub material_costs: MapVec, pub labors: MapVec, @@ -91,7 +91,7 @@ impl Economy { (Farmer, (Flour, 2.0)), (Lumberjack, (Wood, 0.5)), (Miner, (Stone, 0.5)), - (Fisher, (Meat, 3.0)), + (Fisher, (Meat, 4.0)), (Hunter, (Meat, 1.0)), (Cook, (Food, 16.0)), ], @@ -102,7 +102,7 @@ impl Economy { pub fn replenish(&mut self, time: f32) { use rand::Rng; - for (i, (g, v)) in [(Wheat, 195.0), (Logs, 120.0), (Rock, 120.0), (Game, 20.0)] + for (i, (g, v)) in [(Wheat, 50.0), (Logs, 20.0), (Rock, 120.0), (Game, 12.0), (Fish, 10.0)] .iter() .enumerate() { From 10757d0325a1ccc3d35417fdd04b4adff9fc165a Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 16 Jul 2020 00:12:12 +0100 Subject: [PATCH 14/33] Added experimental worldgen caves --- world/src/civ/mod.rs | 74 +++++++++++++++++++++++++++++++++++---- world/src/column/mod.rs | 6 +++- world/src/layer/mod.rs | 37 ++++++++++++++++++++ world/src/lib.rs | 3 +- world/src/sim/map.rs | 7 +++- world/src/sim/mod.rs | 64 ++++++++++++++++++++++----------- world/src/sim/path.rs | 41 ---------------------- world/src/sim/way.rs | 72 +++++++++++++++++++++++++++++++++++++ world/src/sim2/mod.rs | 5 +-- world/src/site/economy.rs | 12 +++++-- 10 files changed, 245 insertions(+), 76 deletions(-) delete mode 100644 world/src/sim/path.rs create mode 100644 world/src/sim/way.rs diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 9a34d83de0..96ff4c98f9 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -76,6 +76,10 @@ impl Civs { let rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); let mut ctx = GenCtx { sim, rng }; + for _ in 0..100 { + this.generate_cave(&mut ctx); + } + for _ in 0..INITIAL_CIV_COUNT { debug!("Creating civilisation..."); if this.birth_civ(&mut ctx.reseed()).is_none() { @@ -208,6 +212,62 @@ impl Civs { this } + // TODO: Move this + fn generate_cave(&self, ctx: &mut GenCtx) { + let mut pos = ctx.sim + .get_size() + .map(|sz| ctx.rng.gen_range(0, sz as i32) as f32); + let mut vel = pos + .map2(ctx.sim.get_size(), |pos, sz| sz as f32 / 2.0 - pos) + .try_normalized() + .unwrap_or_else(Vec2::unit_y); + + let path = (-100..100) + .filter_map(|i: i32| { + let depth = (i.abs() as f32 / 100.0 * std::f32::consts::PI / 2.0).cos(); + vel = (vel + Vec2::new( + ctx.rng.gen_range(-0.25, 0.25), + ctx.rng.gen_range(-0.25, 0.25), + )) + .try_normalized() + .unwrap_or_else(Vec2::unit_y); + let old_pos = pos.map(|e| e as i32); + pos = (pos + vel * 0.5).clamped(Vec2::zero(), ctx.sim.get_size().map(|e| e as f32 - 1.0)); + Some((pos.map(|e| e as i32), depth)).filter(|(pos, _)| *pos != old_pos) + }) + .collect::>(); + + for locs in path.windows(3) { + let to_prev_idx = NEIGHBORS + .iter() + .enumerate() + .find(|(_, dir)| **dir == locs[0].0 - locs[1].0) + .expect("Track locations must be neighbors") + .0; + let to_next_idx = NEIGHBORS + .iter() + .enumerate() + .find(|(_, dir)| **dir == locs[2].0 - locs[1].0) + .expect("Track locations must be neighbors") + .0; + + ctx.sim.get_mut(locs[0].0).unwrap().cave.0.neighbors |= + 1 << ((to_prev_idx as u8 + 4) % 8); + ctx.sim.get_mut(locs[2].0).unwrap().cave.0.neighbors |= + 1 << ((to_next_idx as u8 + 4) % 8); + let mut chunk = ctx.sim.get_mut(locs[1].0).unwrap(); + chunk.cave.0.neighbors |= + (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8)); + let depth = locs[1].1 * 250.0; + chunk.cave.1.alt = chunk.alt - depth + ctx.rng.gen_range(-4.0, 4.0) * (depth > 10.0) as i32 as f32; + chunk.cave.1.width = ctx.rng.gen_range(12.0, 32.0); + chunk.cave.0.offset = Vec2::new( + ctx.rng.gen_range(-16, 17), + ctx.rng.gen_range(-16, 17), + ); + } + } + pub fn place(&self, id: Id) -> &Place { self.places.get(id) } pub fn sites(&self) -> impl Iterator + '_ { self.sites.values() } @@ -425,16 +485,16 @@ impl Civs { .expect("Track locations must be neighbors") .0; - ctx.sim.get_mut(locs[0]).unwrap().path.neighbors |= + ctx.sim.get_mut(locs[0]).unwrap().path.0.neighbors |= 1 << ((to_prev_idx as u8 + 4) % 8); - ctx.sim.get_mut(locs[2]).unwrap().path.neighbors |= + ctx.sim.get_mut(locs[2]).unwrap().path.0.neighbors |= 1 << ((to_next_idx as u8 + 4) % 8); let mut chunk = ctx.sim.get_mut(locs[1]).unwrap(); - chunk.path.neighbors |= + chunk.path.0.neighbors |= (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8)); - chunk.path.offset = Vec2::new( - ctx.rng.gen_range(-16.0, 16.0), - ctx.rng.gen_range(-16.0, 16.0), + chunk.path.0.offset = Vec2::new( + ctx.rng.gen_range(-16, 17), + ctx.rng.gen_range(-16, 17), ); } @@ -570,7 +630,7 @@ fn walk_in_dir(sim: &WorldSim, a: Vec2, dir: Vec2) -> Option { } else { 0.0 }; - let wild_cost = if b_chunk.path.is_path() { + let wild_cost = if b_chunk.path.0.is_way() { 0.0 // Traversing existing paths has no additional cost! } else { 2.0 diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 129b37545c..7a1cdc72af 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -2,7 +2,8 @@ use crate::{ all::ForestKind, block::StructureMeta, sim::{ - local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, Path, RiverKind, SimChunk, WorldSim, + local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, + Path, Cave, RiverKind, SimChunk, WorldSim, }, util::Sampler, Index, CONFIG, @@ -1082,6 +1083,7 @@ where }; let path = sim.get_nearest_path(wpos); + let cave = sim.get_nearest_cave(wpos); Some(ColumnSample { alt, @@ -1131,6 +1133,7 @@ where stone_col, water_dist, path, + cave, chunk: sim_chunk, }) @@ -1165,6 +1168,7 @@ pub struct ColumnSample<'a> { pub stone_col: Rgb, pub water_dist: Option, pub path: Option<(f32, Vec2, Path, Vec2)>, + pub cave: Option<(f32, Vec2, Cave, Vec2)>, pub chunk: &'a SimChunk, } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 7cb81635e6..565a89b9b5 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -95,3 +95,40 @@ pub fn apply_paths_to<'a>( } } } + +pub fn apply_caves_to<'a>( + wpos2d: Vec2, + mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, + vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), +) { + for y in 0..vol.size_xy().y as i32 { + for x in 0..vol.size_xy().x as i32 { + let offs = Vec2::new(x, y); + + let wpos2d = wpos2d + offs; + + // Sample terrain + let col_sample = if let Some(col_sample) = get_column(offs) { + col_sample + } else { + continue; + }; + let surface_z = col_sample.riverless_alt.floor() as i32; + + if let Some((cave_dist, cave_nearest, cave, _)) = col_sample + .cave + .filter(|(dist, _, cave, _)| *dist < cave.width) + { + let cave_x = (cave_dist / cave.width).min(1.0); + let height = (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width; + + for z in (cave.alt - height) as i32..(cave.alt + height) as i32 { + let _ = vol.set( + Vec3::new(offs.x, offs.y, z), + Block::empty(), + ); + } + } + } + } +} diff --git a/world/src/lib.rs b/world/src/lib.rs index 9aabf4dff8..96d8865da2 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -175,8 +175,9 @@ impl World { let mut rng = rand::thread_rng(); - // Apply paths + // Apply layers (paths, caves, etc.) layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk); + layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk); // Apply site generation sim_chunk.sites.iter().for_each(|site| { diff --git a/world/src/sim/map.rs b/world/src/sim/map.rs index ebed7fae80..6655b84e35 100644 --- a/world/src/sim/map.rs +++ b/world/src/sim/map.rs @@ -157,6 +157,7 @@ impl MapConfig { downhill, river_kind, is_path, + is_cave, near_site, ) = sampler .get(pos) @@ -169,7 +170,8 @@ impl MapConfig { sample.temp, sample.downhill, sample.river.river_kind, - sample.path.is_path(), + sample.path.0.is_way(), + sample.cave.0.is_way(), sample.sites.iter().any(|site| { index.sites[*site] .get_origin() @@ -188,6 +190,7 @@ impl MapConfig { None, false, false, + false, )); let humidity = humidity.min(1.0).max(0.0); let temperature = temperature.min(1.0).max(-1.0) * 0.5 + 0.5; @@ -317,6 +320,8 @@ impl MapConfig { (0x57, 0x39, 0x33, 0xFF) } else if is_path { (0x37, 0x29, 0x23, 0xFF) + } else if is_cave { + (0x37, 0x37, 0x37, 0xFF) } else { rgba }; diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index d90f07d178..584c95dbc0 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -2,7 +2,7 @@ mod diffusion; mod erosion; mod location; mod map; -mod path; +mod way; mod util; // Reexports @@ -15,7 +15,7 @@ pub use self::{ }, location::Location, map::{MapConfig, MapDebug}, - path::{Path, PathData}, + way::{Way, Path, Cave}, util::{ cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors, uniform_idx_as_vec2, uniform_noise, uphill, vec2_as_uniform_idx, InverseCdf, ScaleBias, @@ -1700,10 +1700,14 @@ 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. - pub fn get_nearest_path(&self, wpos: Vec2) -> Option<(f32, Vec2, Path, Vec2)> { + /// Return the distance to the nearest way in blocks, along with the + /// closest point on the way, the way metadata, and the tangent vector + /// of that way. + pub fn get_nearest_way>( + &self, + wpos: Vec2, + get_way: impl Fn(&SimChunk) -> Option<(Way, M)>, + ) -> Option<(f32, Vec2, M, Vec2)> { let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { e.div_euclid(sz as i32) }); @@ -1713,32 +1717,34 @@ impl WorldSim { }) }; + let get_way = &get_way; LOCALITY .iter() .filter_map(|ctrl| { - let chunk = self.get(chunk_pos + *ctrl)?; - let ctrl_pos = - get_chunk_centre(chunk_pos + *ctrl).map(|e| e as f32) + chunk.path.offset; + let (way, meta) = get_way(self.get(chunk_pos + *ctrl)?)?; + let ctrl_pos = get_chunk_centre(chunk_pos + *ctrl).map(|e| e as f32) + way.offset.map(|e| e as f32); - let chunk_connections = chunk.path.neighbors.count_ones(); + let chunk_connections = way.neighbors.count_ones(); if chunk_connections == 0 { return None; } - let (start_pos, _start_idx) = if chunk_connections != 2 { - (ctrl_pos, None) + let (start_pos, _start_idx, start_meta) = if chunk_connections != 2 { + (ctrl_pos, None, meta.clone()) } else { let (start_idx, start_rpos) = NEIGHBORS .iter() .copied() .enumerate() - .find(|(i, _)| chunk.path.neighbors & (1 << *i as u8) != 0) + .find(|(i, _)| way.neighbors & (1 << *i as u8) != 0) .unwrap(); let start_pos_chunk = chunk_pos + *ctrl + start_rpos; + let (start_way, start_meta) = get_way(self.get(start_pos_chunk)?)?; ( get_chunk_centre(start_pos_chunk).map(|e| e as f32) - + self.get(start_pos_chunk)?.path.offset, + + start_way.offset.map(|e| e as f32), Some(start_idx), + start_meta, ) }; @@ -1746,11 +1752,12 @@ impl WorldSim { NEIGHBORS .iter() .enumerate() - .filter(move |(i, _)| chunk.path.neighbors & (1 << *i as u8) != 0) + .filter(move |(i, _)| way.neighbors & (1 << *i as u8) != 0) .filter_map(move |(_, end_rpos)| { let end_pos_chunk = chunk_pos + *ctrl + end_rpos; + let (end_way, end_meta) = get_way(self.get(end_pos_chunk)?)?; let end_pos = get_chunk_centre(end_pos_chunk).map(|e| e as f32) - + self.get(end_pos_chunk)?.path.offset; + + end_way.offset.map(|e| e as f32); let bez = QuadraticBezier2 { start: (start_pos + ctrl_pos) / 2.0, @@ -1763,7 +1770,12 @@ impl WorldSim { .clamped(0.0, 1.0); let pos = bez.evaluate(nearest_interval); let dist_sqrd = pos.distance_squared(wpos.map(|e| e as f32)); - Some((dist_sqrd, pos, chunk.path.path, move || { + let meta = if nearest_interval < 0.5 { + Lerp::lerp(start_meta.clone(), meta.clone(), 0.5 + nearest_interval) + } else { + Lerp::lerp(meta.clone(), end_meta, nearest_interval - 0.5) + }; + Some((dist_sqrd, pos, meta, move || { bez.evaluate_derivative(nearest_interval).normalized() })) }), @@ -1771,7 +1783,15 @@ impl WorldSim { }) .flatten() .min_by_key(|(dist_sqrd, _, _, _)| (dist_sqrd * 1024.0) as i32) - .map(|(dist, pos, path, calc_tangent)| (dist.sqrt(), pos, path, calc_tangent())) + .map(|(dist, pos, meta, calc_tangent)| (dist.sqrt(), pos, meta, calc_tangent())) + } + + pub fn get_nearest_path(&self, wpos: Vec2) -> Option<(f32, Vec2, Path, Vec2)> { + self.get_nearest_way(wpos, |chunk| Some(chunk.path)) + } + + pub fn get_nearest_cave(&self, wpos: Vec2) -> Option<(f32, Vec2, Cave, Vec2)> { + self.get_nearest_way(wpos, |chunk| Some(chunk.cave)) } } @@ -1797,7 +1817,10 @@ pub struct SimChunk { pub sites: Vec>, pub place: Option>, - pub path: PathData, + + pub path: (Way, Path), + pub cave: (Way, Cave), + pub contains_waypoint: bool, } @@ -2033,7 +2056,8 @@ impl SimChunk { sites: Vec::new(), place: None, - path: PathData::default(), + path: Default::default(), + cave: Default::default(), contains_waypoint: false, } } diff --git a/world/src/sim/path.rs b/world/src/sim/path.rs deleted file mode 100644 index 3756b14dc6..0000000000 --- a/world/src/sim/path.rs +++ /dev/null @@ -1,41 +0,0 @@ -use vek::*; - -#[derive(Copy, Clone, Debug)] -pub struct Path { - pub width: f32, // Actually radius -} - -#[derive(Debug)] -pub struct PathData { - pub offset: Vec2, /* Offset from centre of chunk: must not be more than half chunk - * width in any direction */ - pub path: Path, - pub neighbors: u8, // One bit for each neighbor -} - -impl PathData { - pub fn is_path(&self) -> bool { self.neighbors != 0 } - - pub fn clear(&mut self) { self.neighbors = 0; } -} - -impl Default for PathData { - fn default() -> Self { - Self { - offset: Vec2::zero(), - path: Path { width: 5.0 }, - neighbors: 0, - } - } -} - -impl Path { - /// Return the number of blocks of headspace required at the given path - /// distance - pub fn head_space(&self, dist: f32) -> i32 { - (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) -> Rgb { col.map(|e| (e as f32 * 0.7) as u8) } -} diff --git a/world/src/sim/way.rs b/world/src/sim/way.rs new file mode 100644 index 0000000000..bc573a4b17 --- /dev/null +++ b/world/src/sim/way.rs @@ -0,0 +1,72 @@ +use vek::*; + +#[derive(Copy, Clone, Debug, Default)] +pub struct Way { + /// Offset from chunk center in blocks (no more than half chunk width) + pub offset: Vec2, + /// Neighbor connections, one bit each + pub neighbors: u8, +} + +impl Way { + pub fn is_way(&self) -> bool { self.neighbors != 0 } + + pub fn clear(&mut self) { self.neighbors = 0; } +} + +#[derive(Copy, Clone, Debug)] +pub struct Path { + pub width: f32, // Actually radius +} + +impl Default for Path { + fn default() -> Self { + Self { width: 5.0 } + } +} + +impl Lerp for Path { + type Output = Self; + + fn lerp_unclamped(from: Self, to: Self, factor: f32) -> Self::Output { + Self { width: Lerp::lerp(from.width, to.width, factor) } + } +} + +impl Path { + /// Return the number of blocks of headspace required at the given path + /// distance + /// TODO: make this generic over width + pub fn head_space(&self, dist: f32) -> i32 { + (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) -> Rgb { col.map(|e| (e as f32 * 0.7) as u8) } +} + +#[derive(Copy, Clone, Debug)] +pub struct Cave { + pub width: f32, // Actually radius + pub alt: f32, // Actually radius +} + +impl Default for Cave { + fn default() -> Self { + Self { + width: 32.0, + alt: 0.0, + } + } +} + +impl Lerp for Cave { + type Output = Self; + + fn lerp_unclamped(from: Self, to: Self, factor: f32) -> Self::Output { + Self { + width: Lerp::lerp(from.width, to.width, factor), + alt: Lerp::lerp(from.alt, to.alt, factor), + } + } +} diff --git a/world/src/sim2/mod.rs b/world/src/sim2/mod.rs index 1a45d48511..3d07e8ad34 100644 --- a/world/src/sim2/mod.rs +++ b/world/src/sim2/mod.rs @@ -121,7 +121,7 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { } } - let mut supply = site.economy.stocks.clone();//MapVec::from_default(0.0); + let mut supply = site.economy.stocks.clone(); //MapVec::from_default(0.0); for (labor, (output_good, _)) in productivity.iter() { supply[*output_good] += site.economy.yields[labor] * site.economy.labors[labor] * site.economy.pop; @@ -231,7 +231,8 @@ 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 * (1.0 + workers / 100.0).min(3.0); + let yield_per_worker = + labor_productivity * final_rate * (1.0 + workers / 100.0).min(3.0); site.economy.yields[*labor] = yield_per_worker; site.economy.productivity[*labor] = labor_productivity; let total_output = yield_per_worker * workers; diff --git a/world/src/site/economy.rs b/world/src/site/economy.rs index 2c59d5c31b..f152560702 100644 --- a/world/src/site/economy.rs +++ b/world/src/site/economy.rs @@ -102,9 +102,15 @@ impl Economy { pub fn replenish(&mut self, time: f32) { use rand::Rng; - for (i, (g, v)) in [(Wheat, 50.0), (Logs, 20.0), (Rock, 120.0), (Game, 12.0), (Fish, 10.0)] - .iter() - .enumerate() + for (i, (g, v)) in [ + (Wheat, 50.0), + (Logs, 20.0), + (Rock, 120.0), + (Game, 12.0), + (Fish, 10.0), + ] + .iter() + .enumerate() { self.stocks[*g] = (*v * (1.25 + (((time * 0.0001 + i as f32).sin() + 1.0) % 1.0) * 0.5) From 41229b4665df753c527cfafdd67a44786ac491a6 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 Jul 2020 12:19:13 +0100 Subject: [PATCH 15/33] Added large caves, cave scatter, removed old caves --- assets/common/cave_scatter.ron | 10 +++++ common/src/comp/inventory/item/mod.rs | 4 +- common/src/lib.rs | 1 + .../src/{comp/inventory/item => }/lottery.rs | 20 +++++----- server/src/events/entity_manipulation.rs | 5 ++- world/src/block/mod.rs | 28 +------------ world/src/civ/mod.rs | 40 +++++++++---------- world/src/column/mod.rs | 36 +---------------- world/src/index.rs | 10 +++++ world/src/layer/mod.rs | 29 +++++++++++--- world/src/lib.rs | 6 +-- world/src/sim/mod.rs | 9 +++-- world/src/site/dungeon/mod.rs | 10 ++++- 13 files changed, 97 insertions(+), 111 deletions(-) create mode 100644 assets/common/cave_scatter.ron rename common/src/{comp/inventory/item => }/lottery.rs (81%) diff --git a/assets/common/cave_scatter.ron b/assets/common/cave_scatter.ron new file mode 100644 index 0000000000..d45653c98e --- /dev/null +++ b/assets/common/cave_scatter.ron @@ -0,0 +1,10 @@ +[ + (25, Velorite), + (50, VeloriteFrag), + (15, WhiteFlower), + (80, ShortGrass), + (150, Mushroom), + (5, Chest), + (2, Crate), + (1, Scarecrow), +] diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 3990e1c5d7..4ea11edd80 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -1,5 +1,4 @@ pub mod armor; -pub mod lottery; pub mod tool; // Reexports @@ -9,6 +8,7 @@ use crate::{ assets::{self, Asset}, effect::Effect, terrain::{Block, BlockKind}, + lottery::Lottery, }; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; @@ -161,7 +161,7 @@ impl Item { BlockKind::ShortGrass => Some(assets::load_expect_cloned("common.items.grasses.short")), BlockKind::Coconut => Some(assets::load_expect_cloned("common.items.food.coconut")), BlockKind::Chest => { - let chosen = assets::load_expect::>("common.loot_table"); + let chosen = assets::load_expect::>("common.loot_table"); let chosen = chosen.choose(); Some(assets::load_expect_cloned(chosen)) diff --git a/common/src/lib.rs b/common/src/lib.rs index 3bddacde79..6f0632e0b6 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -39,5 +39,6 @@ pub mod terrain; pub mod util; pub mod vol; pub mod volumes; +pub mod lottery; pub use loadout_builder::LoadoutBuilder; diff --git a/common/src/comp/inventory/item/lottery.rs b/common/src/lottery.rs similarity index 81% rename from common/src/comp/inventory/item/lottery.rs rename to common/src/lottery.rs index 6cbeed52d7..13b5ff8142 100644 --- a/common/src/comp/inventory/item/lottery.rs +++ b/common/src/lottery.rs @@ -1,25 +1,19 @@ use crate::assets::{self, Asset}; use rand::prelude::*; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; use std::{fs::File, io::BufReader}; -// Generate a random float between 0 and 1 -pub fn rand() -> f32 { - let mut rng = rand::thread_rng(); - rng.gen() -} - #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Lottery { items: Vec<(f32, T)>, total: f32, } -impl Asset for Lottery { +impl Asset for Lottery { const ENDINGS: &'static [&'static str] = &["ron"]; fn parse(buf_reader: BufReader) -> Result { - ron::de::from_reader::, Vec<(f32, String)>>(buf_reader) + ron::de::from_reader::, Vec<(f32, T)>>(buf_reader) .map(|items| Lottery::from_rates(items.into_iter())) .map_err(assets::Error::parse_error) } @@ -37,8 +31,8 @@ impl Lottery { Self { items, total } } - pub fn choose(&self) -> &T { - let x = rand() * self.total; + pub fn choose_seeded(&self, seed: u32) -> &T { + let x = ((seed % 65536) as f32 / 65536.0) * self.total; &self.items[self .items .binary_search_by(|(y, _)| y.partial_cmp(&x).unwrap()) @@ -46,6 +40,10 @@ impl Lottery { .1 } + pub fn choose(&self) -> &T { + self.choose_seeded(thread_rng().gen()) + } + pub fn iter(&self) -> impl Iterator { self.items.iter() } } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index ff6f4ae2b6..852b8317bc 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -2,7 +2,7 @@ use crate::{client::Client, Server, SpawnPoint, StateExt}; use common::{ assets, comp::{ - self, item::lottery::Lottery, object, Alignment, Body, Damage, DamageSource, Group, + self, object, Alignment, Body, Damage, DamageSource, Group, HealthChange, HealthSource, Player, Pos, Stats, }, msg::{PlayerListUpdate, ServerMsg}, @@ -12,6 +12,7 @@ use common::{ sys::combat::BLOCK_ANGLE, terrain::{Block, TerrainGrid}, vol::{ReadVol, Vox}, + lottery::Lottery, }; use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt}; use tracing::error; @@ -182,7 +183,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc item_drops.remove(entity); item_drop.0 } else { - let chosen = assets::load_expect::>("common.loot_table"); + let chosen = assets::load_expect::>("common.loot_table"); let chosen = chosen.choose(); assets::load_expect_cloned(chosen) diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 4edef6b8d4..323450661f 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -164,8 +164,6 @@ impl<'a> BlockGen<'a> { //tree_density, //forest_kind, //close_structures, - cave_xy, - cave_alt, marble, marble_small, rock, @@ -370,23 +368,6 @@ impl<'a> BlockGen<'a> { None } }) - .and_then(|block| { - // Caves - // Underground - let cave = cave_xy.powf(2.0) - * (wposf.z as f32 - cave_alt) - .div(40.0) - .powf(4.0) - .neg() - .add(1.0) - > 0.9993; - - if cave && wposf.z as f32 > water_height + 3.0 { - None - } else { - Some(block) - } - }) .or_else(|| { // Water if (wposf.z as f32) < water_height { @@ -422,14 +403,7 @@ pub struct ZCache<'a> { impl<'a> ZCache<'a> { pub fn get_z_limits(&self, block_gen: &mut BlockGen, index: &Index) -> (f32, f32, f32) { - let cave_depth = - if self.sample.cave_xy.abs() > 0.9 && self.sample.water_level <= self.sample.alt { - (self.sample.alt - self.sample.cave_alt + 8.0).max(0.0) - } else { - 0.0 - }; - - let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0 + cave_depth); + let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0); let min = min - 4.0; let cliff = BlockGen::get_cliff_height( diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 96ff4c98f9..53888f46de 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -214,7 +214,8 @@ impl Civs { // TODO: Move this fn generate_cave(&self, ctx: &mut GenCtx) { - let mut pos = ctx.sim + let mut pos = ctx + .sim .get_size() .map(|sz| ctx.rng.gen_range(0, sz as i32) as f32); let mut vel = pos @@ -225,14 +226,16 @@ impl Civs { let path = (-100..100) .filter_map(|i: i32| { let depth = (i.abs() as f32 / 100.0 * std::f32::consts::PI / 2.0).cos(); - vel = (vel + Vec2::new( - ctx.rng.gen_range(-0.25, 0.25), - ctx.rng.gen_range(-0.25, 0.25), - )) - .try_normalized() - .unwrap_or_else(Vec2::unit_y); + vel = (vel + + Vec2::new( + ctx.rng.gen_range(-0.35, 0.35), + ctx.rng.gen_range(-0.35, 0.35), + )) + .try_normalized() + .unwrap_or_else(Vec2::unit_y); let old_pos = pos.map(|e| e as i32); - pos = (pos + vel * 0.5).clamped(Vec2::zero(), ctx.sim.get_size().map(|e| e as f32 - 1.0)); + pos = (pos + vel * 0.5) + .clamped(Vec2::zero(), ctx.sim.get_size().map(|e| e as f32 - 1.0)); Some((pos.map(|e| e as i32), depth)).filter(|(pos, _)| *pos != old_pos) }) .collect::>(); @@ -256,15 +259,12 @@ impl Civs { ctx.sim.get_mut(locs[2].0).unwrap().cave.0.neighbors |= 1 << ((to_next_idx as u8 + 4) % 8); let mut chunk = ctx.sim.get_mut(locs[1].0).unwrap(); - chunk.cave.0.neighbors |= - (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8)); - let depth = locs[1].1 * 250.0; - chunk.cave.1.alt = chunk.alt - depth + ctx.rng.gen_range(-4.0, 4.0) * (depth > 10.0) as i32 as f32; - chunk.cave.1.width = ctx.rng.gen_range(12.0, 32.0); - chunk.cave.0.offset = Vec2::new( - ctx.rng.gen_range(-16, 17), - ctx.rng.gen_range(-16, 17), - ); + chunk.cave.0.neighbors |= (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8)); + let depth = locs[1].1 * 250.0 - 20.0; + chunk.cave.1.alt = + chunk.alt - depth + ctx.rng.gen_range(-4.0, 4.0) * (depth > 10.0) as i32 as f32; + chunk.cave.1.width = ctx.rng.gen_range(6.0, 32.0); + chunk.cave.0.offset = Vec2::new(ctx.rng.gen_range(-16, 17), ctx.rng.gen_range(-16, 17)); } } @@ -492,10 +492,8 @@ impl Civs { let mut chunk = ctx.sim.get_mut(locs[1]).unwrap(); chunk.path.0.neighbors |= (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8)); - chunk.path.0.offset = Vec2::new( - ctx.rng.gen_range(-16, 17), - ctx.rng.gen_range(-16, 17), - ); + chunk.path.0.offset = + Vec2::new(ctx.rng.gen_range(-16, 17), ctx.rng.gen_range(-16, 17)); } // Take note of the track diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 7a1cdc72af..47c28a3bab 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -2,8 +2,8 @@ use crate::{ all::ForestKind, block::StructureMeta, sim::{ - local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, - Path, Cave, RiverKind, SimChunk, WorldSim, + local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, Cave, Path, RiverKind, SimChunk, + WorldSim, }, util::Sampler, Index, CONFIG, @@ -1038,34 +1038,6 @@ where (alt, ground, sub_surface_color) }; - // Caves - let cave_at = |wposf: Vec2| { - (sim.gen_ctx.cave_0_nz.get( - Vec3::new(wposf.x, wposf.y, alt as f64 * 8.0) - .div(800.0) - .into_array(), - ) as f32) - .powf(2.0) - .neg() - .add(1.0) - .mul((1.32 - chaos).min(1.0)) - }; - let cave_xy = cave_at(wposf); - let cave_alt = alt - 24.0 - + (sim - .gen_ctx - .cave_1_nz - .get(Vec2::new(wposf.x, wposf.y).div(48.0).into_array()) as f32) - * 8.0 - + (sim - .gen_ctx - .cave_1_nz - .get(Vec2::new(wposf.x, wposf.y).div(500.0).into_array()) as f32) - .add(1.0) - .mul(0.5) - .powf(15.0) - .mul(150.0); - let near_ocean = max_river.and_then(|(_, _, river_data, _)| { if (river_data.is_lake() || river_data.river_kind == Some(RiverKind::Ocean)) && ((alt <= water_level.max(CONFIG.sea_level + 5.0) && !is_cliffs) || !near_cliffs) @@ -1118,8 +1090,6 @@ where }, forest_kind: sim_chunk.forest_kind, close_structures: self.gen_close_structures(wpos), - cave_xy, - cave_alt, marble, marble_small, rock, @@ -1153,8 +1123,6 @@ pub struct ColumnSample<'a> { pub tree_density: f32, pub forest_kind: ForestKind, pub close_structures: [Option; 9], - pub cave_xy: f32, - pub cave_alt: f32, pub marble: f32, pub marble_small: f32, pub rock: f32, diff --git a/world/src/index.rs b/world/src/index.rs index 242cbf3dcc..e181e0ed68 100644 --- a/world/src/index.rs +++ b/world/src/index.rs @@ -3,6 +3,16 @@ use common::store::{Id, Store}; #[derive(Default)] pub struct Index { + pub seed: u32, pub time: f32, pub sites: Store, } + +impl Index { + pub fn new(seed: u32) -> Self { + Self { + seed, + ..Self::default() + } + } +} diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 565a89b9b5..612b5ca9cc 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -1,10 +1,13 @@ use crate::{ column::ColumnSample, util::{RandomField, Sampler}, + Index, }; use common::{ terrain::{Block, BlockKind}, vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol}, + lottery::Lottery, + assets, }; use std::f32; use vek::*; @@ -13,6 +16,7 @@ pub fn apply_paths_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), + index: &Index, ) { for y in 0..vol.size_xy().y as i32 { for x in 0..vol.size_xy().x as i32 { @@ -100,6 +104,7 @@ pub fn apply_caves_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), + index: &Index, ) { for y in 0..vol.size_xy().y as i32 { for x in 0..vol.size_xy().x as i32 { @@ -120,14 +125,26 @@ pub fn apply_caves_to<'a>( .filter(|(dist, _, cave, _)| *dist < cave.width) { let cave_x = (cave_dist / cave.width).min(1.0); - let height = (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width; - for z in (cave.alt - height) as i32..(cave.alt + height) as i32 { - let _ = vol.set( - Vec3::new(offs.x, offs.y, z), - Block::empty(), - ); + // Relative units + let cave_floor = 0.0 - 0.5 * (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width; + let cave_height = (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width; + + // Abs units + let cave_base = (cave.alt + cave_floor) as i32; + let cave_roof = (cave.alt + cave_height) as i32; + + for z in cave_base..cave_roof { + let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::empty()); } + + // Scatter things in caves + if RandomField::new(index.seed).chance(wpos2d.into(), 0.002) && cave_base < surface_z as i32 - 25 { + let kind = *assets::load_expect::>("common.cave_scatter") + .choose_seeded(RandomField::new(index.seed + 1).get(wpos2d.into())); + let _ = vol.set(Vec3::new(offs.x, offs.y, cave_base), Block::new(kind, Rgb::zero())); + } + } } } diff --git a/world/src/lib.rs b/world/src/lib.rs index 96d8865da2..d36576595b 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -48,7 +48,7 @@ pub struct World { impl World { pub fn generate(seed: u32, opts: sim::WorldOpts) -> Self { let mut sim = sim::WorldSim::generate(seed, opts); - let mut index = Index::default(); + let mut index = Index::new(seed); let civs = civ::Civs::generate(seed, &mut sim, &mut index); sim2::simulate(&mut index, &mut sim); @@ -176,8 +176,8 @@ impl World { let mut rng = rand::thread_rng(); // Apply layers (paths, caves, etc.) - layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk); - layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk); + layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk, &self.index); + layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, &self.index); // Apply site generation sim_chunk.sites.iter().for_each(|site| { diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 584c95dbc0..92a1fe59fe 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -2,8 +2,8 @@ mod diffusion; mod erosion; mod location; mod map; -mod way; mod util; +mod way; // Reexports use self::erosion::Compute; @@ -15,12 +15,12 @@ pub use self::{ }, location::Location, map::{MapConfig, MapDebug}, - way::{Way, Path, Cave}, util::{ cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors, uniform_idx_as_vec2, uniform_noise, uphill, vec2_as_uniform_idx, InverseCdf, ScaleBias, NEIGHBOR_DELTA, }, + way::{Cave, Path, Way}, }; use crate::{ @@ -1703,7 +1703,7 @@ impl WorldSim { /// Return the distance to the nearest way in blocks, along with the /// closest point on the way, the way metadata, and the tangent vector /// of that way. - pub fn get_nearest_way>( + pub fn get_nearest_way>( &self, wpos: Vec2, get_way: impl Fn(&SimChunk) -> Option<(Way, M)>, @@ -1722,7 +1722,8 @@ impl WorldSim { .iter() .filter_map(|ctrl| { let (way, meta) = get_way(self.get(chunk_pos + *ctrl)?)?; - let ctrl_pos = get_chunk_centre(chunk_pos + *ctrl).map(|e| e as f32) + way.offset.map(|e| e as f32); + let ctrl_pos = get_chunk_centre(chunk_pos + *ctrl).map(|e| e as f32) + + way.offset.map(|e| e as f32); let chunk_connections = way.neighbors.count_ones(); if chunk_connections == 0 { diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 3e8d7ecc5e..e2decc7fc3 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -467,7 +467,15 @@ impl Floor { if room.boss { let boss_spawn_tile = room.area.center(); // Don't spawn the boss in a pillar - let boss_spawn_tile = boss_spawn_tile + if tile_is_pillar { 1 } else { 0 }; + let boss_tile_is_pillar = room + .pillars + .map(|pillar_space| { + boss_spawn_tile + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + }) + .unwrap_or(false); + let boss_spawn_tile = boss_spawn_tile + if boss_tile_is_pillar { 1 } else { 0 }; if tile_pos == boss_spawn_tile && tile_wcenter.xy() == wpos2d { let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)) From c547cdd72c6f691ffd13e5a41529298882cf57fd Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 25 Jul 2020 13:37:00 +0100 Subject: [PATCH 16/33] Fixed castle bug, better field RNG --- assets/common/cave_scatter.ron | 10 ++++++---- world/src/civ/mod.rs | 2 ++ world/src/layer/mod.rs | 2 +- world/src/site/castle/mod.rs | 2 +- world/src/site/dungeon/mod.rs | 6 +++++- world/src/util/random.rs | 17 ++++++++++++++++- 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/assets/common/cave_scatter.ron b/assets/common/cave_scatter.ron index d45653c98e..ea35812776 100644 --- a/assets/common/cave_scatter.ron +++ b/assets/common/cave_scatter.ron @@ -1,9 +1,11 @@ [ (25, Velorite), - (50, VeloriteFrag), - (15, WhiteFlower), - (80, ShortGrass), - (150, Mushroom), + (35, VeloriteFrag), + (60, Stones), + (15, PurpleFlower), + (150, ShortGrass), + (80, Mushroom), + (8, ShinyGem), (5, Chest), (2, Crate), (1, Scarecrow), diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 53888f46de..f443373a5c 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -156,6 +156,7 @@ impl Civs { - (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius) * 1.25) .min(1.0); + let rng = &mut ctx.rng; ctx.sim .get_mut(pos) // Don't disrupt chunks that are near water @@ -166,6 +167,7 @@ impl Civs { chunk.basement += diff; chunk.rockiness = 0.0; chunk.warp_factor = 0.0; + chunk.surface_veg *= 1.0 - factor * rng.gen_range(0.25, 0.9); }); } } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 612b5ca9cc..e64d54e501 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -139,7 +139,7 @@ pub fn apply_caves_to<'a>( } // Scatter things in caves - if RandomField::new(index.seed).chance(wpos2d.into(), 0.002) && cave_base < surface_z as i32 - 25 { + if RandomField::new(index.seed).chance(wpos2d.into(), 0.001) && cave_base < surface_z as i32 - 25 { let kind = *assets::load_expect::>("common.cave_scatter") .choose_seeded(RandomField::new(index.seed + 1).get(wpos2d.into())); let _ = vol.set(Vec3::new(offs.x, offs.y, cave_base), Block::new(kind, Rgb::zero())); diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index d3039e11bc..307c888568 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -202,7 +202,7 @@ impl Castle { for z in -5..3 { let pos = Vec3::new(offs.x, offs.y, surface_z + z); - if z >= 0 { + if z > 0 { if vol.get(pos).unwrap().kind() != BlockKind::Water { let _ = vol.set(pos, Block::empty()); } diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index e2decc7fc3..42476f53c6 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -201,6 +201,7 @@ pub struct Floor { hollow_depth: i32, #[allow(dead_code)] stair_tile: Vec2, + final_level: bool, } const FLOOR_SIZE: Vec2 = Vec2::new(18, 18); @@ -233,6 +234,7 @@ impl Floor { solid_depth: if level == 0 { 80 } else { 32 }, hollow_depth: 30, stair_tile: new_stair_tile - tile_offset, + final_level, }; const STAIR_ROOM_HEIGHT: i32 = 13; @@ -632,10 +634,12 @@ impl Floor { empty }; + let tunnel_height = if self.final_level { 16.0 } else { 8.0 }; + move |z| match self.tiles.get(tile_pos) { Some(Tile::Solid) => BlockMask::nothing(), Some(Tile::Tunnel) => { - if dist_to_wall >= wall_thickness && (z as f32) < 8.0 - 8.0 * tunnel_dist.powf(4.0) + if dist_to_wall >= wall_thickness && (z as f32) < tunnel_height * (1.0 - tunnel_dist.powf(4.0)) { if z == 0 { floor_sprite } else { empty } } else { diff --git a/world/src/util/random.rs b/world/src/util/random.rs index f9d1afdcc8..390ab51e8e 100644 --- a/world/src/util/random.rs +++ b/world/src/util/random.rs @@ -20,7 +20,22 @@ impl Sampler<'static> for RandomField { fn get(&self, pos: Self::Index) -> Self::Sample { let pos = pos.map(|e| u32::from_le_bytes(e.to_le_bytes())); - seed_expan::diffuse_mult(&[self.seed, pos.x, pos.y, pos.z]) + + let mut a = self.seed; + a = (a ^ 61) ^ (a >> 16); + a = a.wrapping_add(a << 3); + a = a ^ pos.x; + a = a ^ (a >> 4); + a = a.wrapping_mul(0x27d4eb2d); + a = a ^ (a >> 15); + a = a ^ pos.y; + a = (a ^ 61) ^ (a >> 16); + a = a.wrapping_add(a << 3); + a = a ^ (a >> 4); + a = a ^ pos.z; + a = a.wrapping_mul(0x27d4eb2d); + a = a ^ (a >> 15); + a } } From c8ae5163b3d8c07495c2ea173813d24edaa761a5 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 26 Jul 2020 12:41:46 +0100 Subject: [PATCH 17/33] Added stalagtites --- world/src/index.rs | 19 +++++++++++++++++-- world/src/layer/mod.rs | 15 ++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/world/src/index.rs b/world/src/index.rs index e181e0ed68..b3a5ddb88e 100644 --- a/world/src/index.rs +++ b/world/src/index.rs @@ -1,10 +1,11 @@ use crate::site::Site; use common::store::{Id, Store}; +use noise::{NoiseFn, Seedable, SuperSimplex}; -#[derive(Default)] pub struct Index { pub seed: u32, pub time: f32, + pub noise: Noise, pub sites: Store, } @@ -12,7 +13,21 @@ impl Index { pub fn new(seed: u32) -> Self { Self { seed, - ..Self::default() + time: 0.0, + noise: Noise::new(seed), + sites: Store::default(), + } + } +} + +pub struct Noise { + pub cave_nz: SuperSimplex, +} + +impl Noise { + fn new(seed: u32) -> Self { + Self { + cave_nz: SuperSimplex::new().set_seed(seed), } } } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index e64d54e501..fde2b3fa49 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -9,7 +9,8 @@ use common::{ lottery::Lottery, assets, }; -use std::f32; +use noise::NoiseFn; +use std::{f32, ops::{Sub, Mul}}; use vek::*; pub fn apply_paths_to<'a>( @@ -138,6 +139,18 @@ pub fn apply_caves_to<'a>( let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::empty()); } + // Stalagtites + let stalagtites = index.noise.cave_nz + .get(wpos2d.map(|e| e as f64 * 0.1).into_array()) + .sub(0.6) + .max(0.0) + .mul((col_sample.alt - cave_roof as f32 - 5.0).mul(0.15).clamped(0.0, 1.0) as f64) + .mul(40.0) as i32; + + for z in cave_roof - stalagtites..cave_roof { + let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::new(BlockKind::Normal, Rgb::broadcast(200))); + } + // Scatter things in caves if RandomField::new(index.seed).chance(wpos2d.into(), 0.001) && cave_base < surface_z as i32 - 25 { let kind = *assets::load_expect::>("common.cave_scatter") From 0bad704719a9c38e6996b17f9ca7127e98b733be Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 29 Jul 2020 20:58:14 +0100 Subject: [PATCH 18/33] Made agents flee --- common/src/comp/agent.rs | 34 ++++++++++++++++++++++++++++++++-- common/src/path.rs | 15 ++++++++++++--- common/src/sys/agent.rs | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 41eb83a1cd..8edc90221b 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,4 +1,5 @@ -use crate::{path::Chaser, sync::Uid}; +use crate::{path::Chaser, sync::Uid, comp::Body}; +use serde::{Deserialize, Serialize}; use specs::{Component, Entity as EcsEntity}; use specs_idvs::IdvStorage; use vek::*; @@ -54,6 +55,33 @@ impl Component for Alignment { type Storage = IdvStorage; } +#[derive(Clone, Debug, Default)] +pub struct Psyche { + pub aggro: f32, // 0.0 = always flees, 1.0 = always attacks +} + +impl<'a> From<&'a Body> for Psyche { + fn from(body: &'a Body) -> Self { + Self { + aggro: match body { + Body::Humanoid(_) => 0.8, + Body::QuadrupedSmall(_) => 0.35, + Body::QuadrupedMedium(_) => 0.5, + Body::QuadrupedLow(_) => 0.65, + Body::BirdMedium(_) => 1.0, + Body::BirdSmall(_) => 0.2, + Body::FishMedium(_) => 0.15, + Body::FishSmall(_) => 0.0, + Body::BipedLarge(_) => 1.0, + Body::Object(_) => 0.0, + Body::Golem(_) => 1.0, + Body::Critter(_) => 0.1, + Body::Dragon(_) => 1.0, + }, + } + } +} + #[derive(Clone, Debug, Default)] pub struct Agent { pub patrol_origin: Option>, @@ -61,6 +89,7 @@ pub struct Agent { /// Does the agent talk when e.g. hit by the player // TODO move speech patterns into a Behavior component pub can_speak: bool, + pub psyche: Psyche, } impl Agent { @@ -69,11 +98,12 @@ impl Agent { self } - pub fn new(origin: Vec3, can_speak: bool) -> Self { + pub fn new(origin: Vec3, can_speak: bool, body: &Body) -> Self { let patrol_origin = Some(origin); Agent { patrol_origin, can_speak, + psyche: Psyche::from(body), ..Default::default() } } diff --git a/common/src/path.rs b/common/src/path.rs index d6c10bed98..2fbf1e1887 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -402,7 +402,7 @@ fn walkable(vol: &V, pos: Vec3) -> bool where V: BaseVol + ReadVol, { - vol.get(pos - Vec3::new(0, 0, 1)) + (vol.get(pos - Vec3::new(0, 0, 1)) .map(|b| b.is_solid() && b.get_height() == 1.0) .unwrap_or(false) && vol @@ -412,7 +412,15 @@ where && vol .get(pos + Vec3::new(0, 0, 1)) .map(|b| !b.is_solid()) + .unwrap_or(true)) + || (vol + .get(pos + Vec3::new(0, 0, 0)) + .map(|b| b.is_fluid()) .unwrap_or(true) + && vol + .get(pos + Vec3::new(0, 0, 1)) + .map(|b| b.is_fluid()) + .unwrap_or(true)) } #[allow(clippy::float_cmp)] // TODO: Pending review in #587 @@ -449,7 +457,7 @@ where let heuristic = |pos: &Vec3| (pos.distance_squared(end) as f32).sqrt(); let neighbors = |pos: &Vec3| { let pos = *pos; - const DIRS: [Vec3; 17] = [ + const DIRS: [Vec3; 18] = [ Vec3::new(0, 1, 0), // Forward Vec3::new(0, 1, 1), // Forward upward Vec3::new(0, 1, 2), // Forward Upwardx2 @@ -466,7 +474,8 @@ where Vec3::new(-1, 0, 1), // Left upward Vec3::new(-1, 0, 2), // Left Upwardx2 Vec3::new(-1, 0, -1), // Left downward - Vec3::new(0, 0, -1), // Downwards + Vec3::new(0, 0, -1), // Downwards (water) + Vec3::new(0, 0, 1), // Upwards (water) ]; // let walkable = [ diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index e2cb88b750..8dc66fae2b 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -240,6 +240,7 @@ impl<'a> System<'a> for Sys { bearing.xy().try_normalized().unwrap_or(Vec2::zero()) * speed.min(0.2 + (dist - AVG_FOLLOW_DIST) / 8.0); inputs.jump.set_state(bearing.z > 1.5); + inputs.swim.set_state(bearing.z > 0.5); } } else { do_idle = true; @@ -297,7 +298,37 @@ impl<'a> System<'a> for Sys { } let dist_sqrd = pos.0.distance_squared(tgt_pos.0); - if dist_sqrd < (MIN_ATTACK_DIST * scale).powf(2.0) { + + let damage = stats + .get(entity) + .map(|s| s.health.current() as f32 / s.health.maximum() as f32) + .unwrap_or(0.5); + + // Flee + if 1.0 - agent.psyche.aggro > damage { + if let Some((bearing, speed)) = chaser.chase( + &*terrain, + pos.0, + vel.0, + // Away from the target (ironically) + tgt_pos.0 + (pos.0 - tgt_pos.0) + .try_normalized() + .unwrap_or_else(Vec3::unit_y) * 32.0, + TraversalConfig { + node_tolerance, + slow_factor, + on_ground: physics_state.on_ground, + min_tgt_dist: 1.25, + }, + ) { + inputs.move_dir = Vec2::from(bearing) + .try_normalized() + .unwrap_or(Vec2::zero()) + * speed; + inputs.jump.set_state(bearing.z > 1.5); + inputs.swim.set_state(bearing.z > 0.5); + } + } else if dist_sqrd < (MIN_ATTACK_DIST * scale).powf(2.0) { // Close-range attack inputs.move_dir = Vec2::from(tgt_pos.0 - pos.0) .try_normalized() @@ -360,6 +391,7 @@ impl<'a> System<'a> for Sys { .unwrap_or(Vec2::zero()) * speed; inputs.jump.set_state(bearing.z > 1.5); + inputs.swim.set_state(bearing.z > 0.5); } if dist_sqrd < 16.0f32.powf(2.0) From 9bb10a6d000a3d48684c9c26117cfcbe7ec39cd3 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 30 Jul 2020 00:14:06 +0100 Subject: [PATCH 19/33] Various improvements to castles, dungeons and more --- common/src/lib.rs | 2 +- common/src/sys/agent.rs | 47 ++++++------ common/src/sys/phys.rs | 6 +- server/src/sys/terrain.rs | 4 +- world/src/layer/mod.rs | 50 +++++++++---- world/src/site/castle/mod.rs | 12 +-- world/src/site/dungeon/mod.rs | 10 ++- .../settlement/building/archetype/keep.rs | 75 ++++++++++++++----- 8 files changed, 137 insertions(+), 69 deletions(-) diff --git a/common/src/lib.rs b/common/src/lib.rs index 6f0632e0b6..f5bf9ef71a 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -22,6 +22,7 @@ pub mod event; pub mod figure; pub mod generation; pub mod loadout_builder; +pub mod lottery; pub mod msg; pub mod npc; pub mod outcome; @@ -39,6 +40,5 @@ pub mod terrain; pub mod util; pub mod vol; pub mod volumes; -pub mod lottery; pub use loadout_builder::LoadoutBuilder; diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 8dc66fae2b..6dc9d5cf42 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -151,6 +151,7 @@ impl<'a> System<'a> for Sys { const SEARCH_DIST: f32 = 48.0; const SIGHT_DIST: f32 = 128.0; const MIN_ATTACK_DIST: f32 = 3.5; + const MAX_FLEE_DIST: f32 = 32.0; let scale = scales.get(entity).map(|s| s.0).unwrap_or(1.0); @@ -306,27 +307,31 @@ impl<'a> System<'a> for Sys { // Flee if 1.0 - agent.psyche.aggro > damage { - if let Some((bearing, speed)) = chaser.chase( - &*terrain, - pos.0, - vel.0, - // Away from the target (ironically) - tgt_pos.0 + (pos.0 - tgt_pos.0) - .try_normalized() - .unwrap_or_else(Vec3::unit_y) * 32.0, - TraversalConfig { - node_tolerance, - slow_factor, - on_ground: physics_state.on_ground, - min_tgt_dist: 1.25, - }, - ) { - inputs.move_dir = Vec2::from(bearing) - .try_normalized() - .unwrap_or(Vec2::zero()) - * speed; - inputs.jump.set_state(bearing.z > 1.5); - inputs.swim.set_state(bearing.z > 0.5); + if dist_sqrd < MAX_FLEE_DIST.powf(2.0) { + if let Some((bearing, speed)) = chaser.chase( + &*terrain, + pos.0, + vel.0, + // Away from the target (ironically) + pos.0 + (pos.0 - tgt_pos.0) + .try_normalized() + .unwrap_or_else(Vec3::unit_y) * 8.0, + TraversalConfig { + node_tolerance, + slow_factor, + on_ground: physics_state.on_ground, + min_tgt_dist: 1.25, + }, + ) { + inputs.move_dir = Vec2::from(bearing) + .try_normalized() + .unwrap_or(Vec2::zero()) + * speed; + inputs.jump.set_state(bearing.z > 1.5); + inputs.swim.set_state(bearing.z > 0.5); + } + } else { + do_idle = true; } } else if dist_sqrd < (MIN_ATTACK_DIST * scale).powf(2.0) { // Close-range attack diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 0df848919c..b3cec5dca2 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -157,9 +157,9 @@ impl<'a> System<'a> for Sys { z_max, } => { // Scale collider - let radius = *radius * scale; - let z_min = *z_min * scale; - let z_max = *z_max * scale; + let radius = *radius;// * scale; + let z_min = *z_min;// * scale; + let z_max = *z_max;// * scale; // Probe distances let hdist = radius.ceil() as i32; diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index e37ceb1847..739d1988b1 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -319,12 +319,12 @@ impl<'a> System<'a> for Sys { pos: Pos(entity.pos), stats, loadout, - body, agent: if entity.has_agency { - Some(comp::Agent::new(entity.pos, can_speak)) + Some(comp::Agent::new(entity.pos, can_speak, &body)) } else { None }, + body, alignment, scale: comp::Scale(scale), drop_item: entity.loot_drop, diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index fde2b3fa49..687eb44bd8 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -4,13 +4,16 @@ use crate::{ Index, }; use common::{ + assets, + lottery::Lottery, terrain::{Block, BlockKind}, vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol}, - lottery::Lottery, - assets, }; use noise::NoiseFn; -use std::{f32, ops::{Sub, Mul}}; +use std::{ + f32, + ops::{Mul, Sub}, +}; use vek::*; pub fn apply_paths_to<'a>( @@ -136,28 +139,49 @@ pub fn apply_caves_to<'a>( let cave_roof = (cave.alt + cave_height) as i32; for z in cave_base..cave_roof { - let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::empty()); + if cave_x < 0.95 + || index.noise.cave_nz.get( + Vec3::new(wpos2d.x, wpos2d.y, z) + .map(|e| e as f64 * 0.15) + .into_array(), + ) < 0.0 + { + let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::empty()); + } } // Stalagtites - let stalagtites = index.noise.cave_nz - .get(wpos2d.map(|e| e as f64 * 0.1).into_array()) - .sub(0.6) + let stalagtites = index + .noise + .cave_nz + .get(wpos2d.map(|e| e as f64 * 0.125).into_array()) + .sub(0.5) .max(0.0) - .mul((col_sample.alt - cave_roof as f32 - 5.0).mul(0.15).clamped(0.0, 1.0) as f64) - .mul(40.0) as i32; + .mul( + (col_sample.alt - cave_roof as f32 - 5.0) + .mul(0.15) + .clamped(0.0, 1.0) as f64, + ) + .mul(45.0) as i32; for z in cave_roof - stalagtites..cave_roof { - let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::new(BlockKind::Normal, Rgb::broadcast(200))); + let _ = vol.set( + Vec3::new(offs.x, offs.y, z), + Block::new(BlockKind::Rock, Rgb::broadcast(200)), + ); } // Scatter things in caves - if RandomField::new(index.seed).chance(wpos2d.into(), 0.001) && cave_base < surface_z as i32 - 25 { + if RandomField::new(index.seed).chance(wpos2d.into(), 0.001) + && cave_base < surface_z as i32 - 25 + { let kind = *assets::load_expect::>("common.cave_scatter") .choose_seeded(RandomField::new(index.seed + 1).get(wpos2d.into())); - let _ = vol.set(Vec3::new(offs.x, offs.y, cave_base), Block::new(kind, Rgb::zero())); + let _ = vol.set( + Vec3::new(offs.x, offs.y, cave_base), + Block::new(kind, Rgb::zero()), + ); } - } } } diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index 307c888568..60b570bb99 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -33,7 +33,7 @@ use vek::*; struct Keep { offset: Vec2, locus: i32, - height: i32, + storeys: i32, is_tower: bool, alt: i32, } @@ -123,12 +123,12 @@ impl Castle { let locus = ctx.rng.gen_range(20, 26); let offset = (dir * dist).map(|e| e as i32); - let height = ctx.rng.gen_range(25, 70).clamped(30, 48); + let storeys = ctx.rng.gen_range(1, 5).clamped(2, 3); Keep { offset, locus, - height, + storeys, is_tower: true, alt: ctx .sim @@ -301,7 +301,7 @@ impl Castle { 4, 0, &Attr { - height: 16, + storeys: 1, is_tower: false, ridged: false, rounded: true, @@ -336,7 +336,7 @@ impl Castle { tower_locus, 0, &Attr { - height: 28, + storeys: 2, is_tower: true, ridged: false, rounded: self.rounded_towers, @@ -371,7 +371,7 @@ impl Castle { keep.locus, 0, &Attr { - height: keep.height, + storeys: keep.storeys, is_tower: keep.is_tower, ridged: true, rounded: self.rounded_towers, diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 42476f53c6..f95adc2cf8 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -408,10 +408,12 @@ impl Floor { * FLOOR_SIZE.x as f32 / 2.0 - 8.0; - supplement.add_entity( - EntityInfo::at((origin + stair_rcenter).map(|e| e as f32) + Vec3::from(offs)) - .into_waypoint(), - ); + if !self.final_level { + supplement.add_entity( + EntityInfo::at((origin + stair_rcenter).map(|e| e as f32) + Vec3::from(offs)) + .into_waypoint(), + ); + } } for x in area.min.x..area.max.x { diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 3f234dc737..ee197e3848 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -1,5 +1,8 @@ use super::{super::skeleton::*, Archetype}; -use crate::site::BlockMask; +use crate::{ + site::BlockMask, + util::{RandomField, Sampler}, +}; use common::{ terrain::{Block, BlockKind}, vol::Vox, @@ -12,7 +15,7 @@ pub struct Keep { } pub struct Attr { - pub height: i32, + pub storeys: i32, pub is_tower: bool, pub ridged: bool, pub rounded: bool, @@ -29,7 +32,7 @@ impl Archetype for Keep { root: Branch { len, attr: Attr { - height: rng.gen_range(12, 16), + storeys: 1, is_tower: false, ridged: false, rounded: true, @@ -43,12 +46,12 @@ impl Archetype for Keep { Branch { len: 0, attr: Attr { - height: rng.gen_range(20, 28), + storeys: 2, is_tower: true, ridged: false, rounded: true, }, - locus: 4 + rng.gen_range(0, 5), + locus: 6 + rng.gen_range(0, 3), border: 3, children: Vec::new(), }, @@ -93,8 +96,10 @@ impl Archetype for Keep { ) }; - let foundation = make_block(100, 100, 100); - let wall = make_block(100, 100, 110); + let brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1); + let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24; + let foundation = make_block(80 + brick_tex, 80 + brick_tex, 80 + brick_tex); + let wall = make_block(100 + brick_tex, 100 + brick_tex, 110 + brick_tex); let floor = make_block( 80 + (pos.y.abs() % 2) as u8 * 15, 60 + (pos.y.abs() % 2) as u8 * 15, @@ -107,6 +112,26 @@ impl Archetype for Keep { let internal = BlockMask::new(Block::empty(), internal_layer); let empty = BlockMask::nothing(); + let make_staircase = move |pos: Vec3, radius: f32, inner_radius: f32, stretch: f32| { + let stone = BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(150, 150, 175)), 5); + + if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) { + stone + } else if (pos.xy().magnitude_squared() as f32) < radius.powf(2.0) { + if ((pos.x as f32).atan2(pos.y as f32) / (std::f32::consts::PI * 2.0) * stretch + + pos.z as f32) + .rem_euclid(stretch) + < 1.5 + { + stone + } else { + internal + } + } else { + BlockMask::nothing() + } + }; + let edge_pos = if (bound_offset.x.abs() > bound_offset.y.abs()) ^ (ori == Ori::East) { pos.y } else { @@ -114,15 +139,16 @@ impl Archetype for Keep { }; let width = locus - + if edge_pos % 4 == 0 && attr.ridged && !attr.rounded { + + if (edge_pos + 1000) % 8 < 4 && attr.ridged && !attr.rounded { 1 } else { 0 }; let rampart_width = 2 + width; - let ceil_height = attr.height; + let storey_height = 16; + let roof_height = attr.storeys * storey_height; let door_height = 6; - let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 }; + let rampart_height = roof_height + if edge_pos % 2 == 0 { 3 } else { 4 }; let min_dist = if attr.rounded { bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32 } else { @@ -132,31 +158,32 @@ impl Archetype for Keep { if profile.y <= 0 - (min_dist - width - 1).max(0) && min_dist < width + 3 { // Foundations foundation - } else if profile.y == ceil_height && min_dist < rampart_width { + } else if (0..=roof_height).contains(&profile.y) && profile.y % storey_height == 0 && min_dist < rampart_width { if min_dist < width { floor } else { wall } } else if !attr.is_tower && bound_offset.x.abs() == 4 && min_dist == width + 1 - && profile.y < ceil_height + && profile.y < roof_height { wall } else if bound_offset.x.abs() < 3 && profile.y < door_height - bound_offset.x.abs() && profile.y > 0 + && min_dist >= width - 2 { internal - } else if min_dist == width && profile.y <= ceil_height { + } else if min_dist == width && profile.y <= roof_height { wall - } else if profile.y >= ceil_height { - if profile.y > ceil_height && min_dist < rampart_width { - if attr.is_tower && center_offset == Vec2::zero() && profile.y < ceil_height + 16 { + } else if profile.y >= roof_height { + if profile.y > roof_height && min_dist < rampart_width { + if attr.is_tower && center_offset == Vec2::zero() && profile.y < roof_height + 16 { pole } else if attr.is_tower && center_offset.x == 0 && center_offset.y > 0 && center_offset.y < 8 - && profile.y > ceil_height + 8 - && profile.y < ceil_height + 14 + && profile.y > roof_height + 8 + && profile.y < roof_height + 14 { flag } else { @@ -171,10 +198,20 @@ impl Archetype for Keep { } else { empty } - } else if profile.y < ceil_height && min_dist < width { + } else if profile.y < roof_height && min_dist < width { internal } else { empty } + .resolve_with(if attr.is_tower && profile.y > 0 && profile.y <= roof_height { + make_staircase( + Vec3::new(center_offset.x, center_offset.y, pos.z), + 7.0f32.min(width as f32 - 1.0), + 0.5, + 9.0, + ) + } else { + BlockMask::nothing() + }) } } From dfecf0dad66b540307f495d6a093cb1fa9c55401 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 30 Jul 2020 16:46:03 +0100 Subject: [PATCH 20/33] Massively improved pathfinding reliability --- common/src/path.rs | 46 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/common/src/path.rs b/common/src/path.rs index 2fbf1e1887..fdfe3ec340 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -135,12 +135,12 @@ impl Route { && (pos.z - closest_tgt.z < 1.2 || (pos.z - closest_tgt.z < 2.9 && vel.z < -0.05)) && vel.z <= 0.0 // Only consider the node reached if there's nothing solid between us and it - && vol + && (vol .ray(pos + Vec3::unit_z() * 1.5, closest_tgt + Vec3::unit_z() * 1.5) .until(|block| block.is_solid()) .cast() .0 - > pos.distance(closest_tgt) * 0.9 + > pos.distance(closest_tgt) * 0.9 || dist_sqrd < 0.5) && self.next_idx < self.path.len() { // Node completed, move on to the next one @@ -358,13 +358,6 @@ impl Chaser { self.route .as_mut() .and_then(|r| r.traverse(vol, pos, vel, traversal_cfg)) - // In theory this filter isn't needed, but in practice agents often try to take - // stale paths that start elsewhere. This code makes sure that we're only using - // paths that start near us, avoiding the agent doubling back to chase a stale - // path. - .filter(|(bearing, _)| bearing.xy() - .magnitude_squared() < 1.75f32.powf(2.0) - && thread_rng().gen::() > 0.025) } } else { None @@ -384,12 +377,20 @@ impl Chaser { || self.route.is_none() { let (start_pos, path) = find_path(&mut self.astar, vol, pos, tgt); - // Don't use a stale path - if start_pos.distance_squared(pos) < 4.0f32.powf(2.0) { - self.route = path.map(Route::from); - } else { - self.route = None; - } + + self.route = path.map(|path| { + let tgt_dir = (tgt - pos).try_normalized().unwrap_or_default(); + let start_index = path + .iter() + .enumerate() + .min_by_key(|(_, node)| node.map(|e| e as f32).distance_squared(pos + tgt_dir) as i32) + .map(|(idx, _)| idx); + + Route { + path, + next_idx: start_index.unwrap_or(0), + } + }); } Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 0.75)) @@ -402,7 +403,7 @@ fn walkable(vol: &V, pos: Vec3) -> bool where V: BaseVol + ReadVol, { - (vol.get(pos - Vec3::new(0, 0, 1)) + vol.get(pos - Vec3::new(0, 0, 1)) .map(|b| b.is_solid() && b.get_height() == 1.0) .unwrap_or(false) && vol @@ -412,15 +413,7 @@ where && vol .get(pos + Vec3::new(0, 0, 1)) .map(|b| !b.is_solid()) - .unwrap_or(true)) - || (vol - .get(pos + Vec3::new(0, 0, 0)) - .map(|b| b.is_fluid()) .unwrap_or(true) - && vol - .get(pos + Vec3::new(0, 0, 1)) - .map(|b| b.is_fluid()) - .unwrap_or(true)) } #[allow(clippy::float_cmp)] // TODO: Pending review in #587 @@ -457,7 +450,7 @@ where let heuristic = |pos: &Vec3| (pos.distance_squared(end) as f32).sqrt(); let neighbors = |pos: &Vec3| { let pos = *pos; - const DIRS: [Vec3; 18] = [ + const DIRS: [Vec3; 17] = [ Vec3::new(0, 1, 0), // Forward Vec3::new(0, 1, 1), // Forward upward Vec3::new(0, 1, 2), // Forward Upwardx2 @@ -474,8 +467,7 @@ where Vec3::new(-1, 0, 1), // Left upward Vec3::new(-1, 0, 2), // Left Upwardx2 Vec3::new(-1, 0, -1), // Left downward - Vec3::new(0, 0, -1), // Downwards (water) - Vec3::new(0, 0, 1), // Upwards (water) + Vec3::new(0, 0, -1), // Downwards ]; // let walkable = [ From a966841c5a8e16606d26334bc655371871aa68de Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 30 Jul 2020 16:46:17 +0100 Subject: [PATCH 21/33] Castle and cave tweaks --- world/src/civ/mod.rs | 14 ++- world/src/site/castle/mod.rs | 41 ++++++--- .../settlement/building/archetype/keep.rs | 89 ++++++++++++------- 3 files changed, 96 insertions(+), 48 deletions(-) diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index f443373a5c..f569b8b054 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -258,15 +258,23 @@ impl Civs { ctx.sim.get_mut(locs[0].0).unwrap().cave.0.neighbors |= 1 << ((to_prev_idx as u8 + 4) % 8); + ctx.sim.get_mut(locs[1].0).unwrap().cave.0.neighbors |= + (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8)); ctx.sim.get_mut(locs[2].0).unwrap().cave.0.neighbors |= 1 << ((to_next_idx as u8 + 4) % 8); - let mut chunk = ctx.sim.get_mut(locs[1].0).unwrap(); - chunk.cave.0.neighbors |= (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8)); - let depth = locs[1].1 * 250.0 - 20.0; + } + + for (i, loc) in path.iter().enumerate() { + let mut chunk = ctx.sim.get_mut(loc.0).unwrap(); + let depth = loc.1 * 250.0 - 20.0; chunk.cave.1.alt = chunk.alt - depth + ctx.rng.gen_range(-4.0, 4.0) * (depth > 10.0) as i32 as f32; chunk.cave.1.width = ctx.rng.gen_range(6.0, 32.0); chunk.cave.0.offset = Vec2::new(ctx.rng.gen_range(-16, 17), ctx.rng.gen_range(-16, 17)); + + if chunk.cave.1.alt + chunk.cave.1.width + 5.0 > chunk.alt { + chunk.spawn_rate = 0.0; + } } } diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index 60b570bb99..afd2404442 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -51,6 +51,7 @@ pub struct Castle { towers: Vec, keeps: Vec, rounded_towers: bool, + ridged: bool, } pub struct GenCtx<'a, R: Rng> { @@ -114,6 +115,7 @@ impl Castle { }) .collect(), rounded_towers: ctx.rng.gen(), + ridged: ctx.rng.gen(), keeps: (0..keep_count) .map(|i| { let angle = (i as f32 / keep_count as f32) * f32::consts::PI * 2.0; @@ -123,7 +125,7 @@ impl Castle { let locus = ctx.rng.gen_range(20, 26); let offset = (dir * dist).map(|e| e as i32); - let storeys = ctx.rng.gen_range(1, 5).clamped(2, 3); + let storeys = ctx.rng.gen_range(1, 8).clamped(3, 5); Keep { offset, @@ -244,9 +246,9 @@ impl Castle { let wall_ori = if (tower0.offset.x - tower1.offset.x).abs() < (tower0.offset.y - tower1.offset.y).abs() { - Ori::East - } else { Ori::North + } else { + Ori::East }; ( @@ -271,13 +273,15 @@ impl Castle { .map(|(dist, _, path, _)| path.head_space(dist)) .unwrap_or(0); - // Apply the dungeon entrance let wall_sample = if let Some(col) = get_column(offs + wall_pos - rpos) { col } else { col_sample }; + // Make sure particularly weird terrain doesn't give us underground walls + let wall_alt = wall_alt + (wall_sample.alt as i32 - wall_alt - 10).max(0); + let keep_archetype = KeepArchetype { flag_color: Rgb::new(200, 80, 40), }; @@ -294,17 +298,18 @@ impl Castle { let mut mask = keep_archetype.draw( Vec3::from(wall_rpos) + Vec3::unit_z() * wpos.z - wall_alt, wall_dist, - Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()), + border_pos, rpos - wall_pos, wall_z, wall_ori, 4, 0, &Attr { - storeys: 1, + storeys: 2, is_tower: false, ridged: false, rounded: true, + has_doors: false, }, ); @@ -329,17 +334,22 @@ impl Castle { ) }, border_pos.reduce_max() - tower_locus, - Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()), + Vec2::new(border_pos.reduce_min(), border_pos.reduce_max()), (wpos - tower_wpos).xy(), wpos.z - tower.alt, - Ori::East, + if border_pos.x > border_pos.y { + Ori::East + } else { + Ori::North + }, tower_locus, 0, &Attr { - storeys: 2, + storeys: 3, is_tower: true, - ridged: false, + ridged: self.ridged, rounded: self.rounded_towers, + has_doors: false, }, )); } @@ -364,17 +374,22 @@ impl Castle { ) }, border_pos.reduce_max() - keep.locus, - Vec2::new(border_pos.reduce_max(), border_pos.reduce_min()), + Vec2::new(border_pos.reduce_min(), border_pos.reduce_max()), (wpos - keep_wpos).xy(), wpos.z - keep.alt, - Ori::East, + if border_pos.x > border_pos.y { + Ori::East + } else { + Ori::North + }, keep.locus, 0, &Attr { storeys: keep.storeys, is_tower: keep.is_tower, - ridged: true, + ridged: self.ridged, rounded: self.rounded_towers, + has_doors: true, }, )); } diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index ee197e3848..476ab57f8d 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -19,6 +19,7 @@ pub struct Attr { pub is_tower: bool, pub ridged: bool, pub rounded: bool, + pub has_doors: bool, } impl Archetype for Keep { @@ -26,16 +27,18 @@ impl Archetype for Keep { fn generate(rng: &mut R) -> (Self, Skeleton) { let len = rng.gen_range(-8, 24).max(0); + let storeys = 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 }, root: Branch { len, attr: Attr { - storeys: 1, + storeys, is_tower: false, ridged: false, rounded: true, + has_doors: true, }, locus: 10 + rng.gen_range(0, 5), border: 3, @@ -46,10 +49,11 @@ impl Archetype for Keep { Branch { len: 0, attr: Attr { - storeys: 2, + storeys: storeys + rng.gen_range(1, 3), is_tower: true, ridged: false, rounded: true, + has_doors: false, }, locus: 6 + rng.gen_range(0, 3), border: 3, @@ -89,6 +93,17 @@ impl Archetype for Keep { let important_layer = normal_layer + 1; let internal_layer = important_layer + 1; + let make_meta = |ori| { + Rgb::new( + match ori { + Ori::East => 0, + Ori::North => 2, + }, + 0, + 0, + ) + }; + let make_block = |r, g, b| { BlockMask::new( Block::new(BlockKind::Normal, Rgb::new(r, g, b)), @@ -100,6 +115,10 @@ impl Archetype for Keep { let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24; let foundation = make_block(80 + brick_tex, 80 + brick_tex, 80 + brick_tex); let wall = make_block(100 + brick_tex, 100 + brick_tex, 110 + brick_tex); + let window = BlockMask::new( + Block::new(BlockKind::Window1, make_meta(ori.flip())), + normal_layer, + ); let floor = make_block( 80 + (pos.y.abs() % 2) as u8 * 15, 60 + (pos.y.abs() % 2) as u8 * 15, @@ -132,23 +151,19 @@ impl Archetype for Keep { } }; - let edge_pos = if (bound_offset.x.abs() > bound_offset.y.abs()) ^ (ori == Ori::East) { - pos.y - } else { - pos.x - }; - + let ridge_x = (center_offset.map(|e| e.abs()).reduce_min() + 2) % 8; let width = locus - + if (edge_pos + 1000) % 8 < 4 && attr.ridged && !attr.rounded { + + if ridge_x < 4 && attr.ridged && !attr.rounded { 1 } else { 0 }; let rampart_width = 2 + width; - let storey_height = 16; + let storey_height = 9; let roof_height = attr.storeys * storey_height; + let storey_y = profile.y % storey_height; let door_height = 6; - let rampart_height = roof_height + if edge_pos % 2 == 0 { 3 } else { 4 }; + let rampart_height = roof_height + if ridge_x % 2 == 0 { 3 } else { 4 }; let min_dist = if attr.rounded { bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32 } else { @@ -158,24 +173,32 @@ impl Archetype for Keep { if profile.y <= 0 - (min_dist - width - 1).max(0) && min_dist < width + 3 { // Foundations foundation - } else if (0..=roof_height).contains(&profile.y) && profile.y % storey_height == 0 && min_dist < rampart_width { + } else if (0..=roof_height).contains(&profile.y) && storey_y == 0 && min_dist <= width + 1 { if min_dist < width { floor } else { wall } - } else if !attr.is_tower - && bound_offset.x.abs() == 4 - && min_dist == width + 1 - && profile.y < roof_height - { - wall } else if bound_offset.x.abs() < 3 && profile.y < door_height - bound_offset.x.abs() && profile.y > 0 && min_dist >= width - 2 + && min_dist <= width + 1 + && attr.has_doors { internal - } else if min_dist == width && profile.y <= roof_height { - wall + } else if (min_dist == width || (!attr.is_tower && min_dist == width + 1)) + && profile.y <= roof_height + { + if attr.is_tower + && (3..7).contains(&storey_y) + && bound_offset.x.abs() < width - 2 + && (5..7).contains(&ridge_x) + { + window + } else { + wall + } } else if profile.y >= roof_height { - if profile.y > roof_height && min_dist < rampart_width { + if profile.y > roof_height + && (min_dist < rampart_width - 1 || (attr.is_tower && min_dist < rampart_width)) + { if attr.is_tower && center_offset == Vec2::zero() && profile.y < roof_height + 16 { pole } else if attr.is_tower @@ -189,7 +212,7 @@ impl Archetype for Keep { } else { empty } - } else if min_dist == rampart_width { + } else if min_dist <= rampart_width { if profile.y < rampart_height { wall } else { @@ -203,15 +226,17 @@ impl Archetype for Keep { } else { empty } - .resolve_with(if attr.is_tower && profile.y > 0 && profile.y <= roof_height { - make_staircase( - Vec3::new(center_offset.x, center_offset.y, pos.z), - 7.0f32.min(width as f32 - 1.0), - 0.5, - 9.0, - ) - } else { - BlockMask::nothing() - }) + .resolve_with( + if attr.is_tower && profile.y > 0 && profile.y <= roof_height { + make_staircase( + Vec3::new(center_offset.x, center_offset.y, pos.z), + 7.0f32.min(width as f32 - 1.0), + 0.5, + 9.0, + ) + } else { + BlockMask::nothing() + }, + ) } } From ee83479748e51d2e7d300ad8589bb653301b8d5b Mon Sep 17 00:00:00 2001 From: Pfauenauge Date: Fri, 31 Jul 2020 01:18:33 +0200 Subject: [PATCH 22/33] drop gate parts --- .../voxel/sprite/castle/drop_gate_bars-0.vox | 3 +++ .../sprite/castle/drop_gate_bottom-0.vox | 3 +++ common/src/terrain/block.rs | 11 ++++++++ voxygen/src/scene/terrain.rs | 25 +++++++++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 assets/voxygen/voxel/sprite/castle/drop_gate_bars-0.vox create mode 100644 assets/voxygen/voxel/sprite/castle/drop_gate_bottom-0.vox diff --git a/assets/voxygen/voxel/sprite/castle/drop_gate_bars-0.vox b/assets/voxygen/voxel/sprite/castle/drop_gate_bars-0.vox new file mode 100644 index 0000000000..af1d7539c8 --- /dev/null +++ b/assets/voxygen/voxel/sprite/castle/drop_gate_bars-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c336506cf0c529f31d0f09e9f5eef136607b08ea3f6f0eb859d46820646d921 +size 1588 diff --git a/assets/voxygen/voxel/sprite/castle/drop_gate_bottom-0.vox b/assets/voxygen/voxel/sprite/castle/drop_gate_bottom-0.vox new file mode 100644 index 0000000000..d4f2b9f807 --- /dev/null +++ b/assets/voxygen/voxel/sprite/castle/drop_gate_bottom-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e954d6f39d2eaede5fb4f44b900f9575e6cc6f33c5918032f1e17fb54168fbd5 +size 1228 diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index cc0188d266..39e940448b 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -88,6 +88,8 @@ pub enum BlockKind { Stones, Twigs, ShinyGem, + DropGate, + DropGateBottom, } impl fmt::Display for BlockKind { @@ -191,6 +193,8 @@ impl BlockKind { BlockKind::Stones => true, BlockKind::Twigs => true, BlockKind::ShinyGem => true, + BlockKind::DropGate => false, + BlockKind::DropGateBottom => false, _ => false, } } @@ -288,6 +292,8 @@ impl BlockKind { BlockKind::Stones => false, BlockKind::Twigs => false, BlockKind::ShinyGem => false, + BlockKind::DropGate => false, + BlockKind::DropGateBottom => false, _ => true, } } @@ -362,6 +368,8 @@ impl BlockKind { BlockKind::Stones => false, BlockKind::Twigs => false, BlockKind::ShinyGem => false, + BlockKind::DropGate => true, + BlockKind::DropGateBottom => false, _ => true, } } @@ -486,7 +494,10 @@ impl Block { | BlockKind::WardrobeSingle | BlockKind::WardrobeDouble | BlockKind::Pot + | BlockKind::DropGate + | BlockKind::DropGateBottom | BlockKind::Door => Some(self.color[0] & 0b111), + _ => None, } } diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 6cffd77fd8..6fa942cda8 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -350,6 +350,14 @@ fn sprite_config_for(kind: BlockKind) -> Option { variations: 3, wind_sway: 0.0, }), + BlockKind::DropGate => Some(SpriteConfig { + variations: 1, + wind_sway: 0.0, + }), + BlockKind::DropGateBottom => Some(SpriteConfig { + variations: 1, + wind_sway: 0.0, + }), _ => None, } } @@ -2733,6 +2741,23 @@ impl Terrain { Vec3::one(), ), ), + // Drop Gate Parts + ( + (BlockKind::DropGate, 0), + make_models( + "voxygen.voxel.sprite.castle.drop_gate_bars-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::DropGateBottom, 0), + make_models( + "voxygen.voxel.sprite.castle.drop_gate_bottom-0", + Vec3::new(-5.5, -5.5, 0.0), + Vec3::one(), + ), + ), ] .into_iter() .collect(), From d48ed42fda193ea683cd1056688ea9bbb99c3895 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 30 Jul 2020 17:10:10 +0100 Subject: [PATCH 23/33] Prevented NPCs running gleefully off cliffs without a second thought --- common/src/path.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/common/src/path.rs b/common/src/path.rs index fdfe3ec340..ba22fde6c2 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -4,7 +4,6 @@ use crate::{ vol::{BaseVol, ReadVol}, }; use hashbrown::hash_map::DefaultHashBuilder; -use rand::prelude::*; use std::iter::FromIterator; use vek::*; @@ -366,6 +365,8 @@ impl Chaser { if let Some((bearing, speed)) = bearing { Some((bearing, speed)) } else { + let tgt_dir = (tgt - pos).xy().try_normalized().unwrap_or_default(); + // Only search for a path if the target has moved from their last position. We // don't want to be thrashing the pathfinding code for targets that // we're unable to access! @@ -379,11 +380,10 @@ impl Chaser { let (start_pos, path) = find_path(&mut self.astar, vol, pos, tgt); self.route = path.map(|path| { - let tgt_dir = (tgt - pos).try_normalized().unwrap_or_default(); let start_index = path .iter() .enumerate() - .min_by_key(|(_, node)| node.map(|e| e as f32).distance_squared(pos + tgt_dir) as i32) + .min_by_key(|(_, node)| node.xy().map(|e| e as f32).distance_squared(pos.xy() + tgt_dir) as i32) .map(|(idx, _)| idx); Route { @@ -393,7 +393,11 @@ impl Chaser { }); } - Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 0.75)) + if walkable(vol, (pos + Vec3::::from(tgt_dir) * 3.0).map(|e| e as i32)) { + Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 0.75)) + } else { + None + } } } } From d7ccb28ea70f69b1865b0f4838b5551e12a0241a Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 30 Jul 2020 20:26:49 +0100 Subject: [PATCH 24/33] Pathfinding improvements --- common/src/comp/agent.rs | 2 +- common/src/path.rs | 68 ++++++++++++++++++++++------------------ common/src/sys/agent.rs | 38 ++++++++++++---------- 3 files changed, 61 insertions(+), 47 deletions(-) diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 8edc90221b..a776eb2890 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -64,7 +64,7 @@ impl<'a> From<&'a Body> for Psyche { fn from(body: &'a Body) -> Self { Self { aggro: match body { - Body::Humanoid(_) => 0.8, + Body::Humanoid(_) => 0.5, Body::QuadrupedSmall(_) => 0.35, Body::QuadrupedMedium(_) => 0.5, Body::QuadrupedLow(_) => 0.65, diff --git a/common/src/path.rs b/common/src/path.rs index ba22fde6c2..5cfb245fc8 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -4,6 +4,7 @@ use crate::{ vol::{BaseVol, ReadVol}, }; use hashbrown::hash_map::DefaultHashBuilder; +use rand::prelude::*; use std::iter::FromIterator; use vek::*; @@ -67,6 +68,17 @@ pub struct TraversalConfig { pub min_tgt_dist: f32, } +const DIAGONALS: [Vec2; 8] = [ + Vec2::new(1, 0), + Vec2::new(1, 1), + Vec2::new(0, 1), + Vec2::new(-1, 1), + Vec2::new(-1, 0), + Vec2::new(-1, -1), + Vec2::new(0, -1), + Vec2::new(1, -1), +]; + impl Route { pub fn path(&self) -> &Path> { &self.path } @@ -90,42 +102,31 @@ impl Route { let next0 = self .next(0) .unwrap_or_else(|| pos.map(|e| e.floor() as i32)); + let next1 = self.next(1).unwrap_or(next0); // Stop using obstructed paths - if vol.get(next0).map(|b| b.is_solid()).unwrap_or(false) { + if !walkable(vol, next1) { return None; } - let diagonals = [ - Vec2::new(1, 0), - Vec2::new(1, 1), - Vec2::new(0, 1), - Vec2::new(-1, 1), - Vec2::new(-1, 0), - Vec2::new(-1, -1), - Vec2::new(0, -1), - Vec2::new(1, -1), - ]; - - let next1 = self.next(1).unwrap_or(next0); - - let be_precise = diagonals.iter().any(|pos| { - !walkable(vol, next0 + Vec3::new(pos.x, pos.y, 0)) - && !walkable(vol, next0 + Vec3::new(pos.x, pos.y, -1)) - && !walkable(vol, next0 + Vec3::new(pos.x, pos.y, -2)) - && !walkable(vol, next0 + Vec3::new(pos.x, pos.y, 1)) + let be_precise = DIAGONALS.iter().any(|pos| { + (-2..2) + .all(|z| vol.get(next0 + Vec3::new(pos.x, pos.y, z)) + .map(|b| !b.is_solid()) + .unwrap_or(false)) }); let next0_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); let next1_tgt = next1.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); let next_tgt = next0_tgt; - // Maybe skip a node (useful with traversing downhill) - let closest_tgt = if next0_tgt.distance_squared(pos) < next1_tgt.distance_squared(pos) { - next0_tgt - } else { - next1_tgt - }; + // // Maybe skip a node (useful with traversing downhill) + // let closest_tgt = if next0_tgt.distance_squared(pos) < next1_tgt.distance_squared(pos) { + // next0_tgt + // } else { + // next1_tgt + // }; + let closest_tgt = next0_tgt.map2(pos, |tgt, pos| pos.clamped(tgt.floor(), tgt.ceil())); // Determine whether we're close enough to the next to to consider it completed let dist_sqrd = pos.xy().distance_squared(closest_tgt.xy()); @@ -349,9 +350,7 @@ impl Chaser { // theory this shouldn't happen, but in practice the world is full // of unpredictable obstacles that are more than willing to mess up // our day. TODO: Come up with a better heuristic for this - if end_to_tgt > pos_to_tgt * 0.3 + 5.0 - /* || thread_rng().gen::() < 0.005 */ - { + if end_to_tgt > pos_to_tgt * 0.3 + 5.0 || thread_rng().gen::() < 0.001 { None } else { self.route @@ -393,7 +392,12 @@ impl Chaser { }); } - if walkable(vol, (pos + Vec3::::from(tgt_dir) * 3.0).map(|e| e as i32)) { + let walking_towards_edge = (-3..2) + .all(|z| vol.get((pos + Vec3::::from(tgt_dir) * 2.5).map(|e| e as i32) + Vec3::unit_z() * z) + .map(|b| !b.is_solid()) + .unwrap_or(false)); + + if !walking_towards_edge { Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 0.75)) } else { None @@ -454,23 +458,27 @@ where let heuristic = |pos: &Vec3| (pos.distance_squared(end) as f32).sqrt(); let neighbors = |pos: &Vec3| { let pos = *pos; - const DIRS: [Vec3; 17] = [ + const DIRS: [Vec3; 21] = [ Vec3::new(0, 1, 0), // Forward Vec3::new(0, 1, 1), // Forward upward Vec3::new(0, 1, 2), // Forward Upwardx2 Vec3::new(0, 1, -1), // Forward downward + Vec3::new(0, 1, -2), // Forward downwardx2 Vec3::new(1, 0, 0), // Right Vec3::new(1, 0, 1), // Right upward Vec3::new(1, 0, 2), // Right Upwardx2 Vec3::new(1, 0, -1), // Right downward + Vec3::new(1, 0, -2), // Right downwardx2 Vec3::new(0, -1, 0), // Backwards Vec3::new(0, -1, 1), // Backward Upward Vec3::new(0, -1, 2), // Backward Upwardx2 Vec3::new(0, -1, -1), // Backward downward + Vec3::new(0, -1, -2), // Backward downwardx2 Vec3::new(-1, 0, 0), // Left Vec3::new(-1, 0, 1), // Left upward Vec3::new(-1, 0, 2), // Left Upwardx2 Vec3::new(-1, 0, -1), // Left downward + Vec3::new(-1, 0, -2), // Left downwardx2 Vec3::new(0, 0, -1), // Downwards ]; diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 6dc9d5cf42..5c49227a6e 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -159,7 +159,7 @@ impl<'a> System<'a> for Sys { // and so can afford to be less precise when trying to move around // the world (especially since they would otherwise get stuck on // obstacles that smaller entities would not). - let node_tolerance = scale + vel.0.xy().magnitude() * 0.2; + let node_tolerance = scale * 1.5; let slow_factor = body.map(|b| b.base_accel() / 250.0).unwrap_or(0.0).min(1.0); let mut do_idle = false; @@ -306,7 +306,10 @@ impl<'a> System<'a> for Sys { .unwrap_or(0.5); // Flee - if 1.0 - agent.psyche.aggro > damage { + let flees = alignment + .map(|a| !matches!(a, Alignment::Enemy | Alignment::Owned(_))) + .unwrap_or(true); + if 1.0 - agent.psyche.aggro > damage && flees { if dist_sqrd < MAX_FLEE_DIST.powf(2.0) { if let Some((bearing, speed)) = chaser.chase( &*terrain, @@ -464,7 +467,7 @@ impl<'a> System<'a> for Sys { // Attack a target that's attacking us if let Some(my_stats) = stats.get(entity) { // Only if the attack was recent - if my_stats.health.last_change.0 < 5.0 { + if my_stats.health.last_change.0 < 3.0 { if let comp::HealthSource::Attack { by } | comp::HealthSource::Projectile { owner: Some(by) } = my_stats.health.last_change.1.cause @@ -473,20 +476,23 @@ impl<'a> System<'a> for Sys { if let Some(attacker) = uid_allocator.retrieve_entity_internal(by.id()) { if stats.get(attacker).map_or(false, |a| !a.is_dead) { - if agent.can_speak { - let msg = "npc.speech.villager_under_attack".to_string(); - event_bus.emit_now(ServerEvent::Chat( - UnresolvedChatMsg::npc(*uid, msg), - )); - } + match agent.activity { + Activity::Attack { target, .. } if target == attacker => {}, + _ => { + if agent.can_speak { + let msg = "npc.speech.villager_under_attack".to_string(); + event_bus.emit_now(ServerEvent::Chat(UnresolvedChatMsg::npc(*uid, msg))); + } - agent.activity = Activity::Attack { - target: attacker, - chaser: Chaser::default(), - time: time.0, - been_close: false, - powerup: 0.0, - }; + agent.activity = Activity::Attack { + target: attacker, + chaser: Chaser::default(), + time: time.0, + been_close: false, + powerup: 0.0, + }; + }, + } } } } From 9329b4ce5526aa6d407d9f3436bad7d7b7d450c6 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 7 Aug 2020 17:47:50 +0100 Subject: [PATCH 25/33] Added monsters to caves --- common/src/astar.rs | 12 ++- common/src/path.rs | 52 +++++---- world/src/layer/mod.rs | 102 ++++++++++++++++++ world/src/lib.rs | 3 + world/src/site/castle/keep.rs | 20 ++++ world/src/site/castle/mod.rs | 27 ++++- .../settlement/building/archetype/keep.rs | 15 ++- world/src/util/wgrid.rs | 26 +++++ 8 files changed, 230 insertions(+), 27 deletions(-) create mode 100644 world/src/site/castle/keep.rs create mode 100644 world/src/util/wgrid.rs diff --git a/common/src/astar.rs b/common/src/astar.rs index 920aeeee88..35781f0317 100644 --- a/common/src/astar.rs +++ b/common/src/astar.rs @@ -122,11 +122,9 @@ impl Astar { let iter_limit = self.max_iters.min(self.iter + iters); while self.iter < iter_limit { if let Some(PathEntry { node, cost }) = self.potential_nodes.pop() { - self.cheapest_cost = Some(cost); if satisfied(&node) { return PathResult::Path(self.reconstruct_path_to(node)); } else { - self.cheapest_node = Some(node.clone()); for neighbor in neighbors(&node) { let node_cheapest = self.cheapest_scores.get(&node).unwrap_or(&f32::MAX); let neighbor_cheapest = @@ -136,12 +134,18 @@ impl Astar { if cost < *neighbor_cheapest { self.came_from.insert(neighbor.clone(), node.clone()); self.cheapest_scores.insert(neighbor.clone(), cost); - let neighbor_cost = cost + heuristic(&neighbor); + let h = heuristic(&neighbor); + let neighbor_cost = cost + h; self.final_scores.insert(neighbor.clone(), neighbor_cost); + if self.cheapest_cost.map(|cc| h < cc).unwrap_or(true) { + self.cheapest_node = Some(node.clone()); + self.cheapest_cost = Some(h); + }; + if self.visited.insert(neighbor.clone()) { self.potential_nodes.push(PathEntry { - node: neighbor.clone(), + node: neighbor, cost: neighbor_cost, }); } diff --git a/common/src/path.rs b/common/src/path.rs index 5cfb245fc8..510724b597 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -99,6 +99,11 @@ impl Route { V: BaseVol + ReadVol, { let (next0, next1, next_tgt, be_precise) = loop { + // If we've reached the end of the path, stop + if self.next(0).is_none() { + return None; + } + let next0 = self .next(0) .unwrap_or_else(|| pos.map(|e| e.floor() as i32)); @@ -110,7 +115,7 @@ impl Route { } let be_precise = DIAGONALS.iter().any(|pos| { - (-2..2) + (-1..2) .all(|z| vol.get(next0 + Vec3::new(pos.x, pos.y, z)) .map(|b| !b.is_solid()) .unwrap_or(false)) @@ -312,7 +317,7 @@ impl Route { #[derive(Default, Clone, Debug)] pub struct Chaser { last_search_tgt: Option>, - route: Option, + route: Option<(Route, bool)>, /// We use this hasher (AAHasher) because: /// (1) we care about DDOS attacks (ruling out FxHash); /// (2) we don't care about determinism across computers (we can use @@ -335,14 +340,22 @@ impl Chaser { let pos_to_tgt = pos.distance(tgt); // If we're already close to the target then there's nothing to do - if ((pos - tgt) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared() + let end = self.route + .as_ref() + .and_then(|(r, _)| r.path.end().copied()) + .map(|e| e.map(|e| e as f32 + 0.5)) + .unwrap_or(tgt); + if ((pos - end) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared() < traversal_cfg.min_tgt_dist.powf(2.0) { self.route = None; return None; } - let bearing = if let Some(end) = self.route.as_ref().and_then(|r| r.path().end().copied()) { + let bearing = if let Some((end, complete)) = self.route + .as_ref() + .and_then(|(r, complete)| Some((r.path().end().copied()?, *complete))) + { let end_to_tgt = end.map(|e| e as f32).distance(tgt); // If the target has moved significantly since the path was generated then it's // time to search for a new path. Also, do this randomly from time @@ -350,12 +363,12 @@ impl Chaser { // theory this shouldn't happen, but in practice the world is full // of unpredictable obstacles that are more than willing to mess up // our day. TODO: Come up with a better heuristic for this - if end_to_tgt > pos_to_tgt * 0.3 + 5.0 || thread_rng().gen::() < 0.001 { + if (end_to_tgt > pos_to_tgt * 0.3 + 5.0 && complete) || thread_rng().gen::() < 0.001 { None } else { self.route .as_mut() - .and_then(|r| r.traverse(vol, pos, vel, traversal_cfg)) + .and_then(|(r, _)| r.traverse(vol, pos, vel, traversal_cfg)) } } else { None @@ -376,7 +389,9 @@ impl Chaser { || self.astar.is_some() || self.route.is_none() { - let (start_pos, path) = find_path(&mut self.astar, vol, pos, tgt); + self.last_search_tgt = Some(tgt); + + let (path, complete) = find_path(&mut self.astar, vol, pos, tgt); self.route = path.map(|path| { let start_index = path @@ -385,10 +400,10 @@ impl Chaser { .min_by_key(|(_, node)| node.xy().map(|e| e as f32).distance_squared(pos.xy() + tgt_dir) as i32) .map(|(idx, _)| idx); - Route { + (Route { path, next_idx: start_index.unwrap_or(0), - } + }, complete) }); } @@ -424,13 +439,14 @@ where .unwrap_or(true) } -#[allow(clippy::float_cmp)] // TODO: Pending review in #587 +/// Attempt to search for a path to a target, returning the path (if one was found) +/// and whether it is complete (reaches the target) fn find_path( astar: &mut Option, DefaultHashBuilder>>, vol: &V, startf: Vec3, endf: Vec3, -) -> (Vec3, Option>>) +) -> (Option>>, bool) where V: BaseVol + ReadVol, { @@ -452,7 +468,7 @@ where get_walkable_z(endf.map(|e| e.floor() as i32)), ) { (Some(start), Some(end)) => (start, end), - _ => return (startf, None), + _ => return (None, false), }; let heuristic = |pos: &Vec3| (pos.distance_squared(end) as f32).sqrt(); @@ -554,19 +570,19 @@ where *astar = Some(new_astar); - (startf, match path_result { + match path_result { PathResult::Path(path) => { *astar = None; - Some(path) + (Some(path), true) }, PathResult::None(path) => { *astar = None; - Some(path) + (Some(path), false) }, PathResult::Exhausted(path) => { *astar = None; - Some(path) + (Some(path), false) }, - PathResult::Pending => None, - }) + PathResult::Pending => (None, false), + } } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 687eb44bd8..100ff61f1e 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -5,9 +5,11 @@ use crate::{ }; use common::{ assets, + comp, lottery::Lottery, terrain::{Block, BlockKind}, vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol}, + generation::{ChunkSupplement, EntityInfo}, }; use noise::NoiseFn; use std::{ @@ -15,6 +17,7 @@ use std::{ ops::{Mul, Sub}, }; use vek::*; +use rand::prelude::*; pub fn apply_paths_to<'a>( wpos2d: Vec2, @@ -186,3 +189,102 @@ pub fn apply_caves_to<'a>( } } } + +pub fn apply_caves_supplement<'a>( + rng: &mut impl Rng, + wpos2d: Vec2, + mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, + vol: &(impl BaseVol + RectSizedVol + ReadVol + WriteVol), + index: &Index, + supplement: &mut ChunkSupplement, +) { + for y in 0..vol.size_xy().y as i32 { + for x in 0..vol.size_xy().x as i32 { + let offs = Vec2::new(x, y); + + let wpos2d = wpos2d + offs; + + // Sample terrain + let col_sample = if let Some(col_sample) = get_column(offs) { + col_sample + } else { + continue; + }; + let surface_z = col_sample.riverless_alt.floor() as i32; + + if let Some((cave_dist, cave_nearest, cave, _)) = col_sample + .cave + .filter(|(dist, _, cave, _)| *dist < cave.width) + { + let cave_x = (cave_dist / cave.width).min(1.0); + + // Relative units + let cave_floor = 0.0 - 0.5 * (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width; + let cave_height = (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width; + + // Abs units + let cave_base = (cave.alt + cave_floor) as i32; + let cave_roof = (cave.alt + cave_height) as i32; + + // Scatter things in caves + if RandomField::new(index.seed).chance(wpos2d.into(), 0.0001) + && cave_base < surface_z as i32 - 40 + { + let entity = EntityInfo::at(Vec3::new(wpos2d.x as f32, wpos2d.y as f32, cave_base as f32)) + .with_alignment(comp::Alignment::Enemy) + .with_body(match rng.gen_range(0, 6) { + 0 => { + let species = match rng.gen_range(0, 2) { + 0 => comp::quadruped_small::Species::Truffler, + _ => comp::quadruped_small::Species::Hyena, + }; + comp::quadruped_small::Body::random_with(rng, &species).into() + }, + 1 => { + let species = match rng.gen_range(0, 3) { + 0 => comp::quadruped_medium::Species::Tarasque, + 1 => comp::quadruped_medium::Species::Frostfang, + _ => comp::quadruped_medium::Species::Bonerattler, + }; + comp::quadruped_medium::Body::random_with(rng, &species).into() + }, + 2 => { + let species = match rng.gen_range(0, 3) { + 0 => comp::quadruped_low::Species::Maneater, + 1 => comp::quadruped_low::Species::Rocksnapper, + _ => comp::quadruped_low::Species::Salamander, + }; + comp::quadruped_low::Body::random_with(rng, &species).into() + }, + 3 => { + let species = match rng.gen_range(0, 3) { + 0 => comp::critter::Species::Fungome, + 1 => comp::critter::Species::Axolotl, + _ => comp::critter::Species::Rat, + }; + comp::critter::Body::random_with(rng, &species).into() + }, + 4 => { + let species = match rng.gen_range(0, 1) { + _ => comp::golem::Species::StoneGolem, + }; + comp::golem::Body::random_with(rng, &species).into() + }, + _ => { + let species = match rng.gen_range(0, 4) { + 0 => comp::biped_large::Species::Ogre, + 1 => comp::biped_large::Species::Cyclops, + 2 => comp::biped_large::Species::Wendigo, + _ => comp::biped_large::Species::Troll, + }; + comp::biped_large::Body::random_with(rng, &species).into() + }, + }) + .with_automatic_name(); + + supplement.add_entity(entity); + } + } + } + } +} diff --git a/world/src/lib.rs b/world/src/lib.rs index d36576595b..9f08d58570 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -228,6 +228,9 @@ impl World { supplement.add_entity(EntityInfo::at(gen_entity_pos()).into_waypoint()); } + // Apply layer supplement + layer::apply_caves_supplement(&mut rng, chunk_wpos2d, sample_get, &chunk, &self.index, &mut supplement); + // Apply site supplementary information sim_chunk.sites.iter().for_each(|site| { self.index.sites[*site].apply_supplement( diff --git a/world/src/site/castle/keep.rs b/world/src/site/castle/keep.rs new file mode 100644 index 0000000000..2dff94facc --- /dev/null +++ b/world/src/site/castle/keep.rs @@ -0,0 +1,20 @@ +use vek::*; +use crate::{ + util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, +}; + +pub struct Keep { + offset: Vec2, + cols: Grid, +} + +const KEEP_CELL_STOREY: i32 = 12; + +pub struct KeepCol { + z_offset: i32, + storeys: Vec, +} + +enum KeepCell { + Cube, +} diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index afd2404442..5d600ece84 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -1,3 +1,5 @@ +mod keep; + use super::SpawnRules; use crate::{ block::block_from_structure, @@ -52,6 +54,11 @@ pub struct Castle { keeps: Vec, rounded_towers: bool, ridged: bool, + flags: bool, + + evil: bool, + + keep: Option, } pub struct GenCtx<'a, R: Rng> { @@ -70,7 +77,7 @@ impl Castle { let radius = 150; - let this = Self { + let mut this = Self { origin: wpos, alt: ctx .sim @@ -116,6 +123,8 @@ impl Castle { .collect(), rounded_towers: ctx.rng.gen(), ridged: ctx.rng.gen(), + flags: ctx.rng.gen(), + evil: ctx.rng.gen(), keeps: (0..keep_count) .map(|i| { let angle = (i as f32 / keep_count as f32) * f32::consts::PI * 2.0; @@ -141,6 +150,8 @@ impl Castle { } }) .collect(), + + keep: None, }; this @@ -283,7 +294,16 @@ impl Castle { let wall_alt = wall_alt + (wall_sample.alt as i32 - wall_alt - 10).max(0); let keep_archetype = KeepArchetype { - flag_color: Rgb::new(200, 80, 40), + flag_color: if self.evil { + Rgb::new(80, 10, 130) + } else { + Rgb::new(200, 80, 40) + }, + stone_color: if self.evil { + Rgb::new(65, 60, 55) + } else { + Rgb::new(100, 100, 110) + }, }; for z in -10..64 { @@ -307,6 +327,7 @@ impl Castle { &Attr { storeys: 2, is_tower: false, + flag: self.flags, ridged: false, rounded: true, has_doors: false, @@ -347,6 +368,7 @@ impl Castle { &Attr { storeys: 3, is_tower: true, + flag: self.flags, ridged: self.ridged, rounded: self.rounded_towers, has_doors: false, @@ -387,6 +409,7 @@ impl Castle { &Attr { storeys: keep.storeys, is_tower: keep.is_tower, + flag: self.flags, ridged: self.ridged, rounded: self.rounded_towers, has_doors: true, diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 476ab57f8d..a77df1c6ce 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -12,11 +12,13 @@ use vek::*; pub struct Keep { pub flag_color: Rgb, + pub stone_color: Rgb, } pub struct Attr { pub storeys: i32, pub is_tower: bool, + pub flag: bool, pub ridged: bool, pub rounded: bool, pub has_doors: bool, @@ -36,6 +38,7 @@ impl Archetype for Keep { attr: Attr { storeys, is_tower: false, + flag: false, ridged: false, rounded: true, has_doors: true, @@ -51,6 +54,7 @@ impl Archetype for Keep { attr: Attr { storeys: storeys + rng.gen_range(1, 3), is_tower: true, + flag: true, ridged: false, rounded: true, has_doors: false, @@ -68,6 +72,7 @@ impl Archetype for Keep { ( Self { flag_color: Rgb::new(200, 80, 40), + stone_color: Rgb::new(100, 100, 110), }, skel, ) @@ -114,7 +119,11 @@ impl Archetype for Keep { let brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1); let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24; let foundation = make_block(80 + brick_tex, 80 + brick_tex, 80 + brick_tex); - let wall = make_block(100 + brick_tex, 100 + brick_tex, 110 + brick_tex); + let wall = make_block( + self.stone_color.r + brick_tex, + self.stone_color.g + brick_tex, + self.stone_color.b + brick_tex, + ); let window = BlockMask::new( Block::new(BlockKind::Window1, make_meta(ori.flip())), normal_layer, @@ -199,9 +208,9 @@ impl Archetype for Keep { if profile.y > roof_height && (min_dist < rampart_width - 1 || (attr.is_tower && min_dist < rampart_width)) { - if attr.is_tower && center_offset == Vec2::zero() && profile.y < roof_height + 16 { + if attr.is_tower && attr.flag && center_offset == Vec2::zero() && profile.y < roof_height + 16 { pole - } else if attr.is_tower + } else if attr.is_tower && attr.flag && center_offset.x == 0 && center_offset.y > 0 && center_offset.y < 8 diff --git a/world/src/util/wgrid.rs b/world/src/util/wgrid.rs new file mode 100644 index 0000000000..92b71f6dad --- /dev/null +++ b/world/src/util/wgrid.rs @@ -0,0 +1,26 @@ +use super::Grid; +use vek::*; + +pub struct WGrid { + cell_size: u32, + grid: Grid, +} + +impl WGrid { + pub fn new(radius: u32, cell_size: u32, default_cell: T) -> Self + where T: Clone + { + Self { + cell_size, + grid: Grid::new(Vec2::broadcast(radius as i32 * 2 + 1), default_cell), + } + } + + fn offset(&self) -> Vec2 { + self.grid.size() / 2 + } + + pub fn get_local(&self, pos: Vec2) -> Option<&T> { + self.grid.get(pos + self.offset()) + } +} From 6633298722f412b5abcc9e005698009a9530e0fc Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 9 Aug 2020 17:34:48 +0100 Subject: [PATCH 26/33] Added terrain scatter densities --- voxygen/src/scene/terrain.rs | 42 ++++++++++++---------- world/src/block/mod.rs | 2 ++ world/src/index.rs | 4 ++- world/src/layer/mod.rs | 67 +++++++++++++++++++++++++++++++++++- world/src/lib.rs | 1 + 5 files changed, 96 insertions(+), 20 deletions(-) diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 6fa942cda8..5f18cd4880 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -20,6 +20,7 @@ use dot_vox::DotVoxData; use hashbrown::HashMap; use std::{f32, fmt::Debug, i32, marker::PhantomData, time::Duration}; use treeculler::{BVol, Frustum, AABB}; +use tracing::warn; use vek::*; struct TerrainChunkData { @@ -142,7 +143,7 @@ fn sprite_config_for(kind: BlockKind) -> Option { wind_sway: 0.1, }), BlockKind::LargeGrass => Some(SpriteConfig { - variations: 3, + variations: 1, wind_sway: 0.5, }), @@ -3141,23 +3142,28 @@ impl Terrain { let dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center); if dist_sqrd < sprite_render_distance.powf(2.0) { for (kind, instances) in &chunk.sprite_instances { - renderer.render_sprites( - if dist_sqrd < sprite_high_detail_distance.powf(2.0) { - &self.sprite_models[&kind][0] - } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { - &self.sprite_models[&kind][1] - } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { - &self.sprite_models[&kind][2] - } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { - &self.sprite_models[&kind][3] - } else { - &self.sprite_models[&kind][4] - }, - globals, - &instances, - lights, - shadows, - ); + + if let Some(models) = self.sprite_models.get(&kind) { + renderer.render_sprites( + if dist_sqrd < sprite_high_detail_distance.powf(2.0) { + &self.sprite_models[&kind][0] + } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { + &self.sprite_models[&kind][1] + } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { + &self.sprite_models[&kind][2] + } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { + &self.sprite_models[&kind][3] + } else { + &self.sprite_models[&kind][4] + }, + globals, + &instances, + lights, + shadows, + ); + } else { + warn!("Sprite model for {:?} does not exists", kind); + } } } } diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 323450661f..152c4a5bba 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -283,6 +283,7 @@ impl<'a> BlockGen<'a> { && marble_small > 0.55 && (marble * 3173.7).fract() < 0.6 && humidity > CONFIG.desert_hum + && false { let treasures = [BlockKind::Chest, BlockKind::Velorite]; @@ -322,6 +323,7 @@ impl<'a> BlockGen<'a> { } else if (wposf.z as f32) < height + 0.9 && temp > CONFIG.desert_temp && (marble * 4423.5).fract() < 0.0005 + && false { let large_cacti = [ BlockKind::LargeCactus, diff --git a/world/src/index.rs b/world/src/index.rs index b3a5ddb88e..1c05427d5c 100644 --- a/world/src/index.rs +++ b/world/src/index.rs @@ -22,12 +22,14 @@ impl Index { pub struct Noise { pub cave_nz: SuperSimplex, + pub scatter_nz: SuperSimplex, } impl Noise { fn new(seed: u32) -> Self { Self { - cave_nz: SuperSimplex::new().set_seed(seed), + cave_nz: SuperSimplex::new().set_seed(seed + 0), + scatter_nz: SuperSimplex::new().set_seed(seed + 1), } } } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 100ff61f1e..7cd5ceb449 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -1,6 +1,7 @@ use crate::{ column::ColumnSample, util::{RandomField, Sampler}, + sim::SimChunk, Index, }; use common::{ @@ -19,6 +20,70 @@ use std::{ use vek::*; use rand::prelude::*; +fn close(x: f32, tgt: f32, falloff: f32) -> f32 { (1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.5) } + +pub fn apply_scatter_to<'a>( + wpos2d: Vec2, + mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, + vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), + index: &Index, + chunk: &SimChunk, +) { + use BlockKind::*; + let scatter: &[(_, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[ + // (density, Option<(wavelen, threshold)>) + (BlueFlower, |c| (close(c.temp, -0.3, 0.7).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.6)))), + (PinkFlower, |c| (close(c.temp, 0.15, 0.5).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.6)))), + (DeadBush, |c| (close(c.temp, 0.8, 0.3).min(close(c.humidity, 0.0, 0.4)) * 0.015, None)), + (Twigs, |c| ((c.tree_density - 0.5).max(0.0) * 0.0025, None)), + (Stones, |c| ((c.rockiness - 0.5).max(0.0) * 0.005, None)), + ]; + + for y in 0..vol.size_xy().y as i32 { + for x in 0..vol.size_xy().x as i32 { + let offs = Vec2::new(x, y); + + let wpos2d = wpos2d + offs; + + // Sample terrain + let col_sample = if let Some(col_sample) = get_column(offs) { + col_sample + } else { + continue; + }; + + let bk = scatter + .iter() + .enumerate() + .find_map(|(i, (bk, f))| { + let (density, patch) = f(chunk); + if density <= 0.0 || patch.map(|(wavelen, threshold)| index + .noise + .scatter_nz + .get(wpos2d.map(|e| e as f64 / wavelen as f64 + i as f64 * 43.0).into_array()) < threshold as f64) + .unwrap_or(false) + || !RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) + { + None + } else { + Some(*bk) + } + }); + + if let Some(bk) = bk { + let mut z = col_sample.alt as i32 - 4; + for _ in 0..8 { + if vol.get(Vec3::new(offs.x, offs.y, z)).map(|b| !b.is_solid()).unwrap_or(true) { + let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::new(bk, Rgb::broadcast(0))); + break; + } + z += 1; + } + } + } + } +} + pub fn apply_paths_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, @@ -227,7 +292,7 @@ pub fn apply_caves_supplement<'a>( let cave_roof = (cave.alt + cave_height) as i32; // Scatter things in caves - if RandomField::new(index.seed).chance(wpos2d.into(), 0.0001) + if RandomField::new(index.seed).chance(wpos2d.into(), 0.00005) && cave_base < surface_z as i32 - 40 { let entity = EntityInfo::at(Vec3::new(wpos2d.x as f32, wpos2d.y as f32, cave_base as f32)) diff --git a/world/src/lib.rs b/world/src/lib.rs index 9f08d58570..9503afd10b 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -176,6 +176,7 @@ impl World { let mut rng = rand::thread_rng(); // Apply layers (paths, caves, etc.) + layer::apply_scatter_to(chunk_wpos2d, sample_get, &mut chunk, &self.index, sim_chunk); layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk, &self.index); layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, &self.index); From c1b07465cf6cbdfa66e6b9acc784ae3192582191 Mon Sep 17 00:00:00 2001 From: Monty Marz Date: Mon, 10 Aug 2020 09:57:01 +0200 Subject: [PATCH 27/33] added grass sprites --- .../voxel/sprite/grass/grass_snow_0.vox | 3 + .../voxel/sprite/grass/grass_snow_1.vox | 3 + .../voxel/sprite/grass/grass_snow_2.vox | 3 + .../voxel/sprite/grass/grass_snow_3.vox | 3 + .../voxel/sprite/grass/grass_snow_4.vox | 3 + .../voxel/sprite/grass/grass_snow_5.vox | 3 + .../voxel/sprite/grass/grass_snow_6.vox | 3 + .../voxel/sprite/grass/grass_snow_7.vox | 3 + .../voxel/sprite/grass/grass_snow_8.vox | 3 + .../voxel/sprite/grass/grass_snow_9.vox | 3 + common/src/terrain/block.rs | 9 +- voxygen/src/scene/terrain.rs | 88 +++++++- world/src/layer/mod.rs | 209 +++++++++++------- 13 files changed, 253 insertions(+), 83 deletions(-) create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_0.vox create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_1.vox create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_2.vox create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_3.vox create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_4.vox create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_5.vox create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_6.vox create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_7.vox create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_8.vox create mode 100644 assets/voxygen/voxel/sprite/grass/grass_snow_9.vox diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_0.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_0.vox new file mode 100644 index 0000000000..f91d3f6f24 --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:45ee10e5805fa443292b063410caa5f5a7b7b6a935731bc9079c4a4fa2693ce5 +size 55651 diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_1.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_1.vox new file mode 100644 index 0000000000..3d2697d58d --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89470bacf9a12f6f7473f7b61d4d0a8827d320f1b52d44d26fc199a554c7d53a +size 55663 diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_2.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_2.vox new file mode 100644 index 0000000000..c501954b89 --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c89055c2cd7f71975d0ddbfccbc3d5b96b15a158ba3f9f1000f36a4b7217ec3 +size 55663 diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_3.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_3.vox new file mode 100644 index 0000000000..b1fe51ccbd --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acc14c82052df18f96d002ad401722ad53ce4decdb36ad02045346638be2003b +size 55643 diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_4.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_4.vox new file mode 100644 index 0000000000..25881db9ff --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c1cbbd8090fa8e378055ebb191c8d528ab7e70215a3a171246cfa6332eb143c +size 55679 diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_5.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_5.vox new file mode 100644 index 0000000000..20fd053c82 --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9bda5fd9430404feeb8b042e864528f9e9572bc00fb5d454bf546aadac509b5 +size 55767 diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_6.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_6.vox new file mode 100644 index 0000000000..57ef237bce --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_6.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f285aebc92333f7d3a0970191ee398d22c850372504b9d38061cd8e07f566171 +size 55823 diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_7.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_7.vox new file mode 100644 index 0000000000..1d6bab161a --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_7.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25b79468d0fb6080f41d98cc8db0e8a69aa5eb8fa2e74bdb31a1fa183d9a483b +size 55787 diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_8.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_8.vox new file mode 100644 index 0000000000..ad5661ceb8 --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_8.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:803febeaed019bc690af3906a1cbcbaf8409388d265a23ff41d62f074b167ea8 +size 55791 diff --git a/assets/voxygen/voxel/sprite/grass/grass_snow_9.vox b/assets/voxygen/voxel/sprite/grass/grass_snow_9.vox new file mode 100644 index 0000000000..451d4252ea --- /dev/null +++ b/assets/voxygen/voxel/sprite/grass/grass_snow_9.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ffb358971c47cfd6819ea7971117c051dfde09feab2cb59ddc079a3fe6f236b +size 55787 diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 39e940448b..ad2c551ffc 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -90,6 +90,7 @@ pub enum BlockKind { ShinyGem, DropGate, DropGateBottom, + GrassSnow, } impl fmt::Display for BlockKind { @@ -195,6 +196,7 @@ impl BlockKind { BlockKind::ShinyGem => true, BlockKind::DropGate => false, BlockKind::DropGateBottom => false, + BlockKind::GrassSnow => true, _ => false, } } @@ -294,6 +296,7 @@ impl BlockKind { BlockKind::ShinyGem => false, BlockKind::DropGate => false, BlockKind::DropGateBottom => false, + BlockKind::GrassSnow => false, _ => true, } } @@ -370,13 +373,14 @@ impl BlockKind { BlockKind::ShinyGem => false, BlockKind::DropGate => true, BlockKind::DropGateBottom => false, + BlockKind::GrassSnow => false, _ => true, } } pub fn is_explodable(&self) -> bool { match self { - BlockKind::Leaves | BlockKind::Grass | BlockKind::Rock => true, + BlockKind::Leaves | BlockKind::Grass | BlockKind::Rock | BlockKind::GrassSnow => true, _ => false, } } @@ -494,10 +498,9 @@ impl Block { | BlockKind::WardrobeSingle | BlockKind::WardrobeDouble | BlockKind::Pot - | BlockKind::DropGate + | BlockKind::DropGate | BlockKind::DropGateBottom | BlockKind::Door => Some(self.color[0] & 0b111), - _ => None, } } diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 5f18cd4880..52438575bd 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -19,8 +19,8 @@ use crossbeam::channel; use dot_vox::DotVoxData; use hashbrown::HashMap; use std::{f32, fmt::Debug, i32, marker::PhantomData, time::Duration}; -use treeculler::{BVol, Frustum, AABB}; use tracing::warn; +use treeculler::{BVol, Frustum, AABB}; use vek::*; struct TerrainChunkData { @@ -359,6 +359,10 @@ fn sprite_config_for(kind: BlockKind) -> Option { variations: 1, wind_sway: 0.0, }), + BlockKind::GrassSnow => Some(SpriteConfig { + variations: 10, + wind_sway: 0.2, + }), _ => None, } } @@ -2759,6 +2763,87 @@ impl Terrain { Vec3::one(), ), ), + // Snow covered Grass + ( + (BlockKind::GrassSnow, 0), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_0", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::GrassSnow, 1), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_1", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::GrassSnow, 2), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_2", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::GrassSnow, 3), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_3", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::GrassSnow, 4), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_4", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::GrassSnow, 5), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_5", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::GrassSnow, 6), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_6", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::GrassSnow, 7), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_7", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::GrassSnow, 8), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_8", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), + ( + (BlockKind::GrassSnow, 9), + make_models( + "voxygen.voxel.sprite.grass.grass_snow_9", + Vec3::new(-2.5, -2.5, 0.0), + Vec3::one(), + ), + ), ] .into_iter() .collect(), @@ -3142,7 +3227,6 @@ impl Terrain { let dist_sqrd = Vec2::from(focus_pos).distance_squared(chunk_center); if dist_sqrd < sprite_render_distance.powf(2.0) { for (kind, instances) in &chunk.sprite_instances { - if let Some(models) = self.sprite_models.get(&kind) { renderer.render_sprites( if dist_sqrd < sprite_high_detail_distance.powf(2.0) { diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 7cd5ceb449..6d989cb4f6 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -1,26 +1,27 @@ use crate::{ column::ColumnSample, - util::{RandomField, Sampler}, sim::SimChunk, + util::{RandomField, Sampler}, Index, }; use common::{ - assets, - comp, + assets, comp, + generation::{ChunkSupplement, EntityInfo}, lottery::Lottery, terrain::{Block, BlockKind}, vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol}, - generation::{ChunkSupplement, EntityInfo}, }; use noise::NoiseFn; +use rand::prelude::*; use std::{ f32, ops::{Mul, Sub}, }; use vek::*; -use rand::prelude::*; -fn close(x: f32, tgt: f32, falloff: f32) -> f32 { (1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.5) } +fn close(x: f32, tgt: f32, falloff: f32) -> f32 { + (1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.5) +} pub fn apply_scatter_to<'a>( wpos2d: Vec2, @@ -32,11 +33,50 @@ pub fn apply_scatter_to<'a>( use BlockKind::*; let scatter: &[(_, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[ // (density, Option<(wavelen, threshold)>) - (BlueFlower, |c| (close(c.temp, -0.3, 0.7).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.6)))), - (PinkFlower, |c| (close(c.temp, 0.15, 0.5).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.6)))), - (DeadBush, |c| (close(c.temp, 0.8, 0.3).min(close(c.humidity, 0.0, 0.4)) * 0.015, None)), + (BlueFlower, |c| { + ( + close(c.temp, -0.3, 0.7).min(close(c.humidity, 0.6, 0.35)) * 0.05, + Some((48.0, 0.6)), + ) + }), + (PinkFlower, |c| { + ( + close(c.temp, 0.15, 0.5).min(close(c.humidity, 0.6, 0.35)) * 0.05, + Some((48.0, 0.6)), + ) + }), + (DeadBush, |c| { + ( + close(c.temp, 0.8, 0.3).min(close(c.humidity, 0.0, 0.4)) * 0.015, + None, + ) + }), (Twigs, |c| ((c.tree_density - 0.5).max(0.0) * 0.0025, None)), (Stones, |c| ((c.rockiness - 0.5).max(0.0) * 0.005, None)), + (ShortGrass, |c| { + ( + close(c.temp, 0.3, 0.4).min(close(c.humidity, 0.6, 0.35)) * 0.05, + Some((48.0, 0.4)), + ) + }), + (MediumGrass, |c| { + ( + close(c.temp, 0.0, 0.6).min(close(c.humidity, 0.6, 0.35)) * 0.05, + Some((48.0, 0.2)), + ) + }), + (LongGrass, |c| { + ( + close(c.temp, 0.4, 0.4).min(close(c.humidity, 0.8, 0.2)) * 0.05, + Some((48.0, 0.1)), + ) + }), + (GrassSnow, |c| { + ( + close(c.temp, -0.4, 0.4).min(close(c.rockiness, 0.0, 0.5)), + Some((48.0, 0.6)), + ) + }), ]; for y in 0..vol.size_xy().y as i32 { @@ -52,29 +92,38 @@ pub fn apply_scatter_to<'a>( continue; }; - let bk = scatter - .iter() - .enumerate() - .find_map(|(i, (bk, f))| { - let (density, patch) = f(chunk); - if density <= 0.0 || patch.map(|(wavelen, threshold)| index - .noise - .scatter_nz - .get(wpos2d.map(|e| e as f64 / wavelen as f64 + i as f64 * 43.0).into_array()) < threshold as f64) + let bk = scatter.iter().enumerate().find_map(|(i, (bk, f))| { + let (density, patch) = f(chunk); + if density <= 0.0 + || patch + .map(|(wavelen, threshold)| { + index.noise.scatter_nz.get( + wpos2d + .map(|e| e as f64 / wavelen as f64 + i as f64 * 43.0) + .into_array(), + ) < threshold as f64 + }) .unwrap_or(false) - || !RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) - { - None - } else { - Some(*bk) - } - }); + || !RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) + { + None + } else { + Some(*bk) + } + }); if let Some(bk) = bk { let mut z = col_sample.alt as i32 - 4; for _ in 0..8 { - if vol.get(Vec3::new(offs.x, offs.y, z)).map(|b| !b.is_solid()).unwrap_or(true) { - let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::new(bk, Rgb::broadcast(0))); + if vol + .get(Vec3::new(offs.x, offs.y, z)) + .map(|b| !b.is_solid()) + .unwrap_or(true) + { + let _ = vol.set( + Vec3::new(offs.x, offs.y, z), + Block::new(bk, Rgb::broadcast(0)), + ); break; } z += 1; @@ -295,57 +344,61 @@ pub fn apply_caves_supplement<'a>( if RandomField::new(index.seed).chance(wpos2d.into(), 0.00005) && cave_base < surface_z as i32 - 40 { - let entity = EntityInfo::at(Vec3::new(wpos2d.x as f32, wpos2d.y as f32, cave_base as f32)) - .with_alignment(comp::Alignment::Enemy) - .with_body(match rng.gen_range(0, 6) { - 0 => { - let species = match rng.gen_range(0, 2) { - 0 => comp::quadruped_small::Species::Truffler, - _ => comp::quadruped_small::Species::Hyena, - }; - comp::quadruped_small::Body::random_with(rng, &species).into() - }, - 1 => { - let species = match rng.gen_range(0, 3) { - 0 => comp::quadruped_medium::Species::Tarasque, - 1 => comp::quadruped_medium::Species::Frostfang, - _ => comp::quadruped_medium::Species::Bonerattler, - }; - comp::quadruped_medium::Body::random_with(rng, &species).into() - }, - 2 => { - let species = match rng.gen_range(0, 3) { - 0 => comp::quadruped_low::Species::Maneater, - 1 => comp::quadruped_low::Species::Rocksnapper, - _ => comp::quadruped_low::Species::Salamander, - }; - comp::quadruped_low::Body::random_with(rng, &species).into() - }, - 3 => { - let species = match rng.gen_range(0, 3) { - 0 => comp::critter::Species::Fungome, - 1 => comp::critter::Species::Axolotl, - _ => comp::critter::Species::Rat, - }; - comp::critter::Body::random_with(rng, &species).into() - }, - 4 => { - let species = match rng.gen_range(0, 1) { - _ => comp::golem::Species::StoneGolem, - }; - comp::golem::Body::random_with(rng, &species).into() - }, - _ => { - let species = match rng.gen_range(0, 4) { - 0 => comp::biped_large::Species::Ogre, - 1 => comp::biped_large::Species::Cyclops, - 2 => comp::biped_large::Species::Wendigo, - _ => comp::biped_large::Species::Troll, - }; - comp::biped_large::Body::random_with(rng, &species).into() - }, - }) - .with_automatic_name(); + let entity = EntityInfo::at(Vec3::new( + wpos2d.x as f32, + wpos2d.y as f32, + cave_base as f32, + )) + .with_alignment(comp::Alignment::Enemy) + .with_body(match rng.gen_range(0, 6) { + 0 => { + let species = match rng.gen_range(0, 2) { + 0 => comp::quadruped_small::Species::Truffler, + _ => comp::quadruped_small::Species::Hyena, + }; + comp::quadruped_small::Body::random_with(rng, &species).into() + }, + 1 => { + let species = match rng.gen_range(0, 3) { + 0 => comp::quadruped_medium::Species::Tarasque, + 1 => comp::quadruped_medium::Species::Frostfang, + _ => comp::quadruped_medium::Species::Bonerattler, + }; + comp::quadruped_medium::Body::random_with(rng, &species).into() + }, + 2 => { + let species = match rng.gen_range(0, 3) { + 0 => comp::quadruped_low::Species::Maneater, + 1 => comp::quadruped_low::Species::Rocksnapper, + _ => comp::quadruped_low::Species::Salamander, + }; + comp::quadruped_low::Body::random_with(rng, &species).into() + }, + 3 => { + let species = match rng.gen_range(0, 3) { + 0 => comp::critter::Species::Fungome, + 1 => comp::critter::Species::Axolotl, + _ => comp::critter::Species::Rat, + }; + comp::critter::Body::random_with(rng, &species).into() + }, + 4 => { + let species = match rng.gen_range(0, 1) { + _ => comp::golem::Species::StoneGolem, + }; + comp::golem::Body::random_with(rng, &species).into() + }, + _ => { + let species = match rng.gen_range(0, 4) { + 0 => comp::biped_large::Species::Ogre, + 1 => comp::biped_large::Species::Cyclops, + 2 => comp::biped_large::Species::Wendigo, + _ => comp::biped_large::Species::Troll, + }; + comp::biped_large::Body::random_with(rng, &species).into() + }, + }) + .with_automatic_name(); supplement.add_entity(entity); } From 6992194ad4621014ec6910a52c14322a0092adb7 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 11 Aug 2020 12:13:18 +0100 Subject: [PATCH 28/33] Better surface swimming, no underwater sprites --- client/src/lib.rs | 8 +-- common/src/comp/agent.rs | 1 - common/src/comp/phys.rs | 2 +- common/src/state.rs | 4 +- common/src/states/glide.rs | 2 +- common/src/states/glide_wield.rs | 2 +- common/src/states/utils.rs | 14 ++--- common/src/sys/agent.rs | 9 ++- common/src/sys/phys.rs | 70 +++++++++++++++--------- server/src/cmd.rs | 24 ++++---- server/src/events/entity_manipulation.rs | 2 +- server/src/events/inventory_manip.rs | 14 ++--- server/src/events/player.rs | 2 +- server/src/state_ext.rs | 4 +- voxygen/src/hud/social.rs | 4 +- voxygen/src/scene/figure/mod.rs | 26 ++++----- world/src/layer/mod.rs | 32 +++++------ 17 files changed, 119 insertions(+), 101 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 0901a8fd27..e74be1610e 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -396,7 +396,7 @@ impl Client { } pub fn pick_up(&mut self, entity: EcsEntity) { - if let Some(uid) = self.state.read_component_copied(entity) { + if let Some(uid) = self.state.read_component_cloned(entity) { self.singleton_stream .send(ClientMsg::ControlEvent(ControlEvent::InventoryManip( InventoryManip::Pickup(uid), @@ -520,7 +520,7 @@ impl Client { } pub fn mount(&mut self, entity: EcsEntity) { - if let Some(uid) = self.state.read_component_copied(entity) { + if let Some(uid) = self.state.read_component_cloned(entity) { self.singleton_stream .send(ClientMsg::ControlEvent(ControlEvent::Mount(uid))) .unwrap(); @@ -1294,7 +1294,7 @@ impl Client { pub fn entity(&self) -> EcsEntity { self.entity } /// Get the player's Uid. - pub fn uid(&self) -> Option { self.state.read_component_copied(self.entity) } + pub fn uid(&self) -> Option { self.state.read_component_cloned(self.entity) } /// Get the client state pub fn get_client_state(&self) -> ClientState { self.client_state } @@ -1347,7 +1347,7 @@ impl Client { pub fn is_admin(&self) -> bool { let client_uid = self .state - .read_component_copied::(self.entity) + .read_component_cloned::(self.entity) .expect("Client doesn't have a Uid!!!"); self.player_list diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index a776eb2890..59a27fe6b0 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,5 +1,4 @@ use crate::{path::Chaser, sync::Uid, comp::Body}; -use serde::{Deserialize, Serialize}; use specs::{Component, Entity as EcsEntity}; use specs_idvs::IdvStorage; use vek::*; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index 466734cb4c..2035666375 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -80,7 +80,7 @@ pub struct PhysicsState { pub on_ceiling: bool, pub on_wall: Option>, pub touch_entity: Option, - pub in_fluid: bool, + pub in_fluid: Option, // Depth } impl PhysicsState { diff --git a/common/src/state.rs b/common/src/state.rs index 3d003b7a4f..d53e91f964 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -199,8 +199,8 @@ impl State { } /// Read a component attributed to a particular entity. - pub fn read_component_copied(&self, entity: EcsEntity) -> Option { - self.ecs.read_storage().get(entity).copied() + pub fn read_component_cloned(&self, entity: EcsEntity) -> Option { + self.ecs.read_storage().get(entity).cloned() } /// Get a read-only reference to the storage of a particular component type. diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 93449a2ad3..e45b56e88e 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -24,7 +24,7 @@ impl CharacterBehavior for Data { update.character = CharacterState::GlideWield; return update; } - if data.physics.in_fluid { + if data.physics.in_fluid.map(|depth| depth > 0.5).unwrap_or(false) { update.character = CharacterState::Idle; } // If there is a wall in front of character and they are trying to climb go to diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index 0cc4d826e0..548e076155 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -19,7 +19,7 @@ impl CharacterBehavior for Data { if !data.physics.on_ground { update.character = CharacterState::Glide; } - if data.physics.in_fluid { + if data.physics.in_fluid.map(|depth| depth > 0.5).unwrap_or(false) { update.character = CharacterState::Idle; } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 7a2b4f19a1..10730d75ef 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -8,7 +8,7 @@ use crate::{ sys::{character_behavior::JoinData, phys::GRAVITY}, util::Dir, }; -use vek::vec::Vec2; +use vek::*; pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; const BASE_HUMANOID_AIR_ACCEL: f32 = 8.0; @@ -67,8 +67,8 @@ impl Body { /// Handles updating `Components` to move player based on state of `JoinData` pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { - if data.physics.in_fluid { - swim_move(data, update, efficiency); + if let Some(depth) = data.physics.in_fluid { + swim_move(data, update, efficiency, depth); } else { basic_move(data, update, efficiency); } @@ -104,7 +104,7 @@ pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, rate: f32) } /// Updates components to move player as if theyre swimming -fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { +fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, depth: f32) { // Update velocity update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir @@ -120,7 +120,7 @@ fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { // Swim if data.inputs.swimup.is_pressed() { update.vel.0.z = - (update.vel.0.z + data.dt.0 * GRAVITY * 4.0).min(BASE_HUMANOID_WATER_SPEED); + (update.vel.0.z + data.dt.0 * GRAVITY * 4.0 * depth.clamped(0.0, 1.0).powf(3.0)).min(BASE_HUMANOID_WATER_SPEED); } // Swim if data.inputs.swimdown.is_pressed() { @@ -192,14 +192,14 @@ pub fn attempt_swap_loadout(data: &JoinData, update: &mut StateUpdate) { /// Checks that player can wield the glider and updates `CharacterState` if so pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) { - if data.physics.on_ground && !data.physics.in_fluid && data.body.is_humanoid() { + if data.physics.on_ground && !data.physics.in_fluid.map(|depth| depth > 1.0).unwrap_or(false) && data.body.is_humanoid() { update.character = CharacterState::GlideWield; } } /// Checks that player can jump and sends jump event if so pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) { - if data.inputs.jump.is_pressed() && data.physics.on_ground && !data.physics.in_fluid { + if data.inputs.jump.is_pressed() && data.physics.on_ground && !data.physics.in_fluid.map(|depth| depth > 1.0).unwrap_or(false) { update .local_events .push_front(LocalEvent::Jump(data.entity)); diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 5c49227a6e..679bddb36b 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -241,7 +241,8 @@ impl<'a> System<'a> for Sys { bearing.xy().try_normalized().unwrap_or(Vec2::zero()) * speed.min(0.2 + (dist - AVG_FOLLOW_DIST) / 8.0); inputs.jump.set_state(bearing.z > 1.5); - inputs.swim.set_state(bearing.z > 0.5); + inputs.swimup.set_state(bearing.z > 0.5); + inputs.swimdown.set_state(bearing.z < 0.5); } } else { do_idle = true; @@ -331,7 +332,8 @@ impl<'a> System<'a> for Sys { .unwrap_or(Vec2::zero()) * speed; inputs.jump.set_state(bearing.z > 1.5); - inputs.swim.set_state(bearing.z > 0.5); + inputs.swimup.set_state(bearing.z > 0.5); + inputs.swimdown.set_state(bearing.z < 0.5); } } else { do_idle = true; @@ -399,7 +401,8 @@ impl<'a> System<'a> for Sys { .unwrap_or(Vec2::zero()) * speed; inputs.jump.set_state(bearing.z > 1.5); - inputs.swim.set_state(bearing.z > 0.5); + inputs.swimup.set_state(bearing.z > 0.5); + inputs.swimdown.set_state(bearing.z < 0.5); } if dist_sqrd < 16.0f32.powf(2.0) diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index b3cec5dca2..cf0caf31bb 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -13,6 +13,7 @@ use specs::{ saveload::MarkerAllocator, Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage, }; use vek::*; +use std::ops::Range; pub const GRAVITY: f32 = 9.81 * 5.0; const BOUYANCY: f32 = 1.0; @@ -123,7 +124,7 @@ impl<'a> System<'a> for Sys { } else { 0.0 }) - .max(if physics_state.in_fluid { + .max(if physics_state.in_fluid.is_some() { FRIC_FLUID } else { 0.0 @@ -133,7 +134,7 @@ impl<'a> System<'a> for Sys { .is_some(); let downward_force = if !in_loaded_chunk { 0.0 // No gravity in unloaded chunks - } else if physics_state.in_fluid { + } else if physics_state.in_fluid.map(|depth| depth > 0.75).unwrap_or(false) { (1.0 - BOUYANCY) * GRAVITY } else { GRAVITY @@ -175,31 +176,47 @@ impl<'a> System<'a> for Sys { .flatten() .flatten(); - // Function for determining whether the player at a specific position collides - // with the ground - let collision_with = |pos: Vec3, - hit: &dyn Fn(&Block) -> bool, - near_iter| { - for (i, j, k) in near_iter { - let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); + // Function for iterating over the blocks the player at a specific position collides + // with + fn collision_iter<'a>( + pos: Vec3, + terrain: &'a TerrainGrid, + hit: &'a dyn Fn(&Block) -> bool, + near_iter: impl Iterator + 'a, + radius: f32, + z_range: Range, + ) -> impl Iterator> + 'a { + near_iter + .filter_map(move |(i, j, k)| { + let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); - if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) { - let player_aabb = Aabb { - min: pos + Vec3::new(-radius, -radius, z_min), - max: pos + Vec3::new(radius, radius, z_max), - }; - let block_aabb = Aabb { - min: block_pos.map(|e| e as f32), - max: block_pos.map(|e| e as f32) - + Vec3::new(1.0, 1.0, block.get_height()), - }; + if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) { + let player_aabb = Aabb { + min: pos + Vec3::new(-radius, -radius, z_range.start), + max: pos + Vec3::new(radius, radius, z_range.end), + }; + let block_aabb = Aabb { + min: block_pos.map(|e| e as f32), + max: block_pos.map(|e| e as f32) + + Vec3::new(1.0, 1.0, block.get_height()), + }; - if player_aabb.collides_with_aabb(block_aabb) { - return true; + if player_aabb.collides_with_aabb(block_aabb) { + return Some(block_aabb); + } } - } - } - false + + None + }) + }; + + // Function for determining whether the player at a specific position collides + // with blocks with the given criteria + let collision_with = |pos: Vec3, + hit: &dyn Fn(&Block) -> bool, + near_iter| + { + collision_iter(pos, &terrain, hit, near_iter, radius, z_min..z_max).count() > 0 }; let was_on_ground = physics_state.on_ground; @@ -400,8 +417,9 @@ impl<'a> System<'a> for Sys { } // Figure out if we're in water - physics_state.in_fluid = - collision_with(pos.0, &|block| block.is_fluid(), near_iter.clone()); + physics_state.in_fluid = collision_iter(pos.0, &terrain, &|block| block.is_fluid(), near_iter.clone(), radius, z_min..z_max) + .max_by_key(|block_aabb| (block_aabb.max.z * 100.0) as i32) + .map(|block_aabb| block_aabb.max.z - pos.0.z); }, Collider::Point => { let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta).ignore_error().cast(); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index a97b687c46..9abd4f49a4 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -262,7 +262,7 @@ fn handle_jump( action: &ChatCommand, ) { if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) { - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(current_pos) => { server .state @@ -287,7 +287,7 @@ fn handle_goto( if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) { if server .state - .read_component_copied::(target) + .read_component_cloned::(target) .is_some() { server @@ -498,9 +498,9 @@ fn handle_tp( ); return; }; - if let Some(_pos) = server.state.read_component_copied::(target) { + if let Some(_pos) = server.state.read_component_cloned::(target) { if let Some(player) = opt_player { - if let Some(pos) = server.state.read_component_copied::(player) { + if let Some(pos) = server.state.read_component_cloned::(player) { server.state.write_component(target, pos); server.state.write_component(target, comp::ForceUpdate); } else { @@ -545,7 +545,7 @@ fn handle_spawn( (Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount, opt_ai) => { let uid = server .state - .read_component_copied(target) + .read_component_cloned(target) .expect("Expected player to have a UID"); if let Some(alignment) = parse_alignment(uid, &opt_align) { let amount = opt_amount @@ -556,7 +556,7 @@ fn handle_spawn( let ai = opt_ai.unwrap_or_else(|| "true".to_string()); - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { let agent = if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment { @@ -666,7 +666,7 @@ fn handle_spawn_training_dummy( _args: String, _action: &ChatCommand, ) { - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { let vel = Vec3::new( rand::thread_rng().gen_range(-2.0, 3.0), @@ -707,7 +707,7 @@ fn handle_spawn_campfire( _args: String, _action: &ChatCommand, ) { - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { server .state @@ -1066,7 +1066,7 @@ fn handle_explosion( let ecs = server.state.ecs(); - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { ecs.read_resource::>() .emit_now(ServerEvent::Explosion { @@ -1090,7 +1090,7 @@ fn handle_waypoint( _args: String, _action: &ChatCommand, ) { - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { let time = server.state.ecs().read_resource(); let _ = server @@ -1126,7 +1126,7 @@ fn handle_adminify( Some(player) => { let is_admin = if server .state - .read_component_copied::(player) + .read_component_cloned::(player) .is_some() { ecs.write_storage::().remove(player); @@ -1670,7 +1670,7 @@ fn handle_remove_lights( action: &ChatCommand, ) { let opt_radius = scan_fmt_some!(&args, &action.arg_fmt(), f32); - let opt_player_pos = server.state.read_component_copied::(target); + let opt_player_pos = server.state.read_component_cloned::(target); let mut to_delete = vec![]; match opt_player_pos { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 852b8317bc..310c846308 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -251,7 +251,7 @@ pub fn handle_respawn(server: &Server, entity: EcsEntity) { .is_some() { let respawn_point = state - .read_component_copied::(entity) + .read_component_cloned::(entity) .map(|wp| wp.get_pos()) .unwrap_or(state.ecs().read_resource::().0); diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 549804d6f8..e213d312d9 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -167,10 +167,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv thrown_items.push(( *pos, state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), *kind, )); @@ -185,7 +185,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv state.read_storage::().get(entity) { let uid = state - .read_component_copied(entity) + .read_component_cloned(entity) .expect("Expected player to have a UID"); if ( &state.read_storage::(), @@ -341,7 +341,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv dropped_items.push(( *pos, state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), item, )); @@ -373,10 +373,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv for _ in 0..amount { dropped_items.push(( state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), item.clone(), )); @@ -407,7 +407,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv + Vec3::unit_z() * 15.0 + Vec3::::zero().map(|_| rand::thread_rng().gen::() - 0.5) * 4.0; - let uid = state.read_component_copied::(entity); + let uid = state.read_component_cloned::(entity); let mut new_entity = state .create_object(Default::default(), match kind { diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 2c9a06e54a..439fc613d1 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -20,7 +20,7 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { // Note: If other `ServerEvent`s are referring to this entity they will be // disrupted let maybe_client = state.ecs().write_storage::().remove(entity); - let maybe_uid = state.read_component_copied::(entity); + let maybe_uid = state.read_component_cloned::(entity); let maybe_player = state.ecs().write_storage::().remove(entity); let maybe_group = state .ecs() diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 77b42d53a0..816b41401c 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -173,7 +173,7 @@ impl StateExt for State { self.write_component(entity, comp::CharacterState::default()); self.write_component( entity, - comp::Alignment::Owned(self.read_component_copied(entity).unwrap()), + comp::Alignment::Owned(self.read_component_cloned(entity).unwrap()), ); // Set the character id for the player @@ -213,7 +213,7 @@ impl StateExt for State { // Notify clients of a player list update let client_uid = self - .read_component_copied::(entity) + .read_component_cloned::(entity) .map(|u| u) .expect("Client doesn't have a Uid!!!"); diff --git a/voxygen/src/hud/social.rs b/voxygen/src/hud/social.rs index 78a9ef46c4..77e0a15372 100644 --- a/voxygen/src/hud/social.rs +++ b/voxygen/src/hud/social.rs @@ -505,7 +505,7 @@ impl<'a> Widget for Social<'a> { }) .or_else(|| { self.selected_entity - .and_then(|s| self.client.state().read_component_copied(s.0)) + .and_then(|s| self.client.state().read_component_cloned(s.0)) }) .filter(|selected| { // Prevent inviting entities already in the same group @@ -564,7 +564,7 @@ impl<'a> Widget for Social<'a> { }); } } - } // End of Online Tab + } // End of Online Tab events } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index f825ac585b..aafde9bc41 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -581,7 +581,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::character::StandAnimation::update_skeleton( @@ -823,7 +823,7 @@ impl FigureMgr { ) }, CharacterState::Wielding { .. } => { - if physics.in_fluid { + if physics.in_fluid.is_some() { anim::character::SwimWieldAnimation::update_skeleton( &target_base, (active_tool_kind, second_tool_kind, vel.0.magnitude(), time), @@ -949,7 +949,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => { @@ -1047,7 +1047,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => { @@ -1143,7 +1143,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => { @@ -1237,7 +1237,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton( @@ -1329,7 +1329,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::fish_medium::IdleAnimation::update_skeleton( @@ -1404,7 +1404,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::dragon::IdleAnimation::update_skeleton( @@ -1478,7 +1478,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::critter::IdleAnimation::update_skeleton( @@ -1553,7 +1553,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::bird_small::IdleAnimation::update_skeleton( @@ -1628,7 +1628,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::fish_small::IdleAnimation::update_skeleton( @@ -1703,7 +1703,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::biped_large::IdleAnimation::update_skeleton( @@ -1795,7 +1795,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::golem::IdleAnimation::update_skeleton( diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 6d989cb4f6..1a334b6d63 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -31,49 +31,44 @@ pub fn apply_scatter_to<'a>( chunk: &SimChunk, ) { use BlockKind::*; - let scatter: &[(_, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[ + let scatter: &[(_, bool, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[ // (density, Option<(wavelen, threshold)>) - (BlueFlower, |c| { + (BlueFlower, false, |c| { ( close(c.temp, -0.3, 0.7).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.6)), ) }), - (PinkFlower, |c| { + (PinkFlower, false, |c| { ( close(c.temp, 0.15, 0.5).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.6)), ) }), - (DeadBush, |c| { + (DeadBush, false, |c| { ( close(c.temp, 0.8, 0.3).min(close(c.humidity, 0.0, 0.4)) * 0.015, None, ) }), - (Twigs, |c| ((c.tree_density - 0.5).max(0.0) * 0.0025, None)), - (Stones, |c| ((c.rockiness - 0.5).max(0.0) * 0.005, None)), - (ShortGrass, |c| { - ( - close(c.temp, 0.3, 0.4).min(close(c.humidity, 0.6, 0.35)) * 0.05, - Some((48.0, 0.4)), - ) - }), - (MediumGrass, |c| { + (Twigs, false, |c| ((c.tree_density - 0.5).max(0.0) * 0.0025, None)), + (Stones, false, |c| ((c.rockiness - 0.5).max(0.0) * 0.005, None)), + (ShortGrass, false, |c| (close(c.temp, 0.3, 0.4).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.4)))), + (MediumGrass, false, |c| { ( close(c.temp, 0.0, 0.6).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.2)), ) }), - (LongGrass, |c| { + (LongGrass, false, |c| { ( close(c.temp, 0.4, 0.4).min(close(c.humidity, 0.8, 0.2)) * 0.05, Some((48.0, 0.1)), ) }), - (GrassSnow, |c| { + (GrassSnow, false, |c| { ( - close(c.temp, -0.4, 0.4).min(close(c.rockiness, 0.0, 0.5)), + close(c.temp, -0.4, 0.4).min(close(c.rockiness, 0.0, 0.5)) * 0.1, Some((48.0, 0.6)), ) }), @@ -92,7 +87,9 @@ pub fn apply_scatter_to<'a>( continue; }; - let bk = scatter.iter().enumerate().find_map(|(i, (bk, f))| { + let underwater = col_sample.water_level > col_sample.alt; + + let bk = scatter.iter().enumerate().find_map(|(i, (bk, is_underwater, f))| { let (density, patch) = f(chunk); if density <= 0.0 || patch @@ -105,6 +102,7 @@ pub fn apply_scatter_to<'a>( }) .unwrap_or(false) || !RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) + || underwater != *is_underwater { None } else { From 15b17172954759a92750444093649f90be10a410 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 12 Aug 2020 14:19:26 +0100 Subject: [PATCH 29/33] Added decals to towns --- assets/common/loot_table.ron | 17 +++---- common/src/generation.rs | 2 + common/src/terrain/block.rs | 3 +- voxygen/src/scene/terrain.rs | 2 +- .../settlement/building/archetype/house.rs | 47 ++++++++++++++++++- world/src/site/settlement/mod.rs | 7 ++- 6 files changed, 63 insertions(+), 15 deletions(-) diff --git a/assets/common/loot_table.ron b/assets/common/loot_table.ron index 4d0a7e98bf..f0a98900c7 100644 --- a/assets/common/loot_table.ron +++ b/assets/common/loot_table.ron @@ -7,17 +7,14 @@ (1, "common.items.food.coconut"), // miscellaneous (0.4, "common.items.ore.velorite"), - (0.6, "common.items.ore.veloritefrag"), + (0.6, "common.items.ore.veloritefrag"), (0.1, "common.items.consumable.potion_minor"), (0.01, "common.items.utility.collar"), (0.01, "common.items.utility.bomb_pile"), (0.1, "common.items.utility.bomb"), // crafting ingredients - (0.5, "common.items.crafting_ing.shiny_gem"), (2, "common.items.crafting_ing.leather_scraps"), (1, "common.items.crafting_ing.empty_vial"), - (2, "common.items.crafting_ing.stones"), - (3, "common.items.crafting_ing.twigs"), // swords (0.1, "common.items.weapons.sword.starter_sword"), (0.1, "common.items.weapons.sword.wood_sword"), @@ -137,7 +134,7 @@ // belts (0.17, "common.items.armor.belt.cloth_blue_0"), (0.17, "common.items.armor.belt.cloth_green_0"), - (0.17, "common.items.armor.belt.cloth_purple_0"), + (0.17, "common.items.armor.belt.cloth_purple_0"), (0.08, "common.items.armor.belt.druid"), (0.06, "common.items.armor.belt.leather_0"), (0.06, "common.items.armor.belt.leather_2"), @@ -159,7 +156,7 @@ (0.025, "common.items.armor.chest.worker_red_0"), (0.025, "common.items.armor.chest.worker_red_1"), (0.025, "common.items.armor.chest.worker_yellow_0"), - (0.025, "common.items.armor.chest.worker_yellow_1"), + (0.025, "common.items.armor.chest.worker_yellow_1"), (0.08, "common.items.armor.chest.druid"), (0.06, "common.items.armor.chest.leather_0"), (0.06, "common.items.armor.chest.leather_2"), @@ -171,7 +168,7 @@ // shoes (0.15, "common.items.armor.foot.cloth_blue_0"), (0.15, "common.items.armor.foot.cloth_green_0"), - (0.15, "common.items.armor.foot.cloth_purple_0"), + (0.15, "common.items.armor.foot.cloth_purple_0"), (0.08, "common.items.armor.foot.druid"), (0.06, "common.items.armor.foot.leather_0"), (0.06, "common.items.armor.foot.leather_2"), @@ -184,7 +181,7 @@ (0.125, "common.items.armor.pants.cloth_blue_0"), (0.125, "common.items.armor.pants.cloth_green_0"), (0.125, "common.items.armor.pants.cloth_purple_0"), - (0.125, "common.items.armor.pants.worker_blue_0"), + (0.125, "common.items.armor.pants.worker_blue_0"), (0.08, "common.items.armor.pants.druid"), (0.04, "common.items.armor.pants.leather_0"), (0.04, "common.items.armor.pants.leather_2"), @@ -198,7 +195,7 @@ (0.125, "common.items.armor.shoulder.cloth_blue_0"), (0.125, "common.items.armor.shoulder.cloth_blue_1"), (0.125, "common.items.armor.shoulder.cloth_green_0"), - (0.125, "common.items.armor.shoulder.cloth_purple_0"), + (0.125, "common.items.armor.shoulder.cloth_purple_0"), (0.06, "common.items.armor.shoulder.druidshoulder"), (0.06, "common.items.armor.shoulder.leather_strips"), (0.04, "common.items.armor.shoulder.leather_0"), @@ -217,7 +214,7 @@ //gloves (0.17, "common.items.armor.hand.cloth_blue_0"), (0.17, "common.items.armor.hand.cloth_green_0"), - (0.17, "common.items.armor.hand.cloth_purple_0"), + (0.17, "common.items.armor.hand.cloth_purple_0"), (0.08, "common.items.armor.hand.druid"), (0.06, "common.items.armor.hand.leather_0"), (0.06, "common.items.armor.hand.leather_2"), diff --git a/common/src/generation.rs b/common/src/generation.rs index cb285faf1d..e5d19437ab 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -116,6 +116,8 @@ impl EntityInfo { }, Body::Dragon(body) => Some(get_npc_name(&NPC_NAMES.dragon, body.species)), Body::QuadrupedLow(body) => Some(get_npc_name(&NPC_NAMES.quadruped_low, body.species)), + Body::Golem(body) => Some(get_npc_name(&NPC_NAMES.golem, body.species)), + Body::BipedLarge(body) => Some(get_npc_name(&NPC_NAMES.biped_large, body.species)), _ => None, } .map(|s| { diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index ad2c551ffc..d523667d1d 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -381,7 +381,8 @@ impl BlockKind { pub fn is_explodable(&self) -> bool { match self { BlockKind::Leaves | BlockKind::Grass | BlockKind::Rock | BlockKind::GrassSnow => true, - _ => false, + BlockKind::Air => false, + bk => bk.is_air(), // Temporary catch for terrain sprites } } diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 52438575bd..d27badb9cd 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -2482,7 +2482,7 @@ impl Terrain { (BlockKind::HangingSign, 0), make_models( "voxygen.voxel.sprite.furniture.hanging_sign-0", - Vec3::new(-3.5, -17.0, 0.0), + Vec3::new(-3.5, -28.0, -4.0), Vec3::one(), ), ), diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index 0703058d31..cff969bd25 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -54,7 +54,7 @@ const WALL_COLORS: &[Rgb] = &[ Rgb::new(0xE1, 0xAB, 0x91), Rgb::new(0x82, 0x57, 0x4C), Rgb::new(0xB9, 0x96, 0x77), - Rgb::new(0x6E, 0x4D, 0x3C), + Rgb::new(0xAE, 0x8D, 0x9C), ]; const SUPPORT_COLORS: &[Rgb] = &[ @@ -292,6 +292,13 @@ impl Archetype for House { let foundation_height = 0 - (dist - width - 1).max(0); let roof_top = storey_height * attr.levels + 2 + width; + let edge_ori = if bound_offset.x.abs() > bound_offset.y.abs() { + if center_offset.x > 0 { 6 } else { 2 } + } else { + if (center_offset.y > 0) ^ (ori == Ori::East) { 0 } else { 4 } + }; + let edge_ori = if ori == Ori::East { (edge_ori + 2) % 8 } else { edge_ori }; + if let Pillar::Chimney(chimney_height) = attr.pillar { let chimney_top = roof_top + chimney_height; // Chimney shaft @@ -480,12 +487,48 @@ impl Archetype for House { || (!attr.storey_fill.has_upper() && profile.y >= ceil_height) { return Some(empty); + // Furniture + } else if dist == width - 1 + && center_offset.sum() % 2 == 0 + && profile.y == floor_height + 1 + && self.noise.chance(Vec3::new(center_offset.x, center_offset.y, z), 0.2) + { + let furniture = match self.noise.get(Vec3::new(center_offset.x, center_offset.y, z + 100)) % 11 { + 0 => BlockKind::Planter, + 1 => BlockKind::ChairSingle, + 2 => BlockKind::ChairDouble, + 3 => BlockKind::CoatRack, + 4 => BlockKind::Crate, + 6 => BlockKind::DrawerMedium, + 7 => BlockKind::DrawerSmall, + 8 => BlockKind::TableSide, + 9 => BlockKind::WardrobeSingle, + _ => BlockKind::Pot, + }; + + return Some(BlockMask::new(Block::new(furniture, Rgb::new(edge_ori, 0, 0)), internal_layer)); } else { return Some(internal); } } - None + // Wall ornaments + if dist == width + 1 + && center_offset.map(|e| e.abs()).reduce_min() == 0 + && profile.y == floor_height + 3 + && self.noise.chance(Vec3::new(center_offset.x, center_offset.y, z), 0.35) + && attr.storey_fill.has_lower() + { + let ornament = match self.noise.get(Vec3::new(center_offset.x, center_offset.y, z + 100)) % 4 { + 0 => BlockKind::HangingSign, + 1 | 2 => BlockKind::HangingBasket, + _ => BlockKind::DungeonWallDecor, + }; + + return Some(BlockMask::new(Block::new(ornament, Rgb::new((edge_ori + 4) % 8, 0, 0)), internal_layer)); + } else { + None + } }; let mut cblock = empty; diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 99ef6a1fc9..af141ff916 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -746,11 +746,16 @@ impl Settlement { for z in -8 - diff..3 + diff { let pos = Vec3::new(offs.x, offs.y, surface_z + z); + let block = vol.get(pos).ok().copied().unwrap_or(Block::empty()); + + if block.is_empty() { + break; + } if let (0, Some(block)) = (z, surface_block) { let _ = vol.set(pos, block); } else if z >= 0 { - if vol.get(pos).unwrap().kind() != BlockKind::Water { + if block.kind() != BlockKind::Water { let _ = vol.set(pos, Block::empty()); } } else { From 85ed5ad356ddfb3eaede098671816b252a28bb25 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 12 Aug 2020 14:51:21 +0100 Subject: [PATCH 30/33] Updated changelog, cleaned up warnings, minor fixes --- CHANGELOG.md | 7 +- common/src/astar.rs | 2 +- common/src/path.rs | 13 +--- common/src/sys/phys.rs | 5 +- voxygen/src/scene/terrain.rs | 10 +-- world/src/block/mod.rs | 2 +- world/src/civ/mod.rs | 2 +- world/src/column/mod.rs | 2 +- world/src/index.rs | 4 +- world/src/layer/mod.rs | 18 ++++-- world/src/sim/mod.rs | 1 - world/src/sim2/mod.rs | 18 +++--- world/src/site/castle/keep.rs | 20 ------ world/src/site/castle/mod.rs | 64 ++++++------------- world/src/site/economy.rs | 2 +- world/src/site/mod.rs | 3 +- .../settlement/building/archetype/house.rs | 8 +-- .../settlement/building/archetype/keep.rs | 4 +- world/src/site/settlement/mod.rs | 9 +-- world/src/site/settlement/town.rs | 2 +- world/src/util/mod.rs | 2 +- 21 files changed, 77 insertions(+), 121 deletions(-) delete mode 100644 world/src/site/castle/keep.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 06d7096ee9..10b75f920f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,10 +49,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Loading-Screen tips - Feeding animation for some animals - Power stat to weapons which affects weapon damage -- Add detection of entities under the cursor +- Add detection of entities under the cursor - Functional group-system with exp-sharing and disabled damage to group members - Some Campfire, fireball & bomb; particle, light & sound effects. - Added setting to change resolution +- Rare (unfinished) castles +- Caves with monsters and treasure +- Furniture and decals in towns ### Changed @@ -85,6 +88,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Dehardcoded many item variants - Tooltips avoid the mouse better and disappear when hovered - Improved social window functions and visuals +- Changed agent behaviour to allow fleeing +- Waypoints now spawn on dungeon staircases ### Removed diff --git a/common/src/astar.rs b/common/src/astar.rs index 35781f0317..79a523c4d1 100644 --- a/common/src/astar.rs +++ b/common/src/astar.rs @@ -121,7 +121,7 @@ impl Astar { { let iter_limit = self.max_iters.min(self.iter + iters); while self.iter < iter_limit { - if let Some(PathEntry { node, cost }) = self.potential_nodes.pop() { + if let Some(PathEntry { node, .. }) = self.potential_nodes.pop() { if satisfied(&node) { return PathResult::Path(self.reconstruct_path_to(node)); } else { diff --git a/common/src/path.rs b/common/src/path.rs index 510724b597..0c7f35479c 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -121,17 +121,8 @@ impl Route { .unwrap_or(false)) }); - let next0_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); - let next1_tgt = next1.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); - let next_tgt = next0_tgt; - - // // Maybe skip a node (useful with traversing downhill) - // let closest_tgt = if next0_tgt.distance_squared(pos) < next1_tgt.distance_squared(pos) { - // next0_tgt - // } else { - // next1_tgt - // }; - let closest_tgt = next0_tgt.map2(pos, |tgt, pos| pos.clamped(tgt.floor(), tgt.ceil())); + let next_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); + let closest_tgt = next_tgt.map2(pos, |tgt, pos| pos.clamped(tgt.floor(), tgt.ceil())); // Determine whether we're close enough to the next to to consider it completed let dist_sqrd = pos.xy().distance_squared(closest_tgt.xy()); diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index cf0caf31bb..25855e6baa 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -94,7 +94,7 @@ impl<'a> System<'a> for Sys { let mut event_emitter = event_bus.emitter(); // Apply movement inputs - for (entity, scale, sticky, collider, mut pos, mut vel, _ori, _) in ( + for (entity, _scale, sticky, collider, mut pos, mut vel, _ori, _) in ( &entities, scales.maybe(), stickies.maybe(), @@ -113,7 +113,8 @@ impl<'a> System<'a> for Sys { continue; } - let scale = scale.map(|s| s.0).unwrap_or(1.0); + // TODO: Use this + //let scale = scale.map(|s| s.0).unwrap_or(1.0); let old_vel = *vel; // Integrate forces diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index d27badb9cd..f3d95593ad 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -3230,15 +3230,15 @@ impl Terrain { if let Some(models) = self.sprite_models.get(&kind) { renderer.render_sprites( if dist_sqrd < sprite_high_detail_distance.powf(2.0) { - &self.sprite_models[&kind][0] + &models[0] } else if dist_sqrd < sprite_hid_detail_distance.powf(2.0) { - &self.sprite_models[&kind][1] + &models[1] } else if dist_sqrd < sprite_mid_detail_distance.powf(2.0) { - &self.sprite_models[&kind][2] + &models[2] } else if dist_sqrd < sprite_low_detail_distance.powf(2.0) { - &self.sprite_models[&kind][3] + &models[3] } else { - &self.sprite_models[&kind][4] + &models[4] }, globals, &instances, diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 152c4a5bba..5b12bb657c 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -9,7 +9,7 @@ use common::{ terrain::{structure::StructureBlock, Block, BlockKind, Structure}, vol::{ReadVol, Vox}, }; -use std::ops::{Add, Div, Mul, Neg}; +use std::ops::{Div, Mul}; use vek::*; pub struct BlockGen<'a> { diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index f569b8b054..fc686d8d03 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -264,7 +264,7 @@ impl Civs { 1 << ((to_next_idx as u8 + 4) % 8); } - for (i, loc) in path.iter().enumerate() { + for loc in path.iter() { let mut chunk = ctx.sim.get_mut(loc.0).unwrap(); let depth = loc.1 * 250.0 - 20.0; chunk.cave.1.alt = diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 47c28a3bab..11b824b027 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -14,7 +14,7 @@ use roots::find_roots_cubic; use std::{ cmp::Reverse, f32, f64, - ops::{Add, Div, Mul, Neg, Sub}, + ops::{Add, Div, Mul, Sub}, }; use tracing::error; use vek::*; diff --git a/world/src/index.rs b/world/src/index.rs index 1c05427d5c..2b0e829c4f 100644 --- a/world/src/index.rs +++ b/world/src/index.rs @@ -1,6 +1,6 @@ use crate::site::Site; -use common::store::{Id, Store}; -use noise::{NoiseFn, Seedable, SuperSimplex}; +use common::store::Store; +use noise::{Seedable, SuperSimplex}; pub struct Index { pub seed: u32, diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 1a334b6d63..6ab1036c3c 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -135,7 +135,7 @@ pub fn apply_paths_to<'a>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), - index: &Index, + _index: &Index, ) { for y in 0..vol.size_xy().y as i32 { for x in 0..vol.size_xy().x as i32 { @@ -239,7 +239,7 @@ pub fn apply_caves_to<'a>( }; let surface_z = col_sample.riverless_alt.floor() as i32; - if let Some((cave_dist, cave_nearest, cave, _)) = col_sample + if let Some((cave_dist, _, cave, _)) = col_sample .cave .filter(|(dist, _, cave, _)| *dist < cave.width) { @@ -286,8 +286,11 @@ pub fn apply_caves_to<'a>( ); } + let cave_depth = (col_sample.alt - cave.alt).max(0.0); + let difficulty = cave_depth / 200.0; + // Scatter things in caves - if RandomField::new(index.seed).chance(wpos2d.into(), 0.001) + if RandomField::new(index.seed).chance(wpos2d.into(), 0.002 * difficulty) && cave_base < surface_z as i32 - 25 { let kind = *assets::load_expect::>("common.cave_scatter") @@ -324,7 +327,7 @@ pub fn apply_caves_supplement<'a>( }; let surface_z = col_sample.riverless_alt.floor() as i32; - if let Some((cave_dist, cave_nearest, cave, _)) = col_sample + if let Some((cave_dist, _, cave, _)) = col_sample .cave .filter(|(dist, _, cave, _)| *dist < cave.width) { @@ -332,14 +335,15 @@ pub fn apply_caves_supplement<'a>( // Relative units let cave_floor = 0.0 - 0.5 * (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width; - let cave_height = (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width; // Abs units let cave_base = (cave.alt + cave_floor) as i32; - let cave_roof = (cave.alt + cave_height) as i32; + + let cave_depth = (col_sample.alt - cave.alt).max(0.0); + let difficulty = cave_depth / 200.0; // Scatter things in caves - if RandomField::new(index.seed).chance(wpos2d.into(), 0.00005) + if RandomField::new(index.seed).chance(wpos2d.into(), 0.0001 * difficulty) && cave_base < surface_z as i32 - 40 { let entity = EntityInfo::at(Vec3::new( diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 92a1fe59fe..d8a3239e23 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -26,7 +26,6 @@ pub use self::{ use crate::{ all::ForestKind, civ::Place, - column::{ColumnGen, ColumnSample}, site::Site, util::{seed_expan, FastNoise, RandomField, StructureGen2d, LOCALITY, NEIGHBORS}, Index, CONFIG, diff --git a/world/src/sim2/mod.rs b/world/src/sim2/mod.rs index 3d07e8ad34..ffccc9516f 100644 --- a/world/src/sim2/mod.rs +++ b/world/src/sim2/mod.rs @@ -8,7 +8,7 @@ use crate::{ Index, }; use common::store::Id; -use tracing::{debug, info, warn}; +use tracing::debug; const MONTH: f32 = 30.0; const YEAR: f32 = 12.0 * MONTH; @@ -78,7 +78,7 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) { } } -pub fn tick(index: &mut Index, world: &mut WorldSim, dt: f32) { +pub fn tick(index: &mut Index, _world: &mut WorldSim, dt: f32) { for site in index.sites.ids() { tick_site_economy(index, site, dt); } @@ -137,8 +137,6 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { // Note that values are used for workforce allocation and are not the same thing // as price let values = &mut site.economy.values; - let marginal_surplus = &site.economy.marginal_surplus; - let stocks = &site.economy.stocks; site.economy.surplus.iter().for_each(|(good, surplus)| { // Value rationalisation let val = 2.0f32.powf(1.0 - *surplus / demand[good]); @@ -151,12 +149,12 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { }); // Update export targets based on relative values - let value_avg = values - .iter() - .map(|(_, v)| (*v).unwrap_or(0.0)) - .sum::() - .max(0.01) - / values.iter().filter(|(_, v)| v.is_some()).count() as f32; + // let value_avg = values + // .iter() + // .map(|(_, v)| (*v).unwrap_or(0.0)) + // .sum::() + // .max(0.01) + // / values.iter().filter(|(_, v)| v.is_some()).count() as f32; //let export_targets = &mut site.economy.export_targets; //let last_exports = &self.last_exports; // site.economy.values.iter().for_each(|(stock, value)| { diff --git a/world/src/site/castle/keep.rs b/world/src/site/castle/keep.rs deleted file mode 100644 index 2dff94facc..0000000000 --- a/world/src/site/castle/keep.rs +++ /dev/null @@ -1,20 +0,0 @@ -use vek::*; -use crate::{ - util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, -}; - -pub struct Keep { - offset: Vec2, - cols: Grid, -} - -const KEEP_CELL_STOREY: i32 = 12; - -pub struct KeepCol { - z_offset: i32, - storeys: Vec, -} - -enum KeepCell { - Cube, -} diff --git a/world/src/site/castle/mod.rs b/world/src/site/castle/mod.rs index 5d600ece84..cc367448ed 100644 --- a/world/src/site/castle/mod.rs +++ b/world/src/site/castle/mod.rs @@ -1,35 +1,19 @@ -mod keep; - use super::SpawnRules; use crate::{ - block::block_from_structure, column::ColumnSample, sim::WorldSim, - site::{ - settlement::building::{ - archetype::keep::{Attr, Keep as KeepArchetype}, - Archetype, Branch, Ori, - }, - BlockMask, + site::settlement::building::{ + archetype::keep::{Attr, Keep as KeepArchetype}, + Archetype, Ori, }, - util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, }; use common::{ - assets, - astar::Astar, - comp, - generation::{ChunkSupplement, EntityInfo}, - npc, - spiral::Spiral2d, - store::{Id, Store}, - terrain::{Block, BlockKind, Structure, TerrainChunkSize}, - vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol}, + generation::ChunkSupplement, + terrain::{Block, BlockKind}, + vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol}, }; -use core::{f32, hash::BuildHasherDefault}; -use fxhash::FxHasher64; -use lazy_static::lazy_static; +use core::f32; use rand::prelude::*; -use std::sync::Arc; use vek::*; struct Keep { @@ -47,8 +31,7 @@ struct Tower { pub struct Castle { origin: Vec2, - alt: i32, - seed: u32, + //seed: u32, radius: i32, towers: Vec, keeps: Vec, @@ -57,8 +40,6 @@ pub struct Castle { flags: bool, evil: bool, - - keep: Option, } pub struct GenCtx<'a, R: Rng> { @@ -69,7 +50,7 @@ pub struct GenCtx<'a, R: Rng> { impl Castle { #[allow(clippy::let_and_return)] // TODO: Pending review in #587 pub fn generate(wpos: Vec2, sim: Option<&mut WorldSim>, rng: &mut impl Rng) -> Self { - let mut ctx = GenCtx { sim, rng }; + let ctx = GenCtx { sim, rng }; let boundary_towers = ctx.rng.gen_range(5, 10); let keep_count = ctx.rng.gen_range(1, 4); @@ -77,15 +58,15 @@ impl Castle { let radius = 150; - let mut this = Self { + let this = Self { origin: wpos, - alt: ctx - .sim - .as_ref() - .and_then(|sim| sim.get_alt_approx(wpos)) - .unwrap_or(0.0) as i32 - + 6, - seed: ctx.rng.gen(), + // alt: ctx + // .sim + // .as_ref() + // .and_then(|sim| sim.get_alt_approx(wpos)) + // .unwrap_or(0.0) as i32 + // + 6, + //seed: ctx.rng.gen(), radius, towers: (0..boundary_towers) @@ -150,8 +131,6 @@ impl Castle { } }) .collect(), - - keep: None, }; this @@ -231,7 +210,7 @@ impl Castle { } } - let (wall_dist, wall_pos, wall_alt, wall_ori, towers) = (0..self.towers.len()) + let (wall_dist, wall_pos, wall_alt, wall_ori, _towers) = (0..self.towers.len()) .map(|i| { let tower0 = &self.towers[i]; let tower1 = &self.towers[(i + 1) % self.towers.len()]; @@ -273,7 +252,6 @@ impl Castle { .min_by_key(|x| x.0) .unwrap(); let border_pos = (wall_pos - rpos).map(|e| e.abs()); - let wall_normal = (rpos - wall_pos).map(|e| e as f32); let wall_rpos = if wall_ori == Ori::East { rpos } else { @@ -428,10 +406,10 @@ impl Castle { #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 pub fn apply_supplement<'a>( &'a self, - rng: &mut impl Rng, - wpos2d: Vec2, + _rng: &mut impl Rng, + _wpos2d: Vec2, _get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, - supplement: &mut ChunkSupplement, + _supplement: &mut ChunkSupplement, ) { // TODO } diff --git a/world/src/site/economy.rs b/world/src/site/economy.rs index f152560702..665ad52c1f 100644 --- a/world/src/site/economy.rs +++ b/world/src/site/economy.rs @@ -101,7 +101,7 @@ impl Economy { } pub fn replenish(&mut self, time: f32) { - use rand::Rng; + //use rand::Rng; for (i, (g, v)) in [ (Wheat, 50.0), (Logs, 20.0), diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index d32b8028b1..82c83b2183 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -14,10 +14,9 @@ use crate::column::ColumnSample; use common::{ generation::ChunkSupplement, terrain::Block, - vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol}, + vol::{BaseVol, ReadVol, RectSizedVol, WriteVol}, }; use rand::Rng; -use std::{fmt, sync::Arc}; use vek::*; pub struct SpawnRules { diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index cff969bd25..97c5a0186d 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -122,7 +122,7 @@ pub struct Attr { } impl Attr { - pub fn generate(rng: &mut R, locus: i32) -> Self { + pub fn generate(rng: &mut R, _locus: i32) -> Self { Self { central_supports: rng.gen(), storey_fill: match rng.gen_range(0, 2) { @@ -222,14 +222,14 @@ impl Archetype for House { #[allow(clippy::int_plus_one)] // TODO: Pending review in #587 fn draw( &self, - pos: Vec3, + _pos: Vec3, dist: i32, bound_offset: Vec2, center_offset: Vec2, z: i32, ori: Ori, locus: i32, - len: i32, + _len: i32, attr: &Self::Attr, ) -> BlockMask { let profile = Vec2::new(bound_offset.x, z); @@ -521,7 +521,7 @@ impl Archetype for House { { let ornament = match self.noise.get(Vec3::new(center_offset.x, center_offset.y, z + 100)) % 4 { 0 => BlockKind::HangingSign, - 1 | 2 => BlockKind::HangingBasket, + 1 | 2 | 3 => BlockKind::HangingBasket, _ => BlockKind::DungeonWallDecor, }; diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index a77df1c6ce..178cfaf4a6 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -82,13 +82,13 @@ impl Archetype for Keep { fn draw( &self, pos: Vec3, - dist: i32, + _dist: i32, bound_offset: Vec2, center_offset: Vec2, z: i32, ori: Ori, locus: i32, - len: i32, + _len: i32, attr: &Self::Attr, ) -> BlockMask { let profile = Vec2::new(bound_offset.x, z); diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index af141ff916..ea20a5df76 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -640,8 +640,8 @@ impl Settlement { Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)), Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)), Some(Plot::Water) => Some(Rgb::new(100, 150, 250)), - Some(Plot::Town { district }) => None, - Some(Plot::Town { district }) => { + //Some(Plot::Town { district }) => None, + Some(Plot::Town { .. }) => { if let Some((_, path_nearest, _, _)) = col_sample.path { let path_dir = (path_nearest - wpos2d.map(|e| e as f32)) .rotated_z(f32::consts::PI / 2.0) @@ -662,7 +662,7 @@ impl Settlement { } } - Some(Rgb::new(100, 90, 75).map2(Rgb::iota(), |e: u8, i: i32| { + Some(Rgb::new(100, 105, 75).map2(Rgb::iota(), |e: u8, i: i32| { e.saturating_add( (self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 1) as u8, @@ -744,7 +744,7 @@ impl Settlement { { let diff = (surface_z - land_surface_z).abs(); - for z in -8 - diff..3 + diff { + for z in -8 - diff..4 + diff { let pos = Vec3::new(offs.x, offs.y, surface_z + z); let block = vol.get(pos).ok().copied().unwrap_or(Block::empty()); @@ -1162,6 +1162,7 @@ impl Land { self.tiles.get(&pos).map(|tile| self.plots.get(tile.plot)) } + #[allow(dead_code)] pub fn plot_at_mut(&mut self, pos: Vec2) -> Option<&mut Plot> { self.tiles .get(&pos) diff --git a/world/src/site/settlement/town.rs b/world/src/site/settlement/town.rs index 6d6a082cd9..468408678b 100644 --- a/world/src/site/settlement/town.rs +++ b/world/src/site/settlement/town.rs @@ -1,5 +1,5 @@ use super::{GenCtx, AREA_SIZE}; -use common::store::{Id, Store}; +use common::store::Store; use rand::prelude::*; use vek::*; diff --git a/world/src/util/mod.rs b/world/src/util/mod.rs index 7ddc7e85e0..de31f42c71 100644 --- a/world/src/util/mod.rs +++ b/world/src/util/mod.rs @@ -20,7 +20,7 @@ pub use self::{ unit_chooser::UnitChooser, }; -use fxhash::{FxHasher32, FxHasher64}; +use fxhash::FxHasher32; use hashbrown::{HashMap, HashSet}; use std::hash::BuildHasherDefault; use vek::*; From 9cefaaa7afe32235462a2d438d18258e1009f1b3 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 12 Aug 2020 15:04:17 +0100 Subject: [PATCH 31/33] Improved spawn rates of castles and cave monsters --- assets/common/cave_scatter.ron | 12 +++---- world/src/civ/mod.rs | 6 ++-- world/src/layer/mod.rs | 65 ++++++++++++++++++++-------------- world/src/lib.rs | 9 ++++- 4 files changed, 56 insertions(+), 36 deletions(-) diff --git a/assets/common/cave_scatter.ron b/assets/common/cave_scatter.ron index ea35812776..524ef2ff57 100644 --- a/assets/common/cave_scatter.ron +++ b/assets/common/cave_scatter.ron @@ -1,12 +1,12 @@ [ - (25, Velorite), - (35, VeloriteFrag), + (20, Velorite), + (30, VeloriteFrag), (60, Stones), (15, PurpleFlower), - (150, ShortGrass), - (80, Mushroom), + (130, ShortGrass), + (120, Mushroom), (8, ShinyGem), - (5, Chest), + (15, Chest), (2, Crate), - (1, Scarecrow), + (0.25, Scarecrow), ] diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index fc686d8d03..f1395ab1b4 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -90,9 +90,9 @@ impl Civs { for _ in 0..INITIAL_CIV_COUNT * 3 { attempt(5, || { - let (kind, size) = match ctx.rng.gen_range(0, 2) { - 0 => (SiteKind::Dungeon, 0), - _ => (SiteKind::Castle, 3), + let (kind, size) = match ctx.rng.gen_range(0, 8) { + 0 => (SiteKind::Castle, 3), + _ => (SiteKind::Dungeon, 0), }; let loc = find_site_loc(&mut ctx, None, size)?; this.establish_site(&mut ctx.reseed(), loc, |place| Site { diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 6ab1036c3c..6aeda93976 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -51,9 +51,18 @@ pub fn apply_scatter_to<'a>( None, ) }), - (Twigs, false, |c| ((c.tree_density - 0.5).max(0.0) * 0.0025, None)), - (Stones, false, |c| ((c.rockiness - 0.5).max(0.0) * 0.005, None)), - (ShortGrass, false, |c| (close(c.temp, 0.3, 0.4).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.4)))), + (Twigs, false, |c| { + ((c.tree_density - 0.5).max(0.0) * 0.0025, None) + }), + (Stones, false, |c| { + ((c.rockiness - 0.5).max(0.0) * 0.005, None) + }), + (ShortGrass, false, |c| { + ( + close(c.temp, 0.3, 0.4).min(close(c.humidity, 0.6, 0.35)) * 0.05, + Some((48.0, 0.4)), + ) + }), (MediumGrass, false, |c| { ( close(c.temp, 0.0, 0.6).min(close(c.humidity, 0.6, 0.35)) * 0.05, @@ -89,26 +98,30 @@ pub fn apply_scatter_to<'a>( let underwater = col_sample.water_level > col_sample.alt; - let bk = scatter.iter().enumerate().find_map(|(i, (bk, is_underwater, f))| { - let (density, patch) = f(chunk); - if density <= 0.0 - || patch - .map(|(wavelen, threshold)| { - index.noise.scatter_nz.get( - wpos2d - .map(|e| e as f64 / wavelen as f64 + i as f64 * 43.0) - .into_array(), - ) < threshold as f64 - }) - .unwrap_or(false) - || !RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) - || underwater != *is_underwater - { - None - } else { - Some(*bk) - } - }); + let bk = scatter + .iter() + .enumerate() + .find_map(|(i, (bk, is_underwater, f))| { + let (density, patch) = f(chunk); + if density <= 0.0 + || patch + .map(|(wavelen, threshold)| { + index.noise.scatter_nz.get( + wpos2d + .map(|e| e as f64 / wavelen as f64 + i as f64 * 43.0) + .into_array(), + ) < threshold as f64 + }) + .unwrap_or(false) + || !RandomField::new(i as u32) + .chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) + || underwater != *is_underwater + { + None + } else { + Some(*bk) + } + }); if let Some(bk) = bk { let mut z = col_sample.alt as i32 - 4; @@ -287,10 +300,10 @@ pub fn apply_caves_to<'a>( } let cave_depth = (col_sample.alt - cave.alt).max(0.0); - let difficulty = cave_depth / 200.0; + let difficulty = cave_depth / 100.0; // Scatter things in caves - if RandomField::new(index.seed).chance(wpos2d.into(), 0.002 * difficulty) + if RandomField::new(index.seed).chance(wpos2d.into(), 0.002 * difficulty.powf(1.5)) && cave_base < surface_z as i32 - 25 { let kind = *assets::load_expect::>("common.cave_scatter") @@ -343,7 +356,7 @@ pub fn apply_caves_supplement<'a>( let difficulty = cave_depth / 200.0; // Scatter things in caves - if RandomField::new(index.seed).chance(wpos2d.into(), 0.0001 * difficulty) + if RandomField::new(index.seed).chance(wpos2d.into(), 0.00005 * difficulty) && cave_base < surface_z as i32 - 40 { let entity = EntityInfo::at(Vec3::new( diff --git a/world/src/lib.rs b/world/src/lib.rs index 9503afd10b..d6ca1a0023 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -230,7 +230,14 @@ impl World { } // Apply layer supplement - layer::apply_caves_supplement(&mut rng, chunk_wpos2d, sample_get, &chunk, &self.index, &mut supplement); + layer::apply_caves_supplement( + &mut rng, + chunk_wpos2d, + sample_get, + &chunk, + &self.index, + &mut supplement, + ); // Apply site supplementary information sim_chunk.sites.iter().for_each(|site| { From 50a85853e30332d53bdf0c2c0bee8e7f47b6d547 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 12 Aug 2020 15:10:12 +0100 Subject: [PATCH 32/33] Fmt and clippy lints fixes --- client/src/lib.rs | 2 +- common/src/comp/agent.rs | 2 +- common/src/comp/inventory/item/mod.rs | 2 +- common/src/comp/inventory/mod.rs | 2 +- common/src/lottery.rs | 6 +- common/src/path.rs | 54 +++--- common/src/spiral.rs | 2 +- common/src/states/glide.rs | 7 +- common/src/states/glide_wield.rs | 7 +- common/src/states/utils.rs | 23 ++- common/src/sys/agent.rs | 15 +- common/src/sys/phys.rs | 78 +++++---- common/src/terrain/block.rs | 13 +- server/src/events/entity_manipulation.rs | 6 +- voxygen/src/anim/src/character/beta.rs | 1 - voxygen/src/anim/src/character/spin.rs | 1 - voxygen/src/scene/figure/mod.rs | 24 +-- world/src/block/mod.rs | 155 +++++++++--------- world/src/civ/mod.rs | 3 +- world/src/column/mod.rs | 1 - world/src/index.rs | 1 + world/src/layer/mod.rs | 9 +- world/src/sim/way.rs | 20 +-- world/src/sim2/mod.rs | 108 ++++++------ world/src/site/dungeon/mod.rs | 6 +- .../settlement/building/archetype/house.rs | 50 ++++-- .../settlement/building/archetype/keep.rs | 9 +- 27 files changed, 352 insertions(+), 255 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index e74be1610e..fe71cde3e5 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -150,7 +150,7 @@ impl Client { recipe_book, } => { // TODO: Display that versions don't match in Voxygen - if &server_info.git_hash != *common::util::GIT_HASH { + if server_info.git_hash != *common::util::GIT_HASH { warn!( "Server is running {}[{}], you are running {}[{}], versions \ might be incompatible!", diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 59a27fe6b0..93642ab75e 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,4 +1,4 @@ -use crate::{path::Chaser, sync::Uid, comp::Body}; +use crate::{comp::Body, path::Chaser, sync::Uid}; use specs::{Component, Entity as EcsEntity}; use specs_idvs::IdvStorage; use vek::*; diff --git a/common/src/comp/inventory/item/mod.rs b/common/src/comp/inventory/item/mod.rs index 4ea11edd80..d9f9340115 100644 --- a/common/src/comp/inventory/item/mod.rs +++ b/common/src/comp/inventory/item/mod.rs @@ -7,8 +7,8 @@ pub use tool::{Hands, Tool, ToolCategory, ToolKind}; use crate::{ assets::{self, Asset}, effect::Effect, - terrain::{Block, BlockKind}, lottery::Lottery, + terrain::{Block, BlockKind}, }; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; diff --git a/common/src/comp/inventory/mod.rs b/common/src/comp/inventory/mod.rs index 12d2180f5f..b0204eb60c 100644 --- a/common/src/comp/inventory/mod.rs +++ b/common/src/comp/inventory/mod.rs @@ -503,7 +503,7 @@ impl Inventory { } } - if missing.len() == 0 { + if missing.is_empty() { Ok(slot_claims) } else { Err(missing) diff --git a/common/src/lottery.rs b/common/src/lottery.rs index 13b5ff8142..0a7a6698a1 100644 --- a/common/src/lottery.rs +++ b/common/src/lottery.rs @@ -1,6 +1,6 @@ use crate::assets::{self, Asset}; use rand::prelude::*; -use serde::{Deserialize, Serialize, de::DeserializeOwned}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{fs::File, io::BufReader}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -40,9 +40,7 @@ impl Lottery { .1 } - pub fn choose(&self) -> &T { - self.choose_seeded(thread_rng().gen()) - } + pub fn choose(&self) -> &T { self.choose_seeded(thread_rng().gen()) } pub fn iter(&self) -> impl Iterator { self.items.iter() } } diff --git a/common/src/path.rs b/common/src/path.rs index 0c7f35479c..78f0d76e51 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -100,9 +100,7 @@ impl Route { { let (next0, next1, next_tgt, be_precise) = loop { // If we've reached the end of the path, stop - if self.next(0).is_none() { - return None; - } + self.next(0)?; let next0 = self .next(0) @@ -115,10 +113,11 @@ impl Route { } let be_precise = DIAGONALS.iter().any(|pos| { - (-1..2) - .all(|z| vol.get(next0 + Vec3::new(pos.x, pos.y, z)) + (-1..2).all(|z| { + vol.get(next0 + Vec3::new(pos.x, pos.y, z)) .map(|b| !b.is_solid()) - .unwrap_or(false)) + .unwrap_or(false) + }) }); let next_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); @@ -331,7 +330,8 @@ impl Chaser { let pos_to_tgt = pos.distance(tgt); // If we're already close to the target then there's nothing to do - let end = self.route + let end = self + .route .as_ref() .and_then(|(r, _)| r.path.end().copied()) .map(|e| e.map(|e| e as f32 + 0.5)) @@ -343,7 +343,8 @@ impl Chaser { return None; } - let bearing = if let Some((end, complete)) = self.route + let bearing = if let Some((end, complete)) = self + .route .as_ref() .and_then(|(r, complete)| Some((r.path().end().copied()?, *complete))) { @@ -354,7 +355,9 @@ impl Chaser { // theory this shouldn't happen, but in practice the world is full // of unpredictable obstacles that are more than willing to mess up // our day. TODO: Come up with a better heuristic for this - if (end_to_tgt > pos_to_tgt * 0.3 + 5.0 && complete) || thread_rng().gen::() < 0.001 { + if (end_to_tgt > pos_to_tgt * 0.3 + 5.0 && complete) + || thread_rng().gen::() < 0.001 + { None } else { self.route @@ -388,20 +391,31 @@ impl Chaser { let start_index = path .iter() .enumerate() - .min_by_key(|(_, node)| node.xy().map(|e| e as f32).distance_squared(pos.xy() + tgt_dir) as i32) + .min_by_key(|(_, node)| { + node.xy() + .map(|e| e as f32) + .distance_squared(pos.xy() + tgt_dir) + as i32 + }) .map(|(idx, _)| idx); - (Route { - path, - next_idx: start_index.unwrap_or(0), - }, complete) + ( + Route { + path, + next_idx: start_index.unwrap_or(0), + }, + complete, + ) }); } - let walking_towards_edge = (-3..2) - .all(|z| vol.get((pos + Vec3::::from(tgt_dir) * 2.5).map(|e| e as i32) + Vec3::unit_z() * z) - .map(|b| !b.is_solid()) - .unwrap_or(false)); + let walking_towards_edge = (-3..2).all(|z| { + vol.get( + (pos + Vec3::::from(tgt_dir) * 2.5).map(|e| e as i32) + Vec3::unit_z() * z, + ) + .map(|b| !b.is_solid()) + .unwrap_or(false) + }); if !walking_towards_edge { Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 0.75)) @@ -430,8 +444,8 @@ where .unwrap_or(true) } -/// Attempt to search for a path to a target, returning the path (if one was found) -/// and whether it is complete (reaches the target) +/// Attempt to search for a path to a target, returning the path (if one was +/// found) and whether it is complete (reaches the target) fn find_path( astar: &mut Option, DefaultHashBuilder>>, vol: &V, diff --git a/common/src/spiral.rs b/common/src/spiral.rs index 93c59fef11..7b0cb2da51 100644 --- a/common/src/spiral.rs +++ b/common/src/spiral.rs @@ -21,7 +21,7 @@ impl Spiral2d { impl Iterator for Spiral2d { type Item = Vec2; - #[allow(clippy::erasing_op)] + #[allow(clippy::erasing_op, clippy::identity_op)] fn next(&mut self) -> Option { let layer_size = (self.layer * 8 + 4 * self.layer.min(1) - 4).max(1); if self.i >= layer_size { diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index e45b56e88e..c2269e298b 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -24,7 +24,12 @@ impl CharacterBehavior for Data { update.character = CharacterState::GlideWield; return update; } - if data.physics.in_fluid.map(|depth| depth > 0.5).unwrap_or(false) { + if data + .physics + .in_fluid + .map(|depth| depth > 0.5) + .unwrap_or(false) + { update.character = CharacterState::Idle; } // If there is a wall in front of character and they are trying to climb go to diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index 548e076155..c1b486dc74 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -19,7 +19,12 @@ impl CharacterBehavior for Data { if !data.physics.on_ground { update.character = CharacterState::Glide; } - if data.physics.in_fluid.map(|depth| depth > 0.5).unwrap_or(false) { + if data + .physics + .in_fluid + .map(|depth| depth > 0.5) + .unwrap_or(false) + { update.character = CharacterState::Idle; } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 10730d75ef..8bd5788dec 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -119,8 +119,9 @@ fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, depth: // Swim if data.inputs.swimup.is_pressed() { - update.vel.0.z = - (update.vel.0.z + data.dt.0 * GRAVITY * 4.0 * depth.clamped(0.0, 1.0).powf(3.0)).min(BASE_HUMANOID_WATER_SPEED); + update.vel.0.z = (update.vel.0.z + + data.dt.0 * GRAVITY * 4.0 * depth.clamped(0.0, 1.0).powf(3.0)) + .min(BASE_HUMANOID_WATER_SPEED); } // Swim if data.inputs.swimdown.is_pressed() { @@ -192,14 +193,28 @@ pub fn attempt_swap_loadout(data: &JoinData, update: &mut StateUpdate) { /// Checks that player can wield the glider and updates `CharacterState` if so pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) { - if data.physics.on_ground && !data.physics.in_fluid.map(|depth| depth > 1.0).unwrap_or(false) && data.body.is_humanoid() { + if data.physics.on_ground + && !data + .physics + .in_fluid + .map(|depth| depth > 1.0) + .unwrap_or(false) + && data.body.is_humanoid() + { update.character = CharacterState::GlideWield; } } /// Checks that player can jump and sends jump event if so pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) { - if data.inputs.jump.is_pressed() && data.physics.on_ground && !data.physics.in_fluid.map(|depth| depth > 1.0).unwrap_or(false) { + if data.inputs.jump.is_pressed() + && data.physics.on_ground + && !data + .physics + .in_fluid + .map(|depth| depth > 1.0) + .unwrap_or(false) + { update .local_events .push_front(LocalEvent::Jump(data.entity)); diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 679bddb36b..a2fed71eb3 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -317,9 +317,11 @@ impl<'a> System<'a> for Sys { pos.0, vel.0, // Away from the target (ironically) - pos.0 + (pos.0 - tgt_pos.0) - .try_normalized() - .unwrap_or_else(Vec3::unit_y) * 8.0, + pos.0 + + (pos.0 - tgt_pos.0) + .try_normalized() + .unwrap_or_else(Vec3::unit_y) + * 8.0, TraversalConfig { node_tolerance, slow_factor, @@ -483,8 +485,11 @@ impl<'a> System<'a> for Sys { Activity::Attack { target, .. } if target == attacker => {}, _ => { if agent.can_speak { - let msg = "npc.speech.villager_under_attack".to_string(); - event_bus.emit_now(ServerEvent::Chat(UnresolvedChatMsg::npc(*uid, msg))); + let msg = + "npc.speech.villager_under_attack".to_string(); + event_bus.emit_now(ServerEvent::Chat( + UnresolvedChatMsg::npc(*uid, msg), + )); } agent.activity = Activity::Attack { diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 25855e6baa..03bc0e38c4 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -12,8 +12,8 @@ use crate::{ use specs::{ saveload::MarkerAllocator, Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage, }; -use vek::*; use std::ops::Range; +use vek::*; pub const GRAVITY: f32 = 9.81 * 5.0; const BOUYANCY: f32 = 1.0; @@ -135,7 +135,11 @@ impl<'a> System<'a> for Sys { .is_some(); let downward_force = if !in_loaded_chunk { 0.0 // No gravity in unloaded chunks - } else if physics_state.in_fluid.map(|depth| depth > 0.75).unwrap_or(false) { + } else if physics_state + .in_fluid + .map(|depth| depth > 0.75) + .unwrap_or(false) + { (1.0 - BOUYANCY) * GRAVITY } else { GRAVITY @@ -159,9 +163,9 @@ impl<'a> System<'a> for Sys { z_max, } => { // Scale collider - let radius = *radius;// * scale; - let z_min = *z_min;// * scale; - let z_max = *z_max;// * scale; + let radius = *radius; // * scale; + let z_min = *z_min; // * scale; + let z_max = *z_max; // * scale; // Probe distances let hdist = radius.ceil() as i32; @@ -177,47 +181,46 @@ impl<'a> System<'a> for Sys { .flatten() .flatten(); - // Function for iterating over the blocks the player at a specific position collides - // with + // Function for iterating over the blocks the player at a specific position + // collides with fn collision_iter<'a>( pos: Vec3, terrain: &'a TerrainGrid, hit: &'a dyn Fn(&Block) -> bool, - near_iter: impl Iterator + 'a, + near_iter: impl Iterator + 'a, radius: f32, z_range: Range, - ) -> impl Iterator> + 'a { - near_iter - .filter_map(move |(i, j, k)| { - let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); + ) -> impl Iterator> + 'a { + near_iter.filter_map(move |(i, j, k)| { + let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); - if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) { - let player_aabb = Aabb { - min: pos + Vec3::new(-radius, -radius, z_range.start), - max: pos + Vec3::new(radius, radius, z_range.end), - }; - let block_aabb = Aabb { - min: block_pos.map(|e| e as f32), - max: block_pos.map(|e| e as f32) - + Vec3::new(1.0, 1.0, block.get_height()), - }; + if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) { + let player_aabb = Aabb { + min: pos + Vec3::new(-radius, -radius, z_range.start), + max: pos + Vec3::new(radius, radius, z_range.end), + }; + let block_aabb = Aabb { + min: block_pos.map(|e| e as f32), + max: block_pos.map(|e| e as f32) + + Vec3::new(1.0, 1.0, block.get_height()), + }; - if player_aabb.collides_with_aabb(block_aabb) { - return Some(block_aabb); - } + if player_aabb.collides_with_aabb(block_aabb) { + return Some(block_aabb); } + } - None - }) + None + }) }; // Function for determining whether the player at a specific position collides // with blocks with the given criteria let collision_with = |pos: Vec3, - hit: &dyn Fn(&Block) -> bool, - near_iter| - { - collision_iter(pos, &terrain, hit, near_iter, radius, z_min..z_max).count() > 0 + hit: &dyn Fn(&Block) -> bool, + near_iter| { + collision_iter(pos, &terrain, hit, near_iter, radius, z_min..z_max).count() + > 0 }; let was_on_ground = physics_state.on_ground; @@ -418,9 +421,16 @@ impl<'a> System<'a> for Sys { } // Figure out if we're in water - physics_state.in_fluid = collision_iter(pos.0, &terrain, &|block| block.is_fluid(), near_iter.clone(), radius, z_min..z_max) - .max_by_key(|block_aabb| (block_aabb.max.z * 100.0) as i32) - .map(|block_aabb| block_aabb.max.z - pos.0.z); + physics_state.in_fluid = collision_iter( + pos.0, + &terrain, + &|block| block.is_fluid(), + near_iter.clone(), + radius, + z_min..z_max, + ) + .max_by_key(|block_aabb| (block_aabb.max.z * 100.0) as i32) + .map(|block_aabb| block_aabb.max.z - pos.0.z); }, Collider::Point => { let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta).ignore_error().cast(); diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index d523667d1d..e228381c36 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -209,12 +209,13 @@ impl BlockKind { } pub fn get_glow(&self) -> Option { - match self { - // TODO: When we have proper volumetric lighting - //BlockKind::StreetLamp | BlockKind::StreetLampTall => Some(20), - //BlockKind::Velorite | BlockKind::VeloriteFrag => Some(10), - _ => None, - } + // TODO: When we have proper volumetric lighting + // match self { + // BlockKind::StreetLamp | BlockKind::StreetLampTall => Some(20), + // BlockKind::Velorite | BlockKind::VeloriteFrag => Some(10), + // _ => None, + // } + None } pub fn is_opaque(&self) -> bool { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 310c846308..f3d126cf35 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -2,9 +2,10 @@ use crate::{client::Client, Server, SpawnPoint, StateExt}; use common::{ assets, comp::{ - self, object, Alignment, Body, Damage, DamageSource, Group, - HealthChange, HealthSource, Player, Pos, Stats, + self, object, Alignment, Body, Damage, DamageSource, Group, HealthChange, HealthSource, + Player, Pos, Stats, }, + lottery::Lottery, msg::{PlayerListUpdate, ServerMsg}, outcome::Outcome, state::BlockChange, @@ -12,7 +13,6 @@ use common::{ sys::combat::BLOCK_ANGLE, terrain::{Block, TerrainGrid}, vol::{ReadVol, Vox}, - lottery::Lottery, }; use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt}; use tracing::error; diff --git a/voxygen/src/anim/src/character/beta.rs b/voxygen/src/anim/src/character/beta.rs index bfa3466638..3facc7a2e0 100644 --- a/voxygen/src/anim/src/character/beta.rs +++ b/voxygen/src/anim/src/character/beta.rs @@ -12,7 +12,6 @@ impl Animation for BetaAnimation { const UPDATE_FN: &'static [u8] = b"character_beta\0"; #[cfg_attr(feature = "be-dyn-lib", export_name = "character_beta")] - #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 fn update_skeleton_inner( skeleton: &Self::Skeleton, (active_tool_kind, second_tool_kind, _velocity, _global_time): Self::Dependency, diff --git a/voxygen/src/anim/src/character/spin.rs b/voxygen/src/anim/src/character/spin.rs index 5c8efb2c4e..36aa169bab 100644 --- a/voxygen/src/anim/src/character/spin.rs +++ b/voxygen/src/anim/src/character/spin.rs @@ -16,7 +16,6 @@ impl Animation for SpinAnimation { const UPDATE_FN: &'static [u8] = b"character_spin\0"; #[cfg_attr(feature = "be-dyn-lib", export_name = "character_spin")] - #[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587 fn update_skeleton_inner( skeleton: &Self::Skeleton, (active_tool_kind, second_tool_kind, _global_time): Self::Dependency, diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index aafde9bc41..12081df5f7 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -581,7 +581,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::character::StandAnimation::update_skeleton( @@ -949,7 +949,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => { @@ -1047,7 +1047,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => { @@ -1143,7 +1143,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => { @@ -1237,7 +1237,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton( @@ -1329,7 +1329,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::fish_medium::IdleAnimation::update_skeleton( @@ -1404,7 +1404,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::dragon::IdleAnimation::update_skeleton( @@ -1478,7 +1478,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::critter::IdleAnimation::update_skeleton( @@ -1553,7 +1553,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::bird_small::IdleAnimation::update_skeleton( @@ -1628,7 +1628,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::fish_small::IdleAnimation::update_skeleton( @@ -1703,7 +1703,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::biped_large::IdleAnimation::update_skeleton( @@ -1795,7 +1795,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid.is_some(), // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::golem::IdleAnimation::update_skeleton( diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 5b12bb657c..7f98b5acb5 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -3,7 +3,7 @@ mod natural; use crate::{ column::{ColumnGen, ColumnSample}, util::{RandomField, Sampler, SmallCache}, - Index, CONFIG, + Index, }; use common::{ terrain::{structure::StructureBlock, Block, BlockKind, Structure}, @@ -31,7 +31,9 @@ impl<'a> BlockGen<'a> { wpos: Vec2, index: &Index, ) -> Option<&'b ColumnSample<'a>> { - cache.get(wpos, |wpos| column_gen.get((wpos, index))).as_ref() + cache + .get(wpos, |wpos| column_gen.get((wpos, index))) + .as_ref() } fn get_cliff_height( @@ -48,7 +50,7 @@ impl<'a> BlockGen<'a> { |max_height, (cliff_pos, seed)| match Self::sample_column( column_gen, cache, - Vec2::from(*cliff_pos), + *cliff_pos, index, ) { Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => { @@ -164,14 +166,14 @@ impl<'a> BlockGen<'a> { //tree_density, //forest_kind, //close_structures, - marble, - marble_small, + // marble, + // marble_small, rock, //cliffs, cliff_hill, close_cliffs, - temp, - humidity, + // temp, + // humidity, stone_col, .. } = sample; @@ -181,7 +183,7 @@ impl<'a> BlockGen<'a> { let wposf = wpos.map(|e| e as f64); let (block, _height) = if !only_structures { - let (_definitely_underground, height, on_cliff, basement_height, water_height) = + let (_definitely_underground, height, _on_cliff, basement_height, water_height) = if (wposf.z as f32) < alt - 64.0 * chaos { // Shortcut warping (true, alt, false, basement, water_level) @@ -276,83 +278,84 @@ impl<'a> BlockGen<'a> { }, col.map(|e| (e * 255.0) as u8), )) - } else if (wposf.z as f32) < height + 0.9 - && temp < CONFIG.desert_temp - && (wposf.z as f32 > water_height + 3.0) - && marble > 0.6 - && marble_small > 0.55 - && (marble * 3173.7).fract() < 0.6 - && humidity > CONFIG.desert_hum - && false - { - let treasures = [BlockKind::Chest, BlockKind::Velorite]; + // } else if (wposf.z as f32) < height + 0.9 + // && temp < CONFIG.desert_temp + // && (wposf.z as f32 > water_height + 3.0) + // && marble > 0.6 + // && marble_small > 0.55 + // && (marble * 3173.7).fract() < 0.6 + // && humidity > CONFIG.desert_hum + // && false + // { + // let treasures = [BlockKind::Chest, BlockKind::Velorite]; - let flowers = [ - BlockKind::BlueFlower, - BlockKind::PinkFlower, - BlockKind::PurpleFlower, - BlockKind::RedFlower, - BlockKind::WhiteFlower, - BlockKind::YellowFlower, - BlockKind::Sunflower, - BlockKind::Mushroom, //TODO: Better spawnrules - BlockKind::LeafyPlant, - BlockKind::Blueberry, - BlockKind::LingonBerry, - BlockKind::Fern, - /*BlockKind::Twigs, // TODO: Better spawnrules - *BlockKind::Stones, // TODO: Better spawnrules - *BlockKind::ShinyGem, // TODO: Better spawnrules */ - ]; - let grasses = [ - BlockKind::LongGrass, - BlockKind::MediumGrass, - BlockKind::ShortGrass, - ]; + // let flowers = [ + // BlockKind::BlueFlower, + // BlockKind::PinkFlower, + // BlockKind::PurpleFlower, + // BlockKind::RedFlower, + // BlockKind::WhiteFlower, + // BlockKind::YellowFlower, + // BlockKind::Sunflower, + // BlockKind::Mushroom, //TODO: Better spawnrules + // BlockKind::LeafyPlant, + // BlockKind::Blueberry, + // BlockKind::LingonBerry, + // BlockKind::Fern, + // /*BlockKind::Twigs, // TODO: Better spawnrules + // *BlockKind::Stones, // TODO: Better spawnrules + // *BlockKind::ShinyGem, // TODO: Better spawnrules */ + // ]; + // let grasses = [ + // BlockKind::LongGrass, + // BlockKind::MediumGrass, + // BlockKind::ShortGrass, + // ]; - Some(Block::new( - if on_cliff && (height * 1271.0).fract() < 0.015 { - treasures[(height * 731.3) as usize % treasures.len()] - } else if (height * 1271.0).fract() < 0.1 { - flowers[(height * 0.2) as usize % flowers.len()] - } else { - grasses[(height * 103.3) as usize % grasses.len()] - }, - Rgb::broadcast(0), - )) - } else if (wposf.z as f32) < height + 0.9 - && temp > CONFIG.desert_temp - && (marble * 4423.5).fract() < 0.0005 - && false - { - let large_cacti = [ - BlockKind::LargeCactus, - BlockKind::MedFlatCactus, - BlockKind::Welwitch, - ]; + // Some(Block::new( + // if on_cliff && (height * 1271.0).fract() < 0.015 { + // treasures[(height * 731.3) as usize % treasures.len()] + // } else if (height * 1271.0).fract() < 0.1 { + // flowers[(height * 0.2) as usize % flowers.len()] + // } else { + // grasses[(height * 103.3) as usize % grasses.len()] + // }, + // Rgb::broadcast(0), + // )) + // } else if (wposf.z as f32) < height + 0.9 + // && temp > CONFIG.desert_temp + // && (marble * 4423.5).fract() < 0.0005 + // && false + // { + // let large_cacti = [ + // BlockKind::LargeCactus, + // BlockKind::MedFlatCactus, + // BlockKind::Welwitch, + // ]; - let small_cacti = [ - BlockKind::BarrelCactus, - BlockKind::RoundCactus, - BlockKind::ShortCactus, - BlockKind::ShortFlatCactus, - BlockKind::DeadBush, - ]; + // let small_cacti = [ + // BlockKind::BarrelCactus, + // BlockKind::RoundCactus, + // BlockKind::ShortCactus, + // BlockKind::ShortFlatCactus, + // BlockKind::DeadBush, + // ]; - Some(Block::new( - if (height * 1271.0).fract() < 0.5 { - large_cacti[(height * 0.2) as usize % large_cacti.len()] - } else { - small_cacti[(height * 0.3) as usize % small_cacti.len()] - }, - Rgb::broadcast(0), - )) + // Some(Block::new( + // if (height * 1271.0).fract() < 0.5 { + // large_cacti[(height * 0.2) as usize % large_cacti.len()] + // } else { + // small_cacti[(height * 0.3) as usize % small_cacti.len()] + // }, + // Rgb::broadcast(0), + // )) } else { None } .or_else(|| { // Rocks if (height + 2.5 - wposf.z as f32).div(7.5).abs().powf(2.0) < rock { + #[allow(clippy::identity_op)] let field0 = RandomField::new(world.seed + 0); let field1 = RandomField::new(world.seed + 1); let field2 = RandomField::new(world.seed + 2); @@ -536,7 +539,7 @@ pub fn block_from_structure( structure_seed: u32, sample: &ColumnSample, ) -> Option { - let field = RandomField::new(structure_seed + 0); + let field = RandomField::new(structure_seed); let lerp = ((field.get(Vec3::from(structure_pos)).rem_euclid(256)) as f32 / 255.0) * 0.85 + ((field.get(pos + std::i32::MAX / 2).rem_euclid(256)) as f32 / 255.0) * 0.15; diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index f1395ab1b4..fcb599832e 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -681,7 +681,6 @@ fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2) -> 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, @@ -716,7 +715,7 @@ fn find_site_loc( loc = ctx.sim.get(test_loc).and_then(|c| { Some( c.downhill? - .map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| { + .map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { e / (sz as i32) }), ) diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 11b824b027..92746650b5 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -179,7 +179,6 @@ where #[allow(clippy::if_same_then_else)] // TODO: Pending review in #587 #[allow(clippy::nonminimal_bool)] // TODO: Pending review in #587 #[allow(clippy::single_match)] // TODO: Pending review in #587 - #[allow(clippy::bind_instead_of_map)] // TODO: Pending review in #587 fn get(&self, (wpos, index): Self::Index) -> Option> { let wposf = wpos.map(|e| e as f64); let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32); diff --git a/world/src/index.rs b/world/src/index.rs index 2b0e829c4f..ed6a6ce157 100644 --- a/world/src/index.rs +++ b/world/src/index.rs @@ -26,6 +26,7 @@ pub struct Noise { } impl Noise { + #[allow(clippy::identity_op)] fn new(seed: u32) -> Self { Self { cave_nz: SuperSimplex::new().set_seed(seed + 0), diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 6aeda93976..23c057d93e 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -31,6 +31,7 @@ pub fn apply_scatter_to<'a>( chunk: &SimChunk, ) { use BlockKind::*; + #[allow(clippy::type_complexity)] let scatter: &[(_, bool, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[ // (density, Option<(wavelen, threshold)>) (BlueFlower, false, |c| { @@ -103,8 +104,7 @@ pub fn apply_scatter_to<'a>( .enumerate() .find_map(|(i, (bk, is_underwater, f))| { let (density, patch) = f(chunk); - if density <= 0.0 - || patch + let is_patch = patch .map(|(wavelen, threshold)| { index.noise.scatter_nz.get( wpos2d @@ -112,7 +112,9 @@ pub fn apply_scatter_to<'a>( .into_array(), ) < threshold as f64 }) - .unwrap_or(false) + .unwrap_or(false); + if density <= 0.0 + || is_patch || !RandomField::new(i as u32) .chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) || underwater != *is_underwater @@ -398,6 +400,7 @@ pub fn apply_caves_supplement<'a>( comp::critter::Body::random_with(rng, &species).into() }, 4 => { + #[allow(clippy::match_single_binding)] let species = match rng.gen_range(0, 1) { _ => comp::golem::Species::StoneGolem, }; diff --git a/world/src/sim/way.rs b/world/src/sim/way.rs index bc573a4b17..350e522d63 100644 --- a/world/src/sim/way.rs +++ b/world/src/sim/way.rs @@ -2,10 +2,10 @@ use vek::*; #[derive(Copy, Clone, Debug, Default)] pub struct Way { - /// Offset from chunk center in blocks (no more than half chunk width) - pub offset: Vec2, - /// Neighbor connections, one bit each - pub neighbors: u8, + /// Offset from chunk center in blocks (no more than half chunk width) + pub offset: Vec2, + /// Neighbor connections, one bit each + pub neighbors: u8, } impl Way { @@ -20,23 +20,23 @@ pub struct Path { } impl Default for Path { - fn default() -> Self { - Self { width: 5.0 } - } + fn default() -> Self { Self { width: 5.0 } } } impl Lerp for Path { type Output = Self; fn lerp_unclamped(from: Self, to: Self, factor: f32) -> Self::Output { - Self { width: Lerp::lerp(from.width, to.width, factor) } + Self { + width: Lerp::lerp(from.width, to.width, factor), + } } } impl Path { /// Return the number of blocks of headspace required at the given path /// distance - /// TODO: make this generic over width + /// TODO: make this generic over width pub fn head_space(&self, dist: f32) -> i32 { (8 - (dist * 0.25).powf(6.0).round() as i32).max(1) } @@ -48,7 +48,7 @@ impl Path { #[derive(Copy, Clone, Debug)] pub struct Cave { pub width: f32, // Actually radius - pub alt: f32, // Actually radius + pub alt: f32, // Actually radius } impl Default for Cave { diff --git a/world/src/sim2/mod.rs b/world/src/sim2/mod.rs index ffccc9516f..0a5dc35012 100644 --- a/world/src/sim2/mod.rs +++ b/world/src/sim2/mod.rs @@ -15,32 +15,39 @@ const YEAR: f32 = 12.0 * MONTH; const TICK_PERIOD: f32 = 3.0 * MONTH; // 3 months const HISTORY_DAYS: f32 = 500.0 * YEAR; // 500 years +const GENERATE_CSV: bool = false; + pub fn simulate(index: &mut Index, world: &mut WorldSim) { use std::io::Write; - let mut f = std::fs::File::create("economy.csv").unwrap(); - write!(f, "Population,").unwrap(); - for g in Good::list() { - write!(f, "{:?} Value,", g).unwrap(); - } - for g in Good::list() { - write!(f, "{:?} LaborVal,", g).unwrap(); - } - for g in Good::list() { - write!(f, "{:?} Stock,", g).unwrap(); - } - for g in Good::list() { - write!(f, "{:?} Surplus,", g).unwrap(); - } - for l in Labor::list() { - write!(f, "{:?} Labor,", l).unwrap(); - } - for l in Labor::list() { - write!(f, "{:?} Productivity,", l).unwrap(); - } - for l in Labor::list() { - write!(f, "{:?} Yields,", l).unwrap(); - } - writeln!(f, "").unwrap(); + let mut f = if GENERATE_CSV { + let mut f = std::fs::File::create("economy.csv").unwrap(); + write!(f, "Population,").unwrap(); + for g in Good::list() { + write!(f, "{:?} Value,", g).unwrap(); + } + for g in Good::list() { + write!(f, "{:?} LaborVal,", g).unwrap(); + } + for g in Good::list() { + write!(f, "{:?} Stock,", g).unwrap(); + } + for g in Good::list() { + write!(f, "{:?} Surplus,", g).unwrap(); + } + for l in Labor::list() { + write!(f, "{:?} Labor,", l).unwrap(); + } + for l in Labor::list() { + write!(f, "{:?} Productivity,", l).unwrap(); + } + for l in Labor::list() { + write!(f, "{:?} Yields,", l).unwrap(); + } + writeln!(f).unwrap(); + Some(f) + } else { + None + }; for i in 0..(HISTORY_DAYS / TICK_PERIOD) as i32 { if (index.time / YEAR) as i32 % 50 == 0 && (index.time % YEAR) as i32 == 0 { @@ -49,31 +56,33 @@ pub fn simulate(index: &mut Index, world: &mut WorldSim) { tick(index, world, TICK_PERIOD); - if i % 5 == 0 { - let site = index.sites.values().next().unwrap(); - write!(f, "{},", site.economy.pop).unwrap(); - for g in Good::list() { - write!(f, "{:?},", site.economy.values[*g].unwrap_or(-1.0)).unwrap(); + if let Some(f) = f.as_mut() { + if i % 5 == 0 { + let site = index.sites.values().next().unwrap(); + write!(f, "{},", site.economy.pop).unwrap(); + for g in Good::list() { + write!(f, "{:?},", site.economy.values[*g].unwrap_or(-1.0)).unwrap(); + } + for g in Good::list() { + write!(f, "{:?},", site.economy.labor_values[*g].unwrap_or(-1.0)).unwrap(); + } + for g in Good::list() { + write!(f, "{:?},", site.economy.stocks[*g]).unwrap(); + } + for g in Good::list() { + write!(f, "{:?},", site.economy.marginal_surplus[*g]).unwrap(); + } + for l in Labor::list() { + write!(f, "{:?},", site.economy.labors[*l] * site.economy.pop).unwrap(); + } + 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(); } - for g in Good::list() { - write!(f, "{:?},", site.economy.labor_values[*g].unwrap_or(-1.0)).unwrap(); - } - for g in Good::list() { - write!(f, "{:?},", site.economy.stocks[*g]).unwrap(); - } - for g in Good::list() { - write!(f, "{:?},", site.economy.marginal_surplus[*g]).unwrap(); - } - for l in Labor::list() { - write!(f, "{:?},", site.economy.labors[*l] * site.economy.pop).unwrap(); - } - 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(); } } } @@ -204,8 +213,7 @@ pub fn tick_site_economy(index: &mut Index, site: Id, dt: f32) { // What quantity is this order requesting? let _quantity = *amount * scale; // What proportion of this order is the economy able to satisfy? - let satisfaction = (stocks_before[*good] / demand[*good]).min(1.0); - satisfaction + (stocks_before[*good] / demand[*good]).min(1.0) }) .min_by(|a, b| a.partial_cmp(b).unwrap()) .unwrap_or_else(|| panic!("Industry {:?} requires at least one input order", labor)); diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index f95adc2cf8..e2d8bdc102 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -479,7 +479,8 @@ impl Floor { .reduce_and() }) .unwrap_or(false); - let boss_spawn_tile = boss_spawn_tile + if boss_tile_is_pillar { 1 } else { 0 }; + let boss_spawn_tile = + boss_spawn_tile + if boss_tile_is_pillar { 1 } else { 0 }; if tile_pos == boss_spawn_tile && tile_wcenter.xy() == wpos2d { let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32)) @@ -641,7 +642,8 @@ impl Floor { move |z| match self.tiles.get(tile_pos) { Some(Tile::Solid) => BlockMask::nothing(), Some(Tile::Tunnel) => { - if dist_to_wall >= wall_thickness && (z as f32) < tunnel_height * (1.0 - tunnel_dist.powf(4.0)) + if dist_to_wall >= wall_thickness + && (z as f32) < tunnel_height * (1.0 - tunnel_dist.powf(4.0)) { if z == 0 { floor_sprite } else { empty } } else { diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index 97c5a0186d..fc0f3365be 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -294,10 +294,16 @@ impl Archetype for House { let edge_ori = if bound_offset.x.abs() > bound_offset.y.abs() { if center_offset.x > 0 { 6 } else { 2 } + } else if (center_offset.y > 0) ^ (ori == Ori::East) { + 0 } else { - if (center_offset.y > 0) ^ (ori == Ori::East) { 0 } else { 4 } + 4 + }; + let edge_ori = if ori == Ori::East { + (edge_ori + 2) % 8 + } else { + edge_ori }; - let edge_ori = if ori == Ori::East { (edge_ori + 2) % 8 } else { edge_ori }; if let Pillar::Chimney(chimney_height) = attr.pillar { let chimney_top = roof_top + chimney_height; @@ -491,9 +497,16 @@ impl Archetype for House { } else if dist == width - 1 && center_offset.sum() % 2 == 0 && profile.y == floor_height + 1 - && self.noise.chance(Vec3::new(center_offset.x, center_offset.y, z), 0.2) + && self + .noise + .chance(Vec3::new(center_offset.x, center_offset.y, z), 0.2) { - let furniture = match self.noise.get(Vec3::new(center_offset.x, center_offset.y, z + 100)) % 11 { + let furniture = match self.noise.get(Vec3::new( + center_offset.x, + center_offset.y, + z + 100, + )) % 11 + { 0 => BlockKind::Planter, 1 => BlockKind::ChairSingle, 2 => BlockKind::ChairDouble, @@ -506,7 +519,10 @@ impl Archetype for House { _ => BlockKind::Pot, }; - return Some(BlockMask::new(Block::new(furniture, Rgb::new(edge_ori, 0, 0)), internal_layer)); + return Some(BlockMask::new( + Block::new(furniture, Rgb::new(edge_ori, 0, 0)), + internal_layer, + )); } else { return Some(internal); } @@ -516,16 +532,26 @@ impl Archetype for House { if dist == width + 1 && center_offset.map(|e| e.abs()).reduce_min() == 0 && profile.y == floor_height + 3 - && self.noise.chance(Vec3::new(center_offset.x, center_offset.y, z), 0.35) + && self + .noise + .chance(Vec3::new(center_offset.x, center_offset.y, z), 0.35) && attr.storey_fill.has_lower() { - let ornament = match self.noise.get(Vec3::new(center_offset.x, center_offset.y, z + 100)) % 4 { - 0 => BlockKind::HangingSign, - 1 | 2 | 3 => BlockKind::HangingBasket, - _ => BlockKind::DungeonWallDecor, - }; + let ornament = + match self + .noise + .get(Vec3::new(center_offset.x, center_offset.y, z + 100)) + % 4 + { + 0 => BlockKind::HangingSign, + 1 | 2 | 3 => BlockKind::HangingBasket, + _ => BlockKind::DungeonWallDecor, + }; - return Some(BlockMask::new(Block::new(ornament, Rgb::new((edge_ori + 4) % 8, 0, 0)), internal_layer)); + Some(BlockMask::new( + Block::new(ornament, Rgb::new((edge_ori + 4) % 8, 0, 0)), + internal_layer, + )) } else { None } diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 178cfaf4a6..2028fb75d0 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -208,9 +208,14 @@ impl Archetype for Keep { if profile.y > roof_height && (min_dist < rampart_width - 1 || (attr.is_tower && min_dist < rampart_width)) { - if attr.is_tower && attr.flag && center_offset == Vec2::zero() && profile.y < roof_height + 16 { + if attr.is_tower + && attr.flag + && center_offset == Vec2::zero() + && profile.y < roof_height + 16 + { pole - } else if attr.is_tower && attr.flag + } else if attr.is_tower + && attr.flag && center_offset.x == 0 && center_offset.y > 0 && center_offset.y < 8 From e3cab272c3c003d4afe31be731d20d123b5960e2 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Wed, 12 Aug 2020 18:29:51 +0100 Subject: [PATCH 33/33] Fixed floating sprites in towns, clippy fixes --- common/src/lottery.rs | 9 +- server/src/cmd.rs | 2 +- voxygen/src/anim/src/character/beta.rs | 101 +++++++++--------- voxygen/src/anim/src/character/spin.rs | 96 ++++++++--------- voxygen/src/anim/src/lib.rs | 1 + .../audio/sfx/event_mapper/movement/tests.rs | 22 ++-- world/examples/view.rs | 2 +- world/examples/water.rs | 4 +- world/src/block/mod.rs | 21 ++-- world/src/civ/mod.rs | 4 +- world/src/column/mod.rs | 38 +++---- world/src/layer/mod.rs | 16 +-- world/src/site/dungeon/mod.rs | 2 +- .../site/settlement/building/archetype/mod.rs | 2 + world/src/site/settlement/mod.rs | 8 +- world/src/util/random.rs | 14 +-- 16 files changed, 164 insertions(+), 178 deletions(-) diff --git a/common/src/lottery.rs b/common/src/lottery.rs index 0a7a6698a1..6e4546f88e 100644 --- a/common/src/lottery.rs +++ b/common/src/lottery.rs @@ -47,14 +47,11 @@ impl Lottery { #[cfg(test)] mod tests { - use crate::{ - assets, - comp::inventory::item::{lottery::Lottery, Item}, - }; + use super::*; + use crate::{assets, comp::Item}; #[test] fn test_loot_table() { - let test = assets::load_expect::>("common.loot_table"); - let test = test; + let test = assets::load_expect::>("common.loot_table"); for (_, item) in test.iter() { assert!( diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 9abd4f49a4..61e8eed5ed 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -209,7 +209,7 @@ fn handle_make_block( } else { server.notify_client( client, - ChatType::CommandError.server_msg(String::from(action.help_string())), + ChatType::CommandError.server_msg(action.help_string()), ); } } diff --git a/voxygen/src/anim/src/character/beta.rs b/voxygen/src/anim/src/character/beta.rs index 3facc7a2e0..720ce56bf3 100644 --- a/voxygen/src/anim/src/character/beta.rs +++ b/voxygen/src/anim/src/character/beta.rs @@ -41,65 +41,60 @@ impl Animation for BetaAnimation { .sqrt()) * ((anim_time as f32 * lab as f32 * 14.0).sin()); - match active_tool_kind { - //TODO: Inventory - Some(ToolKind::Axe(_)) - | Some(ToolKind::Hammer(_)) - | Some(ToolKind::Sword(_)) - | Some(ToolKind::Dagger(_)) => { - //INTENTION: SWORD - next.head.offset = - Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); - next.head.ori = Quaternion::rotation_z(slow * -0.18) - * Quaternion::rotation_x(-0.1 + slow * -0.28) - * Quaternion::rotation_y(0.2 + slow * 0.18); - next.head.scale = Vec3::one() * skeleton_attr.head_scale; + if let Some( + ToolKind::Axe(_) | ToolKind::Hammer(_) | ToolKind::Sword(_) | ToolKind::Dagger(_), + ) = active_tool_kind + { + //INTENTION: SWORD + next.head.offset = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1); + next.head.ori = Quaternion::rotation_z(slow * -0.18) + * Quaternion::rotation_x(-0.1 + slow * -0.28) + * Quaternion::rotation_y(0.2 + slow * 0.18); + next.head.scale = Vec3::one() * skeleton_attr.head_scale; - next.chest.offset = Vec3::new(0.0 + foot * 2.0, 0.0, 7.0); - next.chest.ori = Quaternion::rotation_z(slow * 0.2) - * Quaternion::rotation_x(slow * 0.2) - * Quaternion::rotation_y(slow * -0.1); + next.chest.offset = Vec3::new(0.0 + foot * 2.0, 0.0, 7.0); + next.chest.ori = Quaternion::rotation_z(slow * 0.2) + * Quaternion::rotation_x(slow * 0.2) + * Quaternion::rotation_y(slow * -0.1); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = Quaternion::rotation_z(slow * 0.1) - * Quaternion::rotation_x(slow * 0.1) - * Quaternion::rotation_y(slow * -0.04); + next.belt.offset = Vec3::new(0.0, 0.0, -2.0); + next.belt.ori = Quaternion::rotation_z(slow * 0.1) + * Quaternion::rotation_x(slow * 0.1) + * Quaternion::rotation_y(slow * -0.04); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.shorts.ori = Quaternion::rotation_z(slow * 0.1) - * Quaternion::rotation_x(slow * 0.1) - * Quaternion::rotation_y(slow * -0.05); + next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); + next.shorts.ori = Quaternion::rotation_z(slow * 0.1) + * Quaternion::rotation_x(slow * 0.1) + * Quaternion::rotation_y(slow * -0.05); - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); - next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); - next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3); + next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.scale = Vec3::one() * 1.04; + next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.scale = Vec3::one() * 1.05; + next.main.offset = Vec3::new(0.0, 6.0, -1.0); + next.main.ori = Quaternion::rotation_x(-0.3); - next.control.offset = Vec3::new(-8.0 + slow * 1.5, 1.5 + slow * 1.0, 0.0); - next.control.ori = Quaternion::rotation_x(-1.4) - * Quaternion::rotation_y(slow * 2.0 + 0.7) - * Quaternion::rotation_z(1.7 - slow * 0.4 + fast * 0.6); - next.control.scale = Vec3::one(); - next.l_foot.offset = Vec3::new( - -skeleton_attr.foot.0, - footquick * -9.5, - skeleton_attr.foot.2, - ); - next.l_foot.ori = Quaternion::rotation_x(footquick * 0.3) - * Quaternion::rotation_y(footquick * -0.6); + next.control.offset = Vec3::new(-8.0 + slow * 1.5, 1.5 + slow * 1.0, 0.0); + next.control.ori = Quaternion::rotation_x(-1.4) + * Quaternion::rotation_y(slow * 2.0 + 0.7) + * Quaternion::rotation_z(1.7 - slow * 0.4 + fast * 0.6); + next.control.scale = Vec3::one(); + next.l_foot.offset = Vec3::new( + -skeleton_attr.foot.0, + footquick * -9.5, + skeleton_attr.foot.2, + ); + next.l_foot.ori = + Quaternion::rotation_x(footquick * 0.3) * Quaternion::rotation_y(footquick * -0.6); - next.r_foot.offset = - Vec3::new(skeleton_attr.foot.0, footquick * 9.5, skeleton_attr.foot.2); - next.r_foot.ori = Quaternion::rotation_x(footquick * -0.3) - * Quaternion::rotation_y(footquick * 0.2); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - }, - _ => {}, + next.r_foot.offset = + Vec3::new(skeleton_attr.foot.0, footquick * 9.5, skeleton_attr.foot.2); + next.r_foot.ori = + Quaternion::rotation_x(footquick * -0.3) * Quaternion::rotation_y(footquick * 0.2); + next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } next.l_shoulder.offset = Vec3::new( diff --git a/voxygen/src/anim/src/character/spin.rs b/voxygen/src/anim/src/character/spin.rs index 36aa169bab..52fa49eef5 100644 --- a/voxygen/src/anim/src/character/spin.rs +++ b/voxygen/src/anim/src/character/spin.rs @@ -38,60 +38,56 @@ impl Animation for SpinAnimation { let spin = (anim_time as f32 * 2.8 * lab as f32).sin(); let spinhalf = (anim_time as f32 * 1.4 * lab as f32).sin(); - match active_tool_kind { - //TODO: Inventory - Some(ToolKind::Axe(_)) - | Some(ToolKind::Hammer(_)) - | Some(ToolKind::Sword(_)) - | Some(ToolKind::Dagger(_)) => { - //INTENTION: SWORD - next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); - next.l_hand.ori = Quaternion::rotation_x(1.27); - next.l_hand.scale = Vec3::one() * 1.04; - next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); - next.r_hand.ori = Quaternion::rotation_x(1.27); - next.r_hand.scale = Vec3::one() * 1.05; - next.main.offset = Vec3::new(0.0, 6.0, -1.0); - next.main.ori = Quaternion::rotation_x(-0.3) - * Quaternion::rotation_y(0.0) - * Quaternion::rotation_z(0.0); - next.main.scale = Vec3::one(); + if let Some( + ToolKind::Axe(_) | ToolKind::Hammer(_) | ToolKind::Sword(_) | ToolKind::Dagger(_), + ) = active_tool_kind + { + //INTENTION: SWORD + next.l_hand.offset = Vec3::new(-0.75, -1.0, -2.5); + next.l_hand.ori = Quaternion::rotation_x(1.27); + next.l_hand.scale = Vec3::one() * 1.04; + next.r_hand.offset = Vec3::new(0.75, -1.5, -5.5); + next.r_hand.ori = Quaternion::rotation_x(1.27); + next.r_hand.scale = Vec3::one() * 1.05; + next.main.offset = Vec3::new(0.0, 6.0, -1.0); + next.main.ori = Quaternion::rotation_x(-0.3) + * Quaternion::rotation_y(0.0) + * Quaternion::rotation_z(0.0); + next.main.scale = Vec3::one(); - next.control.offset = Vec3::new(-4.5 + spinhalf * 4.0, 11.0, 8.0); - next.control.ori = Quaternion::rotation_x(-1.7) - * Quaternion::rotation_y(0.2 + spin * -2.0) - * Quaternion::rotation_z(1.4 + spin * 0.1); - next.control.scale = Vec3::one(); - next.head.offset = Vec3::new( - 0.0, - -2.0 + skeleton_attr.head.0 + spin * -0.8, - skeleton_attr.head.1, - ); - next.head.ori = Quaternion::rotation_z(spin * -0.25) - * Quaternion::rotation_x(0.0 + spin * -0.1) - * Quaternion::rotation_y(spin * -0.2); - next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); - next.chest.ori = Quaternion::rotation_z(spin * 0.1) - * Quaternion::rotation_x(0.0 + spin * 0.1) - * Quaternion::rotation_y(decel * -0.2); - next.chest.scale = Vec3::one(); + next.control.offset = Vec3::new(-4.5 + spinhalf * 4.0, 11.0, 8.0); + next.control.ori = Quaternion::rotation_x(-1.7) + * Quaternion::rotation_y(0.2 + spin * -2.0) + * Quaternion::rotation_z(1.4 + spin * 0.1); + next.control.scale = Vec3::one(); + next.head.offset = Vec3::new( + 0.0, + -2.0 + skeleton_attr.head.0 + spin * -0.8, + skeleton_attr.head.1, + ); + next.head.ori = Quaternion::rotation_z(spin * -0.25) + * Quaternion::rotation_x(0.0 + spin * -0.1) + * Quaternion::rotation_y(spin * -0.2); + next.chest.offset = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.ori = Quaternion::rotation_z(spin * 0.1) + * Quaternion::rotation_x(0.0 + spin * 0.1) + * Quaternion::rotation_y(decel * -0.2); + next.chest.scale = Vec3::one(); - next.belt.offset = Vec3::new(0.0, 0.0, -2.0); - next.belt.ori = next.chest.ori * -0.1; - next.belt.scale = Vec3::one(); + next.belt.offset = Vec3::new(0.0, 0.0, -2.0); + next.belt.ori = next.chest.ori * -0.1; + next.belt.scale = Vec3::one(); - next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); - next.belt.ori = next.chest.ori * -0.08; - next.shorts.scale = Vec3::one(); - next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_z((spin * 7.0).max(0.3)) - * Quaternion::rotation_x(0.0) - * Quaternion::rotation_y(0.0); - next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; - }, - - _ => {}, + next.shorts.offset = Vec3::new(0.0, 0.0, -5.0); + next.belt.ori = next.chest.ori * -0.08; + next.shorts.scale = Vec3::one(); + next.torso.offset = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.ori = Quaternion::rotation_z((spin * 7.0).max(0.3)) + * Quaternion::rotation_x(0.0) + * Quaternion::rotation_y(0.0); + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; } + next.l_foot.offset = Vec3::new(-skeleton_attr.foot.0, foot * 1.0, skeleton_attr.foot.2); next.l_foot.ori = Quaternion::rotation_x(foot * -1.2); next.l_foot.scale = Vec3::one(); diff --git a/voxygen/src/anim/src/lib.rs b/voxygen/src/anim/src/lib.rs index b697eb89f4..8fa5542505 100644 --- a/voxygen/src/anim/src/lib.rs +++ b/voxygen/src/anim/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(or_patterns)] #[cfg(all(feature = "be-dyn-lib", feature = "use-dyn-lib"))] compile_error!("Can't use both \"be-dyn-lib\" and \"use-dyn-lib\" features at once"); diff --git a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs index f407231ab6..6a004489d3 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs @@ -85,7 +85,7 @@ fn maps_idle() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Idle, @@ -107,7 +107,7 @@ fn maps_run_with_sufficient_velocity() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Idle, @@ -129,7 +129,7 @@ fn does_not_map_run_with_insufficient_velocity() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Idle, @@ -151,7 +151,7 @@ fn does_not_map_run_with_sufficient_velocity_but_not_on_ground() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Idle, @@ -176,7 +176,7 @@ fn maps_roll() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Run, @@ -198,7 +198,7 @@ fn maps_land_on_ground_to_run() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Idle, @@ -220,7 +220,7 @@ fn maps_glider_open() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Jump, @@ -242,7 +242,7 @@ fn maps_glide() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Glide, @@ -264,7 +264,7 @@ fn maps_glider_close_when_closing_mid_flight() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Glide, @@ -287,7 +287,7 @@ fn maps_glider_close_when_landing() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, &PreviousEntityState { event: SfxEvent::Glide, @@ -308,7 +308,7 @@ fn maps_quadrupeds_running() { on_ceiling: false, on_wall: None, touch_entity: None, - in_fluid: false, + in_fluid: None, }, Vec3::new(0.5, 0.8, 0.0), ); diff --git a/world/examples/view.rs b/world/examples/view.rs index f80bbca0de..b137e8abbe 100644 --- a/world/examples/view.rs +++ b/world/examples/view.rs @@ -28,7 +28,7 @@ fn main() { let pos = focus + Vec2::new(i as i32, j as i32) * scale; let (alt, place) = sampler - .get(pos) + .get((pos, world.index())) .map(|sample| { ( sample.alt.sub(64.0).add(gain).mul(0.7).max(0.0).min(255.0) as u8, diff --git a/world/examples/water.rs b/world/examples/water.rs index 1b1c47354f..36303763c5 100644 --- a/world/examples/water.rs +++ b/world/examples/water.rs @@ -85,7 +85,7 @@ fn main() { lakes, oceans, quads, - } = config.generate(sampler, |pos, (r, g, b, a)| { + } = config.generate(sampler, world.index(), |pos, (r, g, b, a)| { let i = pos.x; let j = pos.y; buf[j * W + i] = u32::from_le_bytes([b, g, r, a]); @@ -104,7 +104,7 @@ fn main() { ..config }; let mut buf = vec![0u8; 4 * len]; - config.generate(sampler, |pos, (r, g, b, a)| { + config.generate(sampler, world.index(), |pos, (r, g, b, a)| { let i = pos.x; let j = pos.y; (&mut buf[(j * x + i) * 4..]).write(&[r, g, b, a]).unwrap(); diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 7f98b5acb5..77f7d3ae08 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -48,10 +48,7 @@ impl<'a> BlockGen<'a> { close_cliffs.iter().fold( 0.0f32, |max_height, (cliff_pos, seed)| match Self::sample_column( - column_gen, - cache, - *cliff_pos, - index, + column_gen, cache, *cliff_pos, index, ) { Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => { let cliff_pos3d = Vec3::from(*cliff_pos); @@ -314,10 +311,10 @@ impl<'a> BlockGen<'a> { // Some(Block::new( // if on_cliff && (height * 1271.0).fract() < 0.015 { - // treasures[(height * 731.3) as usize % treasures.len()] - // } else if (height * 1271.0).fract() < 0.1 { - // flowers[(height * 0.2) as usize % flowers.len()] - // } else { + // treasures[(height * 731.3) as usize % + // treasures.len()] } else if (height * + // 1271.0).fract() < 0.1 { flowers[(height * + // 0.2) as usize % flowers.len()] } else { // grasses[(height * 103.3) as usize % grasses.len()] // }, // Rgb::broadcast(0), @@ -343,10 +340,10 @@ impl<'a> BlockGen<'a> { // Some(Block::new( // if (height * 1271.0).fract() < 0.5 { - // large_cacti[(height * 0.2) as usize % large_cacti.len()] - // } else { - // small_cacti[(height * 0.3) as usize % small_cacti.len()] - // }, + // large_cacti[(height * 0.2) as usize % + // large_cacti.len()] } else { + // small_cacti[(height * 0.3) as usize % + // small_cacti.len()] }, // Rgb::broadcast(0), // )) } else { diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index fcb599832e..9d9058b925 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -715,9 +715,7 @@ fn find_site_loc( loc = ctx.sim.get(test_loc).and_then(|c| { Some( c.downhill? - .map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { - e / (sz as i32) - }), + .map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / (sz as i32)), ) }); } diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 92746650b5..5f08f73f39 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -614,7 +614,7 @@ where .unwrap_or_else(|| { max_border_river .river_kind - .and_then(|river_kind| { + .map(|river_kind| { match river_kind { RiverKind::Ocean => { let ( @@ -648,7 +648,7 @@ where let river_dist = wposf.distance(river_pos); let _river_height_factor = river_dist / (river_width * 0.5); - return Some(( + return ( true, Some((river_dist - river_width * 0.5) as f32), alt_for_river @@ -656,10 +656,10 @@ where lake_water_alt - river_gouge, alt_for_river.max(lake_water_alt), 0.0, - )); + ); } - Some(( + ( river_scale_factor <= 1.0, Some( (wposf.distance(river_pos) - river_width * 0.5) @@ -669,7 +669,7 @@ where downhill_water_alt, alt_for_river, river_scale_factor as f32, - )) + ) }, RiverKind::Lake { .. } => { let lake_dist = (max_border_river_pos.map(|e| e as f64) @@ -691,7 +691,7 @@ where || in_bounds { let gouge_factor = 0.0; - return Some(( + return ( in_bounds || downhill_water_alt .max(river_chunk.water_alt) @@ -703,16 +703,16 @@ where alt_for_river, river_scale_factor as f32 * (1.0 - gouge_factor), - )); + ); } else { - return Some(( + return ( false, Some(lake_dist as f32), alt_for_river, downhill_water_alt, alt_for_river, river_scale_factor as f32, - )); + ); }; let lake_dist = dist.y; @@ -724,7 +724,7 @@ where river_t as f32, ); if dist == Vec2::zero() { - return Some(( + return ( true, Some(lake_dist as f32), alt_for_river @@ -732,7 +732,7 @@ where lake_water_alt - river_gouge, alt_for_river.max(lake_water_alt), 0.0, - )); + ); } if lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 1.0 || in_bounds @@ -745,7 +745,7 @@ where let in_bounds_ = lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 0.5; if gouge_factor == 1.0 { - return Some(( + return ( true, Some(lake_dist as f32), alt.min(lake_water_alt - 1.0 - river_gouge), @@ -753,9 +753,9 @@ where - river_gouge, alt.max(lake_water_alt), 0.0, - )); + ); } else { - return Some(( + return ( true, None, alt_for_river, @@ -767,17 +767,17 @@ where alt_for_river, river_scale_factor as f32 * (1.0 - gouge_factor), - )); + ); } } - Some(( + ( river_scale_factor <= 1.0, Some(lake_dist as f32), alt_for_river, downhill_water_alt, alt_for_river, river_scale_factor as f32, - )) + ) }, RiverKind::River { .. } => { let (_, _, river_width, (_, (river_pos, _), _)) = @@ -785,14 +785,14 @@ where let river_dist = wposf.distance(river_pos); // FIXME: Make water altitude accurate. - Some(( + ( river_scale_factor <= 1.0, Some((river_dist - river_width * 0.5) as f32), alt_for_river, downhill_water_alt, alt_for_river, river_scale_factor as f32, - )) + ) }, } }) diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 23c057d93e..72986bb1ec 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -105,14 +105,14 @@ pub fn apply_scatter_to<'a>( .find_map(|(i, (bk, is_underwater, f))| { let (density, patch) = f(chunk); let is_patch = patch - .map(|(wavelen, threshold)| { - index.noise.scatter_nz.get( - wpos2d - .map(|e| e as f64 / wavelen as f64 + i as f64 * 43.0) - .into_array(), - ) < threshold as f64 - }) - .unwrap_or(false); + .map(|(wavelen, threshold)| { + index.noise.scatter_nz.get( + wpos2d + .map(|e| e as f64 / wavelen as f64 + i as f64 * 43.0) + .into_array(), + ) < threshold as f64 + }) + .unwrap_or(false); if density <= 0.0 || is_patch || !RandomField::new(i as u32) diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index e2d8bdc102..b34619c487 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -404,7 +404,7 @@ impl Floor { if area.contains_point(stair_rcenter.xy()) { let offs = Vec2::new(rng.gen_range(-1.0, 1.0), rng.gen_range(-1.0, 1.0)) .try_normalized() - .unwrap_or(Vec2::unit_y()) + .unwrap_or_else(Vec2::unit_y) * FLOOR_SIZE.x as f32 / 2.0 - 8.0; diff --git a/world/src/site/settlement/building/archetype/mod.rs b/world/src/site/settlement/building/archetype/mod.rs index d6b858ff78..d20e943ef2 100644 --- a/world/src/site/settlement/building/archetype/mod.rs +++ b/world/src/site/settlement/building/archetype/mod.rs @@ -12,6 +12,8 @@ pub trait Archetype { fn generate(rng: &mut R) -> (Self, Skeleton) where Self: Sized; + + #[allow(clippy::too_many_arguments)] fn draw( &self, pos: Vec3, diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index ea20a5df76..7b9f418965 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -232,7 +232,7 @@ impl Settlement { for _ in 0..PATH_COUNT { dir = (Vec2::new(rng.gen::() - 0.5, rng.gen::() - 0.5) * 2.0 - dir) .try_normalized() - .unwrap_or(Vec2::zero()); + .unwrap_or_else(Vec2::zero); let origin = dir.map(|e| (e * 100.0) as i32); let origin = self .land @@ -662,7 +662,7 @@ impl Settlement { } } - Some(Rgb::new(100, 105, 75).map2(Rgb::iota(), |e: u8, i: i32| { + Some(Rgb::new(100, 95, 65).map2(Rgb::iota(), |e: u8, i: i32| { e.saturating_add( (self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 1) as u8, @@ -746,9 +746,9 @@ impl Settlement { for z in -8 - diff..4 + diff { let pos = Vec3::new(offs.x, offs.y, surface_z + z); - let block = vol.get(pos).ok().copied().unwrap_or(Block::empty()); + let block = vol.get(pos).ok().copied().unwrap_or_else(Block::empty); - if block.is_empty() { + if block.kind() == BlockKind::Air { break; } diff --git a/world/src/util/random.rs b/world/src/util/random.rs index 390ab51e8e..15eb502188 100644 --- a/world/src/util/random.rs +++ b/world/src/util/random.rs @@ -24,17 +24,17 @@ impl Sampler<'static> for RandomField { let mut a = self.seed; a = (a ^ 61) ^ (a >> 16); a = a.wrapping_add(a << 3); - a = a ^ pos.x; - a = a ^ (a >> 4); + a ^= pos.x; + a ^= a >> 4; a = a.wrapping_mul(0x27d4eb2d); - a = a ^ (a >> 15); - a = a ^ pos.y; + a ^= a >> 15; + a ^= pos.y; a = (a ^ 61) ^ (a >> 16); a = a.wrapping_add(a << 3); - a = a ^ (a >> 4); - a = a ^ pos.z; + a ^= a >> 4; + a ^= pos.z; a = a.wrapping_mul(0x27d4eb2d); - a = a ^ (a >> 15); + a ^= a >> 15; a } }