1use azalea_block::{fluid_state::FluidKind, Block, BlockState};
2use azalea_client::{inventory::Inventory, Client};
3use azalea_entity::{FluidOnEyes, Physics};
4use azalea_inventory::{components, ItemStack, Menu};
5
6#[derive(Debug)]
7pub struct BestToolResult {
8 pub index: usize,
9 pub percentage_per_tick: f32,
10}
11
12pub trait AutoToolClientExt {
13 fn best_tool_in_hotbar_for_block(&self, block: BlockState) -> BestToolResult;
14}
15
16impl AutoToolClientExt for Client {
17 fn best_tool_in_hotbar_for_block(&self, block: BlockState) -> BestToolResult {
18 let mut ecs = self.ecs.lock();
19 let (inventory, physics, fluid_on_eyes) =
20 self.query::<(&Inventory, &Physics, &FluidOnEyes)>(&mut ecs);
21 let menu = &inventory.inventory_menu;
22
23 accurate_best_tool_in_hotbar_for_block(block, menu, physics, fluid_on_eyes)
24 }
25}
26
27pub fn best_tool_in_hotbar_for_block(block: BlockState, menu: &Menu) -> BestToolResult {
33 let mut physics = Physics::default();
34 physics.set_on_ground(true);
35
36 accurate_best_tool_in_hotbar_for_block(
37 block,
38 menu,
39 &physics,
40 &FluidOnEyes::new(FluidKind::Empty),
41 )
42}
43
44pub fn accurate_best_tool_in_hotbar_for_block(
45 block: BlockState,
46 menu: &Menu,
47 physics: &Physics,
48 fluid_on_eyes: &FluidOnEyes,
49) -> BestToolResult {
50 let hotbar_slots = &menu.slots()[menu.hotbar_slots_range()];
51
52 let mut best_speed = 0.;
53 let mut best_slot = None;
54
55 let block = Box::<dyn Block>::from(block);
56 let registry_block = block.as_registry_block();
57
58 if matches!(
59 registry_block,
60 azalea_registry::Block::Water | azalea_registry::Block::Lava
61 ) {
62 return BestToolResult {
64 index: 0,
65 percentage_per_tick: 0.,
66 };
67 }
68
69 for (i, item_slot) in hotbar_slots.iter().enumerate() {
71 let this_item_speed;
72 match item_slot {
73 ItemStack::Empty => {
74 this_item_speed = Some(azalea_entity::mining::get_mine_progress(
75 block.as_ref(),
76 azalea_registry::Item::Air,
77 menu,
78 fluid_on_eyes,
79 physics,
80 ));
81 }
82 ItemStack::Present(item_stack) => {
83 if !item_stack.components.has::<components::Damage>() {
86 this_item_speed = Some(azalea_entity::mining::get_mine_progress(
87 block.as_ref(),
88 item_stack.kind,
89 menu,
90 fluid_on_eyes,
91 physics,
92 ));
93 } else {
94 this_item_speed = None;
95 }
96 }
97 }
98 if let Some(this_item_speed) = this_item_speed {
99 if this_item_speed > best_speed {
100 best_slot = Some(i);
101 best_speed = this_item_speed;
102 }
103 }
104 }
105
106 for (i, item_slot) in hotbar_slots.iter().enumerate() {
108 if let ItemStack::Present(item_slot) = item_slot {
109 let this_item_speed = azalea_entity::mining::get_mine_progress(
110 block.as_ref(),
111 item_slot.kind,
112 menu,
113 fluid_on_eyes,
114 physics,
115 );
116 if this_item_speed > best_speed {
117 best_slot = Some(i);
118 best_speed = this_item_speed;
119 }
120 }
121 }
122
123 BestToolResult {
124 index: best_slot.unwrap_or(0),
125 percentage_per_tick: best_speed,
126 }
127}