Add Entity loot
not done, but works
This commit is contained in:
parent
9004d307bf
commit
f16d4a6247
@ -9,6 +9,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
use syn::{Ident, LitInt, LitStr};
|
use syn::{Ident, LitInt, LitStr};
|
||||||
|
|
||||||
|
use crate::loot::LootTableStruct;
|
||||||
|
|
||||||
fn const_block_name_from_block_name(block: &str) -> String {
|
fn const_block_name_from_block_name(block: &str) -> String {
|
||||||
block.to_shouty_snake_case()
|
block.to_shouty_snake_case()
|
||||||
}
|
}
|
||||||
@ -469,486 +471,6 @@ impl ToTokens for BlockStateRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// These are required to be defined twice because serde can't deseralize into static context for obvious reasons.
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
|
||||||
pub struct LootTableStruct {
|
|
||||||
r#type: LootTableTypeStruct,
|
|
||||||
random_sequence: Option<String>,
|
|
||||||
pools: Option<Vec<LootPoolStruct>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for LootTableStruct {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let loot_table_type = self.r#type.to_token_stream();
|
|
||||||
let random_sequence = match &self.random_sequence {
|
|
||||||
Some(seq) => quote! { Some(#seq) },
|
|
||||||
None => quote! { None },
|
|
||||||
};
|
|
||||||
let pools = match &self.pools {
|
|
||||||
Some(pools) => {
|
|
||||||
let pool_tokens: Vec<_> = pools.iter().map(|pool| pool.to_token_stream()).collect();
|
|
||||||
quote! { Some(&[#(#pool_tokens),*]) }
|
|
||||||
}
|
|
||||||
None => 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<LootPoolEntryStruct>,
|
|
||||||
rolls: f32, // TODO
|
|
||||||
bonus_rolls: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for LootPoolStruct {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let entries_tokens: Vec<_> = self
|
|
||||||
.entries
|
|
||||||
.iter()
|
|
||||||
.map(|entry| entry.to_token_stream())
|
|
||||||
.collect();
|
|
||||||
let rolls = &self.rolls;
|
|
||||||
let bonus_rolls = &self.bonus_rolls;
|
|
||||||
|
|
||||||
tokens.extend(quote! {
|
|
||||||
LootPool {
|
|
||||||
entries: &[#(#entries_tokens),*],
|
|
||||||
rolls: #rolls,
|
|
||||||
bonus_rolls: #bonus_rolls,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<LootPoolEntryStruct>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for AlternativeEntryStruct {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let children = self.children.iter().map(|entry| entry.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 {
|
|
||||||
LootPoolEntryTypesStruct::Empty => {
|
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::Empty });
|
|
||||||
}
|
|
||||||
LootPoolEntryTypesStruct::Item(item) => {
|
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::Item(#item) });
|
|
||||||
}
|
|
||||||
LootPoolEntryTypesStruct::LootTable => {
|
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::LootTable });
|
|
||||||
}
|
|
||||||
LootPoolEntryTypesStruct::Dynamic => {
|
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::Dynamic });
|
|
||||||
}
|
|
||||||
LootPoolEntryTypesStruct::Tag => {
|
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::Tag });
|
|
||||||
}
|
|
||||||
LootPoolEntryTypesStruct::Alternatives(alt) => {
|
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::Alternatives(#alt) });
|
|
||||||
}
|
|
||||||
LootPoolEntryTypesStruct::Sequence => {
|
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::Sequence });
|
|
||||||
}
|
|
||||||
LootPoolEntryTypesStruct::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: HashMap<String, String>,
|
|
||||||
},
|
|
||||||
#[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 {
|
|
||||||
LootConditionStruct::Inverted => quote! { LootCondition::Inverted },
|
|
||||||
LootConditionStruct::AnyOf => quote! { LootCondition::AnyOf },
|
|
||||||
LootConditionStruct::AllOf => quote! { LootCondition::AllOf },
|
|
||||||
LootConditionStruct::RandomChance => quote! { LootCondition::RandomChance },
|
|
||||||
LootConditionStruct::RandomChanceWithEnchantedBonus => {
|
|
||||||
quote! { LootCondition::RandomChanceWithEnchantedBonus }
|
|
||||||
}
|
|
||||||
LootConditionStruct::EntityProperties => quote! { LootCondition::EntityProperties },
|
|
||||||
LootConditionStruct::KilledByPlayer => quote! { LootCondition::KilledByPlayer },
|
|
||||||
LootConditionStruct::EntityScores => quote! { LootCondition::EntityScores },
|
|
||||||
LootConditionStruct::BlockStateProperty { block, properties } => {
|
|
||||||
let properties: Vec<_> = properties
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| quote! { (#k, #v) })
|
|
||||||
.collect();
|
|
||||||
quote! { LootCondition::BlockStateProperty { block: #block, properties: &[#(#properties),*] } }
|
|
||||||
}
|
|
||||||
LootConditionStruct::MatchTool => quote! { LootCondition::MatchTool },
|
|
||||||
LootConditionStruct::TableBonus => quote! { LootCondition::TableBonus },
|
|
||||||
LootConditionStruct::SurvivesExplosion => quote! { LootCondition::SurvivesExplosion },
|
|
||||||
LootConditionStruct::DamageSourceProperties => {
|
|
||||||
quote! { LootCondition::DamageSourceProperties }
|
|
||||||
}
|
|
||||||
LootConditionStruct::LocationCheck => quote! { LootCondition::LocationCheck },
|
|
||||||
LootConditionStruct::WeatherCheck => quote! { LootCondition::WeatherCheck },
|
|
||||||
LootConditionStruct::Reference => quote! { LootCondition::Reference },
|
|
||||||
LootConditionStruct::TimeCheck => quote! { LootCondition::TimeCheck },
|
|
||||||
LootConditionStruct::ValueCheck => quote! { LootCondition::ValueCheck },
|
|
||||||
LootConditionStruct::EnchantmentActiveCheck => {
|
|
||||||
quote! { LootCondition::EnchantmentActiveCheck }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tokens.extend(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
|
||||||
pub struct LootFunctionStruct {
|
|
||||||
#[serde(flatten)]
|
|
||||||
content: LootFunctionTypesStruct,
|
|
||||||
conditions: Option<Vec<LootConditionStruct>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for LootFunctionStruct {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let functions_tokens = &self.content.to_token_stream();
|
|
||||||
|
|
||||||
let conditions_tokens = match &self.conditions {
|
|
||||||
Some(conds) => {
|
|
||||||
let cond_tokens: Vec<_> = conds.iter().map(|c| c.to_token_stream()).collect();
|
|
||||||
quote! { Some(&[#(#cond_tokens),*]) }
|
|
||||||
}
|
|
||||||
None => 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<bool>,
|
|
||||||
},
|
|
||||||
#[serde(rename = "minecraft:limit_count")]
|
|
||||||
LimitCount { limit: LootFunctionLimitCountStruct },
|
|
||||||
#[serde(rename = "minecraft:apply_bonus")]
|
|
||||||
ApplyBonus {
|
|
||||||
enchantment: String,
|
|
||||||
formula: String,
|
|
||||||
parameters: Option<LootFunctionBonusParameterStruct>,
|
|
||||||
},
|
|
||||||
#[serde(rename = "minecraft:copy_components")]
|
|
||||||
CopyComponents {
|
|
||||||
source: String,
|
|
||||||
include: Vec<String>,
|
|
||||||
},
|
|
||||||
#[serde(rename = "minecraft:copy_state")]
|
|
||||||
CopyState {
|
|
||||||
block: String,
|
|
||||||
properties: Vec<String>,
|
|
||||||
},
|
|
||||||
#[serde(rename = "minecraft:explosion_decay")]
|
|
||||||
ExplosionDecay,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for LootFunctionTypesStruct {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let name = match self {
|
|
||||||
LootFunctionTypesStruct::SetCount { count, add } => {
|
|
||||||
let count = count.to_token_stream();
|
|
||||||
let add = add.unwrap_or(false);
|
|
||||||
quote! { LootFunctionTypes::SetCount { count: #count, add: #add } }
|
|
||||||
}
|
|
||||||
LootFunctionTypesStruct::LimitCount { limit } => {
|
|
||||||
let min = match limit.min {
|
|
||||||
Some(min) => quote! { Some(#min) },
|
|
||||||
None => quote! { None },
|
|
||||||
};
|
|
||||||
let max = match limit.max {
|
|
||||||
Some(max) => quote! { Some(#max) },
|
|
||||||
None => quote! { None },
|
|
||||||
};
|
|
||||||
quote! { LootFunctionTypes::LimitCount { min: #min, max: #max } }
|
|
||||||
}
|
|
||||||
LootFunctionTypesStruct::ApplyBonus {
|
|
||||||
enchantment,
|
|
||||||
formula,
|
|
||||||
parameters,
|
|
||||||
} => {
|
|
||||||
let parameters = match parameters {
|
|
||||||
Some(params) => {
|
|
||||||
let params = params.to_token_stream();
|
|
||||||
quote! { Some(#params) }
|
|
||||||
}
|
|
||||||
None => quote! { None },
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
LootFunctionTypes::ApplyBonus {
|
|
||||||
enchantment: #enchantment,
|
|
||||||
formula: #formula,
|
|
||||||
parameters: #parameters,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LootFunctionTypesStruct::CopyComponents { source, include } => {
|
|
||||||
quote! {
|
|
||||||
LootFunctionTypes::CopyComponents {
|
|
||||||
source: #source,
|
|
||||||
include: &[#(#include),*],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LootFunctionTypesStruct::CopyState { block, properties } => {
|
|
||||||
quote! {
|
|
||||||
LootFunctionTypes::CopyState {
|
|
||||||
block: #block,
|
|
||||||
properties: &[#(#properties),*],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LootFunctionTypesStruct::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<f32>,
|
|
||||||
max: Option<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<Vec<LootConditionStruct>>,
|
|
||||||
functions: Option<Vec<LootFunctionStruct>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for LootPoolEntryStruct {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let content = &self.content;
|
|
||||||
let conditions_tokens = match &self.conditions {
|
|
||||||
Some(conds) => {
|
|
||||||
let cond_tokens: Vec<_> = conds.iter().map(|c| c.to_token_stream()).collect();
|
|
||||||
quote! { Some(&[#(#cond_tokens),*]) }
|
|
||||||
}
|
|
||||||
None => quote! { None },
|
|
||||||
};
|
|
||||||
let functions_tokens = match &self.functions {
|
|
||||||
Some(fns) => {
|
|
||||||
let cond_tokens: Vec<_> = fns.iter().map(|c| c.to_token_stream()).collect();
|
|
||||||
quote! { Some(&[#(#cond_tokens),*]) }
|
|
||||||
}
|
|
||||||
None => 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: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 {
|
|
||||||
LootTableTypeStruct::Empty => quote! { LootTableType::Empty },
|
|
||||||
LootTableTypeStruct::Block => quote! { LootTableType::Block },
|
|
||||||
LootTableTypeStruct::Chest => quote! { LootTableType::Chest },
|
|
||||||
};
|
|
||||||
|
|
||||||
tokens.extend(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub id: u16,
|
pub id: u16,
|
||||||
@ -1404,21 +926,19 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
|
|
||||||
pub fn get_block_collision_shapes(state_id: u16) -> Option<Vec<CollisionShape>> {
|
pub fn get_block_collision_shapes(state_id: u16) -> Option<Vec<CollisionShape>> {
|
||||||
let state = get_state_by_state_id(state_id)?;
|
let state = get_state_by_state_id(state_id)?;
|
||||||
let mut shapes: Vec<CollisionShape> = vec![];
|
let shapes: Vec<CollisionShape> = state.collision_shapes
|
||||||
for i in 0..state.collision_shapes.len() {
|
.iter()
|
||||||
let shape = &COLLISION_SHAPES[state.collision_shapes[i] as usize];
|
.map(|&id| COLLISION_SHAPES[id as usize])
|
||||||
shapes.push(*shape);
|
.collect();
|
||||||
}
|
|
||||||
Some(shapes)
|
Some(shapes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_block_outline_shapes(state_id: u16) -> Option<Vec<CollisionShape>> {
|
pub fn get_block_outline_shapes(state_id: u16) -> Option<Vec<CollisionShape>> {
|
||||||
let state = get_state_by_state_id(state_id)?;
|
let state = get_state_by_state_id(state_id)?;
|
||||||
let mut shapes: Vec<CollisionShape> = vec![];
|
let mut shapes: Vec<CollisionShape> = state.outline_shapes
|
||||||
for i in 0..state.outline_shapes.len() {
|
.iter()
|
||||||
let shape = &COLLISION_SHAPES[state.outline_shapes[i] as usize];
|
.map(|&id| COLLISION_SHAPES[id as usize])
|
||||||
shapes.push(*shape);
|
.collect();
|
||||||
}
|
|
||||||
let block = get_block_by_state_id(state_id)?;
|
let block = get_block_by_state_id(state_id)?;
|
||||||
if block.properties(state.id).and_then(|properties| {
|
if block.properties(state.id).and_then(|properties| {
|
||||||
properties
|
properties
|
||||||
|
|||||||
@ -14,6 +14,7 @@ mod entity_type;
|
|||||||
mod fluid;
|
mod fluid;
|
||||||
mod game_event;
|
mod game_event;
|
||||||
mod item;
|
mod item;
|
||||||
|
pub mod loot;
|
||||||
mod message_type;
|
mod message_type;
|
||||||
mod noise_parameter;
|
mod noise_parameter;
|
||||||
mod noise_router;
|
mod noise_router;
|
||||||
|
|||||||
@ -6,11 +6,14 @@ use quote::{ToTokens, format_ident, quote};
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use syn::LitInt;
|
use syn::LitInt;
|
||||||
|
|
||||||
|
use crate::loot::LootTableStruct;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct EntityType {
|
pub struct EntityType {
|
||||||
pub id: u16,
|
pub id: u16,
|
||||||
pub max_health: Option<f32>,
|
pub max_health: Option<f32>,
|
||||||
pub attackable: Option<bool>,
|
pub attackable: Option<bool>,
|
||||||
|
pub loot_table: Option<LootTableStruct>,
|
||||||
pub summonable: bool,
|
pub summonable: bool,
|
||||||
pub fire_immune: bool,
|
pub fire_immune: bool,
|
||||||
pub dimension: [f32; 2],
|
pub dimension: [f32; 2],
|
||||||
@ -79,6 +82,14 @@ impl ToTokens for NamedEntityType<'_> {
|
|||||||
let dimension0 = entity.dimension[0];
|
let dimension0 = entity.dimension[0];
|
||||||
let dimension1 = entity.dimension[1];
|
let dimension1 = entity.dimension[1];
|
||||||
|
|
||||||
|
let loot_table = match &entity.loot_table {
|
||||||
|
Some(table) => {
|
||||||
|
let table_tokens = table.to_token_stream();
|
||||||
|
quote! { Some(#table_tokens) }
|
||||||
|
}
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
|
||||||
tokens.extend(quote! {
|
tokens.extend(quote! {
|
||||||
EntityType {
|
EntityType {
|
||||||
id: #id,
|
id: #id,
|
||||||
@ -86,6 +97,7 @@ impl ToTokens for NamedEntityType<'_> {
|
|||||||
attackable: #attackable,
|
attackable: #attackable,
|
||||||
summonable: #summonable,
|
summonable: #summonable,
|
||||||
fire_immune: #fire_immune,
|
fire_immune: #fire_immune,
|
||||||
|
loot_table: #loot_table,
|
||||||
dimension: [#dimension0, #dimension1], // Correctly construct the array
|
dimension: [#dimension0, #dimension1], // Correctly construct the array
|
||||||
eye_height: #eye_height,
|
eye_height: #eye_height,
|
||||||
spawn_restriction: #spawn_restriction,
|
spawn_restriction: #spawn_restriction,
|
||||||
@ -126,6 +138,7 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
quote! {
|
quote! {
|
||||||
|
use pumpkin_util::loot_table::*;
|
||||||
use pumpkin_util::HeightMap;
|
use pumpkin_util::HeightMap;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
@ -135,6 +148,7 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
pub attackable: Option<bool>,
|
pub attackable: Option<bool>,
|
||||||
pub summonable: bool,
|
pub summonable: bool,
|
||||||
pub fire_immune: bool,
|
pub fire_immune: bool,
|
||||||
|
pub loot_table: Option<LootTable>,
|
||||||
pub dimension: [f32; 2],
|
pub dimension: [f32; 2],
|
||||||
pub eye_height: f32,
|
pub eye_height: f32,
|
||||||
pub spawn_restriction: SpawnRestriction,
|
pub spawn_restriction: SpawnRestriction,
|
||||||
|
|||||||
511
pumpkin-data/build/loot.rs
Normal file
511
pumpkin-data/build/loot.rs
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
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 deseralize into static context for obvious reasons.
|
||||||
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
pub struct LootTableStruct {
|
||||||
|
r#type: LootTableTypeStruct,
|
||||||
|
random_sequence: Option<String>,
|
||||||
|
pools: Option<Vec<LootPoolStruct>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for LootTableStruct {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let loot_table_type = self.r#type.to_token_stream();
|
||||||
|
let random_sequence = match &self.random_sequence {
|
||||||
|
Some(seq) => quote! { Some(#seq) },
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
let pools = match &self.pools {
|
||||||
|
Some(pools) => {
|
||||||
|
let pool_tokens: Vec<_> = pools.iter().map(|pool| pool.to_token_stream()).collect();
|
||||||
|
quote! { Some(&[#(#pool_tokens),*]) }
|
||||||
|
}
|
||||||
|
None => 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<LootPoolEntryStruct>,
|
||||||
|
rolls: LootNumberProviderTypes, // TODO
|
||||||
|
bonus_rolls: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for LootPoolStruct {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let entries_tokens: Vec<_> = self
|
||||||
|
.entries
|
||||||
|
.iter()
|
||||||
|
.map(|entry| entry.to_token_stream())
|
||||||
|
.collect();
|
||||||
|
let rolls = &self.rolls;
|
||||||
|
let bonus_rolls = &self.bonus_rolls;
|
||||||
|
|
||||||
|
tokens.extend(quote! {
|
||||||
|
LootPool {
|
||||||
|
entries: &[#(#entries_tokens),*],
|
||||||
|
rolls: #rolls,
|
||||||
|
bonus_rolls: #bonus_rolls,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<LootPoolEntryStruct>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for AlternativeEntryStruct {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let children = self.children.iter().map(|entry| entry.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 {
|
||||||
|
LootPoolEntryTypesStruct::Empty => {
|
||||||
|
tokens.extend(quote! { LootPoolEntryTypes::Empty });
|
||||||
|
}
|
||||||
|
LootPoolEntryTypesStruct::Item(item) => {
|
||||||
|
tokens.extend(quote! { LootPoolEntryTypes::Item(#item) });
|
||||||
|
}
|
||||||
|
LootPoolEntryTypesStruct::LootTable => {
|
||||||
|
tokens.extend(quote! { LootPoolEntryTypes::LootTable });
|
||||||
|
}
|
||||||
|
LootPoolEntryTypesStruct::Dynamic => {
|
||||||
|
tokens.extend(quote! { LootPoolEntryTypes::Dynamic });
|
||||||
|
}
|
||||||
|
LootPoolEntryTypesStruct::Tag => {
|
||||||
|
tokens.extend(quote! { LootPoolEntryTypes::Tag });
|
||||||
|
}
|
||||||
|
LootPoolEntryTypesStruct::Alternatives(alt) => {
|
||||||
|
tokens.extend(quote! { LootPoolEntryTypes::Alternatives(#alt) });
|
||||||
|
}
|
||||||
|
LootPoolEntryTypesStruct::Sequence => {
|
||||||
|
tokens.extend(quote! { LootPoolEntryTypes::Sequence });
|
||||||
|
}
|
||||||
|
LootPoolEntryTypesStruct::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: HashMap<String, String>,
|
||||||
|
},
|
||||||
|
#[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 {
|
||||||
|
LootConditionStruct::Inverted => quote! { LootCondition::Inverted },
|
||||||
|
LootConditionStruct::AnyOf => quote! { LootCondition::AnyOf },
|
||||||
|
LootConditionStruct::AllOf => quote! { LootCondition::AllOf },
|
||||||
|
LootConditionStruct::RandomChance => quote! { LootCondition::RandomChance },
|
||||||
|
LootConditionStruct::RandomChanceWithEnchantedBonus => {
|
||||||
|
quote! { LootCondition::RandomChanceWithEnchantedBonus }
|
||||||
|
}
|
||||||
|
LootConditionStruct::EntityProperties => quote! { LootCondition::EntityProperties },
|
||||||
|
LootConditionStruct::KilledByPlayer => quote! { LootCondition::KilledByPlayer },
|
||||||
|
LootConditionStruct::EntityScores => quote! { LootCondition::EntityScores },
|
||||||
|
LootConditionStruct::BlockStateProperty { block, properties } => {
|
||||||
|
let properties: Vec<_> = properties
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| quote! { (#k, #v) })
|
||||||
|
.collect();
|
||||||
|
quote! { LootCondition::BlockStateProperty { block: #block, properties: &[#(#properties),*] } }
|
||||||
|
}
|
||||||
|
LootConditionStruct::MatchTool => quote! { LootCondition::MatchTool },
|
||||||
|
LootConditionStruct::TableBonus => quote! { LootCondition::TableBonus },
|
||||||
|
LootConditionStruct::SurvivesExplosion => quote! { LootCondition::SurvivesExplosion },
|
||||||
|
LootConditionStruct::DamageSourceProperties => {
|
||||||
|
quote! { LootCondition::DamageSourceProperties }
|
||||||
|
}
|
||||||
|
LootConditionStruct::LocationCheck => quote! { LootCondition::LocationCheck },
|
||||||
|
LootConditionStruct::WeatherCheck => quote! { LootCondition::WeatherCheck },
|
||||||
|
LootConditionStruct::Reference => quote! { LootCondition::Reference },
|
||||||
|
LootConditionStruct::TimeCheck => quote! { LootCondition::TimeCheck },
|
||||||
|
LootConditionStruct::ValueCheck => quote! { LootCondition::ValueCheck },
|
||||||
|
LootConditionStruct::EnchantmentActiveCheck => {
|
||||||
|
quote! { LootCondition::EnchantmentActiveCheck }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.extend(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, Debug)]
|
||||||
|
pub struct LootFunctionStruct {
|
||||||
|
#[serde(flatten)]
|
||||||
|
content: LootFunctionTypesStruct,
|
||||||
|
conditions: Option<Vec<LootConditionStruct>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for LootFunctionStruct {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let functions_tokens = &self.content.to_token_stream();
|
||||||
|
|
||||||
|
let conditions_tokens = match &self.conditions {
|
||||||
|
Some(conds) => {
|
||||||
|
let cond_tokens: Vec<_> = conds.iter().map(|c| c.to_token_stream()).collect();
|
||||||
|
quote! { Some(&[#(#cond_tokens),*]) }
|
||||||
|
}
|
||||||
|
None => 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<bool>,
|
||||||
|
},
|
||||||
|
#[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<LootFunctionBonusParameterStruct>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:copy_components")]
|
||||||
|
CopyComponents {
|
||||||
|
source: String,
|
||||||
|
include: Vec<String>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:copy_state")]
|
||||||
|
CopyState {
|
||||||
|
block: String,
|
||||||
|
properties: Vec<String>,
|
||||||
|
},
|
||||||
|
#[serde(rename = "minecraft:explosion_decay")]
|
||||||
|
ExplosionDecay,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for LootFunctionTypesStruct {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let name = match self {
|
||||||
|
LootFunctionTypesStruct::SetCount { count, add } => {
|
||||||
|
let count = count.to_token_stream();
|
||||||
|
let add = add.unwrap_or(false);
|
||||||
|
quote! { LootFunctionTypes::SetCount { count: #count, add: #add } }
|
||||||
|
}
|
||||||
|
LootFunctionTypesStruct::SetOminousBottleAmplifier => {
|
||||||
|
quote! { LootFunctionTypes::SetOminousBottleAmplifier }
|
||||||
|
}
|
||||||
|
LootFunctionTypesStruct::FurnaceSmelt => {
|
||||||
|
quote! { LootFunctionTypes::FurnaceSmelt }
|
||||||
|
}
|
||||||
|
LootFunctionTypesStruct::SetPotion => {
|
||||||
|
quote! { LootFunctionTypes::SetPotion }
|
||||||
|
}
|
||||||
|
LootFunctionTypesStruct::EnchantedCountIncrease => {
|
||||||
|
quote! { LootFunctionTypes::EnchantedCountIncrease }
|
||||||
|
}
|
||||||
|
LootFunctionTypesStruct::LimitCount { limit } => {
|
||||||
|
let min = match limit.min {
|
||||||
|
Some(min) => quote! { Some(#min) },
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
let max = match limit.max {
|
||||||
|
Some(max) => quote! { Some(#max) },
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
quote! { LootFunctionTypes::LimitCount { min: #min, max: #max } }
|
||||||
|
}
|
||||||
|
LootFunctionTypesStruct::ApplyBonus {
|
||||||
|
enchantment,
|
||||||
|
formula,
|
||||||
|
parameters,
|
||||||
|
} => {
|
||||||
|
let parameters = match parameters {
|
||||||
|
Some(params) => {
|
||||||
|
let params = params.to_token_stream();
|
||||||
|
quote! { Some(#params) }
|
||||||
|
}
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
LootFunctionTypes::ApplyBonus {
|
||||||
|
enchantment: #enchantment,
|
||||||
|
formula: #formula,
|
||||||
|
parameters: #parameters,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LootFunctionTypesStruct::CopyComponents { source, include } => {
|
||||||
|
quote! {
|
||||||
|
LootFunctionTypes::CopyComponents {
|
||||||
|
source: #source,
|
||||||
|
include: &[#(#include),*],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LootFunctionTypesStruct::CopyState { block, properties } => {
|
||||||
|
quote! {
|
||||||
|
LootFunctionTypes::CopyState {
|
||||||
|
block: #block,
|
||||||
|
properties: &[#(#properties),*],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LootFunctionTypesStruct::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<f32>,
|
||||||
|
max: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<Vec<LootConditionStruct>>,
|
||||||
|
functions: Option<Vec<LootFunctionStruct>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for LootPoolEntryStruct {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let content = &self.content;
|
||||||
|
let conditions_tokens = match &self.conditions {
|
||||||
|
Some(conds) => {
|
||||||
|
let cond_tokens: Vec<_> = conds.iter().map(|c| c.to_token_stream()).collect();
|
||||||
|
quote! { Some(&[#(#cond_tokens),*]) }
|
||||||
|
}
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
let functions_tokens = match &self.functions {
|
||||||
|
Some(fns) => {
|
||||||
|
let cond_tokens: Vec<_> = fns.iter().map(|c| c.to_token_stream()).collect();
|
||||||
|
quote! { Some(&[#(#cond_tokens),*]) }
|
||||||
|
}
|
||||||
|
None => 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 {
|
||||||
|
LootTableTypeStruct::Empty => quote! { LootTableType::Empty },
|
||||||
|
LootTableTypeStruct::Entity => quote! { LootTableType::Entity },
|
||||||
|
LootTableTypeStruct::Block => quote! { LootTableType::Block },
|
||||||
|
LootTableTypeStruct::Chest => quote! { LootTableType::Chest },
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.extend(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,28 +1,34 @@
|
|||||||
#[derive(Clone, Debug)]
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{ToTokens, quote};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::random::RandomImpl;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub struct LootTable {
|
pub struct LootTable {
|
||||||
pub r#type: LootTableType,
|
pub r#type: LootTableType,
|
||||||
pub random_sequence: Option<&'static str>,
|
pub random_sequence: Option<&'static str>,
|
||||||
pub pools: Option<&'static [LootPool]>,
|
pub pools: Option<&'static [LootPool]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct LootPool {
|
pub struct LootPool {
|
||||||
pub entries: &'static [LootPoolEntry],
|
pub entries: &'static [LootPoolEntry],
|
||||||
pub rolls: f32,
|
pub rolls: LootNumberProviderTypes,
|
||||||
pub bonus_rolls: f32,
|
pub bonus_rolls: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct ItemEntry {
|
pub struct ItemEntry {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct AlternativeEntry {
|
pub struct AlternativeEntry {
|
||||||
pub children: &'static [LootPoolEntry],
|
pub children: &'static [LootPoolEntry],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum LootPoolEntryTypes {
|
pub enum LootPoolEntryTypes {
|
||||||
Empty,
|
Empty,
|
||||||
Item(ItemEntry),
|
Item(ItemEntry),
|
||||||
@ -34,7 +40,7 @@ pub enum LootPoolEntryTypes {
|
|||||||
Group,
|
Group,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum LootCondition {
|
pub enum LootCondition {
|
||||||
Inverted,
|
Inverted,
|
||||||
AnyOf,
|
AnyOf,
|
||||||
@ -60,18 +66,22 @@ pub enum LootCondition {
|
|||||||
EnchantmentActiveCheck,
|
EnchantmentActiveCheck,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct LootFunction {
|
pub struct LootFunction {
|
||||||
pub content: LootFunctionTypes,
|
pub content: LootFunctionTypes,
|
||||||
pub conditions: Option<&'static [LootCondition]>,
|
pub conditions: Option<&'static [LootCondition]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum LootFunctionTypes {
|
pub enum LootFunctionTypes {
|
||||||
SetCount {
|
SetCount {
|
||||||
count: LootFunctionNumberProvider,
|
count: LootFunctionNumberProvider,
|
||||||
add: bool,
|
add: bool,
|
||||||
},
|
},
|
||||||
|
EnchantedCountIncrease,
|
||||||
|
FurnaceSmelt,
|
||||||
|
SetPotion,
|
||||||
|
SetOminousBottleAmplifier,
|
||||||
LimitCount {
|
LimitCount {
|
||||||
min: Option<f32>,
|
min: Option<f32>,
|
||||||
max: Option<f32>,
|
max: Option<f32>,
|
||||||
@ -92,32 +102,131 @@ pub enum LootFunctionTypes {
|
|||||||
ExplosionDecay,
|
ExplosionDecay,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum LootFunctionNumberProvider {
|
pub enum LootFunctionNumberProvider {
|
||||||
Constant { value: f32 },
|
Constant { value: f32 },
|
||||||
Uniform { min: f32, max: f32 },
|
Uniform { min: f32, max: f32 },
|
||||||
Binomial { n: f32, p: f32 },
|
Binomial { n: f32, p: f32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum LootFunctionBonusParameter {
|
pub enum LootFunctionBonusParameter {
|
||||||
Multiplier { bonus_multiplier: i32 },
|
Multiplier { bonus_multiplier: i32 },
|
||||||
Probability { extra: i32, probability: f32 },
|
Probability { extra: i32, probability: f32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct LootPoolEntry {
|
pub struct LootPoolEntry {
|
||||||
pub content: LootPoolEntryTypes,
|
pub content: LootPoolEntryTypes,
|
||||||
pub conditions: Option<&'static [LootCondition]>,
|
pub conditions: Option<&'static [LootCondition]>,
|
||||||
pub functions: Option<&'static [LootFunction]>,
|
pub functions: Option<&'static [LootFunction]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub enum LootTableType {
|
pub enum LootTableType {
|
||||||
/// Nothing will be dropped
|
|
||||||
Empty,
|
Empty,
|
||||||
/// A block will be dropped
|
Entity,
|
||||||
Block,
|
Block,
|
||||||
/// An item will be dropped
|
|
||||||
Chest,
|
Chest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, PartialEq, Clone, Copy, Debug)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum LootNumberProviderTypesProvider {
|
||||||
|
#[serde(rename = "minecraft:uniform")]
|
||||||
|
Uniform(UniformLootNumberProvider),
|
||||||
|
}
|
||||||
|
impl ToTokens for LootNumberProviderTypesProvider {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
match self {
|
||||||
|
LootNumberProviderTypesProvider::Uniform(uniform) => {
|
||||||
|
tokens.extend(quote! {
|
||||||
|
LootNumberProviderTypesProvider::Uniform(#uniform)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, PartialEq, Clone, Copy, Debug)]
|
||||||
|
pub struct UniformLootNumberProvider {
|
||||||
|
pub min: f32,
|
||||||
|
pub max: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for UniformLootNumberProvider {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let min_inclusive = self.min;
|
||||||
|
let max_inclusive = self.max;
|
||||||
|
|
||||||
|
tokens.extend(quote! {
|
||||||
|
UniformLootNumberProvider { min: #min_inclusive, max: #max_inclusive }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UniformLootNumberProvider {
|
||||||
|
pub fn get_min(&self) -> f32 {
|
||||||
|
self.min
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, random: &mut impl RandomImpl) -> f32 {
|
||||||
|
// TODO
|
||||||
|
random.next_f32()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max(&self) -> f32 {
|
||||||
|
self.max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, PartialEq, Clone, Copy, Debug)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum LootNumberProviderTypes {
|
||||||
|
Object(LootNumberProviderTypesProvider),
|
||||||
|
Constant(f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for LootNumberProviderTypes {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
match self {
|
||||||
|
LootNumberProviderTypes::Object(provider) => {
|
||||||
|
tokens.extend(quote! {
|
||||||
|
LootNumberProviderTypes::Object(#provider)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
LootNumberProviderTypes::Constant(i) => tokens.extend(quote! {
|
||||||
|
LootNumberProviderTypes::Constant(#i)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LootNumberProviderTypes {
|
||||||
|
pub fn get_min(&self) -> f32 {
|
||||||
|
match self {
|
||||||
|
LootNumberProviderTypes::Object(int_provider) => match int_provider {
|
||||||
|
LootNumberProviderTypesProvider::Uniform(uniform) => uniform.get_min(),
|
||||||
|
},
|
||||||
|
LootNumberProviderTypes::Constant(i) => *i,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, random: &mut impl RandomImpl) -> f32 {
|
||||||
|
match self {
|
||||||
|
LootNumberProviderTypes::Object(int_provider) => match int_provider {
|
||||||
|
LootNumberProviderTypesProvider::Uniform(uniform) => uniform.get(random),
|
||||||
|
},
|
||||||
|
LootNumberProviderTypes::Constant(i) => *i,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max(&self) -> f32 {
|
||||||
|
match self {
|
||||||
|
LootNumberProviderTypes::Object(int_provider) => match int_provider {
|
||||||
|
LootNumberProviderTypesProvider::Uniform(uniform) => uniform.get_max(),
|
||||||
|
},
|
||||||
|
LootNumberProviderTypes::Constant(i) => *i,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,26 +1,40 @@
|
|||||||
use pumpkin_data::item::Item;
|
use pumpkin_data::{Block, BlockState, block_properties::get_block_by_state_id, item::Item};
|
||||||
use pumpkin_util::loot_table::{
|
use pumpkin_util::{
|
||||||
|
loot_table::{
|
||||||
LootCondition, LootFunctionNumberProvider, LootFunctionTypes, LootPoolEntry,
|
LootCondition, LootFunctionNumberProvider, LootFunctionTypes, LootPoolEntry,
|
||||||
LootPoolEntryTypes, LootTable,
|
LootPoolEntryTypes, LootTable,
|
||||||
|
},
|
||||||
|
random::{RandomGenerator, xoroshiro128::Xoroshiro},
|
||||||
};
|
};
|
||||||
use pumpkin_world::item::ItemStack;
|
use pumpkin_world::item::ItemStack;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
pub(super) trait LootTableExt {
|
#[derive(Default)]
|
||||||
fn get_loot(&self, block_props: &[(String, String)]) -> Vec<ItemStack>;
|
pub struct LootContextParameters {
|
||||||
|
pub explosion_radius: Option<f32>,
|
||||||
|
pub block_state: Option<BlockState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LootTableExt {
|
||||||
|
fn get_loot(&self, params: LootContextParameters) -> Vec<ItemStack>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LootTableExt for LootTable {
|
impl LootTableExt for LootTable {
|
||||||
fn get_loot(&self, block_props: &[(String, String)]) -> Vec<ItemStack> {
|
fn get_loot(&self, params: LootContextParameters) -> Vec<ItemStack> {
|
||||||
let mut stacks = Vec::new();
|
let mut stacks = Vec::new();
|
||||||
|
|
||||||
if let Some(pools) = self.pools {
|
if let Some(pools) = self.pools {
|
||||||
for pool in pools {
|
for pool in pools {
|
||||||
let rolls = pool.rolls.round() + pool.bonus_rolls.floor(); // TODO: multiply by luck
|
// TODO
|
||||||
|
let rolls = pool
|
||||||
|
.rolls
|
||||||
|
.get(&mut RandomGenerator::Xoroshiro(Xoroshiro::from_seed(123)))
|
||||||
|
.round()
|
||||||
|
+ pool.bonus_rolls.floor(); // TODO: multiply by luck
|
||||||
|
|
||||||
for _ in 0..(rolls as i32) {
|
for _ in 0..(rolls as i32) {
|
||||||
for entry in pool.entries {
|
for entry in pool.entries {
|
||||||
if let Some(loot) = entry.get_loot(block_props) {
|
if let Some(loot) = entry.get_loot(¶ms) {
|
||||||
stacks.extend(loot);
|
stacks.extend(loot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,23 +47,23 @@ impl LootTableExt for LootTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait LootPoolEntryExt {
|
trait LootPoolEntryExt {
|
||||||
fn get_loot(&self, block_props: &[(String, String)]) -> Option<Vec<ItemStack>>;
|
fn get_loot(&self, params: &LootContextParameters) -> Option<Vec<ItemStack>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LootPoolEntryExt for LootPoolEntry {
|
impl LootPoolEntryExt for LootPoolEntry {
|
||||||
fn get_loot(&self, block_props: &[(String, String)]) -> Option<Vec<ItemStack>> {
|
fn get_loot(&self, params: &LootContextParameters) -> Option<Vec<ItemStack>> {
|
||||||
if let Some(conditions) = self.conditions {
|
if let Some(conditions) = self.conditions {
|
||||||
if !conditions.iter().all(|cond| cond.is_fulfilled(block_props)) {
|
if !conditions.iter().all(|cond| cond.is_fulfilled(params)) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut stacks = self.content.get_stacks(block_props);
|
let mut stacks = self.content.get_stacks(params);
|
||||||
|
|
||||||
if let Some(functions) = self.functions {
|
if let Some(functions) = self.functions {
|
||||||
for function in functions {
|
for function in functions {
|
||||||
if let Some(conditions) = function.conditions {
|
if let Some(conditions) = function.conditions {
|
||||||
if !conditions.iter().all(|cond| cond.is_fulfilled(block_props)) {
|
if !conditions.iter().all(|cond| cond.is_fulfilled(params)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,6 +108,10 @@ impl LootPoolEntryExt for LootPoolEntry {
|
|||||||
block: _,
|
block: _,
|
||||||
properties: _,
|
properties: _,
|
||||||
}
|
}
|
||||||
|
| LootFunctionTypes::EnchantedCountIncrease
|
||||||
|
| LootFunctionTypes::SetOminousBottleAmplifier
|
||||||
|
| LootFunctionTypes::SetPotion
|
||||||
|
| LootFunctionTypes::FurnaceSmelt
|
||||||
| LootFunctionTypes::ExplosionDecay => {
|
| LootFunctionTypes::ExplosionDecay => {
|
||||||
// TODO: shouldnt crash here but needs to be implemented someday
|
// TODO: shouldnt crash here but needs to be implemented someday
|
||||||
}
|
}
|
||||||
@ -106,11 +124,11 @@ impl LootPoolEntryExt for LootPoolEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait LootPoolEntryTypesExt {
|
trait LootPoolEntryTypesExt {
|
||||||
fn get_stacks(&self, block_props: &[(String, String)]) -> Vec<ItemStack>;
|
fn get_stacks(&self, params: &LootContextParameters) -> Vec<ItemStack>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LootPoolEntryTypesExt for LootPoolEntryTypes {
|
impl LootPoolEntryTypesExt for LootPoolEntryTypes {
|
||||||
fn get_stacks(&self, block_props: &[(String, String)]) -> Vec<ItemStack> {
|
fn get_stacks(&self, params: &LootContextParameters) -> Vec<ItemStack> {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty => Vec::new(),
|
Self::Empty => Vec::new(),
|
||||||
Self::Item(item_entry) => {
|
Self::Item(item_entry) => {
|
||||||
@ -123,7 +141,7 @@ impl LootPoolEntryTypesExt for LootPoolEntryTypes {
|
|||||||
Self::Alternatives(alternative_entry) => alternative_entry
|
Self::Alternatives(alternative_entry) => alternative_entry
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|entry| entry.get_loot(block_props))
|
.filter_map(|entry| entry.get_loot(params))
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect(),
|
.collect(),
|
||||||
Self::Sequence => todo!(),
|
Self::Sequence => todo!(),
|
||||||
@ -133,20 +151,34 @@ impl LootPoolEntryTypesExt for LootPoolEntryTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait LootConditionExt {
|
trait LootConditionExt {
|
||||||
fn is_fulfilled(&self, block_props: &[(String, String)]) -> bool;
|
fn is_fulfilled(&self, params: &LootContextParameters) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LootConditionExt for LootCondition {
|
impl LootConditionExt for LootCondition {
|
||||||
// TODO: This is trash. Make this right
|
// TODO: This is trash. Make this right
|
||||||
fn is_fulfilled(&self, block_props: &[(String, String)]) -> bool {
|
fn is_fulfilled(&self, params: &LootContextParameters) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::SurvivesExplosion => true,
|
Self::SurvivesExplosion => {
|
||||||
|
if let Some(radius) = params.explosion_radius {
|
||||||
|
return rand::rng().random::<f32>() <= 1.0 / radius;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
Self::BlockStateProperty {
|
Self::BlockStateProperty {
|
||||||
block: _,
|
block: _,
|
||||||
properties,
|
properties,
|
||||||
} => properties
|
} => {
|
||||||
|
if let Some(state) = ¶ms.block_state {
|
||||||
|
let props =
|
||||||
|
Block::properties(&get_block_by_state_id(state.id).unwrap(), state.id)
|
||||||
|
.map_or_else(Vec::new, |props| props.to_props());
|
||||||
|
|
||||||
|
return properties
|
||||||
.iter()
|
.iter()
|
||||||
.all(|(key, value)| block_props.iter().any(|(k, v)| k == key && v == value)),
|
.all(|(key, value)| props.iter().any(|(k, v)| k == key && v == value));
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,19 +65,15 @@ use fluid::lava::FlowingLava;
|
|||||||
use fluid::water::FlowingWater;
|
use fluid::water::FlowingWater;
|
||||||
use loot::LootTableExt;
|
use loot::LootTableExt;
|
||||||
use pumpkin_data::block_properties::Integer0To15;
|
use pumpkin_data::block_properties::Integer0To15;
|
||||||
use pumpkin_data::entity::EntityType;
|
|
||||||
use pumpkin_data::{Block, BlockState};
|
use pumpkin_data::{Block, BlockState};
|
||||||
|
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
|
||||||
use pumpkin_util::random::{RandomGenerator, get_seed, xoroshiro128::Xoroshiro};
|
use pumpkin_util::random::{RandomGenerator, get_seed, xoroshiro128::Xoroshiro};
|
||||||
use pumpkin_world::BlockStateId;
|
use pumpkin_world::BlockStateId;
|
||||||
use pumpkin_world::item::ItemStack;
|
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
use crate::block::blocks::plant::roots::RootsBlock;
|
use crate::block::blocks::plant::roots::RootsBlock;
|
||||||
|
use crate::block::loot::LootContextParameters;
|
||||||
use crate::block::registry::BlockRegistry;
|
use crate::block::registry::BlockRegistry;
|
||||||
use crate::entity::item::ItemEntity;
|
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
use crate::{block::blocks::crafting_table::CraftingTableBlock, entity::player::Player};
|
use crate::{block::blocks::crafting_table::CraftingTableBlock, entity::player::Player};
|
||||||
use crate::{block::blocks::jukebox::JukeboxBlock, entity::experience_orb::ExperienceOrbEntity};
|
use crate::{block::blocks::jukebox::JukeboxBlock, entity::experience_orb::ExperienceOrbEntity};
|
||||||
@ -85,7 +81,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
pub mod blocks;
|
pub mod blocks;
|
||||||
mod fluid;
|
mod fluid;
|
||||||
mod loot;
|
pub mod loot;
|
||||||
pub mod pumpkin_block;
|
pub mod pumpkin_block;
|
||||||
pub mod pumpkin_fluid;
|
pub mod pumpkin_fluid;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
@ -192,14 +188,11 @@ pub async fn drop_loot(
|
|||||||
block: &Block,
|
block: &Block,
|
||||||
pos: &BlockPos,
|
pos: &BlockPos,
|
||||||
experience: bool,
|
experience: bool,
|
||||||
state_id: BlockStateId,
|
params: LootContextParameters,
|
||||||
) {
|
) {
|
||||||
if let Some(loot_table) = &block.loot_table {
|
if let Some(loot_table) = &block.loot_table {
|
||||||
let props =
|
for stack in loot_table.get_loot(params) {
|
||||||
Block::properties(block, state_id).map_or_else(Vec::new, |props| props.to_props());
|
world.drop_stack(pos, stack).await;
|
||||||
|
|
||||||
for stack in loot_table.get_loot(&props) {
|
|
||||||
drop_stack(world, pos, stack).await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,19 +208,6 @@ pub async fn drop_loot(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn drop_stack(world: &Arc<World>, pos: &BlockPos, stack: ItemStack) {
|
|
||||||
let height = EntityType::ITEM.dimension[1] / 2.0;
|
|
||||||
let pos = Vector3::new(
|
|
||||||
f64::from(pos.0.x) + 0.5 + rand::rng().random_range(-0.25..0.25),
|
|
||||||
f64::from(pos.0.y) + 0.5 + rand::rng().random_range(-0.25..0.25) - f64::from(height),
|
|
||||||
f64::from(pos.0.z) + 0.5 + rand::rng().random_range(-0.25..0.25),
|
|
||||||
);
|
|
||||||
|
|
||||||
let entity = world.create_entity(pos, EntityType::ITEM);
|
|
||||||
let item_entity = Arc::new(ItemEntity::new(entity, stack).await);
|
|
||||||
world.spawn_entity(item_entity).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn calc_block_breaking(player: &Player, state: &BlockState, block_name: &str) -> f32 {
|
pub async fn calc_block_breaking(player: &Player, state: &BlockState, block_name: &str) -> f32 {
|
||||||
let hardness = state.hardness;
|
let hardness = state.hardness;
|
||||||
#[expect(clippy::float_cmp)]
|
#[expect(clippy::float_cmp)]
|
||||||
|
|||||||
@ -57,7 +57,7 @@ impl CommandExecutor for Executor {
|
|||||||
(player.world().await, pos)
|
(player.world().await, pos)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mob = mob::from_type(entity, pos, &world).await;
|
let mob = mob::from_type(entity, pos, &world);
|
||||||
world.spawn_entity(mob).await;
|
world.spawn_entity(mob).await;
|
||||||
|
|
||||||
sender
|
sender
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
use pumpkin_data::block_properties::get_block_collision_shapes;
|
||||||
use pumpkin_protocol::client::play::CUpdateEntityPos;
|
use pumpkin_protocol::client::play::CUpdateEntityPos;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||||
|
|
||||||
use crate::entity::living::LivingEntity;
|
use crate::entity::living::LivingEntity;
|
||||||
|
|
||||||
@ -36,6 +37,8 @@ impl Navigator {
|
|||||||
let mut best_move = Vector3::new(0.0, 0.0, 0.0);
|
let mut best_move = Vector3::new(0.0, 0.0, 0.0);
|
||||||
let mut lowest_cost = f64::MAX;
|
let mut lowest_cost = f64::MAX;
|
||||||
|
|
||||||
|
let world = entity.entity.world.read().await;
|
||||||
|
|
||||||
for x in -1..=1 {
|
for x in -1..=1 {
|
||||||
for z in -1..=1 {
|
for z in -1..=1 {
|
||||||
let x = f64::from(x);
|
let x = f64::from(x);
|
||||||
@ -45,6 +48,16 @@ impl Navigator {
|
|||||||
goal.current_progress.y,
|
goal.current_progress.y,
|
||||||
goal.current_progress.z + z,
|
goal.current_progress.z + z,
|
||||||
);
|
);
|
||||||
|
let shapes = get_block_collision_shapes(
|
||||||
|
world
|
||||||
|
.get_block_state(&BlockPos(potential_pos.to_i32()))
|
||||||
|
.await
|
||||||
|
.id,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
if !shapes.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let node = Node::new(potential_pos);
|
let node = Node::new(potential_pos);
|
||||||
let cost = node.get_expense(goal.destination);
|
let cost = node.get_expense(goal.destination);
|
||||||
@ -61,6 +74,7 @@ impl Navigator {
|
|||||||
if best_move.x == 0.0 && best_move.z == 0.0 {
|
if best_move.x == 0.0 && best_move.z == 0.0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update current progress based on the best move
|
// Update current progress based on the best move
|
||||||
goal.current_progress += best_move.normalize() * goal.speed;
|
goal.current_progress += best_move.normalize() * goal.speed;
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use std::{collections::HashMap, sync::atomic::AtomicI32};
|
|||||||
|
|
||||||
use super::EntityBase;
|
use super::EntityBase;
|
||||||
use super::{Entity, EntityId, NBTStorage, effect::Effect};
|
use super::{Entity, EntityId, NBTStorage, effect::Effect};
|
||||||
|
use crate::block::loot::{LootContextParameters, LootTableExt};
|
||||||
use crate::server::Server;
|
use crate::server::Server;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
@ -268,6 +269,20 @@ impl LivingEntity {
|
|||||||
EntityStatus::PlayDeathSoundOrAddProjectileHitParticles,
|
EntityStatus::PlayDeathSoundOrAddProjectileHitParticles,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
self.drop_loot().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn drop_loot(&self) {
|
||||||
|
if let Some(loot_table) = &self.get_entity().entity_type.loot_table {
|
||||||
|
let world = self.entity.world.read().await;
|
||||||
|
let pos = self.entity.block_pos.load();
|
||||||
|
let params = LootContextParameters {
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
for stack in loot_table.get_loot(params) {
|
||||||
|
world.drop_stack(&pos, stack).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tick_move(&self, entity: &dyn EntityBase, server: &Server) {
|
async fn tick_move(&self, entity: &dyn EntityBase, server: &Server) {
|
||||||
|
|||||||
@ -51,29 +51,24 @@ impl EntityBase for MobEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn from_type(
|
pub fn from_type(
|
||||||
entity_type: EntityType,
|
entity_type: EntityType,
|
||||||
position: Vector3<f64>,
|
position: Vector3<f64>,
|
||||||
world: &Arc<World>,
|
world: &Arc<World>,
|
||||||
) -> Arc<dyn EntityBase> {
|
) -> Arc<dyn EntityBase> {
|
||||||
let entity = world.create_entity(position, entity_type);
|
let entity = world.create_entity(position, entity_type);
|
||||||
let mob = MobEntity {
|
|
||||||
|
#[allow(clippy::single_match)]
|
||||||
|
let mob = match entity_type {
|
||||||
|
EntityType::ZOMBIE => Zombie::make(entity),
|
||||||
|
// TODO
|
||||||
|
_ => MobEntity {
|
||||||
living_entity: LivingEntity::new(entity),
|
living_entity: LivingEntity::new(entity),
|
||||||
goals: Mutex::new(vec![]),
|
goals: Mutex::new(vec![]),
|
||||||
navigator: Mutex::new(Navigator::default()),
|
navigator: Mutex::new(Navigator::default()),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(clippy::single_match)]
|
|
||||||
match entity_type {
|
|
||||||
EntityType::ZOMBIE => Zombie::make(&mob).await,
|
|
||||||
// TODO
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
Arc::new(mob)
|
Arc::new(mob)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MobEntity {
|
impl MobEntity {}
|
||||||
pub async fn goal<T: Goal + 'static>(&self, goal: T) {
|
|
||||||
self.goals.lock().await.push((Arc::new(goal), false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,12 +1,29 @@
|
|||||||
use crate::entity::ai::goal::{look_at_entity::LookAtEntityGoal, target_goal::TargetGoal};
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::entity::{
|
||||||
|
Entity,
|
||||||
|
ai::{
|
||||||
|
goal::{look_at_entity::LookAtEntityGoal, target_goal::TargetGoal},
|
||||||
|
path::Navigator,
|
||||||
|
},
|
||||||
|
living::LivingEntity,
|
||||||
|
};
|
||||||
|
|
||||||
use super::MobEntity;
|
use super::MobEntity;
|
||||||
|
|
||||||
pub struct Zombie;
|
pub struct Zombie;
|
||||||
|
|
||||||
impl Zombie {
|
impl Zombie {
|
||||||
pub async fn make(mob: &MobEntity) {
|
pub fn make(entity: Entity) -> MobEntity {
|
||||||
mob.goal(LookAtEntityGoal::new(8.0)).await;
|
MobEntity {
|
||||||
mob.goal(TargetGoal::new(16.0)).await;
|
living_entity: LivingEntity::new(entity),
|
||||||
|
goals: Mutex::new(vec![
|
||||||
|
(Arc::new(LookAtEntityGoal::new(8.0)), false),
|
||||||
|
(Arc::new(TargetGoal::new(16.0)), false),
|
||||||
|
]),
|
||||||
|
navigator: Mutex::new(Navigator::default()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1623,7 +1623,7 @@ impl Player {
|
|||||||
|
|
||||||
let world = self.world().await;
|
let world = self.world().await;
|
||||||
// Create a new mob and UUID based on the spawn egg id
|
// Create a new mob and UUID based on the spawn egg id
|
||||||
let mob = mob::from_type(EntityType::from_raw(entity_type.id).unwrap(), pos, &world).await;
|
let mob = mob::from_type(EntityType::from_raw(entity_type.id).unwrap(), pos, &world);
|
||||||
|
|
||||||
// Set the rotation
|
// Set the rotation
|
||||||
mob.get_entity().set_rotation(yaw, 0.0);
|
mob.get_entity().set_rotation(yaw, 0.0);
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use pumpkin_data::block_properties::get_state_by_state_id;
|
||||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||||
|
|
||||||
use crate::{block::drop_loot, server::Server};
|
use crate::{
|
||||||
|
block::{drop_loot, loot::LootContextParameters},
|
||||||
|
server::Server,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{BlockFlags, World};
|
use super::{BlockFlags, World};
|
||||||
|
|
||||||
@ -83,7 +87,11 @@ impl Explosion {
|
|||||||
world.set_block_state(&pos, 0, BlockFlags::NOTIFY_ALL).await;
|
world.set_block_state(&pos, 0, BlockFlags::NOTIFY_ALL).await;
|
||||||
|
|
||||||
if pumpkin_block.is_none_or(|s| s.should_drop_items_on_explosion()) {
|
if pumpkin_block.is_none_or(|s| s.should_drop_items_on_explosion()) {
|
||||||
drop_loot(world, &block, &pos, false, block_state.id).await;
|
let params = LootContextParameters {
|
||||||
|
block_state: get_state_by_state_id(block_state.id),
|
||||||
|
explosion_radius: Some(self.power),
|
||||||
|
};
|
||||||
|
drop_loot(world, &block, &pos, false, params).await;
|
||||||
}
|
}
|
||||||
if let Some(pumpkin_block) = pumpkin_block {
|
if let Some(pumpkin_block) = pumpkin_block {
|
||||||
pumpkin_block.explode(&block, world, pos).await;
|
pumpkin_block.explode(&block, world, pos).await;
|
||||||
|
|||||||
@ -8,7 +8,6 @@ pub mod explosion;
|
|||||||
pub mod portal;
|
pub mod portal;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
use crate::block::BlockEvent;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
PLUGIN_MANAGER,
|
PLUGIN_MANAGER,
|
||||||
block::{self, registry::BlockRegistry},
|
block::{self, registry::BlockRegistry},
|
||||||
@ -22,6 +21,10 @@ use crate::{
|
|||||||
},
|
},
|
||||||
server::Server,
|
server::Server,
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
|
block::{BlockEvent, loot::LootContextParameters},
|
||||||
|
entity::item::ItemEntity,
|
||||||
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use border::Worldborder;
|
use border::Worldborder;
|
||||||
use bytes::{BufMut, Bytes};
|
use bytes::{BufMut, Bytes};
|
||||||
@ -77,6 +80,7 @@ use pumpkin_util::{
|
|||||||
};
|
};
|
||||||
use pumpkin_world::{
|
use pumpkin_world::{
|
||||||
BlockStateId, GENERATION_SETTINGS, GeneratorSetting, biome, block::entities::BlockEntity,
|
BlockStateId, GENERATION_SETTINGS, GeneratorSetting, biome, block::entities::BlockEntity,
|
||||||
|
item::ItemStack,
|
||||||
};
|
};
|
||||||
use pumpkin_world::{chunk::ChunkData, world::BlockAccessor};
|
use pumpkin_world::{chunk::ChunkData, world::BlockAccessor};
|
||||||
use pumpkin_world::{chunk::TickPriority, level::Level};
|
use pumpkin_world::{chunk::TickPriority, level::Level};
|
||||||
@ -1607,7 +1611,11 @@ impl World {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if !flags.contains(BlockFlags::SKIP_DROPS) {
|
if !flags.contains(BlockFlags::SKIP_DROPS) {
|
||||||
block::drop_loot(self, &broken_block, position, true, broken_state_id).await;
|
let params = LootContextParameters {
|
||||||
|
block_state: get_state_by_state_id(broken_state_id),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
block::drop_loot(self, &broken_block, position, true, params).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
match cause {
|
match cause {
|
||||||
@ -1620,6 +1628,19 @@ impl World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn drop_stack(self: &Arc<Self>, pos: &BlockPos, stack: ItemStack) {
|
||||||
|
let height = EntityType::ITEM.dimension[1] / 2.0;
|
||||||
|
let pos = Vector3::new(
|
||||||
|
f64::from(pos.0.x) + 0.5 + rand::rng().random_range(-0.25..0.25),
|
||||||
|
f64::from(pos.0.y) + 0.5 + rand::rng().random_range(-0.25..0.25) - f64::from(height),
|
||||||
|
f64::from(pos.0.z) + 0.5 + rand::rng().random_range(-0.25..0.25),
|
||||||
|
);
|
||||||
|
|
||||||
|
let entity = self.create_entity(pos, EntityType::ITEM);
|
||||||
|
let item_entity = Arc::new(ItemEntity::new(entity, stack).await);
|
||||||
|
self.spawn_entity(item_entity).await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn sync_world_event(&self, world_event: WorldEvent, position: BlockPos, data: i32) {
|
pub async fn sync_world_event(&self, world_event: WorldEvent, position: BlockPos, data: i32) {
|
||||||
self.broadcast_packet_all(&CWorldEvent::new(world_event as i32, position, data, false))
|
self.broadcast_packet_all(&CWorldEvent::new(world_event as i32, position, data, false))
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user