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`](`ModifyActions::execute`) and [`next`](`ModifyActions::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`](`ModifyActions::start`) property to `false`.
26/// Otherwise, you will effectively call [`execute`](`ModifyActions::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/// Proxy method for modifying actions.
129pub trait ActionsProxy {
130 /// Returns a type for modifying actions for specified `agent`.
131 fn actions(&mut self, agent: Entity) -> impl ModifyActions;
132}
133
134/// Methods for modifying actions.
135pub trait ModifyActions {
136 /// Sets the current [`config`](AddConfig) for actions to be added.
137 fn config(&mut self, config: AddConfig) -> &mut Self;
138
139 /// Sets the [`start`](AddConfig::start) field in the current [`config`](AddConfig).
140 ///
141 /// Default is `true`.
142 fn start(&mut self, start: bool) -> &mut Self;
143
144 /// Sets the [`order`](AddConfig::order) field in the current [`config`](AddConfig).
145 ///
146 /// Default is [`AddOrder::Back`].
147 fn order(&mut self, order: AddOrder) -> &mut Self;
148
149 /// Adds one or more actions to the queue.
150 fn add(&mut self, actions: impl IntoBoxedActions) -> &mut Self;
151
152 /// [`Starts`](Action::on_start) the next [`action`](Action) in the queue,
153 /// but only if there is no current action.
154 fn execute(&mut self) -> &mut Self;
155
156 /// [`Starts`](Action::on_start) the next [`action`](Action) in the queue.
157 ///
158 /// Current action is [`stopped`](Action::on_stop) as [`canceled`](StopReason::Canceled).
159 fn next(&mut self) -> &mut Self;
160
161 /// [`Stops`](Action::on_stop) the current action as [`canceled`](StopReason::Canceled).
162 ///
163 /// To resume the action queue, call either [`execute`](Self::execute) or [`next`](Self::next).
164 fn cancel(&mut self) -> &mut Self;
165
166 /// [`Stops`](Action::on_stop) the current action as [`paused`](StopReason::Paused).
167 ///
168 /// To resume the action queue, call either [`execute`](Self::execute) or [`next`](Self::next).
169 fn pause(&mut self) -> &mut Self;
170
171 /// Skips the next `n` actions in the queue.
172 fn skip(&mut self, n: usize) -> &mut Self;
173
174 /// Clears the action queue.
175 ///
176 /// Current action is [`stopped`](Action::on_stop) as [`canceled`](StopReason::Canceled).
177 fn clear(&mut self) -> &mut Self;
178}
179
180/// Conversion of an [`Action`] to a [`BoxedAction`].
181pub trait IntoBoxedAction {
182 /// Converts `self` into [BoxedAction].
183 fn into_boxed_action(self) -> BoxedAction;
184}
185
186impl<T: Action> IntoBoxedAction for T {
187 fn into_boxed_action(self) -> BoxedAction {
188 Box::new(self)
189 }
190}
191
192impl IntoBoxedAction for BoxedAction {
193 fn into_boxed_action(self) -> BoxedAction {
194 self
195 }
196}
197
198/// Conversion of actions to a collection of boxed actions.
199pub trait IntoBoxedActions {
200 /// Converts `self` into collection of boxed actions.
201 fn into_boxed_actions(
202 self,
203 ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static;
204}
205
206impl<T: Action> IntoBoxedActions for T {
207 fn into_boxed_actions(
208 self,
209 ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
210 {
211 [self.into_boxed_action()].into_iter()
212 }
213}
214
215macro_rules! impl_action_tuple {
216 ($($T:ident),+) => {
217 impl<$($T:Action),+> IntoBoxedActions for ($($T,)+) {
218 fn into_boxed_actions(
219 self,
220 ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
221 {
222 #[allow(non_snake_case)]
223 let ($($T,)+) = self;
224 [$( $T.into_boxed_action() ),+].into_iter()
225 }
226 }
227 };
228}
229
230variadics_please::all_tuples!(impl_action_tuple, 1, 15, T);
231
232impl IntoBoxedActions for BoxedAction {
233 fn into_boxed_actions(
234 self,
235 ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
236 {
237 [self].into_iter()
238 }
239}
240
241impl<const N: usize> IntoBoxedActions for [BoxedAction; N] {
242 fn into_boxed_actions(
243 self,
244 ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
245 {
246 self.into_iter()
247 }
248}
249
250impl IntoBoxedActions for Vec<BoxedAction> {
251 fn into_boxed_actions(
252 self,
253 ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
254 {
255 self.into_iter()
256 }
257}
258
259impl IntoBoxedActions for VecDeque<BoxedAction> {
260 fn into_boxed_actions(
261 self,
262 ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
263 {
264 self.into_iter()
265 }
266}
267
268impl IntoBoxedActions for std::collections::LinkedList<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 IntoBoxedActions for std::collections::BinaryHeap<BoxedAction> {
278 fn into_boxed_actions(
279 self,
280 ) -> impl DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Send + Debug + 'static
281 {
282 self.into_iter()
283 }
284}