diff --git a/world/examples/view.rs b/world/examples/view.rs index 61b100e140..2fa077389e 100644 --- a/world/examples/view.rs +++ b/world/examples/view.rs @@ -37,8 +37,8 @@ fn main() { let loc_color = location .map(|l| { ( - l.name().bytes().nth(0).unwrap() * 17, - l.name().bytes().nth(1).unwrap() * 17, + l.loc.name().bytes().nth(0).unwrap() * 17, + l.loc.name().bytes().nth(1).unwrap() * 17, ) }) .unwrap_or((0, 0)); @@ -67,10 +67,10 @@ fn main() { gain -= 10.0; } if win.is_key_down(minifb::Key::R) { - scale += 1; + scale += 6; } if win.is_key_down(minifb::Key::F) { - scale -= 1; + scale -= 6; } win.update_with_buffer(&buf).unwrap(); diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index bc9b365660..52a417a92d 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -103,8 +103,7 @@ impl<'a> SamplerMut for BlockGen<'a> { (true, alt, water_level) } else { // Apply warping - let warp = (self - .world + let warp = (world .sim() .gen_ctx .warp_nz diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 2ee4660d67..20027229ab 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,4 +1,10 @@ -use crate::{all::ForestKind, sim::Location, util::Sampler, World, CONFIG}; +use crate::{ + all::ForestKind, + sim::{Location, LocationInfo}, + util::Sampler, + World, + CONFIG, +}; use common::{ terrain::{Block, TerrainChunkSize}, vol::{VolSize, Vox}, @@ -116,6 +122,30 @@ impl<'a> Sampler for ColumnGen<'a> { temp.sub(CONFIG.desert_temp).mul(32.0), ); + // Work out if we're on a path + let near_0 = sim_chunk.location.as_ref().map(|l| l.near[0].block_pos).unwrap_or(Vec2::zero()).map(|e| e as f32); + let near_1 = sim_chunk.location.as_ref().map(|l| l.near[1].block_pos).unwrap_or(Vec2::zero()).map(|e| e as f32); + + let dist_to_path = (0.0 + + (near_1.y - near_0.y) * wposf.x as f32 + - (near_1.x - near_0.x) * wposf.y as f32 + + near_1.x * near_0.y + - near_0.x * near_1.y) + .abs() + .div(near_0.distance(near_1)); + + let alt = if dist_to_path < 15.0 { + alt - 100.0 + } else { + alt + }; + + let ground = if dist_to_path < 5.0 { + Rgb::new(0.0, 0.0, 0.0) + } else { + ground + }; + // Caves let cave_at = |wposf: Vec2| { (sim.gen_ctx.cave_0_nz.get( @@ -202,5 +232,5 @@ pub struct ColumnSample<'a> { pub cliff_hill: f32, pub close_cliffs: [(Vec2, u32); 9], pub temp: f32, - pub location: Option<&'a Arc>, + pub location: Option<&'a LocationInfo>, } diff --git a/world/src/lib.rs b/world/src/lib.rs index 5c4a4b56f5..36af8e2536 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(euclidean_division, bind_by_move_pattern_guards)] +#![feature(euclidean_division, bind_by_move_pattern_guards, option_flattening)] mod all; mod block; diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 0d47ae8e75..0f57feb2db 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -3,7 +3,11 @@ mod location; // Reexports pub use self::location::Location; -use crate::{all::ForestKind, util::StructureGen2d, CONFIG}; +use crate::{ + all::ForestKind, + util::{StructureGen2d, Sampler}, + CONFIG, +}; use common::{ terrain::{BiomeKind, TerrainChunkSize}, vol::VolSize, @@ -115,14 +119,80 @@ impl WorldSim { pub fn seed_elements(&mut self) { let mut rng = self.rng.clone(); - for _ in 0..250 { - let loc_center = Vec2::new( - self.rng.gen::() % WORLD_SIZE.x as i32, - self.rng.gen::() % WORLD_SIZE.y as i32, - ); + let cell_size = 32; + let grid_size = WORLD_SIZE / cell_size; + let loc_count = 250; - if let Some(chunk) = self.get_mut(loc_center) { - chunk.location = Some(Location::generate(loc_center, &mut rng).into()); + let mut locations = vec![None; grid_size.product()]; + + // Seed the world with some locations + for _ in 0..loc_count { + let cell_pos = Vec2::new( + self.rng.gen::() % grid_size.x, + self.rng.gen::() % grid_size.y, + ); + let wpos = (cell_pos * cell_size) + .map2( + Vec2::from(TerrainChunkSize::SIZE), + |e, sz: u32| e as i32 * sz as i32, + ); + + locations[cell_pos.y * grid_size.x + cell_pos.x] = Some(Arc::new(Location::generate(wpos, &mut rng))); + } + + // Simulate invasion! + let invasion_cycles = 25; + for _ in 0..invasion_cycles { + for i in 0..grid_size.x { + for j in 0..grid_size.y { + if locations[j * grid_size.x + i].is_none() { + const R_COORDS: [i32; 5] = [-1, 0, 1, 0, -1]; + let idx = self.rng.gen::() % 4; + let loc = Vec2::new( + i as i32 + R_COORDS[idx], + j as i32 + R_COORDS[idx + 1], + ).map(|e| e as usize); + + locations[j * grid_size.x + i] = locations + .get(loc.y * grid_size.x + loc.x) + .cloned() + .flatten(); + } + } + } + } + + // Place the locations onto the world + let gen = StructureGen2d::new(self.seed, cell_size as u32, cell_size as u32 / 2); + for i in 0..WORLD_SIZE.x { + for j in 0..WORLD_SIZE.y { + let chunk_pos = Vec2::new(i as i32, j as i32); + let cell_pos = Vec2::new(i / cell_size, j / cell_size); + + // Find the distance to each region + let near = gen.get(chunk_pos); + let mut near = near + .iter() + .map(|(pos, seed)| RegionInfo { + chunk_pos: *pos, + block_pos: pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| e * sz as i32), + dist: (pos - chunk_pos).map(|e| e as f32).magnitude(), + seed: *seed, + }) + .collect::>(); + + // Sort regions based on distance + near.sort_by(|a, b| a.dist.partial_cmp(&b.dist).unwrap()); + + let nearest_cell_pos = near[0].chunk_pos.map(|e| e as usize) / cell_size; + self.get_mut(chunk_pos).unwrap().location = locations + .get(nearest_cell_pos.y * grid_size.x + nearest_cell_pos.x) + .cloned() + .unwrap_or(None) + .map(|loc| LocationInfo { + loc, + near, + }); } } @@ -242,7 +312,21 @@ pub struct SimChunk { pub near_cliffs: bool, pub tree_density: f32, pub forest_kind: ForestKind, - pub location: Option>, + pub location: Option, +} + +#[derive(Copy, Clone)] +pub struct RegionInfo { + pub chunk_pos: Vec2, + pub block_pos: Vec2, + pub dist: f32, + pub seed: u32, +} + +#[derive(Clone)] +pub struct LocationInfo { + pub loc: Arc, + pub near: Vec, } impl SimChunk { @@ -367,7 +451,7 @@ impl SimChunk { } pub fn get_name(&self) -> Option { - self.location.as_ref().map(|l| l.name().to_string()) + self.location.as_ref().map(|l| l.loc.name().to_string()) } pub fn get_biome(&self) -> BiomeKind {