Skip to main content

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/// * You should not add new actions when the queue is being cleared or some actions are skipped.
57///   In order to reuse the allocated queue, actions are removed one by one until the queue is empty.
58///   Since you can add new actions to the queue between each removal, you can in practice do this forever.
59///
60///   ```rust,no_run
61///   # use bevy_ecs::prelude::*;
62///   # use bevy_sequential_actions::*;
63///   # struct EmptyAction;
64///   # impl Action for EmptyAction {
65///   #   fn is_finished(&self, _a: Entity, _w: &World) -> bool { true }
66///   #   fn on_start(&mut self, _a: Entity, _w: &mut World) -> bool { true }
67///   #   fn on_stop(&mut self, _a: Option<Entity>, _w: &mut World, _r: StopReason) {}
68///   # }
69///   # struct TestAction;
70///   # impl Action for TestAction {
71///   #   fn is_finished(&self, _a: Entity, _w: &World) -> bool { true }
72///   #   fn on_start(&mut self, _a: Entity, _w: &mut World) -> bool { true }
73///   #   fn on_stop(&mut self, _a: Option<Entity>, _w: &mut World, _r: StopReason) {}
74///   fn on_drop(self: Box<Self>, agent: Option<Entity>, world: &mut World, reason: DropReason) {
75///       match reason {
76///           DropReason::Done => {
77///               // ...
78///           }
79///           DropReason::Skipped => {
80///               // New actions added to the front of the queue here
81///               // will also be skipped if more skips follow
82///           }
83///           DropReason::Cleared => {
84///               // All new actions added to the queue here
85///               // will also be cleared
86///           }
87///       }
88///   }
89///   # }
90///   ```
91#[allow(unused_variables)]
92pub trait Action: downcast_rs::Downcast + Send + Sync + 'static {
93    /// Determines if an action is finished or not.
94    /// Advances the action queue when returning `true`.
95    ///
96    /// By default, this method is called every frame in the [`Last`] schedule.
97    fn is_finished(&self, agent: Entity, world: &World) -> bool;
98
99    /// The method that is called when an action is started.
100    ///
101    /// Typically here you would insert components to `agent` or a new entity
102    /// to make systems run. If you wish to despawn `agent` as an action,
103    /// **this is where you should do it**.
104    ///
105    /// Returning `true` here marks the action as already finished,
106    /// and will immediately advance the action queue.
107    fn on_start(&mut self, agent: Entity, world: &mut World) -> bool;
108
109    /// The method that is called when an action is stopped.
110    ///
111    /// Typically here you would clean up any stuff from [`on_start`](`Self::on_start`),
112    /// depending on the [`reason`](`StopReason`).
113    ///
114    /// When paused, the action will be put back into the action queue again to the front.
115    /// This means that it will start again when the next action is executed.
116    fn on_stop(&mut self, agent: Option<Entity>, world: &mut World, reason: StopReason);
117
118    /// The method that is called when an action is added to the queue.
119    ///
120    /// You can think of this as the _constructor_, as it will only be called once.
121    fn on_add(&mut self, agent: Entity, world: &mut World) {}
122
123    /// The method that is called when an action is removed from the queue.
124    ///
125    /// You can think of this as the _destructor_, as it will only be called once.
126    fn on_remove(&mut self, agent: Option<Entity>, world: &mut World) {}
127
128    /// The last method that is called with full ownership.
129    ///
130    /// This is useful for actions that might want to alter the behavior of the action queue.
131    /// For example, a `RepeatAction` could keep readding itself to the action queue based on some counter.
132    fn on_drop(self: Box<Self>, agent: Option<Entity>, world: &mut World, reason: DropReason) {}
133
134    /// Returns the type name of an action.
135    fn type_name(&self) -> &'static str {
136        std::any::type_name::<Self>()
137    }
138}
139
140downcast_rs::impl_downcast!(Action);
141
142impl std::fmt::Debug for BoxedAction {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        write!(f, "{}", self.type_name())
145    }
146}
147
148impl<OnStart> Action for OnStart
149where
150    OnStart: FnMut(Entity, &mut World) -> bool + Send + Sync + 'static,
151{
152    fn is_finished(&self, _agent: Entity, _world: &World) -> bool {
153        true
154    }
155
156    fn on_start(&mut self, agent: Entity, world: &mut World) -> bool {
157        (self)(agent, world)
158    }
159
160    fn on_stop(&mut self, _agent: Option<Entity>, _world: &mut World, _reason: StopReason) {}
161}
162
163/// Extension method for managing actions.
164/// Implemented for both [`Commands`] and [`World`].
165pub trait ActionsProxy {
166    /// Returns a type for managing actions for specified `agent`.
167    fn actions(&mut self, agent: Entity) -> impl ManageActions;
168}
169
170/// Methods for managing actions.
171pub trait ManageActions {
172    /// Sets the current [`config`](AddConfig) for actions to be added.
173    fn config(&mut self, config: AddConfig) -> &mut Self;
174
175    /// Sets the [`start`](AddConfig::start) field in the current [`config`](AddConfig).
176    ///
177    /// Default is `true`.
178    fn start(&mut self, start: bool) -> &mut Self;
179
180    /// Sets the [`order`](AddConfig::order) field in the current [`config`](AddConfig).
181    ///
182    /// Default is [`AddOrder::Back`].
183    fn order(&mut self, order: AddOrder) -> &mut Self;
184
185    /// Adds one or more actions to the queue.
186    fn add(&mut self, actions: impl IntoBoxedActions) -> &mut Self;
187
188    /// [`Starts`](Action::on_start) the next [`action`](Action) in the queue,
189    /// but only if there is no current action.
190    fn execute(&mut self) -> &mut Self;
191
192    /// [`Starts`](Action::on_start) the next [`action`](Action) in the queue.
193    ///
194    /// Current action is [`stopped`](Action::on_stop) as [`canceled`](StopReason::Canceled).
195    fn next(&mut self) -> &mut Self;
196
197    /// [`Stops`](Action::on_stop) the current action as [`canceled`](StopReason::Canceled).
198    ///
199    /// To resume the action queue, call either [`execute`](Self::execute) or [`next`](Self::next).
200    fn cancel(&mut self) -> &mut Self;
201
202    /// [`Stops`](Action::on_stop) the current action as [`paused`](StopReason::Paused).
203    ///
204    /// To resume the action queue, call either [`execute`](Self::execute) or [`next`](Self::next).
205    fn pause(&mut self) -> &mut Self;
206
207    /// Skips the next `n` actions in the queue.
208    fn skip(&mut self, n: usize) -> &mut Self;
209
210    /// Clears the action queue.
211    ///
212    /// Current action is [`stopped`](Action::on_stop) as [`canceled`](StopReason::Canceled).
213    fn clear(&mut self) -> &mut Self;
214}
215
216/// Conversion of an [`Action`] to a [`BoxedAction`].
217pub trait IntoBoxedAction {
218    /// Converts `self` into [BoxedAction].
219    fn into_boxed_action(self) -> BoxedAction;
220}
221
222impl<T: Action> IntoBoxedAction for T {
223    fn into_boxed_action(self) -> BoxedAction {
224        Box::new(self)
225    }
226}
227
228impl IntoBoxedAction for BoxedAction {
229    fn into_boxed_action(self) -> BoxedAction {
230        self
231    }
232}
233
234/// Conversion of actions to a collection of boxed actions.
235pub trait IntoBoxedActions {
236    /// Converts `self` into collection of boxed actions.
237    fn into_boxed_actions(
238        self,
239    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static;
240}
241
242impl<T: Action> IntoBoxedActions for T {
243    fn into_boxed_actions(
244        self,
245    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
246    {
247        [self.into_boxed_action()].into_iter()
248    }
249}
250
251macro_rules! impl_action_tuple {
252    ($($T:ident),+) => {
253        impl<$($T:Action),+> IntoBoxedActions for ($($T,)+) {
254            fn into_boxed_actions(
255                self,
256            ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
257            {
258                #[allow(non_snake_case)]
259                let ($($T,)+) = self;
260                [$( $T.into_boxed_action() ),+].into_iter()
261            }
262        }
263    };
264}
265
266variadics_please::all_tuples!(impl_action_tuple, 1, 15, T);
267
268impl IntoBoxedActions for BoxedAction {
269    fn into_boxed_actions(
270        self,
271    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
272    {
273        [self].into_iter()
274    }
275}
276
277impl<const N: usize> IntoBoxedActions for [BoxedAction; N] {
278    fn into_boxed_actions(
279        self,
280    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
281    {
282        self.into_iter()
283    }
284}
285
286impl IntoBoxedActions for Vec<BoxedAction> {
287    fn into_boxed_actions(
288        self,
289    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
290    {
291        self.into_iter()
292    }
293}
294
295impl IntoBoxedActions for VecDeque<BoxedAction> {
296    fn into_boxed_actions(
297        self,
298    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
299    {
300        self.into_iter()
301    }
302}
303
304impl IntoBoxedActions for std::collections::LinkedList<BoxedAction> {
305    fn into_boxed_actions(
306        self,
307    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
308    {
309        self.into_iter()
310    }
311}
312
313impl IntoBoxedActions for std::collections::BinaryHeap<BoxedAction> {
314    fn into_boxed_actions(
315        self,
316    ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
317    {
318        self.into_iter()
319    }
320}