mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Remove spurious uses of Vox.
In the process, also try to address a few edge cases related to block detection, such as adding back previously solid sprites and removing filters that may be vestiges of earlier logic.
This commit is contained in:
parent
98c8240879
commit
938039a56e
@ -4,7 +4,7 @@ use vek::*;
|
||||
use veloren_common::{
|
||||
terrain::{
|
||||
block::{Block, BlockKind},
|
||||
TerrainChunk, TerrainChunkMeta,
|
||||
SpriteKind, TerrainChunk, TerrainChunkMeta,
|
||||
},
|
||||
vol::*,
|
||||
};
|
||||
@ -17,7 +17,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
let mut chunk = TerrainChunk::new(
|
||||
MIN_Z,
|
||||
Block::new(BlockKind::Rock, Rgb::zero()),
|
||||
Block::empty(),
|
||||
Block::air(SpriteKind::Empty),
|
||||
TerrainChunkMeta::void(),
|
||||
);
|
||||
for pos in chunk.pos_iter(
|
||||
|
@ -133,7 +133,7 @@ impl Route {
|
||||
// Only consider the node reached if there's nothing solid between us and it
|
||||
&& (vol
|
||||
.ray(pos + Vec3::unit_z() * 1.5, closest_tgt + Vec3::unit_z() * 1.5)
|
||||
.until(|block| block.is_solid())
|
||||
.until(Block::is_solid)
|
||||
.cast()
|
||||
.0
|
||||
> pos.distance(closest_tgt) * 0.9 || dist_sqrd < 0.5)
|
||||
|
@ -1,11 +1,8 @@
|
||||
use crate::{
|
||||
span,
|
||||
vol::{ReadVol, Vox},
|
||||
};
|
||||
use crate::{span, vol::ReadVol};
|
||||
use vek::*;
|
||||
|
||||
pub trait RayUntil<V: Vox> = FnMut(&V) -> bool;
|
||||
pub trait RayForEach<V: Vox> = FnMut(&V, Vec3<i32>);
|
||||
pub trait RayUntil<V> = FnMut(&V) -> bool;
|
||||
pub trait RayForEach<V> = FnMut(&V, Vec3<i32>);
|
||||
|
||||
pub struct Ray<'a, V: ReadVol, F: RayUntil<V::Vox>, G: RayForEach<V::Vox>> {
|
||||
vol: &'a V,
|
||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||
span,
|
||||
state::{DeltaTime, Time},
|
||||
sync::{Uid, UidAllocator},
|
||||
terrain::TerrainGrid,
|
||||
terrain::{Block, TerrainGrid},
|
||||
util::Dir,
|
||||
vol::ReadVol,
|
||||
};
|
||||
@ -195,7 +195,7 @@ impl<'a> System<'a> for Sys {
|
||||
* 5.0
|
||||
+ Vec3::unit_z(),
|
||||
)
|
||||
.until(|block| block.is_solid())
|
||||
.until(Block::is_solid)
|
||||
.cast()
|
||||
.1
|
||||
.map_or(true, |b| b.is_none())
|
||||
@ -374,7 +374,7 @@ impl<'a> System<'a> for Sys {
|
||||
{
|
||||
let can_see_tgt = terrain
|
||||
.ray(pos.0 + Vec3::unit_z(), tgt_pos.0 + Vec3::unit_z())
|
||||
.until(|block| !block.is_air())
|
||||
.until(Block::is_opaque)
|
||||
.cast()
|
||||
.0
|
||||
.powf(2.0)
|
||||
@ -473,7 +473,7 @@ impl<'a> System<'a> for Sys {
|
||||
// Can we even see them?
|
||||
.filter(|(_, e_pos, _, _)| terrain
|
||||
.ray(pos.0 + Vec3::unit_z(), e_pos.0 + Vec3::unit_z())
|
||||
.until(|block| !block.is_air())
|
||||
.until(Block::is_opaque)
|
||||
.cast()
|
||||
.0 >= e_pos.0.distance(pos.0))
|
||||
.min_by_key(|(_, e_pos, _, _)| (e_pos.0.distance_squared(pos.0) * 100.0) as i32)
|
||||
|
@ -622,7 +622,7 @@ impl<'a> System<'a> for Sys {
|
||||
},
|
||||
Collider::Point => {
|
||||
let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta)
|
||||
.until(|vox| !vox.is_air() && !vox.is_liquid())
|
||||
.until(|block| block.is_filled())
|
||||
.ignore_error().cast();
|
||||
|
||||
pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::SpriteKind;
|
||||
use crate::{make_case_elim, vol::Vox};
|
||||
use crate::make_case_elim;
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use lazy_static::lazy_static;
|
||||
use num_derive::FromPrimitive;
|
||||
@ -101,20 +101,10 @@ impl Deref for Block {
|
||||
fn deref(&self) -> &Self::Target { &self.kind }
|
||||
}
|
||||
|
||||
impl Vox for Block {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
kind: BlockKind::Air,
|
||||
attr: [0; 3],
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool { *self == Block::empty() }
|
||||
}
|
||||
|
||||
impl Block {
|
||||
pub const MAX_HEIGHT: f32 = 3.0;
|
||||
|
||||
#[inline]
|
||||
pub const fn new(kind: BlockKind, color: Rgb<u8>) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
@ -127,6 +117,7 @@ impl Block {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn air(sprite: SpriteKind) -> Self {
|
||||
Self {
|
||||
kind: BlockKind::Air,
|
||||
@ -237,7 +228,9 @@ impl Block {
|
||||
if self.is_fluid() {
|
||||
Block::new(self.kind(), Rgb::zero())
|
||||
} else {
|
||||
Block::empty()
|
||||
// FIXME: Figure out if there's some sensible way to determine what medium to
|
||||
// replace a filled block with if it's removed.
|
||||
Block::air(SpriteKind::Empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
vol::{
|
||||
BaseVol, IntoPosIterator, IntoVolIterator, ReadVol, RectRasterableVol, RectVolSize,
|
||||
VolSize, Vox, WriteVol,
|
||||
VolSize, WriteVol,
|
||||
},
|
||||
volumes::chunk::{Chunk, ChunkError, ChunkPosIter, ChunkVolIter},
|
||||
};
|
||||
@ -34,7 +34,7 @@ impl<ChonkSize: RectVolSize> VolSize for SubChunkSize<ChonkSize> {
|
||||
type SubChunk<V, S, M> = Chunk<V, SubChunkSize<S>, M>;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Chonk<V: Vox, S: RectVolSize, M: Clone> {
|
||||
pub struct Chonk<V, S: RectVolSize, M: Clone> {
|
||||
z_offset: i32,
|
||||
sub_chunks: Vec<SubChunk<V, S, M>>,
|
||||
below: V,
|
||||
@ -43,7 +43,7 @@ pub struct Chonk<V: Vox, S: RectVolSize, M: Clone> {
|
||||
phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<V: Vox, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
impl<V, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
pub fn new(z_offset: i32, below: V, above: V, meta: M) -> Self {
|
||||
Self {
|
||||
z_offset,
|
||||
@ -82,16 +82,16 @@ impl<V: Vox, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
fn sub_chunk_min_z(&self, z: i32) -> i32 { z - self.sub_chunk_z(z) }
|
||||
}
|
||||
|
||||
impl<V: Vox, S: RectVolSize, M: Clone> BaseVol for Chonk<V, S, M> {
|
||||
impl<V, S: RectVolSize, M: Clone> BaseVol for Chonk<V, S, M> {
|
||||
type Error = ChonkError;
|
||||
type Vox = V;
|
||||
}
|
||||
|
||||
impl<V: Vox, S: RectVolSize, M: Clone> RectRasterableVol for Chonk<V, S, M> {
|
||||
impl<V, S: RectVolSize, M: Clone> RectRasterableVol for Chonk<V, S, M> {
|
||||
const RECT_SIZE: Vec2<u32> = S::RECT_SIZE;
|
||||
}
|
||||
|
||||
impl<V: Vox, S: RectVolSize, M: Clone> ReadVol for Chonk<V, S, M> {
|
||||
impl<V, S: RectVolSize, M: Clone> ReadVol for Chonk<V, S, M> {
|
||||
#[inline(always)]
|
||||
fn get(&self, pos: Vec3<i32>) -> Result<&V, Self::Error> {
|
||||
if pos.z < self.get_min_z() {
|
||||
@ -113,7 +113,7 @@ impl<V: Vox, S: RectVolSize, M: Clone> ReadVol for Chonk<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, S: RectVolSize, M: Clone> WriteVol for Chonk<V, S, M> {
|
||||
impl<V: Clone + PartialEq, S: RectVolSize, M: Clone> WriteVol for Chonk<V, S, M> {
|
||||
#[inline(always)]
|
||||
fn set(&mut self, pos: Vec3<i32>, block: Self::Vox) -> Result<(), Self::Error> {
|
||||
let mut sub_chunk_idx = self.sub_chunk_idx(pos.z);
|
||||
@ -140,14 +140,14 @@ impl<V: Vox, S: RectVolSize, M: Clone> WriteVol for Chonk<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
struct ChonkIterHelper<V: Vox, S: RectVolSize, M: Clone> {
|
||||
struct ChonkIterHelper<V, S: RectVolSize, M: Clone> {
|
||||
sub_chunk_min_z: i32,
|
||||
lower_bound: Vec3<i32>,
|
||||
upper_bound: Vec3<i32>,
|
||||
phantom: PhantomData<Chonk<V, S, M>>,
|
||||
}
|
||||
|
||||
impl<V: Vox, S: RectVolSize, M: Clone> Iterator for ChonkIterHelper<V, S, M> {
|
||||
impl<V, S: RectVolSize, M: Clone> Iterator for ChonkIterHelper<V, S, M> {
|
||||
type Item = (i32, Vec3<i32>, Vec3<i32>);
|
||||
|
||||
#[inline(always)]
|
||||
@ -168,12 +168,12 @@ impl<V: Vox, S: RectVolSize, M: Clone> Iterator for ChonkIterHelper<V, S, M> {
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
|
||||
pub struct ChonkPosIter<V: Vox, S: RectVolSize, M: Clone> {
|
||||
pub struct ChonkPosIter<V, S: RectVolSize, M: Clone> {
|
||||
outer: ChonkIterHelper<V, S, M>,
|
||||
opt_inner: Option<(i32, ChunkPosIter<V, SubChunkSize<S>, M>)>,
|
||||
}
|
||||
|
||||
impl<V: Vox, S: RectVolSize, M: Clone> Iterator for ChonkPosIter<V, S, M> {
|
||||
impl<V, S: RectVolSize, M: Clone> Iterator for ChonkPosIter<V, S, M> {
|
||||
type Item = Vec3<i32>;
|
||||
|
||||
#[inline(always)]
|
||||
@ -195,18 +195,18 @@ impl<V: Vox, S: RectVolSize, M: Clone> Iterator for ChonkPosIter<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
enum InnerChonkVolIter<'a, V: Vox, S: RectVolSize, M: Clone> {
|
||||
enum InnerChonkVolIter<'a, V, S: RectVolSize, M: Clone> {
|
||||
Vol(ChunkVolIter<'a, V, SubChunkSize<S>, M>),
|
||||
Pos(ChunkPosIter<V, SubChunkSize<S>, M>),
|
||||
}
|
||||
|
||||
pub struct ChonkVolIter<'a, V: Vox, S: RectVolSize, M: Clone> {
|
||||
pub struct ChonkVolIter<'a, V, S: RectVolSize, M: Clone> {
|
||||
chonk: &'a Chonk<V, S, M>,
|
||||
outer: ChonkIterHelper<V, S, M>,
|
||||
opt_inner: Option<(i32, InnerChonkVolIter<'a, V, S, M>)>,
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, S: RectVolSize, M: Clone> Iterator for ChonkVolIter<'a, V, S, M> {
|
||||
impl<'a, V, S: RectVolSize, M: Clone> Iterator for ChonkVolIter<'a, V, S, M> {
|
||||
type Item = (Vec3<i32>, &'a V);
|
||||
|
||||
#[inline(always)]
|
||||
@ -249,7 +249,7 @@ impl<'a, V: Vox, S: RectVolSize, M: Clone> Iterator for ChonkVolIter<'a, V, S, M
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, S: RectVolSize, M: Clone> IntoPosIterator for &'a Chonk<V, S, M> {
|
||||
impl<'a, V, S: RectVolSize, M: Clone> IntoPosIterator for &'a Chonk<V, S, M> {
|
||||
type IntoIter = ChonkPosIter<V, S, M>;
|
||||
|
||||
fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
|
||||
@ -265,7 +265,7 @@ impl<'a, V: Vox, S: RectVolSize, M: Clone> IntoPosIterator for &'a Chonk<V, S, M
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, S: RectVolSize, M: Clone> IntoVolIterator<'a> for &'a Chonk<V, S, M> {
|
||||
impl<'a, V, S: RectVolSize, M: Clone> IntoVolIterator<'a> for &'a Chonk<V, S, M> {
|
||||
type IntoIter = ChonkVolIter<'a, V, S, M>;
|
||||
|
||||
fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
|
||||
|
@ -139,6 +139,24 @@ impl SpriteKind {
|
||||
SpriteKind::WardrobeSingle => 3.0,
|
||||
SpriteKind::WardrobeDouble => 3.0,
|
||||
SpriteKind::Pot => 0.90,
|
||||
// TODO: Find suitable heights.
|
||||
SpriteKind::BarrelCactus
|
||||
| SpriteKind::RoundCactus
|
||||
| SpriteKind::ShortCactus
|
||||
| SpriteKind::MedFlatCactus
|
||||
| SpriteKind::ShortFlatCactus
|
||||
| SpriteKind::Apple
|
||||
| SpriteKind::Velorite
|
||||
| SpriteKind::VeloriteFrag
|
||||
| SpriteKind::Coconut
|
||||
| SpriteKind::StreetLampTall
|
||||
| SpriteKind::Window1
|
||||
| SpriteKind::Window2
|
||||
| SpriteKind::Window3
|
||||
| SpriteKind::Window4
|
||||
| SpriteKind::DropGate => 1.0,
|
||||
// TODO: Figure out if this should be solid or not.
|
||||
SpriteKind::Shelf => 1.0,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use super::BlockKind;
|
||||
use crate::{
|
||||
assets::{self, Asset, Ron},
|
||||
make_case_elim,
|
||||
vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol},
|
||||
vol::{BaseVol, ReadVol, SizedVol, WriteVol},
|
||||
volumes::dyna::{Dyna, DynaError},
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
@ -34,12 +34,6 @@ make_case_elim!(
|
||||
}
|
||||
);
|
||||
|
||||
impl Vox for StructureBlock {
|
||||
fn empty() -> Self { StructureBlock::None }
|
||||
|
||||
fn is_empty(&self) -> bool { matches!(self, StructureBlock::None) }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StructureError {}
|
||||
|
||||
@ -112,7 +106,7 @@ impl Asset for Structure {
|
||||
|
||||
let mut vol = Dyna::filled(
|
||||
Vec3::new(model.size.x, model.size.y, model.size.z),
|
||||
StructureBlock::empty(),
|
||||
StructureBlock::None,
|
||||
(),
|
||||
);
|
||||
|
||||
@ -147,14 +141,14 @@ impl Asset for Structure {
|
||||
Ok(Structure {
|
||||
center: Vec3::zero(),
|
||||
vol,
|
||||
empty: StructureBlock::empty(),
|
||||
empty: StructureBlock::None,
|
||||
default_kind: BlockKind::Misc,
|
||||
})
|
||||
} else {
|
||||
Ok(Self {
|
||||
center: Vec3::zero(),
|
||||
vol: Dyna::filled(Vec3::zero(), StructureBlock::empty(), ()),
|
||||
empty: StructureBlock::empty(),
|
||||
vol: Dyna::filled(Vec3::zero(), StructureBlock::None, ()),
|
||||
empty: StructureBlock::None,
|
||||
default_kind: BlockKind::Misc,
|
||||
})
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ pub trait Vox: Sized + Clone + PartialEq {
|
||||
|
||||
/// A volume that contains voxel data.
|
||||
pub trait BaseVol {
|
||||
type Vox: Vox;
|
||||
type Vox;
|
||||
type Error: Debug;
|
||||
|
||||
fn scaled_by(self, scale: Vec3<f32>) -> Scaled<Self>
|
||||
@ -98,6 +98,9 @@ pub trait ReadVol: BaseVol {
|
||||
fn get<'a>(&'a self, pos: Vec3<i32>) -> Result<&'a Self::Vox, Self::Error>;
|
||||
|
||||
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
|
||||
/// NOTE: By default, this ray will simply run from `from` to `to` without
|
||||
/// stopping. To make something interesting happen, call `until` or
|
||||
/// `for_each`.
|
||||
fn ray<'a>(
|
||||
&'a self,
|
||||
from: Vec3<f32>,
|
||||
@ -106,7 +109,7 @@ pub trait ReadVol: BaseVol {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ray::new(self, from, to, |vox| !vox.is_empty())
|
||||
Ray::new(self, from, to, |_| true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::vol::{
|
||||
BaseVol, IntoPosIterator, IntoVolIterator, RasterableVol, ReadVol, VolSize, Vox, WriteVol,
|
||||
BaseVol, IntoPosIterator, IntoVolIterator, RasterableVol, ReadVol, VolSize, WriteVol,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{iter::Iterator, marker::PhantomData};
|
||||
@ -46,7 +46,7 @@ pub enum ChunkError {
|
||||
/// index buffer can consist of `u8`s. This keeps the space requirement for the
|
||||
/// index buffer as low as 4 cache lines.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Chunk<V: Vox, S: VolSize, M> {
|
||||
pub struct Chunk<V, S: VolSize, M> {
|
||||
indices: Vec<u8>, /* TODO (haslersn): Box<[u8; S::SIZE.x * S::SIZE.y * S::SIZE.z]>, this is
|
||||
* however not possible in Rust yet */
|
||||
vox: Vec<V>,
|
||||
@ -55,7 +55,7 @@ pub struct Chunk<V: Vox, S: VolSize, M> {
|
||||
phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
impl<V, S: VolSize, M> Chunk<V, S, M> {
|
||||
const GROUP_COUNT: Vec3<u32> = Vec3::new(
|
||||
S::SIZE.x / Self::GROUP_SIZE.x,
|
||||
S::SIZE.y / Self::GROUP_SIZE.y,
|
||||
@ -151,7 +151,10 @@ impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn force_idx_unchecked(&mut self, pos: Vec3<i32>) -> usize {
|
||||
fn force_idx_unchecked(&mut self, pos: Vec3<i32>) -> usize
|
||||
where
|
||||
V: Clone,
|
||||
{
|
||||
let grp_idx = Self::grp_idx(pos);
|
||||
let rel_idx = Self::rel_idx(pos);
|
||||
let base = &mut self.indices[grp_idx as usize];
|
||||
@ -173,7 +176,10 @@ impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_unchecked(&mut self, pos: Vec3<i32>, vox: V) {
|
||||
fn set_unchecked(&mut self, pos: Vec3<i32>, vox: V)
|
||||
where
|
||||
V: Clone + PartialEq,
|
||||
{
|
||||
if vox != self.default {
|
||||
let idx = self.force_idx_unchecked(pos);
|
||||
self.vox[idx] = vox;
|
||||
@ -183,16 +189,16 @@ impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> BaseVol for Chunk<V, S, M> {
|
||||
impl<V, S: VolSize, M> BaseVol for Chunk<V, S, M> {
|
||||
type Error = ChunkError;
|
||||
type Vox = V;
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> RasterableVol for Chunk<V, S, M> {
|
||||
impl<V, S: VolSize, M> RasterableVol for Chunk<V, S, M> {
|
||||
const SIZE: Vec3<u32> = S::SIZE;
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> ReadVol for Chunk<V, S, M> {
|
||||
impl<V, S: VolSize, M> ReadVol for Chunk<V, S, M> {
|
||||
#[inline(always)]
|
||||
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Error> {
|
||||
if !pos
|
||||
@ -206,7 +212,7 @@ impl<V: Vox, S: VolSize, M> ReadVol for Chunk<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> WriteVol for Chunk<V, S, M> {
|
||||
impl<V: Clone + PartialEq, S: VolSize, M> WriteVol for Chunk<V, S, M> {
|
||||
#[inline(always)]
|
||||
#[allow(clippy::unit_arg)] // TODO: Pending review in #587
|
||||
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), Self::Error> {
|
||||
@ -221,7 +227,7 @@ impl<V: Vox, S: VolSize, M> WriteVol for Chunk<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChunkPosIter<V: Vox, S: VolSize, M> {
|
||||
pub struct ChunkPosIter<V, S: VolSize, M> {
|
||||
// Store as `u8`s so as to reduce memory footprint.
|
||||
lb: Vec3<i32>,
|
||||
ub: Vec3<i32>,
|
||||
@ -229,7 +235,7 @@ pub struct ChunkPosIter<V: Vox, S: VolSize, M> {
|
||||
phantom: PhantomData<Chunk<V, S, M>>,
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> ChunkPosIter<V, S, M> {
|
||||
impl<V, S: VolSize, M> ChunkPosIter<V, S, M> {
|
||||
fn new(lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self {
|
||||
// If the range is empty, then we have the special case `ub = lower_bound`.
|
||||
let ub = if lower_bound.map2(upper_bound, |l, u| l < u).reduce_and() {
|
||||
@ -246,7 +252,7 @@ impl<V: Vox, S: VolSize, M> ChunkPosIter<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> Iterator for ChunkPosIter<V, S, M> {
|
||||
impl<V, S: VolSize, M> Iterator for ChunkPosIter<V, S, M> {
|
||||
type Item = Vec3<i32>;
|
||||
|
||||
#[inline(always)]
|
||||
@ -301,12 +307,12 @@ impl<V: Vox, S: VolSize, M> Iterator for ChunkPosIter<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ChunkVolIter<'a, V: Vox, S: VolSize, M> {
|
||||
pub struct ChunkVolIter<'a, V, S: VolSize, M> {
|
||||
chunk: &'a Chunk<V, S, M>,
|
||||
iter_impl: ChunkPosIter<V, S, M>,
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, S: VolSize, M> Iterator for ChunkVolIter<'a, V, S, M> {
|
||||
impl<'a, V, S: VolSize, M> Iterator for ChunkVolIter<'a, V, S, M> {
|
||||
type Item = (Vec3<i32>, &'a V);
|
||||
|
||||
#[inline(always)]
|
||||
@ -317,7 +323,7 @@ impl<'a, V: Vox, S: VolSize, M> Iterator for ChunkVolIter<'a, V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
impl<V, S: VolSize, M> Chunk<V, S, M> {
|
||||
/// It's possible to obtain a positional iterator without having a `Chunk`
|
||||
/// instance.
|
||||
pub fn pos_iter(lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ChunkPosIter<V, S, M> {
|
||||
@ -325,7 +331,7 @@ impl<V: Vox, S: VolSize, M> Chunk<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, S: VolSize, M> IntoPosIterator for &'a Chunk<V, S, M> {
|
||||
impl<'a, V, S: VolSize, M> IntoPosIterator for &'a Chunk<V, S, M> {
|
||||
type IntoIter = ChunkPosIter<V, S, M>;
|
||||
|
||||
fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
|
||||
@ -333,7 +339,7 @@ impl<'a, V: Vox, S: VolSize, M> IntoPosIterator for &'a Chunk<V, S, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, S: VolSize, M> IntoVolIterator<'a> for &'a Chunk<V, S, M> {
|
||||
impl<'a, V, S: VolSize, M> IntoVolIterator<'a> for &'a Chunk<V, S, M> {
|
||||
type IntoIter = ChunkVolIter<'a, V, S, M>;
|
||||
|
||||
fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::vol::{
|
||||
BaseVol, DefaultPosIterator, DefaultVolIterator, IntoPosIterator, IntoVolIterator, ReadVol,
|
||||
SizedVol, Vox, WriteVol,
|
||||
SizedVol, WriteVol,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::*;
|
||||
@ -15,14 +15,14 @@ pub enum DynaError {
|
||||
// S = Size (replace when const generics are a thing)
|
||||
// M = Metadata
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Dyna<V: Vox, M, A: Access = ColumnAccess> {
|
||||
pub struct Dyna<V, M, A: Access = ColumnAccess> {
|
||||
vox: Vec<V>,
|
||||
meta: M,
|
||||
pub sz: Vec3<u32>,
|
||||
_phantom: std::marker::PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<V: Vox, M: Clone, A: Access> Clone for Dyna<V, M, A> {
|
||||
impl<V: Clone, M: Clone, A: Access> Clone for Dyna<V, M, A> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
vox: self.vox.clone(),
|
||||
@ -33,7 +33,7 @@ impl<V: Vox, M: Clone, A: Access> Clone for Dyna<V, M, A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> Dyna<V, M, A> {
|
||||
impl<V, M, A: Access> Dyna<V, M, A> {
|
||||
/// Used to transform a voxel position in the volume into its corresponding
|
||||
/// index in the voxel array.
|
||||
#[inline(always)]
|
||||
@ -46,12 +46,12 @@ impl<V: Vox, M, A: Access> Dyna<V, M, A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> BaseVol for Dyna<V, M, A> {
|
||||
impl<V, M, A: Access> BaseVol for Dyna<V, M, A> {
|
||||
type Error = DynaError;
|
||||
type Vox = V;
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> SizedVol for Dyna<V, M, A> {
|
||||
impl<V, M, A: Access> SizedVol for Dyna<V, M, A> {
|
||||
#[inline(always)]
|
||||
fn lower_bound(&self) -> Vec3<i32> { Vec3::zero() }
|
||||
|
||||
@ -59,7 +59,7 @@ impl<V: Vox, M, A: Access> SizedVol for Dyna<V, M, A> {
|
||||
fn upper_bound(&self) -> Vec3<i32> { self.sz.map(|e| e as i32) }
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, M, A: Access> SizedVol for &'a Dyna<V, M, A> {
|
||||
impl<'a, V, M, A: Access> SizedVol for &'a Dyna<V, M, A> {
|
||||
#[inline(always)]
|
||||
fn lower_bound(&self) -> Vec3<i32> { (*self).lower_bound() }
|
||||
|
||||
@ -67,7 +67,7 @@ impl<'a, V: Vox, M, A: Access> SizedVol for &'a Dyna<V, M, A> {
|
||||
fn upper_bound(&self) -> Vec3<i32> { (*self).upper_bound() }
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> ReadVol for Dyna<V, M, A> {
|
||||
impl<V, M, A: Access> ReadVol for Dyna<V, M, A> {
|
||||
#[inline(always)]
|
||||
fn get(&self, pos: Vec3<i32>) -> Result<&V, DynaError> {
|
||||
Self::idx_for(self.sz, pos)
|
||||
@ -76,7 +76,7 @@ impl<V: Vox, M, A: Access> ReadVol for Dyna<V, M, A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox, M, A: Access> WriteVol for Dyna<V, M, A> {
|
||||
impl<V, M, A: Access> WriteVol for Dyna<V, M, A> {
|
||||
#[inline(always)]
|
||||
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), DynaError> {
|
||||
Self::idx_for(self.sz, pos)
|
||||
@ -86,7 +86,7 @@ impl<V: Vox, M, A: Access> WriteVol for Dyna<V, M, A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, M, A: Access> IntoPosIterator for &'a Dyna<V, M, A> {
|
||||
impl<'a, V, M, A: Access> IntoPosIterator for &'a Dyna<V, M, A> {
|
||||
type IntoIter = DefaultPosIterator;
|
||||
|
||||
fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
|
||||
@ -94,7 +94,7 @@ impl<'a, V: Vox, M, A: Access> IntoPosIterator for &'a Dyna<V, M, A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: Vox, M, A: Access> IntoVolIterator<'a> for &'a Dyna<V, M, A> {
|
||||
impl<'a, V, M, A: Access> IntoVolIterator<'a> for &'a Dyna<V, M, A> {
|
||||
type IntoIter = DefaultVolIterator<'a, Dyna<V, M, A>>;
|
||||
|
||||
fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter {
|
||||
@ -102,7 +102,7 @@ impl<'a, V: Vox, M, A: Access> IntoVolIterator<'a> for &'a Dyna<V, M, A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Vox + Clone, M, A: Access> Dyna<V, M, A> {
|
||||
impl<V: Clone, M, A: Access> Dyna<V, M, A> {
|
||||
/// Create a new `Dyna` with the provided dimensions and all voxels filled
|
||||
/// with duplicates of the provided voxel.
|
||||
pub fn filled(sz: Vec3<u32>, vox: V, meta: M) -> Self {
|
||||
|
@ -11,7 +11,10 @@ impl<V: BaseVol> BaseVol for Scaled<V> {
|
||||
type Vox = V::Vox;
|
||||
}
|
||||
|
||||
impl<V: ReadVol> ReadVol for Scaled<V> {
|
||||
impl<V: ReadVol> ReadVol for Scaled<V>
|
||||
where
|
||||
V::Vox: Vox,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Error> {
|
||||
// let ideal_pos = pos.map2(self.scale, |e, scale| (e as f32 + 0.5) / scale);
|
||||
|
@ -14,7 +14,7 @@ use common::{
|
||||
sync::{Uid, WorldSyncExt},
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||
util::Dir,
|
||||
vol::{RectVolSize, Vox},
|
||||
vol::RectVolSize,
|
||||
LoadoutBuilder,
|
||||
};
|
||||
use rand::Rng;
|
||||
@ -233,7 +233,8 @@ fn handle_make_sprite(
|
||||
let new_block = server
|
||||
.state
|
||||
.get_block(pos)
|
||||
.unwrap_or_else(Block::empty)
|
||||
// TODO: Make more principled.
|
||||
.unwrap_or_else(|| Block::air(SpriteKind::Empty))
|
||||
.with_sprite(sk);
|
||||
server.state.set_block(pos, new_block);
|
||||
},
|
||||
|
@ -534,6 +534,7 @@ pub fn handle_explosion(
|
||||
let _ = ecs
|
||||
.read_resource::<TerrainGrid>()
|
||||
.ray(pos, pos + dir * color_range)
|
||||
// TODO: Faster RNG
|
||||
.until(|_| rand::random::<f32>() < 0.05)
|
||||
.for_each(|_: &Block, pos| touched_blocks.push(pos))
|
||||
.cast();
|
||||
@ -569,6 +570,7 @@ pub fn handle_explosion(
|
||||
let terrain = ecs.read_resource::<TerrainGrid>();
|
||||
let _ = terrain
|
||||
.ray(pos, pos + dir * power)
|
||||
// TODO: Faster RNG
|
||||
.until(|block| block.is_liquid() || rand::random::<f32>() < 0.05)
|
||||
.for_each(|block: &Block, pos| {
|
||||
if block.is_explodable() {
|
||||
|
@ -22,8 +22,8 @@ use common::{
|
||||
span,
|
||||
state::{BlockChange, Time},
|
||||
sync::Uid,
|
||||
terrain::{Block, TerrainChunkSize, TerrainGrid},
|
||||
vol::{RectVolSize, Vox},
|
||||
terrain::{TerrainChunkSize, TerrainGrid},
|
||||
vol::{ReadVol, RectVolSize},
|
||||
};
|
||||
use futures_executor::block_on;
|
||||
use futures_timer::Delay;
|
||||
@ -309,8 +309,8 @@ impl Sys {
|
||||
_ => client.error_state(RequestStateError::Impossible),
|
||||
},
|
||||
ClientMsg::BreakBlock(pos) => {
|
||||
if can_build.get(entity).is_some() {
|
||||
block_changes.set(pos, Block::empty());
|
||||
if let Some(block) = can_build.get(entity).and_then(|_| terrain.get(pos).ok()) {
|
||||
block_changes.set(pos, block.into_vacant());
|
||||
}
|
||||
},
|
||||
ClientMsg::PlaceBlock(pos, block) => {
|
||||
|
@ -1,7 +1,9 @@
|
||||
use common::{
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
terrain::{Block, BlockKind, MapSizeLg, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||
vol::{ReadVol, RectVolSize, Vox, WriteVol},
|
||||
terrain::{
|
||||
Block, BlockKind, MapSizeLg, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize,
|
||||
},
|
||||
vol::{ReadVol, RectVolSize, WriteVol},
|
||||
};
|
||||
use rand::{prelude::*, rngs::SmallRng};
|
||||
use std::time::Duration;
|
||||
@ -60,7 +62,7 @@ impl World {
|
||||
TerrainChunk::new(
|
||||
256 + if rng.gen::<u8>() < 64 { height } else { 0 },
|
||||
Block::new(BlockKind::Grass, Rgb::new(11, 102, 35)),
|
||||
Block::empty(),
|
||||
Block::air(SpriteKind::Empty),
|
||||
TerrainChunkMeta::void(),
|
||||
),
|
||||
supplement,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use common::{
|
||||
terrain::{Block, TerrainGrid},
|
||||
vol::{SampleVol, Vox},
|
||||
terrain::{Block, SpriteKind, TerrainGrid},
|
||||
vol::SampleVol,
|
||||
};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Benchmark, Criterion};
|
||||
use std::sync::Arc;
|
||||
@ -69,7 +69,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
"meshing",
|
||||
Benchmark::new("copying 1,1 into flat array", move |b| {
|
||||
b.iter(|| {
|
||||
let mut flat = vec![Block::empty(); range.size().product() as usize];
|
||||
let mut flat = vec![Block::air(SpriteKind::Empty); range.size().product() as usize];
|
||||
let mut i = 0;
|
||||
let mut volume = volume.cached();
|
||||
for x in 0..range.size().w {
|
||||
|
@ -9,7 +9,7 @@ use common::{
|
||||
span,
|
||||
terrain::Block,
|
||||
util::either_with,
|
||||
vol::{ReadVol, RectRasterableVol, Vox},
|
||||
vol::{ReadVol, RectRasterableVol},
|
||||
volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d},
|
||||
};
|
||||
use std::{collections::VecDeque, fmt::Debug};
|
||||
@ -255,7 +255,12 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
|
||||
let d = d + 2;
|
||||
let flat = {
|
||||
let mut volume = self.cached();
|
||||
let mut flat = vec![Block::empty(); (w * h * d) as usize];
|
||||
|
||||
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
|
||||
|
||||
// TODO: Once we can manage it sensibly, consider using something like
|
||||
// Option<Block> instead of just assuming air.
|
||||
let mut flat = vec![AIR; (w * h * d) as usize];
|
||||
let mut i = 0;
|
||||
for x in 0..range.size().w {
|
||||
for y in 0..range.size().h {
|
||||
@ -263,7 +268,9 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
|
||||
let block = volume
|
||||
.get(range.min + Vec3::new(x, y, z))
|
||||
.map(|b| *b)
|
||||
.unwrap_or(Block::empty());
|
||||
// TODO: Replace with None or some other more reasonable value,
|
||||
// since it's not clear this will work properly with liquid.
|
||||
.unwrap_or(AIR);
|
||||
if block.is_opaque() {
|
||||
opaque_limits = opaque_limits
|
||||
.map(|l| l.including(z))
|
||||
|
@ -1,7 +1,4 @@
|
||||
use common::{
|
||||
span,
|
||||
vol::{ReadVol, Vox},
|
||||
};
|
||||
use common::{span, terrain::TerrainGrid, vol::ReadVol};
|
||||
use std::f32::consts::PI;
|
||||
use treeculler::Frustum;
|
||||
use vek::*;
|
||||
@ -80,7 +77,16 @@ impl Camera {
|
||||
|
||||
/// Compute the transformation matrices (view matrix and projection matrix)
|
||||
/// and position of the camera.
|
||||
pub fn compute_dependents(&mut self, terrain: &impl ReadVol) {
|
||||
pub fn compute_dependents(&mut self, terrain: &TerrainGrid) {
|
||||
self.compute_dependents_full(terrain, |block| !block.is_opaque())
|
||||
}
|
||||
|
||||
/// The is_fluid argument should return true for transparent voxels.
|
||||
pub fn compute_dependents_full<V: ReadVol>(
|
||||
&mut self,
|
||||
terrain: &V,
|
||||
is_transparent: fn(&V::Vox) -> bool,
|
||||
) {
|
||||
span!(_guard, "compute_dependents", "Camera::compute_dependents");
|
||||
let dist = {
|
||||
let (start, end) = (self.focus - self.forward() * self.dist, self.focus);
|
||||
@ -89,7 +95,7 @@ impl Camera {
|
||||
.ray(start, end)
|
||||
.ignore_error()
|
||||
.max_iter(500)
|
||||
.until(|b| b.is_empty())
|
||||
.until(is_transparent)
|
||||
.cast()
|
||||
{
|
||||
(d, Ok(Some(_))) => f32::min(self.dist - d - 0.03, self.dist),
|
||||
|
@ -22,27 +22,18 @@ use common::{
|
||||
comp::{humanoid, item::ItemKind, Loadout},
|
||||
figure::Segment,
|
||||
terrain::BlockKind,
|
||||
vol::{BaseVol, ReadVol, Vox},
|
||||
vol::{BaseVol, ReadVol},
|
||||
};
|
||||
use tracing::error;
|
||||
use vek::*;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
struct VoidVox;
|
||||
impl Vox for VoidVox {
|
||||
fn empty() -> Self { VoidVox }
|
||||
|
||||
fn is_empty(&self) -> bool { true }
|
||||
|
||||
fn or(self, _other: Self) -> Self { VoidVox }
|
||||
}
|
||||
struct VoidVol;
|
||||
impl BaseVol for VoidVol {
|
||||
type Error = ();
|
||||
type Vox = VoidVox;
|
||||
type Vox = ();
|
||||
}
|
||||
impl ReadVol for VoidVol {
|
||||
fn get<'a>(&'a self, _pos: Vec3<i32>) -> Result<&'a Self::Vox, Self::Error> { Ok(&VoidVox) }
|
||||
fn get<'a>(&'a self, _pos: Vec3<i32>) -> Result<&'a Self::Vox, Self::Error> { Ok(&()) }
|
||||
}
|
||||
|
||||
fn generate_mesh<'a>(
|
||||
@ -234,7 +225,7 @@ impl Scene {
|
||||
scene_data.mouse_smoothing,
|
||||
);
|
||||
|
||||
self.camera.compute_dependents(&VoidVol);
|
||||
self.camera.compute_dependents_full(&VoidVol, |_| true);
|
||||
let camera::Dependents {
|
||||
view_mat,
|
||||
proj_mat,
|
||||
|
@ -18,7 +18,7 @@ use common::{
|
||||
span,
|
||||
spiral::Spiral2d,
|
||||
terrain::{sprite, Block, SpriteKind, TerrainChunk},
|
||||
vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, Vox},
|
||||
vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol},
|
||||
volumes::vol_grid_2d::{VolGrid2d, VolGrid2dError},
|
||||
};
|
||||
use core::{f32, fmt::Debug, i32, marker::PhantomData, time::Duration};
|
||||
@ -145,7 +145,11 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug>(
|
||||
let rel_pos = Vec3::new(x, y, z);
|
||||
let wpos = Vec3::from(pos * V::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos;
|
||||
|
||||
let block = volume.get(wpos).ok().copied().unwrap_or(Block::empty());
|
||||
let block = if let Ok(block) = volume.get(wpos) {
|
||||
block
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let sprite = if let Some(sprite) = block.get_sprite() {
|
||||
sprite
|
||||
} else {
|
||||
|
@ -10,7 +10,7 @@ use common::{
|
||||
structure::{self, StructureBlock},
|
||||
Block, BlockKind, SpriteKind, Structure,
|
||||
},
|
||||
vol::{ReadVol, Vox},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use core::ops::{Div, Mul, Range};
|
||||
use serde::Deserialize;
|
||||
@ -164,7 +164,8 @@ impl<'a> BlockGen<'a> {
|
||||
} = self;
|
||||
let world = column_gen.sim;
|
||||
|
||||
let sample = &z_cache?.sample;
|
||||
let z_cache = z_cache?;
|
||||
let sample = &z_cache.sample;
|
||||
let &ColumnSample {
|
||||
alt,
|
||||
basement,
|
||||
@ -188,7 +189,7 @@ impl<'a> BlockGen<'a> {
|
||||
..
|
||||
} = sample;
|
||||
|
||||
let structures = &z_cache?.structures;
|
||||
let structures = &z_cache.structures;
|
||||
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
|
||||
@ -330,7 +331,7 @@ impl<'a> BlockGen<'a> {
|
||||
})
|
||||
.or(block);
|
||||
|
||||
Some(block.unwrap_or_else(Block::empty))
|
||||
block
|
||||
}
|
||||
}
|
||||
|
||||
@ -467,6 +468,8 @@ impl StructureInfo {
|
||||
self.pos.into(),
|
||||
self.seed,
|
||||
sample,
|
||||
// TODO: Take environment into account.
|
||||
Block::air,
|
||||
)
|
||||
})
|
||||
},
|
||||
@ -481,49 +484,55 @@ pub fn block_from_structure(
|
||||
structure_pos: Vec2<i32>,
|
||||
structure_seed: u32,
|
||||
sample: &ColumnSample,
|
||||
mut with_sprite: impl FnMut(SpriteKind) -> Block,
|
||||
) -> Option<Block> {
|
||||
let field = RandomField::new(structure_seed);
|
||||
|
||||
let lerp = ((field.get(Vec3::from(structure_pos)).rem_euclid(256)) as f32 / 255.0) * 0.85
|
||||
+ ((field.get(pos + std::i32::MAX / 2).rem_euclid(256)) as f32 / 255.0) * 0.15;
|
||||
|
||||
const EMPTY_SPRITE: Rgb<u8> = Rgb::new(SpriteKind::Empty as u8, 0, 0);
|
||||
|
||||
match sblock {
|
||||
StructureBlock::None => None,
|
||||
StructureBlock::Hollow => Some(Block::empty()),
|
||||
StructureBlock::Hollow => Some(with_sprite(SpriteKind::Empty)),
|
||||
StructureBlock::Grass => Some(Block::new(
|
||||
BlockKind::Grass,
|
||||
sample.surface_color.map(|e| (e * 255.0) as u8),
|
||||
)),
|
||||
StructureBlock::Normal(color) => {
|
||||
Some(Block::new(BlockKind::Misc, color)).filter(|block| !block.is_empty())
|
||||
},
|
||||
// Water / sludge throw away their color bits currently, so we don't set anyway.
|
||||
StructureBlock::Water => Some(Block::new(BlockKind::Water, Rgb::zero())),
|
||||
StructureBlock::Normal(color) => Some(Block::new(BlockKind::Misc, color)),
|
||||
StructureBlock::Water => Some(Block::new(BlockKind::Water, EMPTY_SPRITE)),
|
||||
StructureBlock::GreenSludge => Some(Block::new(
|
||||
BlockKind::Water,
|
||||
// TODO: If/when liquid supports other colors again, revisit this.
|
||||
Rgb::zero(),
|
||||
BlockKind::Water,
|
||||
EMPTY_SPRITE,
|
||||
)),
|
||||
// None of these BlockKinds has an orientation, so we just use zero for the other color
|
||||
// bits.
|
||||
StructureBlock::Liana => Some(Block::air(SpriteKind::Liana)),
|
||||
StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 24 == 0 {
|
||||
Block::air(SpriteKind::Beehive)
|
||||
} else if field.get(pos + structure_pos + 1) % 3 == 0 {
|
||||
Block::air(SpriteKind::Apple)
|
||||
} else {
|
||||
Block::empty()
|
||||
}),
|
||||
StructureBlock::Coconut => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
||||
Block::empty()
|
||||
} else {
|
||||
Block::air(SpriteKind::Coconut)
|
||||
}),
|
||||
StructureBlock::Chest => Some(if structure_seed % 10 < 7 {
|
||||
Block::empty()
|
||||
} else {
|
||||
Block::air(SpriteKind::Chest)
|
||||
}),
|
||||
StructureBlock::Liana => Some(with_sprite(SpriteKind::Liana)),
|
||||
StructureBlock::Fruit => {
|
||||
if field.get(pos + structure_pos) % 24 == 0 {
|
||||
Some(with_sprite(SpriteKind::Beehive))
|
||||
} else if field.get(pos + structure_pos + 1) % 3 == 0 {
|
||||
Some(with_sprite(SpriteKind::Apple))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
StructureBlock::Coconut => {
|
||||
if field.get(pos + structure_pos) % 3 > 0 {
|
||||
None
|
||||
} else {
|
||||
Some(with_sprite(SpriteKind::Coconut))
|
||||
}
|
||||
},
|
||||
StructureBlock::Chest => {
|
||||
if structure_seed % 10 < 7 {
|
||||
None
|
||||
} else {
|
||||
Some(with_sprite(SpriteKind::Chest))
|
||||
}
|
||||
},
|
||||
// We interpolate all these BlockKinds as needed.
|
||||
StructureBlock::TemperateLeaves
|
||||
| StructureBlock::PineLeaves
|
||||
|
@ -13,7 +13,7 @@ use common::{
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
lottery::Lottery,
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
||||
};
|
||||
use noise::NoiseFn;
|
||||
use rand::prelude::*;
|
||||
@ -30,6 +30,8 @@ pub struct Colors {
|
||||
pub stalagtite: (u8, u8, u8),
|
||||
}
|
||||
|
||||
const EMPTY_AIR: Block = Block::air(SpriteKind::Empty);
|
||||
|
||||
pub fn apply_paths_to<'a>(
|
||||
wpos2d: Vec2<i32>,
|
||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
@ -113,7 +115,7 @@ pub fn apply_paths_to<'a>(
|
||||
for z in inset..inset + head_space {
|
||||
let pos = Vec3::new(offs.x, offs.y, surface_z + z);
|
||||
if vol.get(pos).unwrap().kind() != BlockKind::Water {
|
||||
let _ = vol.set(pos, Block::empty());
|
||||
let _ = vol.set(pos, EMPTY_AIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,7 +165,7 @@ pub fn apply_caves_to<'a>(
|
||||
.into_array(),
|
||||
) < 0.0
|
||||
{
|
||||
let _ = vol.set(Vec3::new(offs.x, offs.y, z), Block::empty());
|
||||
let _ = vol.set(Vec3::new(offs.x, offs.y, z), EMPTY_AIR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,7 +209,8 @@ pub fn apply_caves_to<'a>(
|
||||
}
|
||||
#[allow(clippy::eval_order_dependence)]
|
||||
pub fn apply_caves_supplement<'a>(
|
||||
rng: &mut impl Rng,
|
||||
// NOTE: Used only for dynamic elemens like chests and entities!
|
||||
dynamic_rng: &mut impl Rng,
|
||||
wpos2d: Vec2<i32>,
|
||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
vol: &(impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
||||
@ -253,42 +256,42 @@ pub fn apply_caves_supplement<'a>(
|
||||
wpos2d.y as f32,
|
||||
cave_base as f32,
|
||||
))
|
||||
.with_body(match rng.gen_range(0, 6) {
|
||||
.with_body(match dynamic_rng.gen_range(0, 6) {
|
||||
0 => {
|
||||
is_hostile = false;
|
||||
let species = match rng.gen_range(0, 4) {
|
||||
let species = match dynamic_rng.gen_range(0, 4) {
|
||||
0 => comp::quadruped_small::Species::Truffler,
|
||||
1 => comp::quadruped_small::Species::Dodarock,
|
||||
2 => comp::quadruped_small::Species::Holladon,
|
||||
_ => comp::quadruped_small::Species::Batfox,
|
||||
};
|
||||
comp::quadruped_small::Body::random_with(rng, &species).into()
|
||||
comp::quadruped_small::Body::random_with(dynamic_rng, &species).into()
|
||||
},
|
||||
1 => {
|
||||
is_hostile = true;
|
||||
let species = match rng.gen_range(0, 5) {
|
||||
let species = match dynamic_rng.gen_range(0, 5) {
|
||||
0 => comp::quadruped_medium::Species::Tarasque,
|
||||
_ => comp::quadruped_medium::Species::Bonerattler,
|
||||
};
|
||||
comp::quadruped_medium::Body::random_with(rng, &species).into()
|
||||
comp::quadruped_medium::Body::random_with(dynamic_rng, &species).into()
|
||||
},
|
||||
2 => {
|
||||
is_hostile = true;
|
||||
let species = match rng.gen_range(0, 4) {
|
||||
let species = match dynamic_rng.gen_range(0, 4) {
|
||||
1 => comp::quadruped_low::Species::Rocksnapper,
|
||||
_ => comp::quadruped_low::Species::Salamander,
|
||||
};
|
||||
comp::quadruped_low::Body::random_with(rng, &species).into()
|
||||
comp::quadruped_low::Body::random_with(dynamic_rng, &species).into()
|
||||
},
|
||||
_ => {
|
||||
is_hostile = true;
|
||||
let species = match rng.gen_range(0, 8) {
|
||||
let species = match dynamic_rng.gen_range(0, 8) {
|
||||
0 => comp::biped_large::Species::Ogre,
|
||||
1 => comp::biped_large::Species::Cyclops,
|
||||
2 => comp::biped_large::Species::Wendigo,
|
||||
_ => comp::biped_large::Species::Troll,
|
||||
};
|
||||
comp::biped_large::Body::random_with(rng, &species).into()
|
||||
comp::biped_large::Body::random_with(dynamic_rng, &species).into()
|
||||
},
|
||||
})
|
||||
.with_alignment(if is_hostile {
|
||||
|
@ -38,8 +38,8 @@ use common::{
|
||||
comp::{self, bird_medium, quadruped_low, quadruped_medium, quadruped_small},
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
msg::server::WorldMapMsg,
|
||||
terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||
vol::{ReadVol, RectVolSize, Vox, WriteVol},
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||
vol::{ReadVol, RectVolSize, WriteVol},
|
||||
};
|
||||
use rand::Rng;
|
||||
use serde::Deserialize;
|
||||
@ -114,7 +114,7 @@ impl World {
|
||||
|offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, index),
|
||||
);
|
||||
|
||||
let air = Block::empty();
|
||||
let air = Block::air(SpriteKind::Empty);
|
||||
let stone = Block::new(
|
||||
BlockKind::Rock,
|
||||
zcache_grid
|
||||
@ -195,7 +195,8 @@ impl World {
|
||||
.map(|zc| &zc.sample)
|
||||
};
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
// Only use for rng affecting dynamic elements like chests and entities!
|
||||
let mut dynamic_rng = rand::thread_rng();
|
||||
|
||||
// Apply layers (paths, caves, etc.)
|
||||
layer::apply_caves_to(chunk_wpos2d, sample_get, &mut chunk, index);
|
||||
@ -207,17 +208,17 @@ impl World {
|
||||
index.sites[*site].apply_to(index, chunk_wpos2d, sample_get, &mut chunk)
|
||||
});
|
||||
|
||||
let gen_entity_pos = || {
|
||||
let gen_entity_pos = |dynamic_rng: &mut rand::rngs::ThreadRng| {
|
||||
let lpos2d = TerrainChunkSize::RECT_SIZE
|
||||
.map(|sz| rand::thread_rng().gen::<u32>().rem_euclid(sz) as i32);
|
||||
.map(|sz| dynamic_rng.gen::<u32>().rem_euclid(sz) as i32);
|
||||
let mut lpos = Vec3::new(
|
||||
lpos2d.x,
|
||||
lpos2d.y,
|
||||
sample_get(lpos2d).map(|s| s.alt as i32 - 32).unwrap_or(0),
|
||||
);
|
||||
|
||||
while chunk.get(lpos).map(|vox| !vox.is_empty()).unwrap_or(false) {
|
||||
lpos.z += 1;
|
||||
while let Some(block) = chunk.get(lpos).ok().copied().filter(Block::is_solid) {
|
||||
lpos.z += block.solid_height().ceil() as i32;
|
||||
}
|
||||
|
||||
(Vec3::from(chunk_wpos2d) + lpos).map(|e: i32| e as f32) + 0.5
|
||||
@ -225,18 +226,18 @@ impl World {
|
||||
|
||||
const SPAWN_RATE: f32 = 0.1;
|
||||
let mut supplement = ChunkSupplement {
|
||||
entities: if rng.gen::<f32>() < SPAWN_RATE
|
||||
entities: if dynamic_rng.gen::<f32>() < 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 = rng.gen_range(0, 8) == 0;
|
||||
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())
|
||||
let entity = EntityInfo::at(gen_entity_pos(&mut dynamic_rng))
|
||||
.do_if(is_giant, |e| e.into_giant())
|
||||
.with_body(match rng.gen_range(0, 5) {
|
||||
.with_body(match dynamic_rng.gen_range(0, 5) {
|
||||
0 => {
|
||||
match quadmed {
|
||||
comp::Body::QuadrupedMedium(quadruped_medium) => {
|
||||
@ -292,12 +293,12 @@ impl World {
|
||||
};
|
||||
|
||||
if sim_chunk.contains_waypoint {
|
||||
supplement.add_entity(EntityInfo::at(gen_entity_pos()).into_waypoint());
|
||||
supplement.add_entity(EntityInfo::at(gen_entity_pos(&mut dynamic_rng)).into_waypoint());
|
||||
}
|
||||
|
||||
// Apply layer supplement
|
||||
layer::apply_caves_supplement(
|
||||
&mut rng,
|
||||
&mut dynamic_rng,
|
||||
chunk_wpos2d,
|
||||
sample_get,
|
||||
&chunk,
|
||||
@ -307,7 +308,12 @@ impl World {
|
||||
|
||||
// Apply site supplementary information
|
||||
sim_chunk.sites.iter().for_each(|site| {
|
||||
index.sites[*site].apply_supplement(&mut rng, chunk_wpos2d, sample_get, &mut supplement)
|
||||
index.sites[*site].apply_supplement(
|
||||
&mut dynamic_rng,
|
||||
chunk_wpos2d,
|
||||
sample_get,
|
||||
&mut supplement,
|
||||
)
|
||||
});
|
||||
|
||||
Ok((chunk, supplement))
|
||||
|
@ -1,27 +1,32 @@
|
||||
use common::{terrain::Block, vol::Vox};
|
||||
use common::terrain::Block;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BlockMask {
|
||||
block: Block,
|
||||
block: Option<Block>,
|
||||
priority: i32,
|
||||
}
|
||||
|
||||
impl BlockMask {
|
||||
pub fn new(block: Block, priority: i32) -> Self { Self { block, priority } }
|
||||
|
||||
pub fn nothing() -> Self {
|
||||
pub const fn new(block: Block, priority: i32) -> Self {
|
||||
Self {
|
||||
block: Block::empty(),
|
||||
block: Some(block),
|
||||
priority,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn nothing() -> Self {
|
||||
Self {
|
||||
block: None,
|
||||
priority: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_priority(mut self, priority: i32) -> Self {
|
||||
pub const fn with_priority(mut self, priority: i32) -> Self {
|
||||
self.priority = priority;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn resolve_with(self, other: Self) -> Self {
|
||||
pub const fn resolve_with(self, other: Self) -> Self {
|
||||
if self.priority >= other.priority {
|
||||
self
|
||||
} else {
|
||||
@ -29,11 +34,5 @@ impl BlockMask {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self) -> Option<Block> {
|
||||
if self.priority > 0 {
|
||||
Some(self.block)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub const fn finish(self) -> Option<Block> { self.block }
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
generation::ChunkSupplement,
|
||||
terrain::{Block, BlockKind},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol},
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
||||
};
|
||||
use core::f32;
|
||||
use rand::prelude::*;
|
||||
@ -202,7 +202,8 @@ impl Castle {
|
||||
|
||||
if z > 0 {
|
||||
if vol.get(pos).unwrap().kind() != BlockKind::Water {
|
||||
let _ = vol.set(pos, Block::empty());
|
||||
// TODO: Take environment into account.
|
||||
let _ = vol.set(pos, Block::air(SpriteKind::Empty));
|
||||
}
|
||||
} else {
|
||||
let _ = vol.set(
|
||||
@ -415,7 +416,8 @@ impl Castle {
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
pub fn apply_supplement<'a>(
|
||||
&'a self,
|
||||
_rng: &mut impl Rng,
|
||||
// NOTE: Used only for dynamic elemens like chests and entities!
|
||||
_dynamic_rng: &mut impl Rng,
|
||||
_wpos2d: Vec2<i32>,
|
||||
_get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
_supplement: &mut ChunkSupplement,
|
||||
|
@ -15,7 +15,7 @@ use common::{
|
||||
lottery::Lottery,
|
||||
store::{Id, Store},
|
||||
terrain::{Block, BlockKind, SpriteKind, Structure, TerrainChunkSize},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol},
|
||||
};
|
||||
use core::{f32, hash::BuildHasherDefault};
|
||||
use fxhash::FxHasher64;
|
||||
@ -127,6 +127,8 @@ impl Dungeon {
|
||||
self.origin,
|
||||
self.seed,
|
||||
col_sample,
|
||||
// TODO: Take environment into account.
|
||||
Block::air,
|
||||
)
|
||||
})
|
||||
.unwrap_or(None)
|
||||
@ -140,7 +142,13 @@ impl Dungeon {
|
||||
for floor in &self.floors {
|
||||
z -= floor.total_depth();
|
||||
|
||||
let mut sampler = floor.col_sampler(index, rpos, z);
|
||||
let mut sampler = floor.col_sampler(
|
||||
index,
|
||||
rpos,
|
||||
z,
|
||||
// TODO: Take environment into account.
|
||||
Block::air,
|
||||
);
|
||||
|
||||
for rz in 0..floor.total_depth() {
|
||||
if let Some(block) = sampler(rz).finish() {
|
||||
@ -155,7 +163,8 @@ impl Dungeon {
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
pub fn apply_supplement<'a>(
|
||||
&'a self,
|
||||
rng: &mut impl Rng,
|
||||
// NOTE: Used only for dynamic elemens like chests and entities!
|
||||
dynamic_rng: &mut impl Rng,
|
||||
wpos2d: Vec2<i32>,
|
||||
_get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
supplement: &mut ChunkSupplement,
|
||||
@ -170,7 +179,7 @@ impl Dungeon {
|
||||
for floor in &self.floors {
|
||||
z -= floor.total_depth();
|
||||
let origin = Vec3::new(self.origin.x, self.origin.y, z);
|
||||
floor.apply_supplement(rng, area, origin, supplement);
|
||||
floor.apply_supplement(dynamic_rng, area, origin, supplement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,7 +217,7 @@ pub struct Room {
|
||||
pillars: Option<i32>, // Pillars with the given separation
|
||||
}
|
||||
|
||||
pub struct Floor {
|
||||
struct Floor {
|
||||
tile_offset: Vec2<i32>,
|
||||
tiles: Grid<Tile>,
|
||||
rooms: Store<Room>,
|
||||
@ -222,7 +231,7 @@ pub struct Floor {
|
||||
const FLOOR_SIZE: Vec2<i32> = Vec2::new(18, 18);
|
||||
|
||||
impl Floor {
|
||||
pub fn generate(
|
||||
fn generate(
|
||||
ctx: &mut GenCtx<impl Rng>,
|
||||
stair_tile: Vec2<i32>,
|
||||
level: i32,
|
||||
@ -406,9 +415,10 @@ impl Floor {
|
||||
}
|
||||
|
||||
#[allow(clippy::match_single_binding)] // TODO: Pending review in #587
|
||||
pub fn apply_supplement(
|
||||
fn apply_supplement(
|
||||
&self,
|
||||
rng: &mut impl Rng,
|
||||
// NOTE: Used only for dynamic elemens like chests and entities!
|
||||
dynamic_rng: &mut impl Rng,
|
||||
area: Aabr<i32>,
|
||||
origin: Vec3<i32>,
|
||||
supplement: &mut ChunkSupplement,
|
||||
@ -417,9 +427,12 @@ impl Floor {
|
||||
Vec3::from((self.stair_tile + self.tile_offset).map(|e| e * TILE_SIZE + TILE_SIZE / 2));
|
||||
|
||||
if area.contains_point(stair_rcenter.xy()) {
|
||||
let offs = Vec2::new(rng.gen_range(-1.0, 1.0), rng.gen_range(-1.0, 1.0))
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::unit_y)
|
||||
let offs = Vec2::new(
|
||||
dynamic_rng.gen_range(-1.0, 1.0),
|
||||
dynamic_rng.gen_range(-1.0, 1.0),
|
||||
)
|
||||
.try_normalized()
|
||||
.unwrap_or_else(Vec2::unit_y)
|
||||
* (TILE_SIZE as f32 / 2.0 - 4.0);
|
||||
if !self.final_level {
|
||||
supplement.add_entity(
|
||||
@ -453,16 +466,17 @@ impl Floor {
|
||||
|
||||
if room
|
||||
.enemy_density
|
||||
.map(|density| rng.gen_range(0, density.recip() as usize) == 0)
|
||||
.map(|density| dynamic_rng.gen_range(0, density.recip() as usize) == 0)
|
||||
.unwrap_or(false)
|
||||
&& !tile_is_pillar
|
||||
{
|
||||
// Bad
|
||||
let chosen = Lottery::<String>::load_expect(match rng.gen_range(0, 5) {
|
||||
0 => "common.loot_tables.loot_table_humanoids",
|
||||
1 => "common.loot_tables.loot_table_armor_misc",
|
||||
_ => "common.loot_tables.loot_table_cultists",
|
||||
});
|
||||
let chosen =
|
||||
Lottery::<String>::load_expect(match dynamic_rng.gen_range(0, 5) {
|
||||
0 => "common.loot_tables.loot_table_humanoids",
|
||||
1 => "common.loot_tables.loot_table_armor_misc",
|
||||
_ => "common.loot_tables.loot_table_cultists",
|
||||
});
|
||||
let chosen = chosen.choose();
|
||||
let entity = EntityInfo::at(
|
||||
tile_wcenter.map(|e| e as f32)
|
||||
@ -476,7 +490,7 @@ impl Floor {
|
||||
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
|
||||
.with_automatic_name()
|
||||
.with_loot_drop(comp::Item::new_from_asset_expect(chosen))
|
||||
.with_main_tool(comp::Item::new_from_asset_expect(match rng.gen_range(0, 6) {
|
||||
.with_main_tool(comp::Item::new_from_asset_expect(match dynamic_rng.gen_range(0, 6) {
|
||||
0 => "common.items.npc_weapons.axe.malachite_axe-0",
|
||||
1 => "common.items.npc_weapons.sword.cultist_purp_2h-0",
|
||||
2 => "common.items.npc_weapons.sword.cultist_purp_2h-0",
|
||||
@ -508,10 +522,10 @@ impl Floor {
|
||||
);
|
||||
let chosen = chosen.choose();
|
||||
let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32))
|
||||
.with_level(rng.gen_range(1, 5))
|
||||
.with_level(dynamic_rng.gen_range(1, 5))
|
||||
.with_alignment(comp::Alignment::Enemy)
|
||||
.with_body(comp::Body::Golem(comp::golem::Body::random_with(
|
||||
rng,
|
||||
dynamic_rng,
|
||||
&comp::golem::Species::StoneGolem,
|
||||
)))
|
||||
.with_name("Stonework Defender".to_string())
|
||||
@ -525,9 +539,9 @@ impl Floor {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn total_depth(&self) -> i32 { self.solid_depth + self.hollow_depth }
|
||||
fn total_depth(&self) -> i32 { self.solid_depth + self.hollow_depth }
|
||||
|
||||
pub fn nearest_wall(&self, rpos: Vec2<i32>) -> Option<Vec2<i32>> {
|
||||
fn nearest_wall(&self, rpos: Vec2<i32>) -> Option<Vec2<i32>> {
|
||||
let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE));
|
||||
|
||||
DIRS.iter()
|
||||
@ -548,11 +562,12 @@ impl Floor {
|
||||
}
|
||||
|
||||
#[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587
|
||||
pub fn col_sampler<'a>(
|
||||
fn col_sampler<'a>(
|
||||
&'a self,
|
||||
index: IndexRef<'a>,
|
||||
pos: Vec2<i32>,
|
||||
floor_z: i32,
|
||||
mut with_sprite: impl FnMut(SpriteKind) -> Block,
|
||||
) -> impl FnMut(i32) -> BlockMask + 'a {
|
||||
let rpos = pos - self.tile_offset * TILE_SIZE;
|
||||
let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE));
|
||||
@ -561,7 +576,7 @@ impl Floor {
|
||||
|
||||
let colors = &index.colors.site.dungeon;
|
||||
|
||||
let empty = BlockMask::new(Block::empty(), 1);
|
||||
let vacant = BlockMask::new(with_sprite(SpriteKind::Empty), 1);
|
||||
|
||||
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
||||
let stone = BlockMask::new(Block::new(BlockKind::Rock, colors.stone.into()), 5);
|
||||
@ -576,7 +591,7 @@ impl Floor {
|
||||
{
|
||||
stone
|
||||
} else {
|
||||
empty
|
||||
vacant
|
||||
}
|
||||
} else {
|
||||
BlockMask::nothing()
|
||||
@ -593,7 +608,7 @@ impl Floor {
|
||||
|
||||
let floor_sprite = if RandomField::new(7331).chance(Vec3::from(pos), 0.00005) {
|
||||
BlockMask::new(
|
||||
Block::air(
|
||||
with_sprite(
|
||||
match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 20 {
|
||||
0 => SpriteKind::Apple,
|
||||
1 => SpriteKind::VeloriteFrag,
|
||||
@ -609,12 +624,12 @@ impl Floor {
|
||||
{
|
||||
let room = &self.rooms[*room];
|
||||
if RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density * 0.5) {
|
||||
BlockMask::new(Block::air(SpriteKind::Chest), 1)
|
||||
BlockMask::new(with_sprite(SpriteKind::Chest), 1)
|
||||
} else {
|
||||
empty
|
||||
vacant
|
||||
}
|
||||
} else {
|
||||
empty
|
||||
vacant
|
||||
};
|
||||
|
||||
let tunnel_height = if self.final_level { 16.0 } else { 8.0 };
|
||||
@ -625,7 +640,7 @@ impl Floor {
|
||||
if dist_to_wall >= wall_thickness
|
||||
&& (z as f32) < tunnel_height * (1.0 - tunnel_dist.powf(4.0))
|
||||
{
|
||||
if z == 0 { floor_sprite } else { empty }
|
||||
if z == 0 { floor_sprite } else { vacant }
|
||||
} else {
|
||||
BlockMask::nothing()
|
||||
}
|
||||
@ -651,12 +666,12 @@ impl Floor {
|
||||
if z == 0 {
|
||||
floor_sprite
|
||||
} else {
|
||||
empty
|
||||
vacant
|
||||
}
|
||||
},
|
||||
Some(Tile::DownStair(_)) => {
|
||||
make_staircase(Vec3::new(rtile_pos.x, rtile_pos.y, z), 0.0, 0.5, 9.0)
|
||||
.resolve_with(empty)
|
||||
.resolve_with(vacant)
|
||||
},
|
||||
Some(Tile::UpStair(room)) => {
|
||||
let mut block = make_staircase(
|
||||
@ -666,7 +681,7 @@ impl Floor {
|
||||
9.0,
|
||||
);
|
||||
if z < self.rooms[*room].height {
|
||||
block = block.resolve_with(empty);
|
||||
block = block.resolve_with(vacant);
|
||||
}
|
||||
block
|
||||
},
|
||||
|
@ -108,15 +108,18 @@ impl Site {
|
||||
|
||||
pub fn apply_supplement<'a>(
|
||||
&'a self,
|
||||
rng: &mut impl Rng,
|
||||
// NOTE: Used only for dynamic elemens like chests and entities!
|
||||
dynamic_rng: &mut impl Rng,
|
||||
wpos2d: Vec2<i32>,
|
||||
get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
supplement: &mut ChunkSupplement,
|
||||
) {
|
||||
match &self.kind {
|
||||
SiteKind::Settlement(s) => s.apply_supplement(rng, wpos2d, get_column, supplement),
|
||||
SiteKind::Dungeon(d) => d.apply_supplement(rng, wpos2d, get_column, supplement),
|
||||
SiteKind::Castle(c) => c.apply_supplement(rng, wpos2d, get_column, supplement),
|
||||
SiteKind::Settlement(s) => {
|
||||
s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement)
|
||||
},
|
||||
SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, get_column, supplement),
|
||||
SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ use crate::{
|
||||
use common::{
|
||||
make_case_elim,
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
vol::Vox,
|
||||
};
|
||||
use rand::prelude::*;
|
||||
use serde::Deserialize;
|
||||
@ -293,8 +292,9 @@ impl Archetype for House {
|
||||
let floor = make_block(colors.floor);
|
||||
let wall = make_block(wall_color).with_priority(facade_layer);
|
||||
let roof = make_block(roof_color).with_priority(facade_layer - 1);
|
||||
let empty = BlockMask::nothing();
|
||||
let internal = BlockMask::new(Block::empty(), internal_layer);
|
||||
const EMPTY: BlockMask = BlockMask::nothing();
|
||||
// TODO: Take environment into account.
|
||||
let internal = BlockMask::new(Block::air(SpriteKind::Empty), internal_layer);
|
||||
let end_window = BlockMask::new(
|
||||
Block::air(attr.window)
|
||||
.with_ori(match ori {
|
||||
@ -399,7 +399,7 @@ impl Archetype for House {
|
||||
let roof_level = roof_top - roof_profile.x.max(mansard);
|
||||
|
||||
if profile.y > roof_level {
|
||||
return None;
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
// Roof
|
||||
@ -409,9 +409,9 @@ impl Archetype for House {
|
||||
if (roof_profile.x == 0 && mansard == 0) || roof_dist == width + 2 || is_ribbing
|
||||
{
|
||||
// Eaves
|
||||
return Some(log);
|
||||
return log;
|
||||
} else {
|
||||
return Some(roof);
|
||||
return roof;
|
||||
}
|
||||
}
|
||||
|
||||
@ -427,44 +427,42 @@ impl Archetype for House {
|
||||
&& attr.storey_fill.has_lower()
|
||||
&& storey == 0
|
||||
{
|
||||
return Some(
|
||||
if (bound_offset.x == (width - 1) / 2
|
||||
|| bound_offset.x == (width - 1) / 2 + 1)
|
||||
&& profile.y <= foundation_height + 3
|
||||
{
|
||||
// Doors on first floor only
|
||||
if profile.y == foundation_height + 1 {
|
||||
BlockMask::new(
|
||||
Block::air(SpriteKind::Door)
|
||||
.with_ori(
|
||||
match ori {
|
||||
Ori::East => 2,
|
||||
Ori::North => 0,
|
||||
} + if bound_offset.x == (width - 1) / 2 {
|
||||
0
|
||||
} else {
|
||||
4
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
structural_layer,
|
||||
)
|
||||
} else {
|
||||
empty.with_priority(structural_layer)
|
||||
}
|
||||
return if (bound_offset.x == (width - 1) / 2
|
||||
|| bound_offset.x == (width - 1) / 2 + 1)
|
||||
&& profile.y <= foundation_height + 3
|
||||
{
|
||||
// Doors on first floor only
|
||||
if profile.y == foundation_height + 1 {
|
||||
BlockMask::new(
|
||||
Block::air(SpriteKind::Door)
|
||||
.with_ori(
|
||||
match ori {
|
||||
Ori::East => 2,
|
||||
Ori::North => 0,
|
||||
} + if bound_offset.x == (width - 1) / 2 {
|
||||
0
|
||||
} else {
|
||||
4
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
structural_layer,
|
||||
)
|
||||
} else {
|
||||
wall
|
||||
},
|
||||
);
|
||||
EMPTY.with_priority(structural_layer)
|
||||
}
|
||||
} else {
|
||||
wall
|
||||
};
|
||||
}
|
||||
|
||||
if bound_offset.x == bound_offset.y || profile.y == ceil_height {
|
||||
// Support beams
|
||||
return Some(log);
|
||||
return log;
|
||||
} else if !attr.storey_fill.has_lower() && profile.y < ceil_height {
|
||||
return Some(empty);
|
||||
return EMPTY;
|
||||
} else if !attr.storey_fill.has_upper() {
|
||||
return Some(empty);
|
||||
return EMPTY;
|
||||
} else {
|
||||
let (frame_bounds, frame_borders) = if profile.y >= ceil_height {
|
||||
(
|
||||
@ -495,19 +493,19 @@ impl Archetype for House {
|
||||
// Window frame is large enough for a window
|
||||
let surface_pos = Vec2::new(bound_offset.x, profile.y);
|
||||
if window_bounds.contains_point(surface_pos) {
|
||||
return Some(end_window);
|
||||
return end_window;
|
||||
} else if frame_bounds.contains_point(surface_pos) {
|
||||
return Some(log.with_priority(structural_layer));
|
||||
return log.with_priority(structural_layer);
|
||||
};
|
||||
}
|
||||
|
||||
// Wall
|
||||
return Some(if attr.central_supports && profile.x == 0 {
|
||||
return if attr.central_supports && profile.x == 0 {
|
||||
// Support beams
|
||||
log.with_priority(structural_layer)
|
||||
} else {
|
||||
wall
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -516,15 +514,15 @@ impl Archetype for House {
|
||||
if profile.y == ceil_height {
|
||||
if profile.x == 0 {
|
||||
// Rafters
|
||||
return Some(log);
|
||||
return log;
|
||||
} else if attr.storey_fill.has_upper() {
|
||||
// Ceiling
|
||||
return Some(floor);
|
||||
return floor;
|
||||
}
|
||||
} else if (!attr.storey_fill.has_lower() && profile.y < ceil_height)
|
||||
|| (!attr.storey_fill.has_upper() && profile.y >= ceil_height)
|
||||
{
|
||||
return Some(empty);
|
||||
return EMPTY;
|
||||
// Furniture
|
||||
} else if dist == width - 1
|
||||
&& center_offset.sum() % 2 == 0
|
||||
@ -533,7 +531,8 @@ impl Archetype for House {
|
||||
.noise
|
||||
.chance(Vec3::new(center_offset.x, center_offset.y, z), 0.2)
|
||||
{
|
||||
let mut rng = rand::thread_rng();
|
||||
// NOTE: Used only for dynamic elemens like chests and entities!
|
||||
let mut dynamic_rng = rand::thread_rng();
|
||||
let furniture = match self.noise.get(Vec3::new(
|
||||
center_offset.x,
|
||||
center_offset.y,
|
||||
@ -545,7 +544,7 @@ impl Archetype for House {
|
||||
2 => SpriteKind::ChairDouble,
|
||||
3 => SpriteKind::CoatRack,
|
||||
4 => {
|
||||
if rng.gen_range(0, 8) == 0 {
|
||||
if dynamic_rng.gen_range(0, 8) == 0 {
|
||||
SpriteKind::Chest
|
||||
} else {
|
||||
SpriteKind::Crate
|
||||
@ -558,12 +557,12 @@ impl Archetype for House {
|
||||
_ => SpriteKind::Pot,
|
||||
};
|
||||
|
||||
return Some(BlockMask::new(
|
||||
return BlockMask::new(
|
||||
Block::air(furniture).with_ori(edge_ori).unwrap(),
|
||||
internal_layer,
|
||||
));
|
||||
);
|
||||
} else {
|
||||
return Some(internal);
|
||||
return internal;
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,38 +586,30 @@ impl Archetype for House {
|
||||
_ => SpriteKind::DungeonWallDecor,
|
||||
};
|
||||
|
||||
Some(BlockMask::new(
|
||||
BlockMask::new(
|
||||
Block::air(ornament).with_ori((edge_ori + 4) % 8).unwrap(),
|
||||
internal_layer,
|
||||
))
|
||||
)
|
||||
} else {
|
||||
None
|
||||
EMPTY
|
||||
}
|
||||
};
|
||||
|
||||
let mut cblock = empty;
|
||||
|
||||
if let Some(block) =
|
||||
do_roof_wall(profile, width, dist, bound_offset, roof_top, attr.mansard)
|
||||
{
|
||||
cblock = cblock.resolve_with(block);
|
||||
}
|
||||
let mut cblock = do_roof_wall(profile, width, dist, bound_offset, roof_top, attr.mansard);
|
||||
|
||||
if let Pillar::Tower(tower_height) = attr.pillar {
|
||||
let tower_top = roof_top + tower_height;
|
||||
let profile = Vec2::new(center_offset.x.abs(), profile.y);
|
||||
let dist = center_offset.map(|e| e.abs()).reduce_max();
|
||||
|
||||
if let Some(block) = do_roof_wall(
|
||||
cblock = cblock.resolve_with(do_roof_wall(
|
||||
profile,
|
||||
4,
|
||||
dist,
|
||||
center_offset.map(|e| e.abs()),
|
||||
tower_top,
|
||||
attr.mansard,
|
||||
) {
|
||||
cblock = cblock.resolve_with(block);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
cblock
|
||||
|
@ -7,7 +7,6 @@ use crate::{
|
||||
use common::{
|
||||
make_case_elim,
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
vol::Vox,
|
||||
};
|
||||
use rand::prelude::*;
|
||||
use serde::Deserialize;
|
||||
@ -168,8 +167,9 @@ impl Archetype for Keep {
|
||||
make_block(colors.pole.0, colors.pole.1, colors.pole.2).with_priority(important_layer);
|
||||
let flag =
|
||||
make_block(flag_color.0, flag_color.1, flag_color.2).with_priority(important_layer);
|
||||
let internal = BlockMask::new(Block::empty(), internal_layer);
|
||||
let empty = BlockMask::nothing();
|
||||
const AIR: Block = Block::air(SpriteKind::Empty);
|
||||
const EMPTY: BlockMask = BlockMask::nothing();
|
||||
let internal = BlockMask::new(AIR, internal_layer);
|
||||
|
||||
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
||||
let stone = BlockMask::new(Block::new(BlockKind::Rock, dungeon_stone.into()), 5);
|
||||
@ -187,7 +187,7 @@ impl Archetype for Keep {
|
||||
internal
|
||||
}
|
||||
} else {
|
||||
BlockMask::nothing()
|
||||
EMPTY
|
||||
}
|
||||
};
|
||||
|
||||
@ -255,21 +255,21 @@ impl Archetype for Keep {
|
||||
{
|
||||
flag
|
||||
} else {
|
||||
empty
|
||||
EMPTY
|
||||
}
|
||||
} else if min_dist <= rampart_width {
|
||||
if profile.y < rampart_height {
|
||||
wall
|
||||
} else {
|
||||
empty
|
||||
EMPTY
|
||||
}
|
||||
} else {
|
||||
empty
|
||||
EMPTY
|
||||
}
|
||||
} else if profile.y < roof_height && min_dist < width {
|
||||
internal
|
||||
} else {
|
||||
empty
|
||||
EMPTY
|
||||
}
|
||||
.resolve_with(
|
||||
if attr.is_tower && profile.y > 0 && profile.y <= roof_height {
|
||||
@ -280,7 +280,7 @@ impl Archetype for Keep {
|
||||
9.0,
|
||||
)
|
||||
} else {
|
||||
BlockMask::nothing()
|
||||
EMPTY
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use common::{
|
||||
spiral::Spiral2d,
|
||||
store::{Id, Store},
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol},
|
||||
};
|
||||
use fxhash::FxHasher64;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
@ -726,25 +726,26 @@ impl Settlement {
|
||||
|
||||
for z in -8 - diff..4 + diff {
|
||||
let pos = Vec3::new(offs.x, offs.y, surface_z + z);
|
||||
let block = vol.get(pos).ok().copied().unwrap_or_else(Block::empty);
|
||||
|
||||
if block.is_empty() {
|
||||
let block = if let Ok(&block) = vol.get(pos) {
|
||||
// TODO: Figure out whether extra filters are needed.
|
||||
block
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if let (0, Some(sprite)) = (z, surface_sprite) {
|
||||
let _ = vol.set(
|
||||
pos,
|
||||
// TODO: Make more principled.
|
||||
if block.is_fluid() {
|
||||
block
|
||||
block.with_sprite(sprite)
|
||||
} else {
|
||||
Block::empty()
|
||||
}
|
||||
.with_sprite(sprite),
|
||||
Block::air(sprite)
|
||||
},
|
||||
);
|
||||
} else if z >= 0 {
|
||||
if block.kind() != BlockKind::Water {
|
||||
let _ = vol.set(pos, Block::empty());
|
||||
let _ = vol.set(pos, Block::air(SpriteKind::Empty));
|
||||
}
|
||||
} else {
|
||||
let _ = vol.set(
|
||||
@ -835,7 +836,8 @@ impl Settlement {
|
||||
#[allow(clippy::eval_order_dependence)] // TODO: Pending review in #587
|
||||
pub fn apply_supplement<'a>(
|
||||
&'a self,
|
||||
rng: &mut impl Rng,
|
||||
// NOTE: Used only for dynamic elemens like chests and entities!
|
||||
dynamic_rng: &mut impl Rng,
|
||||
wpos2d: Vec2<i32>,
|
||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
||||
supplement: &mut ChunkSupplement,
|
||||
@ -865,24 +867,25 @@ impl Settlement {
|
||||
let is_dummy =
|
||||
RandomField::new(self.seed + 1).chance(Vec3::from(wpos2d), 1.0 / 15.0);
|
||||
let entity = EntityInfo::at(entity_wpos)
|
||||
.with_body(match rng.gen_range(0, 4) {
|
||||
.with_body(match dynamic_rng.gen_range(0, 4) {
|
||||
_ if is_dummy => {
|
||||
is_human = false;
|
||||
object::Body::TrainingDummy.into()
|
||||
},
|
||||
0 => {
|
||||
let species = match rng.gen_range(0, 3) {
|
||||
let species = match dynamic_rng.gen_range(0, 3) {
|
||||
0 => quadruped_small::Species::Pig,
|
||||
1 => quadruped_small::Species::Sheep,
|
||||
_ => quadruped_small::Species::Cat,
|
||||
};
|
||||
is_human = false;
|
||||
comp::Body::QuadrupedSmall(quadruped_small::Body::random_with(
|
||||
rng, &species,
|
||||
dynamic_rng,
|
||||
&species,
|
||||
))
|
||||
},
|
||||
1 => {
|
||||
let species = match rng.gen_range(0, 4) {
|
||||
let species = match dynamic_rng.gen_range(0, 4) {
|
||||
0 => bird_medium::Species::Duck,
|
||||
1 => bird_medium::Species::Chicken,
|
||||
2 => bird_medium::Species::Goose,
|
||||
@ -890,7 +893,8 @@ impl Settlement {
|
||||
};
|
||||
is_human = false;
|
||||
comp::Body::BirdMedium(bird_medium::Body::random_with(
|
||||
rng, &species,
|
||||
dynamic_rng,
|
||||
&species,
|
||||
))
|
||||
},
|
||||
_ => {
|
||||
@ -906,9 +910,9 @@ impl Settlement {
|
||||
} else {
|
||||
comp::Alignment::Tame
|
||||
})
|
||||
.do_if(is_human && rng.gen(), |entity| {
|
||||
.do_if(is_human && dynamic_rng.gen(), |entity| {
|
||||
entity.with_main_tool(Item::new_from_asset_expect(
|
||||
match rng.gen_range(0, 7) {
|
||||
match dynamic_rng.gen_range(0, 7) {
|
||||
0 => "common.items.npc_weapons.tool.broom",
|
||||
1 => "common.items.npc_weapons.tool.hoe",
|
||||
2 => "common.items.npc_weapons.tool.pickaxe",
|
||||
|
Loading…
Reference in New Issue
Block a user