mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/better-block-format' into 'master'
Improved representation of Block for better performance, more... See merge request veloren/veloren!1395
This commit is contained in:
commit
b3117feea6
@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Improved first person aiming
|
- Improved first person aiming
|
||||||
- Figure meshing no longer blocks the main thread.
|
- Figure meshing no longer blocks the main thread.
|
||||||
- Overhauled persistence layer including no longer storing serialized JSON items in the database
|
- Overhauled persistence layer including no longer storing serialized JSON items in the database
|
||||||
|
- Overhauled representation of blocks to permit fluid and sprite coexistence
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -2593,6 +2593,17 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-derive"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f09b9841adb6b5e1f89ef7087ea636e0fd94b2851f887c1e3eb5d5f8228fab3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.18",
|
||||||
|
"quote 1.0.7",
|
||||||
|
"syn 1.0.33",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.43"
|
version = "0.1.43"
|
||||||
@ -4639,6 +4650,8 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"notify",
|
"notify",
|
||||||
|
"num-derive",
|
||||||
|
"num-traits",
|
||||||
"parking_lot 0.9.0",
|
"parking_lot 0.9.0",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
@ -1,13 +1,5 @@
|
|||||||
#![enable(unwrap_newtypes)]
|
#![enable(unwrap_newtypes)]
|
||||||
(
|
(
|
||||||
// Non-sprites
|
|
||||||
Air: None,
|
|
||||||
Normal: None,
|
|
||||||
Dense: None,
|
|
||||||
Rock: None,
|
|
||||||
Leaves: None,
|
|
||||||
Water: None,
|
|
||||||
|
|
||||||
// Windows
|
// Windows
|
||||||
Window1: Some((
|
Window1: Some((
|
||||||
variations: [
|
variations: [
|
||||||
|
@ -34,6 +34,8 @@ sum_type = "0.2.0"
|
|||||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60cd18c34c73097640162bfe" }
|
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60cd18c34c73097640162bfe" }
|
||||||
slab = "0.4.2"
|
slab = "0.4.2"
|
||||||
enum-iterator = "0.6"
|
enum-iterator = "0.6"
|
||||||
|
num-traits = "0.2"
|
||||||
|
num-derive = "0.3"
|
||||||
|
|
||||||
# Tracy
|
# Tracy
|
||||||
tracy-client = { version = "0.8.0", optional = true }
|
tracy-client = { version = "0.8.0", optional = true }
|
||||||
|
@ -16,7 +16,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||||||
// Setup: Create chunk and fill it (dense) for z in [140, 220).
|
// Setup: Create chunk and fill it (dense) for z in [140, 220).
|
||||||
let mut chunk = TerrainChunk::new(
|
let mut chunk = TerrainChunk::new(
|
||||||
MIN_Z,
|
MIN_Z,
|
||||||
Block::new(BlockKind::Dense, Default::default()),
|
Block::new(BlockKind::Rock, Rgb::zero()),
|
||||||
Block::empty(),
|
Block::empty(),
|
||||||
TerrainChunkMeta::void(),
|
TerrainChunkMeta::void(),
|
||||||
);
|
);
|
||||||
@ -29,7 +29,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
chunk
|
chunk
|
||||||
.set(pos, Block::new(BlockKind::Dense, Default::default()))
|
.set(pos, Block::new(BlockKind::Rock, Rgb::zero()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
|||||||
MAX_Z,
|
MAX_Z,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
let _ = chunk.set(pos, Block::new(BlockKind::Dense, Default::default()));
|
let _ = chunk.set(pos, Block::new(BlockKind::Rock, Rgb::zero()));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
@ -59,6 +59,7 @@ pub enum ChatCommand {
|
|||||||
Lantern,
|
Lantern,
|
||||||
Light,
|
Light,
|
||||||
MakeBlock,
|
MakeBlock,
|
||||||
|
MakeSprite,
|
||||||
Motd,
|
Motd,
|
||||||
Object,
|
Object,
|
||||||
Players,
|
Players,
|
||||||
@ -105,6 +106,7 @@ pub static CHAT_COMMANDS: &[ChatCommand] = &[
|
|||||||
ChatCommand::Lantern,
|
ChatCommand::Lantern,
|
||||||
ChatCommand::Light,
|
ChatCommand::Light,
|
||||||
ChatCommand::MakeBlock,
|
ChatCommand::MakeBlock,
|
||||||
|
ChatCommand::MakeSprite,
|
||||||
ChatCommand::Motd,
|
ChatCommand::Motd,
|
||||||
ChatCommand::Object,
|
ChatCommand::Object,
|
||||||
ChatCommand::Players,
|
ChatCommand::Players,
|
||||||
@ -162,6 +164,11 @@ lazy_static! {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
static ref SPRITE_KINDS: Vec<String> = terrain::sprite::SPRITE_KINDS
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
/// List of item specifiers. Useful for tab completing
|
/// List of item specifiers. Useful for tab completing
|
||||||
static ref ITEM_SPECS: Vec<String> = {
|
static ref ITEM_SPECS: Vec<String> = {
|
||||||
let path = assets::ASSETS_PATH.join("common").join("items");
|
let path = assets::ASSETS_PATH.join("common").join("items");
|
||||||
@ -306,7 +313,12 @@ impl ChatCommand {
|
|||||||
),
|
),
|
||||||
ChatCommand::MakeBlock => cmd(
|
ChatCommand::MakeBlock => cmd(
|
||||||
vec![Enum("block", BLOCK_KINDS.clone(), Required)],
|
vec![Enum("block", BLOCK_KINDS.clone(), Required)],
|
||||||
"Make a block",
|
"Make a block at your location",
|
||||||
|
Admin,
|
||||||
|
),
|
||||||
|
ChatCommand::MakeSprite => cmd(
|
||||||
|
vec![Enum("sprite", SPRITE_KINDS.clone(), Required)],
|
||||||
|
"Make a sprite at your location",
|
||||||
Admin,
|
Admin,
|
||||||
),
|
),
|
||||||
ChatCommand::Motd => cmd(
|
ChatCommand::Motd => cmd(
|
||||||
@ -422,6 +434,7 @@ impl ChatCommand {
|
|||||||
ChatCommand::Lantern => "lantern",
|
ChatCommand::Lantern => "lantern",
|
||||||
ChatCommand::Light => "light",
|
ChatCommand::Light => "light",
|
||||||
ChatCommand::MakeBlock => "make_block",
|
ChatCommand::MakeBlock => "make_block",
|
||||||
|
ChatCommand::MakeSprite => "make_sprite",
|
||||||
ChatCommand::Motd => "motd",
|
ChatCommand::Motd => "motd",
|
||||||
ChatCommand::Object => "object",
|
ChatCommand::Object => "object",
|
||||||
ChatCommand::Players => "players",
|
ChatCommand::Players => "players",
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
assets::{self, Asset, Error},
|
assets::{self, Asset, Error},
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
lottery::Lottery,
|
lottery::Lottery,
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, SpriteKind},
|
||||||
};
|
};
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
@ -277,23 +277,23 @@ impl Item {
|
|||||||
pub fn try_reclaim_from_block(block: Block) -> Option<Self> {
|
pub fn try_reclaim_from_block(block: Block) -> Option<Self> {
|
||||||
let chosen;
|
let chosen;
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
Some(Item::new_from_asset_expect(match block.kind() {
|
Some(Item::new_from_asset_expect(match block.get_sprite()? {
|
||||||
BlockKind::Apple => "common.items.food.apple",
|
SpriteKind::Apple => "common.items.food.apple",
|
||||||
BlockKind::Mushroom => "common.items.food.mushroom",
|
SpriteKind::Mushroom => "common.items.food.mushroom",
|
||||||
BlockKind::Velorite => "common.items.ore.velorite",
|
SpriteKind::Velorite => "common.items.ore.velorite",
|
||||||
BlockKind::VeloriteFrag => "common.items.ore.veloritefrag",
|
SpriteKind::VeloriteFrag => "common.items.ore.veloritefrag",
|
||||||
BlockKind::BlueFlower => "common.items.flowers.blue",
|
SpriteKind::BlueFlower => "common.items.flowers.blue",
|
||||||
BlockKind::PinkFlower => "common.items.flowers.pink",
|
SpriteKind::PinkFlower => "common.items.flowers.pink",
|
||||||
BlockKind::PurpleFlower => "common.items.flowers.purple",
|
SpriteKind::PurpleFlower => "common.items.flowers.purple",
|
||||||
BlockKind::RedFlower => "common.items.flowers.red",
|
SpriteKind::RedFlower => "common.items.flowers.red",
|
||||||
BlockKind::WhiteFlower => "common.items.flowers.white",
|
SpriteKind::WhiteFlower => "common.items.flowers.white",
|
||||||
BlockKind::YellowFlower => "common.items.flowers.yellow",
|
SpriteKind::YellowFlower => "common.items.flowers.yellow",
|
||||||
BlockKind::Sunflower => "common.items.flowers.sun",
|
SpriteKind::Sunflower => "common.items.flowers.sun",
|
||||||
BlockKind::LongGrass => "common.items.grasses.long",
|
SpriteKind::LongGrass => "common.items.grasses.long",
|
||||||
BlockKind::MediumGrass => "common.items.grasses.medium",
|
SpriteKind::MediumGrass => "common.items.grasses.medium",
|
||||||
BlockKind::ShortGrass => "common.items.grasses.short",
|
SpriteKind::ShortGrass => "common.items.grasses.short",
|
||||||
BlockKind::Coconut => "common.items.food.coconut",
|
SpriteKind::Coconut => "common.items.food.coconut",
|
||||||
BlockKind::Chest => {
|
SpriteKind::Chest => {
|
||||||
chosen = Lottery::<String>::load_expect(match rng.gen_range(0, 7) {
|
chosen = Lottery::<String>::load_expect(match rng.gen_range(0, 7) {
|
||||||
0 => "common.loot_tables.loot_table_weapon_uncommon",
|
0 => "common.loot_tables.loot_table_weapon_uncommon",
|
||||||
1 => "common.loot_tables.loot_table_weapon_common",
|
1 => "common.loot_tables.loot_table_weapon_common",
|
||||||
@ -304,13 +304,13 @@ impl Item {
|
|||||||
});
|
});
|
||||||
chosen.choose()
|
chosen.choose()
|
||||||
},
|
},
|
||||||
BlockKind::Crate => {
|
SpriteKind::Crate => {
|
||||||
chosen = Lottery::<String>::load_expect("common.loot_tables.loot_table_food");
|
chosen = Lottery::<String>::load_expect("common.loot_tables.loot_table_food");
|
||||||
chosen.choose()
|
chosen.choose()
|
||||||
},
|
},
|
||||||
BlockKind::Stones => "common.items.crafting_ing.stones",
|
SpriteKind::Stones => "common.items.crafting_ing.stones",
|
||||||
BlockKind::Twigs => "common.items.crafting_ing.twigs",
|
SpriteKind::Twigs => "common.items.crafting_ing.twigs",
|
||||||
BlockKind::ShinyGem => "common.items.crafting_ing.shiny_gem",
|
SpriteKind::ShinyGem => "common.items.crafting_ing.shiny_gem",
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,7 @@ where
|
|||||||
V: BaseVol<Vox = Block> + ReadVol,
|
V: BaseVol<Vox = Block> + ReadVol,
|
||||||
{
|
{
|
||||||
vol.get(pos - Vec3::new(0, 0, 1))
|
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)
|
.unwrap_or(false)
|
||||||
&& vol
|
&& vol
|
||||||
.get(pos + Vec3::new(0, 0, 0))
|
.get(pos + Vec3::new(0, 0, 0))
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
sys,
|
sys,
|
||||||
terrain::{Block, TerrainChunk, TerrainGrid},
|
terrain::{Block, TerrainChunk, TerrainGrid},
|
||||||
time::DayPeriod,
|
time::DayPeriod,
|
||||||
vol::WriteVol,
|
vol::{ReadVol, WriteVol},
|
||||||
};
|
};
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use rayon::{ThreadPool, ThreadPoolBuilder};
|
use rayon::{ThreadPool, ThreadPoolBuilder};
|
||||||
@ -264,6 +264,11 @@ impl State {
|
|||||||
/// Get a writable reference to this state's terrain.
|
/// Get a writable reference to this state's terrain.
|
||||||
pub fn terrain_mut(&self) -> FetchMut<TerrainGrid> { self.ecs.write_resource() }
|
pub fn terrain_mut(&self) -> FetchMut<TerrainGrid> { self.ecs.write_resource() }
|
||||||
|
|
||||||
|
/// Get a block in this state's terrain.
|
||||||
|
pub fn get_block(&self, pos: Vec3<i32>) -> Option<Block> {
|
||||||
|
self.terrain().get(pos).ok().copied()
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a block in this state's terrain.
|
/// Set a block in this state's terrain.
|
||||||
pub fn set_block(&mut self, pos: Vec3<i32>, block: Block) {
|
pub fn set_block(&mut self, pos: Vec3<i32>, block: Block) {
|
||||||
self.ecs.write_resource::<BlockChange>().set(pos, block);
|
self.ecs.write_resource::<BlockChange>().set(pos, block);
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
span,
|
span,
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
sync::{Uid, UidAllocator},
|
sync::{Uid, UidAllocator},
|
||||||
terrain::{Block, BlockKind, TerrainGrid},
|
terrain::{Block, TerrainGrid},
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
@ -341,7 +341,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
let near_iter = (-hdist..hdist + 1)
|
let near_iter = (-hdist..hdist + 1)
|
||||||
.map(move |i| {
|
.map(move |i| {
|
||||||
(-hdist..hdist + 1).map(move |j| {
|
(-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)
|
..z_max.ceil() as i32 + 1)
|
||||||
.map(move |k| (i, j, k))
|
.map(move |k| (i, j, k))
|
||||||
})
|
})
|
||||||
@ -370,7 +370,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
let block_aabb = Aabb {
|
let block_aabb = Aabb {
|
||||||
min: block_pos.map(|e| e as f32),
|
min: block_pos.map(|e| e as f32),
|
||||||
max: 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) {
|
if player_aabb.collides_with_aabb(block_aabb) {
|
||||||
@ -442,9 +442,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
block_pos,
|
block_pos,
|
||||||
Aabb {
|
Aabb {
|
||||||
min: block_pos.map(|e| e as f32),
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
@ -557,7 +557,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
&terrain,
|
&terrain,
|
||||||
&|block| {
|
&|block| {
|
||||||
block.is_solid()
|
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(),
|
near_iter.clone(),
|
||||||
radius,
|
radius,
|
||||||
@ -571,7 +571,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
)
|
)
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|block| block.is_solid())
|
.filter(|block| block.is_solid())
|
||||||
.map(|block| block.get_height())
|
.map(|block| block.solid_height())
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0);
|
||||||
pos.0.z = (pos.0.z - 0.05).floor() + snap_height;
|
pos.0.z = (pos.0.z - 0.05).floor() + snap_height;
|
||||||
physics_state.on_ground = true;
|
physics_state.on_ground = true;
|
||||||
@ -609,7 +609,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
physics_state.in_fluid = collision_iter(
|
physics_state.in_fluid = collision_iter(
|
||||||
pos.0,
|
pos.0,
|
||||||
&terrain,
|
&terrain,
|
||||||
&|block| block.is_fluid(),
|
&|block| block.is_liquid(),
|
||||||
near_iter.clone(),
|
near_iter.clone(),
|
||||||
radius,
|
radius,
|
||||||
z_min..z_max,
|
z_min..z_max,
|
||||||
@ -619,7 +619,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
},
|
},
|
||||||
Collider::Point => {
|
Collider::Point => {
|
||||||
let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta)
|
let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta)
|
||||||
.until(|vox| !vox.is_air() && !vox.is_fluid())
|
.until(|vox| !vox.is_air() && !vox.is_liquid())
|
||||||
.ignore_error().cast();
|
.ignore_error().cast();
|
||||||
|
|
||||||
pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist;
|
pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist;
|
||||||
@ -655,7 +655,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
physics_state.in_fluid = terrain.get(pos.0.map(|e| e.floor() as i32))
|
physics_state.in_fluid = terrain.get(pos.0.map(|e| e.floor() as i32))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|vox| vox.is_fluid().then_some(1.0));
|
.and_then(|vox| vox.is_liquid().then_some(1.0));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,103 +1,78 @@
|
|||||||
|
use super::SpriteKind;
|
||||||
use crate::{make_case_elim, vol::Vox};
|
use crate::{make_case_elim, vol::Vox};
|
||||||
use enum_iterator::IntoEnumIterator;
|
use enum_iterator::IntoEnumIterator;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use num_derive::FromPrimitive;
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, convert::TryFrom, fmt, ops::Deref};
|
use std::{collections::HashMap, convert::TryFrom, fmt, ops::Deref};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
block_kind,
|
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)]
|
#[repr(u8)]
|
||||||
pub enum BlockKind {
|
pub enum BlockKind {
|
||||||
Air = 0x00,
|
Air = 0x00, // Air counts as a fluid
|
||||||
Normal = 0x01,
|
Water = 0x01,
|
||||||
Dense = 0x02,
|
// 0x02 <= x < 0x10 are reserved for other fluids. These are 2^n aligned to allow bitwise
|
||||||
Rock = 0x03,
|
// checking of common conditions. For example, `is_fluid` is just `block_kind &
|
||||||
Grass = 0x04,
|
// 0x0F == 0` (this is a very common operation used in meshing that could do with
|
||||||
Leaves = 0x05,
|
// being *very* fast).
|
||||||
Water = 0x06,
|
Rock = 0x10,
|
||||||
LargeCactus = 0x07,
|
WeakRock = 0x11, // Explodable
|
||||||
BarrelCactus = 0x08,
|
// 0x12 <= x < 0x20 is reserved for future rocks
|
||||||
RoundCactus = 0x09,
|
Grass = 0x20, // Note: *not* the same as grass sprites
|
||||||
ShortCactus = 0x0A,
|
// 0x21 <= x < 0x30 is reserved for future grasses
|
||||||
MedFlatCactus = 0x0B,
|
Earth = 0x30,
|
||||||
ShortFlatCactus = 0x0C,
|
Sand = 0x31,
|
||||||
BlueFlower = 0x0D,
|
// 0x32 <= x < 0x40 is reserved for future earths/muds/gravels/sands/etc.
|
||||||
PinkFlower = 0x0E,
|
Wood = 0x40,
|
||||||
PurpleFlower = 0x0F,
|
Leaves = 0x41,
|
||||||
RedFlower = 0x10,
|
// 0x42 <= x < 0x50 is reserved for future tree parts
|
||||||
WhiteFlower = 0x11,
|
|
||||||
YellowFlower = 0x12,
|
// Covers all other cases (we sometimes have bizarrely coloured misc blocks, and also we
|
||||||
Sunflower = 0x13,
|
// often want to experiment with new kinds of block without allocating them a
|
||||||
LongGrass = 0x14,
|
// dedicated block kind.
|
||||||
MediumGrass = 0x15,
|
Misc = 0xFE,
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl BlockKind {
|
||||||
|
#[inline]
|
||||||
|
pub const fn is_air(&self) -> bool { matches!(self, BlockKind::Air) }
|
||||||
|
|
||||||
|
/// Determine whether the block kind is a gas or a liquid. This does not
|
||||||
|
/// consider any sprites that may occupy the block (the definition of
|
||||||
|
/// fluid is 'a substance that deforms to fit containers')
|
||||||
|
#[inline]
|
||||||
|
pub const fn is_fluid(&self) -> bool { *self as u8 & 0xF0 == 0x00 }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn is_liquid(&self) -> bool { self.is_fluid() && !self.is_air() }
|
||||||
|
|
||||||
|
/// Determine whether the block is filled (i.e: fully solid). Right now,
|
||||||
|
/// this is the opposite of being a fluid.
|
||||||
|
#[inline]
|
||||||
|
pub const fn is_filled(&self) -> bool { !self.is_fluid() }
|
||||||
|
|
||||||
|
/// Determine whether the block has an RGB color storaged in the attribute
|
||||||
|
/// fields.
|
||||||
|
#[inline]
|
||||||
|
pub const fn has_color(&self) -> bool { self.is_filled() }
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for BlockKind {
|
impl fmt::Display for BlockKind {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) }
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) }
|
||||||
}
|
}
|
||||||
@ -114,408 +89,10 @@ impl<'a> TryFrom<&'a str> for BlockKind {
|
|||||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> { BLOCK_KINDS.get(s).copied().ok_or(()) }
|
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)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[repr(packed)]
|
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
kind: BlockKind,
|
kind: BlockKind,
|
||||||
color: [u8; 3],
|
attr: [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 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Block {
|
impl Deref for Block {
|
||||||
@ -528,11 +105,141 @@ impl Vox for Block {
|
|||||||
fn empty() -> Self {
|
fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: BlockKind::Air,
|
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 const fn air(sprite: SpriteKind) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: BlockKind::Air,
|
||||||
|
attr: [sprite as u8, 0, 0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_color(&self) -> Option<Rgb<u8>> {
|
||||||
|
if self.has_color() {
|
||||||
|
Some(self.attr.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_sprite(&self) -> Option<SpriteKind> {
|
||||||
|
if !self.is_filled() {
|
||||||
|
SpriteKind::from_u8(self.attr[0])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_solid(&self) -> bool {
|
||||||
|
self.get_sprite()
|
||||||
|
.map(|s| s.solid_height().is_some())
|
||||||
|
.unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
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 all is good for
|
||||||
|
// empty fluids.
|
||||||
|
// TODO: Handle the case of terrain sprites we don't want to have explode
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_collectible(&self) -> bool {
|
||||||
|
self.get_sprite()
|
||||||
|
.map(|s| s.is_collectible())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_opaque(&self) -> bool { self.kind().is_filled() }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn solid_height(&self) -> f32 {
|
||||||
|
self.get_sprite()
|
||||||
|
.map(|s| s.solid_height().unwrap_or(0.0))
|
||||||
|
.unwrap_or(1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn kind(&self) -> BlockKind { self.kind }
|
||||||
|
|
||||||
|
/// If this block is a fluid, replace its sprite.
|
||||||
|
#[inline]
|
||||||
|
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, give it a new orientation.
|
||||||
|
#[inline]
|
||||||
|
pub fn with_ori(mut self, ori: u8) -> Option<Self> {
|
||||||
|
if self.get_sprite().map(|s| s.has_ori()).unwrap_or(false) {
|
||||||
|
self.attr[1] = (self.attr[1] & !0b111) | (ori & 0b111);
|
||||||
|
Some(self)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove the terrain sprite or solid aspects of a block
|
||||||
|
#[inline]
|
||||||
|
pub fn into_vacant(self) -> Self {
|
||||||
|
if self.is_fluid() {
|
||||||
|
Block::new(self.kind(), Rgb::zero())
|
||||||
|
} else {
|
||||||
|
Block::empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -2,6 +2,7 @@ pub mod biome;
|
|||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod chonk;
|
pub mod chonk;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
|
pub mod sprite;
|
||||||
pub mod structure;
|
pub mod structure;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
@ -9,6 +10,7 @@ pub use self::{
|
|||||||
biome::BiomeKind,
|
biome::BiomeKind,
|
||||||
block::{Block, BlockKind},
|
block::{Block, BlockKind},
|
||||||
map::MapSizeLg,
|
map::MapSizeLg,
|
||||||
|
sprite::SpriteKind,
|
||||||
structure::Structure,
|
structure::Structure,
|
||||||
};
|
};
|
||||||
use roots::find_roots_cubic;
|
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 {
|
||||||
|
matches!(
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
center: Vec3::zero(),
|
||||||
vol,
|
vol,
|
||||||
empty: StructureBlock::empty(),
|
empty: StructureBlock::empty(),
|
||||||
default_kind: BlockKind::Normal,
|
default_kind: BlockKind::Misc,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
center: Vec3::zero(),
|
center: Vec3::zero(),
|
||||||
vol: Dyna::filled(Vec3::zero(), StructureBlock::empty(), ()),
|
vol: Dyna::filled(Vec3::zero(), StructureBlock::empty(), ()),
|
||||||
empty: 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
|
/// Set the voxel at the provided position in the volume to the provided
|
||||||
/// value.
|
/// value.
|
||||||
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), Self::Error>;
|
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
|
/// A volume (usually rather a reference to a volume) that is convertible into
|
||||||
|
@ -12,9 +12,9 @@ use common::{
|
|||||||
npc::{self, get_npc_name},
|
npc::{self, get_npc_name},
|
||||||
state::TimeOfDay,
|
state::TimeOfDay,
|
||||||
sync::{Uid, WorldSyncExt},
|
sync::{Uid, WorldSyncExt},
|
||||||
terrain::{Block, BlockKind, TerrainChunkSize},
|
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
vol::RectVolSize,
|
vol::{RectVolSize, Vox},
|
||||||
LoadoutBuilder,
|
LoadoutBuilder,
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@ -87,6 +87,7 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler {
|
|||||||
ChatCommand::Lantern => handle_lantern,
|
ChatCommand::Lantern => handle_lantern,
|
||||||
ChatCommand::Light => handle_light,
|
ChatCommand::Light => handle_light,
|
||||||
ChatCommand::MakeBlock => handle_make_block,
|
ChatCommand::MakeBlock => handle_make_block,
|
||||||
|
ChatCommand::MakeSprite => handle_make_sprite,
|
||||||
ChatCommand::Motd => handle_motd,
|
ChatCommand::Motd => handle_motd,
|
||||||
ChatCommand::Object => handle_object,
|
ChatCommand::Object => handle_object,
|
||||||
ChatCommand::Players => handle_players,
|
ChatCommand::Players => handle_players,
|
||||||
@ -217,6 +218,44 @@ fn handle_make_block(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_make_sprite(
|
||||||
|
server: &mut Server,
|
||||||
|
client: EcsEntity,
|
||||||
|
target: EcsEntity,
|
||||||
|
args: String,
|
||||||
|
action: &ChatCommand,
|
||||||
|
) {
|
||||||
|
if let Some(sprite_name) = scan_fmt_some!(&args, &action.arg_fmt(), String) {
|
||||||
|
if let Ok(sk) = SpriteKind::try_from(sprite_name.as_str()) {
|
||||||
|
match server.state.read_component_copied::<comp::Pos>(target) {
|
||||||
|
Some(pos) => {
|
||||||
|
let pos = pos.0.map(|e| e.floor() as i32);
|
||||||
|
let new_block = server
|
||||||
|
.state
|
||||||
|
.get_block(pos)
|
||||||
|
.unwrap_or_else(Block::empty)
|
||||||
|
.with_sprite(sk);
|
||||||
|
server.state.set_block(pos, new_block);
|
||||||
|
},
|
||||||
|
None => server.notify_client(
|
||||||
|
client,
|
||||||
|
ChatType::CommandError.server_msg(String::from("You have no position.")),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
server.notify_client(
|
||||||
|
client,
|
||||||
|
ChatType::CommandError.server_msg(format!("Invalid sprite kind: {}", sprite_name)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
server.notify_client(
|
||||||
|
client,
|
||||||
|
ChatType::CommandError.server_msg(action.help_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_motd(
|
fn handle_motd(
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
client: EcsEntity,
|
client: EcsEntity,
|
||||||
|
@ -14,7 +14,7 @@ use common::{
|
|||||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||||
sys::combat::BLOCK_ANGLE,
|
sys::combat::BLOCK_ANGLE,
|
||||||
terrain::{Block, TerrainGrid},
|
terrain::{Block, TerrainGrid},
|
||||||
vol::{ReadVol, Vox},
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
use comp::item::Reagent;
|
use comp::item::Reagent;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
@ -569,10 +569,10 @@ pub fn handle_explosion(
|
|||||||
let terrain = ecs.read_resource::<TerrainGrid>();
|
let terrain = ecs.read_resource::<TerrainGrid>();
|
||||||
let _ = terrain
|
let _ = terrain
|
||||||
.ray(pos, pos + dir * power)
|
.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| {
|
.for_each(|block: &Block, pos| {
|
||||||
if block.is_explodable() {
|
if block.is_explodable() {
|
||||||
block_change.set(pos, Block::empty());
|
block_change.set(pos, block.into_vacant());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.cast();
|
.cast();
|
||||||
|
@ -8,8 +8,7 @@ use common::{
|
|||||||
msg::ServerMsg,
|
msg::ServerMsg,
|
||||||
recipe::default_recipe_book,
|
recipe::default_recipe_book,
|
||||||
sync::{Uid, WorldSyncExt},
|
sync::{Uid, WorldSyncExt},
|
||||||
terrain::block::Block,
|
vol::ReadVol,
|
||||||
vol::{ReadVol, Vox},
|
|
||||||
};
|
};
|
||||||
use comp::LightEmitter;
|
use comp::LightEmitter;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@ -132,7 +131,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
state.write_component(entity, event);
|
state.write_component(entity, event);
|
||||||
if item_was_added {
|
if item_was_added {
|
||||||
// we made sure earlier the block was not already modified this tick
|
// 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 {
|
} else {
|
||||||
|
@ -97,7 +97,7 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
|||||||
if vol
|
if vol
|
||||||
.get(outer.min + pos)
|
.get(outer.min + pos)
|
||||||
.ok()
|
.ok()
|
||||||
.map_or(false, |b| b.is_air() || b.is_fluid())
|
.map_or(false, |b| b.is_fluid())
|
||||||
{
|
{
|
||||||
*dest = src - 1;
|
*dest = src - 1;
|
||||||
// Can't propagate further
|
// 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
|
// Down is special cased and we know up is a ray
|
||||||
// Special cased ray propagation
|
// Special cased ray propagation
|
||||||
let pos = Vec3::new(pos.x, pos.y, pos.z - 1);
|
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)
|
.get(outer.min + pos)
|
||||||
.ok()
|
.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 {
|
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));
|
prop_que.push_back((pos.x as u8, pos.y as u8, pos.z as u16));
|
||||||
SUNLIGHT
|
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));
|
prop_que.push_back((pos.x as u8, pos.y as u8, pos.z as u16));
|
||||||
SUNLIGHT - 1
|
SUNLIGHT - 1
|
||||||
} else {
|
} else {
|
||||||
@ -268,7 +268,7 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
|
|||||||
opaque_limits = opaque_limits
|
opaque_limits = opaque_limits
|
||||||
.map(|l| l.including(z))
|
.map(|l| l.including(z))
|
||||||
.or_else(|| Some(Limits::from_value(z)));
|
.or_else(|| Some(Limits::from_value(z)));
|
||||||
} else if block.is_fluid() {
|
} else if block.is_liquid() {
|
||||||
fluid_limits = fluid_limits
|
fluid_limits = fluid_limits
|
||||||
.map(|l| l.including(z))
|
.map(|l| l.including(z))
|
||||||
.or_else(|| Some(Limits::from_value(z)));
|
.or_else(|| Some(Limits::from_value(z)));
|
||||||
@ -412,15 +412,15 @@ fn should_draw_greedy(
|
|||||||
let to = flat_get(pos);
|
let to = flat_get(pos);
|
||||||
let from_opaque = from.is_opaque();
|
let from_opaque = from.is_opaque();
|
||||||
if from_opaque == to.is_opaque() {
|
if from_opaque == to.is_opaque() {
|
||||||
// Check the interface of fluid and non-tangible non-fluids (e.g. air).
|
// Check the interface of liquid and non-tangible non-liquid (e.g. air).
|
||||||
let from_fluid = from.is_fluid();
|
let from_liquid = from.is_liquid();
|
||||||
if from_fluid == to.is_fluid() || from.is_tangible() || to.is_tangible() {
|
if from_liquid == to.is_liquid() || from.is_opaque() || to.is_opaque() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
// While fluid is not culled, we still try to keep a consistent orientation as
|
// While liquid is not culled, we still try to keep a consistent orientation as
|
||||||
// we do for land; if going from fluid to non-fluid,
|
// we do for land; if going from liquid to non-liquid,
|
||||||
// forwards-facing; otherwise, backwards-facing.
|
// forwards-facing; otherwise, backwards-facing.
|
||||||
Some((from_fluid, FaceKind::Fluid))
|
Some((from_liquid, FaceKind::Fluid))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If going from transparent to opaque, backward facing; otherwise, forward
|
// If going from transparent to opaque, backward facing; otherwise, forward
|
||||||
@ -428,9 +428,9 @@ fn should_draw_greedy(
|
|||||||
Some((
|
Some((
|
||||||
from_opaque,
|
from_opaque,
|
||||||
FaceKind::Opaque(if from_opaque {
|
FaceKind::Opaque(if from_opaque {
|
||||||
to.is_fluid()
|
to.is_liquid()
|
||||||
} else {
|
} else {
|
||||||
from.is_fluid()
|
from.is_liquid()
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ impl Globals {
|
|||||||
0.0,
|
0.0,
|
||||||
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
|
select_pos: select_pos
|
||||||
.map(|sp| Vec4::from(sp) + Vec4::unit_w())
|
.map(|sp| Vec4::from(sp) + Vec4::unit_w())
|
||||||
.unwrap_or(Vec4::zero())
|
.unwrap_or(Vec4::zero())
|
||||||
|
@ -17,7 +17,7 @@ use common::{
|
|||||||
figure::Segment,
|
figure::Segment,
|
||||||
span,
|
span,
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
terrain::{block, Block, BlockKind, TerrainChunk},
|
terrain::{sprite, Block, SpriteKind, TerrainChunk},
|
||||||
vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, Vox},
|
vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, Vox},
|
||||||
volumes::vol_grid_2d::{VolGrid2d, VolGrid2dError},
|
volumes::vol_grid_2d::{VolGrid2d, VolGrid2dError},
|
||||||
};
|
};
|
||||||
@ -49,7 +49,7 @@ pub struct TerrainChunkData {
|
|||||||
opaque_model: Model<TerrainPipeline>,
|
opaque_model: Model<TerrainPipeline>,
|
||||||
fluid_model: Option<Model<FluidPipeline>>,
|
fluid_model: Option<Model<FluidPipeline>>,
|
||||||
col_lights: guillotiere::AllocId,
|
col_lights: guillotiere::AllocId,
|
||||||
sprite_instances: HashMap<(BlockKind, usize), Instances<SpriteInstance>>,
|
sprite_instances: HashMap<(SpriteKind, usize), Instances<SpriteInstance>>,
|
||||||
locals: Consts<TerrainLocals>,
|
locals: Consts<TerrainLocals>,
|
||||||
pub blocks_of_interest: BlocksOfInterest,
|
pub blocks_of_interest: BlocksOfInterest,
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ struct MeshWorkerResponse {
|
|||||||
opaque_mesh: Mesh<TerrainPipeline>,
|
opaque_mesh: Mesh<TerrainPipeline>,
|
||||||
fluid_mesh: Mesh<FluidPipeline>,
|
fluid_mesh: Mesh<FluidPipeline>,
|
||||||
col_lights_info: ColLightInfo,
|
col_lights_info: ColLightInfo,
|
||||||
sprite_instances: HashMap<(BlockKind, usize), Vec<SpriteInstance>>,
|
sprite_instances: HashMap<(SpriteKind, usize), Vec<SpriteInstance>>,
|
||||||
started_tick: u64,
|
started_tick: u64,
|
||||||
blocks_of_interest: BlocksOfInterest,
|
blocks_of_interest: BlocksOfInterest,
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ struct SpriteModelConfig<Model> {
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
/// Configuration data for a group of sprites (currently associated with a
|
/// Configuration data for a group of sprites (currently associated with a
|
||||||
/// particular BlockKind).
|
/// particular SpriteKind).
|
||||||
struct SpriteConfig<Model> {
|
struct SpriteConfig<Model> {
|
||||||
/// All possible model variations for this sprite.
|
/// All possible model variations for this sprite.
|
||||||
// NOTE: Could make constant per sprite type, but eliminating this indirection and
|
// 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.
|
/// Configuration data for all sprite models.
|
||||||
///
|
///
|
||||||
/// NOTE: Model is an asset path to the appropriate sprite .vox model.
|
/// 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.
|
/// Function executed by worker threads dedicated to chunk meshing.
|
||||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
#[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,
|
max_texture_size: u16,
|
||||||
chunk: Arc<TerrainChunk>,
|
chunk: Arc<TerrainChunk>,
|
||||||
range: Aabb<i32>,
|
range: Aabb<i32>,
|
||||||
sprite_data: &HashMap<(BlockKind, usize), Vec<SpriteData>>,
|
sprite_data: &HashMap<(SpriteKind, usize), Vec<SpriteData>>,
|
||||||
sprite_config: &SpriteSpec,
|
sprite_config: &SpriteSpec,
|
||||||
) -> MeshWorkerResponse {
|
) -> MeshWorkerResponse {
|
||||||
span!(_guard, "mesh_worker");
|
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 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 = 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
|
let seed = wpos.x as u64 * 3
|
||||||
+ wpos.y as u64 * 7
|
+ wpos.y as u64 * 7
|
||||||
+ wpos.x as u64 * wpos.y as u64; // Awful PRNG
|
+ wpos.x as u64 * wpos.y as u64; // Awful PRNG
|
||||||
let ori = (block.get_ori().unwrap_or((seed % 4) as u8 * 2)) & 0b111;
|
let ori = (block.get_ori().unwrap_or((seed % 4) as u8 * 2)) & 0b111;
|
||||||
let variation = seed as usize % cfg.variations.len();
|
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 we called sprite_config_for already.
|
||||||
// NOTE: Safe because 0 ≤ ori < 8
|
// NOTE: Safe because 0 ≤ ori < 8
|
||||||
let sprite_data = &sprite_data[&key][0];
|
let sprite_data = &sprite_data[&key][0];
|
||||||
@ -218,7 +223,7 @@ pub struct Terrain<V: RectRasterableVol> {
|
|||||||
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
mesh_todo: HashMap<Vec2<i32>, ChunkMeshState>,
|
||||||
|
|
||||||
// GPU data
|
// GPU data
|
||||||
sprite_data: Arc<HashMap<(BlockKind, usize), Vec<SpriteData>>>,
|
sprite_data: Arc<HashMap<(SpriteKind, usize), Vec<SpriteData>>>,
|
||||||
sprite_col_lights: Texture<ColLightFmt>,
|
sprite_col_lights: Texture<ColLightFmt>,
|
||||||
col_lights: Texture<ColLightFmt>,
|
col_lights: Texture<ColLightFmt>,
|
||||||
waves: Texture,
|
waves: Texture,
|
||||||
@ -254,7 +259,7 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
let sprite_config_ = &sprite_config;
|
let sprite_config_ = &sprite_config;
|
||||||
// NOTE: Tracks the start vertex of the next model to be meshed.
|
// 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()?)))
|
.filter_map(|kind| Some((kind, kind.elim_case_pure(&sprite_config_).as_ref()?)))
|
||||||
.flat_map(|(kind, sprite_config)| {
|
.flat_map(|(kind, sprite_config)| {
|
||||||
let wind_sway = sprite_config.wind_sway;
|
let wind_sway = sprite_config.wind_sway;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use common::{
|
use common::{
|
||||||
span,
|
span,
|
||||||
terrain::{BlockKind, TerrainChunk},
|
terrain::{BlockKind, SpriteKind, TerrainChunk},
|
||||||
vol::{IntoVolIterator, RectRasterableVol},
|
vol::{IntoVolIterator, RectRasterableVol},
|
||||||
};
|
};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
@ -45,16 +45,18 @@ impl BlocksOfInterest {
|
|||||||
grass.push(pos)
|
grass.push(pos)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BlockKind::Ember => embers.push(pos),
|
_ => match block.get_sprite() {
|
||||||
BlockKind::Beehive => beehives.push(pos),
|
Some(SpriteKind::Ember) => embers.push(pos),
|
||||||
BlockKind::Reed => reeds.push(pos),
|
Some(SpriteKind::Beehive) => beehives.push(pos),
|
||||||
BlockKind::PinkFlower => flowers.push(pos),
|
Some(SpriteKind::Reed) => reeds.push(pos),
|
||||||
BlockKind::PurpleFlower => flowers.push(pos),
|
Some(SpriteKind::PinkFlower) => flowers.push(pos),
|
||||||
BlockKind::RedFlower => flowers.push(pos),
|
Some(SpriteKind::PurpleFlower) => flowers.push(pos),
|
||||||
BlockKind::WhiteFlower => flowers.push(pos),
|
Some(SpriteKind::RedFlower) => flowers.push(pos),
|
||||||
BlockKind::YellowFlower => flowers.push(pos),
|
Some(SpriteKind::WhiteFlower) => flowers.push(pos),
|
||||||
BlockKind::Sunflower => flowers.push(pos),
|
Some(SpriteKind::YellowFlower) => flowers.push(pos),
|
||||||
|
Some(SpriteKind::Sunflower) => flowers.push(pos),
|
||||||
_ => {},
|
_ => {},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -86,7 +86,7 @@ impl SessionState {
|
|||||||
key_state: KeyState::default(),
|
key_state: KeyState::default(),
|
||||||
inputs: comp::ControllerInputs::default(),
|
inputs: comp::ControllerInputs::default(),
|
||||||
hud,
|
hud,
|
||||||
selected_block: Block::new(BlockKind::Normal, Rgb::broadcast(255)),
|
selected_block: Block::new(BlockKind::Misc, Rgb::broadcast(255)),
|
||||||
voxygen_i18n,
|
voxygen_i18n,
|
||||||
walk_forward_dir,
|
walk_forward_dir,
|
||||||
walk_right_dir,
|
walk_right_dir,
|
||||||
@ -1161,7 +1161,7 @@ fn under_cursor(
|
|||||||
|
|
||||||
let cam_ray = terrain
|
let cam_ray = terrain
|
||||||
.ray(cam_pos, cam_pos + cam_dir * 100.0)
|
.ray(cam_pos, cam_pos + cam_dir * 100.0)
|
||||||
.until(|block| block.is_tangible())
|
.until(|block| block.is_filled() || block.is_collectible())
|
||||||
.cast();
|
.cast();
|
||||||
|
|
||||||
let cam_dist = cam_ray.0;
|
let cam_dist = cam_ray.0;
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
use common::{
|
use common::{
|
||||||
terrain::{
|
terrain::{
|
||||||
structure::{self, StructureBlock},
|
structure::{self, StructureBlock},
|
||||||
Block, BlockKind, Structure,
|
Block, BlockKind, SpriteKind, Structure,
|
||||||
},
|
},
|
||||||
vol::{ReadVol, Vox},
|
vol::{ReadVol, Vox},
|
||||||
};
|
};
|
||||||
@ -254,15 +254,19 @@ impl<'a> BlockGen<'a> {
|
|||||||
|
|
||||||
let grass_depth = (1.5 + 2.0 * chaos).min(height - basement_height);
|
let grass_depth = (1.5 + 2.0 * chaos).min(height - basement_height);
|
||||||
let block = if (wposf.z as f32) < height - grass_depth {
|
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(
|
let col = Lerp::lerp(
|
||||||
sub_surface_color,
|
sub_surface_color,
|
||||||
stone_col.map(|e| e as f32 / 255.0),
|
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);
|
.map(|e| (e * 255.0) as u8);
|
||||||
|
|
||||||
// Underground
|
if stone_factor >= 0.5 {
|
||||||
Some(Block::new(BlockKind::Normal, col))
|
Some(Block::new(BlockKind::Rock, col))
|
||||||
|
} else {
|
||||||
|
Some(Block::new(BlockKind::Earth, col))
|
||||||
|
}
|
||||||
} else if (wposf.z as f32) < height {
|
} else if (wposf.z as f32) < height {
|
||||||
let grass_factor = (wposf.z as f32 - (height - grass_depth))
|
let grass_factor = (wposf.z as f32 - (height - grass_depth))
|
||||||
.div(grass_depth)
|
.div(grass_depth)
|
||||||
@ -273,81 +277,10 @@ impl<'a> BlockGen<'a> {
|
|||||||
if grass_factor > 0.7 {
|
if grass_factor > 0.7 {
|
||||||
BlockKind::Grass
|
BlockKind::Grass
|
||||||
} else {
|
} else {
|
||||||
BlockKind::Normal
|
BlockKind::Earth
|
||||||
},
|
},
|
||||||
col.map(|e| (e * 255.0) as u8),
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -360,7 +293,7 @@ impl<'a> BlockGen<'a> {
|
|||||||
let field2 = RandomField::new(world.seed + 2);
|
let field2 = RandomField::new(world.seed + 2);
|
||||||
|
|
||||||
Some(Block::new(
|
Some(Block::new(
|
||||||
BlockKind::Rock,
|
BlockKind::WeakRock,
|
||||||
stone_col.map2(
|
stone_col.map2(
|
||||||
Rgb::new(
|
Rgb::new(
|
||||||
field0.get(wpos) as u8 % 16,
|
field0.get(wpos) as u8 % 16,
|
||||||
@ -510,7 +443,7 @@ impl StructureInfo {
|
|||||||
.reduce_max()
|
.reduce_max()
|
||||||
{
|
{
|
||||||
Some(Block::new(
|
Some(Block::new(
|
||||||
BlockKind::Dense,
|
BlockKind::Rock,
|
||||||
index.colors.block.pyramid.into(),
|
index.colors.block.pyramid.into(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
@ -558,11 +491,11 @@ pub fn block_from_structure(
|
|||||||
StructureBlock::None => None,
|
StructureBlock::None => None,
|
||||||
StructureBlock::Hollow => Some(Block::empty()),
|
StructureBlock::Hollow => Some(Block::empty()),
|
||||||
StructureBlock::Grass => Some(Block::new(
|
StructureBlock::Grass => Some(Block::new(
|
||||||
BlockKind::Normal,
|
BlockKind::Grass,
|
||||||
sample.surface_color.map(|e| (e * 255.0) as u8),
|
sample.surface_color.map(|e| (e * 255.0) as u8),
|
||||||
)),
|
)),
|
||||||
StructureBlock::Normal(color) => {
|
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.
|
// Water / sludge throw away their color bits currently, so we don't set anyway.
|
||||||
StructureBlock::Water => Some(Block::new(BlockKind::Water, Rgb::zero())),
|
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
|
// None of these BlockKinds has an orientation, so we just use zero for the other color
|
||||||
// bits.
|
// bits.
|
||||||
StructureBlock::Liana => Some(Block::new(BlockKind::Liana, Rgb::zero())),
|
StructureBlock::Liana => Some(Block::air(SpriteKind::Liana)),
|
||||||
StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 24 == 0 {
|
StructureBlock::Fruit => Some(if field.get(pos + structure_pos) % 24 == 0 {
|
||||||
Block::new(BlockKind::Beehive, Rgb::zero())
|
Block::air(SpriteKind::Beehive)
|
||||||
} else if field.get(pos + structure_pos + 1) % 3 == 0 {
|
} else if field.get(pos + structure_pos + 1) % 3 == 0 {
|
||||||
Block::new(BlockKind::Apple, Rgb::zero())
|
Block::air(SpriteKind::Apple)
|
||||||
} else {
|
} else {
|
||||||
Block::empty()
|
Block::empty()
|
||||||
}),
|
}),
|
||||||
StructureBlock::Coconut => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
StructureBlock::Coconut => Some(if field.get(pos + structure_pos) % 3 > 0 {
|
||||||
Block::empty()
|
Block::empty()
|
||||||
} else {
|
} else {
|
||||||
Block::new(BlockKind::Coconut, Rgb::zero())
|
Block::air(SpriteKind::Coconut)
|
||||||
}),
|
}),
|
||||||
StructureBlock::Chest => Some(if structure_seed % 10 < 7 {
|
StructureBlock::Chest => Some(if structure_seed % 10 < 7 {
|
||||||
Block::empty()
|
Block::empty()
|
||||||
} else {
|
} else {
|
||||||
Block::new(BlockKind::Chest, Rgb::zero())
|
Block::air(SpriteKind::Chest)
|
||||||
}),
|
}),
|
||||||
// We interpolate all these BlockKinds as needed.
|
// We interpolate all these BlockKinds as needed.
|
||||||
StructureBlock::TemperateLeaves
|
StructureBlock::TemperateLeaves
|
||||||
|
@ -12,7 +12,7 @@ use common::{
|
|||||||
comp,
|
comp,
|
||||||
generation::{ChunkSupplement, EntityInfo},
|
generation::{ChunkSupplement, EntityInfo},
|
||||||
lottery::Lottery,
|
lottery::Lottery,
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind, SpriteKind},
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol},
|
vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol},
|
||||||
};
|
};
|
||||||
use noise::NoiseFn;
|
use noise::NoiseFn;
|
||||||
@ -98,14 +98,14 @@ pub fn apply_paths_to<'a>(
|
|||||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
Vec3::new(offs.x, offs.y, surface_z + z),
|
||||||
if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 {
|
if bridge_offset >= 2.0 && path_dist >= 3.0 || z < inset - 1 {
|
||||||
Block::new(
|
Block::new(
|
||||||
BlockKind::Normal,
|
BlockKind::Rock,
|
||||||
noisy_color(index.colors.layer.bridge.into(), 8),
|
noisy_color(index.colors.layer.bridge.into(), 8),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let path_color = path.surface_color(
|
let path_color = path.surface_color(
|
||||||
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
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 {
|
for z in cave_roof - stalagtites..cave_roof {
|
||||||
let _ = vol.set(
|
let _ = vol.set(
|
||||||
Vec3::new(offs.x, offs.y, z),
|
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))
|
if RandomField::new(index.seed).chance(wpos2d.into(), 0.001 * difficulty.powf(1.5))
|
||||||
&& cave_base < surface_z as i32 - 25
|
&& 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()));
|
.choose_seeded(RandomField::new(index.seed + 1).get(wpos2d.into()));
|
||||||
let _ = vol.set(
|
let _ = vol.map(Vec3::new(offs.x, offs.y, cave_base), |block| {
|
||||||
Vec3::new(offs.x, offs.y, cave_base),
|
block.with_sprite(kind)
|
||||||
Block::new(kind, Rgb::zero()),
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, IndexRef, CONFIG};
|
use crate::{column::ColumnSample, sim::SimChunk, util::RandomField, IndexRef, CONFIG};
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, SpriteKind},
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
||||||
};
|
};
|
||||||
use noise::NoiseFn;
|
use noise::NoiseFn;
|
||||||
@ -18,7 +18,7 @@ pub fn apply_scatter_to<'a>(
|
|||||||
index: IndexRef,
|
index: IndexRef,
|
||||||
chunk: &SimChunk,
|
chunk: &SimChunk,
|
||||||
) {
|
) {
|
||||||
use BlockKind::*;
|
use SpriteKind::*;
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
// TODO: Add back all sprites we had before
|
// TODO: Add back all sprites we had before
|
||||||
let scatter: &[(
|
let scatter: &[(
|
||||||
@ -282,6 +282,8 @@ pub fn apply_scatter_to<'a>(
|
|||||||
Some((128.0, 0.5)),
|
Some((128.0, 0.5)),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
// Underwater chests
|
||||||
|
(Chest, true, |_, _| (MUSH_FACT * 0.1, None)),
|
||||||
];
|
];
|
||||||
|
|
||||||
for y in 0..vol.size_xy().y as i32 {
|
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 underwater = col_sample.water_level > col_sample.alt;
|
||||||
|
|
||||||
let bk = scatter
|
let kind = scatter
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find_map(|(i, (bk, is_underwater, f))| {
|
.find_map(|(i, (kind, is_underwater, f))| {
|
||||||
let (density, patch) = f(chunk, col_sample);
|
let (density, patch) = f(chunk, col_sample);
|
||||||
let is_patch = patch
|
let is_patch = patch
|
||||||
.map(|(wavelen, threshold)| {
|
.map(|(wavelen, threshold)| {
|
||||||
@ -324,13 +326,13 @@ pub fn apply_scatter_to<'a>(
|
|||||||
.chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|
.chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|
||||||
&& underwater == *is_underwater
|
&& underwater == *is_underwater
|
||||||
{
|
{
|
||||||
Some(*bk)
|
Some(*kind)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(bk) = bk {
|
if let Some(kind) = kind {
|
||||||
let alt = col_sample.alt as i32;
|
let alt = col_sample.alt as i32;
|
||||||
|
|
||||||
// Find the intersection between ground and air, if there is one near the
|
// 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(
|
let _ = vol.map(Vec3::new(offs.x, offs.y, alt + solid_end), |block| {
|
||||||
Vec3::new(offs.x, offs.y, alt + solid_end),
|
block.with_sprite(kind)
|
||||||
Block::new(bk, Rgb::broadcast(0)),
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ impl World {
|
|||||||
|
|
||||||
let air = Block::empty();
|
let air = Block::empty();
|
||||||
let stone = Block::new(
|
let stone = Block::new(
|
||||||
BlockKind::Dense,
|
BlockKind::Rock,
|
||||||
zcache_grid
|
zcache_grid
|
||||||
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
|
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
|
||||||
.and_then(|zcache| zcache.as_ref())
|
.and_then(|zcache| zcache.as_ref())
|
||||||
|
@ -208,7 +208,7 @@ impl Castle {
|
|||||||
let _ = vol.set(
|
let _ = vol.set(
|
||||||
pos,
|
pos,
|
||||||
Block::new(
|
Block::new(
|
||||||
BlockKind::Normal,
|
BlockKind::Earth,
|
||||||
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
col_sample.sub_surface_color.map(|e| (e * 255.0) as u8),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -14,7 +14,7 @@ use common::{
|
|||||||
generation::{ChunkSupplement, EntityInfo},
|
generation::{ChunkSupplement, EntityInfo},
|
||||||
lottery::Lottery,
|
lottery::Lottery,
|
||||||
store::{Id, Store},
|
store::{Id, Store},
|
||||||
terrain::{Block, BlockKind, Structure, TerrainChunkSize},
|
terrain::{Block, BlockKind, SpriteKind, Structure, TerrainChunkSize},
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||||
};
|
};
|
||||||
use core::{f32, hash::BuildHasherDefault};
|
use core::{f32, hash::BuildHasherDefault};
|
||||||
@ -564,13 +564,7 @@ impl Floor {
|
|||||||
let empty = BlockMask::new(Block::empty(), 1);
|
let empty = BlockMask::new(Block::empty(), 1);
|
||||||
|
|
||||||
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
||||||
let stone = BlockMask::new(
|
let stone = BlockMask::new(Block::new(BlockKind::Rock, colors.stone.into()), 5);
|
||||||
Block::new(
|
|
||||||
BlockKind::Normal,
|
|
||||||
/* Rgb::new(150, 150, 175) */ colors.stone.into(),
|
|
||||||
),
|
|
||||||
5,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
||||||
stone
|
stone
|
||||||
@ -599,15 +593,14 @@ impl Floor {
|
|||||||
|
|
||||||
let floor_sprite = if RandomField::new(7331).chance(Vec3::from(pos), 0.00005) {
|
let floor_sprite = if RandomField::new(7331).chance(Vec3::from(pos), 0.00005) {
|
||||||
BlockMask::new(
|
BlockMask::new(
|
||||||
Block::new(
|
Block::air(
|
||||||
match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 20 {
|
match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 20 {
|
||||||
0 => BlockKind::Apple,
|
0 => SpriteKind::Apple,
|
||||||
1 => BlockKind::VeloriteFrag,
|
1 => SpriteKind::VeloriteFrag,
|
||||||
2 => BlockKind::Velorite,
|
2 => SpriteKind::Velorite,
|
||||||
3..=8 => BlockKind::Mushroom,
|
3..=8 => SpriteKind::Mushroom,
|
||||||
_ => BlockKind::ShortGrass,
|
_ => SpriteKind::ShortGrass,
|
||||||
},
|
},
|
||||||
Rgb::white(),
|
|
||||||
),
|
),
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
@ -616,7 +609,7 @@ impl Floor {
|
|||||||
{
|
{
|
||||||
let room = &self.rooms[*room];
|
let room = &self.rooms[*room];
|
||||||
if RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density * 0.5) {
|
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::air(SpriteKind::Chest), 1)
|
||||||
} else {
|
} else {
|
||||||
empty
|
empty
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
make_case_elim,
|
make_case_elim,
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind, SpriteKind},
|
||||||
vol::Vox,
|
vol::Vox,
|
||||||
};
|
};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
@ -145,7 +145,7 @@ pub struct Attr {
|
|||||||
pub mansard: i32,
|
pub mansard: i32,
|
||||||
pub pillar: Pillar,
|
pub pillar: Pillar,
|
||||||
pub levels: i32,
|
pub levels: i32,
|
||||||
pub window: BlockKind,
|
pub window: SpriteKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attr {
|
impl Attr {
|
||||||
@ -169,10 +169,10 @@ impl Attr {
|
|||||||
},
|
},
|
||||||
levels: rng.gen_range(1, 3),
|
levels: rng.gen_range(1, 3),
|
||||||
window: match rng.gen_range(0, 4) {
|
window: match rng.gen_range(0, 4) {
|
||||||
0 => BlockKind::Window1,
|
0 => SpriteKind::Window1,
|
||||||
1 => BlockKind::Window2,
|
1 => SpriteKind::Window2,
|
||||||
2 => BlockKind::Window3,
|
2 => SpriteKind::Window3,
|
||||||
_ => BlockKind::Window4,
|
_ => SpriteKind::Window4,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -267,24 +267,13 @@ impl Archetype for House {
|
|||||||
|
|
||||||
let profile = Vec2::new(bound_offset.x, z);
|
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 make_block = |(r, g, b)| {
|
||||||
let nz = self
|
let nz = self
|
||||||
.noise
|
.noise
|
||||||
.get(Vec3::new(center_offset.x, center_offset.y, z * 8));
|
.get(Vec3::new(center_offset.x, center_offset.y, z * 8));
|
||||||
BlockMask::new(
|
BlockMask::new(
|
||||||
Block::new(
|
Block::new(
|
||||||
BlockKind::Normal,
|
BlockKind::Misc,
|
||||||
// TODO: Clarify exactly how this affects the color.
|
// TODO: Clarify exactly how this affects the color.
|
||||||
Rgb::new(r, g, b)
|
Rgb::new(r, g, b)
|
||||||
.map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)),
|
.map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)),
|
||||||
@ -307,10 +296,15 @@ impl Archetype for House {
|
|||||||
let empty = BlockMask::nothing();
|
let empty = BlockMask::nothing();
|
||||||
let internal = BlockMask::new(Block::empty(), internal_layer);
|
let internal = BlockMask::new(Block::empty(), internal_layer);
|
||||||
let end_window = BlockMask::new(
|
let end_window = BlockMask::new(
|
||||||
Block::new(attr.window, make_meta(ori.flip())),
|
Block::air(attr.window)
|
||||||
|
.with_ori(match ori {
|
||||||
|
Ori::East => 2,
|
||||||
|
Ori::North => 0,
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
structural_layer,
|
structural_layer,
|
||||||
);
|
);
|
||||||
let fire = BlockMask::new(Block::new(BlockKind::Ember, Rgb::white()), foundation_layer);
|
let fire = BlockMask::new(Block::air(SpriteKind::Ember), foundation_layer);
|
||||||
|
|
||||||
let storey_height = 6;
|
let storey_height = 6;
|
||||||
let storey = ((z - 1) / storey_height).min(attr.levels - 1);
|
let storey = ((z - 1) / storey_height).min(attr.levels - 1);
|
||||||
@ -441,14 +435,18 @@ impl Archetype for House {
|
|||||||
// Doors on first floor only
|
// Doors on first floor only
|
||||||
if profile.y == foundation_height + 1 {
|
if profile.y == foundation_height + 1 {
|
||||||
BlockMask::new(
|
BlockMask::new(
|
||||||
Block::new(
|
Block::air(SpriteKind::Door)
|
||||||
BlockKind::Door,
|
.with_ori(
|
||||||
if bound_offset.x == (width - 1) / 2 {
|
match ori {
|
||||||
make_meta(ori.flip())
|
Ori::East => 2,
|
||||||
|
Ori::North => 0,
|
||||||
|
} + if bound_offset.x == (width - 1) / 2 {
|
||||||
|
0
|
||||||
} else {
|
} else {
|
||||||
make_meta(ori.flip()) + Rgb::new(4, 0, 0)
|
4
|
||||||
},
|
},
|
||||||
),
|
)
|
||||||
|
.unwrap(),
|
||||||
structural_layer,
|
structural_layer,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -542,26 +540,26 @@ impl Archetype for House {
|
|||||||
z + 100,
|
z + 100,
|
||||||
)) % 11
|
)) % 11
|
||||||
{
|
{
|
||||||
0 => BlockKind::Planter,
|
0 => SpriteKind::Planter,
|
||||||
1 => BlockKind::ChairSingle,
|
1 => SpriteKind::ChairSingle,
|
||||||
2 => BlockKind::ChairDouble,
|
2 => SpriteKind::ChairDouble,
|
||||||
3 => BlockKind::CoatRack,
|
3 => SpriteKind::CoatRack,
|
||||||
4 => {
|
4 => {
|
||||||
if rng.gen_range(0, 8) == 0 {
|
if rng.gen_range(0, 8) == 0 {
|
||||||
BlockKind::Chest
|
SpriteKind::Chest
|
||||||
} else {
|
} else {
|
||||||
BlockKind::Crate
|
SpriteKind::Crate
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
6 => BlockKind::DrawerMedium,
|
6 => SpriteKind::DrawerMedium,
|
||||||
7 => BlockKind::DrawerSmall,
|
7 => SpriteKind::DrawerSmall,
|
||||||
8 => BlockKind::TableSide,
|
8 => SpriteKind::TableSide,
|
||||||
9 => BlockKind::WardrobeSingle,
|
9 => SpriteKind::WardrobeSingle,
|
||||||
_ => BlockKind::Pot,
|
_ => SpriteKind::Pot,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Some(BlockMask::new(
|
return Some(BlockMask::new(
|
||||||
Block::new(furniture, Rgb::new(edge_ori, 0, 0)),
|
Block::air(furniture).with_ori(edge_ori).unwrap(),
|
||||||
internal_layer,
|
internal_layer,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
@ -584,13 +582,13 @@ impl Archetype for House {
|
|||||||
.get(Vec3::new(center_offset.x, center_offset.y, z + 100))
|
.get(Vec3::new(center_offset.x, center_offset.y, z + 100))
|
||||||
% 4
|
% 4
|
||||||
{
|
{
|
||||||
0 => BlockKind::HangingSign,
|
0 => SpriteKind::HangingSign,
|
||||||
1 | 2 | 3 => BlockKind::HangingBasket,
|
1 | 2 | 3 => SpriteKind::HangingBasket,
|
||||||
_ => BlockKind::DungeonWallDecor,
|
_ => SpriteKind::DungeonWallDecor,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(BlockMask::new(
|
Some(BlockMask::new(
|
||||||
Block::new(ornament, Rgb::new((edge_ori + 4) % 8, 0, 0)),
|
Block::air(ornament).with_ori((edge_ori + 4) % 8).unwrap(),
|
||||||
internal_layer,
|
internal_layer,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
make_case_elim,
|
make_case_elim,
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind, SpriteKind},
|
||||||
vol::Vox,
|
vol::Vox,
|
||||||
};
|
};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
@ -134,23 +134,8 @@ impl Archetype for Keep {
|
|||||||
let important_layer = normal_layer + 1;
|
let important_layer = normal_layer + 1;
|
||||||
let internal_layer = important_layer + 1;
|
let internal_layer = important_layer + 1;
|
||||||
|
|
||||||
let make_meta = |ori| {
|
let make_block =
|
||||||
Rgb::new(
|
|r, g, b| BlockMask::new(Block::new(BlockKind::Rock, Rgb::new(r, g, b)), normal_layer);
|
||||||
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 brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
|
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;
|
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,
|
stone_color.2 + brick_tex,
|
||||||
);
|
);
|
||||||
let window = BlockMask::new(
|
let window = BlockMask::new(
|
||||||
Block::new(BlockKind::Window1, make_meta(ori.flip())),
|
Block::air(SpriteKind::Window1)
|
||||||
|
.with_ori(match ori {
|
||||||
|
Ori::East => 2,
|
||||||
|
Ori::North => 0,
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
normal_layer,
|
normal_layer,
|
||||||
);
|
);
|
||||||
let floor = make_block(
|
let floor = make_block(
|
||||||
@ -182,7 +172,7 @@ impl Archetype for Keep {
|
|||||||
let empty = BlockMask::nothing();
|
let empty = BlockMask::nothing();
|
||||||
|
|
||||||
let make_staircase = move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
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) {
|
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
||||||
stone
|
stone
|
||||||
|
@ -19,7 +19,7 @@ use common::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
store::{Id, Store},
|
store::{Id, Store},
|
||||||
terrain::{Block, BlockKind, TerrainChunkSize},
|
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||||
};
|
};
|
||||||
use fxhash::FxHasher64;
|
use fxhash::FxHasher64;
|
||||||
@ -611,7 +611,7 @@ impl Settlement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut surface_block = None;
|
let mut surface_sprite = None;
|
||||||
|
|
||||||
let roll =
|
let roll =
|
||||||
|seed, n| self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n;
|
|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)
|
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))
|
|| (roll(0, 2000) == 0 && col_sample.path.map(|(dist, _, _, _)| dist > 20.0).unwrap_or(true))
|
||||||
{
|
{
|
||||||
surface_block =
|
surface_sprite = Some(SpriteKind::StreetLamp);
|
||||||
Some(Block::new(BlockKind::StreetLamp, Rgb::white()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,41 +675,38 @@ impl Settlement {
|
|||||||
|
|
||||||
if in_furrow {
|
if in_furrow {
|
||||||
if roll(0, 5) == 0 {
|
if roll(0, 5) == 0 {
|
||||||
surface_block = match crop {
|
surface_sprite = match crop {
|
||||||
Crop::Corn => Some(BlockKind::Corn),
|
Crop::Corn => Some(SpriteKind::Corn),
|
||||||
Crop::Wheat if roll(1, 2) == 0 => {
|
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 => {
|
Crop::Cabbage if roll(2, 2) == 0 => {
|
||||||
Some(BlockKind::Cabbage)
|
Some(SpriteKind::Cabbage)
|
||||||
},
|
},
|
||||||
Crop::Pumpkin if roll(3, 2) == 0 => {
|
Crop::Pumpkin if roll(3, 2) == 0 => {
|
||||||
Some(BlockKind::Pumpkin)
|
Some(SpriteKind::Pumpkin)
|
||||||
},
|
},
|
||||||
Crop::Flax if roll(4, 2) == 0 => Some(BlockKind::Flax),
|
Crop::Flax if roll(4, 2) == 0 => Some(SpriteKind::Flax),
|
||||||
Crop::Carrot if roll(5, 2) == 0 => Some(BlockKind::Carrot),
|
Crop::Carrot if roll(5, 2) == 0 => Some(SpriteKind::Carrot),
|
||||||
Crop::Tomato if roll(6, 2) == 0 => Some(BlockKind::Tomato),
|
Crop::Tomato if roll(6, 2) == 0 => Some(SpriteKind::Tomato),
|
||||||
Crop::Radish if roll(7, 2) == 0 => Some(BlockKind::Radish),
|
Crop::Radish if roll(7, 2) == 0 => Some(SpriteKind::Radish),
|
||||||
Crop::Turnip if roll(8, 2) == 0 => Some(BlockKind::Turnip),
|
Crop::Turnip if roll(8, 2) == 0 => Some(SpriteKind::Turnip),
|
||||||
Crop::Sunflower => Some(BlockKind::Sunflower),
|
Crop::Sunflower => Some(SpriteKind::Sunflower),
|
||||||
_ => None,
|
_ => surface_sprite,
|
||||||
}
|
}
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
if roll(9, 400) == 0 {
|
if roll(9, 400) == 0 {
|
||||||
Some(BlockKind::Scarecrow)
|
Some(SpriteKind::Scarecrow)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.map(|kind| Block::new(kind, Rgb::white()));
|
|
||||||
}
|
}
|
||||||
} else if roll(0, 20) == 0 {
|
} else if roll(0, 20) == 0 {
|
||||||
surface_block =
|
surface_sprite = Some(SpriteKind::ShortGrass);
|
||||||
Some(Block::new(BlockKind::ShortGrass, Rgb::white()));
|
|
||||||
} else if roll(1, 30) == 0 {
|
} else if roll(1, 30) == 0 {
|
||||||
surface_block =
|
surface_sprite = Some(SpriteKind::MediumGrass);
|
||||||
Some(Block::new(BlockKind::MediumGrass, Rgb::white()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(if in_furrow { dirt } else { mound })
|
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 pos = Vec3::new(offs.x, offs.y, surface_z + z);
|
||||||
let block = vol.get(pos).ok().copied().unwrap_or_else(Block::empty);
|
let block = vol.get(pos).ok().copied().unwrap_or_else(Block::empty);
|
||||||
|
|
||||||
if block.kind() == BlockKind::Air {
|
if block.is_empty() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (0, Some(block)) = (z, surface_block) {
|
if let (0, Some(sprite)) = (z, surface_sprite) {
|
||||||
let _ = vol.set(pos, block);
|
let _ = vol.set(
|
||||||
|
pos,
|
||||||
|
if block.is_fluid() {
|
||||||
|
block
|
||||||
|
} else {
|
||||||
|
Block::empty()
|
||||||
|
}
|
||||||
|
.with_sprite(sprite),
|
||||||
|
);
|
||||||
} else if z >= 0 {
|
} else if z >= 0 {
|
||||||
if block.kind() != BlockKind::Water {
|
if block.kind() != BlockKind::Water {
|
||||||
let _ = vol.set(pos, Block::empty());
|
let _ = vol.set(pos, Block::empty());
|
||||||
@ -745,7 +749,7 @@ impl Settlement {
|
|||||||
} else {
|
} else {
|
||||||
let _ = vol.set(
|
let _ = vol.set(
|
||||||
pos,
|
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) {
|
if dist / WayKind::Wall.width() < ((1.0 - z as f32 / 12.0) * 2.0).min(1.0) {
|
||||||
let _ = vol.set(
|
let _ = vol.set(
|
||||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
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 {
|
for z in -2..16 {
|
||||||
let _ = vol.set(
|
let _ = vol.set(
|
||||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
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