use yog_data::Ordered;
use yog_data::Schema;
use yog_data::with::{Opt, RefTo};
pub use yog_registry::{ArmorMaterial, SoundGroup, ToolMaterial};
yog_data::kind! {
AdvancementKind = yog_ids::kind::ADVANCEMENT,
LadderKind = yog_ids::kind::LADDER,
LevelKind = yog_ids::kind::LEVEL,
SoundGroupKind = yog_ids::kind::SOUND_GROUP,
ToolMaterialKind = yog_ids::kind::TOOL_MATERIAL,
ArmorMaterialKind = yog_ids::kind::ARMOR_MATERIAL,
}
#[derive(Schema, Debug, Clone)]
#[data(tag = "kind")]
pub enum Def {
Ladder { id: String },
Level {
id: String,
#[data(with = "RefTo<LadderKind>")]
ladder: String,
#[data(with = "Opt<RefTo<LevelKind>>")]
above: Option<String>,
},
SoundGroup(SoundGroup),
RecipeKind(RecipeKindDef),
ToolMaterial(ToolMaterialDef),
ArmorMaterial(ArmorMaterialDef),
Ore(OreDef),
Dimension(DimensionDef),
Table(TableDef),
Tag(TagDef),
DamageType(DamageTypeDef),
Tree(TreeDef),
LootModifier(yog_registry::LootModifier),
LootTable(LootTableDef),
Attribute(yog_registry::AttributeDef),
Effect(yog_registry::EffectDef),
Advancement(AdvancementDef),
}
#[derive(Schema, Debug, Clone)]
pub struct AdvancementDef {
pub id: String,
#[data(with = "Opt<RefTo<AdvancementKind>>")]
pub parent: Option<String>,
#[data(with = "RefTo<yog_data::kinds::Item>")]
pub icon: String,
pub title: yog_data::Text,
pub description: Option<yog_data::Text>,
#[data(default = "task_frame")]
pub frame: String,
#[data(default)]
pub hidden: bool,
pub background: Option<String>,
pub criteria: Vec<CriterionDef>,
}
fn task_frame() -> String {
yog_ids::advancement::TASK.into()
}
#[derive(Schema, Debug, Clone, Default)]
pub struct CriterionDef {
#[data(default)]
pub granted: bool,
pub has_item: Option<yog_data::ItemIdOrTag>,
pub used_item: Option<yog_data::ItemId>,
}
#[derive(Schema, Debug, Clone)]
pub struct LootTableDef {
pub id: String,
pub add: Vec<yog_registry::LootAdd>,
}
#[derive(Schema, Debug, Clone)]
pub struct TreeDef {
pub id: String,
pub trunk: TreeTrunk,
pub leaves: TreeLeaves,
pub dirt: Option<yog_data::BlockId>,
pub min_clipped_height: Option<u32>,
#[data(default)]
pub ignore_vines: bool,
#[data(default)]
pub force_dirt: bool,
}
#[derive(Schema, Debug, Clone)]
pub struct TreeTrunk {
pub blocks: Vec<WeightedBlock>,
pub shape: String,
pub base_height: u32,
#[data(default)]
pub random_a: u32,
#[data(default)]
pub random_b: u32,
}
#[derive(Schema, Debug, Clone)]
pub struct TreeLeaves {
pub blocks: Vec<WeightedBlock>,
pub shape: String,
pub radius: u32,
#[data(default)]
pub offset: i32,
pub height: Option<u32>,
}
#[derive(Schema, Debug, Clone)]
pub struct WeightedBlock {
pub block: yog_data::BlockId,
#[data(default = "one")]
pub weight: u32,
}
#[derive(Schema, Debug, Clone)]
pub struct TagDef {
pub of: String,
pub id: String,
#[data(default)]
pub values: Vec<yog_data::AnyRefOrTag>,
#[data(default)]
pub optional: Vec<yog_data::AnyRefOrTag>,
}
fn scaled_by_mobs() -> String {
"when_caused_by_living_non_player".into()
}
#[derive(Schema, Debug, Clone)]
pub struct DamageTypeDef {
pub id: String,
#[data(default = "scaled_by_mobs")]
pub scaling: String,
#[data(default)]
pub exhaustion: f32,
pub message_id: Option<String>,
pub effects: Option<String>,
pub death_message_type: Option<String>,
#[data(default)]
pub tags: Vec<String>,
}
#[derive(Schema, Debug, Clone)]
pub struct TableDef {
pub id: String,
pub columns: Ordered<String>,
pub rows: Ordered<yog_data::Value>,
}
#[derive(Schema, Debug, Clone)]
pub struct DimensionDef {
pub id: String,
#[data(default)]
pub dimension_type: DimensionTypeProps,
pub biome: Option<yog_data::Id<yog_data::kinds::Biome>>,
#[data(default = "yes")]
pub structures: bool,
#[data(default = "yes")]
pub features: bool,
#[data(default = "yes")]
pub mob_spawning: bool,
pub terrain: Option<yog_worldgen::Terrain>,
pub generator: Option<String>,
}
fn yes() -> bool {
true
}
#[derive(Schema, Debug, Clone, Default)]
pub struct DimensionTypeProps {
pub min_y: Option<i32>,
pub height: Option<i32>,
pub logical_height: Option<i32>,
pub has_sky_light: Option<bool>,
pub has_ceiling: Option<bool>,
#[data(range = "0..=1")]
pub ambient_light: Option<f32>,
pub coordinate_scale: Option<f64>,
pub ultrawarm: Option<bool>,
pub natural: Option<bool>,
pub beds_explode: Option<bool>,
pub respawn_anchors_explode: Option<bool>,
pub piglin_safe: Option<bool>,
pub effects: Option<String>,
}
#[derive(Schema, Debug, Clone)]
pub struct OreDef {
pub id: String,
pub targets: Vec<OreTarget>,
#[data(range = "1..=64")]
pub size: u32,
#[data(range = "0..=256")]
pub per_chunk: u32,
pub height: OreHeight,
#[data(default, range = "0..=1")]
pub air_exposure: f32,
pub biomes: Option<yog_data::TagId<yog_data::kinds::Biome>>,
}
#[derive(Schema, Debug, Clone)]
pub struct OreTarget {
#[data(with = "OreReplaces")]
pub replaces: String,
pub block: yog_data::BlockId,
}
#[derive(Schema, Debug, Clone)]
pub struct OreHeight {
pub min: i32,
pub max: i32,
#[data(default)]
pub shape: HeightShape,
}
#[derive(Schema, Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HeightShape {
#[default]
Uniform,
Triangle,
}
pub struct OreReplaces;
pub const ORE_STONES: [(&str, &str); 4] = [
("stone", "#minecraft:stone_ore_replaceables"),
("deepslate", "#minecraft:deepslate_ore_replaceables"),
(
"netherrack",
yog_ids::id!(yog_ids::ns::MINECRAFT, "netherrack"),
),
(
"end_stone",
yog_ids::id!(yog_ids::ns::MINECRAFT, "end_stone"),
),
];
impl yog_data::with::With<String> for OreReplaces {
fn shape() -> yog_data::Shape {
yog_data::Shape::RefOrTag(&[yog_ids::kind::BLOCK])
}
fn read(v: &yog_data::ValueRef<'_>, cx: &mut yog_data::Reader) -> Option<String> {
use yog_data::ValueRef;
match v {
ValueRef::Str(s) if !s.starts_with('@') => ORE_STONES.iter().find(|(n, _)| n == s).map(|(_, t)| t.to_string()).or_else(|| {
let names: Vec<&str> = ORE_STONES.iter().map(|(n, _)| *n).collect();
cx.problem(format!("`{s}`: one of {} — or a tag of blocks (`@tag:block:…`) or a block (`@block:…`)", names.join(", ")));
None
}),
ValueRef::Tag { .. } => yog_data::TagId::<yog_data::kinds::Block>::read(v, cx).map(|t| format!("#{}", t.id)),
ValueRef::Str(s) if s.starts_with("@tag:") => yog_data::TagId::<yog_data::kinds::Block>::read(v, cx).map(|t| format!("#{}", t.id)),
_ => yog_data::with::RefTo::<yog_data::kinds::Block>::read(v, cx),
}
}
fn write(f: &String) -> yog_data::Value {
if let Some((n, _)) = ORE_STONES.iter().find(|(_, t)| t == f) {
return yog_data::Value::from(*n);
}
match f.strip_prefix('#') {
Some(t) => yog_data::Value::Tag(yog_data::Ref::new(yog_ids::kind::BLOCK, t)),
None => yog_data::Value::Ref(yog_data::Ref::new(yog_ids::kind::BLOCK, f.clone())),
}
}
}
#[derive(Schema, Debug, Clone)]
pub struct ToolMaterialDef {
pub id: String,
#[data(range = "1..=1000000")]
pub durability: u32,
pub speed: f32,
pub attack_damage: f32,
#[data(range = "0..=1000")]
pub enchantability: u32,
#[data(with = "crate::content::LevelRef")]
pub level: String,
pub repair: yog_data::ItemIdOrTag,
}
#[derive(Schema, Debug, Clone)]
pub struct ArmorMaterialDef {
pub id: String,
#[data(range = "1..=10000")]
pub durability: u32,
pub defense: yog_registry::ArmorDefense,
#[data(range = "0..=1000")]
pub enchantability: u32,
pub equip_sound: String,
#[data(default)]
pub toughness: f32,
#[data(default, range = "0..=1")]
pub knockback_resistance: f32,
pub repair: yog_data::ItemIdOrTag,
pub texture: Option<String>,
}
pub use yog_registry::recipe_kinds::{RecipeKindDef, SlotSpec, ValueType};
fn one() -> u32 {
1
}
#[cfg(test)]
mod tests {
#[test]
fn every_kind_of_definition_is_a_kind_of_content() {
use yog_data::{Schema, Shape};
let Shape::Enum(e) = super::Def::shape() else {
panic!("a definition is an enum by its kind")
};
let kinds = yog_ids::groups()
.find(|(g, _)| *g == "kind")
.expect("yog-ids has kinds")
.1;
for v in &e.variants {
assert!(
kinds.iter().any(|(_, k)| *k == v.name),
"`{}` isn't in yog_ids::kind",
v.name
);
}
}
}