mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added large caves, cave scatter, removed old caves
This commit is contained in:
parent
10757d0325
commit
41229b4665
10
assets/common/cave_scatter.ron
Normal file
10
assets/common/cave_scatter.ron
Normal file
@ -0,0 +1,10 @@
|
||||
[
|
||||
(25, Velorite),
|
||||
(50, VeloriteFrag),
|
||||
(15, WhiteFlower),
|
||||
(80, ShortGrass),
|
||||
(150, Mushroom),
|
||||
(5, Chest),
|
||||
(2, Crate),
|
||||
(1, Scarecrow),
|
||||
]
|
@ -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))
|
||||
|
@ -39,5 +39,6 @@ pub mod terrain;
|
||||
pub mod util;
|
||||
pub mod vol;
|
||||
pub mod volumes;
|
||||
pub mod lottery;
|
||||
|
||||
pub use loadout_builder::LoadoutBuilder;
|
||||
|
@ -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() }
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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| {
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user