Added large caves, cave scatter, removed old caves

This commit is contained in:
Joshua Barretto 2020-07-25 12:19:13 +01:00
parent 10757d0325
commit 41229b4665
13 changed files with 97 additions and 111 deletions

View File

@ -0,0 +1,10 @@
[
(25, Velorite),
(50, VeloriteFrag),
(15, WhiteFlower),
(80, ShortGrass),
(150, Mushroom),
(5, Chest),
(2, Crate),
(1, Scarecrow),
]

View File

@ -1,5 +1,4 @@
pub mod armor;
pub mod lottery;
pub mod tool;
// Reexports
@ -9,6 +8,7 @@ use crate::{
assets::{self, Asset},
effect::Effect,
terrain::{Block, BlockKind},
lottery::Lottery,
};
use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage};
@ -161,7 +161,7 @@ impl Item {
BlockKind::ShortGrass => Some(assets::load_expect_cloned("common.items.grasses.short")),
BlockKind::Coconut => Some(assets::load_expect_cloned("common.items.food.coconut")),
BlockKind::Chest => {
let chosen = assets::load_expect::<lottery::Lottery<_>>("common.loot_table");
let chosen = assets::load_expect::<Lottery<String>>("common.loot_table");
let chosen = chosen.choose();
Some(assets::load_expect_cloned(chosen))

View File

@ -39,5 +39,6 @@ pub mod terrain;
pub mod util;
pub mod vol;
pub mod volumes;
pub mod lottery;
pub use loadout_builder::LoadoutBuilder;

View File

@ -1,25 +1,19 @@
use crate::assets::{self, Asset};
use rand::prelude::*;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::{fs::File, io::BufReader};
// Generate a random float between 0 and 1
pub fn rand() -> f32 {
let mut rng = rand::thread_rng();
rng.gen()
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Lottery<T> {
items: Vec<(f32, T)>,
total: f32,
}
impl Asset for Lottery<String> {
impl<T: DeserializeOwned + Send + Sync> Asset for Lottery<T> {
const ENDINGS: &'static [&'static str] = &["ron"];
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
ron::de::from_reader::<BufReader<File>, Vec<(f32, String)>>(buf_reader)
ron::de::from_reader::<BufReader<File>, Vec<(f32, T)>>(buf_reader)
.map(|items| Lottery::from_rates(items.into_iter()))
.map_err(assets::Error::parse_error)
}
@ -37,8 +31,8 @@ impl<T> Lottery<T> {
Self { items, total }
}
pub fn choose(&self) -> &T {
let x = rand() * self.total;
pub fn choose_seeded(&self, seed: u32) -> &T {
let x = ((seed % 65536) as f32 / 65536.0) * self.total;
&self.items[self
.items
.binary_search_by(|(y, _)| y.partial_cmp(&x).unwrap())
@ -46,6 +40,10 @@ impl<T> Lottery<T> {
.1
}
pub fn choose(&self) -> &T {
self.choose_seeded(thread_rng().gen())
}
pub fn iter(&self) -> impl Iterator<Item = &(f32, T)> { self.items.iter() }
}

View File

@ -2,7 +2,7 @@ use crate::{client::Client, Server, SpawnPoint, StateExt};
use common::{
assets,
comp::{
self, item::lottery::Lottery, object, Alignment, Body, Damage, DamageSource, Group,
self, object, Alignment, Body, Damage, DamageSource, Group,
HealthChange, HealthSource, Player, Pos, Stats,
},
msg::{PlayerListUpdate, ServerMsg},
@ -12,6 +12,7 @@ use common::{
sys::combat::BLOCK_ANGLE,
terrain::{Block, TerrainGrid},
vol::{ReadVol, Vox},
lottery::Lottery,
};
use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt};
use tracing::error;
@ -182,7 +183,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
item_drops.remove(entity);
item_drop.0
} else {
let chosen = assets::load_expect::<Lottery<_>>("common.loot_table");
let chosen = assets::load_expect::<Lottery<String>>("common.loot_table");
let chosen = chosen.choose();
assets::load_expect_cloned(chosen)

View File

@ -164,8 +164,6 @@ impl<'a> BlockGen<'a> {
//tree_density,
//forest_kind,
//close_structures,
cave_xy,
cave_alt,
marble,
marble_small,
rock,
@ -370,23 +368,6 @@ impl<'a> BlockGen<'a> {
None
}
})
.and_then(|block| {
// Caves
// Underground
let cave = cave_xy.powf(2.0)
* (wposf.z as f32 - cave_alt)
.div(40.0)
.powf(4.0)
.neg()
.add(1.0)
> 0.9993;
if cave && wposf.z as f32 > water_height + 3.0 {
None
} else {
Some(block)
}
})
.or_else(|| {
// Water
if (wposf.z as f32) < water_height {
@ -422,14 +403,7 @@ pub struct ZCache<'a> {
impl<'a> ZCache<'a> {
pub fn get_z_limits(&self, block_gen: &mut BlockGen, index: &Index) -> (f32, f32, f32) {
let cave_depth =
if self.sample.cave_xy.abs() > 0.9 && self.sample.water_level <= self.sample.alt {
(self.sample.alt - self.sample.cave_alt + 8.0).max(0.0)
} else {
0.0
};
let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0 + cave_depth);
let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0);
let min = min - 4.0;
let cliff = BlockGen::get_cliff_height(

View File

@ -214,7 +214,8 @@ impl Civs {
// TODO: Move this
fn generate_cave(&self, ctx: &mut GenCtx<impl Rng>) {
let mut pos = ctx.sim
let mut pos = ctx
.sim
.get_size()
.map(|sz| ctx.rng.gen_range(0, sz as i32) as f32);
let mut vel = pos
@ -225,14 +226,16 @@ impl Civs {
let path = (-100..100)
.filter_map(|i: i32| {
let depth = (i.abs() as f32 / 100.0 * std::f32::consts::PI / 2.0).cos();
vel = (vel + Vec2::new(
ctx.rng.gen_range(-0.25, 0.25),
ctx.rng.gen_range(-0.25, 0.25),
))
.try_normalized()
.unwrap_or_else(Vec2::unit_y);
vel = (vel
+ Vec2::new(
ctx.rng.gen_range(-0.35, 0.35),
ctx.rng.gen_range(-0.35, 0.35),
))
.try_normalized()
.unwrap_or_else(Vec2::unit_y);
let old_pos = pos.map(|e| e as i32);
pos = (pos + vel * 0.5).clamped(Vec2::zero(), ctx.sim.get_size().map(|e| e as f32 - 1.0));
pos = (pos + vel * 0.5)
.clamped(Vec2::zero(), ctx.sim.get_size().map(|e| e as f32 - 1.0));
Some((pos.map(|e| e as i32), depth)).filter(|(pos, _)| *pos != old_pos)
})
.collect::<Vec<_>>();
@ -256,15 +259,12 @@ impl Civs {
ctx.sim.get_mut(locs[2].0).unwrap().cave.0.neighbors |=
1 << ((to_next_idx as u8 + 4) % 8);
let mut chunk = ctx.sim.get_mut(locs[1].0).unwrap();
chunk.cave.0.neighbors |=
(1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8));
let depth = locs[1].1 * 250.0;
chunk.cave.1.alt = chunk.alt - depth + ctx.rng.gen_range(-4.0, 4.0) * (depth > 10.0) as i32 as f32;
chunk.cave.1.width = ctx.rng.gen_range(12.0, 32.0);
chunk.cave.0.offset = Vec2::new(
ctx.rng.gen_range(-16, 17),
ctx.rng.gen_range(-16, 17),
);
chunk.cave.0.neighbors |= (1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8));
let depth = locs[1].1 * 250.0 - 20.0;
chunk.cave.1.alt =
chunk.alt - depth + ctx.rng.gen_range(-4.0, 4.0) * (depth > 10.0) as i32 as f32;
chunk.cave.1.width = ctx.rng.gen_range(6.0, 32.0);
chunk.cave.0.offset = Vec2::new(ctx.rng.gen_range(-16, 17), ctx.rng.gen_range(-16, 17));
}
}
@ -492,10 +492,8 @@ impl Civs {
let mut chunk = ctx.sim.get_mut(locs[1]).unwrap();
chunk.path.0.neighbors |=
(1 << (to_prev_idx as u8)) | (1 << (to_next_idx as u8));
chunk.path.0.offset = Vec2::new(
ctx.rng.gen_range(-16, 17),
ctx.rng.gen_range(-16, 17),
);
chunk.path.0.offset =
Vec2::new(ctx.rng.gen_range(-16, 17), ctx.rng.gen_range(-16, 17));
}
// Take note of the track

View File

@ -2,8 +2,8 @@ use crate::{
all::ForestKind,
block::StructureMeta,
sim::{
local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx,
Path, Cave, RiverKind, SimChunk, WorldSim,
local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, Cave, Path, RiverKind, SimChunk,
WorldSim,
},
util::Sampler,
Index, CONFIG,
@ -1038,34 +1038,6 @@ where
(alt, ground, sub_surface_color)
};
// Caves
let cave_at = |wposf: Vec2<f64>| {
(sim.gen_ctx.cave_0_nz.get(
Vec3::new(wposf.x, wposf.y, alt as f64 * 8.0)
.div(800.0)
.into_array(),
) as f32)
.powf(2.0)
.neg()
.add(1.0)
.mul((1.32 - chaos).min(1.0))
};
let cave_xy = cave_at(wposf);
let cave_alt = alt - 24.0
+ (sim
.gen_ctx
.cave_1_nz
.get(Vec2::new(wposf.x, wposf.y).div(48.0).into_array()) as f32)
* 8.0
+ (sim
.gen_ctx
.cave_1_nz
.get(Vec2::new(wposf.x, wposf.y).div(500.0).into_array()) as f32)
.add(1.0)
.mul(0.5)
.powf(15.0)
.mul(150.0);
let near_ocean = max_river.and_then(|(_, _, river_data, _)| {
if (river_data.is_lake() || river_data.river_kind == Some(RiverKind::Ocean))
&& ((alt <= water_level.max(CONFIG.sea_level + 5.0) && !is_cliffs) || !near_cliffs)
@ -1118,8 +1090,6 @@ where
},
forest_kind: sim_chunk.forest_kind,
close_structures: self.gen_close_structures(wpos),
cave_xy,
cave_alt,
marble,
marble_small,
rock,
@ -1153,8 +1123,6 @@ pub struct ColumnSample<'a> {
pub tree_density: f32,
pub forest_kind: ForestKind,
pub close_structures: [Option<StructureData>; 9],
pub cave_xy: f32,
pub cave_alt: f32,
pub marble: f32,
pub marble_small: f32,
pub rock: f32,

View File

@ -3,6 +3,16 @@ use common::store::{Id, Store};
#[derive(Default)]
pub struct Index {
pub seed: u32,
pub time: f32,
pub sites: Store<Site>,
}
impl Index {
pub fn new(seed: u32) -> Self {
Self {
seed,
..Self::default()
}
}
}

View File

@ -1,10 +1,13 @@
use crate::{
column::ColumnSample,
util::{RandomField, Sampler},
Index,
};
use common::{
terrain::{Block, BlockKind},
vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol},
lottery::Lottery,
assets,
};
use std::f32;
use vek::*;
@ -13,6 +16,7 @@ pub fn apply_paths_to<'a>(
wpos2d: Vec2<i32>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
index: &Index,
) {
for y in 0..vol.size_xy().y as i32 {
for x in 0..vol.size_xy().x as i32 {
@ -100,6 +104,7 @@ pub fn apply_caves_to<'a>(
wpos2d: Vec2<i32>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
index: &Index,
) {
for y in 0..vol.size_xy().y as i32 {
for x in 0..vol.size_xy().x as i32 {
@ -120,14 +125,26 @@ pub fn apply_caves_to<'a>(
.filter(|(dist, _, cave, _)| *dist < cave.width)
{
let cave_x = (cave_dist / cave.width).min(1.0);
let height = (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width;
for z in (cave.alt - height) as i32..(cave.alt + height) as i32 {
let _ = vol.set(
Vec3::new(offs.x, offs.y, z),
Block::empty(),
);
// Relative units
let cave_floor = 0.0 - 0.5 * (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width;
let cave_height = (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width;
// Abs units
let cave_base = (cave.alt + cave_floor) as i32;
let cave_roof = (cave.alt + cave_height) as i32;
for z in cave_base..cave_roof {
let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::empty());
}
// Scatter things in caves
if RandomField::new(index.seed).chance(wpos2d.into(), 0.002) && cave_base < surface_z as i32 - 25 {
let kind = *assets::load_expect::<Lottery<BlockKind>>("common.cave_scatter")
.choose_seeded(RandomField::new(index.seed + 1).get(wpos2d.into()));
let _ = vol.set(Vec3::new(offs.x, offs.y, cave_base), Block::new(kind, Rgb::zero()));
}
}
}
}

View File

@ -48,7 +48,7 @@ pub struct World {
impl World {
pub fn generate(seed: u32, opts: sim::WorldOpts) -> Self {
let mut sim = sim::WorldSim::generate(seed, opts);
let mut index = Index::default();
let mut index = Index::new(seed);
let civs = civ::Civs::generate(seed, &mut sim, &mut index);
sim2::simulate(&mut index, &mut sim);
@ -176,8 +176,8 @@ impl World {
let mut rng = rand::thread_rng();
// Apply layers (paths, caves, etc.)
layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk);
layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk);
layer::apply_paths_to(chunk_wpos2d, sample_get, &mut chunk, &self.index);
layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, &self.index);
// Apply site generation
sim_chunk.sites.iter().for_each(|site| {

View File

@ -2,8 +2,8 @@ mod diffusion;
mod erosion;
mod location;
mod map;
mod way;
mod util;
mod way;
// Reexports
use self::erosion::Compute;
@ -15,12 +15,12 @@ pub use self::{
},
location::Location,
map::{MapConfig, MapDebug},
way::{Way, Path, Cave},
util::{
cdf_irwin_hall, downhill, get_oceans, local_cells, map_edge_factor, neighbors,
uniform_idx_as_vec2, uniform_noise, uphill, vec2_as_uniform_idx, InverseCdf, ScaleBias,
NEIGHBOR_DELTA,
},
way::{Cave, Path, Way},
};
use crate::{
@ -1703,7 +1703,7 @@ impl WorldSim {
/// Return the distance to the nearest way in blocks, along with the
/// closest point on the way, the way metadata, and the tangent vector
/// of that way.
pub fn get_nearest_way<M: Clone + Lerp<Output=M>>(
pub fn get_nearest_way<M: Clone + Lerp<Output = M>>(
&self,
wpos: Vec2<i32>,
get_way: impl Fn(&SimChunk) -> Option<(Way, M)>,
@ -1722,7 +1722,8 @@ impl WorldSim {
.iter()
.filter_map(|ctrl| {
let (way, meta) = get_way(self.get(chunk_pos + *ctrl)?)?;
let ctrl_pos = get_chunk_centre(chunk_pos + *ctrl).map(|e| e as f32) + way.offset.map(|e| e as f32);
let ctrl_pos = get_chunk_centre(chunk_pos + *ctrl).map(|e| e as f32)
+ way.offset.map(|e| e as f32);
let chunk_connections = way.neighbors.count_ones();
if chunk_connections == 0 {

View File

@ -467,7 +467,15 @@ impl Floor {
if room.boss {
let boss_spawn_tile = room.area.center();
// Don't spawn the boss in a pillar
let boss_spawn_tile = boss_spawn_tile + if tile_is_pillar { 1 } else { 0 };
let boss_tile_is_pillar = room
.pillars
.map(|pillar_space| {
boss_spawn_tile
.map(|e| e.rem_euclid(pillar_space) == 0)
.reduce_and()
})
.unwrap_or(false);
let boss_spawn_tile = boss_spawn_tile + if boss_tile_is_pillar { 1 } else { 0 };
if tile_pos == boss_spawn_tile && tile_wcenter.xy() == wpos2d {
let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32))