1use azalea_block::{BlockState, BlockTrait, fluid_state::FluidKind};
2use azalea_client::{Client, inventory::Inventory};
3use azalea_core::position::BlockPos;
4use azalea_entity::{ActiveEffects, FluidOnEyes, Physics};
5use azalea_inventory::{ItemStack, Menu, components};
6
7use crate::bot::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 self.query_self::<(&Inventory, &Physics, &FluidOnEyes, &ActiveEffects), _>(
23 |(inventory, physics, fluid_on_eyes, active_effects)| {
24 let menu = &inventory.inventory_menu;
25 accurate_best_tool_in_hotbar_for_block(
26 block,
27 menu,
28 physics,
29 fluid_on_eyes,
30 active_effects,
31 )
32 },
33 )
34 }
35
36 async fn mine_with_auto_tool(&self, block_pos: BlockPos) {
37 let block_state = self
38 .world()
39 .read()
40 .get_block_state(block_pos)
41 .unwrap_or_default();
42 let best_tool_result = self.best_tool_in_hotbar_for_block(block_state);
43 self.set_selected_hotbar_slot(best_tool_result.index as u8);
44 self.mine(block_pos).await;
45 }
46}
47
48pub fn best_tool_in_hotbar_for_block(block: BlockState, menu: &Menu) -> BestToolResult {
54 let mut physics = Physics::default();
55 physics.set_on_ground(true);
56
57 let inactive_effects = ActiveEffects::default();
58 accurate_best_tool_in_hotbar_for_block(
59 block,
60 menu,
61 &physics,
62 &FluidOnEyes::new(FluidKind::Empty),
63 &inactive_effects,
64 )
65}
66
67pub fn accurate_best_tool_in_hotbar_for_block(
68 block: BlockState,
69 menu: &Menu,
70 physics: &Physics,
71 fluid_on_eyes: &FluidOnEyes,
72 active_effects: &ActiveEffects,
73) -> BestToolResult {
74 let hotbar_slots = &menu.slots()[menu.hotbar_slots_range()];
75
76 let mut best_speed = 0.;
77 let mut best_slot = None;
78
79 let block = Box::<dyn BlockTrait>::from(block);
80 let registry_block = block.as_registry_block();
81
82 if matches!(
83 registry_block,
84 azalea_registry::Block::Water | azalea_registry::Block::Lava
85 ) {
86 return BestToolResult {
88 index: 0,
89 percentage_per_tick: 0.,
90 };
91 }
92
93 for (i, item_slot) in hotbar_slots.iter().enumerate() {
95 let this_item_speed;
96 match item_slot {
97 ItemStack::Empty => {
98 this_item_speed = Some(azalea_entity::mining::get_mine_progress(
99 block.as_ref(),
100 azalea_registry::Item::Air,
101 menu,
102 fluid_on_eyes,
103 physics,
104 active_effects,
105 ));
106 }
107 ItemStack::Present(item_stack) => {
108 if !item_stack.component_patch.has::<components::Damage>() {
111 this_item_speed = Some(azalea_entity::mining::get_mine_progress(
112 block.as_ref(),
113 item_stack.kind,
114 menu,
115 fluid_on_eyes,
116 physics,
117 active_effects,
118 ));
119 } else {
120 this_item_speed = None;
121 }
122 }
123 }
124 if let Some(this_item_speed) = this_item_speed
125 && this_item_speed > best_speed
126 {
127 best_slot = Some(i);
128 best_speed = this_item_speed;
129 }
130 }
131
132 for (i, item_slot) in hotbar_slots.iter().enumerate() {
134 if let ItemStack::Present(item_slot) = item_slot {
135 let this_item_speed = azalea_entity::mining::get_mine_progress(
136 block.as_ref(),
137 item_slot.kind,
138 menu,
139 fluid_on_eyes,
140 physics,
141 active_effects,
142 );
143 if this_item_speed > best_speed {
144 best_slot = Some(i);
145 best_speed = this_item_speed;
146 }
147 }
148 }
149
150 BestToolResult {
151 index: best_slot.unwrap_or(0),
152 percentage_per_tick: best_speed,
153 }
154}