azalea_entity/
mining.rs

1use azalea_block::{BlockBehavior, BlockTrait};
2use azalea_core::tier::get_item_tier;
3use azalea_registry::{
4    builtin::{BlockKind, ItemKind, MobEffect},
5    tags,
6};
7
8use crate::{ActiveEffects, Attributes, FluidOnEyes, Physics};
9
10/// How much progress is made towards mining the block per tick, as a
11/// percentage.
12///
13/// If this is 1, then the block gets broken instantly.
14///
15/// You can divide 1 by this and then round up to get the number of ticks it
16/// takes to mine the block.
17///
18/// The player inventory is needed to check your armor and offhand for modifiers
19/// to your mining speed.
20pub fn get_mine_progress(
21    block: &dyn BlockTrait,
22    held_item: ItemKind,
23    fluid_on_eyes: &FluidOnEyes,
24    physics: &Physics,
25    attributes: &Attributes,
26    active_effects: &ActiveEffects,
27) -> f32 {
28    let block_behavior: BlockBehavior = block.behavior();
29
30    let destroy_time = block_behavior.destroy_time;
31    if destroy_time == -1. {
32        return 0.;
33    }
34    let divisor = if has_correct_tool_for_drops(block, held_item) {
35        30
36    } else {
37        100
38    };
39
40    let base_destroy_speed = destroy_speed(
41        block.as_registry_block(),
42        held_item,
43        fluid_on_eyes,
44        physics,
45        attributes,
46        active_effects,
47    );
48    (base_destroy_speed / destroy_time) / divisor as f32
49}
50
51fn has_correct_tool_for_drops(block: &dyn BlockTrait, tool: ItemKind) -> bool {
52    if !block.behavior().requires_correct_tool_for_drops {
53        return true;
54    }
55    let registry_block = block.as_registry_block();
56    if tool == ItemKind::Shears {
57        matches!(
58            registry_block,
59            BlockKind::Cobweb | BlockKind::RedstoneWire | BlockKind::Tripwire
60        )
61    } else if tags::items::SWORDS.contains(&tool) {
62        registry_block == BlockKind::Cobweb
63    } else if tags::items::PICKAXES.contains(&tool)
64        || tags::items::SHOVELS.contains(&tool)
65        || tags::items::HOES.contains(&tool)
66        || tags::items::AXES.contains(&tool)
67    {
68        let tier = get_item_tier(tool).expect("all pickaxes and shovels should be matched");
69        let tier_level = tier.level();
70        !((tier_level < 3 && tags::blocks::NEEDS_DIAMOND_TOOL.contains(&registry_block))
71            || (tier_level < 2 && tags::blocks::NEEDS_IRON_TOOL.contains(&registry_block))
72            || (tier_level < 1 && tags::blocks::NEEDS_STONE_TOOL.contains(&registry_block)))
73    } else {
74        false
75    }
76}
77
78/// Returns the destroy speed of the given block with the given tool, taking
79/// enchantments and effects into account.
80///
81/// If the player is not holding anything, then `tool` should be
82/// `ItemKind::Air`.
83fn destroy_speed(
84    block: BlockKind,
85    tool: ItemKind,
86    _fluid_on_eyes: &FluidOnEyes,
87    physics: &Physics,
88    attributes: &Attributes,
89    active_effects: &ActiveEffects,
90) -> f32 {
91    let mut base_destroy_speed = base_destroy_speed(block, tool);
92
93    if base_destroy_speed > 1. {
94        // efficiency enchantment
95        base_destroy_speed += attributes.mining_efficiency.calculate() as f32;
96    }
97
98    if let Some(dig_speed_amplifier) = active_effects.get_dig_speed_amplifier() {
99        base_destroy_speed *= 1. + (dig_speed_amplifier + 1) as f32 * 0.2;
100    }
101
102    if let Some(dig_slowdown) = active_effects.get_level(MobEffect::MiningFatigue) {
103        let multiplier = match dig_slowdown {
104            0 => 0.3,
105            1 => 0.09,
106            2 => 0.0027,
107            _ => 8.1E-4,
108        };
109        base_destroy_speed *= multiplier;
110    }
111
112    // TODO
113    // if **fluid_on_eyes == FluidKind::Water
114    //     && enchantments::get_enchant_level(registry::Enchantment::AquaAffinity,
115    // player_inventory)         == 0
116    // {
117    //     base_destroy_speed /= 5.;
118    // }
119
120    if !physics.on_ground {
121        base_destroy_speed /= 5.;
122    }
123
124    base_destroy_speed
125}
126
127fn base_destroy_speed(block: BlockKind, tool: ItemKind) -> f32 {
128    if tool == ItemKind::Shears {
129        if block == BlockKind::Cobweb || tags::blocks::LEAVES.contains(&block) {
130            15.
131        } else if tags::blocks::WOOL.contains(&block) {
132            5.
133        } else if matches!(block, BlockKind::Vine | BlockKind::GlowLichen) {
134            2.
135        } else {
136            1.
137        }
138    } else if tags::items::SWORDS.contains(&tool) {
139        if block == BlockKind::Cobweb {
140            15.
141        } else if tags::blocks::SWORD_EFFICIENT.contains(&block) {
142            1.5
143        } else {
144            1.
145        }
146    } else if tags::items::PICKAXES.contains(&tool) {
147        if tags::blocks::MINEABLE_PICKAXE.contains(&block) {
148            get_item_tier(tool).unwrap().speed()
149        } else {
150            1.
151        }
152    } else if tags::items::SHOVELS.contains(&tool) {
153        if tags::blocks::MINEABLE_SHOVEL.contains(&block) {
154            get_item_tier(tool).unwrap().speed()
155        } else {
156            1.
157        }
158    } else if tags::items::HOES.contains(&tool) {
159        if tags::blocks::MINEABLE_HOE.contains(&block) {
160            get_item_tier(tool).unwrap().speed()
161        } else {
162            1.
163        }
164    } else if tags::items::AXES.contains(&tool) {
165        if tags::blocks::MINEABLE_AXE.contains(&block) {
166            get_item_tier(tool).unwrap().speed()
167        } else {
168            1.
169        }
170    } else {
171        1.
172    }
173}