stevenarella/src/world/block/mod.rs

265 lines
7.8 KiB
Rust

use std::fmt::{Display, Formatter, Error};
pub use self::Block::*;
macro_rules! consume_token { ($i:tt) => (0) }
macro_rules! offsets {
($first:ident, $($other:ident),*) => (
#[allow(non_upper_case_globals)]
pub const $first: usize = 0;
offsets!(prev($first), $($other),*);
);
(prev($prev:ident), $first:ident, $($other:ident),*) => (
#[allow(non_upper_case_globals)]
pub const $first: usize = $prev + internal_sizes::$prev;
offsets!(prev($first), $($other),*);
);
(prev($prev:ident), $first:ident) => (
#[allow(non_upper_case_globals)]
pub const $first: usize = $prev + internal_sizes::$prev;
)
}
macro_rules! define_blocks {
(
$(
$name:ident {
props {
$(
$fname:ident : $ftype:ty = [$($val:expr),+],
)*
},
data $datafunc:expr,
material $mat:expr,
}
)+
) => (
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Block {
$(
$name {
$(
$fname : $ftype,
)*
},
)+
}
mod internal_ids {
create_ids!(usize, $($name),+);
}
mod internal_sizes {
$(
#[allow(non_upper_case_globals)]
pub const $name : usize = $(($(1 + consume_token!($val) + )+ 0) * )* 1;
)+
}
mod internal_offsets {
use super::internal_sizes;
offsets!($($name),+);
}
mod internal_offset_max {
use super::internal_sizes;
use super::internal_offsets;
$(
#[allow(non_upper_case_globals)]
pub const $name: usize = internal_offsets::$name + internal_sizes::$name - 1;
)+
}
impl Block {
#[allow(unused_variables, unused_mut, unused_assignments)]
pub fn get_steven_id(&self) -> usize {
match *self {
$(
Block::$name {
$($fname,)*
} => {
let mut offset = internal_offsets::$name;
let mut mul = 1;
$(
offset += [$($val),+].into_iter().position(|v| *v == $fname).unwrap() * mul;
mul *= $(1 + consume_token!($val) + )+ 0;
)*
offset
},
)+
}
}
#[allow(unused_variables, unused_assignments)]
pub fn by_steven_id(id: usize) -> Block {
match id {
$(
mut data @ internal_offsets::$name ... internal_offset_max::$name=> {
data -= internal_offsets::$name;
$(
let vals = [$($val),+];
let $fname = vals[data % vals.len()];
data /= vals.len();
)*
Block::$name {
$(
$fname: $fname,
)*
}
},
)*
_ => Block::Missing {}
}
}
pub fn get_vanilla_id(&self) -> Option<usize> {
match *self {
$(
Block::$name {
$($fname,)*
} => {
($datafunc).map(|v| v + (internal_ids::$name << 4))
}
)+
}
}
pub fn by_vanilla_id(id: usize) -> Block {
VANILLA_ID_MAP.get(id).and_then(|v| *v).unwrap_or(Block::Missing{})
}
pub fn get_material(&self) -> Material {
match *self {
$(
Block::$name {
$($fname,)*
} => {
$mat
}
)+
}
}
}
lazy_static! {
static ref VANILLA_ID_MAP: Vec<Option<Block>> = {
let mut blocks = vec![];
for i in 0 .. internal_offsets::Missing {
let block = Block::by_steven_id(i);
if let Some(id) = block.get_vanilla_id() {
if blocks.len() <= id {
blocks.resize(id + 1, None);
}
blocks[id] = Some(block);
}
}
blocks
};
}
);
}
pub struct Material {
pub renderable: bool,
// TEMP
pub texture: &'static str,
}
define_blocks! {
Air {
props {},
data { Some(0) },
material Material {
renderable: false,
texture: "none",
},
}
Stone {
props {
variant: StoneVariant = [
StoneVariant::Normal,
StoneVariant::Granite, StoneVariant::SmoothGranite,
StoneVariant::Diorite, StoneVariant::SmoothDiorite,
StoneVariant::Andesite, StoneVariant::SmoothAndesite
],
},
data { Some(variant.data()) },
material Material {
renderable: true,
texture: match variant {
StoneVariant::Normal => "stone",
StoneVariant::Granite => "stone_granite",
StoneVariant::SmoothGranite => "stone_granite_smooth",
StoneVariant::Diorite => "stone_diorite",
StoneVariant::SmoothDiorite => "stone_diorite_smooth",
StoneVariant::Andesite => "stone_andesite",
StoneVariant::SmoothAndesite => "stone_andesite_smooth",
},
},
}
Grass {
props {
},
data { Some(0) },
material Material {
renderable: true,
texture: "grass_path_top",
},
}
Dirt {
props {
},
data { Some(0) },
material Material {
renderable: true,
texture: "dirt",
},
}
Missing {
props {},
data { None::<usize> },
material Material {
renderable: true,
texture: "missing_texture",
},
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum StoneVariant {
Normal,
Granite,
SmoothGranite,
Diorite,
SmoothDiorite,
Andesite,
SmoothAndesite,
}
impl Display for StoneVariant {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}", match *self {
StoneVariant::Normal => "stone",
StoneVariant::Granite => "granite",
StoneVariant::SmoothGranite => "smooth_granite",
StoneVariant::Diorite => "diorite",
StoneVariant::SmoothDiorite => "smooth_diorite",
StoneVariant::Andesite => "andesite",
StoneVariant::SmoothAndesite => "smooth_andesite",
})
}
}
impl StoneVariant {
fn data(&self) -> usize {
match *self {
StoneVariant::Normal => 0,
StoneVariant::Granite => 1,
StoneVariant::SmoothGranite => 2,
StoneVariant::Diorite => 3,
StoneVariant::SmoothDiorite => 4,
StoneVariant::Andesite => 5,
StoneVariant::SmoothAndesite => 6,
}
}
}