1pub mod basic;
2pub mod parkour;
3pub mod uncommon;
4
5use std::{
6 fmt::{self, Debug},
7 sync::Arc,
8};
9
10use azalea_block::BlockState;
11use azalea_client::{
12 PhysicsState, SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection,
13 inventory::SetSelectedHotbarSlotEvent, mining::StartMiningBlockEvent,
14};
15use azalea_core::position::{BlockPos, Vec3};
16use azalea_inventory::Menu;
17use azalea_registry::builtin::BlockKind;
18use azalea_world::World;
19use bevy_ecs::{entity::Entity, message::MessageWriter, system::Commands, world::EntityWorldMut};
20use parking_lot::RwLock;
21use tracing::debug;
22
23use super::{
24 astar,
25 custom_state::CustomPathfinderStateRef,
26 mining::MiningCache,
27 positions::RelBlockPos,
28 world::{CachedWorld, is_block_state_passable},
29};
30use crate::{
31 auto_tool::best_tool_in_hotbar_for_block,
32 bot::{JumpEvent, LookAtEvent},
33 pathfinder::player_pos_to_block_pos,
34};
35
36type Edge = astar::Edge<RelBlockPos, MoveData>;
37
38pub type SuccessorsFn = fn(&mut MovesCtx, RelBlockPos);
39
40pub const BARITONE_COMPAT: bool = false;
45
46pub fn default_move(ctx: &mut MovesCtx, node: RelBlockPos) {
47 basic::basic_move(ctx, node);
48 parkour::parkour_move(ctx, node);
49 uncommon::uncommon_move(ctx, node);
50}
51
52#[derive(Clone)]
53pub struct MoveData {
54 pub execute: &'static (dyn Fn(ExecuteCtx) + Send + Sync),
57 pub is_reached: &'static (dyn Fn(IsReachedCtx) -> bool + Send + Sync),
59}
60impl Debug for MoveData {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 f.debug_struct("MoveData")
63 .finish()
65 }
66}
67
68pub struct ExecuteCtx<'s, 'w1, 'w2, 'w3, 'w4, 'w5, 'w6, 'a> {
69 pub entity: Entity,
70 pub target: BlockPos,
72 pub start: BlockPos,
74 pub position: Vec3,
75 pub physics: &'a azalea_entity::Physics,
76 pub is_currently_mining: bool,
77 pub can_mine: bool,
78 pub world: Arc<RwLock<World>>,
79 pub menu: Menu,
80
81 pub commands: &'a mut Commands<'w1, 's>,
82 pub look_at_events: &'a mut MessageWriter<'w2, LookAtEvent>,
83 pub sprint_events: &'a mut MessageWriter<'w3, StartSprintEvent>,
84 pub walk_events: &'a mut MessageWriter<'w4, StartWalkEvent>,
85 pub jump_events: &'a mut MessageWriter<'w5, JumpEvent>,
86 pub start_mining_events: &'a mut MessageWriter<'w6, StartMiningBlockEvent>,
87}
88
89impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_, '_> {
90 pub fn on_tick_start(&mut self) {
91 self.set_sneaking(false);
92 }
93
94 pub fn look_at(&mut self, position: Vec3) {
95 self.look_at_events.write(LookAtEvent {
96 entity: self.entity,
97 position: Vec3 {
98 x: position.x,
99 y: self.position.up(1.53).y,
101 z: position.z,
102 },
103 });
104 }
105
106 pub fn look_at_exact(&mut self, position: Vec3) {
107 self.look_at_events.write(LookAtEvent {
108 entity: self.entity,
109 position,
110 });
111 }
112
113 pub fn sprint(&mut self, direction: SprintDirection) {
114 self.sprint_events.write(StartSprintEvent {
115 entity: self.entity,
116 direction,
117 });
118 }
119
120 pub fn walk(&mut self, direction: WalkDirection) {
121 self.walk_events.write(StartWalkEvent {
122 entity: self.entity,
123 direction,
124 });
125 }
126
127 pub fn jump(&mut self) {
128 self.jump_events.write(JumpEvent {
129 entity: self.entity,
130 });
131 }
132
133 fn set_sneaking(&mut self, sneaking: bool) {
134 self.commands
135 .entity(self.entity)
136 .queue(move |mut entity: EntityWorldMut<'_>| {
137 if let Some(mut physics_state) = entity.get_mut::<PhysicsState>() {
138 physics_state.trying_to_crouch = sneaking;
139 }
140 });
141 }
142 pub fn sneak(&mut self) {
143 self.set_sneaking(true);
144 }
145
146 pub fn jump_if_in_water(&mut self) {
147 if self.physics.is_in_water() {
148 self.jump();
149 }
150 }
151
152 pub fn should_mine(&mut self, block: BlockPos) -> bool {
154 let block_state = self.world.read().get_block_state(block).unwrap_or_default();
155 should_mine_block_state(block_state)
156 }
157
158 pub fn mine(&mut self, block: BlockPos) -> bool {
162 if !self.can_mine {
163 return false;
164 }
165
166 let block_state = self.world.read().get_block_state(block).unwrap_or_default();
167 if is_block_state_passable(block_state) {
168 return false;
170 }
171
172 let best_tool_result = best_tool_in_hotbar_for_block(block_state, &self.menu);
173 debug!("best tool for {block_state:?}: {best_tool_result:?}");
174
175 self.commands.trigger(SetSelectedHotbarSlotEvent {
176 entity: self.entity,
177 slot: best_tool_result.index as u8,
178 });
179
180 self.is_currently_mining = true;
181
182 self.walk(WalkDirection::None);
183 self.look_at_exact(block.center());
184 self.start_mining_events.write(StartMiningBlockEvent {
185 entity: self.entity,
186 position: block,
187 force: true,
188 });
189
190 true
191 }
192
193 pub fn mine_while_at_start(&mut self, block: BlockPos) -> bool {
196 let horizontal_distance_from_start = (self.start.center() - self.position)
197 .horizontal_distance_squared()
198 .sqrt();
199 let at_start_position = player_pos_to_block_pos(self.position) == self.start
200 && horizontal_distance_from_start < 0.25;
201
202 if self.should_mine(block) {
203 if at_start_position {
204 self.look_at(block.center());
205 self.mine(block);
206 } else {
207 self.look_at(self.start.center());
208 self.walk(WalkDirection::Forward);
209 }
210 true
211 } else {
212 false
213 }
214 }
215
216 pub fn get_block_state(&self, block: BlockPos) -> BlockState {
217 self.world.read().get_block_state(block).unwrap_or_default()
218 }
219}
220
221pub fn should_mine_block_state(block_state: BlockState) -> bool {
222 if is_block_state_passable(block_state) || BlockKind::from(block_state) == BlockKind::Water {
223 return false;
225 }
226
227 true
228}
229
230pub struct IsReachedCtx<'a> {
231 pub target: BlockPos,
233 pub start: BlockPos,
235 pub position: Vec3,
236 pub physics: &'a azalea_entity::Physics,
237}
238
239#[must_use]
242pub fn default_is_reached(
243 IsReachedCtx {
244 position,
245 target,
246 physics,
247 ..
248 }: IsReachedCtx,
249) -> bool {
250 let block_pos = player_pos_to_block_pos(position);
251 if block_pos == target {
252 return true;
253 }
254 if physics.is_in_water() && block_pos.down(1) == target {
256 return true;
257 }
258
259 false
260}
261
262pub struct MovesCtx<'a> {
263 pub edges: &'a mut Vec<Edge>,
264 pub world: &'a CachedWorld,
265 pub mining_cache: &'a MiningCache,
266 pub custom_state: &'a CustomPathfinderStateRef,
267}