bevy_sequential_actions/
traits.rs

1use super::*;
2
3/// The trait that all actions must implement.
4///
5/// It contains various methods that together defines the _lifecycle_ of an action.
6/// From this, you can create any action that can last as long as you like,
7/// and do as much as you like.
8///
9/// In general, the act of _starting_ the action should happen inside [`on_start`](`Self::on_start`).
10/// This is especially true for despawning an `agent`.
11/// The other methods should be used for initializing and cleaning up.
12/// See below for more information.
13///
14/// ## ⚠️ Warning
15///
16/// Since you are given a mutable [`World`], you can in practice do _anything_.
17/// Depending on what you do, the logic for advancing the action queue might not work properly.
18///
19/// There are a few things you should keep in mind:
20///
21/// * If you want to despawn an `agent` as an action, this should be done in [`on_start`](`Self::on_start`).
22/// * The [`execute`](`ManageActions::execute`) and [`next`](`ManageActions::next`) methods should not be used,
23///   as that will immediately advance the action queue while inside any of the trait methods.
24///   Instead, you should return `true` in [`on_start`](`Self::on_start`).
25/// * When adding new actions, you should set the [`start`](`ManageActions::start`) property to `false`.
26///   Otherwise, you will effectively call [`execute`](`ManageActions::execute`) which, again, should not be used.
27///   At worst, you will cause a **stack overflow** if the action adds itself.
28///
29/// ```rust,no_run
30/// # use bevy_ecs::prelude::*;
31/// # use bevy_sequential_actions::*;
32/// # struct EmptyAction;
33/// # impl Action for EmptyAction {
34/// #   fn is_finished(&self, _a: Entity, _w: &World) -> bool { true }
35/// #   fn on_start(&mut self, _a: Entity, _w: &mut World) -> bool { true }
36/// #   fn on_stop(&mut self, _a: Option<Entity>, _w: &mut World, _r: StopReason) {}
37/// # }
38/// # struct TestAction;
39/// # impl Action for TestAction {
40/// #   fn is_finished(&self, _a: Entity, _w: &World) -> bool { true }
41///     fn on_start(&mut self, agent: Entity, world: &mut World) -> bool {
42/// #       let action_a = EmptyAction;
43/// #       let action_b = EmptyAction;
44/// #       let action_c = EmptyAction;
45///         world
46///             .actions(agent)
47///             .start(false) // Do not start next action
48///             .add((action_a, action_b, action_c));
49///
50///         // Immediately advance the action queue
51///         true
52///     }
53/// #   fn on_stop(&mut self, _a: Option<Entity>, _w: &mut World, _r: StopReason) {}
54/// # }
55/// ```
56#[allow(unused_variables)]
57pub trait Action: downcast_rs::Downcast + Send + Sync + 'static {
58    /// Determines if an action is finished or not.
59    /// Advances the action queue when returning `true`.
60    ///
61    /// By default, this method is called every frame in the [`Last`] schedule.
62    fn is_finished(&self, agent: Entity, world: &World) -> bool;
63
64    /// The method that is called when an action is started.
65    ///
66    /// Typically here you would insert components to `agent` or a new entity
67    /// to make systems run. If you wish to despawn `agent` as an action,
68    /// **this is where you should do it**.
69    ///
70    /// Returning `true` here marks the action as already finished,
71    /// and will immediately advance the action queue.
72    fn on_start(&mut self, agent: Entity, world: &mut World) -> bool;
73
74    /// The method that is called when an action is stopped.
75    ///
76    /// Typically here you would clean up any stuff from [`on_start`](`Self::on_start`),
77    /// depending on the [`reason`](`StopReason`).
78    ///
79    /// When paused, the action will be put back into the action queue again to the front.
80    /// This means that it will start again when the next action is executed.
81    fn on_stop(&mut self, agent: Option<Entity>, world: &mut World, reason: StopReason);
82
83    /// The method that is called when an action is added to the queue.
84    ///
85    /// You can think of this as the _constructor_, as it will only be called once.
86    fn on_add(&mut self, agent: Entity, world: &mut World) {}
87
88    /// The method that is called when an action is removed from the queue.
89    ///
90    /// You can think of this as the _destructor_, as it will only be called once.
91    fn on_remove(&mut self, agent: Option<Entity>, world: &mut World) {}
92
93    /// The last method that is called with full ownership.
94    ///
95    /// This is useful for actions that might want to alter the behavior of the action queue.
96    /// For example, a `RepeatAction` could keep readding itself to the action queue based on some counter.
97    fn on_drop(self: Box<Self>, agent: Option<Entity>, world: &mut World, reason: DropReason) {}
98
99    /// Returns the type name of an action.
100    fn type_name(&self) -> &'static str {
101        std::any::type_name::<Self>()
102    }
103}
104
105downcast_rs::impl_downcast!(Action);
106
107impl std::fmt::Debug for BoxedAction {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        write!(f, "{}", self.type_name())
110    }
111}
112
113impl<OnStart> Action for OnStart
114where
115    OnStart: FnMut(Entity, &mut World) -> bool + Send + Sync + 'static,
116{
117    fn is_finished(&self, _agent: Entity, _world: &World) -> bool {
118        true
119    }
120
121    fn on_start(&mut self, agent: Entity, world: &mut World) -> bool {
122        (self)(agent, world)
123    }
124
125    fn on_stop(&mut self, _agent: Option<Entity>, _world: &mut World, _reason: StopReason) {}
126}
127
128/// Extension method for managing actions.
129/// Implemented for both [`Commands`] and [`World`].
130pub trait ActionsProxy {
131    /// Returns a type for managing actions for specified `agent`.
132    fn actions(&mut self, agent: Entity) -> impl ManageActions;
133}
134
135/// Methods for managing actions.
136pub trait ManageActions {
137    /// Sets the current [`config`](AddConfig) for actions to be added.
138    fn config(&mut self, config: AddConfig) -> &mut Self;
139
140    /// Sets the [`start`](AddConfig::start) field in the current [`config`](AddConfig).
141    ///
142    /// Default is `true`.
143    fn start(&mut self, start: bool) -> &mut Self;
144
145    /// Sets the [`order`](AddConfig::order) field in the current [`config`](AddConfig).
146    ///
147    /// Default is [`AddOrder::Back`].
148    fn order(&mut self, order: AddOrder) -> &mut Self;
149
150    /// Adds one or more actions to the queue.
151    fn add(&mut self, actions: impl IntoBoxedActions) -> &mut Self;
152
153    /// [`Starts`](Action::on_start) the next [`action`](Action) in the queue,
154    /// but only if there is no current action.
155    fn execute(&mut self) -> &mut Self;
156
157    /// [`Starts`](Action::on_start) the next [`action`](Action) in the queue.
158    ///
159    /// Current action is [`stopped`](Action::on_stop) as [`canceled`](StopReason::Canceled).
160    fn next(&mut self) -> &mut Self;
161
162    /// [`Stops`](Action::on_stop) the current action as [`canceled`](StopReason::Canceled).
163    ///
164    /// To resume the action queue, call either [`execute`](Self::execute) or [`next`](Self::next).
165    fn cancel(&mut self) -> &mut Self;
166
167    /// [`Stops`](Action::on_stop) the current action as [`paused`](StopReason::Paused).
168    ///
169    /// To resume the action queue, call either [`execute`](Self::execute) or [`next`](Self::next).
170    fn pause(&mut self) -> &mut Self;
171
172    /// Skips the next `n` actions in the queue.
173    fn skip(&mut self, n: usize) -> &mut Self;
174
175    /// Clears the action queue.
176    ///
177    /// Current action is [`stopped`](Action::on_stop) as [`canceled`](StopReason::Canceled).
178    fn clear(&mut self) -> &mut Self;
179}
180
181/// Conversion of an [`Action`] to a [`BoxedAction`].
182pub trait IntoBoxedAction {
183    /// Converts `self` into [BoxedAction].
184    fn into_boxed_action(self) -> BoxedAction;
185}
186
187impl<T: Action> IntoBoxedAction for T {
188    fn into_boxed_action(self) -> BoxedAction {
189        Box::new(self)
190    }
191}
192
193impl IntoBoxedAction for BoxedAction {
194    fn into_boxed_action(self) -> BoxedAction {
195        self
196    }
197}
198
199/// Conversion of actions to a collection of boxed actions.
200pub trait IntoBoxedActions {
201    /// Converts `self` into collection of boxed actions.
202    fn into_boxed_actions(
203        self,
204    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static;
205}
206
207impl<T: Action> IntoBoxedActions for T {
208    fn into_boxed_actions(
209        self,
210    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
211    {
212        [self.into_boxed_action()].into_iter()
213    }
214}
215
216macro_rules! impl_action_tuple {
217    ($($T:ident),+) => {
218        impl<$($T:Action),+> IntoBoxedActions for ($($T,)+) {
219            fn into_boxed_actions(
220                self,
221            ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
222            {
223                #[allow(non_snake_case)]
224                let ($($T,)+) = self;
225                [$( $T.into_boxed_action() ),+].into_iter()
226            }
227        }
228    };
229}
230
231variadics_please::all_tuples!(impl_action_tuple, 1, 15, T);
232
233impl IntoBoxedActions for BoxedAction {
234    fn into_boxed_actions(
235        self,
236    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
237    {
238        [self].into_iter()
239    }
240}
241
242impl<const N: usize> IntoBoxedActions for [BoxedAction; N] {
243    fn into_boxed_actions(
244        self,
245    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
246    {
247        self.into_iter()
248    }
249}
250
251impl IntoBoxedActions for Vec<BoxedAction> {
252    fn into_boxed_actions(
253        self,
254    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
255    {
256        self.into_iter()
257    }
258}
259
260impl IntoBoxedActions for VecDeque<BoxedAction> {
261    fn into_boxed_actions(
262        self,
263    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
264    {
265        self.into_iter()
266    }
267}
268
269impl IntoBoxedActions for std::collections::LinkedList<BoxedAction> {
270    fn into_boxed_actions(
271        self,
272    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
273    {
274        self.into_iter()
275    }
276}
277
278impl IntoBoxedActions for std::collections::BinaryHeap<BoxedAction> {
279    fn into_boxed_actions(
280        self,
281    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
282    {
283        self.into_iter()
284    }
285}