1use azalea_block::{BlockState, BlockTrait, fluid_state::FluidKind};
2use azalea_client::{Client, inventory::Inventory};
3use azalea_core::position::BlockPos;
4use azalea_entity::{FluidOnEyes, Physics};
5use azalea_inventory::{ItemStack, Menu, components};
6
7use crate::BotClientExt;
8
9#[derive(Debug)]
10pub struct BestToolResult {
11 pub index: usize,
12 pub percentage_per_tick: f32,
13}
14
15pub trait AutoToolClientExt {
16 fn best_tool_in_hotbar_for_block(&self, block: BlockState) -> BestToolResult;
17 fn mine_with_auto_tool(&self, block_pos: BlockPos) -> impl Future<Output = ()> + Send;
18}
19
20impl AutoToolClientExt for Client {
21 fn best_tool_in_hotbar_for_block(&self, block: BlockState) -> BestToolResult {
22 let mut ecs = self.ecs.lock();
23 let (inventory, physics, fluid_on_eyes) =
24 self.query::<(&Inventory, &Physics, &FluidOnEyes)>(&mut ecs);
25 let menu = &inventory.inventory_menu;
26
27 accurate_best_tool_in_hotbar_for_block(block, menu, physics, fluid_on_eyes)
28 }
29
30 async fn mine_with_auto_tool(&self, block_pos: BlockPos) {
31 let block_state = self
32 .world()
33 .read()
34 .get_block_state(block_pos)
35 .unwrap_or_default();
36 let best_tool_result = self.best_tool_in_hotbar_for_block(block_state);
37 self.set_selected_hotbar_slot(best_tool_result.index as u8);
38 self.mine(block_pos).await;
39 }
40}
41
42pub fn best_tool_in_hotbar_for_block(block: BlockState, menu: &Menu) -> BestToolResult {
48 let mut physics = Physics::default();
49 physics.set_on_ground(true);
50
51 accurate_best_tool_in_hotbar_for_block(
52 block,
53 menu,
54 &physics,
55 &FluidOnEyes::new(FluidKind::Empty),
56 )
57}
58
59pub fn accurate_best_tool_in_hotbar_for_block(
60 block: BlockState,
61 menu: &Menu,
62 physics: &Physics,
63 fluid_on_eyes: &FluidOnEyes,
64) -> BestToolResult {
65 let hotbar_slots = &menu.slots()[menu.hotbar_slots_range()];
66
67 let mut best_speed = 0.;
68 let mut best_slot = None;
69
70 let block = Box::<dyn BlockTrait>::from(block);
71 let registry_block = block.as_registry_block();
72
73 if matches!(
74 registry_block,
75 azalea_registry::Block::Water | azalea_registry::Block::Lava
76 ) {
77 return BestToolResult {
79 index: 0,
80 percentage_per_tick: 0.,
81 };
82 }
83
84 for (i, item_slot) in hotbar_slots.iter().enumerate() {
86 let this_item_speed;
87 match item_slot {
88 ItemStack::Empty => {
89 this_item_speed = Some(azalea_entity::mining::get_mine_progress(
90 block.as_ref(),
91 azalea_registry::Item::Air,
92 menu,
93 fluid_on_eyes,
94 physics,
95 ));
96 }
97 ItemStack::Present(item_stack) => {
98 if !item_stack.components.has::<components::Damage>() {
101 this_item_speed = Some(azalea_entity::mining::get_mine_progress(
102 block.as_ref(),
103 item_stack.kind,
104 menu,
105 fluid_on_eyes,
106 physics,
107 ));
108 } else {
109 this_item_speed = None;
110 }
111 }
112 }
113 if let Some(this_item_speed) = this_item_speed
114 && this_item_speed > best_speed
115 {
116 best_slot = Some(i);
117 best_speed = this_item_speed;
118 }
119 }
120
121 for (i, item_slot) in hotbar_slots.iter().enumerate() {
123 if let ItemStack::Present(item_slot) = item_slot {
124 let this_item_speed = azalea_entity::mining::get_mine_progress(
125 block.as_ref(),
126 item_slot.kind,
127 menu,
128 fluid_on_eyes,
129 physics,
130 );
131 if this_item_speed > best_speed {
132 best_slot = Some(i);
133 best_speed = this_item_speed;
134 }
135 }
136 }
137
138 BestToolResult {
139 index: best_slot.unwrap_or(0),
140 percentage_per_tick: best_speed,
141 }
142}