chore: expand loot table
This commit is contained in:
parent
322b7af6ae
commit
03a90a40b1
@ -548,12 +548,15 @@ impl TreeNodePosition {
|
|||||||
) {
|
) {
|
||||||
inner_left = next_inner_left;
|
inner_left = next_inner_left;
|
||||||
inner_right = next_inner_right;
|
inner_right = next_inner_right;
|
||||||
outer_left = Self::previous_or_thread(nodes, outer_left).expect("Tree invariant broken");
|
outer_left =
|
||||||
|
Self::previous_or_thread(nodes, outer_left).expect("Tree invariant broken");
|
||||||
outer_right = Self::next_or_thread(nodes, outer_right).expect("Tree invariant broken");
|
outer_right = Self::next_or_thread(nodes, outer_right).expect("Tree invariant broken");
|
||||||
|
|
||||||
nodes[outer_right].ancestor = idx;
|
nodes[outer_right].ancestor = idx;
|
||||||
|
|
||||||
let shift = (nodes[inner_left].y + shift_inner_left) - (nodes[inner_right].y + shift_inner_right) + 1.0;
|
let shift = (nodes[inner_left].y + shift_inner_left)
|
||||||
|
- (nodes[inner_right].y + shift_inner_right)
|
||||||
|
+ 1.0;
|
||||||
if shift > 0.0 {
|
if shift > 0.0 {
|
||||||
let ancestor_idx = Self::get_ancestor(nodes, inner_left, idx, default_ancestor);
|
let ancestor_idx = Self::get_ancestor(nodes, inner_left, idx, default_ancestor);
|
||||||
Self::move_subtree(nodes, ancestor_idx, idx, shift);
|
Self::move_subtree(nodes, ancestor_idx, idx, shift);
|
||||||
@ -659,7 +662,7 @@ pub struct AdvancementStruct {
|
|||||||
pub sends_telemetry: bool,
|
pub sends_telemetry: bool,
|
||||||
pub requirements: Vec<Vec<String>>,
|
pub requirements: Vec<Vec<String>>,
|
||||||
#[serde(deserialize_with = "deserialize_first_key")]
|
#[serde(deserialize_with = "deserialize_first_key")]
|
||||||
pub criteria : Vec<String>,
|
pub criteria: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_first_key<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
fn deserialize_first_key<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
||||||
@ -846,10 +849,7 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
let advancement_tree = quote! {
|
let advancement_tree = quote! {
|
||||||
pub static ADVANCEMENT_TREE : LazyLock<AdvancementTree> = #tree;
|
pub static ADVANCEMENT_TREE : LazyLock<AdvancementTree> = #tree;
|
||||||
};
|
};
|
||||||
let advancements_holder = tree
|
let advancements_holder = tree.nodes_vector.into_iter().map(|node| node.value);
|
||||||
.nodes_vector
|
|
||||||
.into_iter()
|
|
||||||
.map(|node| node.value);
|
|
||||||
for AdvancementHolder(identifier, advancement) in advancements_holder {
|
for AdvancementHolder(identifier, advancement) in advancements_holder {
|
||||||
let raw_name = identifier.path();
|
let raw_name = identifier.path();
|
||||||
let format_name = format_ident!("{}", raw_name.to_shouty_snake_case());
|
let format_name = format_ident!("{}", raw_name.to_shouty_snake_case());
|
||||||
|
|||||||
@ -29,7 +29,9 @@ pub fn build() -> TokenStream {
|
|||||||
(quote! { Int }, quote! { i64 }, quote! { #i })
|
(quote! { Int }, quote! { i64 }, quote! { #i })
|
||||||
}
|
}
|
||||||
Value::Object(obj) => {
|
Value::Object(obj) => {
|
||||||
let default_val = obj.get("default").expect("Object game rule missing default value");
|
let default_val = obj
|
||||||
|
.get("default")
|
||||||
|
.expect("Object game rule missing default value");
|
||||||
match default_val {
|
match default_val {
|
||||||
Value::Number(n) if n.is_i64() => {
|
Value::Number(n) if n.is_i64() => {
|
||||||
let i = n.as_i64().unwrap();
|
let i = n.as_i64().unwrap();
|
||||||
|
|||||||
@ -281,9 +281,8 @@ impl ToTokens for LootPoolEntryTypesStruct {
|
|||||||
Self::LootTable(entry) => {
|
Self::LootTable(entry) => {
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::LootTable(#entry) });
|
tokens.extend(quote! { LootPoolEntryTypes::LootTable(#entry) });
|
||||||
}
|
}
|
||||||
Self::Dynamic(_entry) => {
|
Self::Dynamic(entry) => {
|
||||||
// TODO
|
tokens.extend(quote! { LootPoolEntryTypes::Dynamic(#entry) });
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::Dynamic });
|
|
||||||
}
|
}
|
||||||
Self::Tag(entry) => {
|
Self::Tag(entry) => {
|
||||||
tokens.extend(quote! { LootPoolEntryTypes::Tag(#entry) });
|
tokens.extend(quote! { LootPoolEntryTypes::Tag(#entry) });
|
||||||
@ -662,13 +661,17 @@ pub enum LootFunctionTypesStruct {
|
|||||||
},
|
},
|
||||||
/// Increases count based on the level of a relevant enchantment.
|
/// Increases count based on the level of a relevant enchantment.
|
||||||
#[serde(rename = "minecraft:enchanted_count_increase")]
|
#[serde(rename = "minecraft:enchanted_count_increase")]
|
||||||
EnchantedCountIncrease,
|
EnchantedCountIncrease {
|
||||||
|
enchantment: String,
|
||||||
|
count: LootFunctionNumberProviderStruct,
|
||||||
|
limit: Option<f32>,
|
||||||
|
},
|
||||||
/// Smelts the item as if processed in a furnace.
|
/// Smelts the item as if processed in a furnace.
|
||||||
#[serde(rename = "minecraft:furnace_smelt")]
|
#[serde(rename = "minecraft:furnace_smelt")]
|
||||||
FurnaceSmelt,
|
FurnaceSmelt,
|
||||||
/// Sets the potion type on the item.
|
/// Sets the potion type on the item.
|
||||||
#[serde(rename = "minecraft:set_potion")]
|
#[serde(rename = "minecraft:set_potion")]
|
||||||
SetPotion,
|
SetPotion { id: String },
|
||||||
/// Sets the amplifier on an ominous bottle item.
|
/// Sets the amplifier on an ominous bottle item.
|
||||||
#[serde(rename = "minecraft:set_ominous_bottle_amplifier")]
|
#[serde(rename = "minecraft:set_ominous_bottle_amplifier")]
|
||||||
SetOminousBottleAmplifier,
|
SetOminousBottleAmplifier,
|
||||||
@ -724,11 +727,21 @@ impl ToTokens for LootFunctionTypesStruct {
|
|||||||
Self::FurnaceSmelt => {
|
Self::FurnaceSmelt => {
|
||||||
quote! { LootFunctionTypes::FurnaceSmelt }
|
quote! { LootFunctionTypes::FurnaceSmelt }
|
||||||
}
|
}
|
||||||
Self::SetPotion => {
|
Self::SetPotion { id } => {
|
||||||
quote! { LootFunctionTypes::SetPotion }
|
let i = LitStr::new(id, Span::call_site());
|
||||||
|
quote! { LootFunctionTypes::SetPotion { id: #i } }
|
||||||
}
|
}
|
||||||
Self::EnchantedCountIncrease => {
|
Self::EnchantedCountIncrease {
|
||||||
quote! { LootFunctionTypes::EnchantedCountIncrease }
|
enchantment,
|
||||||
|
count,
|
||||||
|
limit,
|
||||||
|
} => {
|
||||||
|
let e = LitStr::new(enchantment, Span::call_site());
|
||||||
|
let c = count.to_token_stream();
|
||||||
|
let l = limit
|
||||||
|
.map(|val| quote! { Some(#val) })
|
||||||
|
.unwrap_or(quote! { None });
|
||||||
|
quote! { LootFunctionTypes::EnchantedCountIncrease { enchantment: #e, count: #c, limit: #l } }
|
||||||
}
|
}
|
||||||
Self::LimitCount { limit } => {
|
Self::LimitCount { limit } => {
|
||||||
let min = if let Some(min) = limit.min {
|
let min = if let Some(min) = limit.min {
|
||||||
|
|||||||
@ -3,11 +3,11 @@
|
|||||||
use crate::attributes::Attributes;
|
use crate::attributes::Attributes;
|
||||||
use crate::data_component::DataComponent;
|
use crate::data_component::DataComponent;
|
||||||
use crate::data_component::DataComponent::{
|
use crate::data_component::DataComponent::{
|
||||||
AttributeModifiers, BlockEntityData, BlocksAttacks, BundleContents, ChargedProjectiles,
|
AttributeModifiers, BlockEntityData, BlockState, BlocksAttacks, BundleContents,
|
||||||
Consumable, Container, CustomData, CustomName, Damage, DamageResistant, DeathProtection,
|
ChargedProjectiles, Consumable, Container, CustomData, CustomName, Damage, DamageResistant,
|
||||||
Enchantable, Enchantments, Equippable, FireworkExplosion, Fireworks, Food, ItemModel, ItemName,
|
DeathProtection, Enchantable, Enchantments, Equippable, FireworkExplosion, Fireworks, Food,
|
||||||
JukeboxPlayable, MapId, MaxDamage, MaxStackSize, PotionContents, StoredEnchantments, Tool,
|
ItemModel, ItemName, JukeboxPlayable, MapId, MaxDamage, MaxStackSize, OminousBottleAmplifier,
|
||||||
Unbreakable, UseCooldown, Weapon,
|
PotionContents, StoredEnchantments, Tool, Unbreakable, UseCooldown, Weapon,
|
||||||
};
|
};
|
||||||
use crate::effect::{self, StatusEffect};
|
use crate::effect::{self, StatusEffect};
|
||||||
use crate::entity_type::EntityType;
|
use crate::entity_type::EntityType;
|
||||||
@ -29,6 +29,7 @@ use std::error::Error;
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
collections::hash_map::DefaultHasher,
|
collections::hash_map::DefaultHasher,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
};
|
};
|
||||||
@ -81,6 +82,10 @@ pub fn read_data(id: DataComponent, data: &NbtTag) -> Option<Box<dyn DataCompone
|
|||||||
DataComponent::WritableBookContent => {
|
DataComponent::WritableBookContent => {
|
||||||
Some(WritableBookContentImpl::read_data(data)?.to_dyn())
|
Some(WritableBookContentImpl::read_data(data)?.to_dyn())
|
||||||
}
|
}
|
||||||
|
DataComponent::OminousBottleAmplifier => {
|
||||||
|
Some(OminousBottleAmplifierImpl::read_data(data)?.to_dyn())
|
||||||
|
}
|
||||||
|
DataComponent::BlockState => Some(BlockStateImpl::read_data(data)?.to_dyn()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2062,7 +2067,23 @@ pub struct InstrumentImpl;
|
|||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct ProvidesTrimMaterialImpl;
|
pub struct ProvidesTrimMaterialImpl;
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct OminousBottleAmplifierImpl;
|
pub struct OminousBottleAmplifierImpl {
|
||||||
|
pub amplifier: i32,
|
||||||
|
}
|
||||||
|
impl OminousBottleAmplifierImpl {
|
||||||
|
fn read_data(data: &NbtTag) -> Option<Self> {
|
||||||
|
data.extract_int().map(|amplifier| Self { amplifier })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DataComponentImpl for OminousBottleAmplifierImpl {
|
||||||
|
fn write_data(&self) -> NbtTag {
|
||||||
|
NbtTag::Int(self.amplifier)
|
||||||
|
}
|
||||||
|
fn get_hash(&self) -> i32 {
|
||||||
|
get_i32_hash(self.amplifier) as i32
|
||||||
|
}
|
||||||
|
default_impl!(OminousBottleAmplifier);
|
||||||
|
}
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct JukeboxPlayableImpl {
|
pub struct JukeboxPlayableImpl {
|
||||||
pub song: &'static str,
|
pub song: &'static str,
|
||||||
@ -2311,8 +2332,58 @@ impl ContainerImpl {
|
|||||||
impl DataComponentImpl for ContainerImpl {
|
impl DataComponentImpl for ContainerImpl {
|
||||||
default_impl!(Container);
|
default_impl!(Container);
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BlockStateImpl;
|
pub struct BlockStateImpl {
|
||||||
|
pub properties: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
impl PartialEq for BlockStateImpl {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.properties == other.properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Eq for BlockStateImpl {}
|
||||||
|
impl std::hash::Hash for BlockStateImpl {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
let mut keys: Vec<&String> = self.properties.keys().collect();
|
||||||
|
keys.sort();
|
||||||
|
for key in keys {
|
||||||
|
key.hash(state);
|
||||||
|
self.properties.get(key).hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BlockStateImpl {
|
||||||
|
fn read_data(data: &NbtTag) -> Option<Self> {
|
||||||
|
let compound = data.extract_compound()?;
|
||||||
|
let mut properties = HashMap::new();
|
||||||
|
for (key, val) in compound.child_tags.iter() {
|
||||||
|
if let Some(s) = val.extract_string() {
|
||||||
|
properties.insert(key.to_string(), s.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Self { properties })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DataComponentImpl for BlockStateImpl {
|
||||||
|
fn write_data(&self) -> NbtTag {
|
||||||
|
let mut compound = NbtCompound::new();
|
||||||
|
for (k, v) in &self.properties {
|
||||||
|
compound.put_string(k, v.clone());
|
||||||
|
}
|
||||||
|
NbtTag::Compound(compound)
|
||||||
|
}
|
||||||
|
fn get_hash(&self) -> i32 {
|
||||||
|
let mut digest = Digest::new(Crc32Iscsi);
|
||||||
|
let mut keys: Vec<&String> = self.properties.keys().collect();
|
||||||
|
keys.sort();
|
||||||
|
for key in keys {
|
||||||
|
digest.update(key.as_bytes());
|
||||||
|
digest.update(self.properties.get(key).unwrap().as_bytes());
|
||||||
|
}
|
||||||
|
digest.finalize() as i32
|
||||||
|
}
|
||||||
|
default_impl!(BlockState);
|
||||||
|
}
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct BeesImpl;
|
pub struct BeesImpl;
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
|
|||||||
@ -532782,7 +532782,9 @@ impl Block {
|
|||||||
content: LootPoolEntryTypes::Alternatives(AlternativeEntry {
|
content: LootPoolEntryTypes::Alternatives(AlternativeEntry {
|
||||||
children: &[
|
children: &[
|
||||||
LootPoolEntry {
|
LootPoolEntry {
|
||||||
content: LootPoolEntryTypes::Dynamic,
|
content: LootPoolEntryTypes::Dynamic(DynamicEntry {
|
||||||
|
name: "minecraft:sherds",
|
||||||
|
}),
|
||||||
weight: 1i32,
|
weight: 1i32,
|
||||||
quality: 0i32,
|
quality: 0i32,
|
||||||
conditions: Some(&[LootCondition::BlockStateProperty {
|
conditions: Some(&[LootCondition::BlockStateProperty {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -67,12 +67,17 @@ pub struct LootTableEntry {
|
|||||||
pub value: &'static str,
|
pub value: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DynamicEntry {
|
||||||
|
pub name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum LootPoolEntryTypes {
|
pub enum LootPoolEntryTypes {
|
||||||
Empty,
|
Empty,
|
||||||
Item(ItemEntry),
|
Item(ItemEntry),
|
||||||
LootTable(LootTableEntry),
|
LootTable(LootTableEntry),
|
||||||
Dynamic,
|
Dynamic(DynamicEntry),
|
||||||
Tag(TagEntry),
|
Tag(TagEntry),
|
||||||
Alternatives(AlternativeEntry),
|
Alternatives(AlternativeEntry),
|
||||||
Sequence(SequenceEntry),
|
Sequence(SequenceEntry),
|
||||||
@ -173,11 +178,15 @@ pub enum LootFunctionTypes {
|
|||||||
add: bool,
|
add: bool,
|
||||||
},
|
},
|
||||||
/// Increases the count of items based on the item's enchantments.
|
/// Increases the count of items based on the item's enchantments.
|
||||||
EnchantedCountIncrease,
|
EnchantedCountIncrease {
|
||||||
|
enchantment: &'static str,
|
||||||
|
count: LootFunctionNumberProvider,
|
||||||
|
limit: Option<f32>,
|
||||||
|
},
|
||||||
/// Smelts the item as if it passed through a furnace.
|
/// Smelts the item as if it passed through a furnace.
|
||||||
FurnaceSmelt,
|
FurnaceSmelt,
|
||||||
/// Sets the potion type for potion items.
|
/// Sets the potion type for potion items.
|
||||||
SetPotion,
|
SetPotion { id: &'static str },
|
||||||
/// Sets the amplifier for ominous bottles.
|
/// Sets the amplifier for ominous bottles.
|
||||||
SetOminousBottleAmplifier,
|
SetOminousBottleAmplifier,
|
||||||
/// Limits the count of items to a minimum and/or maximum value.
|
/// Limits the count of items to a minimum and/or maximum value.
|
||||||
|
|||||||
@ -1317,6 +1317,27 @@ impl LivingEntity {
|
|||||||
|
|
||||||
// Plays the death sound
|
// Plays the death sound
|
||||||
world.send_entity_status(&self.entity, EntityStatus::Death);
|
world.send_entity_status(&self.entity, EntityStatus::Death);
|
||||||
|
let tool = if let Some(cause_ent) = cause {
|
||||||
|
if let Some(player) = cause_ent
|
||||||
|
.cast_any()
|
||||||
|
.downcast_ref::<crate::entity::player::Player>()
|
||||||
|
{
|
||||||
|
let hand_stack = player
|
||||||
|
.inventory
|
||||||
|
.get_stack_in_hand(pumpkin_util::Hand::Right)
|
||||||
|
.await;
|
||||||
|
let stack_guard = hand_stack.lock().await;
|
||||||
|
(stack_guard.item_count > 0).then(|| stack_guard.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_raining = world.is_raining().await;
|
||||||
|
let is_thundering = world.is_thundering().await;
|
||||||
|
|
||||||
let params = LootContextParameters {
|
let params = LootContextParameters {
|
||||||
killed_by_player: cause.map(|c| c.get_entity().entity_type == &EntityType::PLAYER),
|
killed_by_player: cause.map(|c| c.get_entity().entity_type == &EntityType::PLAYER),
|
||||||
this_entity: Some(self.entity.entity_type),
|
this_entity: Some(self.entity.entity_type),
|
||||||
@ -1325,11 +1346,14 @@ impl LivingEntity {
|
|||||||
position: Some(self.entity.pos.load()),
|
position: Some(self.entity.pos.load()),
|
||||||
world_time: world.level_info.load().day_time as u64,
|
world_time: world.level_info.load().day_time as u64,
|
||||||
damage_type: Some(damage_type),
|
damage_type: Some(damage_type),
|
||||||
|
tool,
|
||||||
|
is_raining: Some(is_raining),
|
||||||
|
is_thundering: Some(is_thundering),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Drop loot
|
// Drop loot
|
||||||
self.drop_loot(params).await;
|
self.drop_loot(params.clone()).await;
|
||||||
|
|
||||||
// Award experience
|
// Award experience
|
||||||
if params.killed_by_player.unwrap_or(false)
|
if params.killed_by_player.unwrap_or(false)
|
||||||
|
|||||||
@ -89,7 +89,14 @@ impl VehicleEntity {
|
|||||||
|
|
||||||
if entity_drops && let Some(loot_table) = &self.entity.entity_type.loot_table {
|
if entity_drops && let Some(loot_table) = &self.entity.entity_type.loot_table {
|
||||||
let pos = self.entity.block_pos.load();
|
let pos = self.entity.block_pos.load();
|
||||||
let params = LootContextParameters::default();
|
let is_raining = world.is_raining().await;
|
||||||
|
let is_thundering = world.is_thundering().await;
|
||||||
|
let params = LootContextParameters {
|
||||||
|
is_raining: Some(is_raining),
|
||||||
|
is_thundering: Some(is_thundering),
|
||||||
|
world_time: world.level_info.load().day_time as u64,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
for stack in loot_table.get_loot(params) {
|
for stack in loot_table.get_loot(params) {
|
||||||
world.drop_stack(&pos, stack).await;
|
world.drop_stack(&pos, stack).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -272,6 +272,8 @@ impl Explosion {
|
|||||||
let pumpkin_block = world.block_registry.get_pumpkin_block(block.id);
|
let pumpkin_block = world.block_registry.get_pumpkin_block(block.id);
|
||||||
|
|
||||||
if pumpkin_block.is_none_or(|s| s.should_drop_items_on_explosion()) {
|
if pumpkin_block.is_none_or(|s| s.should_drop_items_on_explosion()) {
|
||||||
|
let is_raining = world.is_raining().await;
|
||||||
|
let is_thundering = world.is_thundering().await;
|
||||||
let params = LootContextParameters {
|
let params = LootContextParameters {
|
||||||
block_state: Some(state),
|
block_state: Some(state),
|
||||||
explosion_radius: Some(self.power),
|
explosion_radius: Some(self.power),
|
||||||
@ -281,6 +283,8 @@ impl Explosion {
|
|||||||
pos.0.z as f64,
|
pos.0.z as f64,
|
||||||
)),
|
)),
|
||||||
world_time: world.level_info.load().day_time as u64,
|
world_time: world.level_info.load().day_time as u64,
|
||||||
|
is_raining: Some(is_raining),
|
||||||
|
is_thundering: Some(is_thundering),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
drop_loot(world, block, pos, false, params).await;
|
drop_loot(world, block, pos, false, params).await;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ use pumpkin_util::{
|
|||||||
};
|
};
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone)]
|
#[derive(Default, Clone)]
|
||||||
pub struct LootContextParameters {
|
pub struct LootContextParameters {
|
||||||
pub explosion_radius: Option<f32>,
|
pub explosion_radius: Option<f32>,
|
||||||
pub block_state: Option<&'static BlockState>,
|
pub block_state: Option<&'static BlockState>,
|
||||||
@ -24,6 +24,9 @@ pub struct LootContextParameters {
|
|||||||
pub position: Option<pumpkin_util::math::vector3::Vector3<f64>>,
|
pub position: Option<pumpkin_util::math::vector3::Vector3<f64>>,
|
||||||
pub world_time: u64,
|
pub world_time: u64,
|
||||||
pub damage_type: Option<DamageType>,
|
pub damage_type: Option<DamageType>,
|
||||||
|
pub tool: Option<ItemStack>,
|
||||||
|
pub is_raining: Option<bool>,
|
||||||
|
pub is_thundering: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait LootTableExt {
|
pub trait LootTableExt {
|
||||||
@ -100,12 +103,15 @@ trait LootFunctionExt {
|
|||||||
|
|
||||||
fn apply_bonus(
|
fn apply_bonus(
|
||||||
stacks: &mut [ItemStack],
|
stacks: &mut [ItemStack],
|
||||||
|
enchantment_name: &str,
|
||||||
formula: &str,
|
formula: &str,
|
||||||
parameters: Option<&LootFunctionBonusParameter>,
|
parameters: Option<&LootFunctionBonusParameter>,
|
||||||
|
params: &LootContextParameters,
|
||||||
) {
|
) {
|
||||||
// We currently don't have tool/enchantment data in LootContextParameters.
|
let enchantment_level = params.tool.as_ref().map_or(0, |tool| {
|
||||||
// Assuming enchantment level is 0 for now.
|
pumpkin_data::Enchantment::from_name(enchantment_name)
|
||||||
let enchantment_level = 0;
|
.map_or(0, |enchantment| tool.get_enchantment_level(enchantment))
|
||||||
|
});
|
||||||
if enchantment_level > 0 {
|
if enchantment_level > 0 {
|
||||||
for stack in stacks {
|
for stack in stacks {
|
||||||
match formula {
|
match formula {
|
||||||
@ -145,6 +151,7 @@ fn apply_bonus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LootFunctionExt for LootFunction {
|
impl LootFunctionExt for LootFunction {
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn apply(&self, stacks: &mut Vec<ItemStack>, params: &LootContextParameters) {
|
fn apply(&self, stacks: &mut Vec<ItemStack>, params: &LootContextParameters) {
|
||||||
if let Some(conditions) = self.conditions
|
if let Some(conditions) = self.conditions
|
||||||
&& !conditions.iter().all(|cond| cond.is_fulfilled(params))
|
&& !conditions.iter().all(|cond| cond.is_fulfilled(params))
|
||||||
@ -196,25 +203,128 @@ impl LootFunctionExt for LootFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
LootFunctionTypes::ApplyBonus {
|
LootFunctionTypes::ApplyBonus {
|
||||||
enchantment: _,
|
enchantment,
|
||||||
formula,
|
formula,
|
||||||
parameters,
|
parameters,
|
||||||
} => {
|
} => {
|
||||||
apply_bonus(stacks, formula, parameters.as_ref());
|
apply_bonus(stacks, enchantment, formula, parameters.as_ref(), params);
|
||||||
}
|
}
|
||||||
LootFunctionTypes::CopyComponents {
|
LootFunctionTypes::EnchantedCountIncrease {
|
||||||
source: _,
|
enchantment,
|
||||||
include: _,
|
count,
|
||||||
|
limit,
|
||||||
|
} => {
|
||||||
|
let level = params.tool.as_ref().map_or(0.0, |tool| {
|
||||||
|
pumpkin_data::Enchantment::from_name(enchantment)
|
||||||
|
.map_or(0.0, |enc| tool.get_enchantment_level(enc) as f32)
|
||||||
|
});
|
||||||
|
let mut additional = (count.generate() * level).round() as u32;
|
||||||
|
if let Some(lim) = limit {
|
||||||
|
let lim_u32 = lim.round() as u32;
|
||||||
|
if additional > lim_u32 {
|
||||||
|
additional = lim_u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for stack in stacks {
|
||||||
|
stack.item_count = stack.item_count.saturating_add(additional as u8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
| LootFunctionTypes::CopyState {
|
LootFunctionTypes::CopyComponents { source, include } => {
|
||||||
|
tracing::warn!(
|
||||||
|
"CopyComponents not supported from source: {} for {:?}",
|
||||||
|
source,
|
||||||
|
include
|
||||||
|
);
|
||||||
|
}
|
||||||
|
LootFunctionTypes::CopyState {
|
||||||
block: _,
|
block: _,
|
||||||
properties: _,
|
properties,
|
||||||
|
} => {
|
||||||
|
if let Some(state) = params.block_state
|
||||||
|
&& let Some(props_data) =
|
||||||
|
Block::properties(Block::from_state_id(state.id), state.id)
|
||||||
|
{
|
||||||
|
let actual_props = props_data.to_props();
|
||||||
|
let mut properties_to_copy = std::collections::HashMap::new();
|
||||||
|
for &prop_name in *properties {
|
||||||
|
if let Some((_, value)) = actual_props.iter().find(|(k, _)| k == &prop_name)
|
||||||
|
{
|
||||||
|
properties_to_copy.insert(prop_name.to_string(), value.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !properties_to_copy.is_empty() {
|
||||||
|
for stack in stacks.iter_mut() {
|
||||||
|
if let Some(block_state_comp) = stack.get_data_component_mut::<pumpkin_data::data_component_impl::BlockStateImpl>() {
|
||||||
|
for (k, v) in &properties_to_copy {
|
||||||
|
block_state_comp.properties.insert(k.clone(), v.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stack.patch.push((
|
||||||
|
pumpkin_data::data_component::DataComponent::BlockState,
|
||||||
|
Some(Box::new(pumpkin_data::data_component_impl::BlockStateImpl {
|
||||||
|
properties: properties_to_copy.clone(),
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
| LootFunctionTypes::EnchantedCountIncrease
|
LootFunctionTypes::SetOminousBottleAmplifier => {
|
||||||
| LootFunctionTypes::SetOminousBottleAmplifier
|
let amplifier = rand::random_range(0..5); // Random 0 to 4
|
||||||
| LootFunctionTypes::SetPotion
|
for stack in stacks.iter_mut() {
|
||||||
| LootFunctionTypes::FurnaceSmelt => {
|
if let Some(amplifier_comp) = stack.get_data_component_mut::<pumpkin_data::data_component_impl::OminousBottleAmplifierImpl>() {
|
||||||
// TODO: shouldnt crash here but needs to be implemented someday
|
amplifier_comp.amplifier = amplifier;
|
||||||
|
} else {
|
||||||
|
stack.patch.push((
|
||||||
|
pumpkin_data::data_component::DataComponent::OminousBottleAmplifier,
|
||||||
|
Some(Box::new(pumpkin_data::data_component_impl::OminousBottleAmplifierImpl {
|
||||||
|
amplifier,
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LootFunctionTypes::SetPotion { id } => {
|
||||||
|
let name = id.strip_prefix("minecraft:").unwrap_or(id);
|
||||||
|
if let Some(potion) = pumpkin_data::potion::Potion::from_name(name) {
|
||||||
|
let potion_id = Some(potion.id as i32);
|
||||||
|
for stack in stacks.iter_mut() {
|
||||||
|
if let Some(potion_contents) = stack.get_data_component_mut::<pumpkin_data::data_component_impl::PotionContentsImpl>() {
|
||||||
|
potion_contents.potion_id = potion_id;
|
||||||
|
} else {
|
||||||
|
stack.patch.push((
|
||||||
|
pumpkin_data::data_component::DataComponent::PotionContents,
|
||||||
|
Some(Box::new(pumpkin_data::data_component_impl::PotionContentsImpl {
|
||||||
|
potion_id,
|
||||||
|
custom_color: None,
|
||||||
|
custom_effects: Vec::new(),
|
||||||
|
custom_name: None,
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LootFunctionTypes::FurnaceSmelt => {
|
||||||
|
for stack in stacks.iter_mut() {
|
||||||
|
for recipe_type in pumpkin_data::recipes::RECIPES_COOKING {
|
||||||
|
if let pumpkin_data::recipes::CookingRecipeType::Smelting(recipe) =
|
||||||
|
recipe_type
|
||||||
|
&& recipe.ingredient.match_item(stack.item)
|
||||||
|
{
|
||||||
|
let result_key = recipe
|
||||||
|
.result
|
||||||
|
.id
|
||||||
|
.strip_prefix("minecraft:")
|
||||||
|
.unwrap_or(recipe.result.id);
|
||||||
|
if let Some(smelted_item) = Item::from_registry_key(result_key) {
|
||||||
|
stack.item = smelted_item;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,7 +357,7 @@ trait LootPoolEntryTypesExt {
|
|||||||
impl LootPoolEntryTypesExt for LootPoolEntryTypes {
|
impl LootPoolEntryTypesExt for LootPoolEntryTypes {
|
||||||
fn get_stacks(&self, params: &LootContextParameters) -> Vec<ItemStack> {
|
fn get_stacks(&self, params: &LootContextParameters) -> Vec<ItemStack> {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty | Self::Dynamic => Vec::new(),
|
Self::Empty | Self::Dynamic(_) => Vec::new(),
|
||||||
Self::LootTable(entry) => {
|
Self::LootTable(entry) => {
|
||||||
let key = entry
|
let key = entry
|
||||||
.value
|
.value
|
||||||
@ -395,7 +505,7 @@ fn check_damage_source_properties(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LootConditionExt for LootCondition {
|
impl LootConditionExt for LootCondition {
|
||||||
// TODO: This is trash. Make this right
|
#[allow(clippy::too_many_lines)]
|
||||||
fn is_fulfilled(&self, params: &LootContextParameters) -> bool {
|
fn is_fulfilled(&self, params: &LootContextParameters) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::SurvivesExplosion => {
|
Self::SurvivesExplosion => {
|
||||||
@ -437,32 +547,27 @@ impl LootConditionExt for LootCondition {
|
|||||||
Self::AnyOf(terms) => terms.iter().any(|cond| cond.is_fulfilled(params)),
|
Self::AnyOf(terms) => terms.iter().any(|cond| cond.is_fulfilled(params)),
|
||||||
Self::AllOf(terms) => terms.iter().all(|cond| cond.is_fulfilled(params)),
|
Self::AllOf(terms) => terms.iter().all(|cond| cond.is_fulfilled(params)),
|
||||||
Self::RandomChanceWithEnchantedBonus {
|
Self::RandomChanceWithEnchantedBonus {
|
||||||
enchantment: _,
|
enchantment,
|
||||||
chances,
|
chances,
|
||||||
} => chances.as_ref().is_some_and(|chances| {
|
} => chances.as_ref().is_some_and(|chances| {
|
||||||
// Missing tool context in params, assume enchantment level 0
|
let level = params.tool.as_ref().map_or(0, |tool| {
|
||||||
let level = 0;
|
pumpkin_data::Enchantment::from_name(enchantment)
|
||||||
|
.map_or(0, |enc| tool.get_enchantment_level(enc) as usize)
|
||||||
|
});
|
||||||
let chance = chances.get(level).unwrap_or(chances.last().unwrap_or(&0.0));
|
let chance = chances.get(level).unwrap_or(chances.last().unwrap_or(&0.0));
|
||||||
rand::rng().random::<f32>() < *chance
|
rand::rng().random::<f32>() < *chance
|
||||||
}),
|
}),
|
||||||
Self::TableBonus {
|
Self::TableBonus {
|
||||||
enchantment: _,
|
enchantment,
|
||||||
chances,
|
chances,
|
||||||
} => {
|
} => {
|
||||||
// Missing tool context in params, assume enchantment level 0
|
let level = params.tool.as_ref().map_or(0, |tool| {
|
||||||
let level = 0;
|
pumpkin_data::Enchantment::from_name(enchantment)
|
||||||
|
.map_or(0, |enc| tool.get_enchantment_level(enc) as usize)
|
||||||
|
});
|
||||||
let chance = chances.get(level).unwrap_or(chances.last().unwrap_or(&0.0));
|
let chance = chances.get(level).unwrap_or(chances.last().unwrap_or(&0.0));
|
||||||
rand::rng().random::<f32>() < *chance
|
rand::rng().random::<f32>() < *chance
|
||||||
}
|
}
|
||||||
Self::EntityScores { .. }
|
|
||||||
| Self::WeatherCheck { .. }
|
|
||||||
| Self::MatchTool { .. }
|
|
||||||
| Self::Reference { .. }
|
|
||||||
| Self::EnchantmentActiveCheck { .. }
|
|
||||||
| Self::LocationCheck { .. } => {
|
|
||||||
// TODO: Implement these
|
|
||||||
false
|
|
||||||
}
|
|
||||||
Self::TimeCheck { range, period } => {
|
Self::TimeCheck { range, period } => {
|
||||||
let mut time = params.world_time;
|
let mut time = params.world_time;
|
||||||
if let Some(period) = period {
|
if let Some(period) = period {
|
||||||
@ -484,6 +589,51 @@ impl LootConditionExt for LootCondition {
|
|||||||
} => {
|
} => {
|
||||||
check_damage_source_properties(params, *expected_source_type, *expected_direct_type)
|
check_damage_source_properties(params, *expected_source_type, *expected_direct_type)
|
||||||
}
|
}
|
||||||
|
Self::WeatherCheck {
|
||||||
|
raining,
|
||||||
|
thundering,
|
||||||
|
} => {
|
||||||
|
let r_match = raining.is_none_or(|r| params.is_raining.unwrap_or(false) == r);
|
||||||
|
let t_match = thundering.is_none_or(|t| params.is_thundering.unwrap_or(false) == t);
|
||||||
|
r_match && t_match
|
||||||
|
}
|
||||||
|
Self::MatchTool { items } => params.tool.as_ref().is_some_and(|tool| {
|
||||||
|
items.as_ref().map_or_else(
|
||||||
|
|| {
|
||||||
|
pumpkin_data::Enchantment::from_name("minecraft:silk_touch")
|
||||||
|
.is_some_and(|silk_touch| tool.get_enchantment_level(silk_touch) > 0)
|
||||||
|
},
|
||||||
|
|items| {
|
||||||
|
items.iter().any(|&item_name| {
|
||||||
|
let expected =
|
||||||
|
item_name.strip_prefix("minecraft:").unwrap_or(item_name);
|
||||||
|
let actual = tool
|
||||||
|
.item
|
||||||
|
.registry_key
|
||||||
|
.strip_prefix("minecraft:")
|
||||||
|
.unwrap_or(tool.item.registry_key);
|
||||||
|
expected == actual
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
Self::LocationCheck { expected_biome, .. } => expected_biome.is_none(),
|
||||||
|
Self::EntityScores { entity } => {
|
||||||
|
tracing::warn!("EntityScores check not supported for entity: {}", entity);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Self::Reference { name } => {
|
||||||
|
tracing::warn!("Loot condition reference not supported: {}", name);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Self::EnchantmentActiveCheck { active } => {
|
||||||
|
params.tool.as_ref().map_or(!*active, |tool| {
|
||||||
|
let has_enchantments = tool
|
||||||
|
.get_data_component::<pumpkin_data::data_component_impl::EnchantmentsImpl>()
|
||||||
|
.is_some_and(|e| !e.enchantment.is_empty());
|
||||||
|
has_enchantments == *active
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4226,6 +4226,7 @@ impl World {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return new state
|
// Return new state
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
pub async fn break_block(
|
pub async fn break_block(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
position: &BlockPos,
|
position: &BlockPos,
|
||||||
@ -4295,7 +4296,7 @@ impl World {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
let chunk_pos = position.chunk_position();
|
let chunk_pos = position.chunk_position();
|
||||||
match cause {
|
match &cause {
|
||||||
Some(player) => {
|
Some(player) => {
|
||||||
self.broadcast_to_chunk_except(
|
self.broadcast_to_chunk_except(
|
||||||
chunk_pos,
|
chunk_pos,
|
||||||
@ -4307,6 +4308,20 @@ impl World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !flags.contains(BlockFlags::SKIP_DROPS) {
|
if !flags.contains(BlockFlags::SKIP_DROPS) {
|
||||||
|
let tool = if let Some(player) = &cause {
|
||||||
|
let hand_stack = player
|
||||||
|
.inventory
|
||||||
|
.get_stack_in_hand(pumpkin_util::Hand::Right)
|
||||||
|
.await;
|
||||||
|
let stack_guard = hand_stack.lock().await;
|
||||||
|
(stack_guard.item_count > 0).then(|| stack_guard.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_raining = self.is_raining().await;
|
||||||
|
let is_thundering = self.is_thundering().await;
|
||||||
|
|
||||||
let params = LootContextParameters {
|
let params = LootContextParameters {
|
||||||
block_state: Some(BlockState::from_id(broken_state_id)),
|
block_state: Some(BlockState::from_id(broken_state_id)),
|
||||||
luck,
|
luck,
|
||||||
@ -4316,6 +4331,9 @@ impl World {
|
|||||||
position.0.z as f64,
|
position.0.z as f64,
|
||||||
)),
|
)),
|
||||||
world_time: self.level_info.load().day_time as u64,
|
world_time: self.level_info.load().day_time as u64,
|
||||||
|
tool,
|
||||||
|
is_raining: Some(is_raining),
|
||||||
|
is_thundering: Some(is_thundering),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
block::drop_loot(self, broken_block, position, true, params).await;
|
block::drop_loot(self, broken_block, position, true, params).await;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user