use crate::common::{
Aabb, BiomeInfo, BlockInfo, BlockPhysics, Collision, DigMaterial, EffectInfo, EnchantmentInfo,
EntityInfo, EntityKind, Fluid, FluidKind, FoodInfo, ItemInfo, NeighbourShape, PhysicsFlags,
ShapeDependency, lookup_id, lookup_name,
};
use crate::generated::v1_8 as g;
pub const BLOCK_COUNT: usize = g::blocks::BLOCKS.len();
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum MaterialKind {
Air,
Water,
Lava,
Ice,
Other,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Material {
pub kind: MaterialKind,
pub blocks_movement: bool,
pub solid: bool,
pub fluid: bool,
pub opaque: bool,
pub replaceable: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct VanillaProps {
pub full_cube: bool,
pub normal_block: bool,
pub full_block: bool,
pub render_as_normal_block: bool,
pub has_collision: bool,
pub material: Material,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Selection {
pub bounds: Aabb,
pub dependency: ShapeDependency,
pub multi_pass_ray: bool,
pub can_collide: bool,
pub can_collide_liquid: bool,
pub has_collision_box: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Block(pub(crate) u8);
impl Block {
#[must_use]
pub const fn from_id(id: u8) -> Option<Block> {
if (id as usize) < BLOCK_COUNT {
Some(Block(id))
} else {
None
}
}
#[must_use]
pub fn from_name(name: &str) -> Option<Block> {
lookup_name(&g::blocks::BLOCK_NAMES, name).map(Block)
}
#[must_use]
pub const fn id(self) -> u8 {
self.0
}
#[must_use]
pub fn info(self) -> &'static BlockInfo {
&g::blocks::BLOCKS[self.0 as usize]
}
#[must_use]
pub fn name(self) -> &'static str {
self.info().name
}
#[must_use]
pub fn physics(self) -> &'static BlockPhysics {
&g::blocks::BLOCKS[self.0 as usize].physics
}
#[must_use]
pub fn vanilla(self) -> &'static VanillaProps {
&g::blocks::VANILLA_PROPS[self.0 as usize]
}
#[must_use]
pub fn material(self) -> Material {
g::blocks::VANILLA_PROPS[self.0 as usize].material
}
#[must_use]
pub fn full_cube(self) -> bool {
g::blocks::VANILLA_PROPS[self.0 as usize].full_cube
}
#[must_use]
pub fn shape_dependency(self) -> ShapeDependency {
g::blocks::BLOCK_SHAPE_DEPENDENCY[self.0 as usize]
}
#[must_use]
pub const fn with_meta(self, meta: u8) -> BlockState {
BlockState(((self.0 as u16) << 4) | (meta & 15) as u16)
}
#[must_use]
pub const fn default_state(self) -> BlockState {
self.with_meta(0)
}
pub fn all() -> impl ExactSizeIterator<Item = Block> {
(0..BLOCK_COUNT as u8).map(Block)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct BlockState(u16);
impl BlockState {
pub const AIR: BlockState = BlockState(0);
#[must_use]
pub const fn from_raw(raw: u16) -> Option<BlockState> {
if ((raw >> 4) as usize) < BLOCK_COUNT {
Some(BlockState(raw))
} else {
None
}
}
#[must_use]
pub fn from_raw_vanilla(raw: u16) -> BlockState {
match BlockState::from_raw(raw) {
Some(s) if s.is_registered() => s,
_ => BlockState::AIR,
}
}
#[must_use]
pub fn is_registered(self) -> bool {
g::blocks::REGISTERED_METAS[(self.0 >> 4) as usize] & (1 << (self.0 & 15)) != 0
}
#[must_use]
pub const fn new(block: Block, meta: u8) -> BlockState {
block.with_meta(meta)
}
#[must_use]
pub const fn raw(self) -> u16 {
self.0
}
#[must_use]
pub const fn block(self) -> Block {
Block((self.0 >> 4) as u8)
}
#[must_use]
pub const fn meta(self) -> u8 {
(self.0 & 15) as u8
}
#[must_use]
pub const fn is_air(self) -> bool {
self.0 >> 4 == 0
}
#[must_use]
pub fn collision(self) -> Collision {
Collision {
boxes: g::blocks::SHAPES[g::blocks::STATE_SHAPE[self.0 as usize] as usize],
dependency: g::blocks::BLOCK_SHAPE_DEPENDENCY[(self.0 >> 4) as usize],
world_coords: g::blocks::WORLD_COORDS
[g::blocks::STATE_WORLD_COORDS[self.0 as usize] as usize],
}
}
#[must_use]
pub fn selection(self) -> Selection {
let f = g::blocks::STATE_SELECTION_FLAGS[self.0 as usize];
let dependency = g::blocks::BLOCK_SHAPE_DEPENDENCY[(self.0 >> 4) as usize];
Selection {
bounds: g::blocks::SELECTION_BOUNDS
[g::blocks::STATE_SELECTION[self.0 as usize] as usize],
dependency,
multi_pass_ray: dependency == ShapeDependency::Neighbours(NeighbourShape::Stairs),
can_collide: f & 1 != 0,
can_collide_liquid: f & 2 != 0,
has_collision_box: f & 4 != 0,
}
}
#[must_use]
pub fn shape(self) -> &'static [Aabb] {
g::blocks::SHAPES[g::blocks::STATE_SHAPE[self.0 as usize] as usize]
}
#[must_use]
pub fn fluid(self) -> Option<Fluid> {
let flags = self.block().physics().flags;
let kind = if flags.contains(PhysicsFlags::WATER) {
FluidKind::Water
} else if flags.contains(PhysicsFlags::LAVA) {
FluidKind::Lava
} else {
return None;
};
Some(Fluid {
kind,
level: self.meta(),
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Recipe {
Shaped {
width: u8,
height: u8,
inputs: &'static [Option<(u16, u16)>],
output: (u16, u8, u16),
},
Shapeless {
inputs: &'static [(u16, u16)],
output: (u16, u8, u16),
},
Special(SpecialRecipe),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SpecialRecipe {
ArmorDye,
Firework,
BannerDuplicate,
BookCloning,
MapCloning,
MapUpscale,
Repairing,
BannerPattern,
}
pub const RECIPE_ANY_DATA: u16 = 32767;
#[must_use]
pub fn recipes() -> &'static [Recipe] {
&g::recipes::RECIPES
}
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn slime_dimensions(n: i32) -> crate::common::EntityDimensions {
let s = 0.510_000_05_f32 * n as f32;
crate::common::EntityDimensions {
width: s,
height: s,
eye_height: 0.625 * s,
}
}
crate::macros::common_api!();