Add Entity loot

not done, but works
This commit is contained in:
Alexander Medvedev
2025-06-21 16:38:05 +02:00
parent 9004d307bf
commit f16d4a6247
15 changed files with 817 additions and 580 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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
View 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);
}
}

View File

@@ -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,
}
}
}

View File

@@ -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(&params) {
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) = &params.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,
}
}

View File

@@ -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)]

View File

@@ -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

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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 {}

View File

@@ -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()),
}
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;