mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Improved representation of Block for better performance, more features, and better backwards-compatibility
This commit is contained in:
parent
b2f42a117c
commit
ece4a01867
@ -34,6 +34,8 @@ sum_type = "0.2.0"
|
||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60cd18c34c73097640162bfe" }
|
||||
slab = "0.4.2"
|
||||
enum-iterator = "0.6"
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.3"
|
||||
|
||||
# Tracy
|
||||
tracy-client = { version = "0.8.0", optional = true }
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
assets::{self, Asset, Error},
|
||||
effect::Effect,
|
||||
lottery::Lottery,
|
||||
terrain::{Block, BlockKind},
|
||||
terrain::{Block, SpriteKind},
|
||||
};
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use rand::prelude::*;
|
||||
@ -277,23 +277,23 @@ impl Item {
|
||||
pub fn try_reclaim_from_block(block: Block) -> Option<Self> {
|
||||
let chosen;
|
||||
let mut rng = rand::thread_rng();
|
||||
Some(Item::new_from_asset_expect(match block.kind() {
|
||||
BlockKind::Apple => "common.items.food.apple",
|
||||
BlockKind::Mushroom => "common.items.food.mushroom",
|
||||
BlockKind::Velorite => "common.items.ore.velorite",
|
||||
BlockKind::VeloriteFrag => "common.items.ore.veloritefrag",
|
||||
BlockKind::BlueFlower => "common.items.flowers.blue",
|
||||
BlockKind::PinkFlower => "common.items.flowers.pink",
|
||||
BlockKind::PurpleFlower => "common.items.flowers.purple",
|
||||
BlockKind::RedFlower => "common.items.flowers.red",
|
||||
BlockKind::WhiteFlower => "common.items.flowers.white",
|
||||
BlockKind::YellowFlower => "common.items.flowers.yellow",
|
||||
BlockKind::Sunflower => "common.items.flowers.sun",
|
||||
BlockKind::LongGrass => "common.items.grasses.long",
|
||||
BlockKind::MediumGrass => "common.items.grasses.medium",
|
||||
BlockKind::ShortGrass => "common.items.grasses.short",
|
||||
BlockKind::Coconut => "common.items.food.coconut",
|
||||
BlockKind::Chest => {
|
||||
Some(Item::new_from_asset_expect(match block.get_sprite()? {
|
||||
SpriteKind::Apple => "common.items.food.apple",
|
||||
SpriteKind::Mushroom => "common.items.food.mushroom",
|
||||
SpriteKind::Velorite => "common.items.ore.velorite",
|
||||
SpriteKind::VeloriteFrag => "common.items.ore.veloritefrag",
|
||||
SpriteKind::BlueFlower => "common.items.flowers.blue",
|
||||
SpriteKind::PinkFlower => "common.items.flowers.pink",
|
||||
SpriteKind::PurpleFlower => "common.items.flowers.purple",
|
||||
SpriteKind::RedFlower => "common.items.flowers.red",
|
||||
SpriteKind::WhiteFlower => "common.items.flowers.white",
|
||||
SpriteKind::YellowFlower => "common.items.flowers.yellow",
|
||||
SpriteKind::Sunflower => "common.items.flowers.sun",
|
||||
SpriteKind::LongGrass => "common.items.grasses.long",
|
||||
SpriteKind::MediumGrass => "common.items.grasses.medium",
|
||||
SpriteKind::ShortGrass => "common.items.grasses.short",
|
||||
SpriteKind::Coconut => "common.items.food.coconut",
|
||||
SpriteKind::Chest => {
|
||||
chosen = Lottery::<String>::load_expect(match rng.gen_range(0, 7) {
|
||||
0 => "common.loot_tables.loot_table_weapon_uncommon",
|
||||
1 => "common.loot_tables.loot_table_weapon_common",
|
||||
@ -304,13 +304,13 @@ impl Item {
|
||||
});
|
||||
chosen.choose()
|
||||
},
|
||||
BlockKind::Crate => {
|
||||
SpriteKind::Crate => {
|
||||
chosen = Lottery::<String>::load_expect("common.loot_tables.loot_table_food");
|
||||
chosen.choose()
|
||||
},
|
||||
BlockKind::Stones => "common.items.crafting_ing.stones",
|
||||
BlockKind::Twigs => "common.items.crafting_ing.twigs",
|
||||
BlockKind::ShinyGem => "common.items.crafting_ing.shiny_gem",
|
||||
SpriteKind::Stones => "common.items.crafting_ing.stones",
|
||||
SpriteKind::Twigs => "common.items.crafting_ing.twigs",
|
||||
SpriteKind::ShinyGem => "common.items.crafting_ing.shiny_gem",
|
||||
_ => return None,
|
||||
}))
|
||||
}
|
||||
|
@ -434,7 +434,7 @@ where
|
||||
V: BaseVol<Vox = Block> + ReadVol,
|
||||
{
|
||||
vol.get(pos - Vec3::new(0, 0, 1))
|
||||
.map(|b| b.is_solid() && b.get_height() == 1.0)
|
||||
.map(|b| b.is_solid() && b.solid_height() == 1.0)
|
||||
.unwrap_or(false)
|
||||
&& vol
|
||||
.get(pos + Vec3::new(0, 0, 0))
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
span,
|
||||
state::DeltaTime,
|
||||
sync::{Uid, UidAllocator},
|
||||
terrain::{Block, BlockKind, TerrainGrid},
|
||||
terrain::{Block, TerrainGrid},
|
||||
vol::ReadVol,
|
||||
};
|
||||
use rayon::iter::ParallelIterator;
|
||||
@ -341,7 +341,7 @@ impl<'a> System<'a> for Sys {
|
||||
let near_iter = (-hdist..hdist + 1)
|
||||
.map(move |i| {
|
||||
(-hdist..hdist + 1).map(move |j| {
|
||||
(1 - BlockKind::MAX_HEIGHT.ceil() as i32 + z_min.floor() as i32
|
||||
(1 - Block::MAX_HEIGHT.ceil() as i32 + z_min.floor() as i32
|
||||
..z_max.ceil() as i32 + 1)
|
||||
.map(move |k| (i, j, k))
|
||||
})
|
||||
@ -370,7 +370,7 @@ impl<'a> System<'a> for Sys {
|
||||
let block_aabb = Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32)
|
||||
+ Vec3::new(1.0, 1.0, block.get_height()),
|
||||
+ Vec3::new(1.0, 1.0, block.solid_height()),
|
||||
};
|
||||
|
||||
if player_aabb.collides_with_aabb(block_aabb) {
|
||||
@ -442,9 +442,9 @@ impl<'a> System<'a> for Sys {
|
||||
block_pos,
|
||||
Aabb {
|
||||
min: block_pos.map(|e| e as f32),
|
||||
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.get_height()),
|
||||
max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.solid_height()),
|
||||
},
|
||||
block.get_height(),
|
||||
block.solid_height(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
@ -557,7 +557,7 @@ impl<'a> System<'a> for Sys {
|
||||
&terrain,
|
||||
&|block| {
|
||||
block.is_solid()
|
||||
&& block.get_height() >= (pos.0.z - 0.05).rem_euclid(1.0)
|
||||
&& block.solid_height() >= (pos.0.z - 0.05).rem_euclid(1.0)
|
||||
},
|
||||
near_iter.clone(),
|
||||
radius,
|
||||
@ -571,7 +571,7 @@ impl<'a> System<'a> for Sys {
|
||||
)
|
||||
.ok()
|
||||
.filter(|block| block.is_solid())
|
||||
.map(|block| block.get_height())
|
||||
.map(|block| block.solid_height())
|
||||
.unwrap_or(0.0);
|
||||
pos.0.z = (pos.0.z - 0.05).floor() + snap_height;
|
||||
physics_state.on_ground = true;
|
||||
@ -609,7 +609,7 @@ impl<'a> System<'a> for Sys {
|
||||
physics_state.in_fluid = collision_iter(
|
||||
pos.0,
|
||||
&terrain,
|
||||
&|block| block.is_fluid(),
|
||||
&|block| block.is_liquid(),
|
||||
near_iter.clone(),
|
||||
radius,
|
||||
z_min..z_max,
|
||||
|
@ -1,103 +1,74 @@
|
||||
use super::SpriteKind;
|
||||
use crate::{make_case_elim, vol::Vox};
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use lazy_static::lazy_static;
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, convert::TryFrom, fmt, ops::Deref};
|
||||
use vek::*;
|
||||
|
||||
make_case_elim!(
|
||||
block_kind,
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, IntoEnumIterator)]
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Hash,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
IntoEnumIterator,
|
||||
FromPrimitive,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum BlockKind {
|
||||
Air = 0x00,
|
||||
Normal = 0x01,
|
||||
Dense = 0x02,
|
||||
Rock = 0x03,
|
||||
Grass = 0x04,
|
||||
Leaves = 0x05,
|
||||
Water = 0x06,
|
||||
LargeCactus = 0x07,
|
||||
BarrelCactus = 0x08,
|
||||
RoundCactus = 0x09,
|
||||
ShortCactus = 0x0A,
|
||||
MedFlatCactus = 0x0B,
|
||||
ShortFlatCactus = 0x0C,
|
||||
BlueFlower = 0x0D,
|
||||
PinkFlower = 0x0E,
|
||||
PurpleFlower = 0x0F,
|
||||
RedFlower = 0x10,
|
||||
WhiteFlower = 0x11,
|
||||
YellowFlower = 0x12,
|
||||
Sunflower = 0x13,
|
||||
LongGrass = 0x14,
|
||||
MediumGrass = 0x15,
|
||||
ShortGrass = 0x16,
|
||||
Apple = 0x17,
|
||||
Mushroom = 0x18,
|
||||
Liana = 0x19,
|
||||
Velorite = 0x1A,
|
||||
VeloriteFrag = 0x1B,
|
||||
Chest = 0x1C,
|
||||
Pumpkin = 0x1D,
|
||||
Welwitch = 0x1E,
|
||||
LingonBerry = 0x1F,
|
||||
LeafyPlant = 0x20,
|
||||
Fern = 0x21,
|
||||
DeadBush = 0x22,
|
||||
Blueberry = 0x23,
|
||||
Ember = 0x24,
|
||||
Corn = 0x25,
|
||||
WheatYellow = 0x26,
|
||||
WheatGreen = 0x27,
|
||||
Cabbage = 0x28,
|
||||
Flax = 0x29,
|
||||
Carrot = 0x2A,
|
||||
Tomato = 0x2B,
|
||||
Radish = 0x2C,
|
||||
Coconut = 0x2D,
|
||||
Turnip = 0x2E,
|
||||
Window1 = 0x2F,
|
||||
Window2 = 0x30,
|
||||
Window3 = 0x31,
|
||||
Window4 = 0x32,
|
||||
Scarecrow = 0x33,
|
||||
StreetLamp = 0x34,
|
||||
StreetLampTall = 0x35,
|
||||
Door = 0x36,
|
||||
Bed = 0x37,
|
||||
Bench = 0x38,
|
||||
ChairSingle = 0x39,
|
||||
ChairDouble = 0x3A,
|
||||
CoatRack = 0x3B,
|
||||
Crate = 0x3C,
|
||||
DrawerLarge = 0x3D,
|
||||
DrawerMedium = 0x3E,
|
||||
DrawerSmall = 0x3F,
|
||||
DungeonWallDecor = 0x40,
|
||||
HangingBasket = 0x41,
|
||||
HangingSign = 0x42,
|
||||
WallLamp = 0x43,
|
||||
Planter = 0x44,
|
||||
Shelf = 0x45,
|
||||
TableSide = 0x46,
|
||||
TableDining = 0x47,
|
||||
TableDouble = 0x48,
|
||||
WardrobeSingle = 0x49,
|
||||
WardrobeDouble = 0x4A,
|
||||
LargeGrass = 0x4B,
|
||||
Pot = 0x4C,
|
||||
Stones = 0x4D,
|
||||
Twigs = 0x4E,
|
||||
ShinyGem = 0x4F,
|
||||
DropGate = 0x50,
|
||||
DropGateBottom = 0x51,
|
||||
GrassSnow = 0x52,
|
||||
Reed = 0x53,
|
||||
Beehive = 0x54,
|
||||
Air = 0x00, // Air counts as a fluid
|
||||
Water = 0x01,
|
||||
// 0x02 <= x < 0x10 are reserved for other fluids. These are 2^n aligned to allow bitwise
|
||||
// checking of common conditions. For example, `is_fluid` is just `block_kind &
|
||||
// 0x0F == 0` (this is a very common operation used in meshing that could do with
|
||||
// being *very* fast).
|
||||
Rock = 0x10,
|
||||
WeakRock = 0x11, // Explodable
|
||||
// 0x12 <= x < 0x20 is reserved for future rocks
|
||||
Grass = 0x20, // Note: *not* the same as grass sprites
|
||||
// 0x21 <= x < 0x30 is reserved for future grasses
|
||||
Earth = 0x30,
|
||||
Sand = 0x31,
|
||||
// 0x32 <= x < 0x40 is reserved for future earths/muds/gravels/sands/etc.
|
||||
Wood = 0x40,
|
||||
Leaves = 0x41,
|
||||
// 0x42 <= x < 0x50 is reserved for future tree parts
|
||||
|
||||
// Covers all other cases (we sometimes have bizarrely coloured misc blocks, and also we
|
||||
// often want to experiment with new kinds of block without allocating them a
|
||||
// dedicated block kind.
|
||||
Misc = 0xFF,
|
||||
}
|
||||
);
|
||||
|
||||
impl BlockKind {
|
||||
pub const fn is_air(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::Air => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_fluid(&self) -> bool { *self as u8 & 0xF0 == 0x00 }
|
||||
|
||||
pub const fn is_liquid(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::Water => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_filled(&self) -> bool { !self.is_fluid() }
|
||||
}
|
||||
|
||||
impl fmt::Display for BlockKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) }
|
||||
}
|
||||
@ -114,408 +85,11 @@ impl<'a> TryFrom<&'a str> for BlockKind {
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> { BLOCK_KINDS.get(s).copied().ok_or(()) }
|
||||
}
|
||||
|
||||
impl BlockKind {
|
||||
pub const MAX_HEIGHT: f32 = 3.0;
|
||||
|
||||
pub fn is_tangible(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::Air => false,
|
||||
kind => !kind.is_fluid(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_air(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::Air => true,
|
||||
BlockKind::LargeCactus => true,
|
||||
BlockKind::BarrelCactus => true,
|
||||
BlockKind::RoundCactus => true,
|
||||
BlockKind::ShortCactus => true,
|
||||
BlockKind::MedFlatCactus => true,
|
||||
BlockKind::ShortFlatCactus => true,
|
||||
BlockKind::BlueFlower => true,
|
||||
BlockKind::PinkFlower => true,
|
||||
BlockKind::PurpleFlower => true,
|
||||
BlockKind::RedFlower => true,
|
||||
BlockKind::WhiteFlower => true,
|
||||
BlockKind::YellowFlower => true,
|
||||
BlockKind::Sunflower => true,
|
||||
BlockKind::LongGrass => true,
|
||||
BlockKind::MediumGrass => true,
|
||||
BlockKind::ShortGrass => true,
|
||||
BlockKind::Apple => true,
|
||||
BlockKind::Mushroom => true,
|
||||
BlockKind::Liana => true,
|
||||
BlockKind::Velorite => true,
|
||||
BlockKind::VeloriteFrag => true,
|
||||
BlockKind::Chest => true,
|
||||
BlockKind::Welwitch => true,
|
||||
BlockKind::LingonBerry => true,
|
||||
BlockKind::LeafyPlant => true,
|
||||
BlockKind::Fern => true,
|
||||
BlockKind::DeadBush => true,
|
||||
BlockKind::Blueberry => true,
|
||||
BlockKind::Ember => true,
|
||||
BlockKind::Corn => true,
|
||||
BlockKind::WheatYellow => true,
|
||||
BlockKind::WheatGreen => true,
|
||||
BlockKind::Cabbage => false,
|
||||
BlockKind::Pumpkin => false,
|
||||
BlockKind::Flax => true,
|
||||
BlockKind::Carrot => true,
|
||||
BlockKind::Tomato => false,
|
||||
BlockKind::Radish => true,
|
||||
BlockKind::Turnip => true,
|
||||
BlockKind::Coconut => true,
|
||||
BlockKind::Window1 => true,
|
||||
BlockKind::Window2 => true,
|
||||
BlockKind::Window3 => true,
|
||||
BlockKind::Window4 => true,
|
||||
BlockKind::Scarecrow => true,
|
||||
BlockKind::StreetLamp => true,
|
||||
BlockKind::StreetLampTall => true,
|
||||
BlockKind::Door => false,
|
||||
BlockKind::Bed => false,
|
||||
BlockKind::Bench => false,
|
||||
BlockKind::ChairSingle => false,
|
||||
BlockKind::ChairDouble => false,
|
||||
BlockKind::CoatRack => false,
|
||||
BlockKind::Crate => false,
|
||||
BlockKind::DrawerLarge => false,
|
||||
BlockKind::DrawerMedium => false,
|
||||
BlockKind::DrawerSmall => false,
|
||||
BlockKind::DungeonWallDecor => false,
|
||||
BlockKind::HangingBasket => true,
|
||||
BlockKind::HangingSign => true,
|
||||
BlockKind::WallLamp => true,
|
||||
BlockKind::Planter => false,
|
||||
BlockKind::Shelf => true,
|
||||
BlockKind::TableSide => false,
|
||||
BlockKind::TableDining => false,
|
||||
BlockKind::TableDouble => false,
|
||||
BlockKind::WardrobeSingle => false,
|
||||
BlockKind::WardrobeDouble => false,
|
||||
BlockKind::Pot => false,
|
||||
BlockKind::Stones => true,
|
||||
BlockKind::Twigs => true,
|
||||
BlockKind::ShinyGem => true,
|
||||
BlockKind::DropGate => false,
|
||||
BlockKind::DropGateBottom => false,
|
||||
BlockKind::GrassSnow => true,
|
||||
BlockKind::Reed => true,
|
||||
BlockKind::Beehive => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_fluid(&self) -> bool { matches!(self, BlockKind::Water) }
|
||||
|
||||
pub fn get_glow(&self) -> Option<u8> {
|
||||
// TODO: When we have proper volumetric lighting
|
||||
// match self {
|
||||
// BlockKind::StreetLamp | BlockKind::StreetLampTall => Some(20),
|
||||
// BlockKind::Velorite | BlockKind::VeloriteFrag => Some(10),
|
||||
// _ => None,
|
||||
// }
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_opaque(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::Air => false,
|
||||
BlockKind::Water => false,
|
||||
BlockKind::LargeCactus => false,
|
||||
BlockKind::BarrelCactus => false,
|
||||
BlockKind::RoundCactus => false,
|
||||
BlockKind::ShortCactus => false,
|
||||
BlockKind::MedFlatCactus => false,
|
||||
BlockKind::ShortFlatCactus => false,
|
||||
BlockKind::BlueFlower => false,
|
||||
BlockKind::PinkFlower => false,
|
||||
BlockKind::PurpleFlower => false,
|
||||
BlockKind::RedFlower => false,
|
||||
BlockKind::WhiteFlower => false,
|
||||
BlockKind::YellowFlower => false,
|
||||
BlockKind::Sunflower => false,
|
||||
BlockKind::LongGrass => false,
|
||||
BlockKind::MediumGrass => false,
|
||||
BlockKind::ShortGrass => false,
|
||||
BlockKind::Apple => false,
|
||||
BlockKind::Mushroom => false,
|
||||
BlockKind::Liana => false,
|
||||
BlockKind::Velorite => false,
|
||||
BlockKind::VeloriteFrag => false,
|
||||
BlockKind::Chest => false,
|
||||
BlockKind::Pumpkin => false,
|
||||
BlockKind::Welwitch => false,
|
||||
BlockKind::LingonBerry => false,
|
||||
BlockKind::LeafyPlant => false,
|
||||
BlockKind::Fern => false,
|
||||
BlockKind::DeadBush => false,
|
||||
BlockKind::Blueberry => false,
|
||||
BlockKind::Ember => false,
|
||||
BlockKind::Corn => false,
|
||||
BlockKind::WheatYellow => false,
|
||||
BlockKind::WheatGreen => false,
|
||||
BlockKind::Cabbage => false,
|
||||
BlockKind::Flax => false,
|
||||
BlockKind::Carrot => false,
|
||||
BlockKind::Tomato => false,
|
||||
BlockKind::Radish => false,
|
||||
BlockKind::Turnip => false,
|
||||
BlockKind::Coconut => false,
|
||||
BlockKind::Window1 => false,
|
||||
BlockKind::Window2 => false,
|
||||
BlockKind::Window3 => false,
|
||||
BlockKind::Window4 => false,
|
||||
BlockKind::Scarecrow => false,
|
||||
BlockKind::StreetLamp => false,
|
||||
BlockKind::StreetLampTall => false,
|
||||
BlockKind::Door => false,
|
||||
BlockKind::Bed => false,
|
||||
BlockKind::Bench => false,
|
||||
BlockKind::ChairSingle => false,
|
||||
BlockKind::ChairDouble => false,
|
||||
BlockKind::CoatRack => false,
|
||||
BlockKind::Crate => false,
|
||||
BlockKind::DrawerLarge => false,
|
||||
BlockKind::DrawerMedium => false,
|
||||
BlockKind::DrawerSmall => false,
|
||||
BlockKind::DungeonWallDecor => false,
|
||||
BlockKind::HangingBasket => false,
|
||||
BlockKind::HangingSign => false,
|
||||
BlockKind::WallLamp => false,
|
||||
BlockKind::Planter => false,
|
||||
BlockKind::Shelf => false,
|
||||
BlockKind::TableSide => false,
|
||||
BlockKind::TableDining => false,
|
||||
BlockKind::TableDouble => false,
|
||||
BlockKind::WardrobeSingle => false,
|
||||
BlockKind::WardrobeDouble => false,
|
||||
BlockKind::LargeGrass => false,
|
||||
BlockKind::Pot => false,
|
||||
BlockKind::Stones => false,
|
||||
BlockKind::Twigs => false,
|
||||
BlockKind::ShinyGem => false,
|
||||
BlockKind::DropGate => false,
|
||||
BlockKind::DropGateBottom => false,
|
||||
BlockKind::GrassSnow => false,
|
||||
BlockKind::Reed => false,
|
||||
BlockKind::Beehive => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_solid(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::Air => false,
|
||||
BlockKind::Water => false,
|
||||
BlockKind::LargeCactus => true,
|
||||
BlockKind::BarrelCactus => true,
|
||||
BlockKind::RoundCactus => true,
|
||||
BlockKind::ShortCactus => true,
|
||||
BlockKind::MedFlatCactus => true,
|
||||
BlockKind::ShortFlatCactus => true,
|
||||
BlockKind::BlueFlower => false,
|
||||
BlockKind::PinkFlower => false,
|
||||
BlockKind::PurpleFlower => false,
|
||||
BlockKind::RedFlower => false,
|
||||
BlockKind::WhiteFlower => false,
|
||||
BlockKind::YellowFlower => false,
|
||||
BlockKind::Sunflower => false,
|
||||
BlockKind::LongGrass => false,
|
||||
BlockKind::MediumGrass => false,
|
||||
BlockKind::ShortGrass => false,
|
||||
BlockKind::Apple => true,
|
||||
BlockKind::Mushroom => false,
|
||||
BlockKind::Liana => false,
|
||||
BlockKind::Chest => true,
|
||||
BlockKind::Pumpkin => true,
|
||||
BlockKind::Welwitch => false,
|
||||
BlockKind::LingonBerry => false,
|
||||
BlockKind::LeafyPlant => false,
|
||||
BlockKind::Fern => false,
|
||||
BlockKind::DeadBush => false,
|
||||
BlockKind::Blueberry => false,
|
||||
BlockKind::Ember => false,
|
||||
BlockKind::Corn => false,
|
||||
BlockKind::WheatYellow => false,
|
||||
BlockKind::WheatGreen => false,
|
||||
BlockKind::Cabbage => true,
|
||||
BlockKind::Flax => false,
|
||||
BlockKind::Carrot => true,
|
||||
BlockKind::Tomato => true,
|
||||
BlockKind::Radish => true,
|
||||
BlockKind::Turnip => true,
|
||||
BlockKind::Coconut => true,
|
||||
BlockKind::Scarecrow => true,
|
||||
BlockKind::StreetLamp => true,
|
||||
BlockKind::StreetLampTall => true,
|
||||
BlockKind::Door => false,
|
||||
BlockKind::Bed => true,
|
||||
BlockKind::Bench => true,
|
||||
BlockKind::ChairSingle => true,
|
||||
BlockKind::ChairDouble => true,
|
||||
BlockKind::CoatRack => true,
|
||||
BlockKind::Crate => true,
|
||||
BlockKind::DrawerLarge => true,
|
||||
BlockKind::DrawerMedium => true,
|
||||
BlockKind::DrawerSmall => true,
|
||||
BlockKind::DungeonWallDecor => true,
|
||||
BlockKind::HangingBasket => false,
|
||||
BlockKind::HangingSign => false,
|
||||
BlockKind::WallLamp => false,
|
||||
BlockKind::Planter => true,
|
||||
BlockKind::Shelf => false,
|
||||
BlockKind::TableSide => true,
|
||||
BlockKind::TableDining => true,
|
||||
BlockKind::TableDouble => true,
|
||||
BlockKind::WardrobeSingle => true,
|
||||
BlockKind::WardrobeDouble => true,
|
||||
BlockKind::Pot => true,
|
||||
BlockKind::Stones => false,
|
||||
BlockKind::Twigs => false,
|
||||
BlockKind::ShinyGem => false,
|
||||
BlockKind::DropGate => true,
|
||||
BlockKind::DropGateBottom => false,
|
||||
BlockKind::GrassSnow => false,
|
||||
BlockKind::Reed => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_explodable(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::Leaves | BlockKind::Grass | BlockKind::Rock | BlockKind::GrassSnow => true,
|
||||
BlockKind::Air => false,
|
||||
bk => bk.is_air(), // Temporary catch for terrain sprites
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Integrate this into `is_solid` by returning an `Option<f32>`
|
||||
pub fn get_height(&self) -> f32 {
|
||||
// Beware: the height *must* be <= `MAX_HEIGHT` or the collision system will not
|
||||
// properly detect it!
|
||||
match self {
|
||||
BlockKind::Tomato => 1.65,
|
||||
BlockKind::LargeCactus => 2.5,
|
||||
BlockKind::Scarecrow => 3.0,
|
||||
BlockKind::Turnip => 0.36,
|
||||
BlockKind::Pumpkin => 0.81,
|
||||
BlockKind::Cabbage => 0.45,
|
||||
BlockKind::Chest => 1.09,
|
||||
BlockKind::StreetLamp => 3.0,
|
||||
BlockKind::Carrot => 0.18,
|
||||
BlockKind::Radish => 0.18,
|
||||
BlockKind::Door => 3.0,
|
||||
BlockKind::Bed => 1.54,
|
||||
BlockKind::Bench => 0.5,
|
||||
BlockKind::ChairSingle => 0.5,
|
||||
BlockKind::ChairDouble => 0.5,
|
||||
BlockKind::CoatRack => 2.36,
|
||||
BlockKind::Crate => 0.90,
|
||||
BlockKind::DrawerSmall => 1.0,
|
||||
BlockKind::DrawerMedium => 2.0,
|
||||
BlockKind::DrawerLarge => 2.0,
|
||||
BlockKind::DungeonWallDecor => 1.0,
|
||||
BlockKind::Planter => 1.09,
|
||||
BlockKind::TableSide => 1.27,
|
||||
BlockKind::TableDining => 1.45,
|
||||
BlockKind::TableDouble => 1.45,
|
||||
BlockKind::WardrobeSingle => 3.0,
|
||||
BlockKind::WardrobeDouble => 3.0,
|
||||
BlockKind::Pot => 0.90,
|
||||
_ => 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_collectible(&self) -> bool {
|
||||
match self {
|
||||
BlockKind::BlueFlower => false,
|
||||
BlockKind::PinkFlower => false,
|
||||
BlockKind::PurpleFlower => false,
|
||||
BlockKind::RedFlower => false,
|
||||
BlockKind::WhiteFlower => false,
|
||||
BlockKind::YellowFlower => false,
|
||||
BlockKind::Sunflower => false,
|
||||
BlockKind::LongGrass => false,
|
||||
BlockKind::MediumGrass => false,
|
||||
BlockKind::ShortGrass => false,
|
||||
BlockKind::Apple => true,
|
||||
BlockKind::Mushroom => true,
|
||||
BlockKind::Velorite => true,
|
||||
BlockKind::VeloriteFrag => true,
|
||||
BlockKind::Chest => true,
|
||||
BlockKind::Coconut => true,
|
||||
BlockKind::Stones => true,
|
||||
BlockKind::Twigs => true,
|
||||
BlockKind::ShinyGem => true,
|
||||
BlockKind::Crate => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[repr(packed)]
|
||||
pub struct Block {
|
||||
kind: BlockKind,
|
||||
color: [u8; 3],
|
||||
}
|
||||
|
||||
impl Block {
|
||||
pub const fn new(kind: BlockKind, color: Rgb<u8>) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
color: [color.r, color.g, color.b],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_color(&self) -> Option<Rgb<u8>> {
|
||||
if !self.is_air() {
|
||||
Some(self.color.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ori(&self) -> Option<u8> {
|
||||
match self.kind {
|
||||
BlockKind::Window1
|
||||
| BlockKind::Window2
|
||||
| BlockKind::Window3
|
||||
| BlockKind::Window4
|
||||
| BlockKind::Bed
|
||||
| BlockKind::Bench
|
||||
| BlockKind::ChairSingle
|
||||
| BlockKind::ChairDouble
|
||||
| BlockKind::CoatRack
|
||||
| BlockKind::Crate
|
||||
| BlockKind::DrawerLarge
|
||||
| BlockKind::DrawerMedium
|
||||
| BlockKind::DrawerSmall
|
||||
| BlockKind::DungeonWallDecor
|
||||
| BlockKind::HangingBasket
|
||||
| BlockKind::HangingSign
|
||||
| BlockKind::WallLamp
|
||||
| BlockKind::Planter
|
||||
| BlockKind::Shelf
|
||||
| BlockKind::TableSide
|
||||
| BlockKind::TableDining
|
||||
| BlockKind::TableDouble
|
||||
| BlockKind::WardrobeSingle
|
||||
| BlockKind::WardrobeDouble
|
||||
| BlockKind::Pot
|
||||
| BlockKind::Chest
|
||||
| BlockKind::DropGate
|
||||
| BlockKind::DropGateBottom
|
||||
| BlockKind::Door
|
||||
| BlockKind::Beehive => Some(self.color[0] & 0b111),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> BlockKind { self.kind }
|
||||
attr: [u8; 3],
|
||||
}
|
||||
|
||||
impl Deref for Block {
|
||||
@ -528,11 +102,119 @@ impl Vox for Block {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
kind: BlockKind::Air,
|
||||
color: [0; 3],
|
||||
attr: [0; 3],
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool { self.is_air() }
|
||||
fn is_empty(&self) -> bool { *self == Block::empty() }
|
||||
}
|
||||
|
||||
impl Block {
|
||||
pub const MAX_HEIGHT: f32 = 3.0;
|
||||
|
||||
pub const fn new(kind: BlockKind, color: Rgb<u8>) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
// Colours are only valid for non-fluids
|
||||
attr: if kind.is_filled() {
|
||||
[color.r, color.g, color.b]
|
||||
} else {
|
||||
[0; 3]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_color(&self) -> Option<Rgb<u8>> {
|
||||
if self.is_filled() {
|
||||
Some(self.attr.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_sprite(&self) -> Option<SpriteKind> {
|
||||
if !self.is_filled() {
|
||||
SpriteKind::from_u8(self.attr[0])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ori(&self) -> Option<u8> {
|
||||
if self.get_sprite()?.has_ori() {
|
||||
// TODO: Formalise this a bit better
|
||||
Some(self.attr[1] & 0b111)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_glow(&self) -> Option<u8> {
|
||||
// TODO: When we have proper volumetric lighting
|
||||
// match self.get_sprite()? {
|
||||
// SpriteKind::StreetLamp | SpriteKind::StreetLampTall => Some(20),
|
||||
// SpriteKind::Velorite | SpriteKind::VeloriteFrag => Some(10),
|
||||
// _ => None,
|
||||
// }
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_solid(&self) -> bool {
|
||||
self.get_sprite()
|
||||
.map(|s| s.solid_height().is_some())
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
pub fn is_explodable(&self) -> bool {
|
||||
match self.kind() {
|
||||
BlockKind::Leaves | BlockKind::Grass | BlockKind::WeakRock => true,
|
||||
// Explodable means that the terrain sprite will get removed anyway, so is good for
|
||||
// empty fluids TODO: Handle the case of terrain sprites we don't want to
|
||||
// have explode
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_collectible(&self) -> bool {
|
||||
self.get_sprite()
|
||||
.map(|s| s.is_collectible())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn is_opaque(&self) -> bool { self.kind().is_filled() }
|
||||
|
||||
pub fn solid_height(&self) -> f32 {
|
||||
self.get_sprite()
|
||||
.map(|s| s.solid_height().unwrap_or(0.0))
|
||||
.unwrap_or(1.0)
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> BlockKind { self.kind }
|
||||
|
||||
/// If this block is a fluid, replace its sprite.
|
||||
pub fn with_sprite(mut self, sprite: SpriteKind) -> Self {
|
||||
if !self.is_filled() {
|
||||
self.attr[0] = sprite as u8;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// If this block can have orientation, replace its sprite.
|
||||
pub fn with_ori(mut self, ori: u8) -> Self {
|
||||
if self.get_sprite().map(|s| s.has_ori()).unwrap_or(false) {
|
||||
self.attr[1] = (self.attr[1] & !0b111) | (ori & 0b111);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Remove the terrain sprite or solid aspects of a block
|
||||
pub fn into_vacant(mut self) -> Self {
|
||||
if self.is_fluid() {
|
||||
Block::new(self.kind(), Rgb::zero())
|
||||
} else {
|
||||
Block::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -2,6 +2,7 @@ pub mod biome;
|
||||
pub mod block;
|
||||
pub mod chonk;
|
||||
pub mod map;
|
||||
pub mod sprite;
|
||||
pub mod structure;
|
||||
|
||||
// Reexports
|
||||
@ -9,6 +10,7 @@ pub use self::{
|
||||
biome::BiomeKind,
|
||||
block::{Block, BlockKind},
|
||||
map::MapSizeLg,
|
||||
sprite::SpriteKind,
|
||||
structure::Structure,
|
||||
};
|
||||
use roots::find_roots_cubic;
|
||||
|
223
common/src/terrain/sprite.rs
Normal file
223
common/src/terrain/sprite.rs
Normal file
@ -0,0 +1,223 @@
|
||||
use crate::make_case_elim;
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use lazy_static::lazy_static;
|
||||
use num_derive::FromPrimitive;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, convert::TryFrom, fmt};
|
||||
|
||||
make_case_elim!(
|
||||
sprite_kind,
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Hash,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
IntoEnumIterator,
|
||||
FromPrimitive,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum SpriteKind {
|
||||
// Note that the values of these should be linearly contiguous to allow for quick
|
||||
// bounds-checking when casting to a u8.
|
||||
Empty = 0x00,
|
||||
BarrelCactus = 0x01,
|
||||
RoundCactus = 0x02,
|
||||
ShortCactus = 0x03,
|
||||
MedFlatCactus = 0x04,
|
||||
ShortFlatCactus = 0x05,
|
||||
BlueFlower = 0x06,
|
||||
PinkFlower = 0x07,
|
||||
PurpleFlower = 0x08,
|
||||
RedFlower = 0x09,
|
||||
WhiteFlower = 0x0A,
|
||||
YellowFlower = 0x0B,
|
||||
Sunflower = 0x0C,
|
||||
LongGrass = 0x0D,
|
||||
MediumGrass = 0x0E,
|
||||
ShortGrass = 0x0F,
|
||||
Apple = 0x10,
|
||||
Mushroom = 0x11,
|
||||
Liana = 0x12,
|
||||
Velorite = 0x13,
|
||||
VeloriteFrag = 0x14,
|
||||
Chest = 0x15,
|
||||
Pumpkin = 0x16,
|
||||
Welwitch = 0x17,
|
||||
LingonBerry = 0x18,
|
||||
LeafyPlant = 0x19,
|
||||
Fern = 0x1A,
|
||||
DeadBush = 0x1B,
|
||||
Blueberry = 0x1C,
|
||||
Ember = 0x1D,
|
||||
Corn = 0x1E,
|
||||
WheatYellow = 0x1F,
|
||||
WheatGreen = 0x20,
|
||||
Cabbage = 0x21,
|
||||
Flax = 0x22,
|
||||
Carrot = 0x23,
|
||||
Tomato = 0x24,
|
||||
Radish = 0x25,
|
||||
Coconut = 0x26,
|
||||
Turnip = 0x27,
|
||||
Window1 = 0x28,
|
||||
Window2 = 0x29,
|
||||
Window3 = 0x2A,
|
||||
Window4 = 0x2B,
|
||||
Scarecrow = 0x2C,
|
||||
StreetLamp = 0x2D,
|
||||
StreetLampTall = 0x2E,
|
||||
Door = 0x2F,
|
||||
Bed = 0x30,
|
||||
Bench = 0x31,
|
||||
ChairSingle = 0x32,
|
||||
ChairDouble = 0x33,
|
||||
CoatRack = 0x34,
|
||||
Crate = 0x35,
|
||||
DrawerLarge = 0x36,
|
||||
DrawerMedium = 0x37,
|
||||
DrawerSmall = 0x38,
|
||||
DungeonWallDecor = 0x39,
|
||||
HangingBasket = 0x3A,
|
||||
HangingSign = 0x3B,
|
||||
WallLamp = 0x3C,
|
||||
Planter = 0x3D,
|
||||
Shelf = 0x3E,
|
||||
TableSide = 0x3F,
|
||||
TableDining = 0x40,
|
||||
TableDouble = 0x41,
|
||||
WardrobeSingle = 0x42,
|
||||
WardrobeDouble = 0x43,
|
||||
LargeGrass = 0x44,
|
||||
Pot = 0x45,
|
||||
Stones = 0x46,
|
||||
Twigs = 0x47,
|
||||
ShinyGem = 0x48,
|
||||
DropGate = 0x49,
|
||||
DropGateBottom = 0x4A,
|
||||
GrassSnow = 0x4B,
|
||||
Reed = 0x4C,
|
||||
Beehive = 0x4D,
|
||||
LargeCactus = 0x4E,
|
||||
}
|
||||
);
|
||||
|
||||
impl SpriteKind {
|
||||
pub fn solid_height(&self) -> Option<f32> {
|
||||
// Beware: the height *must* be <= `MAX_HEIGHT` or the collision system will not
|
||||
// properly detect it!
|
||||
Some(match self {
|
||||
SpriteKind::Tomato => 1.65,
|
||||
SpriteKind::LargeCactus => 2.5,
|
||||
SpriteKind::Scarecrow => 3.0,
|
||||
SpriteKind::Turnip => 0.36,
|
||||
SpriteKind::Pumpkin => 0.81,
|
||||
SpriteKind::Cabbage => 0.45,
|
||||
SpriteKind::Chest => 1.09,
|
||||
SpriteKind::StreetLamp => 3.0,
|
||||
SpriteKind::Carrot => 0.18,
|
||||
SpriteKind::Radish => 0.18,
|
||||
// TODO: Uncomment this when we have a way to open doors
|
||||
// SpriteKind::Door => 3.0,
|
||||
SpriteKind::Bed => 1.54,
|
||||
SpriteKind::Bench => 0.5,
|
||||
SpriteKind::ChairSingle => 0.5,
|
||||
SpriteKind::ChairDouble => 0.5,
|
||||
SpriteKind::CoatRack => 2.36,
|
||||
SpriteKind::Crate => 0.90,
|
||||
SpriteKind::DrawerSmall => 1.0,
|
||||
SpriteKind::DrawerMedium => 2.0,
|
||||
SpriteKind::DrawerLarge => 2.0,
|
||||
SpriteKind::DungeonWallDecor => 1.0,
|
||||
SpriteKind::Planter => 1.09,
|
||||
SpriteKind::TableSide => 1.27,
|
||||
SpriteKind::TableDining => 1.45,
|
||||
SpriteKind::TableDouble => 1.45,
|
||||
SpriteKind::WardrobeSingle => 3.0,
|
||||
SpriteKind::WardrobeDouble => 3.0,
|
||||
SpriteKind::Pot => 0.90,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_collectible(&self) -> bool {
|
||||
match self {
|
||||
SpriteKind::BlueFlower => false,
|
||||
SpriteKind::PinkFlower => false,
|
||||
SpriteKind::PurpleFlower => false,
|
||||
SpriteKind::RedFlower => false,
|
||||
SpriteKind::WhiteFlower => false,
|
||||
SpriteKind::YellowFlower => false,
|
||||
SpriteKind::Sunflower => false,
|
||||
SpriteKind::LongGrass => false,
|
||||
SpriteKind::MediumGrass => false,
|
||||
SpriteKind::ShortGrass => false,
|
||||
SpriteKind::Apple => true,
|
||||
SpriteKind::Mushroom => true,
|
||||
SpriteKind::Velorite => true,
|
||||
SpriteKind::VeloriteFrag => true,
|
||||
SpriteKind::Chest => true,
|
||||
SpriteKind::Coconut => true,
|
||||
SpriteKind::Stones => true,
|
||||
SpriteKind::Twigs => true,
|
||||
SpriteKind::ShinyGem => true,
|
||||
SpriteKind::Crate => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_ori(&self) -> bool {
|
||||
match self {
|
||||
SpriteKind::Window1
|
||||
| SpriteKind::Window2
|
||||
| SpriteKind::Window3
|
||||
| SpriteKind::Window4
|
||||
| SpriteKind::Bed
|
||||
| SpriteKind::Bench
|
||||
| SpriteKind::ChairSingle
|
||||
| SpriteKind::ChairDouble
|
||||
| SpriteKind::CoatRack
|
||||
| SpriteKind::Crate
|
||||
| SpriteKind::DrawerLarge
|
||||
| SpriteKind::DrawerMedium
|
||||
| SpriteKind::DrawerSmall
|
||||
| SpriteKind::DungeonWallDecor
|
||||
| SpriteKind::HangingBasket
|
||||
| SpriteKind::HangingSign
|
||||
| SpriteKind::WallLamp
|
||||
| SpriteKind::Planter
|
||||
| SpriteKind::Shelf
|
||||
| SpriteKind::TableSide
|
||||
| SpriteKind::TableDining
|
||||
| SpriteKind::TableDouble
|
||||
| SpriteKind::WardrobeSingle
|
||||
| SpriteKind::WardrobeDouble
|
||||
| SpriteKind::Pot
|
||||
| SpriteKind::Chest
|
||||
| SpriteKind::DropGate
|
||||
| SpriteKind::DropGateBottom
|
||||
| SpriteKind::Door
|
||||
| SpriteKind::Beehive => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SpriteKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) }
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SPRITE_KINDS: HashMap<String, SpriteKind> = SpriteKind::into_enum_iter()
|
||||
.map(|sk| (sk.to_string(), sk))
|
||||
.collect();
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for SpriteKind {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> { SPRITE_KINDS.get(s).copied().ok_or(()) }
|
||||
}
|
@ -148,14 +148,14 @@ impl Asset for Structure {
|
||||
center: Vec3::zero(),
|
||||
vol,
|
||||
empty: StructureBlock::empty(),
|
||||
default_kind: BlockKind::Normal,
|
||||
default_kind: BlockKind::Misc,
|
||||
})
|
||||
} else {
|
||||
Ok(Self {
|
||||
center: Vec3::zero(),
|
||||
vol: Dyna::filled(Vec3::zero(), StructureBlock::empty(), ()),
|
||||
empty: StructureBlock::empty(),
|
||||
default_kind: BlockKind::Normal,
|
||||
default_kind: BlockKind::Misc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +131,22 @@ pub trait WriteVol: BaseVol {
|
||||
/// Set the voxel at the provided position in the volume to the provided
|
||||
/// value.
|
||||
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), Self::Error>;
|
||||
|
||||
/// Map a voxel to another using the provided function.
|
||||
// TODO: Is `map` the right name? Implies a change in type.
|
||||
fn map<F: FnOnce(Self::Vox) -> Self::Vox>(
|
||||
&mut self,
|
||||
pos: Vec3<i32>,
|
||||
f: F,
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
Self: ReadVol,
|
||||
Self::Vox: Clone,
|
||||
{
|
||||
// This is *deliberately* not using a get_mut since this might trigger a
|
||||
// repr change of the underlying volume
|
||||
self.set(pos, f(self.get(pos)?.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// A volume (usually rather a reference to a volume) that is convertible into
|
||||
|
@ -569,7 +569,7 @@ pub fn handle_explosion(
|
||||
let terrain = ecs.read_resource::<TerrainGrid>();
|
||||
let _ = terrain
|
||||
.ray(pos, pos + dir * power)
|
||||
.until(|block| block.is_fluid() || rand::random::<f32>() < 0.05)
|
||||
.until(|block| block.is_liquid() || rand::random::<f32>() < 0.05)
|
||||
.for_each(|block: &Block, pos| {
|
||||
if block.is_explodable() {
|
||||
block_change.set(pos, Block::empty());
|
||||
|
@ -132,7 +132,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
state.write_component(entity, event);
|
||||
if item_was_added {
|
||||
// we made sure earlier the block was not already modified this tick
|
||||
state.set_block(pos, Block::empty())
|
||||
state.set_block(pos, block.into_vacant())
|
||||
};
|
||||
}
|
||||
} else {
|
||||
|
@ -97,7 +97,7 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
||||
if vol
|
||||
.get(outer.min + pos)
|
||||
.ok()
|
||||
.map_or(false, |b| b.is_air() || b.is_fluid())
|
||||
.map_or(false, |b| b.is_fluid())
|
||||
{
|
||||
*dest = src - 1;
|
||||
// Can't propagate further
|
||||
@ -127,14 +127,14 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
||||
// Down is special cased and we know up is a ray
|
||||
// Special cased ray propagation
|
||||
let pos = Vec3::new(pos.x, pos.y, pos.z - 1);
|
||||
let (is_air, is_fluid) = vol_cached
|
||||
let (is_air, is_liquid) = vol_cached
|
||||
.get(outer.min + pos)
|
||||
.ok()
|
||||
.map_or((false, false), |b| (b.is_air(), b.is_fluid()));
|
||||
.map_or((false, false), |b| (b.is_air(), b.is_liquid()));
|
||||
light_map[lm_idx(pos.x, pos.y, pos.z)] = if is_air {
|
||||
prop_que.push_back((pos.x as u8, pos.y as u8, pos.z as u16));
|
||||
SUNLIGHT
|
||||
} else if is_fluid {
|
||||
} else if is_liquid {
|
||||
prop_que.push_back((pos.x as u8, pos.y as u8, pos.z as u16));
|
||||
SUNLIGHT - 1
|
||||
} else {
|
||||
@ -268,7 +268,7 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
|
||||
opaque_limits = opaque_limits
|
||||
.map(|l| l.including(z))
|
||||
.or_else(|| Some(Limits::from_value(z)));
|
||||
} else if block.is_fluid() {
|
||||
} else if block.is_liquid() {
|
||||
fluid_limits = fluid_limits
|
||||
.map(|l| l.including(z))
|
||||
.or_else(|| Some(Limits::from_value(z)));
|
||||
@ -412,15 +412,15 @@ fn should_draw_greedy(
|
||||
let to = flat_get(pos);
|
||||
let from_opaque = from.is_opaque();
|
||||
if from_opaque == to.is_opaque() {
|
||||
// Check the interface of fluid and non-tangible non-fluids (e.g. air).
|
||||
let from_fluid = from.is_fluid();
|
||||
if from_fluid == to.is_fluid() || from.is_tangible() || to.is_tangible() {
|
||||
// Check the interface of liquid and non-tangible non-liquid (e.g. air).
|
||||
let from_liquid = from.is_liquid();
|
||||
if from_liquid == to.is_liquid() || from.is_opaque() || to.is_opaque() {
|
||||
None
|
||||
} else {
|
||||
// While fluid is not culled, we still try to keep a consistent orientation as
|
||||
// we do for land; if going from fluid to non-fluid,
|
||||
// While liquid is not culled, we still try to keep a consistent orientation as
|
||||
// we do for land; if going from liquid to non-liquid,
|
||||
// forwards-facing; otherwise, backwards-facing.
|
||||
Some((from_fluid, FaceKind::Fluid))
|
||||
Some((from_liquid, FaceKind::Fluid))
|
||||
}
|
||||
} else {
|
||||
// If going from transparent to opaque, backward facing; otherwise, forward
|
||||
@ -428,9 +428,9 @@ fn should_draw_greedy(
|
||||
Some((
|
||||
from_opaque,
|
||||
FaceKind::Opaque(if from_opaque {
|
||||
to.is_fluid()
|
||||
to.is_liquid()
|
||||
} else {
|
||||
from.is_fluid()
|
||||
from.is_liquid()
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ impl Globals {
|
||||
0.0,
|
||||
0.0,
|
||||
],
|
||||
medium: [if medium.is_fluid() { 1 } else { 0 }; 4],
|
||||
medium: [if medium.is_liquid() { 1 } else { 0 }; 4],
|
||||
select_pos: select_pos
|
||||
.map(|sp| Vec4::from(sp) + Vec4::unit_w())
|
||||
.unwrap_or(Vec4::zero())
|
||||
|
@ -17,7 +17,7 @@ use common::{
|
||||
figure::Segment,
|
||||
span,
|
||||
spiral::Spiral2d,
|
||||
terrain::{block, Block, BlockKind, TerrainChunk},
|
||||
terrain::{sprite, Block, SpriteKind, TerrainChunk},
|
||||
vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, Vox},
|
||||
volumes::vol_grid_2d::{VolGrid2d, VolGrid2dError},
|
||||
};
|
||||
@ -49,7 +49,7 @@ pub struct TerrainChunkData {
|
||||
opaque_model: Model<TerrainPipeline>,
|
||||
fluid_model: Option<Model<FluidPipeline>>,
|
||||
col_lights: guillotiere::AllocId,
|
||||
sprite_instances: HashMap<(BlockKind, usize), Instances<SpriteInstance>>,
|
||||
sprite_instances: HashMap<(SpriteKind, usize), Instances<SpriteInstance>>,
|
||||
locals: Consts<TerrainLocals>,
|
||||
pub blocks_of_interest: BlocksOfInterest,
|
||||
|
||||
@ -75,7 +75,7 @@ struct MeshWorkerResponse {
|
||||
opaque_mesh: Mesh<TerrainPipeline>,
|
||||
fluid_mesh: Mesh<FluidPipeline>,
|
||||
col_lights_info: ColLightInfo,
|
||||
sprite_instances: HashMap<(BlockKind, usize), Vec<SpriteInstance>>,
|
||||
sprite_instances: HashMap<(SpriteKind, usize), Vec<SpriteInstance>>,
|
||||
started_tick: u64,
|
||||
blocks_of_interest: BlocksOfInterest,
|
||||
}
|
||||
@ -94,7 +94,7 @@ struct SpriteModelConfig<Model> {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
/// Configuration data for a group of sprites (currently associated with a
|
||||
/// particular BlockKind).
|
||||
/// particular SpriteKind).
|
||||
struct SpriteConfig<Model> {
|
||||
/// All possible model variations for this sprite.
|
||||
// NOTE: Could make constant per sprite type, but eliminating this indirection and
|
||||
@ -109,7 +109,7 @@ struct SpriteConfig<Model> {
|
||||
/// Configuration data for all sprite models.
|
||||
///
|
||||
/// NOTE: Model is an asset path to the appropriate sprite .vox model.
|
||||
type SpriteSpec = block::block_kind::PureCases<Option<SpriteConfig<String>>>;
|
||||
type SpriteSpec = sprite::sprite_kind::PureCases<Option<SpriteConfig<String>>>;
|
||||
|
||||
/// Function executed by worker threads dedicated to chunk meshing.
|
||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||
@ -122,7 +122,7 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug>(
|
||||
max_texture_size: u16,
|
||||
chunk: Arc<TerrainChunk>,
|
||||
range: Aabb<i32>,
|
||||
sprite_data: &HashMap<(BlockKind, usize), Vec<SpriteData>>,
|
||||
sprite_data: &HashMap<(SpriteKind, usize), Vec<SpriteData>>,
|
||||
sprite_config: &SpriteSpec,
|
||||
) -> MeshWorkerResponse {
|
||||
span!(_guard, "mesh_worker");
|
||||
@ -146,14 +146,19 @@ fn mesh_worker<V: BaseVol<Vox = Block> + RectRasterableVol + ReadVol + Debug>(
|
||||
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 sprite = if let Some(sprite) = block.get_sprite() {
|
||||
sprite
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(cfg) = block.kind().elim_case_pure(&sprite_config) {
|
||||
if let Some(cfg) = sprite.elim_case_pure(&sprite_config) {
|
||||
let seed = wpos.x as u64 * 3
|
||||
+ wpos.y as u64 * 7
|
||||
+ wpos.x as u64 * wpos.y as u64; // Awful PRNG
|
||||
let ori = (block.get_ori().unwrap_or((seed % 4) as u8 * 2)) & 0b111;
|
||||
let variation = seed as usize % cfg.variations.len();
|
||||
let key = (block.kind(), variation);
|
||||
let key = (sprite, variation);
|
||||
// NOTE: Safe because we called sprite_config_for already.
|
||||
// NOTE: Safe because 0 ≤ ori < 8
|
||||
let sprite_data = &sprite_data[&key][0];
|
||||
@ -218,7 +223,7 @@ pub struct Terrain<V: RectRasterableVol> {
|
||||
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
||||
|
||||
// GPU data
|
||||
sprite_data: Arc<HashMap<(BlockKind, usize), Vec<SpriteData>>>,
|
||||
sprite_data: Arc<HashMap<(SpriteKind, usize), Vec<SpriteData>>>,
|
||||
sprite_col_lights: Texture<ColLightFmt>,
|
||||
col_lights: Texture<ColLightFmt>,
|
||||
waves: Texture,
|
||||
@ -254,7 +259,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
let sprite_config_ = &sprite_config;
|
||||
// NOTE: Tracks the start vertex of the next model to be meshed.
|
||||
|
||||
let sprite_data: HashMap<(BlockKind, usize), _> = BlockKind::into_enum_iter()
|
||||
let sprite_data: HashMap<(SpriteKind, usize), _> = SpriteKind::into_enum_iter()
|
||||
.filter_map(|kind| Some((kind, kind.elim_case_pure(&sprite_config_).as_ref()?)))
|
||||
.flat_map(|(kind, sprite_config)| {
|
||||
let wind_sway = sprite_config.wind_sway;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use common::{
|
||||
span,
|
||||
terrain::{BlockKind, TerrainChunk},
|
||||
terrain::{BlockKind, SpriteKind, TerrainChunk},
|
||||
vol::{IntoVolIterator, RectRasterableVol},
|
||||
};
|
||||
use rand::prelude::*;
|
||||
@ -45,16 +45,18 @@ impl BlocksOfInterest {
|
||||
grass.push(pos)
|
||||
}
|
||||
},
|
||||
BlockKind::Ember => embers.push(pos),
|
||||
BlockKind::Beehive => beehives.push(pos),
|
||||
BlockKind::Reed => reeds.push(pos),
|
||||
BlockKind::PinkFlower => flowers.push(pos),
|
||||
BlockKind::PurpleFlower => flowers.push(pos),
|
||||
BlockKind::RedFlower => flowers.push(pos),
|
||||
BlockKind::WhiteFlower => flowers.push(pos),
|
||||
BlockKind::YellowFlower => flowers.push(pos),
|
||||
BlockKind::Sunflower => flowers.push(pos),
|
||||
_ => {},
|
||||
_ => match block.get_sprite() {
|
||||
Some(SpriteKind::Ember) => embers.push(pos),
|
||||
Some(SpriteKind::Beehive) => beehives.push(pos),
|
||||
Some(SpriteKind::Reed) => reeds.push(pos),
|
||||
Some(SpriteKind::PinkFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::PurpleFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::RedFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::WhiteFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::YellowFlower) => flowers.push(pos),
|
||||
Some(SpriteKind::Sunflower) => flowers.push(pos),
|
||||
_ => {},
|
||||
},
|
||||
});
|
||||
|
||||
Self {
|
||||
|
@ -86,7 +86,7 @@ impl SessionState {
|
||||
key_state: KeyState::default(),
|
||||
inputs: comp::ControllerInputs::default(),
|
||||
hud,
|
||||
selected_block: Block::new(BlockKind::Normal, Rgb::broadcast(255)),
|
||||
selected_block: Block::new(BlockKind::Misc, Rgb::broadcast(255)),
|
||||
voxygen_i18n,
|
||||
walk_forward_dir,
|
||||
walk_right_dir,
|
||||
@ -1161,7 +1161,7 @@ fn under_cursor(
|
||||
|
||||
let cam_ray = terrain
|
||||
.ray(cam_pos, cam_pos + cam_dir * 100.0)
|
||||
.until(|block| block.is_tangible())
|
||||
.until(|block| block.is_filled() || block.is_collectible())
|
||||
.cast();
|
||||
|
||||
let cam_dist = cam_ray.0;
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
use common::{
|
||||
terrain::{
|
||||
structure::{self, StructureBlock},
|
||||
Block, BlockKind, Structure,
|
||||
Block, BlockKind, SpriteKind, Structure,
|
||||
},
|
||||
vol::{ReadVol, Vox},
|
||||
};
|
||||
@ -254,15 +254,19 @@ impl<'a> BlockGen<'a> {
|
||||
|
||||
let grass_depth = (1.5 + 2.0 * chaos).min(height - basement_height);
|
||||
let block = if (wposf.z as f32) < height - grass_depth {
|
||||
let stone_factor = (height - grass_depth - wposf.z as f32) * 0.15;
|
||||
let col = Lerp::lerp(
|
||||
sub_surface_color,
|
||||
stone_col.map(|e| e as f32 / 255.0),
|
||||
(height - grass_depth - wposf.z as f32) * 0.15,
|
||||
stone_factor,
|
||||
)
|
||||
.map(|e| (e * 255.0) as u8);
|
||||
|
||||
// Underground
|
||||
Some(Block::new(BlockKind::Normal, col))
|
||||
if stone_factor >= 0.5 {
|
||||
Some(Block::new(BlockKind::Rock, col))
|
||||
} else {
|
||||
Some(Block::new(BlockKind::Earth, col))
|
||||
}
|
||||
} else if (wposf.z as f32) < height {
|
||||
let grass_factor = (wposf.z as f32 - (height - grass_depth))
|
||||
.div(grass_depth)
|
||||
@ -273,81 +277,10 @@ impl<'a> BlockGen<'a> {
|
||||
if grass_factor > 0.7 {
|
||||
BlockKind::Grass
|
||||
} else {
|
||||
BlockKind::Normal
|
||||
BlockKind::Earth
|
||||
},
|
||||
col.map(|e| (e * 255.0) as u8),
|
||||
))
|
||||
// } else if (wposf.z as f32) < height + 0.9
|
||||
// && temp < CONFIG.desert_temp
|
||||
// && (wposf.z as f32 > water_height + 3.0)
|
||||
// && marble > 0.6
|
||||
// && marble_small > 0.55
|
||||
// && (marble * 3173.7).fract() < 0.6
|
||||
// && humidity > CONFIG.desert_hum
|
||||
// && false
|
||||
// {
|
||||
// let treasures = [BlockKind::Chest, BlockKind::Velorite];
|
||||
|
||||
// let flowers = [
|
||||
// BlockKind::BlueFlower,
|
||||
// BlockKind::PinkFlower,
|
||||
// BlockKind::PurpleFlower,
|
||||
// BlockKind::RedFlower,
|
||||
// BlockKind::WhiteFlower,
|
||||
// BlockKind::YellowFlower,
|
||||
// BlockKind::Sunflower,
|
||||
// BlockKind::Mushroom, //TODO: Better spawnrules
|
||||
// BlockKind::LeafyPlant,
|
||||
// BlockKind::Blueberry,
|
||||
// BlockKind::LingonBerry,
|
||||
// BlockKind::Fern,
|
||||
// /*BlockKind::Twigs, // TODO: Better spawnrules
|
||||
// *BlockKind::Stones, // TODO: Better spawnrules
|
||||
// *BlockKind::ShinyGem, // TODO: Better spawnrules */
|
||||
// ];
|
||||
// let grasses = [
|
||||
// BlockKind::LongGrass,
|
||||
// BlockKind::MediumGrass,
|
||||
// BlockKind::ShortGrass,
|
||||
// ];
|
||||
|
||||
// Some(Block::new(
|
||||
// if on_cliff && (height * 1271.0).fract() < 0.015 {
|
||||
// treasures[(height * 731.3) as usize %
|
||||
// treasures.len()] } else if (height *
|
||||
// 1271.0).fract() < 0.1 { flowers[(height *
|
||||
// 0.2) as usize % flowers.len()] } else {
|
||||
// grasses[(height * 103.3) as usize % grasses.len()]
|
||||
// },
|
||||
// Rgb::broadcast(0),
|
||||
// ))
|
||||
// } else if (wposf.z as f32) < height + 0.9
|
||||
// && temp > CONFIG.desert_temp
|
||||
// && (marble * 4423.5).fract() < 0.0005
|
||||
// && false
|
||||
// {
|
||||
// let large_cacti = [
|
||||
// BlockKind::LargeCactus,
|
||||
// BlockKind::MedFlatCactus,
|
||||
// BlockKind::Welwitch,
|
||||
// ];
|
||||
|
||||
// let small_cacti = [
|
||||
// BlockKind::BarrelCactus,
|
||||
// BlockKind::RoundCactus,
|
||||
// BlockKind::ShortCactus,
|
||||
// BlockKind::ShortFlatCactus,
|
||||
// BlockKind::DeadBush,
|
||||
// ];
|
||||
|
||||
// Some(Block::new(
|
||||
// if (height * 1271.0).fract() < 0.5 {
|
||||
// large_cacti[(height * 0.2) as usize %
|
||||
// large_cacti.len()] } else {
|
||||
// small_cacti[(height * 0.3) as usize %
|
||||
// small_cacti.len()] },
|
||||
// Rgb::broadcast(0),
|
||||
// ))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -360,7 +293,7 @@ impl<'a> BlockGen<'a> {
|
||||
let field2 = RandomField::new(world.seed + 2);
|
||||
|
||||
Some(Block::new(
|
||||
BlockKind::Rock,
|
||||
BlockKind::WeakRock,
|
||||
stone_col.map2(
|
||||
Rgb::new(
|
||||
field0.get(wpos) as u8 % 16,
|
||||
@ -510,7 +443,7 @@ impl StructureInfo {
|
||||
.reduce_max()
|
||||
{
|
||||
Some(Block::new(
|
||||
BlockKind::Dense,
|
||||
BlockKind::Rock,
|
||||
index.colors.block.pyramid.into(),
|
||||
))
|
||||
} else {
|
||||
@ -558,11 +491,11 @@ pub fn block_from_structure(
|
||||
StructureBlock::None => None,
|
||||
StructureBlock::Hollow => Some(Block::empty()),
|
||||
StructureBlock::Grass => Some(Block::new(
|
||||
BlockKind::Normal,
|
||||
BlockKind::Grass,
|
||||
sample.surface_color.map(|e| (e * 255.0) as u8),
|
||||
)),
|
||||
StructureBlock::Normal(color) => {
|
||||
Some(Block::new(BlockKind::Normal, color)).filter(|block| !block.is_empty())
|
||||
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())),
|
||||
@ -573,23 +506,23 @@ pub fn block_from_structure(
|
||||
)),
|
||||
// None of these BlockKinds has an orientation, so we just use zero for the other color
|
||||
// bits.
|
||||
StructureBlock::Liana => Some(Block::new(BlockKind::Liana, Rgb::zero())),
|
||||
StructureBlock::Liana => Some(Block::empty().with_sprite(SpriteKind::Liana)),
|
||||
StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 24 == 0 {
|
||||
Block::new(BlockKind::Beehive, Rgb::zero())
|
||||
Block::empty().with_sprite(SpriteKind::Beehive)
|
||||
} else if field.get(pos + structure_pos + 1) % 3 == 0 {
|
||||
Block::new(BlockKind::Apple, Rgb::zero())
|
||||
Block::empty().with_sprite(SpriteKind::Apple)
|
||||
} else {
|
||||
Block::empty()
|
||||
}),
|
||||
StructureBlock::Coconut => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
||||
Block::empty()
|
||||
} else {
|
||||
Block::new(BlockKind::Coconut, Rgb::zero())
|
||||
Block::empty().with_sprite(SpriteKind::Coconut)
|
||||
}),
|
||||
StructureBlock::Chest => Some(if structure_seed % 10 < 7 {
|
||||
Block::empty()
|
||||
} else {
|
||||
Block::new(BlockKind::Chest, Rgb::zero())
|
||||
Block::empty().with_sprite(SpriteKind::Chest)
|
||||
}),
|
||||
// We interpolate all these BlockKinds as needed.
|
||||
StructureBlock::TemperateLeaves
|
||||
|
@ -12,7 +12,7 @@ use common::{
|
||||
comp,
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
lottery::Lottery,
|
||||
terrain::{Block, BlockKind},
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol},
|
||||
};
|
||||
use noise::NoiseFn;
|
||||
@ -98,14 +98,14 @@ pub fn apply_paths_to<'a>(
|
||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
||||
if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 {
|
||||
Block::new(
|
||||
BlockKind::Normal,
|
||||
BlockKind::Rock,
|
||||
noisy_color(index.colors.layer.bridge.into(), 8),
|
||||
)
|
||||
} else {
|
||||
let path_color = path.surface_color(
|
||||
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
||||
);
|
||||
Block::new(BlockKind::Normal, noisy_color(path_color, 8))
|
||||
Block::new(BlockKind::Earth, noisy_color(path_color, 8))
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -184,7 +184,7 @@ pub fn apply_caves_to<'a>(
|
||||
for z in cave_roof - stalagtites..cave_roof {
|
||||
let _ = vol.set(
|
||||
Vec3::new(offs.x, offs.y, z),
|
||||
Block::new(BlockKind::Rock, index.colors.layer.stalagtite.into()),
|
||||
Block::new(BlockKind::WeakRock, index.colors.layer.stalagtite.into()),
|
||||
);
|
||||
}
|
||||
|
||||
@ -195,12 +195,11 @@ pub fn apply_caves_to<'a>(
|
||||
if RandomField::new(index.seed).chance(wpos2d.into(), 0.001 * difficulty.powf(1.5))
|
||||
&& cave_base < surface_z as i32 - 25
|
||||
{
|
||||
let kind = *Lottery::<BlockKind>::load_expect("common.cave_scatter")
|
||||
let kind = *Lottery::<SpriteKind>::load_expect("common.cave_scatter")
|
||||
.choose_seeded(RandomField::new(index.seed + 1).get(wpos2d.into()));
|
||||
let _ = vol.set(
|
||||
Vec3::new(offs.x, offs.y, cave_base),
|
||||
Block::new(kind, Rgb::zero()),
|
||||
);
|
||||
let _ = vol.map(Vec3::new(offs.x, offs.y, cave_base), |block| {
|
||||
block.with_sprite(kind)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, IndexRef, CONFIG};
|
||||
use common::{
|
||||
terrain::{Block, BlockKind},
|
||||
terrain::{Block, SpriteKind},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
||||
};
|
||||
use noise::NoiseFn;
|
||||
@ -18,7 +18,7 @@ pub fn apply_scatter_to<'a>(
|
||||
index: IndexRef,
|
||||
chunk: &SimChunk,
|
||||
) {
|
||||
use BlockKind::*;
|
||||
use SpriteKind::*;
|
||||
#[allow(clippy::type_complexity)]
|
||||
// TODO: Add back all sprites we had before
|
||||
let scatter: &[(
|
||||
@ -282,6 +282,8 @@ pub fn apply_scatter_to<'a>(
|
||||
Some((128.0, 0.5)),
|
||||
)
|
||||
}),
|
||||
// Underwater chests
|
||||
(Chest, true, |c, col| (MUSH_FACT * 0.25, None)),
|
||||
];
|
||||
|
||||
for y in 0..vol.size_xy().y as i32 {
|
||||
@ -299,10 +301,10 @@ pub fn apply_scatter_to<'a>(
|
||||
|
||||
let underwater = col_sample.water_level > col_sample.alt;
|
||||
|
||||
let bk = scatter
|
||||
let kind = scatter
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, (bk, is_underwater, f))| {
|
||||
.find_map(|(i, (kind, is_underwater, f))| {
|
||||
let (density, patch) = f(chunk, col_sample);
|
||||
let is_patch = patch
|
||||
.map(|(wavelen, threshold)| {
|
||||
@ -324,13 +326,13 @@ pub fn apply_scatter_to<'a>(
|
||||
.chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|
||||
&& underwater == *is_underwater
|
||||
{
|
||||
Some(*bk)
|
||||
Some(*kind)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(bk) = bk {
|
||||
if let Some(kind) = kind {
|
||||
let alt = col_sample.alt as i32;
|
||||
|
||||
// Find the intersection between ground and air, if there is one near the
|
||||
@ -349,10 +351,9 @@ pub fn apply_scatter_to<'a>(
|
||||
})
|
||||
})
|
||||
{
|
||||
let _ = vol.set(
|
||||
Vec3::new(offs.x, offs.y, alt + solid_end),
|
||||
Block::new(bk, Rgb::broadcast(0)),
|
||||
);
|
||||
let _ = vol.map(Vec3::new(offs.x, offs.y, alt + solid_end), |block| {
|
||||
block.with_sprite(kind)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ impl World {
|
||||
|
||||
let air = Block::empty();
|
||||
let stone = Block::new(
|
||||
BlockKind::Dense,
|
||||
BlockKind::Rock,
|
||||
zcache_grid
|
||||
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
|
||||
.and_then(|zcache| zcache.as_ref())
|
||||
|
@ -208,7 +208,7 @@ impl Castle {
|
||||
let _ = vol.set(
|
||||
pos,
|
||||
Block::new(
|
||||
BlockKind::Normal,
|
||||
BlockKind::Earth,
|
||||
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
||||
),
|
||||
);
|
||||
|
@ -14,7 +14,7 @@ use common::{
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
lottery::Lottery,
|
||||
store::{Id, Store},
|
||||
terrain::{Block, BlockKind, Structure, TerrainChunkSize},
|
||||
terrain::{Block, BlockKind, SpriteKind, Structure, TerrainChunkSize},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||
};
|
||||
use core::{f32, hash::BuildHasherDefault};
|
||||
@ -564,13 +564,7 @@ impl Floor {
|
||||
let empty = BlockMask::new(Block::empty(), 1);
|
||||
|
||||
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
||||
let stone = BlockMask::new(
|
||||
Block::new(
|
||||
BlockKind::Normal,
|
||||
/* Rgb::new(150, 150, 175) */ colors.stone.into(),
|
||||
),
|
||||
5,
|
||||
);
|
||||
let stone = BlockMask::new(Block::new(BlockKind::Rock, colors.stone.into()), 5);
|
||||
|
||||
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
||||
stone
|
||||
@ -599,15 +593,14 @@ impl Floor {
|
||||
|
||||
let floor_sprite = if RandomField::new(7331).chance(Vec3::from(pos), 0.00005) {
|
||||
BlockMask::new(
|
||||
Block::new(
|
||||
Block::empty().with_sprite(
|
||||
match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 20 {
|
||||
0 => BlockKind::Apple,
|
||||
1 => BlockKind::VeloriteFrag,
|
||||
2 => BlockKind::Velorite,
|
||||
3..=8 => BlockKind::Mushroom,
|
||||
_ => BlockKind::ShortGrass,
|
||||
0 => SpriteKind::Apple,
|
||||
1 => SpriteKind::VeloriteFrag,
|
||||
2 => SpriteKind::Velorite,
|
||||
3..=8 => SpriteKind::Mushroom,
|
||||
_ => SpriteKind::ShortGrass,
|
||||
},
|
||||
Rgb::white(),
|
||||
),
|
||||
1,
|
||||
)
|
||||
@ -616,7 +609,7 @@ impl Floor {
|
||||
{
|
||||
let room = &self.rooms[*room];
|
||||
if RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density * 0.5) {
|
||||
BlockMask::new(Block::new(BlockKind::Chest, Rgb::white()), 1)
|
||||
BlockMask::new(Block::empty().with_sprite(SpriteKind::Chest), 1)
|
||||
} else {
|
||||
empty
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
make_case_elim,
|
||||
terrain::{Block, BlockKind},
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
vol::Vox,
|
||||
};
|
||||
use rand::prelude::*;
|
||||
@ -145,7 +145,7 @@ pub struct Attr {
|
||||
pub mansard: i32,
|
||||
pub pillar: Pillar,
|
||||
pub levels: i32,
|
||||
pub window: BlockKind,
|
||||
pub window: SpriteKind,
|
||||
}
|
||||
|
||||
impl Attr {
|
||||
@ -169,10 +169,10 @@ impl Attr {
|
||||
},
|
||||
levels: rng.gen_range(1, 3),
|
||||
window: match rng.gen_range(0, 4) {
|
||||
0 => BlockKind::Window1,
|
||||
1 => BlockKind::Window2,
|
||||
2 => BlockKind::Window3,
|
||||
_ => BlockKind::Window4,
|
||||
0 => SpriteKind::Window1,
|
||||
1 => SpriteKind::Window2,
|
||||
2 => SpriteKind::Window3,
|
||||
_ => SpriteKind::Window4,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -267,24 +267,13 @@ impl Archetype for House {
|
||||
|
||||
let profile = Vec2::new(bound_offset.x, z);
|
||||
|
||||
let make_meta = |ori| {
|
||||
Rgb::new(
|
||||
match ori {
|
||||
Ori::East => 0,
|
||||
Ori::North => 2,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
)
|
||||
};
|
||||
|
||||
let make_block = |(r, g, b)| {
|
||||
let nz = self
|
||||
.noise
|
||||
.get(Vec3::new(center_offset.x, center_offset.y, z * 8));
|
||||
BlockMask::new(
|
||||
Block::new(
|
||||
BlockKind::Normal,
|
||||
BlockKind::Misc,
|
||||
// TODO: Clarify exactly how this affects the color.
|
||||
Rgb::new(r, g, b)
|
||||
.map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)),
|
||||
@ -307,10 +296,16 @@ impl Archetype for House {
|
||||
let empty = BlockMask::nothing();
|
||||
let internal = BlockMask::new(Block::empty(), internal_layer);
|
||||
let end_window = BlockMask::new(
|
||||
Block::new(attr.window, make_meta(ori.flip())),
|
||||
Block::empty().with_sprite(attr.window).with_ori(match ori {
|
||||
Ori::East => 2,
|
||||
Ori::North => 0,
|
||||
}),
|
||||
structural_layer,
|
||||
);
|
||||
let fire = BlockMask::new(Block::new(BlockKind::Ember, Rgb::white()), foundation_layer);
|
||||
let fire = BlockMask::new(
|
||||
Block::empty().with_sprite(SpriteKind::Ember),
|
||||
foundation_layer,
|
||||
);
|
||||
|
||||
let storey_height = 6;
|
||||
let storey = ((z - 1) / storey_height).min(attr.levels - 1);
|
||||
@ -441,12 +436,14 @@ impl Archetype for House {
|
||||
// Doors on first floor only
|
||||
if profile.y == foundation_height + 1 {
|
||||
BlockMask::new(
|
||||
Block::new(
|
||||
BlockKind::Door,
|
||||
if bound_offset.x == (width - 1) / 2 {
|
||||
make_meta(ori.flip())
|
||||
Block::empty().with_sprite(SpriteKind::Door).with_ori(
|
||||
match ori {
|
||||
Ori::East => 2,
|
||||
Ori::North => 0,
|
||||
} + if bound_offset.x == (width - 1) / 2 {
|
||||
0
|
||||
} else {
|
||||
make_meta(ori.flip()) + Rgb::new(4, 0, 0)
|
||||
4
|
||||
},
|
||||
),
|
||||
structural_layer,
|
||||
@ -542,26 +539,26 @@ impl Archetype for House {
|
||||
z + 100,
|
||||
)) % 11
|
||||
{
|
||||
0 => BlockKind::Planter,
|
||||
1 => BlockKind::ChairSingle,
|
||||
2 => BlockKind::ChairDouble,
|
||||
3 => BlockKind::CoatRack,
|
||||
0 => SpriteKind::Planter,
|
||||
1 => SpriteKind::ChairSingle,
|
||||
2 => SpriteKind::ChairDouble,
|
||||
3 => SpriteKind::CoatRack,
|
||||
4 => {
|
||||
if rng.gen_range(0, 8) == 0 {
|
||||
BlockKind::Chest
|
||||
SpriteKind::Chest
|
||||
} else {
|
||||
BlockKind::Crate
|
||||
SpriteKind::Crate
|
||||
}
|
||||
},
|
||||
6 => BlockKind::DrawerMedium,
|
||||
7 => BlockKind::DrawerSmall,
|
||||
8 => BlockKind::TableSide,
|
||||
9 => BlockKind::WardrobeSingle,
|
||||
_ => BlockKind::Pot,
|
||||
6 => SpriteKind::DrawerMedium,
|
||||
7 => SpriteKind::DrawerSmall,
|
||||
8 => SpriteKind::TableSide,
|
||||
9 => SpriteKind::WardrobeSingle,
|
||||
_ => SpriteKind::Pot,
|
||||
};
|
||||
|
||||
return Some(BlockMask::new(
|
||||
Block::new(furniture, Rgb::new(edge_ori, 0, 0)),
|
||||
Block::empty().with_sprite(furniture).with_ori(edge_ori),
|
||||
internal_layer,
|
||||
));
|
||||
} else {
|
||||
@ -584,13 +581,15 @@ impl Archetype for House {
|
||||
.get(Vec3::new(center_offset.x, center_offset.y, z + 100))
|
||||
% 4
|
||||
{
|
||||
0 => BlockKind::HangingSign,
|
||||
1 | 2 | 3 => BlockKind::HangingBasket,
|
||||
_ => BlockKind::DungeonWallDecor,
|
||||
0 => SpriteKind::HangingSign,
|
||||
1 | 2 | 3 => SpriteKind::HangingBasket,
|
||||
_ => SpriteKind::DungeonWallDecor,
|
||||
};
|
||||
|
||||
Some(BlockMask::new(
|
||||
Block::new(ornament, Rgb::new((edge_ori + 4) % 8, 0, 0)),
|
||||
Block::empty()
|
||||
.with_sprite(ornament)
|
||||
.with_ori((edge_ori + 4) % 8),
|
||||
internal_layer,
|
||||
))
|
||||
} else {
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
make_case_elim,
|
||||
terrain::{Block, BlockKind},
|
||||
terrain::{Block, BlockKind, SpriteKind},
|
||||
vol::Vox,
|
||||
};
|
||||
use rand::prelude::*;
|
||||
@ -134,23 +134,8 @@ impl Archetype for Keep {
|
||||
let important_layer = normal_layer + 1;
|
||||
let internal_layer = important_layer + 1;
|
||||
|
||||
let make_meta = |ori| {
|
||||
Rgb::new(
|
||||
match ori {
|
||||
Ori::East => 0,
|
||||
Ori::North => 2,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
)
|
||||
};
|
||||
|
||||
let make_block = |r, g, b| {
|
||||
BlockMask::new(
|
||||
Block::new(BlockKind::Normal, Rgb::new(r, g, b)),
|
||||
normal_layer,
|
||||
)
|
||||
};
|
||||
let make_block =
|
||||
|r, g, b| BlockMask::new(Block::new(BlockKind::Rock, Rgb::new(r, g, b)), normal_layer);
|
||||
|
||||
let brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
|
||||
let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24;
|
||||
@ -165,7 +150,12 @@ impl Archetype for Keep {
|
||||
stone_color.2 + brick_tex,
|
||||
);
|
||||
let window = BlockMask::new(
|
||||
Block::new(BlockKind::Window1, make_meta(ori.flip())),
|
||||
Block::empty()
|
||||
.with_sprite(SpriteKind::Window1)
|
||||
.with_ori(match ori {
|
||||
Ori::East => 2,
|
||||
Ori::North => 0,
|
||||
}),
|
||||
normal_layer,
|
||||
);
|
||||
let floor = make_block(
|
||||
@ -182,7 +172,7 @@ impl Archetype for Keep {
|
||||
let empty = BlockMask::nothing();
|
||||
|
||||
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
||||
let stone = BlockMask::new(Block::new(BlockKind::Normal, dungeon_stone.into()), 5);
|
||||
let stone = BlockMask::new(Block::new(BlockKind::Rock, dungeon_stone.into()), 5);
|
||||
|
||||
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
||||
stone
|
||||
|
@ -19,7 +19,7 @@ use common::{
|
||||
path::Path,
|
||||
spiral::Spiral2d,
|
||||
store::{Id, Store},
|
||||
terrain::{Block, BlockKind, TerrainChunkSize},
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||
};
|
||||
use fxhash::FxHasher64;
|
||||
@ -611,7 +611,7 @@ impl Settlement {
|
||||
}
|
||||
|
||||
{
|
||||
let mut surface_block = None;
|
||||
let mut surface_sprite = None;
|
||||
|
||||
let roll =
|
||||
|seed, n| self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n;
|
||||
@ -637,8 +637,7 @@ impl Settlement {
|
||||
if (col_sample.path.map(|(dist, _, _, _)| dist > 6.0 && dist < 7.0).unwrap_or(false) && is_lamp) //roll(0, 50) == 0)
|
||||
|| (roll(0, 2000) == 0 && col_sample.path.map(|(dist, _, _, _)| dist > 20.0).unwrap_or(true))
|
||||
{
|
||||
surface_block =
|
||||
Some(Block::new(BlockKind::StreetLamp, Rgb::white()));
|
||||
surface_sprite = Some(SpriteKind::StreetLamp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -676,41 +675,38 @@ impl Settlement {
|
||||
|
||||
if in_furrow {
|
||||
if roll(0, 5) == 0 {
|
||||
surface_block = match crop {
|
||||
Crop::Corn => Some(BlockKind::Corn),
|
||||
surface_sprite = match crop {
|
||||
Crop::Corn => Some(SpriteKind::Corn),
|
||||
Crop::Wheat if roll(1, 2) == 0 => {
|
||||
Some(BlockKind::WheatYellow)
|
||||
Some(SpriteKind::WheatYellow)
|
||||
},
|
||||
Crop::Wheat => Some(BlockKind::WheatGreen),
|
||||
Crop::Wheat => Some(SpriteKind::WheatGreen),
|
||||
Crop::Cabbage if roll(2, 2) == 0 => {
|
||||
Some(BlockKind::Cabbage)
|
||||
Some(SpriteKind::Cabbage)
|
||||
},
|
||||
Crop::Pumpkin if roll(3, 2) == 0 => {
|
||||
Some(BlockKind::Pumpkin)
|
||||
Some(SpriteKind::Pumpkin)
|
||||
},
|
||||
Crop::Flax if roll(4, 2) == 0 => Some(BlockKind::Flax),
|
||||
Crop::Carrot if roll(5, 2) == 0 => Some(BlockKind::Carrot),
|
||||
Crop::Tomato if roll(6, 2) == 0 => Some(BlockKind::Tomato),
|
||||
Crop::Radish if roll(7, 2) == 0 => Some(BlockKind::Radish),
|
||||
Crop::Turnip if roll(8, 2) == 0 => Some(BlockKind::Turnip),
|
||||
Crop::Sunflower => Some(BlockKind::Sunflower),
|
||||
_ => None,
|
||||
Crop::Flax if roll(4, 2) == 0 => Some(SpriteKind::Flax),
|
||||
Crop::Carrot if roll(5, 2) == 0 => Some(SpriteKind::Carrot),
|
||||
Crop::Tomato if roll(6, 2) == 0 => Some(SpriteKind::Tomato),
|
||||
Crop::Radish if roll(7, 2) == 0 => Some(SpriteKind::Radish),
|
||||
Crop::Turnip if roll(8, 2) == 0 => Some(SpriteKind::Turnip),
|
||||
Crop::Sunflower => Some(SpriteKind::Sunflower),
|
||||
_ => surface_sprite,
|
||||
}
|
||||
.or_else(|| {
|
||||
if roll(9, 400) == 0 {
|
||||
Some(BlockKind::Scarecrow)
|
||||
Some(SpriteKind::Scarecrow)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.map(|kind| Block::new(kind, Rgb::white()));
|
||||
});
|
||||
}
|
||||
} else if roll(0, 20) == 0 {
|
||||
surface_block =
|
||||
Some(Block::new(BlockKind::ShortGrass, Rgb::white()));
|
||||
surface_sprite = Some(SpriteKind::ShortGrass);
|
||||
} else if roll(1, 30) == 0 {
|
||||
surface_block =
|
||||
Some(Block::new(BlockKind::MediumGrass, Rgb::white()));
|
||||
surface_sprite = Some(SpriteKind::MediumGrass);
|
||||
}
|
||||
|
||||
Some(if in_furrow { dirt } else { mound })
|
||||
@ -732,12 +728,20 @@ impl Settlement {
|
||||
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.kind() == BlockKind::Air {
|
||||
if block.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
if let (0, Some(block)) = (z, surface_block) {
|
||||
let _ = vol.set(pos, block);
|
||||
if let (0, Some(sprite)) = (z, surface_sprite) {
|
||||
let _ = vol.set(
|
||||
pos,
|
||||
if block.is_fluid() {
|
||||
block
|
||||
} else {
|
||||
Block::empty()
|
||||
}
|
||||
.with_sprite(sprite),
|
||||
);
|
||||
} else if z >= 0 {
|
||||
if block.kind() != BlockKind::Water {
|
||||
let _ = vol.set(pos, Block::empty());
|
||||
@ -745,7 +749,7 @@ impl Settlement {
|
||||
} else {
|
||||
let _ = vol.set(
|
||||
pos,
|
||||
Block::new(BlockKind::Normal, noisy_color(color, 4)),
|
||||
Block::new(BlockKind::Earth, noisy_color(color, 4)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -773,7 +777,7 @@ impl Settlement {
|
||||
if dist / WayKind::Wall.width() < ((1.0 - z as f32 / 12.0) * 2.0).min(1.0) {
|
||||
let _ = vol.set(
|
||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
||||
Block::new(BlockKind::Normal, color),
|
||||
Block::new(BlockKind::Wood, color),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -784,7 +788,7 @@ impl Settlement {
|
||||
for z in -2..16 {
|
||||
let _ = vol.set(
|
||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
||||
Block::new(BlockKind::Normal, colors.tower_color.into()),
|
||||
Block::new(BlockKind::Rock, colors.tower_color.into()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user