use yog_data::{Atom, Schema};
use crate::{BlockPos, Point};
pub mod entity_flags {
pub const LIVING: u32 = 1;
pub const PLAYER: u32 = 1 << 1;
pub const ANIMAL: u32 = 1 << 2;
pub const MONSTER: u32 = 1 << 3;
pub const ITEM: u32 = 1 << 4;
pub const MOB: u32 = 1 << 5;
pub const ARMOR_STAND: u32 = 1 << 6;
pub const ALIVE: u32 = 1 << 7;
pub const SPECTATOR: u32 = 1 << 8;
pub const PART: u32 = 1 << 9;
pub const VILLAGER: u32 = 1 << 10;
pub const SNEAKING: u32 = 1 << 11;
pub const ON_GROUND: u32 = 1 << 12;
pub const HORIZONTAL_COLLISION: u32 = 1 << 13;
pub const GLIDING: u32 = 1 << 14;
}
#[derive(Debug, Clone, PartialEq, Schema)]
#[data(view)]
pub struct EntityInfo {
pub type_id: Atom,
pub dimension: Atom,
pub pos: Point,
pub eye_height: f64,
pub width: f64,
pub height: f64,
pub flags: u32,
pub name: String,
}
impl EntityInfo {
pub fn has(&self, flag: u32) -> bool {
self.flags & flag != 0
}
pub fn eye_pos(&self) -> Point {
Point {
y: self.pos.y + self.eye_height,
..self.pos
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Schema)]
pub enum Face {
Down,
Up,
North,
South,
West,
East,
}
impl Face {
pub const ALL: [Face; 6] = [
Face::Down,
Face::Up,
Face::North,
Face::South,
Face::West,
Face::East,
];
pub fn from_index(i: u8) -> Option<Self> {
Self::ALL.get(i as usize).copied()
}
pub fn index(self) -> u8 {
self as u8
}
pub fn normal(self) -> (i32, i32, i32) {
match self {
Face::Down => (0, -1, 0),
Face::Up => (0, 1, 0),
Face::North => (0, 0, -1),
Face::South => (0, 0, 1),
Face::West => (-1, 0, 0),
Face::East => (1, 0, 0),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Schema)]
#[data(view)]
pub struct BlockHit {
pub pos: BlockPos,
pub face: Face,
pub hit: Point,
}
#[derive(Debug, Clone, PartialEq, Schema)]
#[data(view)]
pub struct BlockInfo {
pub state: Atom,
pub hardness: f32,
pub replaceable: bool,
pub air: bool,
pub fluid: Option<String>,
pub fluid_source: bool,
pub redstone: u8,
pub in_world: bool,
}
impl BlockInfo {
pub fn block_id(&self) -> &str {
crate::split_state(&self.state).0
}
}
#[derive(Debug, Clone, PartialEq, Schema)]
#[data(view)]
pub struct PlayerInfo {
pub uuid: String,
pub gamemode: Atom,
pub selected_slot: u32,
pub xp_level: i32,
pub xp_total: i32,
pub air: i32,
pub may_fly: bool,
pub flying: bool,
}
impl PlayerInfo {
pub fn is_creative(&self) -> bool {
self.gamemode == "creative"
}
}
#[derive(Debug, Clone, PartialEq, Schema)]
#[data(view)]
pub struct Stack {
pub item: Atom,
pub count: u32,
pub snbt: String,
}
#[derive(Debug, Clone, PartialEq, Schema)]
#[data(view)]
pub struct SlotStack {
pub slot: u32,
pub stack: Stack,
}
#[derive(Debug, Clone, PartialEq, Schema)]
#[data(view)]
pub struct ScreenSlot {
pub stack: Option<Stack>,
pub x: i32,
pub y: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Default, Schema)]
#[data(view)]
pub struct Rotation {
pub yaw: f32,
pub pitch: f32,
}
#[derive(Debug, Clone, PartialEq, Schema)]
#[data(view)]
pub struct PlacedBlock {
pub pos: BlockPos,
pub state: Atom,
}
impl PlacedBlock {
pub fn block_id(&self) -> &str {
crate::split_state(&self.state).0
}
}
#[cfg(test)]
mod tests {
use super::*;
fn round_trip<T: Schema>(v: &T) -> T {
yog_data::read_bytes(&yog_data::binary::encode(&v.write())).unwrap()
}
#[test]
fn the_faces_go_by_the_games_direction_names() {
let names: Vec<String> = Face::ALL
.iter()
.map(|f| f.write().as_str().unwrap_or_default().to_string())
.collect();
assert_eq!(names, yog_ids::direction::ALL_VALUES);
}
#[test]
fn records_round_trip_as_data() {
let e = EntityInfo {
type_id: "minecraft:zombie".into(),
dimension: "minecraft:overworld".into(),
pos: Point {
x: 1.5,
y: 64.0,
z: -2.0,
},
eye_height: 1.74,
width: 0.6,
height: 1.95,
flags: entity_flags::LIVING | entity_flags::MONSTER,
name: "Zom\tbie".into(),
};
assert!(e.has(entity_flags::LIVING) && !e.has(entity_flags::PLAYER));
assert_eq!(e.eye_pos().y, 65.74);
assert_eq!(round_trip(&e), e);
let h = BlockHit {
pos: BlockPos::new(1, 2, 3),
face: Face::East,
hit: Point {
x: 1.0,
y: 2.5,
z: 3.5,
},
};
assert_eq!(round_trip(&h), h);
assert_eq!(h.face.normal(), (1, 0, 0));
}
#[test]
fn faces_are_named_as_the_game_names_them() {
let names: Vec<String> = Face::ALL
.iter()
.map(|f| yog_data::text::to_string(&f.write()))
.collect();
assert_eq!(
names,
[
"\"down\"",
"\"up\"",
"\"north\"",
"\"south\"",
"\"west\"",
"\"east\""
]
);
}
}