mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Add Entity loot
not done, but works
This commit is contained in:
@@ -9,6 +9,8 @@ use std::{
|
||||
};
|
||||
use syn::{Ident, LitInt, LitStr};
|
||||
|
||||
use crate::loot::LootTableStruct;
|
||||
|
||||
fn const_block_name_from_block_name(block: &str) -> String {
|
||||
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)]
|
||||
pub struct Block {
|
||||
pub id: u16,
|
||||
@@ -1404,21 +926,19 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
pub fn get_block_collision_shapes(state_id: u16) -> Option<Vec<CollisionShape>> {
|
||||
let state = get_state_by_state_id(state_id)?;
|
||||
let mut shapes: Vec<CollisionShape> = vec![];
|
||||
for i in 0..state.collision_shapes.len() {
|
||||
let shape = &COLLISION_SHAPES[state.collision_shapes[i] as usize];
|
||||
shapes.push(*shape);
|
||||
}
|
||||
let shapes: Vec<CollisionShape> = state.collision_shapes
|
||||
.iter()
|
||||
.map(|&id| COLLISION_SHAPES[id as usize])
|
||||
.collect();
|
||||
Some(shapes)
|
||||
}
|
||||
|
||||
pub fn get_block_outline_shapes(state_id: u16) -> Option<Vec<CollisionShape>> {
|
||||
let state = get_state_by_state_id(state_id)?;
|
||||
let mut shapes: Vec<CollisionShape> = vec![];
|
||||
for i in 0..state.outline_shapes.len() {
|
||||
let shape = &COLLISION_SHAPES[state.outline_shapes[i] as usize];
|
||||
shapes.push(*shape);
|
||||
}
|
||||
let mut shapes: Vec<CollisionShape> = state.outline_shapes
|
||||
.iter()
|
||||
.map(|&id| COLLISION_SHAPES[id as usize])
|
||||
.collect();
|
||||
let block = get_block_by_state_id(state_id)?;
|
||||
if block.properties(state.id).and_then(|properties| {
|
||||
properties
|
||||
|
||||
@@ -14,6 +14,7 @@ mod entity_type;
|
||||
mod fluid;
|
||||
mod game_event;
|
||||
mod item;
|
||||
pub mod loot;
|
||||
mod message_type;
|
||||
mod noise_parameter;
|
||||
mod noise_router;
|
||||
|
||||
@@ -6,11 +6,14 @@ use quote::{ToTokens, format_ident, quote};
|
||||
use serde::Deserialize;
|
||||
use syn::LitInt;
|
||||
|
||||
use crate::loot::LootTableStruct;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EntityType {
|
||||
pub id: u16,
|
||||
pub max_health: Option<f32>,
|
||||
pub attackable: Option<bool>,
|
||||
pub loot_table: Option<LootTableStruct>,
|
||||
pub summonable: bool,
|
||||
pub fire_immune: bool,
|
||||
pub dimension: [f32; 2],
|
||||
@@ -79,6 +82,14 @@ impl ToTokens for NamedEntityType<'_> {
|
||||
let dimension0 = entity.dimension[0];
|
||||
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! {
|
||||
EntityType {
|
||||
id: #id,
|
||||
@@ -86,6 +97,7 @@ impl ToTokens for NamedEntityType<'_> {
|
||||
attackable: #attackable,
|
||||
summonable: #summonable,
|
||||
fire_immune: #fire_immune,
|
||||
loot_table: #loot_table,
|
||||
dimension: [#dimension0, #dimension1], // Correctly construct the array
|
||||
eye_height: #eye_height,
|
||||
spawn_restriction: #spawn_restriction,
|
||||
@@ -126,6 +138,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
});
|
||||
}
|
||||
quote! {
|
||||
use pumpkin_util::loot_table::*;
|
||||
use pumpkin_util::HeightMap;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@@ -135,6 +148,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
pub attackable: Option<bool>,
|
||||
pub summonable: bool,
|
||||
pub fire_immune: bool,
|
||||
pub loot_table: Option<LootTable>,
|
||||
pub dimension: [f32; 2],
|
||||
pub eye_height: f32,
|
||||
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 r#type: LootTableType,
|
||||
pub random_sequence: Option<&'static str>,
|
||||
pub pools: Option<&'static [LootPool]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct LootPool {
|
||||
pub entries: &'static [LootPoolEntry],
|
||||
pub rolls: f32,
|
||||
pub rolls: LootNumberProviderTypes,
|
||||
pub bonus_rolls: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct ItemEntry {
|
||||
pub name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct AlternativeEntry {
|
||||
pub children: &'static [LootPoolEntry],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum LootPoolEntryTypes {
|
||||
Empty,
|
||||
Item(ItemEntry),
|
||||
@@ -34,7 +40,7 @@ pub enum LootPoolEntryTypes {
|
||||
Group,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum LootCondition {
|
||||
Inverted,
|
||||
AnyOf,
|
||||
@@ -60,18 +66,22 @@ pub enum LootCondition {
|
||||
EnchantmentActiveCheck,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct LootFunction {
|
||||
pub content: LootFunctionTypes,
|
||||
pub conditions: Option<&'static [LootCondition]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum LootFunctionTypes {
|
||||
SetCount {
|
||||
count: LootFunctionNumberProvider,
|
||||
add: bool,
|
||||
},
|
||||
EnchantedCountIncrease,
|
||||
FurnaceSmelt,
|
||||
SetPotion,
|
||||
SetOminousBottleAmplifier,
|
||||
LimitCount {
|
||||
min: Option<f32>,
|
||||
max: Option<f32>,
|
||||
@@ -92,32 +102,131 @@ pub enum LootFunctionTypes {
|
||||
ExplosionDecay,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum LootFunctionNumberProvider {
|
||||
Constant { value: f32 },
|
||||
Uniform { min: f32, max: f32 },
|
||||
Binomial { n: f32, p: f32 },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum LootFunctionBonusParameter {
|
||||
Multiplier { bonus_multiplier: i32 },
|
||||
Probability { extra: i32, probability: f32 },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct LootPoolEntry {
|
||||
pub content: LootPoolEntryTypes,
|
||||
pub conditions: Option<&'static [LootCondition]>,
|
||||
pub functions: Option<&'static [LootFunction]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum LootTableType {
|
||||
/// Nothing will be dropped
|
||||
Empty,
|
||||
/// A block will be dropped
|
||||
Entity,
|
||||
Block,
|
||||
/// An item will be dropped
|
||||
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_util::loot_table::{
|
||||
LootCondition, LootFunctionNumberProvider, LootFunctionTypes, LootPoolEntry,
|
||||
LootPoolEntryTypes, LootTable,
|
||||
use pumpkin_data::{Block, BlockState, block_properties::get_block_by_state_id, item::Item};
|
||||
use pumpkin_util::{
|
||||
loot_table::{
|
||||
LootCondition, LootFunctionNumberProvider, LootFunctionTypes, LootPoolEntry,
|
||||
LootPoolEntryTypes, LootTable,
|
||||
},
|
||||
random::{RandomGenerator, xoroshiro128::Xoroshiro},
|
||||
};
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use rand::Rng;
|
||||
|
||||
pub(super) trait LootTableExt {
|
||||
fn get_loot(&self, block_props: &[(String, String)]) -> Vec<ItemStack>;
|
||||
#[derive(Default)]
|
||||
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 {
|
||||
fn get_loot(&self, block_props: &[(String, String)]) -> Vec<ItemStack> {
|
||||
fn get_loot(&self, params: LootContextParameters) -> Vec<ItemStack> {
|
||||
let mut stacks = Vec::new();
|
||||
|
||||
if let Some(pools) = self.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 entry in pool.entries {
|
||||
if let Some(loot) = entry.get_loot(block_props) {
|
||||
if let Some(loot) = entry.get_loot(¶ms) {
|
||||
stacks.extend(loot);
|
||||
}
|
||||
}
|
||||
@@ -33,23 +47,23 @@ impl LootTableExt for LootTable {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 !conditions.iter().all(|cond| cond.is_fulfilled(block_props)) {
|
||||
if !conditions.iter().all(|cond| cond.is_fulfilled(params)) {
|
||||
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 {
|
||||
for function in functions {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -94,6 +108,10 @@ impl LootPoolEntryExt for LootPoolEntry {
|
||||
block: _,
|
||||
properties: _,
|
||||
}
|
||||
| LootFunctionTypes::EnchantedCountIncrease
|
||||
| LootFunctionTypes::SetOminousBottleAmplifier
|
||||
| LootFunctionTypes::SetPotion
|
||||
| LootFunctionTypes::FurnaceSmelt
|
||||
| LootFunctionTypes::ExplosionDecay => {
|
||||
// TODO: shouldnt crash here but needs to be implemented someday
|
||||
}
|
||||
@@ -106,11 +124,11 @@ impl LootPoolEntryExt for LootPoolEntry {
|
||||
}
|
||||
|
||||
trait LootPoolEntryTypesExt {
|
||||
fn get_stacks(&self, block_props: &[(String, String)]) -> Vec<ItemStack>;
|
||||
fn get_stacks(&self, params: &LootContextParameters) -> Vec<ItemStack>;
|
||||
}
|
||||
|
||||
impl LootPoolEntryTypesExt for LootPoolEntryTypes {
|
||||
fn get_stacks(&self, block_props: &[(String, String)]) -> Vec<ItemStack> {
|
||||
fn get_stacks(&self, params: &LootContextParameters) -> Vec<ItemStack> {
|
||||
match self {
|
||||
Self::Empty => Vec::new(),
|
||||
Self::Item(item_entry) => {
|
||||
@@ -123,7 +141,7 @@ impl LootPoolEntryTypesExt for LootPoolEntryTypes {
|
||||
Self::Alternatives(alternative_entry) => alternative_entry
|
||||
.children
|
||||
.iter()
|
||||
.filter_map(|entry| entry.get_loot(block_props))
|
||||
.filter_map(|entry| entry.get_loot(params))
|
||||
.flatten()
|
||||
.collect(),
|
||||
Self::Sequence => todo!(),
|
||||
@@ -133,20 +151,34 @@ impl LootPoolEntryTypesExt for LootPoolEntryTypes {
|
||||
}
|
||||
|
||||
trait LootConditionExt {
|
||||
fn is_fulfilled(&self, block_props: &[(String, String)]) -> bool;
|
||||
fn is_fulfilled(&self, params: &LootContextParameters) -> bool;
|
||||
}
|
||||
|
||||
impl LootConditionExt for LootCondition {
|
||||
// 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 {
|
||||
Self::SurvivesExplosion => true,
|
||||
Self::SurvivesExplosion => {
|
||||
if let Some(radius) = params.explosion_radius {
|
||||
return rand::rng().random::<f32>() <= 1.0 / radius;
|
||||
}
|
||||
true
|
||||
}
|
||||
Self::BlockStateProperty {
|
||||
block: _,
|
||||
properties,
|
||||
} => properties
|
||||
.iter()
|
||||
.all(|(key, value)| block_props.iter().any(|(k, v)| k == key && v == value)),
|
||||
} => {
|
||||
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()
|
||||
.all(|(key, value)| props.iter().any(|(k, v)| k == key && v == value));
|
||||
}
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,19 +65,15 @@ use fluid::lava::FlowingLava;
|
||||
use fluid::water::FlowingWater;
|
||||
use loot::LootTableExt;
|
||||
use pumpkin_data::block_properties::Integer0To15;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_util::random::{RandomGenerator, get_seed, xoroshiro128::Xoroshiro};
|
||||
use pumpkin_world::BlockStateId;
|
||||
use pumpkin_world::item::ItemStack;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::block::blocks::plant::roots::RootsBlock;
|
||||
use crate::block::loot::LootContextParameters;
|
||||
use crate::block::registry::BlockRegistry;
|
||||
use crate::entity::item::ItemEntity;
|
||||
use crate::world::World;
|
||||
use crate::{block::blocks::crafting_table::CraftingTableBlock, entity::player::Player};
|
||||
use crate::{block::blocks::jukebox::JukeboxBlock, entity::experience_orb::ExperienceOrbEntity};
|
||||
@@ -85,7 +81,7 @@ use std::sync::Arc;
|
||||
|
||||
pub mod blocks;
|
||||
mod fluid;
|
||||
mod loot;
|
||||
pub mod loot;
|
||||
pub mod pumpkin_block;
|
||||
pub mod pumpkin_fluid;
|
||||
pub mod registry;
|
||||
@@ -192,14 +188,11 @@ pub async fn drop_loot(
|
||||
block: &Block,
|
||||
pos: &BlockPos,
|
||||
experience: bool,
|
||||
state_id: BlockStateId,
|
||||
params: LootContextParameters,
|
||||
) {
|
||||
if let Some(loot_table) = &block.loot_table {
|
||||
let props =
|
||||
Block::properties(block, state_id).map_or_else(Vec::new, |props| props.to_props());
|
||||
|
||||
for stack in loot_table.get_loot(&props) {
|
||||
drop_stack(world, pos, stack).await;
|
||||
for stack in loot_table.get_loot(params) {
|
||||
world.drop_stack(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 {
|
||||
let hardness = state.hardness;
|
||||
#[expect(clippy::float_cmp)]
|
||||
|
||||
@@ -57,7 +57,7 @@ impl CommandExecutor for Executor {
|
||||
(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;
|
||||
|
||||
sender
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use pumpkin_data::block_properties::get_block_collision_shapes;
|
||||
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;
|
||||
|
||||
@@ -36,6 +37,8 @@ impl Navigator {
|
||||
let mut best_move = Vector3::new(0.0, 0.0, 0.0);
|
||||
let mut lowest_cost = f64::MAX;
|
||||
|
||||
let world = entity.entity.world.read().await;
|
||||
|
||||
for x in -1..=1 {
|
||||
for z in -1..=1 {
|
||||
let x = f64::from(x);
|
||||
@@ -45,6 +48,16 @@ impl Navigator {
|
||||
goal.current_progress.y,
|
||||
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 cost = node.get_expense(goal.destination);
|
||||
@@ -61,6 +74,7 @@ impl Navigator {
|
||||
if best_move.x == 0.0 && best_move.z == 0.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update current progress based on the best move
|
||||
goal.current_progress += best_move.normalize() * goal.speed;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::{collections::HashMap, sync::atomic::AtomicI32};
|
||||
|
||||
use super::EntityBase;
|
||||
use super::{Entity, EntityId, NBTStorage, effect::Effect};
|
||||
use crate::block::loot::{LootContextParameters, LootTableExt};
|
||||
use crate::server::Server;
|
||||
use async_trait::async_trait;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
@@ -268,6 +269,20 @@ impl LivingEntity {
|
||||
EntityStatus::PlayDeathSoundOrAddProjectileHitParticles,
|
||||
)
|
||||
.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) {
|
||||
|
||||
@@ -51,29 +51,24 @@ impl EntityBase for MobEntity {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn from_type(
|
||||
pub fn from_type(
|
||||
entity_type: EntityType,
|
||||
position: Vector3<f64>,
|
||||
world: &Arc<World>,
|
||||
) -> Arc<dyn EntityBase> {
|
||||
let entity = world.create_entity(position, entity_type);
|
||||
let mob = MobEntity {
|
||||
living_entity: LivingEntity::new(entity),
|
||||
goals: Mutex::new(vec![]),
|
||||
navigator: Mutex::new(Navigator::default()),
|
||||
};
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
match entity_type {
|
||||
EntityType::ZOMBIE => Zombie::make(&mob).await,
|
||||
let mob = match entity_type {
|
||||
EntityType::ZOMBIE => Zombie::make(entity),
|
||||
// TODO
|
||||
_ => (),
|
||||
}
|
||||
_ => MobEntity {
|
||||
living_entity: LivingEntity::new(entity),
|
||||
goals: Mutex::new(vec![]),
|
||||
navigator: Mutex::new(Navigator::default()),
|
||||
},
|
||||
};
|
||||
Arc::new(mob)
|
||||
}
|
||||
|
||||
impl MobEntity {
|
||||
pub async fn goal<T: Goal + 'static>(&self, goal: T) {
|
||||
self.goals.lock().await.push((Arc::new(goal), false));
|
||||
}
|
||||
}
|
||||
impl MobEntity {}
|
||||
|
||||
@@ -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;
|
||||
|
||||
pub struct Zombie;
|
||||
|
||||
impl Zombie {
|
||||
pub async fn make(mob: &MobEntity) {
|
||||
mob.goal(LookAtEntityGoal::new(8.0)).await;
|
||||
mob.goal(TargetGoal::new(16.0)).await;
|
||||
pub fn make(entity: Entity) -> MobEntity {
|
||||
MobEntity {
|
||||
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;
|
||||
// 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
|
||||
mob.get_entity().set_rotation(yaw, 0.0);
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::block_properties::get_state_by_state_id;
|
||||
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};
|
||||
|
||||
@@ -83,7 +87,11 @@ impl Explosion {
|
||||
world.set_block_state(&pos, 0, BlockFlags::NOTIFY_ALL).await;
|
||||
|
||||
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 {
|
||||
pumpkin_block.explode(&block, world, pos).await;
|
||||
|
||||
@@ -8,7 +8,6 @@ pub mod explosion;
|
||||
pub mod portal;
|
||||
pub mod time;
|
||||
|
||||
use crate::block::BlockEvent;
|
||||
use crate::{
|
||||
PLUGIN_MANAGER,
|
||||
block::{self, registry::BlockRegistry},
|
||||
@@ -22,6 +21,10 @@ use crate::{
|
||||
},
|
||||
server::Server,
|
||||
};
|
||||
use crate::{
|
||||
block::{BlockEvent, loot::LootContextParameters},
|
||||
entity::item::ItemEntity,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use border::Worldborder;
|
||||
use bytes::{BufMut, Bytes};
|
||||
@@ -77,6 +80,7 @@ use pumpkin_util::{
|
||||
};
|
||||
use pumpkin_world::{
|
||||
BlockStateId, GENERATION_SETTINGS, GeneratorSetting, biome, block::entities::BlockEntity,
|
||||
item::ItemStack,
|
||||
};
|
||||
use pumpkin_world::{chunk::ChunkData, world::BlockAccessor};
|
||||
use pumpkin_world::{chunk::TickPriority, level::Level};
|
||||
@@ -1607,7 +1611,11 @@ impl World {
|
||||
);
|
||||
|
||||
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 {
|
||||
@@ -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) {
|
||||
self.broadcast_packet_all(&CWorldEvent::new(world_event as i32, position, data, false))
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user