From 97ec94fd3d51c9a058c369fbcbaf07b8aabdf106 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 1 Oct 2020 22:00:46 +0100 Subject: [PATCH 1/8] Initial NPC region spawning work --- world/src/layer/mod.rs | 1 + world/src/layer/wildlife.rs | 144 ++++++++++++++++++++++++++++++++++++ world/src/lib.rs | 14 ++++ 3 files changed, 159 insertions(+) create mode 100644 world/src/layer/wildlife.rs diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 77b9b1e1e0..d2af2780f8 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -1,5 +1,6 @@ pub mod scatter; pub mod tree; +pub mod wildlife; pub use self::{scatter::apply_scatter_to, tree::apply_trees_to}; diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs new file mode 100644 index 0000000000..7d188e4309 --- /dev/null +++ b/world/src/layer/wildlife.rs @@ -0,0 +1,144 @@ +use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, IndexRef, CONFIG}; +use common::{ + comp::{Alignment, quadruped_medium, quadruped_small}, + terrain::{Block, SpriteKind}, + vol::{BaseVol, ReadVol, RectSizedVol, WriteVol}, + generation::{ChunkSupplement, EntityInfo}, +}; +use noise::NoiseFn; +use rand::prelude::*; +use std::{f32, ops::Range}; +use vek::*; + +fn close(x: f32, tgt: f32, falloff: f32) -> f32 { + (1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.125) +} + +const BASE_DENSITY: f32 = 1.0e-5; // Base wildlife density + +#[allow(clippy::eval_order_dependence)] +pub fn apply_wildlife_supplement<'a, R: Rng>( + // NOTE: Used only for dynamic elements like chests and entities! + dynamic_rng: &mut R, + wpos2d: Vec2, + mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, + vol: &(impl BaseVol + RectSizedVol + ReadVol + WriteVol), + index: IndexRef, + chunk: &SimChunk, + supplement: &mut ChunkSupplement, +) { + let scatter: &[( + fn(Vec3, &mut R) -> EntityInfo, // Entity + Range, // Group size range + bool, // Underwater? + fn(&SimChunk, &ColumnSample) -> f32, // Density + )] = &[ + // Wolves + ( + |pos, rng| EntityInfo::at(pos) + .with_body(quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Wolf).into()) + .with_alignment(Alignment::Enemy), + 3..8, + false, + |c, col| close(c.temp, CONFIG.snow_temp, 0.7) * col.tree_density * BASE_DENSITY * 0.3, + ), + // Frostfang + ( + |pos, rng| EntityInfo::at(pos) + .with_body(quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Frostfang).into()) + .with_alignment(Alignment::Enemy), + 1..4, + false, + |c, col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.15, + ), + // Bonerattler + ( + |pos, rng| EntityInfo::at(pos) + .with_body(quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Bonerattler).into()) + .with_alignment(Alignment::Enemy), + 1..3, + false, + |c, col| close(c.humidity, CONFIG.desert_hum, 0.3) * BASE_DENSITY * 0.5, + ), + // Deer + ( + |pos, rng| EntityInfo::at(pos) + .with_body(quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Deer).into()) + .with_alignment(Alignment::Wild), + 4..10, + false, + |c, col| close(c.temp, CONFIG.temperate_temp, 0.7) * col.tree_density * BASE_DENSITY * 0.5, + ), + // Frog + ( + |pos, rng| EntityInfo::at(pos) + .with_body(quadruped_small::Body::random_with(rng, &quadruped_small::Species::Frog).into()) + .with_alignment(Alignment::Wild), + 1..3, + false, + |c, col| close(col.temp, CONFIG.tropical_temp, 0.8) * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { 0.0005 } else { 0.0 }, + ), + ]; + + 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 underwater = col_sample.water_level > col_sample.alt; + + let entity_group = scatter + .iter() + .enumerate() + .find_map(|(i, (make_entity, group_size, is_underwater, f))| { + let density = f(chunk, col_sample); + if density > 0.0 + && RandomField::new(i as u32 * 7) + .chance(Vec3::new(wpos2d.x, wpos2d.y, i as i32), density) + && underwater == *is_underwater + { + Some((make_entity, group_size.clone())) + } else { + None + } + }); + + if let Some((make_entity, group_size)) = entity_group { + let alt = col_sample.alt as i32; + + // Find the intersection between ground and air, if there is one near the + // surface + if let Some(solid_end) = (-4..8) + .find(|z| { + vol.get(Vec3::new(offs.x, offs.y, alt + z)) + .map(|b| b.is_solid()) + .unwrap_or(false) + }) + .and_then(|solid_start| { + (1..8).map(|z| solid_start + z).find(|z| { + vol.get(Vec3::new(offs.x, offs.y, alt + z)) + .map(|b| !b.is_solid()) + .unwrap_or(true) + }) + }) + { + let group_size = dynamic_rng.gen_range(group_size.start, group_size.end); + for _ in 0..group_size { + let pos = Vec3::new(wpos2d.x, wpos2d.y, alt + solid_end) + .map(|e| e as f32 + dynamic_rng.gen::()); + supplement.add_entity(make_entity(pos, dynamic_rng) + .with_automatic_name()); + } + } + } + } + } +} diff --git a/world/src/lib.rs b/world/src/lib.rs index 5fccc613c9..bfa5183e19 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -248,6 +248,8 @@ impl World { const SPAWN_RATE: f32 = 0.1; let mut supplement = ChunkSupplement { + entities: Vec::new(), + /* entities: if dynamic_rng.gen::() < SPAWN_RATE && sim_chunk.chaos < 0.5 && !sim_chunk.is_underwater() @@ -320,6 +322,7 @@ impl World { } else { Vec::new() }, + */ }; if sim_chunk.contains_waypoint { @@ -336,6 +339,17 @@ impl World { &mut supplement, ); + // Apply layer supplement + layer::wildlife::apply_wildlife_supplement( + &mut dynamic_rng, + chunk_wpos2d, + sample_get, + &chunk, + index, + sim_chunk, + &mut supplement, + ); + // Apply site supplementary information sim_chunk.sites.iter().for_each(|site| { index.sites[*site].apply_supplement( From 87edbf02abb58089bc2a28266a61846c82e1350c Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 5 Oct 2020 19:52:33 +0100 Subject: [PATCH 2/8] Random orientation for spawned NPCs --- server/src/state_ext.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index e7c672d254..ee7e52dc69 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -17,6 +17,7 @@ use specs::{ }; use tracing::warn; use vek::*; +use rand::prelude::*; pub trait StateExt { /// Updates a component associated with the entity based on the `Effect` @@ -121,7 +122,11 @@ impl StateExt for State { .create_entity_synced() .with(pos) .with(comp::Vel(Vec3::zero())) - .with(comp::Ori::default()) + .with(comp::Ori(Dir::new(Vec3::new( + thread_rng().gen_range(-1.0, 1.0), + thread_rng().gen_range(-1.0, 1.0), + 0.0, + ).try_normalized().unwrap_or_default()))) .with(comp::Collider::Box { radius: body.radius(), z_min: 0.0, From bb6ca9fcbabf60a628f53df00f52749ac08d2d4a Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 5 Oct 2020 19:52:54 +0100 Subject: [PATCH 3/8] Don't despawn entities so eagerly --- server/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index 81dc112430..f8c47f60d3 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -54,6 +54,7 @@ use common::{ }, outcome::Outcome, recipe::default_recipe_book, + spiral::Spiral2d, state::{State, TimeOfDay}, sync::WorldSyncExt, terrain::TerrainChunkSize, @@ -525,7 +526,14 @@ impl Server { !&self.state.ecs().read_storage::(), ) .join() - .filter(|(_, pos, _)| terrain.get(pos.0.map(|e| e.floor() as i32)).is_err()) + .filter(|(_, pos, _)| { + let chunk_key = terrain.pos_key(pos.0.map(|e| e.floor() as i32)); + // Check not only this chunk, but also all neighbours to avoid immediate + // despawning if the entity walks outside of a valid chunk + // briefly. If the entity isn't even near a loaded chunk then we get + // rid of it. + Spiral2d::new().all(|offs| terrain.get_key(chunk_key + offs).is_none()) + }) .map(|(entity, _, _)| entity) .collect::>() }; From 2ae51de7f013fb7de09a6ba45c3ec3985ed006b9 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 6 Oct 2020 09:18:35 +0100 Subject: [PATCH 4/8] Added more creature spawns --- common/src/generation.rs | 1 + server/src/lib.rs | 2 +- world/src/layer/wildlife.rs | 182 +++++++++++++++++++++++++++++------- 3 files changed, 151 insertions(+), 34 deletions(-) diff --git a/common/src/generation.rs b/common/src/generation.rs index 65a70e6bf8..d3bb340817 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -8,6 +8,7 @@ pub enum EntityTemplate { Traveller, } +#[derive(Clone)] pub struct EntityInfo { pub pos: Vec3, pub is_waypoint: bool, // Edge case, overrides everything else diff --git a/server/src/lib.rs b/server/src/lib.rs index f8c47f60d3..caef411514 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -532,7 +532,7 @@ impl Server { // despawning if the entity walks outside of a valid chunk // briefly. If the entity isn't even near a loaded chunk then we get // rid of it. - Spiral2d::new().all(|offs| terrain.get_key(chunk_key + offs).is_none()) + Spiral2d::new().take(9).all(|offs| terrain.get_key(chunk_key + offs).is_none()) }) .map(|(entity, _, _)| entity) .collect::>() diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index 7d188e4309..ad20178fec 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -1,9 +1,9 @@ use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, IndexRef, CONFIG}; use common::{ - comp::{Alignment, quadruped_medium, quadruped_small}, + comp::{quadruped_low, quadruped_medium, quadruped_small, Alignment}, + generation::{ChunkSupplement, EntityInfo}, terrain::{Block, SpriteKind}, vol::{BaseVol, ReadVol, RectSizedVol, WriteVol}, - generation::{ChunkSupplement, EntityInfo}, }; use noise::NoiseFn; use rand::prelude::*; @@ -29,54 +29,168 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( ) { let scatter: &[( fn(Vec3, &mut R) -> EntityInfo, // Entity - Range, // Group size range - bool, // Underwater? + Range, // Group size range + bool, // Underwater? fn(&SimChunk, &ColumnSample) -> f32, // Density )] = &[ // Wolves ( - |pos, rng| EntityInfo::at(pos) - .with_body(quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Wolf).into()) - .with_alignment(Alignment::Enemy), + |pos, rng| { + EntityInfo::at(pos) + .with_body( + quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Wolf) + .into(), + ) + .with_alignment(Alignment::Enemy) + }, 3..8, false, |c, col| close(c.temp, CONFIG.snow_temp, 0.7) * col.tree_density * BASE_DENSITY * 0.3, ), // Frostfang ( - |pos, rng| EntityInfo::at(pos) - .with_body(quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Frostfang).into()) - .with_alignment(Alignment::Enemy), + |pos, rng| { + EntityInfo::at(pos) + .with_body( + quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Frostfang, + ) + .into(), + ) + .with_alignment(Alignment::Enemy) + }, 1..4, false, |c, col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.15, ), // Bonerattler ( - |pos, rng| EntityInfo::at(pos) - .with_body(quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Bonerattler).into()) - .with_alignment(Alignment::Enemy), + |pos, rng| { + EntityInfo::at(pos) + .with_body( + quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Bonerattler, + ) + .into(), + ) + .with_alignment(Alignment::Enemy) + }, 1..3, false, |c, col| close(c.humidity, CONFIG.desert_hum, 0.3) * BASE_DENSITY * 0.5, ), - // Deer + // Forest herd (Deer, Hirdrasil, etc.) ( - |pos, rng| EntityInfo::at(pos) - .with_body(quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Deer).into()) - .with_alignment(Alignment::Wild), - 4..10, + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 10) { + 0 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Deer, + ) + .into(), + 1 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Hirdrasil, + ) + .into(), + 2 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Rabbit, + ) + .into(), + 3 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Jackalope, + ) + .into(), + 4 => { + quadruped_small::Body::random_with(rng, &quadruped_small::Species::Boar) + .into() + }, + 5 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Sheep, + ) + .into(), + 6 => { + quadruped_small::Body::random_with(rng, &quadruped_small::Species::Pig) + .into() + }, + 7 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Truffler, + ) + .into(), + 8 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Squirrel, + ) + .into(), + _ => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Porcupine, + ) + .into(), + }) + .with_alignment(Alignment::Wild) + }, + 3..8, false, - |c, col| close(c.temp, CONFIG.temperate_temp, 0.7) * col.tree_density * BASE_DENSITY * 0.5, + |c, col| { + close(c.temp, CONFIG.temperate_temp, 0.7) * col.tree_density * BASE_DENSITY * 1.5 + }, ), - // Frog + // Amphibians (Frog, Axolotl, Crocodile, etc.) ( - |pos, rng| EntityInfo::at(pos) - .with_body(quadruped_small::Body::random_with(rng, &quadruped_small::Species::Frog).into()) - .with_alignment(Alignment::Wild), + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 6) { + 0 => { + quadruped_small::Body::random_with(rng, &quadruped_small::Species::Frog) + .into() + }, + 1 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Axolotl, + ) + .into(), + 2 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Fungome, + ) + .into(), + // WE GROW 'EM BIG 'ERE + 3 => quadruped_low::Body::random_with( + rng, + &quadruped_low::Species::Crocodile, + ) + .into(), + 4 => quadruped_low::Body::random_with( + rng, + &quadruped_low::Species::Alligator, + ) + .into(), + _ => quadruped_low::Body::random_with( + rng, + &quadruped_low::Species::Salamander, + ) + .into(), + }) + .with_alignment(Alignment::Wild) + }, 1..3, false, - |c, col| close(col.temp, CONFIG.tropical_temp, 0.8) * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { 0.0005 } else { 0.0 }, + |c, col| { + close(col.temp, CONFIG.tropical_temp, 0.8) + * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { + 0.001 + } else { + 0.0 + } + }, ), ]; @@ -95,10 +209,8 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( let underwater = col_sample.water_level > col_sample.alt; - let entity_group = scatter - .iter() - .enumerate() - .find_map(|(i, (make_entity, group_size, is_underwater, f))| { + let entity_group = scatter.iter().enumerate().find_map( + |(i, (make_entity, group_size, is_underwater, f))| { let density = f(chunk, col_sample); if density > 0.0 && RandomField::new(i as u32 * 7) @@ -109,7 +221,8 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( } else { None } - }); + }, + ); if let Some((make_entity, group_size)) = entity_group { let alt = col_sample.alt as i32; @@ -131,11 +244,14 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) { let group_size = dynamic_rng.gen_range(group_size.start, group_size.end); + let entity = make_entity( + Vec3::new(wpos2d.x, wpos2d.y, alt + solid_end).map(|e| e as f32), + dynamic_rng, + ); for _ in 0..group_size { - let pos = Vec3::new(wpos2d.x, wpos2d.y, alt + solid_end) - .map(|e| e as f32 + dynamic_rng.gen::()); - supplement.add_entity(make_entity(pos, dynamic_rng) - .with_automatic_name()); + let mut entity = entity.clone(); + entity.pos = entity.pos.map(|e| e + dynamic_rng.gen::()); + supplement.add_entity(entity.with_automatic_name()); } } } From febe79e60237d6922a86fa6394ea44a3bbb40a79 Mon Sep 17 00:00:00 2001 From: Snowram Date: Wed, 14 Oct 2020 23:02:48 +0200 Subject: [PATCH 5/8] Added remaining creature spawns --- server/src/lib.rs | 4 +- server/src/state_ext.rs | 16 +- world/src/layer/wildlife.rs | 375 +++++++++++++++++++++++++++++++++--- 3 files changed, 364 insertions(+), 31 deletions(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index caef411514..81a949f9ba 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -532,7 +532,9 @@ impl Server { // despawning if the entity walks outside of a valid chunk // briefly. If the entity isn't even near a loaded chunk then we get // rid of it. - Spiral2d::new().take(9).all(|offs| terrain.get_key(chunk_key + offs).is_none()) + Spiral2d::new() + .take(9) + .all(|offs| terrain.get_key(chunk_key + offs).is_none()) }) .map(|(entity, _, _)| entity) .collect::>() diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index ee7e52dc69..b3dc3ead64 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -11,13 +11,13 @@ use common::{ sync::{Uid, UidAllocator, WorldSyncExt}, util::Dir, }; +use rand::prelude::*; use specs::{ saveload::MarkerAllocator, Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder, Join, WorldExt, }; use tracing::warn; use vek::*; -use rand::prelude::*; pub trait StateExt { /// Updates a component associated with the entity based on the `Effect` @@ -122,11 +122,15 @@ impl StateExt for State { .create_entity_synced() .with(pos) .with(comp::Vel(Vec3::zero())) - .with(comp::Ori(Dir::new(Vec3::new( - thread_rng().gen_range(-1.0, 1.0), - thread_rng().gen_range(-1.0, 1.0), - 0.0, - ).try_normalized().unwrap_or_default()))) + .with(comp::Ori(Dir::new( + Vec3::new( + thread_rng().gen_range(-1.0, 1.0), + thread_rng().gen_range(-1.0, 1.0), + 0.0, + ) + .try_normalized() + .unwrap_or_default(), + ))) .with(comp::Collider::Box { radius: body.radius(), z_min: 0.0, diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index ad20178fec..e16670ff1f 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -1,6 +1,6 @@ use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, IndexRef, CONFIG}; use common::{ - comp::{quadruped_low, quadruped_medium, quadruped_small, Alignment}, + comp::{biped_large, bird_medium, quadruped_low, quadruped_medium, quadruped_small, Alignment}, generation::{ChunkSupplement, EntityInfo}, terrain::{Block, SpriteKind}, vol::{BaseVol, ReadVol, RectSizedVol, WriteVol}, @@ -33,7 +33,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( bool, // Underwater? fn(&SimChunk, &ColumnSample) -> f32, // Density )] = &[ - // Wolves + // Taiga pack ennemies ( |pos, rng| { EntityInfo::at(pos) @@ -45,43 +45,104 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 3..8, false, - |c, col| close(c.temp, CONFIG.snow_temp, 0.7) * col.tree_density * BASE_DENSITY * 0.3, + |c, col| { + close(c.temp, CONFIG.snow_temp + 0.2, 0.7) * col.tree_density * BASE_DENSITY * 0.05 + }, ), - // Frostfang + // Taiga pack wild ( |pos, rng| { EntityInfo::at(pos) .with_body( quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Mouflon, + ) + .into(), + ) + .with_alignment(Alignment::Wild) + }, + 1..4, + false, + |c, col| close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * BASE_DENSITY * 0.1, + ), + // Taiga solitary wild + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 4) { + 0 => { + bird_medium::Body::random_with(rng, &bird_medium::Species::Eagle).into() + }, + 1 => quadruped_low::Body::random_with(rng, &quadruped_low::Species::Asp) + .into(), + 2 => bird_medium::Body::random_with(rng, &bird_medium::Species::Snowyowl) + .into(), + _ => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Tuskram, + ) + .into(), + }) + .with_alignment(Alignment::Enemy) + }, + 1..2, + false, + |c, col| close(c.temp, CONFIG.snow_temp + 0.2, 0.7) * BASE_DENSITY * 0.3, + ), + // Tundra pack ennemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 2) { + 0 => quadruped_medium::Body::random_with( rng, &quadruped_medium::Species::Frostfang, ) .into(), - ) + _ => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Grolgar, + ) + .into(), + }) .with_alignment(Alignment::Enemy) }, 1..4, false, - |c, col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.15, + |c, col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.2, ), - // Bonerattler + // Tundra solitary ennemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body( + biped_large::Body::random_with(rng, &biped_large::Species::Wendigo).into(), + ) + .with_alignment(Alignment::Enemy) + }, + 1..2, + false, + |c, col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.1, + ), + // Tundra solitary wild ( |pos, rng| { EntityInfo::at(pos) .with_body( quadruped_medium::Body::random_with( rng, - &quadruped_medium::Species::Bonerattler, + &quadruped_medium::Species::Tarasque, ) .into(), ) - .with_alignment(Alignment::Enemy) + .with_alignment(Alignment::Wild) }, - 1..3, + 1..2, false, - |c, col| close(c.humidity, CONFIG.desert_hum, 0.3) * BASE_DENSITY * 0.5, + |c, col| close(c.temp, CONFIG.temperate_temp, 0.15) * BASE_DENSITY * 0.1, ), - // Forest herd (Deer, Hirdrasil, etc.) + // Forest pack wild ( |pos, rng| { EntityInfo::at(pos) @@ -91,11 +152,10 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( &quadruped_medium::Species::Deer, ) .into(), - 1 => quadruped_medium::Body::random_with( - rng, - &quadruped_medium::Species::Hirdrasil, - ) - .into(), + 1 => { + quadruped_small::Body::random_with(rng, &quadruped_small::Species::Rat) + .into() + }, 2 => quadruped_small::Body::random_with( rng, &quadruped_small::Species::Rabbit, @@ -129,21 +189,108 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( &quadruped_small::Species::Squirrel, ) .into(), - _ => quadruped_small::Body::random_with( + _ => bird_medium::Body::random_with(rng, &bird_medium::Species::Chicken) + .into(), + }) + .with_alignment(Alignment::Wild) + }, + 1..8, + false, + |c, col| { + close(c.temp, CONFIG.temperate_temp, 0.7) * col.tree_density * BASE_DENSITY * 6.0 + }, + ), + // Temperate solitary wild + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 12) { + 0 => { + quadruped_small::Body::random_with(rng, &quadruped_small::Species::Fox) + .into() + }, + 1 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Quokka, + ) + .into(), + 2 => { + bird_medium::Body::random_with(rng, &bird_medium::Species::Goose).into() + }, + 3 => bird_medium::Body::random_with(rng, &bird_medium::Species::Peacock) + .into(), + 4 => quadruped_small::Body::random_with( rng, &quadruped_small::Species::Porcupine, ) .into(), + 5 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Skunk, + ) + .into(), + 6 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Raccoon, + ) + .into(), + 7 => bird_medium::Body::random_with(rng, &bird_medium::Species::Cockatrice) + .into(), + 8 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Catoblepas, + ) + .into(), + 9 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Turtle, + ) + .into(), + 10 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Hirdrasil, + ) + .into(), + _ => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Batfox, + ) + .into(), }) .with_alignment(Alignment::Wild) }, - 3..8, + 1..2, false, - |c, col| { - close(c.temp, CONFIG.temperate_temp, 0.7) * col.tree_density * BASE_DENSITY * 1.5 - }, + |c, col| close(c.temp, CONFIG.temperate_temp, 0.15) * BASE_DENSITY * 10.0, ), - // Amphibians (Frog, Axolotl, Crocodile, etc.) + // Rare temperate solitary enemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 12) { + 0 => { + biped_large::Body::random_with(rng, &biped_large::Species::Ogre).into() + }, + 1 => { + biped_large::Body::random_with(rng, &biped_large::Species::Troll).into() + }, + 2 => biped_large::Body::random_with(rng, &biped_large::Species::Dullahan) + .into(), + 3 => biped_large::Body::random_with(rng, &biped_large::Species::Cyclops) + .into(), + _ => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Batfox, + ) + .into(), + }) + .with_alignment(Alignment::Wild) + }, + 1..2, + false, + |c, col| close(c.temp, CONFIG.temperate_temp, 0.8) * BASE_DENSITY * 0.3, + ), + // Temperate rare river wildlife ( |pos, rng| { EntityInfo::at(pos) @@ -184,7 +331,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( 1..3, false, |c, col| { - close(col.temp, CONFIG.tropical_temp, 0.8) + close(col.temp, CONFIG.tropical_temp, 0.3) * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { 0.001 } else { @@ -192,6 +339,186 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( } }, ), + // Temperate common river wildlife + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 3) { + 0 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Beaver, + ) + .into(), + _ => { + bird_medium::Body::random_with(rng, &bird_medium::Species::Duck).into() + }, + }) + .with_alignment(Alignment::Wild) + }, + 1..3, + false, + |c, col| { + close(col.temp, CONFIG.temperate_temp, 0.6) + * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { + 0.001 + } else { + 0.0 + } + }, + ), + // Tropical rock solitary ennemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 8) { + 0 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Dodarock, + ) + .into(), + _ => quadruped_low::Body::random_with( + rng, + &quadruped_low::Species::Rocksnapper, + ) + .into(), + }) + .with_alignment(Alignment::Wild) + }, + 1..2, + false, + |c, col| close(c.temp, CONFIG.tropical_temp, 0.3) * col.rock * BASE_DENSITY * 5.0, + ), + // Jungle solitary ennemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 3) { + 0 => { + quadruped_low::Body::random_with(rng, &quadruped_low::Species::Maneater) + .into() + }, + 1 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Tiger, + ) + .into(), + _ => quadruped_low::Body::random_with( + rng, + &quadruped_low::Species::Rocksnapper, + ) + .into(), + }) + .with_alignment(Alignment::Enemy) + }, + 1..2, + false, + |c, col| { + close(c.temp, CONFIG.tropical_temp, 0.3) + * close(c.humidity, CONFIG.jungle_hum, 0.3) + * BASE_DENSITY + * 5.0 + }, + ), + // Jungle solitary wild + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 4) { + 0 => bird_medium::Body::random_with(rng, &bird_medium::Species::Parrot) + .into(), + 1 => { + quadruped_low::Body::random_with(rng, &quadruped_low::Species::Monitor) + .into() + }, + _ => { + quadruped_low::Body::random_with(rng, &quadruped_low::Species::Tortoise) + .into() + }, + }) + .with_alignment(Alignment::Wild) + }, + 1..2, + false, + |c, col| { + close(c.temp, CONFIG.tropical_temp, 0.3) + * close(c.humidity, CONFIG.jungle_hum, 0.3) + * BASE_DENSITY + * 5.0 + }, + ), + // Tropical pack enemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 3) { + 0 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Lion, + ) + .into(), + 1 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Hyena, + ) + .into(), + _ => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Saber, + ) + .into(), + }) + .with_alignment(Alignment::Enemy) + }, + 1..3, + false, + |c, col| close(c.temp, CONFIG.tropical_temp, 0.15) * BASE_DENSITY * 0.4, + ), + // Desert solitary enemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body( + quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Bonerattler, + ) + .into(), + ) + .with_alignment(Alignment::Enemy) + }, + 1..2, + false, + |c, col| close(c.humidity, CONFIG.desert_hum, 0.3) * BASE_DENSITY * 0.3, + ), + // Desert solitary wild + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 3) { + 0 => { + bird_medium::Body::random_with(rng, &bird_medium::Species::Eagle).into() + }, + 1 => { + quadruped_low::Body::random_with(rng, &quadruped_low::Species::Pangolin) + .into() + }, + 2 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Holladon, + ) + .into(), + _ => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Gecko, + ) + .into(), + }) + .with_alignment(Alignment::Enemy) + }, + 1..2, + false, + |c, col| close(c.temp, CONFIG.desert_temp + 0.2, 0.7) * BASE_DENSITY * 0.4, + ), ]; for y in 0..vol.size_xy().y as i32 { From 2b08e8b931aa74660054d4a6c7c89bf1edbe9f4b Mon Sep 17 00:00:00 2001 From: Snowram Date: Wed, 14 Oct 2020 23:02:58 +0200 Subject: [PATCH 6/8] Make /debug_column pick current location as default --- server/src/cmd.rs | 107 ++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/server/src/cmd.rs b/server/src/cmd.rs index a157dfe00d..7066e321b8 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1561,38 +1561,49 @@ fn handle_debug_column( fn handle_debug_column( server: &mut Server, client: EcsEntity, - _target: EcsEntity, + target: EcsEntity, args: String, action: &ChatCommand, ) { let sim = server.world.sim(); let sampler = server.world.sample_columns(); + let mut wpos = Vec2::new(0, 0); if let Ok((x, y)) = scan_fmt!(&args, &action.arg_fmt(), i32, i32) { - let wpos = Vec2::new(x, y); - /* let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { - e / sz as i32 - }); */ + //match server.state.read_component_copied::(target) + wpos = Vec2::new(x, y); + } + /* let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { + e / sz as i32 + }); */ + else { + match server.state.read_component_copied::(target) { + Some(pos) => wpos = pos.0.xy().map(|x| x as i32), + None => server.notify_client( + client, + ChatType::CommandError.server_msg(String::from("You have no position.")), + ), + } + } + let msg_generator = || { + // let sim_chunk = sim.get(chunk_pos)?; + let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?; + let basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?; + let water_alt = sim.get_interpolated(wpos, |chunk| chunk.water_alt)?; + let chaos = sim.get_interpolated(wpos, |chunk| chunk.chaos)?; + let temp = sim.get_interpolated(wpos, |chunk| chunk.temp)?; + let humidity = sim.get_interpolated(wpos, |chunk| chunk.humidity)?; + let rockiness = sim.get_interpolated(wpos, |chunk| chunk.rockiness)?; + let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?; + 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, server.index.as_index_ref()))?; + let downhill = chunk.downhill; + let river = &chunk.river; + let flux = chunk.flux; - let msg_generator = || { - // let sim_chunk = sim.get(chunk_pos)?; - let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?; - let basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?; - let water_alt = sim.get_interpolated(wpos, |chunk| chunk.water_alt)?; - let chaos = sim.get_interpolated(wpos, |chunk| chunk.chaos)?; - let temp = sim.get_interpolated(wpos, |chunk| chunk.temp)?; - let humidity = sim.get_interpolated(wpos, |chunk| chunk.humidity)?; - let rockiness = sim.get_interpolated(wpos, |chunk| chunk.rockiness)?; - let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?; - 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, server.index.as_index_ref()))?; - let downhill = chunk.downhill; - let river = &chunk.river; - let flux = chunk.flux; - - Some(format!( - r#"wpos: {:?} + Some(format!( + r#"wpos: {:?} alt {:?} ({:?}) water_alt {:?} ({:?}) basement {:?} @@ -1605,35 +1616,29 @@ humidity {:?} rockiness {:?} tree_density {:?} spawn_rate {:?} "#, - wpos, - alt, - col.alt, - water_alt, - col.water_level, - basement, - river, - downhill, - chaos, - flux, - temp, - humidity, - rockiness, - tree_density, - spawn_rate - )) - }; - if let Some(s) = msg_generator() { - server.notify_client(client, ChatType::CommandInfo.server_msg(s)); - } else { - server.notify_client( - client, - ChatType::CommandError.server_msg("Not a pregenerated chunk."), - ); - } + wpos, + alt, + col.alt, + water_alt, + col.water_level, + basement, + river, + downhill, + chaos, + flux, + temp, + humidity, + rockiness, + tree_density, + spawn_rate + )) + }; + if let Some(s) = msg_generator() { + server.notify_client(client, ChatType::CommandInfo.server_msg(s)); } else { server.notify_client( client, - ChatType::CommandError.server_msg(action.help_string()), + ChatType::CommandError.server_msg("Not a pregenerated chunk."), ); } } From a939eac30d2df8b8598bca3edf788cf354439915 Mon Sep 17 00:00:00 2001 From: Snowram Date: Sat, 24 Oct 2020 19:57:46 +0200 Subject: [PATCH 7/8] Refines spawn values, add gradients to debug_column --- server/src/cmd.rs | 3 + server/src/lib.rs | 2 +- world/src/column/mod.rs | 4 + world/src/layer/wildlife.rs | 460 ++++++++++++++++++++++-------------- world/src/lib.rs | 76 ------ 5 files changed, 296 insertions(+), 249 deletions(-) diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7066e321b8..699813ed61 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1598,6 +1598,7 @@ fn handle_debug_column( 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, server.index.as_index_ref()))?; + let gradient = sim.get_gradient_approx(chunk_pos)?; let downhill = chunk.downhill; let river = &chunk.river; let flux = chunk.flux; @@ -1608,6 +1609,7 @@ alt {:?} ({:?}) water_alt {:?} ({:?}) basement {:?} river {:?} +gradient {:?} downhill {:?} chaos {:?} flux {:?} @@ -1623,6 +1625,7 @@ spawn_rate {:?} "#, col.water_level, basement, river, + gradient, downhill, chaos, flux, diff --git a/server/src/lib.rs b/server/src/lib.rs index 81a949f9ba..1e68a88dee 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -58,7 +58,7 @@ use common::{ state::{State, TimeOfDay}, sync::WorldSyncExt, terrain::TerrainChunkSize, - vol::{ReadVol, RectVolSize}, + vol::RectVolSize, }; use futures_executor::block_on; use metrics::{PhysicsMetrics, ServerMetrics, StateTickMetrics, TickMetrics}; diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 466987a82a..dcfc90f88a 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -981,6 +981,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { 5.0 }; + let gradient = sim.get_gradient_approx(chunk_pos); + let path = sim.get_nearest_path(wpos); let cave = sim.get_nearest_cave(wpos); @@ -1024,6 +1026,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { spawn_rate, stone_col, water_dist, + gradient, path, cave, snow_cover, @@ -1053,6 +1056,7 @@ pub struct ColumnSample<'a> { pub spawn_rate: f32, pub stone_col: Rgb, pub water_dist: Option, + pub gradient: Option, pub path: Option<(f32, Vec2, Path, Vec2)>, pub cave: Option<(f32, Vec2, Cave, Vec2)>, pub snow_cover: bool, diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index e16670ff1f..acc2fbd9ec 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -1,11 +1,10 @@ -use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, IndexRef, CONFIG}; +use crate::{column::ColumnSample, sim::SimChunk, IndexRef, CONFIG}; use common::{ comp::{biped_large, bird_medium, quadruped_low, quadruped_medium, quadruped_small, Alignment}, generation::{ChunkSupplement, EntityInfo}, - terrain::{Block, SpriteKind}, + terrain::Block, vol::{BaseVol, ReadVol, RectSizedVol, WriteVol}, }; -use noise::NoiseFn; use rand::prelude::*; use std::{f32, ops::Range}; use vek::*; @@ -23,7 +22,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( wpos2d: Vec2, mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &(impl BaseVol + RectSizedVol + ReadVol + WriteVol), - index: IndexRef, + _index: IndexRef, chunk: &SimChunk, supplement: &mut ChunkSupplement, ) { @@ -33,6 +32,46 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( bool, // Underwater? fn(&SimChunk, &ColumnSample) -> f32, // Density )] = &[ + // Tundra pack ennemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 3) { + 0 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Frostfang, + ) + .into(), + 1 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Roshwalr, + ) + .into(), + _ => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Grolgar, + ) + .into(), + }) + .with_alignment(Alignment::Enemy) + }, + 1..4, + false, + |c, _col| close(c.temp, CONFIG.snow_temp, 0.3) * BASE_DENSITY * 1.0, + ), + // Tundra rare solitary ennemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body( + biped_large::Body::random_with(rng, &biped_large::Species::Wendigo).into(), + ) + .with_alignment(Alignment::Enemy) + }, + 1..2, + false, + |c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.1, + ), // Taiga pack ennemies ( |pos, rng| { @@ -46,7 +85,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( 3..8, false, |c, col| { - close(c.temp, CONFIG.snow_temp + 0.2, 0.7) * col.tree_density * BASE_DENSITY * 0.05 + close(c.temp, CONFIG.snow_temp + 0.2, 0.6) * col.tree_density * BASE_DENSITY * 1.0 }, ), // Taiga pack wild @@ -64,13 +103,13 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 1..4, false, - |c, col| close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * BASE_DENSITY * 0.1, + |c, _col| close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * BASE_DENSITY * 1.0, ), // Taiga solitary wild ( |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0, 4) { + .with_body(match rng.gen_range(0, 5) { 0 => { bird_medium::Body::random_with(rng, &bird_medium::Species::Eagle).into() }, @@ -78,75 +117,50 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( .into(), 2 => bird_medium::Body::random_with(rng, &bird_medium::Species::Snowyowl) .into(), + 3 => quadruped_small::Body { + species: quadruped_small::Species::Fox, + body_type: quadruped_small::BodyType::Female, + } + .into(), _ => quadruped_medium::Body::random_with( rng, &quadruped_medium::Species::Tuskram, ) .into(), }) - .with_alignment(Alignment::Enemy) + .with_alignment(Alignment::Wild) }, 1..2, false, - |c, col| close(c.temp, CONFIG.snow_temp + 0.2, 0.7) * BASE_DENSITY * 0.3, + |c, _col| close(c.temp, CONFIG.snow_temp + 0.2, 0.6) * BASE_DENSITY * 5.0, ), - // Tundra pack ennemies + // Temperate pack ennemies ( |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 2) { 0 => quadruped_medium::Body::random_with( rng, - &quadruped_medium::Species::Frostfang, + &quadruped_medium::Species::Tarasque, ) .into(), _ => quadruped_medium::Body::random_with( rng, - &quadruped_medium::Species::Grolgar, + &quadruped_medium::Species::Saber, ) .into(), }) .with_alignment(Alignment::Enemy) }, - 1..4, - false, - |c, col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.2, - ), - // Tundra solitary ennemies - ( - |pos, rng| { - EntityInfo::at(pos) - .with_body( - biped_large::Body::random_with(rng, &biped_large::Species::Wendigo).into(), - ) - .with_alignment(Alignment::Enemy) - }, 1..2, false, - |c, col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.1, + |c, _col| close(c.temp, CONFIG.temperate_temp, 0.35) * BASE_DENSITY * 1.0, ), - // Tundra solitary wild + // Temperate pack wild ( |pos, rng| { EntityInfo::at(pos) - .with_body( - quadruped_medium::Body::random_with( - rng, - &quadruped_medium::Species::Tarasque, - ) - .into(), - ) - .with_alignment(Alignment::Wild) - }, - 1..2, - false, - |c, col| close(c.temp, CONFIG.temperate_temp, 0.15) * BASE_DENSITY * 0.1, - ), - // Forest pack wild - ( - |pos, rng| { - EntityInfo::at(pos) - .with_body(match rng.gen_range(0, 10) { + .with_body(match rng.gen_range(0, 11) { 0 => quadruped_medium::Body::random_with( rng, &quadruped_medium::Species::Deer, @@ -181,12 +195,16 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 7 => quadruped_small::Body::random_with( rng, - &quadruped_small::Species::Truffler, + &quadruped_small::Species::Squirrel, ) .into(), - 8 => quadruped_small::Body::random_with( + 8 => { + quadruped_small::Body::random_with(rng, &quadruped_small::Species::Hare) + .into() + }, + 9 => quadruped_medium::Body::random_with( rng, - &quadruped_small::Species::Squirrel, + &quadruped_medium::Species::Horse, ) .into(), _ => bird_medium::Body::random_with(rng, &bird_medium::Species::Chicken) @@ -196,19 +214,24 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 1..8, false, - |c, col| { - close(c.temp, CONFIG.temperate_temp, 0.7) * col.tree_density * BASE_DENSITY * 6.0 + |c, _col| { + close(c.temp, CONFIG.temperate_temp, 0.5) + * close(c.humidity, CONFIG.forest_hum, 0.4) + //* col.tree_density + * BASE_DENSITY + * 4.0 }, ), // Temperate solitary wild ( |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0, 12) { - 0 => { - quadruped_small::Body::random_with(rng, &quadruped_small::Species::Fox) - .into() - }, + .with_body(match rng.gen_range(0, 15) { + 0 => quadruped_small::Body { + species: quadruped_small::Species::Fox, + body_type: quadruped_small::BodyType::Male, + } + .into(), 1 => quadruped_small::Body::random_with( rng, &quadruped_small::Species::Quokka, @@ -251,6 +274,21 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( &quadruped_medium::Species::Hirdrasil, ) .into(), + 11 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Kelpie, + ) + .into(), + 12 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Truffler, + ) + .into(), + 13 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Donkey, + ) + .into(), _ => quadruped_small::Body::random_with( rng, &quadruped_small::Species::Batfox, @@ -261,13 +299,18 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 1..2, false, - |c, col| close(c.temp, CONFIG.temperate_temp, 0.15) * BASE_DENSITY * 10.0, + |c, _col| { + close(c.temp, CONFIG.temperate_temp, 0.5) + * BASE_DENSITY + * close(c.humidity, CONFIG.forest_hum, 0.4) + * 8.0 + }, ), // Rare temperate solitary enemies ( |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0, 12) { + .with_body(match rng.gen_range(0, 4) { 0 => { biped_large::Body::random_with(rng, &biped_large::Species::Ogre).into() }, @@ -276,70 +319,16 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 2 => biped_large::Body::random_with(rng, &biped_large::Species::Dullahan) .into(), - 3 => biped_large::Body::random_with(rng, &biped_large::Species::Cyclops) + _ => biped_large::Body::random_with(rng, &biped_large::Species::Cyclops) .into(), - _ => quadruped_small::Body::random_with( - rng, - &quadruped_small::Species::Batfox, - ) - .into(), }) - .with_alignment(Alignment::Wild) + .with_alignment(Alignment::Enemy) }, 1..2, false, - |c, col| close(c.temp, CONFIG.temperate_temp, 0.8) * BASE_DENSITY * 0.3, + |c, _col| close(c.temp, CONFIG.temperate_temp, 0.8) * BASE_DENSITY * 0.1, ), - // Temperate rare river wildlife - ( - |pos, rng| { - EntityInfo::at(pos) - .with_body(match rng.gen_range(0, 6) { - 0 => { - quadruped_small::Body::random_with(rng, &quadruped_small::Species::Frog) - .into() - }, - 1 => quadruped_small::Body::random_with( - rng, - &quadruped_small::Species::Axolotl, - ) - .into(), - 2 => quadruped_small::Body::random_with( - rng, - &quadruped_small::Species::Fungome, - ) - .into(), - // WE GROW 'EM BIG 'ERE - 3 => quadruped_low::Body::random_with( - rng, - &quadruped_low::Species::Crocodile, - ) - .into(), - 4 => quadruped_low::Body::random_with( - rng, - &quadruped_low::Species::Alligator, - ) - .into(), - _ => quadruped_low::Body::random_with( - rng, - &quadruped_low::Species::Salamander, - ) - .into(), - }) - .with_alignment(Alignment::Wild) - }, - 1..3, - false, - |c, col| { - close(col.temp, CONFIG.tropical_temp, 0.3) - * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { - 0.001 - } else { - 0.0 - } - }, - ), - // Temperate common river wildlife + // Temperate river wildlife ( |pos, rng| { EntityInfo::at(pos) @@ -349,18 +338,44 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( &quadruped_small::Species::Beaver, ) .into(), + 1 => quadruped_low::Body { + species: quadruped_low::Species::Salamander, + body_type: quadruped_low::BodyType::Female, + } + .into(), _ => { bird_medium::Body::random_with(rng, &bird_medium::Species::Duck).into() }, }) .with_alignment(Alignment::Wild) }, - 1..3, + 1..2, false, - |c, col| { + |_c, col| { close(col.temp, CONFIG.temperate_temp, 0.6) * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { - 0.001 + 0.003 + } else { + 0.0 + } + }, + ), + // Temperate river ennemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body( + quadruped_low::Body::random_with(rng, &quadruped_low::Species::Hakulaq) + .into(), + ) + .with_alignment(Alignment::Enemy) + }, + 1..2, + false, + |_c, col| { + close(col.temp, CONFIG.temperate_temp, 0.6) + * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { + 0.0001 } else { 0.0 } @@ -370,23 +385,18 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( ( |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0, 8) { - 0 => quadruped_small::Body::random_with( + .with_body( + quadruped_small::Body::random_with( rng, &quadruped_small::Species::Dodarock, ) .into(), - _ => quadruped_low::Body::random_with( - rng, - &quadruped_low::Species::Rocksnapper, - ) - .into(), - }) - .with_alignment(Alignment::Wild) + ) + .with_alignment(Alignment::Enemy) }, 1..2, false, - |c, col| close(c.temp, CONFIG.tropical_temp, 0.3) * col.rock * BASE_DENSITY * 5.0, + |c, col| close(c.temp, CONFIG.tropical_temp + 0.1, 0.5) * col.rock * BASE_DENSITY * 5.0, ), // Jungle solitary ennemies ( @@ -412,18 +422,18 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 1..2, false, - |c, col| { - close(c.temp, CONFIG.tropical_temp, 0.3) + |c, _col| { + close(c.temp, CONFIG.tropical_temp + 0.1, 0.4) * close(c.humidity, CONFIG.jungle_hum, 0.3) * BASE_DENSITY - * 5.0 + * 4.0 }, ), // Jungle solitary wild ( |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0, 4) { + .with_body(match rng.gen_range(0, 3) { 0 => bird_medium::Body::random_with(rng, &bird_medium::Species::Parrot) .into(), 1 => { @@ -439,31 +449,27 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 1..2, false, - |c, col| { - close(c.temp, CONFIG.tropical_temp, 0.3) + |c, _col| { + close(c.temp, CONFIG.tropical_temp, 0.5) * close(c.humidity, CONFIG.jungle_hum, 0.3) * BASE_DENSITY - * 5.0 + * 8.0 }, ), - // Tropical pack enemies + // Tropical rare river enemy ( |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0, 3) { - 0 => quadruped_medium::Body::random_with( + .with_body(match rng.gen_range(0, 2) { + // WE GROW 'EM BIG 'ERE + 0 => quadruped_low::Body::random_with( rng, - &quadruped_medium::Species::Lion, + &quadruped_low::Species::Crocodile, ) .into(), - 1 => quadruped_small::Body::random_with( + _ => quadruped_low::Body::random_with( rng, - &quadruped_small::Species::Hyena, - ) - .into(), - _ => quadruped_medium::Body::random_with( - rng, - &quadruped_medium::Species::Saber, + &quadruped_low::Species::Alligator, ) .into(), }) @@ -471,45 +477,110 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 1..3, false, - |c, col| close(c.temp, CONFIG.tropical_temp, 0.15) * BASE_DENSITY * 0.4, + |_c, col| { + close(col.temp, CONFIG.tropical_temp + 0.2, 0.5) + * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { + 0.0002 + } else { + 0.0 + } + }, ), - // Desert solitary enemies + // Tropical rare river wild + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 3) { + 0 => { + quadruped_small::Body::random_with(rng, &quadruped_small::Species::Frog) + .into() + }, + 1 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Axolotl, + ) + .into(), + _ => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Fungome, + ) + .into(), + }) + .with_alignment(Alignment::Wild) + }, + 1..3, + false, + |_c, col| { + close(col.temp, CONFIG.tropical_temp, 0.5) + * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { + 0.001 + } else { + 0.0 + } + }, + ), + // Tropical pack enemies + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 2) { + 0 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Lion, + ) + .into(), + _ => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Hyena, + ) + .into(), + }) + .with_alignment(Alignment::Enemy) + }, + 1..3, + false, + |c, _col| { + close(c.temp, CONFIG.tropical_temp + 0.1, 0.4) + * close(c.humidity, CONFIG.desert_hum, 0.4) + * BASE_DENSITY + * 2.0 + }, + ), + // Desert pack wild ( |pos, rng| { EntityInfo::at(pos) .with_body( quadruped_medium::Body::random_with( rng, - &quadruped_medium::Species::Bonerattler, + &quadruped_medium::Species::Antelope, ) .into(), ) - .with_alignment(Alignment::Enemy) + .with_alignment(Alignment::Wild) }, - 1..2, + 3..8, false, - |c, col| close(c.humidity, CONFIG.desert_hum, 0.3) * BASE_DENSITY * 0.3, + |c, _col| { + close(c.temp, CONFIG.tropical_temp + 0.1, 0.4) + * close(c.humidity, CONFIG.desert_hum, 0.4) + * BASE_DENSITY + * 1.0 + }, ), - // Desert solitary wild + // Desert solitary enemies ( |pos, rng| { EntityInfo::at(pos) - .with_body(match rng.gen_range(0, 3) { - 0 => { - bird_medium::Body::random_with(rng, &bird_medium::Species::Eagle).into() - }, - 1 => { - quadruped_low::Body::random_with(rng, &quadruped_low::Species::Pangolin) - .into() - }, - 2 => quadruped_small::Body::random_with( + .with_body(match rng.gen_range(0, 2) { + 0 => quadruped_medium::Body::random_with( rng, - &quadruped_small::Species::Holladon, + &quadruped_medium::Species::Bonerattler, ) .into(), - _ => quadruped_small::Body::random_with( + _ => quadruped_low::Body::random_with( rng, - &quadruped_small::Species::Gecko, + &quadruped_low::Species::Sandshark, ) .into(), }) @@ -517,7 +588,48 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }, 1..2, false, - |c, col| close(c.temp, CONFIG.desert_temp + 0.2, 0.7) * BASE_DENSITY * 0.4, + |c, _col| { + close(c.temp, CONFIG.desert_temp + 0.2, 0.3) + * close(c.humidity, CONFIG.desert_hum, 0.5) + * BASE_DENSITY + * 1.5 + }, + ), + // Desert solitary wild + ( + |pos, rng| { + EntityInfo::at(pos) + .with_body(match rng.gen_range(0, 5) { + 0 => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Holladon, + ) + .into(), + 1 => { + quadruped_low::Body::random_with(rng, &quadruped_low::Species::Pangolin) + .into() + }, + 2 => quadruped_medium::Body::random_with( + rng, + &quadruped_medium::Species::Camel, + ) + .into(), + 3 => quadruped_low::Body { + species: quadruped_low::Species::Salamander, + body_type: quadruped_low::BodyType::Male, + } + .into(), + _ => quadruped_small::Body::random_with( + rng, + &quadruped_small::Species::Gecko, + ) + .into(), + }) + .with_alignment(Alignment::Wild) + }, + 1..2, + false, + |c, _col| close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * BASE_DENSITY * 5.0, ), ]; @@ -537,12 +649,12 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( let underwater = col_sample.water_level > col_sample.alt; let entity_group = scatter.iter().enumerate().find_map( - |(i, (make_entity, group_size, is_underwater, f))| { + |(_i, (make_entity, group_size, is_underwater, f))| { let density = f(chunk, col_sample); if density > 0.0 - && RandomField::new(i as u32 * 7) - .chance(Vec3::new(wpos2d.x, wpos2d.y, i as i32), density) + && dynamic_rng.gen::() < density && underwater == *is_underwater + && col_sample.gradient < Some(1.3) { Some((make_entity, group_size.clone())) } else { @@ -553,7 +665,6 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( if let Some((make_entity, group_size)) = entity_group { let alt = col_sample.alt as i32; - // Find the intersection between ground and air, if there is one near the // surface if let Some(solid_end) = (-4..8) @@ -575,9 +686,14 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( Vec3::new(wpos2d.x, wpos2d.y, alt + solid_end).map(|e| e as f32), dynamic_rng, ); - for _ in 0..group_size { + for e in 0..group_size { let mut entity = entity.clone(); - entity.pos = entity.pos.map(|e| e + dynamic_rng.gen::()); + entity.pos = entity.pos.map(|e| e + dynamic_rng.gen::()) + + Vec3::new( + (e as f32 / group_size as f32 * 2.0 * f32::consts::PI).sin(), + (e as f32 / group_size as f32 * 2.0 * f32::consts::PI).cos(), + 0.0, + ); supplement.add_entity(entity.with_automatic_name()); } } diff --git a/world/src/lib.rs b/world/src/lib.rs index bfa5183e19..172c6f3f1d 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -41,7 +41,6 @@ use crate::{ util::{Grid, Sampler}, }; use common::{ - comp::{self, bird_medium, quadruped_low, quadruped_medium, quadruped_small}, generation::{ChunkSupplement, EntityInfo}, msg::WorldMapMsg, terrain::{Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize}, @@ -246,83 +245,8 @@ impl World { (Vec3::from(chunk_wpos2d) + lpos).map(|e: i32| e as f32) + 0.5 }; - const SPAWN_RATE: f32 = 0.1; let mut supplement = ChunkSupplement { entities: Vec::new(), - /* - entities: if dynamic_rng.gen::() < SPAWN_RATE - && sim_chunk.chaos < 0.5 - && !sim_chunk.is_underwater() - { - // TODO: REFACTOR: Define specific alignments in a config file instead of here - let is_hostile: bool; - let is_giant = dynamic_rng.gen_range(0, 8) == 0; - let quadmed = comp::Body::QuadrupedMedium(quadruped_medium::Body::random()); // Not all of them are hostile so we have to do the rng here - let quadlow = comp::Body::QuadrupedLow(quadruped_low::Body::random()); // Not all of them are hostile so we have to do the rng here - let entity = EntityInfo::at(gen_entity_pos(&mut dynamic_rng)) - .do_if(is_giant, |e| e.into_giant()) - .with_body(match dynamic_rng.gen_range(0, 5) { - 0 => { - match quadmed { - comp::Body::QuadrupedMedium(quadruped_medium) => { - match quadruped_medium.species { - quadruped_medium::Species::Catoblepas => is_hostile = false, - quadruped_medium::Species::Mouflon => is_hostile = false, - quadruped_medium::Species::Tuskram => is_hostile = false, - quadruped_medium::Species::Deer => is_hostile = false, - quadruped_medium::Species::Hirdrasil => is_hostile = false, - quadruped_medium::Species::Donkey => is_hostile = false, - quadruped_medium::Species::Camel => is_hostile = false, - quadruped_medium::Species::Zebra => is_hostile = false, - quadruped_medium::Species::Antelope => is_hostile = false, - quadruped_medium::Species::Kelpie => is_hostile = false, - quadruped_medium::Species::Horse => is_hostile = false, - _ => is_hostile = true, - } - }, - _ => is_hostile = true, - }; - quadmed - }, - 1 => { - is_hostile = false; - comp::Body::BirdMedium(bird_medium::Body::random()) - }, - 2 => { - match quadlow { - comp::Body::QuadrupedLow(quadruped_low) => { - match quadruped_low.species { - quadruped_low::Species::Crocodile => is_hostile = true, - quadruped_low::Species::Alligator => is_hostile = true, - quadruped_low::Species::Maneater => is_hostile = true, - quadruped_low::Species::Sandshark => is_hostile = true, - quadruped_low::Species::Hakulaq => is_hostile = true, - _ => is_hostile = false, - } - }, - _ => is_hostile = false, - }; - quadlow - }, - _ => { - is_hostile = false; - comp::Body::QuadrupedSmall(quadruped_small::Body::random()) - }, - }) - .with_alignment(if is_hostile { - comp::Alignment::Enemy - } else if is_giant { - comp::Alignment::Npc - } else { - comp::Alignment::Wild - }) - .with_automatic_name(); - - vec![entity] - } else { - Vec::new() - }, - */ }; if sim_chunk.contains_waypoint { From 8235e94aa42c148ffd04ae5326b7137000085a0b Mon Sep 17 00:00:00 2001 From: Monty Marz Date: Sat, 21 Nov 2020 13:33:52 +0100 Subject: [PATCH 8/8] Conversion to struct to please clippy --- CHANGELOG.md | 1 + server/src/cmd.rs | 8 +- world/src/layer/wildlife.rs | 270 +++++++++++++++++++----------------- 3 files changed, 144 insertions(+), 135 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adbab4c8f9..cf2bf6b0ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Made settings less likely to reset when the format changes - Adjusted some keybindings - Consumables can now trigger multiple effects and buffs +- Overhauled overworld spawns depending on chunk attributes ### Removed diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 699813ed61..88fca9a976 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1569,13 +1569,8 @@ fn handle_debug_column( let sampler = server.world.sample_columns(); let mut wpos = Vec2::new(0, 0); if let Ok((x, y)) = scan_fmt!(&args, &action.arg_fmt(), i32, i32) { - //match server.state.read_component_copied::(target) wpos = Vec2::new(x, y); - } - /* let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| { - e / sz as i32 - }); */ - else { + } else { match server.state.read_component_copied::(target) { Some(pos) => wpos = pos.0.xy().map(|x| x as i32), None => server.notify_client( @@ -1585,7 +1580,6 @@ fn handle_debug_column( } } let msg_generator = || { - // let sim_chunk = sim.get(chunk_pos)?; let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?; let basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?; let water_alt = sim.get_interpolated(wpos, |chunk| chunk.water_alt)?; diff --git a/world/src/layer/wildlife.rs b/world/src/layer/wildlife.rs index acc2fbd9ec..d5552cfbe1 100644 --- a/world/src/layer/wildlife.rs +++ b/world/src/layer/wildlife.rs @@ -26,15 +26,17 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( chunk: &SimChunk, supplement: &mut ChunkSupplement, ) { - let scatter: &[( - fn(Vec3, &mut R) -> EntityInfo, // Entity - Range, // Group size range - bool, // Underwater? - fn(&SimChunk, &ColumnSample) -> f32, // Density - )] = &[ + struct Entry { + make_entity: fn(Vec3, &mut R) -> EntityInfo, // Entity + group_size: Range, // Group size range + is_underwater: bool, // Underwater? + get_density: fn(&SimChunk, &ColumnSample) -> f32, // Density + } + + let scatter: &[Entry] = &[ // Tundra pack ennemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 3) { 0 => quadruped_medium::Body::random_with( @@ -55,26 +57,26 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Enemy) }, - 1..4, - false, - |c, _col| close(c.temp, CONFIG.snow_temp, 0.3) * BASE_DENSITY * 1.0, - ), + group_size: 1..4, + is_underwater: false, + get_density: |c, _col| close(c.temp, CONFIG.snow_temp, 0.3) * BASE_DENSITY * 1.0, + }, // Tundra rare solitary ennemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body( biped_large::Body::random_with(rng, &biped_large::Species::Wendigo).into(), ) .with_alignment(Alignment::Enemy) }, - 1..2, - false, - |c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.1, - ), + group_size: 1..2, + is_underwater: false, + get_density: |c, _col| close(c.temp, CONFIG.snow_temp, 0.15) * BASE_DENSITY * 0.1, + }, // Taiga pack ennemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body( quadruped_medium::Body::random_with(rng, &quadruped_medium::Species::Wolf) @@ -82,15 +84,15 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( ) .with_alignment(Alignment::Enemy) }, - 3..8, - false, - |c, col| { + group_size: 3..8, + is_underwater: false, + get_density: |c, col| { close(c.temp, CONFIG.snow_temp + 0.2, 0.6) * col.tree_density * BASE_DENSITY * 1.0 }, - ), + }, // Taiga pack wild - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body( quadruped_medium::Body::random_with( @@ -101,13 +103,13 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( ) .with_alignment(Alignment::Wild) }, - 1..4, - false, - |c, _col| close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * BASE_DENSITY * 1.0, - ), + group_size: 1..4, + is_underwater: false, + get_density: |c, _col| close(c.temp, CONFIG.snow_temp + 0.2, 0.2) * BASE_DENSITY * 1.0, + }, // Taiga solitary wild - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 5) { 0 => { @@ -130,13 +132,13 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Wild) }, - 1..2, - false, - |c, _col| close(c.temp, CONFIG.snow_temp + 0.2, 0.6) * BASE_DENSITY * 5.0, - ), + group_size: 1..2, + is_underwater: false, + get_density: |c, _col| close(c.temp, CONFIG.snow_temp + 0.2, 0.6) * BASE_DENSITY * 5.0, + }, // Temperate pack ennemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 2) { 0 => quadruped_medium::Body::random_with( @@ -152,13 +154,13 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Enemy) }, - 1..2, - false, - |c, _col| close(c.temp, CONFIG.temperate_temp, 0.35) * BASE_DENSITY * 1.0, - ), + group_size: 1..2, + is_underwater: false, + get_density: |c, _col| close(c.temp, CONFIG.temperate_temp, 0.35) * BASE_DENSITY * 1.0, + }, // Temperate pack wild - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 11) { 0 => quadruped_medium::Body::random_with( @@ -212,19 +214,19 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Wild) }, - 1..8, - false, - |c, _col| { + group_size: 1..8, + is_underwater: false, + get_density: |c, _col| { close(c.temp, CONFIG.temperate_temp, 0.5) * close(c.humidity, CONFIG.forest_hum, 0.4) //* col.tree_density * BASE_DENSITY * 4.0 }, - ), + }, // Temperate solitary wild - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 15) { 0 => quadruped_small::Body { @@ -297,18 +299,18 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Wild) }, - 1..2, - false, - |c, _col| { + group_size: 1..2, + is_underwater: false, + get_density: |c, _col| { close(c.temp, CONFIG.temperate_temp, 0.5) * BASE_DENSITY * close(c.humidity, CONFIG.forest_hum, 0.4) * 8.0 }, - ), + }, // Rare temperate solitary enemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 4) { 0 => { @@ -324,13 +326,13 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Enemy) }, - 1..2, - false, - |c, _col| close(c.temp, CONFIG.temperate_temp, 0.8) * BASE_DENSITY * 0.1, - ), + group_size: 1..2, + is_underwater: false, + get_density: |c, _col| close(c.temp, CONFIG.temperate_temp, 0.8) * BASE_DENSITY * 0.1, + }, // Temperate river wildlife - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 3) { 0 => quadruped_small::Body::random_with( @@ -349,9 +351,9 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Wild) }, - 1..2, - false, - |_c, col| { + group_size: 1..2, + is_underwater: false, + get_density: |_c, col| { close(col.temp, CONFIG.temperate_temp, 0.6) * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { 0.003 @@ -359,10 +361,10 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( 0.0 } }, - ), + }, // Temperate river ennemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body( quadruped_low::Body::random_with(rng, &quadruped_low::Species::Hakulaq) @@ -370,9 +372,9 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( ) .with_alignment(Alignment::Enemy) }, - 1..2, - false, - |_c, col| { + group_size: 1..2, + is_underwater: false, + get_density: |_c, col| { close(col.temp, CONFIG.temperate_temp, 0.6) * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { 0.0001 @@ -380,10 +382,10 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( 0.0 } }, - ), + }, // Tropical rock solitary ennemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body( quadruped_small::Body::random_with( @@ -394,13 +396,15 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( ) .with_alignment(Alignment::Enemy) }, - 1..2, - false, - |c, col| close(c.temp, CONFIG.tropical_temp + 0.1, 0.5) * col.rock * BASE_DENSITY * 5.0, - ), + group_size: 1..2, + is_underwater: false, + get_density: |c, col| { + close(c.temp, CONFIG.tropical_temp + 0.1, 0.5) * col.rock * BASE_DENSITY * 5.0 + }, + }, // Jungle solitary ennemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 3) { 0 => { @@ -420,18 +424,18 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Enemy) }, - 1..2, - false, - |c, _col| { + group_size: 1..2, + is_underwater: false, + get_density: |c, _col| { close(c.temp, CONFIG.tropical_temp + 0.1, 0.4) * close(c.humidity, CONFIG.jungle_hum, 0.3) * BASE_DENSITY * 4.0 }, - ), + }, // Jungle solitary wild - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 3) { 0 => bird_medium::Body::random_with(rng, &bird_medium::Species::Parrot) @@ -447,18 +451,18 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Wild) }, - 1..2, - false, - |c, _col| { + group_size: 1..2, + is_underwater: false, + get_density: |c, _col| { close(c.temp, CONFIG.tropical_temp, 0.5) * close(c.humidity, CONFIG.jungle_hum, 0.3) * BASE_DENSITY * 8.0 }, - ), + }, // Tropical rare river enemy - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 2) { // WE GROW 'EM BIG 'ERE @@ -475,9 +479,9 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Enemy) }, - 1..3, - false, - |_c, col| { + group_size: 1..3, + is_underwater: false, + get_density: |_c, col| { close(col.temp, CONFIG.tropical_temp + 0.2, 0.5) * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { 0.0002 @@ -485,10 +489,10 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( 0.0 } }, - ), + }, // Tropical rare river wild - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 3) { 0 => { @@ -508,9 +512,9 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Wild) }, - 1..3, - false, - |_c, col| { + group_size: 1..3, + is_underwater: false, + get_density: |_c, col| { close(col.temp, CONFIG.tropical_temp, 0.5) * if col.water_dist.map(|d| d < 10.0).unwrap_or(false) { 0.001 @@ -518,10 +522,10 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( 0.0 } }, - ), + }, // Tropical pack enemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 2) { 0 => quadruped_medium::Body::random_with( @@ -537,18 +541,18 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Enemy) }, - 1..3, - false, - |c, _col| { + group_size: 1..3, + is_underwater: false, + get_density: |c, _col| { close(c.temp, CONFIG.tropical_temp + 0.1, 0.4) * close(c.humidity, CONFIG.desert_hum, 0.4) * BASE_DENSITY * 2.0 }, - ), + }, // Desert pack wild - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body( quadruped_medium::Body::random_with( @@ -559,18 +563,18 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( ) .with_alignment(Alignment::Wild) }, - 3..8, - false, - |c, _col| { + group_size: 3..8, + is_underwater: false, + get_density: |c, _col| { close(c.temp, CONFIG.tropical_temp + 0.1, 0.4) * close(c.humidity, CONFIG.desert_hum, 0.4) * BASE_DENSITY * 1.0 }, - ), + }, // Desert solitary enemies - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 2) { 0 => quadruped_medium::Body::random_with( @@ -586,18 +590,18 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Enemy) }, - 1..2, - false, - |c, _col| { + group_size: 1..2, + is_underwater: false, + get_density: |c, _col| { close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * close(c.humidity, CONFIG.desert_hum, 0.5) * BASE_DENSITY * 1.5 }, - ), + }, // Desert solitary wild - ( - |pos, rng| { + Entry { + make_entity: |pos, rng| { EntityInfo::at(pos) .with_body(match rng.gen_range(0, 5) { 0 => quadruped_small::Body::random_with( @@ -627,10 +631,12 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( }) .with_alignment(Alignment::Wild) }, - 1..2, - false, - |c, _col| close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * BASE_DENSITY * 5.0, - ), + group_size: 1..2, + is_underwater: false, + get_density: |c, _col| { + close(c.temp, CONFIG.desert_temp + 0.2, 0.3) * BASE_DENSITY * 5.0 + }, + }, ]; for y in 0..vol.size_xy().y as i32 { @@ -649,8 +655,16 @@ pub fn apply_wildlife_supplement<'a, R: Rng>( let underwater = col_sample.water_level > col_sample.alt; let entity_group = scatter.iter().enumerate().find_map( - |(_i, (make_entity, group_size, is_underwater, f))| { - let density = f(chunk, col_sample); + |( + _i, + Entry { + make_entity, + group_size, + is_underwater, + get_density, + }, + )| { + let density = get_density(chunk, col_sample); if density > 0.0 && dynamic_rng.gen::() < density && underwater == *is_underwater