From a4592dd0d0a66a9414544e2f69d23e9f87962b34 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 22 Jun 2021 00:47:19 +0100 Subject: [PATCH] Initial spot implementation --- world/src/canvas.rs | 87 +++++++++++++++++++++++++++++++++++--- world/src/layer/mod.rs | 3 +- world/src/layer/spot.rs | 94 +++++++++++++++++++++++++++++++++++++++++ world/src/lib.rs | 2 + world/src/sim/mod.rs | 5 +++ 5 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 world/src/layer/spot.rs diff --git a/world/src/canvas.rs b/world/src/canvas.rs index 3b5c568fb1..b5ec3b1f88 100644 --- a/world/src/canvas.rs +++ b/world/src/canvas.rs @@ -1,13 +1,14 @@ use crate::{ - block::ZCache, + block::{block_from_structure, ZCache}, column::ColumnSample, index::IndexRef, land::Land, + layer::spot::Spot, sim::{SimChunk, WorldSim}, util::Grid, }; use common::{ - terrain::{Block, TerrainChunk, TerrainChunkSize}, + terrain::{Block, Structure, TerrainChunk, TerrainChunkSize}, vol::{ReadVol, RectVolSize, WriteVol}, }; use std::ops::Deref; @@ -15,6 +16,7 @@ use vek::*; #[derive(Copy, Clone)] pub struct CanvasInfo<'a> { + pub(crate) chunk_pos: Vec2, pub(crate) wpos: Vec2, pub(crate) column_grid: &'a Grid>>, pub(crate) column_grid_border: i32, @@ -42,6 +44,24 @@ impl<'a> CanvasInfo<'a> { .map(|zc| &zc.sample) } + pub fn nearby_spots(&self) -> impl Iterator, Spot, u32)> + '_ { + (-1..2) + .map(|x| (-1..2).map(move |y| Vec2::new(x, y))) + .flatten() + .filter_map(move |pos| { + let pos = self.chunk_pos + pos; + self.chunks.get(pos).and_then(|c| c.spot).map(|spot| { + let wpos = pos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| { + e * sz as i32 + sz as i32 / 2 + }); + // TODO: Very dumb, not this. + let seed = pos.x as u32 | (pos.y as u32).wrapping_shl(16); + + (wpos, spot, seed) + }) + }) + } + pub fn index(&self) -> IndexRef<'a> { self.index } pub fn chunk(&self) -> &'a SimChunk { self.chunk } @@ -117,11 +137,19 @@ impl<'a> Canvas<'a> { let _ = self.chunk.map(pos - self.wpos(), f); } - /// Execute an operation upon each column in this canvas. - pub fn foreach_col(&mut self, mut f: impl FnMut(&mut Self, Vec2, &ColumnSample)) { - for y in 0..self.area().size().h as i32 { - for x in 0..self.area().size().w as i32 { - let wpos2d = self.wpos() + Vec2::new(x, y); + pub fn foreach_col_area( + &mut self, + aabr: Aabr, + mut f: impl FnMut(&mut Self, Vec2, &ColumnSample), + ) { + let chunk_aabr = Aabr { + min: self.wpos(), + max: self.wpos() + Vec2::from(self.area().size().map(|e| e as i32)), + }; + + for y in chunk_aabr.min.y.max(aabr.min.y)..chunk_aabr.max.y.min(aabr.max.y) { + for x in chunk_aabr.min.x.max(aabr.min.x)..chunk_aabr.max.x.min(aabr.max.x) { + let wpos2d = Vec2::new(x, y); let info = self.info; let col = if let Some(col) = info.col(wpos2d) { col @@ -132,6 +160,51 @@ impl<'a> Canvas<'a> { } } } + + /// Execute an operation upon each column in this canvas. + pub fn foreach_col(&mut self, f: impl FnMut(&mut Self, Vec2, &ColumnSample)) { + self.foreach_col_area( + Aabr { + min: Vec2::broadcast(i32::MIN), + max: Vec2::broadcast(i32::MAX), + }, + f, + ); + } + + /// Blit a structure on to the canvas at the given position. + /// + /// Note that this function should be called with identitical parameters by + /// all chunks within the bounds of the structure to avoid cut-offs + /// occurring at chunk borders. Deterministic RNG is advised! + pub fn blit_structure(&mut self, origin: Vec3, structure: &Structure, seed: u32) { + let aabr = Aabr { + min: origin.xy() + structure.get_bounds().min.xy(), + max: origin.xy() + structure.get_bounds().max.xy(), + }; + let info = self.info(); + self.foreach_col_area(aabr, |canvas, wpos2d, col| { + for z in structure.get_bounds().min.z..structure.get_bounds().max.z { + if let Ok(sblock) = structure.get((wpos2d - origin.xy()).with_z(z)) { + let _ = canvas.map(wpos2d.with_z(origin.z + z), |block| { + if let Some(block) = block_from_structure( + info.index, + *sblock, + wpos2d.with_z(origin.z + z), + wpos2d - origin.xy(), + seed, + col, + |sprite| block.with_sprite(sprite), + ) { + block + } else { + block + } + }); + } + } + }); + } } impl<'a> Deref for Canvas<'a> { diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 56592205df..f7c687b94e 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -1,8 +1,9 @@ pub mod scatter; +pub mod spot; pub mod tree; pub mod wildlife; -pub use self::{scatter::apply_scatter_to, tree::apply_trees_to}; +pub use self::{scatter::apply_scatter_to, spot::apply_spots_to, tree::apply_trees_to}; use crate::{ column::ColumnSample, diff --git a/world/src/layer/spot.rs b/world/src/layer/spot.rs new file mode 100644 index 0000000000..71dce812d0 --- /dev/null +++ b/world/src/layer/spot.rs @@ -0,0 +1,94 @@ +use crate::{ + sim::{SimChunk, WorldSim}, + util::seed_expan, + Canvas, +}; +use common::terrain::{Block, BlockKind, Structure}; +use rand::prelude::*; +use rand_chacha::ChaChaRng; +use vek::*; + +#[derive(Copy, Clone, Debug)] +pub enum Spot { + Camp, + Hideout, +} + +impl Spot { + pub fn generate(world: &mut WorldSim) { + Self::generate_spots( + Spot::Camp, + world, + 10.0, + |g, c| g < 0.25 && !c.near_cliffs() && !c.river.near_water(), + false, + ); + Self::generate_spots( + Spot::Hideout, + world, + 10.0, + |g, c| g < 0.25 && !c.near_cliffs() && !c.river.near_water(), + false, + ); + } + + fn generate_spots( + spot: Spot, + world: &mut WorldSim, + freq: f32, // Per sq km + mut valid: impl FnMut(f32, &SimChunk) -> bool, + trees: bool, + ) { + let world_size = world.get_size(); + for _ in 0..(world_size.product() as f32 / 32.0f32.powi(2) * freq).ceil() as u64 { + let pos = world_size.map(|e| world.rng.gen_range(0..e as i32)); + if let Some((_, chunk)) = world + .get_gradient_approx(pos) + .zip(world.get_mut(pos)) + .filter(|(grad, chunk)| valid(*grad, chunk)) + { + chunk.spot = Some(spot); + if !trees { + chunk.tree_density = 0.0; + } + } + } + } +} + +pub fn apply_spots_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) { + let nearby_spots = canvas.nearby_spots().collect::>(); + + for (spot_wpos, spot, seed) in nearby_spots.iter().copied() { + let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed)); + match spot { + Spot::Camp => { + canvas.foreach_col_area( + Aabr { + min: spot_wpos - 8, + max: spot_wpos + 8, + }, + |canvas, wpos2d, col| { + if nearby_spots + .iter() + .any(|(wpos, _, _)| wpos.distance_squared(wpos2d) < 64) + { + for z in -8..32 { + canvas.set( + wpos2d.with_z(col.alt as i32 + z), + Block::new(BlockKind::Misc, Rgb::broadcast(255)), + ); + } + } + }, + ); + }, + Spot::Hideout => { + let structures = Structure::load_group("dungeon_entrances").read(); + let structure = structures.choose(&mut rng).unwrap(); + let origin = spot_wpos.with_z(canvas.land().get_alt_approx(spot_wpos) as i32); + canvas.blit_structure(origin, &structure, seed); + }, + } + } +} diff --git a/world/src/lib.rs b/world/src/lib.rs index a67775ad06..05804c1634 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -338,6 +338,7 @@ impl World { // Apply layers (paths, caves, etc.) let mut canvas = Canvas { info: CanvasInfo { + chunk_pos, wpos: chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), column_grid: &zcache_grid, column_grid_border: grid_border, @@ -352,6 +353,7 @@ impl World { layer::apply_trees_to(&mut canvas, &mut dynamic_rng); layer::apply_scatter_to(&mut canvas, &mut dynamic_rng); layer::apply_paths_to(&mut canvas); + layer::apply_spots_to(&mut canvas, &mut dynamic_rng); // layer::apply_coral_to(&mut canvas); // Apply site generation diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index dfe38d63b8..7d14c08cc0 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -30,6 +30,7 @@ use crate::{ block::BlockGen, civ::Place, column::ColumnGen, + layer::spot::Spot, site::Site, util::{ seed_expan, DHashSet, FastNoise, FastNoise2d, RandomField, Sampler, StructureGen2d, @@ -1413,6 +1414,8 @@ impl WorldSim { this.generate_cliffs(); + Spot::generate(&mut this); + if opts.seed_elements { this.seed_elements(); } @@ -2134,6 +2137,7 @@ pub struct SimChunk { pub path: (Way, Path), pub cave: (Way, Cave), pub cliff_height: f32, + pub spot: Option, pub contains_waypoint: bool, } @@ -2363,6 +2367,7 @@ impl SimChunk { path: Default::default(), cave: Default::default(), cliff_height: 0.0, + spot: None, contains_waypoint: false, }