azalea/pathfinder/goto_event.rs
1use std::{sync::Arc, time::Duration};
2
3use bevy_ecs::{entity::Entity, event::Event};
4
5use crate::pathfinder::{
6 astar::PathfinderTimeout,
7 goals::Goal,
8 moves::{self, SuccessorsFn},
9};
10
11/// Send this event to start pathfinding to the given goal.
12///
13/// Also see [`PathfinderClientExt::goto`].
14///
15/// This event is read by [`goto_listener`]
16///
17/// [`goto_listener`]: crate::pathfinder::goto_listener
18/// [`PathfinderClientExt::goto`]: crate::pathfinder::PathfinderClientExt::goto
19#[derive(Event)]
20#[non_exhaustive]
21pub struct GotoEvent {
22 /// The local bot entity that will do the pathfinding and execute the path.
23 pub entity: Entity,
24 pub goal: Arc<dyn Goal>,
25 pub opts: PathfinderOpts,
26}
27
28impl GotoEvent {
29 pub fn new(entity: Entity, goal: impl Goal + 'static, opts: PathfinderOpts) -> Self {
30 Self {
31 entity,
32 goal: Arc::new(goal),
33 opts,
34 }
35 }
36}
37
38/// Configuration options that the pathfinder will use when calculating and
39/// executing a path.
40///
41/// This can be passed into [`Client::goto_with_opts`] or
42/// [`Client::start_goto_with_opts`].
43///
44/// ```
45/// # use azalea::pathfinder::{moves, PathfinderOpts};
46/// // example config to disallow mining blocks and to not do parkour
47/// let opts = PathfinderOpts::new()
48/// .allow_mining(false)
49/// .successors_fn(moves::basic::basic_move);
50/// ```
51///
52/// [`Client::goto_with_opts`]: super::PathfinderClientExt::goto_with_opts
53/// [`Client::start_goto_with_opts`]: super::PathfinderClientExt::start_goto_with_opts
54#[derive(Clone, Debug)]
55#[non_exhaustive]
56pub struct PathfinderOpts {
57 pub(crate) successors_fn: SuccessorsFn,
58 pub(crate) allow_mining: bool,
59 pub(crate) retry_on_no_path: bool,
60 pub(crate) min_timeout: PathfinderTimeout,
61 pub(crate) max_timeout: PathfinderTimeout,
62}
63
64impl PathfinderOpts {
65 pub const fn new() -> Self {
66 Self {
67 successors_fn: moves::default_move,
68 allow_mining: true,
69 retry_on_no_path: true,
70 min_timeout: PathfinderTimeout::Time(Duration::from_secs(1)),
71 max_timeout: PathfinderTimeout::Time(Duration::from_secs(5)),
72 }
73 }
74 /// Set the function that's used for checking what moves are possible.
75 ///
76 /// Defaults to [`moves::default_move`].
77 pub fn successors_fn(mut self, successors_fn: SuccessorsFn) -> Self {
78 self.successors_fn = successors_fn;
79 self
80 }
81 /// Set whether the bot is allowed to break blocks while pathfinding.
82 ///
83 /// Defaults to `true`.
84 pub fn allow_mining(mut self, allow_mining: bool) -> Self {
85 self.allow_mining = allow_mining;
86 self
87 }
88 /// Whether we should recalculate the path when the pathfinder timed out and
89 /// there's no partial path to try.
90 ///
91 /// Defaults to `true`.
92 pub fn retry_on_no_path(mut self, retry_on_no_path: bool) -> Self {
93 self.retry_on_no_path = retry_on_no_path;
94 self
95 }
96 /// The minimum amount of time that should pass before the A* pathfinder
97 /// function can return a timeout if it finds a path that seems good enough.
98 /// It may take up to [`Self::max_timeout`] if it can't immediately find
99 /// a usable path.
100 ///
101 /// Defaults to `PathfinderTimeout::Time(Duration::from_secs(1))`.
102 ///
103 /// Also see [`PathfinderTimeout::Nodes`]
104 pub fn min_timeout(mut self, min_timeout: PathfinderTimeout) -> Self {
105 self.min_timeout = min_timeout;
106 self
107 }
108 /// The absolute maximum amount of time that the pathfinder function can
109 /// take to find a path. If it takes this long, it means no usable path was
110 /// found (so it might be impossible).
111 ///
112 /// Defaults to `PathfinderTimeout::Time(Duration::from_secs(5))`.
113 pub fn max_timeout(mut self, max_timeout: PathfinderTimeout) -> Self {
114 self.max_timeout = max_timeout;
115 self
116 }
117}
118impl Default for PathfinderOpts {
119 fn default() -> Self {
120 Self::new()
121 }
122}