Added default sprite attribute state

This commit is contained in:
Joshua Barretto 2024-01-21 19:51:37 +00:00
parent ad8965fdd7
commit 4a89d88e9b
8 changed files with 64 additions and 48 deletions

View File

@ -156,50 +156,44 @@ impl Block {
/* Constructors */ /* Constructors */
// TODO: Rename to `filled`
#[inline] #[inline]
pub const fn new(kind: BlockKind, color: Rgb<u8>) -> Self { pub(super) const fn from_raw(kind: BlockKind, data: [u8; 3]) -> Self { Self { kind, data } }
// TODO: we should probably assert this, overwriting the data fields with a
// colour is bad news
/*
#[cfg(debug_assertions)]
assert!(kind.is_filled());
*/
Self { // TODO: Rename to `filled`, make caller guarantees stronger
kind, #[inline]
// Colours are only valid for non-fluids #[track_caller]
data: if kind.is_filled() { pub const fn new(kind: BlockKind, color: Rgb<u8>) -> Self {
[color.r, color.g, color.b] if kind.is_filled() {
Self::from_raw(kind, [color.r, color.g, color.b])
} else { } else {
[0; 3] // Works because `SpriteKind::Empty` has no attributes
}, let data = (SpriteKind::Empty as u32).to_be_bytes();
Self::from_raw(kind, [data[1], data[2], data[3]])
} }
} }
// Only valid if `block_kind` is unfilled, so this is just a private utility // Only valid if `block_kind` is unfilled, so this is just a private utility
// method // method
#[inline] #[inline]
const fn unfilled(kind: BlockKind, sprite: SpriteKind) -> Self { pub fn unfilled(kind: BlockKind, sprite: SpriteKind) -> Self {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
assert!(!kind.is_filled()); assert!(!kind.is_filled());
let sprite_bytes = (sprite as u32).to_be_bytes(); Self::from_raw(kind, sprite.to_initial_bytes())
Self {
kind,
data: [sprite_bytes[1], sprite_bytes[2], sprite_bytes[3]],
}
} }
#[inline] #[inline]
pub const fn air(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Air, sprite) } pub fn air(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Air, sprite) }
#[inline] #[inline]
pub const fn empty() -> Self { Self::air(SpriteKind::Empty) } pub const fn empty() -> Self {
// Works because `SpriteKind::Empty` has no attributes
let data = (SpriteKind::Empty as u32).to_be_bytes();
Self::from_raw(BlockKind::Air, [data[1], data[2], data[3]])
}
#[inline] #[inline]
pub const fn water(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Water, sprite) } pub fn water(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Water, sprite) }
/* Sprite decoding */ /* Sprite decoding */
@ -258,6 +252,9 @@ impl Block {
} }
} }
#[inline(always)]
pub(super) const fn data(&self) -> [u8; 3] { self.data }
#[inline(always)] #[inline(always)]
pub(super) const fn with_data(mut self, data: [u8; 3]) -> Self { pub(super) const fn with_data(mut self, data: [u8; 3]) -> Self {
self.data = data; self.data = data;
@ -428,12 +425,12 @@ impl Block {
}; };
if self if self
.get_attr::<sprite::LightDisabled>() .get_attr::<sprite::LightEnabled>()
.map_or(false, |l| l.0) .map_or(true, |l| l.0)
{ {
None
} else {
Some(glow_level) Some(glow_level)
} else {
None
} }
} }
@ -636,7 +633,7 @@ impl Block {
/// Apply a light toggle to this block, if possible /// Apply a light toggle to this block, if possible
pub fn with_toggle_light(self, enable: bool) -> Option<Self> { pub fn with_toggle_light(self, enable: bool) -> Option<Self> {
self.with_attr(sprite::LightDisabled(!enable)).ok() self.with_attr(sprite::LightEnabled(enable)).ok()
} }
#[inline] #[inline]
@ -662,7 +659,7 @@ impl Block {
#[must_use] #[must_use]
pub fn into_vacant(self) -> Self { pub fn into_vacant(self) -> Self {
if self.is_fluid() { if self.is_fluid() {
Block::new(self.kind(), Rgb::zero()) Block::unfilled(self.kind(), SpriteKind::Empty)
} else { } else {
// FIXME: Figure out if there's some sensible way to determine what medium to // FIXME: Figure out if there's some sensible way to determine what medium to
// replace a filled block with if it's removed. // replace a filled block with if it's removed.
@ -698,7 +695,7 @@ mod tests {
#[test] #[test]
fn convert_u32() { fn convert_u32() {
for bk in BlockKind::iter() { for bk in BlockKind::iter() {
let block = Block::new(bk, Rgb::new(165, 90, 204)); // Pretty unique bit patterns let block = Block::from_raw(bk, [165, 90, 204]); // Pretty unique bit patterns
if bk.is_filled() { if bk.is_filled() {
assert_eq!(Block::from_u32(block.to_u32()), Some(block)); assert_eq!(Block::from_u32(block.to_u32()), Some(block));
} else { } else {

View File

@ -286,7 +286,7 @@ impl TerrainChunk {
pub fn water(sea_level: i32) -> TerrainChunk { pub fn water(sea_level: i32) -> TerrainChunk {
TerrainChunk::new( TerrainChunk::new(
sea_level, sea_level,
Block::new(BlockKind::Water, Rgb::zero()), Block::water(SpriteKind::Empty),
Block::air(SpriteKind::Empty), Block::air(SpriteKind::Empty),
TerrainChunkMeta::void(), TerrainChunkMeta::void(),
) )

View File

@ -349,7 +349,7 @@ sprites! {
SeaDecorPillar = 0x1E, SeaDecorPillar = 0x1E,
MagicalSeal = 0x1F, MagicalSeal = 0x1F,
}, },
Lamp = 7 has Ori, LightDisabled { Lamp = 7 has Ori, LightEnabled {
// Standalone lights // Standalone lights
Lantern = 0, Lantern = 0,
StreetLamp = 1, StreetLamp = 1,
@ -362,20 +362,28 @@ sprites! {
attributes! { attributes! {
Ori { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Ori(x)| x as u16 }, Ori { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Ori(x)| x as u16 },
Growth { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Growth(x)| x as u16 }, Growth { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Growth(x)| x as u16 },
LightDisabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightDisabled(x)| x as u16 }, LightEnabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightEnabled(x)| x as u16 },
} }
// The orientation of the sprite, 0..16 // The orientation of the sprite, 0..16
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct Ori(pub u8); pub struct Ori(pub u8);
// The growth of the plant, 0..16 // The growth of the plant, 0..16
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Growth(pub u8); pub struct Growth(pub u8);
// Whether a light has been toggled off. impl Default for Growth {
fn default() -> Self { Self(15) }
}
// Whether a light has been toggled on or off.
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct LightDisabled(pub bool); pub struct LightEnabled(pub bool);
impl Default for LightEnabled {
fn default() -> Self { Self(true) }
}
impl SpriteKind { impl SpriteKind {
#[inline] #[inline]

View File

@ -158,6 +158,15 @@ macro_rules! sprites {
},)* },)*
} }
} }
#[inline] pub(super) fn to_initial_bytes(self) -> [u8; 3] {
let sprite_bytes = (*self as u32).to_be_bytes();
let block = Block::from_raw(super::BlockKind::Air, [sprite_bytes[1], sprite_bytes[2], sprite_bytes[3]]);
match self.category() {
$(Category::$category_name => block$($(.with_attr($attr::default()).unwrap())*)?,)*
}
.data()
}
} }
}; };
} }
@ -170,12 +179,14 @@ pub enum AttributeError<E> {
Attribute(E), Attribute(E),
} }
pub trait Attribute: Sized { pub trait Attribute: Default + Sized {
/// The unique index assigned to this attribute, used to index offset arrays /// The unique index assigned to this attribute, used to index offset
/// arrays.
const INDEX: usize; const INDEX: usize;
/// The number of bits required to represent this attribute /// The number of bits required to represent this attribute.
const BITS: u8; const BITS: u8;
type Error; /// The error that might occur when decoding the attribute from bits.
type Error: core::fmt::Debug;
fn from_bits(bits: u16) -> Result<Self, Self::Error>; fn from_bits(bits: u16) -> Result<Self, Self::Error>;
fn into_bits(self) -> u16; fn into_bits(self) -> u16;
} }

View File

@ -294,7 +294,7 @@ pub fn generate_mesh<'a>(
let d = d + 2; let d = d + 2;
let flat = { let flat = {
let mut volume = vol.cached(); let mut volume = vol.cached();
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty); const AIR: Block = Block::empty();
// TODO: Once we can manage it sensibly, consider using something like // TODO: Once we can manage it sensibly, consider using something like
// Option<Block> instead of just assuming air. // Option<Block> instead of just assuming air.
let mut flat = vec![AIR; (w * h * d) as usize]; let mut flat = vec![AIR; (w * h * d) as usize];

View File

@ -142,8 +142,8 @@ impl BlocksOfInterest {
_ => { _ => {
if let Some(sprite) = block.get_sprite() { if let Some(sprite) = block.get_sprite() {
if sprite.category() == sprite::Category::Lamp { if sprite.category() == sprite::Category::Lamp {
if let Ok(sprite::LightDisabled(disabled)) = block.get_attr() { if let Ok(sprite::LightEnabled(enabled)) = block.get_attr() {
interactables.push((pos, Interaction::LightToggle(disabled))); interactables.push((pos, Interaction::LightToggle(!enabled)));
} }
} }

View File

@ -47,7 +47,7 @@ pub struct Colors {
pub vein: (u8, u8, u8), pub vein: (u8, u8, u8),
} }
const EMPTY_AIR: Block = Block::air(SpriteKind::Empty); const EMPTY_AIR: Block = Block::empty();
pub struct PathLocals { pub struct PathLocals {
pub riverless_alt: f32, pub riverless_alt: f32,

View File

@ -167,7 +167,7 @@ impl Archetype for Keep {
make_block(colors.pole.0, colors.pole.1, colors.pole.2).with_priority(important_layer); make_block(colors.pole.0, colors.pole.1, colors.pole.2).with_priority(important_layer);
let flag = let flag =
make_block(flag_color.0, flag_color.1, flag_color.2).with_priority(important_layer); make_block(flag_color.0, flag_color.1, flag_color.2).with_priority(important_layer);
const AIR: Block = Block::air(SpriteKind::Empty); const AIR: Block = Block::empty();
const EMPTY: BlockMask = BlockMask::nothing(); const EMPTY: BlockMask = BlockMask::nothing();
let internal = BlockMask::new(AIR, internal_layer); let internal = BlockMask::new(AIR, internal_layer);