use yog_data::{Ordered, Reader, Schema, Shape, Value, ValueRef};
#[derive(Debug, Clone, PartialEq)]
pub enum Texture {
Slot(String),
Ref(String),
}
pub const SLOT_MARK: char = '#';
impl Schema for Texture {
fn shape() -> Shape {
Shape::Ref(&[yog_ids::kind::TEXTURE])
}
fn read(v: &ValueRef<'_>, cx: &mut Reader) -> Option<Self> {
match v {
ValueRef::Str(s) if s.starts_with(SLOT_MARK) => {
if s.len() == SLOT_MARK.len_utf8() {
cx.problem("`#` needs a slot name");
return None;
}
Some(Texture::Slot(s[SLOT_MARK.len_utf8()..].to_string()))
}
ValueRef::Ref { kind, id } if *kind == yog_ids::kind::TEXTURE => {
Some(Texture::Ref(yog_ids::qualify(id)))
}
ValueRef::Str(s) if s.starts_with(yog_ids::sep::TYPED) => {
match yog_data::typed::classify(s) {
Ok(Value::Ref(r)) if r.kind == yog_ids::kind::TEXTURE => {
Some(Texture::Ref(yog_ids::qualify(&r.id)))
}
Ok(_) => {
cx.problem(format!("`{s}`: a texture is `#slot` or `@texture:<ns>:<path>` (a .ytexture, or the game's)"));
None
}
Err(e) => {
cx.problem(e);
None
}
}
}
other => {
cx.expected(
"a texture: `#slot` or `@texture:<ns>:<path>` (a .ytexture, or the game's)",
other,
);
None
}
}
}
fn write(&self) -> Value {
match self {
Texture::Slot(s) => Value::Str(format!("{SLOT_MARK}{s}")),
Texture::Ref(id) => Value::Ref(yog_data::Ref::new(yog_ids::kind::TEXTURE, id.clone())),
}
}
}
#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq)]
pub enum Axis {
X,
Y,
Z,
}
#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq)]
pub enum GuiLight {
Front,
Side,
}
#[derive(Schema, Debug, Clone)]
pub struct ElementRotation {
#[data(range = "-16..=32")]
pub origin: Option<[f64; 3]>,
pub axis: Axis,
#[data(range = "-45..=45")]
pub angle: f64,
#[data(default)]
pub rescale: bool,
}
#[derive(Schema, Debug, Clone)]
pub struct Face {
pub texture: String,
#[data(range = "0..=16")]
pub uv: Option<[f64; 4]>,
pub rotation: Option<u32>,
pub cullface: Option<yog_core::Face>,
#[data(range = "-1..=255")]
pub tint: Option<i64>,
}
#[derive(Schema, Debug, Clone, Default)]
pub struct Faces {
pub down: Option<Face>,
pub up: Option<Face>,
pub north: Option<Face>,
pub south: Option<Face>,
pub west: Option<Face>,
pub east: Option<Face>,
}
impl Faces {
pub fn iter(&self) -> impl Iterator<Item = (&'static str, &Face)> {
[
("down", &self.down),
("up", &self.up),
("north", &self.north),
("south", &self.south),
("west", &self.west),
("east", &self.east),
]
.into_iter()
.filter_map(|(d, f)| f.as_ref().map(|f| (d, f)))
}
}
#[derive(Schema, Debug, Clone)]
pub struct Element {
pub name: Option<String>,
#[data(range = "-16..=32")]
pub from: [f64; 3],
#[data(range = "-16..=32")]
pub to: [f64; 3],
pub rotation: Option<ElementRotation>,
pub shade: Option<bool>,
pub faces: Option<Faces>,
}
#[derive(Schema, Debug, Clone)]
pub struct Transform {
#[data(range = "-360..=360")]
pub rotation: Option<[f64; 3]>,
#[data(range = "-80..=80")]
pub translation: Option<[f64; 3]>,
#[data(range = "-4..=4")]
pub scale: Option<[f64; 3]>,
}
#[derive(Schema, Debug, Clone, Default)]
pub struct Display {
pub thirdperson_righthand: Option<Transform>,
pub thirdperson_lefthand: Option<Transform>,
pub firstperson_righthand: Option<Transform>,
pub firstperson_lefthand: Option<Transform>,
pub gui: Option<Transform>,
pub head: Option<Transform>,
pub ground: Option<Transform>,
pub fixed: Option<Transform>,
}
impl Display {
pub fn iter(&self) -> impl Iterator<Item = (&'static str, &Transform)> {
[
("thirdperson_righthand", &self.thirdperson_righthand),
("thirdperson_lefthand", &self.thirdperson_lefthand),
("firstperson_righthand", &self.firstperson_righthand),
("firstperson_lefthand", &self.firstperson_lefthand),
("gui", &self.gui),
("head", &self.head),
("ground", &self.ground),
("fixed", &self.fixed),
]
.into_iter()
.filter_map(|(d, t)| t.as_ref().map(|t| (d, t)))
}
}
#[derive(Schema, Debug, Clone)]
pub struct Model {
pub parent: Option<String>,
#[data(with = "yog_data::with::Opt<yog_data::with::RefTo<yog_data::kinds::Material>>")]
pub material: Option<String>,
#[data(default)]
pub textures: Ordered<Texture>,
pub elements: Option<Vec<Element>>,
pub display: Option<Display>,
pub ambient_occlusion: Option<bool>,
pub gui_light: Option<GuiLight>,
}
#[derive(Schema, Debug, Clone)]
pub struct Rotation {
pub x: Option<u32>,
pub y: Option<u32>,
#[data(range = "1..=1000000")]
pub weight: Option<f64>,
#[data(default)]
pub uvlock: bool,
}
#[derive(Schema, Debug, Clone)]
pub struct Connect {
pub arm: Model,
#[data(default)]
pub uvlock: bool,
}
#[derive(Schema, Debug, Clone)]
pub struct States {
pub rotations: Option<Vec<Rotation>>,
pub connect: Option<Connect>,
pub facing: Option<Facing>,
pub placement: Option<Placement>,
#[data(default)]
pub attach: bool,
#[data(default)]
pub waterlogged: bool,
#[data(default)]
pub uvlock: bool,
#[data(default)]
pub properties: Ordered<Vec<String>>,
#[data(default)]
pub models: Ordered<Model>,
#[data(default)]
pub variants: Ordered<ModelNames>,
}
pub type ModelNames = yog_data::OneOrList<String>;
pub use yog_registry::{Facing, Placement};