diff --git a/assets/world/manifests/dungeon_entrances.ron b/assets/world/manifests/dungeon_entrances.ron new file mode 100644 index 0000000000..166a4a2367 --- /dev/null +++ b/assets/world/manifests/dungeon_entrances.ron @@ -0,0 +1,45 @@ +( + [ + ( + 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) + ), + // Pfau's dungeon entrance + //( + // specifier: "world.structure.dungeon.entrance.1", + // center: (13, 11, 12) + //), + ] +) diff --git a/assets/world/structure/dungeon/jungle_temple/entrance/1.vox b/assets/world/structure/dungeon/jungle_temple/entrance/1.vox new file mode 100644 index 0000000000..cdef59e45a --- /dev/null +++ b/assets/world/structure/dungeon/jungle_temple/entrance/1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4776ba6109db31fce86354e8599e60b9a6e5ba4bd46b3f27ad9fafc36adde02 +size 491808 diff --git a/assets/world/structure/dungeon/pillar_entrance/1.vox b/assets/world/structure/dungeon/pillar_entrance/1.vox new file mode 100644 index 0000000000..72446dca7e --- /dev/null +++ b/assets/world/structure/dungeon/pillar_entrance/1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6171ea50e20bcf61b293aeb6b065379c720c86212a442037e734eee8f15a1d4c +size 78056 diff --git a/assets/world/structure/dungeon/pillar_entrance/2.vox b/assets/world/structure/dungeon/pillar_entrance/2.vox new file mode 100644 index 0000000000..5587d162c6 --- /dev/null +++ b/assets/world/structure/dungeon/pillar_entrance/2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:063773bdd9f2a949ede616eeb4bd909d1ba8d8588b223344870c929ed0e54b3f +size 80208 diff --git a/assets/world/structure/dungeon/pillar_entrance/3.vox b/assets/world/structure/dungeon/pillar_entrance/3.vox new file mode 100644 index 0000000000..287d00532d --- /dev/null +++ b/assets/world/structure/dungeon/pillar_entrance/3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:912a62e77ab47135c7aec62d5d9c4d8ab6681129d02b99ca0da05c2c533fa54f +size 80680 diff --git a/assets/world/structure/dungeon/pillar_entrance/4.vox b/assets/world/structure/dungeon/pillar_entrance/4.vox new file mode 100644 index 0000000000..f9e8e1030d --- /dev/null +++ b/assets/world/structure/dungeon/pillar_entrance/4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81b784fb9eaac194420bd392a0f654a3426a9569b72f4e245054a40a63eae0e9 +size 92924 diff --git a/assets/world/structure/dungeon/pillar_entrance/5.vox b/assets/world/structure/dungeon/pillar_entrance/5.vox new file mode 100644 index 0000000000..40ae42a88b --- /dev/null +++ b/assets/world/structure/dungeon/pillar_entrance/5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c77203935805917bcf58c43b1824e9332e9e46104317b17292c28527c786795 +size 82340 diff --git a/assets/world/structure/dungeon/pillar_entrance/6.vox b/assets/world/structure/dungeon/pillar_entrance/6.vox new file mode 100644 index 0000000000..7a75508f5a --- /dev/null +++ b/assets/world/structure/dungeon/pillar_entrance/6.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:228025703e2d75982f4357a4e821fa0277c937d5f8bdc0a3d104d00c6f4aa30d +size 74852 diff --git a/assets/world/structure/dungeon/pillar_entrance/round/1.vox b/assets/world/structure/dungeon/pillar_entrance/round/1.vox new file mode 100644 index 0000000000..8d8ab442e0 --- /dev/null +++ b/assets/world/structure/dungeon/pillar_entrance/round/1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a80872a97434486f088ea47ca97ec94736855674da065e80f03dbf72ada532c +size 141607 diff --git a/assets/world/structure/dungeon/pillar_entrance/round/2.vox b/assets/world/structure/dungeon/pillar_entrance/round/2.vox new file mode 100644 index 0000000000..dbe746c517 --- /dev/null +++ b/assets/world/structure/dungeon/pillar_entrance/round/2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:959adda39a28692133217afec98ba0ac6f8e009c968e0b3b1fbcd30a464673c6 +size 76000 diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index 8bdf71586e..14ea9806be 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -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> { + let spec = assets::load::(&["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) -> 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); + +impl Asset for StructuresSpec { + const ENDINGS: &'static [&'static str] = &["ron"]; + + fn parse(buf_reader: BufReader) -> Result { + ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error) + } +} diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 8aa8020af6..eb803f5d52 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -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, structure_pos: Vec2, structure_seed: u32, - _sample: &ColumnSample, + sample: &ColumnSample, ) -> Option { let field = RandomField::new(structure_seed + 0); @@ -565,6 +563,7 @@ 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 +638,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()) }, } } diff --git a/world/src/block/natural.rs b/world/src/block/natural.rs index 232708dad6..1ea1103ae9 100644 --- a/world/src/block/natural.rs +++ b/world/src/block/natural.rs @@ -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); - -impl Asset for StructuresSpec { - const ENDINGS: &'static [&'static str] = &["ron"]; - - fn parse(buf_reader: BufReader) -> Result { - ron::de::from_reader(buf_reader).map_err(assets::Error::parse_error) - } -} - -fn load_structures(specifier: &str) -> Vec> { - let spec = assets::load::(&["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> = load_structures("oaks"); - pub static ref OAK_STUMPS: Vec> = load_structures("oak_stumps"); - pub static ref PINES: Vec> = load_structures("pines"); - pub static ref PALMS: Vec> = load_structures("palms"); - pub static ref SNOW_PINES: Vec> = load_structures("snow_pines"); - pub static ref ACACIAS: Vec> = load_structures("acacias"); - pub static ref FRUIT_TREES: Vec> = load_structures("fruit_trees"); - pub static ref BIRCHES: Vec> = load_structures("birch"); - pub static ref MANGROVE_TREES: Vec> = load_structures("mangrove_trees"); - pub static ref QUIRKY: Vec> = load_structures("quirky"); - pub static ref QUIRKY_DRY: Vec> = load_structures("quirky_dry"); + pub static ref OAKS: Vec> = Structure::load_group("oaks"); + pub static ref OAK_STUMPS: Vec> = Structure::load_group("oak_stumps"); + pub static ref PINES: Vec> = Structure::load_group("pines"); + pub static ref PALMS: Vec> = Structure::load_group("palms"); + pub static ref SNOW_PINES: Vec> = Structure::load_group("snow_pines"); + pub static ref ACACIAS: Vec> = Structure::load_group("acacias"); + pub static ref FRUIT_TREES: Vec> = Structure::load_group("fruit_trees"); + pub static ref BIRCHES: Vec> = Structure::load_group("birch"); + pub static ref MANGROVE_TREES: Vec> = Structure::load_group("mangrove_trees"); + pub static ref QUIRKY: Vec> = Structure::load_group("quirky"); + pub static ref QUIRKY_DRY: Vec> = Structure::load_group("quirky_dry"); } diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 1d1515159a..b1a1911ade 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -95,16 +95,16 @@ 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); // Flatten ground - let flatten_radius = 10.0; + let flatten_radius = match &site.kind { + SiteKind::Settlement => 10.0, + SiteKind::Dungeon => 2.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 @@ -335,7 +335,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() diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index a696907e7a..42af6548ed 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -4,6 +4,7 @@ use crate::{ sim::WorldSim, site::BlockMask, util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS}, + block::block_from_structure, }; use common::{ assets, @@ -11,11 +12,12 @@ use common::{ comp, generation::{ChunkSupplement, EntityInfo}, store::{Id, Store}, - terrain::{Block, BlockKind, TerrainChunkSize}, + terrain::{Block, BlockKind, TerrainChunkSize, Structure}, vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol}, }; +use lazy_static::lazy_static; use rand::prelude::*; -use std::f32; +use std::{sync::Arc, f32}; use vek::*; impl WorldSim { @@ -34,6 +36,7 @@ impl WorldSim { pub struct Dungeon { origin: Vec2, alt: i32, + seed: u32, #[allow(dead_code)] noise: RandomField, floors: Vec, @@ -44,6 +47,8 @@ pub struct GenCtx<'a, R: Rng> { rng: &'a mut R, } +const ALT_OFFSET: i32 = 2; + impl Dungeon { pub fn generate(wpos: Vec2, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self { let mut ctx = GenCtx { sim, rng }; @@ -54,6 +59,7 @@ impl Dungeon { .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) -> SpawnRules { + pub fn spawn_rules(&self, wpos: Vec2) -> SpawnRules { SpawnRules { + trees: wpos.distance_squared(self.origin) > 64i32.pow(2), ..SpawnRules::default() } } @@ -80,9 +87,15 @@ impl Dungeon { pub fn apply_to<'a>( &'a self, wpos2d: Vec2, - _get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, + mut get_column: impl FnMut(Vec2) -> Option<&'a ColumnSample<'a>>, vol: &mut (impl BaseVol + RectSizedVol + ReadVol + WriteVol), ) { + lazy_static! { + pub static ref ENTRANCES: Vec> = 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 +103,28 @@ 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(); @@ -133,7 +167,7 @@ impl Dungeon { ); } - 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);