From 0329b355ef7fed4707400fe4caeb5dda730c0d0e Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 20 Apr 2020 01:17:54 +0100 Subject: [PATCH] Made civsim paths visible in-game --- common/src/path.rs | 6 +++- server/src/sys/terrain.rs | 9 ++--- world/src/civ/mod.rs | 48 ++++++++++++++----------- world/src/column/mod.rs | 11 +++++- world/src/sim/mod.rs | 73 +++++++++++++++++++++++++++++++++++++-- world/src/sim/path.rs | 16 +++++++++ world/src/util/mod.rs | 51 +++++++++++++++++++++++++++ 7 files changed, 185 insertions(+), 29 deletions(-) create mode 100644 world/src/sim/path.rs diff --git a/common/src/path.rs b/common/src/path.rs index f826d18bcb..b3b88e3057 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -38,6 +38,10 @@ impl Path { pub fn start(&self) -> Option<&T> { self.nodes.first() } pub fn end(&self) -> Option<&T> { self.nodes.last() } + + pub fn nodes(&self) -> &[T] { + &self.nodes + } } // Route: A path that can be progressed along @@ -74,7 +78,7 @@ impl Route { } else { let next_tgt = next.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); if ((pos - next_tgt) * Vec3::new(1.0, 1.0, 0.3)).magnitude_squared() - < traversal_tolerance.powf(2.0) + < (traversal_tolerance * 2.0).powf(2.0) { self.next_idx += 1; } diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index e7c3b9b073..d268daa5f0 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -9,7 +9,7 @@ use common::{ state::TerrainChanges, terrain::TerrainGrid, }; -use rand::{seq::SliceRandom, Rng}; +use rand::Rng; use specs::{Join, Read, ReadStorage, System, Write, WriteExpect, WriteStorage}; use std::{sync::Arc, time::Duration}; use vek::*; @@ -116,6 +116,7 @@ impl<'a> System<'a> for Sys { &body_data.species[&species].generic } + /* const SPAWN_NPCS: &'static [fn() -> ( String, comp::Body, @@ -181,14 +182,14 @@ impl<'a> System<'a> for Sys { ) }), ]; - let (name, mut body, main, mut alignment) = SPAWN_NPCS + let (name, mut body, main, alignment) = SPAWN_NPCS .choose(&mut rand::thread_rng()) .expect("SPAWN_NPCS is nonempty")( ); - let mut stats = comp::Stats::new(name, body); + */ let mut body = entity.body; - let mut name = entity.name.unwrap_or("Unnamed".to_string()); + let name = entity.name.unwrap_or("Unnamed".to_string()); let alignment = entity.alignment; let main_tool = entity.main_tool; diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index ad060f7eef..712281800c 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -3,7 +3,7 @@ mod econ; use crate::{ sim::{SimChunk, WorldSim}, site::{Dungeon, Settlement, Site as WorldSite}, - util::{attempt, seed_expan}, + util::{attempt, seed_expan, NEIGHBORS, CARDINALS}, }; use common::{ astar::Astar, @@ -19,24 +19,6 @@ use rand_chacha::ChaChaRng; use std::{fmt, hash::Hash, ops::Range}; use vek::*; -const CARDINALS: [Vec2; 4] = [ - Vec2::new(1, 0), - Vec2::new(-1, 0), - Vec2::new(0, 1), - Vec2::new(0, -1), -]; - -const DIAGONALS: [Vec2; 8] = [ - Vec2::new(1, 0), - Vec2::new(1, 1), - Vec2::new(-1, 0), - Vec2::new(-1, 1), - Vec2::new(0, 1), - Vec2::new(1, -1), - Vec2::new(0, -1), - Vec2::new(-1, -1), -]; - const INITIAL_CIV_COUNT: usize = 32; #[derive(Default)] @@ -111,6 +93,30 @@ impl Civs { // Temporary! for track in this.tracks.iter() { + for locs in track.path.nodes().windows(3) { + let to_prev_idx = NEIGHBORS + .iter() + .enumerate() + .find(|(_, dir)| **dir == locs[0] - locs[1]) + .expect("Track locations must be neighbors") + .0; + let to_next_idx = NEIGHBORS + .iter() + .enumerate() + .find(|(_, dir)| **dir == locs[2] - locs[1]) + .expect("Track locations must be neighbors") + .0; + + let mut chunk = ctx.sim.get_mut(locs[1]).unwrap(); + chunk.path.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), + ); + } + for loc in track.path.iter() { ctx.sim.get_mut(*loc).unwrap().place = Some(this.civs.iter().next().unwrap().homeland); @@ -158,7 +164,7 @@ impl Civs { // Place sites in world for site in this.sites.iter() { - let wpos = site.center * Vec2::from(TerrainChunkSize::RECT_SIZE).map(|e: u32| e as i32); + let wpos = site.center.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| e * sz as i32 + sz as i32 / 2); let world_site = match &site.kind { SiteKind::Settlement => { @@ -464,7 +470,7 @@ fn find_path( let heuristic = move |l: &Vec2| (l.distance_squared(b) as f32).sqrt(); let neighbors = |l: &Vec2| { let l = *l; - DIAGONALS + NEIGHBORS .iter() .filter(move |dir| walk_in_dir(sim, l, **dir).is_some()) .map(move |dir| l + *dir) diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 94e993b9aa..0c350d3097 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -141,7 +141,7 @@ fn river_spline_coeffs( /// curve"... hopefully this works out okay and gives us what we want (a /// river that extends outwards tangent to a quadratic curve, with width /// configured by distance along the line). -fn quadratic_nearest_point( +pub fn quadratic_nearest_point( spline: &Vec3>, point: Vec2, ) -> Option<(f64, Vec2, f64)> { @@ -1118,6 +1118,15 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { 5.0 }; + const PATH_WIDTH: f32 = 5.0; + let path_dist_factor = sim.get_nearest_path(wpos).map(|(dist, _)| dist / PATH_WIDTH).unwrap_or(1.0).min(1.0); + let ground = Lerp::lerp( + sub_surface_color, + ground, + (path_dist_factor.max(0.8) - 0.8) / 0.2, + ); + let alt = alt - if path_dist_factor < 0.8 { 1.0 } else { 0.0 }; + Some(ColumnSample { alt, riverless_alt, diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 894dd46b14..89a56f47ec 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -3,6 +3,7 @@ mod erosion; mod location; mod map; mod util; +mod path; // Reexports use self::erosion::Compute; @@ -19,6 +20,7 @@ pub use self::{ uniform_idx_as_vec2, uniform_noise, uphill, vec2_as_uniform_idx, InverseCdf, ScaleBias, NEIGHBOR_DELTA, }, + path::PathData, }; use crate::{ @@ -27,7 +29,7 @@ use crate::{ civ::Place, column::ColumnGen, site::{Settlement, Site}, - util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d}, + util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d, LOCALITY, CARDINAL_LOCALITY, NEIGHBORS}, CONFIG, }; use common::{ @@ -1574,7 +1576,7 @@ impl WorldSim { pub fn get_wpos(&self, wpos: Vec2) -> Option<&SimChunk> { self.get( wpos.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| { - e / sz as i32 + e.div_euclid(sz as i32) }), ) } @@ -1770,6 +1772,71 @@ impl WorldSim { Some(z0 + z1 + z2 + z3) } + + pub fn get_nearest_path(&self, wpos: Vec2) -> Option<(f32, Vec2)> { + let chunk_pos = wpos.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| { + e.div_euclid(sz as i32) + }); + let get_chunk_centre = |chunk_pos: Vec2| chunk_pos.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| { + e * sz as i32 + sz as i32 / 2 + }); + + 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 chunk_connections = chunk.path.neighbors.count_ones(); + if chunk_connections == 0 { + return None; + } + + let (start_pos, start_idx) = if chunk_connections != 2 { + (ctrl_pos, None) + } else { + let (start_idx, start_rpos) = NEIGHBORS + .iter() + .copied() + .enumerate() + .find(|(i, _)| chunk.path.neighbors & (1 << *i as u8) != 0) + .unwrap(); + let start_pos_chunk = chunk_pos + *ctrl + start_rpos; + ( + get_chunk_centre(start_pos_chunk).map(|e| e as f32) + self.get(start_pos_chunk)?.path.offset, + Some(start_idx), + ) + }; + + Some(NEIGHBORS + .iter() + .enumerate() + .filter(move |(i, _)| chunk.path.neighbors & (1 << *i as u8) != 0) + .filter_map(move |(i, end_rpos)| { + let end_pos_chunk = chunk_pos + *ctrl + end_rpos; + let end_pos = get_chunk_centre(end_pos_chunk).map(|e| e as f32) + self.get(end_pos_chunk)?.path.offset; + + let bez = QuadraticBezier2 { + start: (start_pos + ctrl_pos) / 2.0, + ctrl: ctrl_pos, + end: (end_pos + ctrl_pos) / 2.0, + }; + let nearest_interval = bez + .binary_search_point_by_steps( + wpos.map(|e| e as f32), + 6, + 0.01, + ) + .0.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.map(|e| e.floor() as i32))) + })) + }) + .flatten() + .min_by_key(|(dist_sqrd, _)| (dist_sqrd * 1024.0) as i32) + .map(|(dist, pos)| (dist.sqrt(), pos)) + } } #[derive(Debug)] @@ -1793,6 +1860,7 @@ pub struct SimChunk { pub sites: Vec, pub place: Option>, + pub path: PathData, pub contains_waypoint: bool, } @@ -2030,6 +2098,7 @@ impl SimChunk { sites: Vec::new(), place: None, + path: PathData::default(), contains_waypoint: false, } } diff --git a/world/src/sim/path.rs b/world/src/sim/path.rs new file mode 100644 index 0000000000..25563669d2 --- /dev/null +++ b/world/src/sim/path.rs @@ -0,0 +1,16 @@ +use vek::*; + +#[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 neighbors: u8, // One bit for each neighbor +} + +impl Default for PathData { + fn default() -> Self { + Self { + offset: Vec2::zero(), + neighbors: 0, + } + } +} diff --git a/world/src/util/mod.rs b/world/src/util/mod.rs index abe9d8d0e9..89b0be7241 100644 --- a/world/src/util/mod.rs +++ b/world/src/util/mod.rs @@ -20,6 +20,57 @@ pub use self::{ unit_chooser::UnitChooser, }; +use vek::*; + pub fn attempt(max_iters: usize, mut f: impl FnMut() -> Option) -> Option { (0..max_iters).find_map(|_| f()) } + +pub const CARDINALS: [Vec2; 4] = [ + Vec2::new(0, 1), + Vec2::new(1, 0), + Vec2::new(0, -1), + Vec2::new(-1, 0), +]; + +pub const DIRS: [Vec2; 8] = [ + Vec2::new(0, 1), + Vec2::new(1, 0), + Vec2::new(0, -1), + Vec2::new(-1, 0), + Vec2::new(1, 1), + Vec2::new(1, -1), + Vec2::new(-1, 1), + Vec2::new(-1, -1), +]; + +pub const NEIGHBORS: [Vec2; 8] = [ + Vec2::new(0, 1), + Vec2::new(1, 0), + Vec2::new(0, -1), + Vec2::new(-1, 0), + Vec2::new(1, 1), + Vec2::new(1, -1), + Vec2::new(-1, 1), + Vec2::new(-1, -1), +]; + +pub const LOCALITY: [Vec2; 9] = [ + Vec2::new(0, 0), + Vec2::new(0, 1), + Vec2::new(1, 0), + Vec2::new(0, -1), + Vec2::new(-1, 0), + Vec2::new(1, 1), + Vec2::new(1, -1), + Vec2::new(-1, 1), + Vec2::new(-1, -1), +]; + +pub const CARDINAL_LOCALITY: [Vec2; 5] = [ + Vec2::new(0, 0), + Vec2::new(0, 1), + Vec2::new(1, 0), + Vec2::new(0, -1), + Vec2::new(-1, 0), +];