use std::collections::BTreeMap; use proc_macro2::{Span, TokenStream}; use pumpkin_util::loot_table::LootNumberProviderTypes; use quote::{ToTokens, quote}; use serde::Deserialize; use syn::LitStr; /// These are required to be defined twice because serde can't deserialize into static context for obvious reasons. #[derive(Deserialize)] pub struct LootTableStruct { r#type: LootTableTypeStruct, random_sequence: Option, pools: Option>, } impl ToTokens for LootTableStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let loot_table_type = self.r#type.to_token_stream(); let random_sequence = if let Some(seq) = &self.random_sequence { quote! { Some(#seq) } } else { quote! { None } }; let pools = if let Some(pools) = &self.pools { let pool_tokens: Vec<_> = pools.iter().map(quote::ToTokens::to_token_stream).collect(); quote! { Some(&[#(#pool_tokens),*]) } } else { quote! { None } }; tokens.extend(quote! { LootTable { r#type: #loot_table_type, random_sequence: #random_sequence, pools: #pools, } }); } } #[derive(Deserialize, Clone, Debug)] pub struct LootPoolStruct { entries: Vec, rolls: LootNumberProviderTypes, // TODO bonus_rolls: f32, conditions: Option>, functions: Option>, } impl ToTokens for LootPoolStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let entries_tokens: Vec<_> = self .entries .iter() .map(quote::ToTokens::to_token_stream) .collect(); let rolls = &self.rolls; let bonus_rolls = &self.bonus_rolls; let conditions_tokens = if let Some(conds) = &self.conditions { let cond_tokens: Vec<_> = conds.iter().map(quote::ToTokens::to_token_stream).collect(); quote! { Some(&[#(#cond_tokens),*]) } } else { quote! { None } }; let functions_tokens = if let Some(fns) = &self.functions { let cond_tokens: Vec<_> = fns.iter().map(quote::ToTokens::to_token_stream).collect(); quote! { Some(&[#(#cond_tokens),*]) } } else { quote! { None } }; tokens.extend(quote! { LootPool { entries: &[#(#entries_tokens),*], rolls: #rolls, bonus_rolls: #bonus_rolls, conditions: #conditions_tokens, functions: #functions_tokens, } }); } } #[derive(Deserialize, Clone, Debug)] pub struct ItemEntryStruct { name: String, } impl ToTokens for ItemEntryStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let name = LitStr::new(&self.name, Span::call_site()); tokens.extend(quote! { ItemEntry { name: #name, } }); } } #[derive(Deserialize, Clone, Debug)] pub struct AlternativeEntryStruct { children: Vec, } impl ToTokens for AlternativeEntryStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let children = self.children.iter().map(quote::ToTokens::to_token_stream); tokens.extend(quote! { AlternativeEntry { children: &[#(#children),*], } }); } } #[derive(Deserialize, Clone, Debug)] #[serde(tag = "type")] pub enum LootPoolEntryTypesStruct { #[serde(rename = "minecraft:empty")] Empty, #[serde(rename = "minecraft:item")] Item(ItemEntryStruct), #[serde(rename = "minecraft:loot_table")] LootTable, #[serde(rename = "minecraft:dynamic")] Dynamic, #[serde(rename = "minecraft:tag")] Tag, #[serde(rename = "minecraft:alternatives")] Alternatives(AlternativeEntryStruct), #[serde(rename = "minecraft:sequence")] Sequence, #[serde(rename = "minecraft:group")] Group, } impl ToTokens for LootPoolEntryTypesStruct { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Empty => { tokens.extend(quote! { LootPoolEntryTypes::Empty }); } Self::Item(item) => { tokens.extend(quote! { LootPoolEntryTypes::Item(#item) }); } Self::LootTable => { tokens.extend(quote! { LootPoolEntryTypes::LootTable }); } Self::Dynamic => { tokens.extend(quote! { LootPoolEntryTypes::Dynamic }); } Self::Tag => { tokens.extend(quote! { LootPoolEntryTypes::Tag }); } Self::Alternatives(alt) => { tokens.extend(quote! { LootPoolEntryTypes::Alternatives(#alt) }); } Self::Sequence => { tokens.extend(quote! { LootPoolEntryTypes::Sequence }); } Self::Group => { tokens.extend(quote! { LootPoolEntryTypes::Group }); } } } } #[derive(Deserialize, Clone, Debug)] #[serde(tag = "condition")] pub enum LootConditionStruct { #[serde(rename = "minecraft:inverted")] Inverted, #[serde(rename = "minecraft:any_of")] AnyOf, #[serde(rename = "minecraft:all_of")] AllOf, #[serde(rename = "minecraft:random_chance")] RandomChance, #[serde(rename = "minecraft:random_chance_with_enchanted_bonus")] RandomChanceWithEnchantedBonus, #[serde(rename = "minecraft:entity_properties")] EntityProperties, #[serde(rename = "minecraft:killed_by_player")] KilledByPlayer, #[serde(rename = "minecraft:entity_scores")] EntityScores, #[serde(rename = "minecraft:block_state_property")] BlockStateProperty { block: String, properties: BTreeMap, }, #[serde(rename = "minecraft:match_tool")] MatchTool, #[serde(rename = "minecraft:table_bonus")] TableBonus, #[serde(rename = "minecraft:survives_explosion")] SurvivesExplosion, #[serde(rename = "minecraft:damage_source_properties")] DamageSourceProperties, #[serde(rename = "minecraft:location_check")] LocationCheck, #[serde(rename = "minecraft:weather_check")] WeatherCheck, #[serde(rename = "minecraft:reference")] Reference, #[serde(rename = "minecraft:time_check")] TimeCheck, #[serde(rename = "minecraft:value_check")] ValueCheck, #[serde(rename = "minecraft:enchantment_active_check")] EnchantmentActiveCheck, } impl ToTokens for LootConditionStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let name = match self { Self::Inverted => quote! { LootCondition::Inverted }, Self::AnyOf => quote! { LootCondition::AnyOf }, Self::AllOf => quote! { LootCondition::AllOf }, Self::RandomChance => quote! { LootCondition::RandomChance }, Self::RandomChanceWithEnchantedBonus => { quote! { LootCondition::RandomChanceWithEnchantedBonus } } Self::EntityProperties => quote! { LootCondition::EntityProperties }, Self::KilledByPlayer => quote! { LootCondition::KilledByPlayer }, Self::EntityScores => quote! { LootCondition::EntityScores }, Self::BlockStateProperty { block, properties } => { let properties: Vec<_> = properties .iter() .map(|(k, v)| quote! { (#k, #v) }) .collect(); quote! { LootCondition::BlockStateProperty { block: #block, properties: &[#(#properties),*] } } } Self::MatchTool => quote! { LootCondition::MatchTool }, Self::TableBonus => quote! { LootCondition::TableBonus }, Self::SurvivesExplosion => quote! { LootCondition::SurvivesExplosion }, Self::DamageSourceProperties => { quote! { LootCondition::DamageSourceProperties } } Self::LocationCheck => quote! { LootCondition::LocationCheck }, Self::WeatherCheck => quote! { LootCondition::WeatherCheck }, Self::Reference => quote! { LootCondition::Reference }, Self::TimeCheck => quote! { LootCondition::TimeCheck }, Self::ValueCheck => quote! { LootCondition::ValueCheck }, Self::EnchantmentActiveCheck => { quote! { LootCondition::EnchantmentActiveCheck } } }; tokens.extend(name); } } #[derive(Deserialize, Clone, Debug)] pub struct LootFunctionStruct { #[serde(flatten)] content: LootFunctionTypesStruct, conditions: Option>, } impl ToTokens for LootFunctionStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let functions_tokens = &self.content.to_token_stream(); let conditions_tokens = if let Some(conds) = &self.conditions { let cond_tokens: Vec<_> = conds.iter().map(quote::ToTokens::to_token_stream).collect(); quote! { Some(&[#(#cond_tokens),*]) } } else { quote! { None } }; tokens.extend(quote! { LootFunction { content: #functions_tokens, conditions: #conditions_tokens, } }); } } #[derive(Deserialize, Clone, Debug)] #[serde(tag = "function")] pub enum LootFunctionTypesStruct { #[serde(rename = "minecraft:set_count")] SetCount { count: LootFunctionNumberProviderStruct, add: Option, }, #[serde(rename = "minecraft:enchanted_count_increase")] EnchantedCountIncrease, #[serde(rename = "minecraft:furnace_smelt")] FurnaceSmelt, #[serde(rename = "minecraft:set_potion")] SetPotion, #[serde(rename = "minecraft:set_ominous_bottle_amplifier")] SetOminousBottleAmplifier, #[serde(rename = "minecraft:limit_count")] LimitCount { limit: LootFunctionLimitCountStruct }, #[serde(rename = "minecraft:apply_bonus")] ApplyBonus { enchantment: String, formula: String, parameters: Option, }, #[serde(rename = "minecraft:copy_components")] CopyComponents { source: String, include: Vec, }, #[serde(rename = "minecraft:copy_state")] CopyState { block: String, properties: Vec, }, #[serde(rename = "minecraft:explosion_decay")] ExplosionDecay, } impl ToTokens for LootFunctionTypesStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let name = match self { Self::SetCount { count, add } => { let count = count.to_token_stream(); let add = add.unwrap_or(false); quote! { LootFunctionTypes::SetCount { count: #count, add: #add } } } Self::SetOminousBottleAmplifier => { quote! { LootFunctionTypes::SetOminousBottleAmplifier } } Self::FurnaceSmelt => { quote! { LootFunctionTypes::FurnaceSmelt } } Self::SetPotion => { quote! { LootFunctionTypes::SetPotion } } Self::EnchantedCountIncrease => { quote! { LootFunctionTypes::EnchantedCountIncrease } } Self::LimitCount { limit } => { let min = if let Some(min) = limit.min { quote! { Some(#min) } } else { quote! { None } }; let max = if let Some(max) = limit.max { quote! { Some(#max) } } else { quote! { None } }; quote! { LootFunctionTypes::LimitCount { min: #min, max: #max } } } Self::ApplyBonus { enchantment, formula, parameters, } => { let parameters = if let Some(params) = parameters { let params = params.to_token_stream(); quote! { Some(#params) } } else { quote! { None } }; quote! { LootFunctionTypes::ApplyBonus { enchantment: #enchantment, formula: #formula, parameters: #parameters, } } } Self::CopyComponents { source, include } => { quote! { LootFunctionTypes::CopyComponents { source: #source, include: &[#(#include),*], } } } Self::CopyState { block, properties } => { quote! { LootFunctionTypes::CopyState { block: #block, properties: &[#(#properties),*], } } } Self::ExplosionDecay => { quote! { LootFunctionTypes::ExplosionDecay } } }; tokens.extend(name); } } #[derive(Deserialize, Clone, Debug)] #[serde(tag = "type")] pub enum LootFunctionNumberProviderStruct { #[serde(rename = "minecraft:uniform")] Uniform { min: f32, max: f32 }, #[serde(rename = "minecraft:binomial")] Binomial { n: f32, p: f32 }, #[serde(rename = "minecraft:constant", untagged)] Constant(f32), } impl ToTokens for LootFunctionNumberProviderStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let name = match self { Self::Constant(value) => { quote! { LootFunctionNumberProvider::Constant { value: #value } } } Self::Uniform { min, max } => { quote! { LootFunctionNumberProvider::Uniform { min: #min, max: #max } } } Self::Binomial { n, p } => { quote! { LootFunctionNumberProvider::Binomial { n: #n, p: #p } } } }; tokens.extend(name); } } #[derive(Deserialize, Clone, Debug)] pub struct LootFunctionLimitCountStruct { min: Option, max: Option, } #[derive(Deserialize, Clone, Debug)] #[serde(untagged)] pub enum LootFunctionBonusParameterStruct { Multiplier { #[serde(rename = "bonusMultiplier")] bonus_multiplier: i32, }, Probability { extra: i32, probability: f32, }, } impl ToTokens for LootFunctionBonusParameterStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let name = match self { Self::Multiplier { bonus_multiplier } => { quote! { LootFunctionBonusParameter::Multiplier { bonus_multiplier: #bonus_multiplier } } } Self::Probability { extra, probability } => { quote! { LootFunctionBonusParameter::Probability { extra: #extra, probability: #probability } } } }; tokens.extend(name); } } #[derive(Deserialize, Clone, Debug)] pub struct LootPoolEntryStruct { #[serde(flatten)] content: LootPoolEntryTypesStruct, conditions: Option>, functions: Option>, } impl ToTokens for LootPoolEntryStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let content = &self.content; let conditions_tokens = if let Some(conds) = &self.conditions { let cond_tokens: Vec<_> = conds.iter().map(quote::ToTokens::to_token_stream).collect(); quote! { Some(&[#(#cond_tokens),*]) } } else { quote! { None } }; let functions_tokens = if let Some(fns) = &self.functions { let cond_tokens: Vec<_> = fns.iter().map(quote::ToTokens::to_token_stream).collect(); quote! { Some(&[#(#cond_tokens),*]) } } else { quote! { None } }; tokens.extend(quote! { LootPoolEntry { content: #content, conditions: #conditions_tokens, functions: #functions_tokens, } }); } } #[derive(Deserialize, Clone, Debug)] #[serde(rename = "snake_case")] pub enum LootTableTypeStruct { #[serde(rename = "minecraft:empty")] /// Nothing will be dropped. Empty, #[serde(rename = "minecraft:entity")] /// The Entity loot will be dropped. Entity, #[serde(rename = "minecraft:block")] /// A block will be dropped. Block, #[serde(rename = "minecraft:chest")] /// An item will be dropped. Chest, } impl ToTokens for LootTableTypeStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let name = match self { Self::Empty => quote! { LootTableType::Empty }, Self::Entity => quote! { LootTableType::Entity }, Self::Block => quote! { LootTableType::Block }, Self::Chest => quote! { LootTableType::Chest }, }; tokens.extend(name); } }