use yog_data::kinds::Item;
use yog_data::with::{Opt, RefTo};
use yog_data::{Reader, Schema, Shape, Value, ValueRef};
use yog_ids::units::{BLOCK_PIXELS, MAX_STACK};
use super::refs::{BlockTag, LevelRef, OneOrMany, SoundRef, ToolTag};
#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq)]
pub enum RenderLayer {
Solid,
Cutout,
CutoutMipped,
Translucent,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BlockShape {
Auto,
Full,
Box([f64; 6]),
}
impl Schema for BlockShape {
fn shape() -> Shape {
Shape::Any
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
match v {
ValueRef::Str(s) if *s == "auto" => Some(BlockShape::Auto),
ValueRef::Str(s) if *s == "full" => Some(BlockShape::Full),
ValueRef::List(_) => {
let b = <[f64; 6]>::read(v, cx)?;
if b.iter()
.any(|n| !(0.0..=f64::from(BLOCK_PIXELS)).contains(n))
{
cx.problem("a box is [x1, y1, z1, x2, y2, z2] in pixels, 0–16");
return None;
}
Some(BlockShape::Box(b))
}
other => {
cx.expected(
"\"auto\" (the model's bounds), \"full\" or [x1, y1, z1, x2, y2, z2] in pixels",
other,
);
None
}
}
}
fn write(&self) -> Value {
match self {
BlockShape::Auto => Value::from("auto"),
BlockShape::Full => Value::from("full"),
BlockShape::Box(b) => b.write(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DropCount {
Exactly(f64),
Between(f64, f64),
}
impl Schema for DropCount {
fn shape() -> Shape {
Shape::Any
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
let n = match v {
ValueRef::List(_) => {
let [lo, hi] = <[f64; 2]>::read(v, cx)?;
(lo, hi, DropCount::Between(lo, hi))
}
_ => {
let n = f64::read(v, cx)?;
(n, n, DropCount::Exactly(n))
}
};
let stack = 0.0..=f64::from(MAX_STACK);
if !stack.contains(&n.0) || !stack.contains(&n.1) || n.0 > n.1 {
cx.problem("a count is 1–64, or [min, max] within 0–64");
return None;
}
Some(n.2)
}
fn write(&self) -> Value {
match *self {
DropCount::Exactly(n) => Value::Float(n),
DropCount::Between(a, b) => Value::List(vec![Value::Float(a), Value::Float(b)]),
}
}
}
#[derive(Schema, Debug, Clone)]
pub struct DropItem {
#[data(with = "Opt<RefTo<Item>>")]
pub item: Option<String>,
pub count: Option<DropCount>,
}
#[derive(Debug, Clone)]
pub enum Drops {
Itself,
Nothing,
Item(DropItem),
}
impl Schema for Drops {
fn shape() -> Shape {
Shape::Any
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
match v {
ValueRef::Str(s) if *s == "self" => Some(Drops::Itself),
ValueRef::Str(s) if *s == "none" => Some(Drops::Nothing),
ValueRef::Map(_) => DropItem::read(v, cx).map(Drops::Item),
other => {
cx.expected(
"\"self\", \"none\" or { item: \"@item:ns:item\", count: [1, 3] }",
other,
);
None
}
}
}
fn write(&self) -> Value {
match self {
Drops::Itself => Value::from("self"),
Drops::Nothing => Value::from("none"),
Drops::Item(d) => d.write(),
}
}
}
#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TriggeredBy {
#[default]
Everything,
Mobs,
}
#[derive(Schema, Debug, Clone, Default)]
pub struct BlockProps {
#[data(range = "-1..=1000000")]
pub hardness: Option<f64>,
#[data(range = "0..=10000000")]
pub resistance: Option<f64>,
#[data(with = "Opt<SoundRef>")]
pub sound: Option<String>,
#[data(range = "0..=15")]
pub light: Option<u8>,
#[data(with = "OneOrMany<ToolTag>")]
pub tool: Vec<BlockTag>,
#[data(with = "Opt<LevelRef>")]
pub tool_level: Option<String>,
#[data(default)]
pub requires_tool: bool,
pub collision: Option<bool>,
#[data(range = "0..=1")]
pub slipperiness: Option<f64>,
pub shape: Option<BlockShape>,
pub render_layer: Option<RenderLayer>,
pub drops: Option<Drops>,
#[data(default)]
pub connect_groups: Vec<String>,
pub inventory: Option<String>,
#[data(default)]
pub redstone: bool,
pub powered: Option<String>,
pub comparator: Option<String>,
#[data(default)]
pub accepts_items: bool,
pub occludes: Option<bool>,
#[data(with = "Opt<RefTo<yog_data::kinds::Block>>")]
pub base: Option<String>,
pub set: Option<String>,
#[data(range = "1..=1000000")]
pub press_ticks: Option<u32>,
pub triggered_by: Option<TriggeredBy>,
}