1use azalea_block::{BlockState, BlockTrait, fluid_state::FluidKind};
2use azalea_core::position::BlockPos;
3use azalea_entity::{ActiveEffects, Attributes, FluidOnEyes, Physics, inventory::Inventory};
4use azalea_inventory::{ItemStack, Menu, components};
5use azalea_registry::builtin::{BlockKind, EntityKind};
6
7use crate::{Client, client_impl::error::AzaleaResult};
8
9#[derive(Debug)]
10pub struct BestToolResult {
11 pub index: usize,
12 pub percentage_per_tick: f32,
13}
14
15impl Client {
16 pub fn best_tool_in_hotbar_for_block(&self, block: BlockState) -> AzaleaResult<BestToolResult> {
17 self.query_self::<(
18 &Inventory,
19 &Physics,
20 &FluidOnEyes,
21 &Attributes,
22 &ActiveEffects,
23 ), _>(
24 |(inventory, physics, fluid_on_eyes, attributes, active_effects)| {
25 let menu = &inventory.inventory_menu;
26 accurate_best_tool_in_hotbar_for_block(
27 block,
28 menu,
29 physics,
30 fluid_on_eyes,
31 attributes,
32 active_effects,
33 )
34 },
35 )
36 }
37
38 pub async fn mine_with_auto_tool(&self, block_pos: BlockPos) -> AzaleaResult<()> {
39 let block_state = self
40 .world()?
41 .read()
42 .get_block_state(block_pos)
43 .unwrap_or_default();
44 let best_tool_result = self.best_tool_in_hotbar_for_block(block_state)?;
45 self.set_selected_hotbar_slot(best_tool_result.index as u8);
46 self.mine(block_pos).await;
47 Ok(())
48 }
49}
50
51pub fn best_tool_in_hotbar_for_block(block: BlockState, menu: &Menu) -> BestToolResult {
57 let mut physics = Physics::default();
58 physics.set_on_ground(true);
59
60 let inactive_effects = ActiveEffects::default();
61 accurate_best_tool_in_hotbar_for_block(
62 block,
63 menu,
64 &physics,
65 &FluidOnEyes::new(FluidKind::Empty),
66 &Attributes::new(EntityKind::Player),
67 &inactive_effects,
68 )
69}
70
71pub fn accurate_best_tool_in_hotbar_for_block(
72 block: BlockState,
73 menu: &Menu,
74 physics: &Physics,
75 fluid_on_eyes: &FluidOnEyes,
76 attributes: &Attributes,
77 active_effects: &ActiveEffects,
78) -> BestToolResult {
79 let hotbar_slots = &menu.slots()[menu.hotbar_slots_range()];
80
81 let mut best_speed = 0.;
82 let mut best_slot = None;
83
84 let block = Box::<dyn BlockTrait>::from(block);
85 let registry_block = block.as_block_kind();
86
87 if matches!(registry_block, BlockKind::Water | BlockKind::Lava) {
88 return BestToolResult {
90 index: 0,
91 percentage_per_tick: 0.,
92 };
93 }
94
95 for (i, item_stack_data) in hotbar_slots.iter().enumerate() {
97 let this_item_speed;
98 match item_stack_data {
99 ItemStack::Empty => {
100 this_item_speed = Some(azalea_entity::mining::get_mine_progress(
101 block.as_ref(),
102 &ItemStack::Empty,
103 fluid_on_eyes,
104 physics,
105 attributes,
106 active_effects,
107 ));
108 }
109 ItemStack::Present(item_stack) => {
110 if !item_stack.component_patch.has::<components::Damage>() {
113 this_item_speed = Some(azalea_entity::mining::get_mine_progress(
114 block.as_ref(),
115 item_stack_data,
116 fluid_on_eyes,
117 physics,
118 attributes,
119 active_effects,
120 ));
121 } else {
122 this_item_speed = None;
123 }
124 }
125 }
126 if let Some(this_item_speed) = this_item_speed
127 && this_item_speed > best_speed
128 {
129 best_slot = Some(i);
130 best_speed = this_item_speed;
131 }
132 }
133
134 for (i, item_stack) in hotbar_slots.iter().enumerate() {
136 if item_stack.is_present() {
137 let this_item_speed = azalea_entity::mining::get_mine_progress(
138 block.as_ref(),
139 item_stack,
140 fluid_on_eyes,
141 physics,
142 attributes,
143 active_effects,
144 );
145 if this_item_speed > best_speed {
146 best_slot = Some(i);
147 best_speed = this_item_speed;
148 }
149 }
150 }
151
152 BestToolResult {
153 index: best_slot.unwrap_or(0),
154 percentage_per_tick: best_speed,
155 }
156}