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_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");
|
||||
|
||||
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 {
|
||||
let ancestor_idx = Self::get_ancestor(nodes, inner_left, idx, default_ancestor);
|
||||
Self::move_subtree(nodes, ancestor_idx, idx, shift);
|
||||
@ -659,7 +662,7 @@ pub struct AdvancementStruct {
|
||||
pub sends_telemetry: bool,
|
||||
pub requirements: Vec<Vec<String>>,
|
||||
#[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>
|
||||
@ -846,10 +849,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
let advancement_tree = quote! {
|
||||
pub static ADVANCEMENT_TREE : LazyLock<AdvancementTree> = #tree;
|
||||
};
|
||||
let advancements_holder = tree
|
||||
.nodes_vector
|
||||
.into_iter()
|
||||
.map(|node| node.value);
|
||||
let advancements_holder = tree.nodes_vector.into_iter().map(|node| node.value);
|
||||
for AdvancementHolder(identifier, advancement) in advancements_holder {
|
||||
let raw_name = identifier.path();
|
||||
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 })
|
||||
}
|
||||
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 {
|
||||
Value::Number(n) if n.is_i64() => {
|
||||
let i = n.as_i64().unwrap();
|
||||
|
||||
@ -281,9 +281,8 @@ impl ToTokens for LootPoolEntryTypesStruct {
|
||||
Self::LootTable(entry) => {
|
||||
tokens.extend(quote! { LootPoolEntryTypes::LootTable(#entry) });
|
||||
}
|
||||
Self::Dynamic(_entry) => {
|
||||
// TODO
|
||||
tokens.extend(quote! { LootPoolEntryTypes::Dynamic });
|
||||
Self::Dynamic(entry) => {
|
||||
tokens.extend(quote! { LootPoolEntryTypes::Dynamic(#entry) });
|
||||
}
|
||||
Self::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.
|
||||
#[serde(rename = "minecraft:enchanted_count_increase")]
|
||||
EnchantedCountIncrease,
|
||||
EnchantedCountIncrease {
|
||||
enchantment: String,
|
||||
count: LootFunctionNumberProviderStruct,
|
||||
limit: Option<f32>,
|
||||
},
|
||||
/// Smelts the item as if processed in a furnace.
|
||||
#[serde(rename = "minecraft:furnace_smelt")]
|
||||
FurnaceSmelt,
|
||||
/// Sets the potion type on the item.
|
||||
#[serde(rename = "minecraft:set_potion")]
|
||||
SetPotion,
|
||||
SetPotion { id: String },
|
||||
/// Sets the amplifier on an ominous bottle item.
|
||||
#[serde(rename = "minecraft:set_ominous_bottle_amplifier")]
|
||||
SetOminousBottleAmplifier,
|
||||
@ -724,11 +727,21 @@ impl ToTokens for LootFunctionTypesStruct {
|
||||
Self::FurnaceSmelt => {
|
||||
quote! { LootFunctionTypes::FurnaceSmelt }
|
||||
}
|
||||
Self::SetPotion => {
|
||||
quote! { LootFunctionTypes::SetPotion }
|
||||
Self::SetPotion { id } => {
|
||||
let i = LitStr::new(id, Span::call_site());
|
||||
quote! { LootFunctionTypes::SetPotion { id: #i } }
|
||||
}
|
||||
Self::EnchantedCountIncrease => {
|
||||
quote! { LootFunctionTypes::EnchantedCountIncrease }
|
||||
Self::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 } => {
|
||||
let min = if let Some(min) = limit.min {
|
||||
|
||||
@ -3,11 +3,11 @@
|
||||
use crate::attributes::Attributes;
|
||||
use crate::data_component::DataComponent;
|
||||
use crate::data_component::DataComponent::{
|
||||
AttributeModifiers, BlockEntityData, BlocksAttacks, BundleContents, ChargedProjectiles,
|
||||
Consumable, Container, CustomData, CustomName, Damage, DamageResistant, DeathProtection,
|
||||
Enchantable, Enchantments, Equippable, FireworkExplosion, Fireworks, Food, ItemModel, ItemName,
|
||||
JukeboxPlayable, MapId, MaxDamage, MaxStackSize, PotionContents, StoredEnchantments, Tool,
|
||||
Unbreakable, UseCooldown, Weapon,
|
||||
AttributeModifiers, BlockEntityData, BlockState, BlocksAttacks, BundleContents,
|
||||
ChargedProjectiles, Consumable, Container, CustomData, CustomName, Damage, DamageResistant,
|
||||
DeathProtection, Enchantable, Enchantments, Equippable, FireworkExplosion, Fireworks, Food,
|
||||
ItemModel, ItemName, JukeboxPlayable, MapId, MaxDamage, MaxStackSize, OminousBottleAmplifier,
|
||||
PotionContents, StoredEnchantments, Tool, Unbreakable, UseCooldown, Weapon,
|
||||
};
|
||||
use crate::effect::{self, StatusEffect};
|
||||
use crate::entity_type::EntityType;
|
||||
@ -29,6 +29,7 @@ use std::error::Error;
|
||||
use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::hash_map::DefaultHasher,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
@ -81,6 +82,10 @@ pub fn read_data(id: DataComponent, data: &NbtTag) -> Option<Box<dyn DataCompone
|
||||
DataComponent::WritableBookContent => {
|
||||
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,
|
||||
}
|
||||
}
|
||||
@ -2062,7 +2067,23 @@ pub struct InstrumentImpl;
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ProvidesTrimMaterialImpl;
|
||||
#[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)]
|
||||
pub struct JukeboxPlayableImpl {
|
||||
pub song: &'static str,
|
||||
@ -2311,8 +2332,58 @@ impl ContainerImpl {
|
||||
impl DataComponentImpl for ContainerImpl {
|
||||
default_impl!(Container);
|
||||
}
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct BlockStateImpl;
|
||||
#[derive(Clone, Debug)]
|
||||
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)]
|
||||
pub struct BeesImpl;
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
|
||||
@ -532782,7 +532782,9 @@ impl Block {
|
||||
content: LootPoolEntryTypes::Alternatives(AlternativeEntry {
|
||||
children: &[
|
||||
LootPoolEntry {
|
||||
content: LootPoolEntryTypes::Dynamic,
|
||||
content: LootPoolEntryTypes::Dynamic(DynamicEntry {
|
||||
name: "minecraft:sherds",
|
||||
}),
|
||||
weight: 1i32,
|
||||
quality: 0i32,
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DynamicEntry {
|
||||
pub name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum LootPoolEntryTypes {
|
||||
Empty,
|
||||
Item(ItemEntry),
|
||||
LootTable(LootTableEntry),
|
||||
Dynamic,
|
||||
Dynamic(DynamicEntry),
|
||||
Tag(TagEntry),
|
||||
Alternatives(AlternativeEntry),
|
||||
Sequence(SequenceEntry),
|
||||
@ -173,11 +178,15 @@ pub enum LootFunctionTypes {
|
||||
add: bool,
|
||||
},
|
||||
/// 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.
|
||||
FurnaceSmelt,
|
||||
/// Sets the potion type for potion items.
|
||||
SetPotion,
|
||||
SetPotion { id: &'static str },
|
||||
/// Sets the amplifier for ominous bottles.
|
||||
SetOminousBottleAmplifier,
|
||||
/// Limits the count of items to a minimum and/or maximum value.
|
||||
|
||||
@ -1317,6 +1317,27 @@ impl LivingEntity {
|
||||
|
||||
// Plays the death sound
|
||||
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 {
|
||||
killed_by_player: cause.map(|c| c.get_entity().entity_type == &EntityType::PLAYER),
|
||||
this_entity: Some(self.entity.entity_type),
|
||||
@ -1325,11 +1346,14 @@ impl LivingEntity {
|
||||
position: Some(self.entity.pos.load()),
|
||||
world_time: world.level_info.load().day_time as u64,
|
||||
damage_type: Some(damage_type),
|
||||
tool,
|
||||
is_raining: Some(is_raining),
|
||||
is_thundering: Some(is_thundering),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Drop loot
|
||||
self.drop_loot(params).await;
|
||||
self.drop_loot(params.clone()).await;
|
||||
|
||||
// Award experience
|
||||
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 {
|
||||
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) {
|
||||
world.drop_stack(&pos, stack).await;
|
||||
}
|
||||
|
||||
@ -272,6 +272,8 @@ impl Explosion {
|
||||
let pumpkin_block = world.block_registry.get_pumpkin_block(block.id);
|
||||
|
||||
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 {
|
||||
block_state: Some(state),
|
||||
explosion_radius: Some(self.power),
|
||||
@ -281,6 +283,8 @@ impl Explosion {
|
||||
pos.0.z as f64,
|
||||
)),
|
||||
world_time: world.level_info.load().day_time as u64,
|
||||
is_raining: Some(is_raining),
|
||||
is_thundering: Some(is_thundering),
|
||||
..Default::default()
|
||||
};
|
||||
drop_loot(world, block, pos, false, params).await;
|
||||
|
||||
@ -12,7 +12,7 @@ use pumpkin_util::{
|
||||
};
|
||||
use rand::RngExt;
|
||||
|
||||
#[derive(Default, Copy, Clone)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct LootContextParameters {
|
||||
pub explosion_radius: Option<f32>,
|
||||
pub block_state: Option<&'static BlockState>,
|
||||
@ -24,6 +24,9 @@ pub struct LootContextParameters {
|
||||
pub position: Option<pumpkin_util::math::vector3::Vector3<f64>>,
|
||||
pub world_time: u64,
|
||||
pub damage_type: Option<DamageType>,
|
||||
pub tool: Option<ItemStack>,
|
||||
pub is_raining: Option<bool>,
|
||||
pub is_thundering: Option<bool>,
|
||||
}
|
||||
|
||||
pub trait LootTableExt {
|
||||
@ -100,12 +103,15 @@ trait LootFunctionExt {
|
||||
|
||||
fn apply_bonus(
|
||||
stacks: &mut [ItemStack],
|
||||
enchantment_name: &str,
|
||||
formula: &str,
|
||||
parameters: Option<&LootFunctionBonusParameter>,
|
||||
params: &LootContextParameters,
|
||||
) {
|
||||
// We currently don't have tool/enchantment data in LootContextParameters.
|
||||
// Assuming enchantment level is 0 for now.
|
||||
let enchantment_level = 0;
|
||||
let enchantment_level = params.tool.as_ref().map_or(0, |tool| {
|
||||
pumpkin_data::Enchantment::from_name(enchantment_name)
|
||||
.map_or(0, |enchantment| tool.get_enchantment_level(enchantment))
|
||||
});
|
||||
if enchantment_level > 0 {
|
||||
for stack in stacks {
|
||||
match formula {
|
||||
@ -145,6 +151,7 @@ fn apply_bonus(
|
||||
}
|
||||
|
||||
impl LootFunctionExt for LootFunction {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn apply(&self, stacks: &mut Vec<ItemStack>, params: &LootContextParameters) {
|
||||
if let Some(conditions) = self.conditions
|
||||
&& !conditions.iter().all(|cond| cond.is_fulfilled(params))
|
||||
@ -196,25 +203,128 @@ impl LootFunctionExt for LootFunction {
|
||||
}
|
||||
}
|
||||
LootFunctionTypes::ApplyBonus {
|
||||
enchantment: _,
|
||||
enchantment,
|
||||
formula,
|
||||
parameters,
|
||||
} => {
|
||||
apply_bonus(stacks, formula, parameters.as_ref());
|
||||
apply_bonus(stacks, enchantment, formula, parameters.as_ref(), params);
|
||||
}
|
||||
LootFunctionTypes::CopyComponents {
|
||||
source: _,
|
||||
include: _,
|
||||
LootFunctionTypes::EnchantedCountIncrease {
|
||||
enchantment,
|
||||
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: _,
|
||||
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::SetPotion
|
||||
| LootFunctionTypes::FurnaceSmelt => {
|
||||
// TODO: shouldnt crash here but needs to be implemented someday
|
||||
LootFunctionTypes::SetOminousBottleAmplifier => {
|
||||
let amplifier = rand::random_range(0..5); // Random 0 to 4
|
||||
for stack in stacks.iter_mut() {
|
||||
if let Some(amplifier_comp) = stack.get_data_component_mut::<pumpkin_data::data_component_impl::OminousBottleAmplifierImpl>() {
|
||||
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 {
|
||||
fn get_stacks(&self, params: &LootContextParameters) -> Vec<ItemStack> {
|
||||
match self {
|
||||
Self::Empty | Self::Dynamic => Vec::new(),
|
||||
Self::Empty | Self::Dynamic(_) => Vec::new(),
|
||||
Self::LootTable(entry) => {
|
||||
let key = entry
|
||||
.value
|
||||
@ -395,7 +505,7 @@ fn check_damage_source_properties(
|
||||
}
|
||||
|
||||
impl LootConditionExt for LootCondition {
|
||||
// TODO: This is trash. Make this right
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn is_fulfilled(&self, params: &LootContextParameters) -> bool {
|
||||
match self {
|
||||
Self::SurvivesExplosion => {
|
||||
@ -437,32 +547,27 @@ impl LootConditionExt for LootCondition {
|
||||
Self::AnyOf(terms) => terms.iter().any(|cond| cond.is_fulfilled(params)),
|
||||
Self::AllOf(terms) => terms.iter().all(|cond| cond.is_fulfilled(params)),
|
||||
Self::RandomChanceWithEnchantedBonus {
|
||||
enchantment: _,
|
||||
enchantment,
|
||||
chances,
|
||||
} => chances.as_ref().is_some_and(|chances| {
|
||||
// Missing tool context in params, assume enchantment level 0
|
||||
let level = 0;
|
||||
let level = params.tool.as_ref().map_or(0, |tool| {
|
||||
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));
|
||||
rand::rng().random::<f32>() < *chance
|
||||
}),
|
||||
Self::TableBonus {
|
||||
enchantment: _,
|
||||
enchantment,
|
||||
chances,
|
||||
} => {
|
||||
// Missing tool context in params, assume enchantment level 0
|
||||
let level = 0;
|
||||
let level = params.tool.as_ref().map_or(0, |tool| {
|
||||
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));
|
||||
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 } => {
|
||||
let mut time = params.world_time;
|
||||
if let Some(period) = period {
|
||||
@ -484,6 +589,51 @@ impl LootConditionExt for LootCondition {
|
||||
} => {
|
||||
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
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub async fn break_block(
|
||||
self: &Arc<Self>,
|
||||
position: &BlockPos,
|
||||
@ -4295,7 +4296,7 @@ impl World {
|
||||
false,
|
||||
);
|
||||
let chunk_pos = position.chunk_position();
|
||||
match cause {
|
||||
match &cause {
|
||||
Some(player) => {
|
||||
self.broadcast_to_chunk_except(
|
||||
chunk_pos,
|
||||
@ -4307,6 +4308,20 @@ impl World {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
block_state: Some(BlockState::from_id(broken_state_id)),
|
||||
luck,
|
||||
@ -4316,6 +4331,9 @@ impl World {
|
||||
position.0.z as f64,
|
||||
)),
|
||||
world_time: self.level_info.load().day_time as u64,
|
||||
tool,
|
||||
is_raining: Some(is_raining),
|
||||
is_thundering: Some(is_thundering),
|
||||
..Default::default()
|
||||
};
|
||||
block::drop_loot(self, broken_block, position, true, params).await;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user