Merge branch 'zesterer/world-changes' into 'master'

Dungeon entrances

See merge request veloren/veloren!955
This commit is contained in:
Monty Marz 2020-04-29 15:36:57 +00:00
commit 46b8f49231
55 changed files with 163 additions and 74 deletions

View File

@ -64,6 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `/sudo` command
- Added a Level of Detail (LoD) system for terrain sprites and entities
- Added owl, hyena, parrot npcs
- Added dungeon entrances
### Changed

View File

@ -0,0 +1,48 @@
(
[ (
specifier: "world.structure.dungeon.jungle_temple.entrance.1",
center: (50, 40, 10)
),
(
specifier: "world.structure.dungeon.pillar_entrance.round.1",
center: (21, 17, 28)
),
(
specifier: "world.structure.dungeon.pillar_entrance.round.2",
center: (20, 28, 15)
),
(
specifier: "world.structure.dungeon.pillar_entrance.1",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.2",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.3",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.4",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.5",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.pillar_entrance.6",
center: (18, 16, 17)
),
(
specifier: "world.structure.dungeon.temperate_entrance.ruins_4",
center: (13, 11, 14)
),
(
specifier: "world.structure.dungeon.misc_entrance.tower-ruin",
center: (13, 16, 9)
),
]
)

View File

@ -1,9 +1,5 @@
(
[
(
specifier: "world.structure.natural.tower-ruin",
center: (11, 14, 5)
),
[
(
specifier: "world.structure.natural.witch-hut",
center: (10, 13, 9)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -5,12 +5,13 @@ use crate::{
volumes::dyna::{Dyna, DynaError},
};
use dot_vox::DotVoxData;
use std::{fs::File, io::BufReader};
use std::{fs::File, io::BufReader, sync::Arc};
use vek::*;
#[derive(Copy, Clone, PartialEq)]
pub enum StructureBlock {
None,
Grass,
TemperateLeaves,
PineLeaves,
Acacia,
@ -50,6 +51,21 @@ pub struct Structure {
}
impl Structure {
pub fn load_group(specifier: &str) -> Vec<Arc<Structure>> {
let spec = assets::load::<StructuresSpec>(&["world.manifests.", specifier].concat());
return spec
.unwrap()
.0
.iter()
.map(|sp| {
assets::load_map(&sp.specifier[..], |s: Structure| {
s.with_center(Vec3::from(sp.center))
})
.unwrap()
})
.collect();
}
pub fn with_center(mut self, center: Vec3<i32>) -> Self {
self.center = center;
self
@ -113,6 +129,7 @@ impl Asset for Structure {
5 => StructureBlock::Mangrove,
6 => StructureBlock::GreenSludge,
7 => StructureBlock::Fruit,
8 => StructureBlock::Grass,
9 => StructureBlock::Liana,
10 => StructureBlock::Chest,
11 => StructureBlock::Coconut,
@ -150,3 +167,19 @@ impl Asset for Structure {
}
}
}
#[derive(Deserialize)]
struct StructureSpec {
specifier: String,
center: [i32; 3],
}
#[derive(Deserialize)]
struct StructuresSpec(Vec<StructureSpec>);
impl Asset for StructuresSpec {
const ENDINGS: &'static [&'static str] = &["ron"];
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error)
}
}

View File

@ -538,7 +538,6 @@ impl StructureInfo {
.and_then(|b| {
block_from_structure(
*b,
volume.default_kind(),
block_pos,
self.pos.into(),
self.seed,
@ -552,11 +551,10 @@ impl StructureInfo {
pub fn block_from_structure(
sblock: StructureBlock,
default_kind: BlockKind,
pos: Vec3<i32>,
structure_pos: Vec2<i32>,
structure_seed: u32,
_sample: &ColumnSample,
sample: &ColumnSample,
) -> Option<Block> {
let field = RandomField::new(structure_seed + 0);
@ -565,6 +563,10 @@ pub fn block_from_structure(
match sblock {
StructureBlock::None => None,
StructureBlock::Grass => Some(Block::new(
BlockKind::Normal,
sample.surface_color.map(|e| (e * 255.0) as u8),
)),
StructureBlock::TemperateLeaves => Some(Block::new(
BlockKind::Leaves,
Lerp::lerp(
@ -639,7 +641,7 @@ pub fn block_from_structure(
)),
StructureBlock::Hollow => Some(Block::empty()),
StructureBlock::Normal(color) => {
Some(Block::new(default_kind, color)).filter(|block| !block.is_empty())
Some(Block::new(BlockKind::Normal, color)).filter(|block| !block.is_empty())
},
}
}

View File

@ -5,11 +5,9 @@ use crate::{
util::{RandomPerm, Sampler, SmallCache, UnitChooser},
CONFIG,
};
use common::{assets, assets::Asset, terrain::Structure};
use common::terrain::Structure;
use lazy_static::lazy_static;
use ron;
use serde::Deserialize;
use std::{fs::File, io::BufReader, sync::Arc, u32};
use std::{sync::Arc, u32};
use vek::*;
static VOLUME_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
@ -76,47 +74,16 @@ pub fn structure_gen<'a>(
})
}
#[derive(Deserialize)]
struct StructureSpec {
specifier: String,
center: [i32; 3],
}
#[derive(Deserialize)]
struct StructuresSpec(Vec<StructureSpec>);
impl Asset for StructuresSpec {
const ENDINGS: &'static [&'static str] = &["ron"];
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error)
}
}
fn load_structures(specifier: &str) -> Vec<Arc<Structure>> {
let spec = assets::load::<StructuresSpec>(&["world.manifests.", specifier].concat());
return spec
.unwrap()
.0
.iter()
.map(|sp| {
assets::load_map(&sp.specifier[..], |s: Structure| {
s.with_center(Vec3::from(sp.center))
})
.unwrap()
})
.collect();
}
lazy_static! {
pub static ref OAKS: Vec<Arc<Structure>> = load_structures("oaks");
pub static ref OAK_STUMPS: Vec<Arc<Structure>> = load_structures("oak_stumps");
pub static ref PINES: Vec<Arc<Structure>> = load_structures("pines");
pub static ref PALMS: Vec<Arc<Structure>> = load_structures("palms");
pub static ref SNOW_PINES: Vec<Arc<Structure>> = load_structures("snow_pines");
pub static ref ACACIAS: Vec<Arc<Structure>> = load_structures("acacias");
pub static ref FRUIT_TREES: Vec<Arc<Structure>> = load_structures("fruit_trees");
pub static ref BIRCHES: Vec<Arc<Structure>> = load_structures("birch");
pub static ref MANGROVE_TREES: Vec<Arc<Structure>> = load_structures("mangrove_trees");
pub static ref QUIRKY: Vec<Arc<Structure>> = load_structures("quirky");
pub static ref QUIRKY_DRY: Vec<Arc<Structure>> = load_structures("quirky_dry");
pub static ref OAKS: Vec<Arc<Structure>> = Structure::load_group("oaks");
pub static ref OAK_STUMPS: Vec<Arc<Structure>> = Structure::load_group("oak_stumps");
pub static ref PINES: Vec<Arc<Structure>> = Structure::load_group("pines");
pub static ref PALMS: Vec<Arc<Structure>> = Structure::load_group("palms");
pub static ref SNOW_PINES: Vec<Arc<Structure>> = Structure::load_group("snow_pines");
pub static ref ACACIAS: Vec<Arc<Structure>> = Structure::load_group("acacias");
pub static ref FRUIT_TREES: Vec<Arc<Structure>> = Structure::load_group("fruit_trees");
pub static ref BIRCHES: Vec<Arc<Structure>> = Structure::load_group("birch");
pub static ref MANGROVE_TREES: Vec<Arc<Structure>> = Structure::load_group("mangrove_trees");
pub static ref QUIRKY: Vec<Arc<Structure>> = Structure::load_group("quirky");
pub static ref QUIRKY_DRY: Vec<Arc<Structure>> = Structure::load_group("quirky_dry");
}

View File

@ -95,21 +95,26 @@ impl Civs {
// Flatten ground around sites
for site in this.sites.iter() {
if let SiteKind::Settlement = &site.kind {
} else {
continue;
}
let radius = 48i32;
let wpos = site.center * Vec2::from(TerrainChunkSize::RECT_SIZE).map(|e: u32| e as i32);
let flatten_radius = match &site.kind {
SiteKind::Settlement => 10.0,
SiteKind::Dungeon => 2.0,
};
let (raise, raise_dist): (f32, i32) = match &site.kind {
SiteKind::Settlement => (10.0, 6),
_ => (0.0, 0),
};
// Flatten ground
let flatten_radius = 10.0;
if let Some(center_alt) = ctx.sim.get_alt_approx(wpos) {
for offs in Spiral2d::new().take(radius.pow(2) as usize) {
let center_alt = center_alt
+ if offs.magnitude_squared() <= 6i32.pow(2) {
16.0
+ if offs.magnitude_squared() <= raise_dist.pow(2) {
raise
} else {
0.0
}; // Raise the town centre up a little
@ -335,7 +340,7 @@ impl Civs {
let site = self.sites.insert(site_fn(place));
// Find neighbors
const MAX_NEIGHBOR_DISTANCE: f32 = 250.0;
const MAX_NEIGHBOR_DISTANCE: f32 = 500.0;
let mut nearby = self
.sites
.iter_ids()

View File

@ -1,5 +1,6 @@
use super::SpawnRules;
use crate::{
block::block_from_structure,
column::ColumnSample,
sim::WorldSim,
site::BlockMask,
@ -11,11 +12,12 @@ use common::{
comp,
generation::{ChunkSupplement, EntityInfo},
store::{Id, Store},
terrain::{Block, BlockKind, TerrainChunkSize},
terrain::{Block, BlockKind, Structure, TerrainChunkSize},
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
};
use lazy_static::lazy_static;
use rand::prelude::*;
use std::f32;
use std::{f32, sync::Arc};
use vek::*;
impl WorldSim {
@ -34,6 +36,7 @@ impl WorldSim {
pub struct Dungeon {
origin: Vec2<i32>,
alt: i32,
seed: u32,
#[allow(dead_code)]
noise: RandomField,
floors: Vec<Floor>,
@ -44,16 +47,19 @@ pub struct GenCtx<'a, R: Rng> {
rng: &'a mut R,
}
const ALT_OFFSET: i32 = -2;
impl Dungeon {
pub fn generate(wpos: Vec2<i32>, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self {
let mut ctx = GenCtx { sim, rng };
let this = Self {
origin: wpos,
origin: wpos - TILE_SIZE / 2,
alt: ctx
.sim
.and_then(|sim| sim.get_alt_approx(wpos))
.unwrap_or(0.0) as i32
+ 6,
seed: ctx.rng.gen(),
noise: RandomField::new(ctx.rng.gen()),
floors: (0..6)
.scan(Vec2::zero(), |stair_tile, level| {
@ -71,8 +77,9 @@ impl Dungeon {
pub fn radius(&self) -> f32 { 1200.0 }
pub fn spawn_rules(&self, _wpos: Vec2<i32>) -> SpawnRules {
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
SpawnRules {
trees: wpos.distance_squared(self.origin) > 64i32.pow(2),
..SpawnRules::default()
}
}
@ -80,9 +87,16 @@ impl Dungeon {
pub fn apply_to<'a>(
&'a self,
wpos2d: Vec2<i32>,
_get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
) {
lazy_static! {
pub static ref ENTRANCES: Vec<Arc<Structure>> =
Structure::load_group("dungeon_entrances");
}
let entrance = &ENTRANCES[self.seed as usize % ENTRANCES.len()];
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);
@ -90,7 +104,30 @@ impl Dungeon {
let wpos2d = wpos2d + offs;
let rpos = wpos2d - self.origin;
let mut z = self.alt;
// Apply the dungeon entrance
let col_sample = if let Some(col) = get_column(offs) {
col
} else {
continue;
};
for z in entrance.get_bounds().min.z..entrance.get_bounds().max.z {
let wpos = Vec3::new(offs.x, offs.y, self.alt + z + ALT_OFFSET);
let spos = Vec3::new(rpos.x - TILE_SIZE / 2, rpos.y - TILE_SIZE / 2, z);
if let Some(block) = entrance
.get(spos)
.ok()
.copied()
.map(|sb| {
block_from_structure(sb, spos, self.origin, self.seed, col_sample)
})
.unwrap_or(None)
{
let _ = vol.set(wpos, block);
}
}
// Apply the dungeon internals
let mut z = self.alt + ALT_OFFSET;
for floor in &self.floors {
z -= floor.total_depth();
@ -123,17 +160,17 @@ impl Dungeon {
let offs = Vec2::new(rng.gen_range(-1.0, 1.0), rng.gen_range(-1.0, 1.0))
.try_normalized()
.unwrap_or(Vec2::unit_y())
* 16.0;
* 12.0;
supplement.add_entity(
EntityInfo::at(
Vec3::new(self.origin.x, self.origin.y, self.alt + 4).map(|e| e as f32)
Vec3::new(self.origin.x, self.origin.y, self.alt + 16).map(|e| e as f32)
+ Vec3::from(offs),
)
.into_waypoint(),
);
}
let mut z = self.alt;
let mut z = self.alt + ALT_OFFSET;
for floor in &self.floors {
z -= floor.total_depth();
let origin = Vec3::new(self.origin.x, self.origin.y, z);