From 8d718a627349bdf1e531fba318abc634a3bec9da Mon Sep 17 00:00:00 2001 From: mat Date: Sat, 7 Feb 2026 21:25:42 +0300 Subject: replace hard-coded block break speed checks --- azalea-client/src/plugins/mining.rs | 4 +- azalea-entity/src/attributes.rs | 2 + azalea-entity/src/lib.rs | 1 + azalea-entity/src/mining.rs | 114 +++++++--------------- azalea-registry/azalea-registry-macros/src/lib.rs | 28 ++++-- azalea-registry/src/lib.rs | 8 ++ azalea/src/auto_tool.rs | 16 +-- 7 files changed, 76 insertions(+), 97 deletions(-) diff --git a/azalea-client/src/plugins/mining.rs b/azalea-client/src/plugins/mining.rs index 5e724071..480ba46b 100644 --- a/azalea-client/src/plugins/mining.rs +++ b/azalea-client/src/plugins/mining.rs @@ -342,7 +342,7 @@ pub fn handle_mining_queued( if block_is_solid && get_mine_progress( block.as_ref(), - held_item.kind(), + held_item, fluid_on_eyes, physics, attributes, @@ -657,7 +657,7 @@ pub fn continue_mining_block( let block = Box::::from(target_block_state); **mine_progress += get_mine_progress( block.as_ref(), - current_mining_item.kind(), + current_mining_item, fluid_on_eyes, physics, attributes, diff --git a/azalea-entity/src/attributes.rs b/azalea-entity/src/attributes.rs index 69e98a7b..26f0618e 100644 --- a/azalea-entity/src/attributes.rs +++ b/azalea-entity/src/attributes.rs @@ -21,6 +21,7 @@ pub struct Attributes { pub attack_speed: AttributeInstance, pub water_movement_efficiency: AttributeInstance, pub mining_efficiency: AttributeInstance, + pub block_break_speed: AttributeInstance, pub block_interaction_range: AttributeInstance, pub entity_interaction_range: AttributeInstance, @@ -41,6 +42,7 @@ impl Attributes { Attribute::BlockInteractionRange => &mut self.block_interaction_range, Attribute::EntityInteractionRange => &mut self.entity_interaction_range, Attribute::StepHeight => &mut self.step_height, + Attribute::BlockBreakSpeed => &mut self.block_break_speed, _ => return None, }; Some(value) diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index fab973b1..fa4feffb 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -398,6 +398,7 @@ impl Attributes { block_interaction_range: AttributeInstance::new(4.5), entity_interaction_range: AttributeInstance::new(3.0), step_height: AttributeInstance::new(0.6), + block_break_speed: AttributeInstance::new(1.0), } } } diff --git a/azalea-entity/src/mining.rs b/azalea-entity/src/mining.rs index bda75bd9..bc2e6c46 100644 --- a/azalea-entity/src/mining.rs +++ b/azalea-entity/src/mining.rs @@ -1,9 +1,6 @@ use azalea_block::{BlockBehavior, BlockTrait}; -use azalea_core::tier::get_item_tier; -use azalea_registry::{ - builtin::{BlockKind, ItemKind, MobEffect}, - tags, -}; +use azalea_inventory::{ItemStack, components::Tool}; +use azalea_registry::builtin::{BlockKind, MobEffect}; use crate::{ActiveEffects, Attributes, FluidOnEyes, Physics}; @@ -19,7 +16,7 @@ use crate::{ActiveEffects, Attributes, FluidOnEyes, Physics}; /// to your mining speed. pub fn get_mine_progress( block: &dyn BlockTrait, - held_item: ItemKind, + held_item: &ItemStack, fluid_on_eyes: &FluidOnEyes, physics: &Physics, attributes: &Attributes, @@ -45,34 +42,26 @@ pub fn get_mine_progress( attributes, active_effects, ); - (base_destroy_speed / destroy_time) / divisor as f32 + (base_destroy_speed / destroy_time) / (divisor as f32) } -fn has_correct_tool_for_drops(block: &dyn BlockTrait, tool: ItemKind) -> bool { +fn has_correct_tool_for_drops(block: &dyn BlockTrait, item: &ItemStack) -> bool { if !block.behavior().requires_correct_tool_for_drops { return true; } + let Some(tool) = item.get_component::() else { + return false; + }; let registry_block = block.as_registry_block(); - if tool == ItemKind::Shears { - matches!( - registry_block, - BlockKind::Cobweb | BlockKind::RedstoneWire | BlockKind::Tripwire - ) - } else if tags::items::SWORDS.contains(&tool) { - registry_block == BlockKind::Cobweb - } else if tags::items::PICKAXES.contains(&tool) - || tags::items::SHOVELS.contains(&tool) - || tags::items::HOES.contains(&tool) - || tags::items::AXES.contains(&tool) - { - let tier = get_item_tier(tool).expect("all pickaxes and shovels should be matched"); - let tier_level = tier.level(); - !((tier_level < 3 && tags::blocks::NEEDS_DIAMOND_TOOL.contains(®istry_block)) - || (tier_level < 2 && tags::blocks::NEEDS_IRON_TOOL.contains(®istry_block)) - || (tier_level < 1 && tags::blocks::NEEDS_STONE_TOOL.contains(®istry_block))) - } else { - false + for rule in &tool.rules { + if let Some(correct) = rule.correct_for_drops + && rule.blocks.contains(registry_block) + { + return correct; + } } + + false } /// Returns the destroy speed of the given block with the given tool, taking @@ -82,21 +71,21 @@ fn has_correct_tool_for_drops(block: &dyn BlockTrait, tool: ItemKind) -> bool { /// `ItemKind::Air`. fn destroy_speed( block: BlockKind, - tool: ItemKind, + tool: &ItemStack, _fluid_on_eyes: &FluidOnEyes, physics: &Physics, attributes: &Attributes, active_effects: &ActiveEffects, ) -> f32 { - let mut base_destroy_speed = base_destroy_speed(block, tool); + let mut speed = base_destroy_speed(block, tool); - if base_destroy_speed > 1. { + if speed > 1. { // efficiency enchantment - base_destroy_speed += attributes.mining_efficiency.calculate() as f32; + speed += attributes.mining_efficiency.calculate() as f32; } if let Some(dig_speed_amplifier) = active_effects.get_dig_speed_amplifier() { - base_destroy_speed *= 1. + (dig_speed_amplifier + 1) as f32 * 0.2; + speed *= 1. + (dig_speed_amplifier + 1) as f32 * 0.2; } if let Some(dig_slowdown) = active_effects.get_level(MobEffect::MiningFatigue) { @@ -106,9 +95,11 @@ fn destroy_speed( 2 => 0.0027, _ => 8.1E-4, }; - base_destroy_speed *= multiplier; + speed *= multiplier; } + speed *= attributes.block_break_speed.calculate() as f32; + // TODO // if **fluid_on_eyes == FluidKind::Water // && enchantments::get_enchant_level(registry::Enchantment::AquaAffinity, @@ -118,56 +109,21 @@ fn destroy_speed( // } if !physics.on_ground { - base_destroy_speed /= 5.; + speed /= 5.; } - base_destroy_speed + speed } -fn base_destroy_speed(block: BlockKind, tool: ItemKind) -> f32 { - if tool == ItemKind::Shears { - if block == BlockKind::Cobweb || tags::blocks::LEAVES.contains(&block) { - 15. - } else if tags::blocks::WOOL.contains(&block) { - 5. - } else if matches!(block, BlockKind::Vine | BlockKind::GlowLichen) { - 2. - } else { - 1. - } - } else if tags::items::SWORDS.contains(&tool) { - if block == BlockKind::Cobweb { - 15. - } else if tags::blocks::SWORD_EFFICIENT.contains(&block) { - 1.5 - } else { - 1. +fn base_destroy_speed(block: BlockKind, item: &ItemStack) -> f32 { + let tool = item.get_component::(); + let Some(tool) = tool else { return 1. }; + for rule in &tool.rules { + if let Some(speed) = rule.speed + && rule.blocks.contains(block) + { + return speed; } - } else if tags::items::PICKAXES.contains(&tool) { - if tags::blocks::MINEABLE_PICKAXE.contains(&block) { - get_item_tier(tool).unwrap().speed() - } else { - 1. - } - } else if tags::items::SHOVELS.contains(&tool) { - if tags::blocks::MINEABLE_SHOVEL.contains(&block) { - get_item_tier(tool).unwrap().speed() - } else { - 1. - } - } else if tags::items::HOES.contains(&tool) { - if tags::blocks::MINEABLE_HOE.contains(&block) { - get_item_tier(tool).unwrap().speed() - } else { - 1. - } - } else if tags::items::AXES.contains(&tool) { - if tags::blocks::MINEABLE_AXE.contains(&block) { - get_item_tier(tool).unwrap().speed() - } else { - 1. - } - } else { - 1. } + tool.default_mining_speed } diff --git a/azalea-registry/azalea-registry-macros/src/lib.rs b/azalea-registry/azalea-registry-macros/src/lib.rs index 6c7d4840..a8c6fe18 100644 --- a/azalea-registry/azalea-registry-macros/src/lib.rs +++ b/azalea-registry/azalea-registry-macros/src/lib.rs @@ -140,28 +140,35 @@ pub fn registry(input: TokenStream) -> TokenStream { }); // Display that uses registry ids - let mut display_items = quote! {}; + let mut to_str_items = quote! {}; let mut from_str_items = quote! {}; for item in &input.items { let name = &item.name; let id = &item.id; - display_items.extend(quote! { - Self::#name => write!(f, concat!("minecraft:", #id)), + to_str_items.extend(quote! { + Self::#name => concat!("minecraft:", #id), }); from_str_items.extend(quote! { #id => Ok(Self::#name), }); } generated.extend(quote! { - /// Convert the value to a stringified identifier, formatted like - /// `"minecraft:air"`. - impl std::fmt::Display for #name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + impl #name { + /// Convert the value to a stringified identifier, formatted like + /// `"minecraft:air"`. + pub fn to_str(&self) -> &'static str { match self { - #display_items + #to_str_items } } } + impl std::fmt::Display for #name { + /// Convert the value to a stringified identifier, formatted like + /// `"minecraft:air"`. + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_str()) + } + } impl<'a> TryFrom<&'a crate::Identifier> for #name { type Error = (); fn try_from(ident: &'a crate::Identifier) -> Result { @@ -172,6 +179,11 @@ pub fn registry(input: TokenStream) -> TokenStream { } } } + impl<'a> From<#name> for crate::Identifier { + fn from(value: #name) -> Self { + Self::new(value.to_str()) + } + } /// Parse the value from a stringified identifier, formatted like /// either `"air"` or `"minecraft:air"`. impl std::str::FromStr for #name { diff --git a/azalea-registry/src/lib.rs b/azalea-registry/src/lib.rs index fe6b85b3..e60a0968 100644 --- a/azalea-registry/src/lib.rs +++ b/azalea-registry/src/lib.rs @@ -204,6 +204,14 @@ impl Default for HolderSet { } } } +impl + PartialEq> HolderSet { + pub fn contains(&self, value: D) -> bool { + match self { + HolderSet::Direct { contents } => contents.contains(&value), + HolderSet::Named { key: _, contents } => contents.contains(&Identifier::from(value)), + } + } +} /// A reference to either a registry or a custom value (usually something with /// an `Identifier`). diff --git a/azalea/src/auto_tool.rs b/azalea/src/auto_tool.rs index 519c5dc9..5fe0f529 100644 --- a/azalea/src/auto_tool.rs +++ b/azalea/src/auto_tool.rs @@ -2,7 +2,7 @@ use azalea_block::{BlockState, BlockTrait, fluid_state::FluidKind}; use azalea_core::position::BlockPos; use azalea_entity::{ActiveEffects, Attributes, FluidOnEyes, Physics, inventory::Inventory}; use azalea_inventory::{ItemStack, Menu, components}; -use azalea_registry::builtin::{BlockKind, EntityKind, ItemKind}; +use azalea_registry::builtin::{BlockKind, EntityKind}; use crate::Client; @@ -92,13 +92,13 @@ pub fn accurate_best_tool_in_hotbar_for_block( } // find the first slot that has an item without durability - for (i, item_slot) in hotbar_slots.iter().enumerate() { + for (i, item_stack_data) in hotbar_slots.iter().enumerate() { let this_item_speed; - match item_slot { + match item_stack_data { ItemStack::Empty => { this_item_speed = Some(azalea_entity::mining::get_mine_progress( block.as_ref(), - ItemKind::Air, + &ItemStack::Empty, fluid_on_eyes, physics, attributes, @@ -111,7 +111,7 @@ pub fn accurate_best_tool_in_hotbar_for_block( if !item_stack.component_patch.has::() { this_item_speed = Some(azalea_entity::mining::get_mine_progress( block.as_ref(), - item_stack.kind, + item_stack_data, fluid_on_eyes, physics, attributes, @@ -131,11 +131,11 @@ pub fn accurate_best_tool_in_hotbar_for_block( } // now check every item - for (i, item_slot) in hotbar_slots.iter().enumerate() { - if let ItemStack::Present(item_slot) = item_slot { + for (i, item_stack) in hotbar_slots.iter().enumerate() { + if item_stack.is_present() { let this_item_speed = azalea_entity::mining::get_mine_progress( block.as_ref(), - item_slot.kind, + item_stack, fluid_on_eyes, physics, attributes, -- cgit v1.2.3