Skip to main content

azalea_entity/
mining.rs

1use azalea_block::{BlockBehavior, BlockTrait};
2use azalea_inventory::{ItemStack, components::Tool};
3use azalea_registry::builtin::{BlockKind, MobEffect};
4
5use crate::{ActiveEffects, Attributes, FluidOnEyes, Physics};
6
7/// How much progress is made towards mining the block per tick, as a
8/// percentage.
9///
10/// If this is 1, then the block gets broken instantly.
11///
12/// You can divide 1 by this and then round up to get the number of ticks it
13/// takes to mine the block.
14///
15/// The player inventory is needed to check your armor and offhand for modifiers
16/// to your mining speed.
17pub fn get_mine_progress(
18    block: &dyn BlockTrait,
19    held_item: &ItemStack,
20    fluid_on_eyes: &FluidOnEyes,
21    physics: &Physics,
22    attributes: &Attributes,
23    active_effects: &ActiveEffects,
24) -> f32 {
25    let block_behavior: BlockBehavior = block.behavior();
26
27    let destroy_time = block_behavior.destroy_time;
28    if destroy_time == -1. {
29        return 0.;
30    }
31    let divisor = if has_correct_tool_for_drops(block, held_item) {
32        30
33    } else {
34        100
35    };
36
37    let base_destroy_speed = destroy_speed(
38        block.as_registry_block(),
39        held_item,
40        fluid_on_eyes,
41        physics,
42        attributes,
43        active_effects,
44    );
45    (base_destroy_speed / destroy_time) / (divisor as f32)
46}
47
48fn has_correct_tool_for_drops(block: &dyn BlockTrait, item: &ItemStack) -> bool {
49    if !block.behavior().requires_correct_tool_for_drops {
50        return true;
51    }
52    let Some(tool) = item.get_component::<Tool>() else {
53        return false;
54    };
55    let registry_block = block.as_registry_block();
56    for rule in &tool.rules {
57        if let Some(correct) = rule.correct_for_drops
58            && rule.blocks.contains(registry_block)
59        {
60            return correct;
61        }
62    }
63
64    false
65}
66
67/// Returns the destroy speed of the given block with the given tool, taking
68/// enchantments and effects into account.
69///
70/// If the player is not holding anything, then `tool` should be
71/// `ItemKind::Air`.
72fn destroy_speed(
73    block: BlockKind,
74    tool: &ItemStack,
75    _fluid_on_eyes: &FluidOnEyes,
76    physics: &Physics,
77    attributes: &Attributes,
78    active_effects: &ActiveEffects,
79) -> f32 {
80    let mut speed = base_destroy_speed(block, tool);
81
82    if speed > 1. {
83        // efficiency enchantment
84        speed += attributes.mining_efficiency.calculate() as f32;
85    }
86
87    if let Some(dig_speed_amplifier) = active_effects.get_dig_speed_amplifier() {
88        speed *= 1. + (dig_speed_amplifier + 1) as f32 * 0.2;
89    }
90
91    if let Some(dig_slowdown) = active_effects.get_level(MobEffect::MiningFatigue) {
92        let multiplier = match dig_slowdown {
93            0 => 0.3,
94            1 => 0.09,
95            2 => 0.0027,
96            _ => 8.1E-4,
97        };
98        speed *= multiplier;
99    }
100
101    speed *= attributes.block_break_speed.calculate() as f32;
102
103    // TODO
104    // if **fluid_on_eyes == FluidKind::Water
105    //     && enchantments::get_enchant_level(registry::Enchantment::AquaAffinity,
106    // player_inventory)         == 0
107    // {
108    //     base_destroy_speed /= 5.;
109    // }
110
111    if !physics.on_ground {
112        speed /= 5.;
113    }
114
115    speed
116}
117
118fn base_destroy_speed(block: BlockKind, item: &ItemStack) -> f32 {
119    let tool = item.get_component::<Tool>();
120    let Some(tool) = tool else { return 1. };
121    for rule in &tool.rules {
122        if let Some(speed) = rule.speed
123            && rule.blocks.contains(block)
124        {
125            return speed;
126        }
127    }
128    tool.default_mining_speed
129}