1use super::*;
2
3pub struct SequentialActionsPlugin;
11
12impl Plugin for SequentialActionsPlugin {
13 fn build(&self, app: &mut App) {
14 app.add_systems(Last, Self::check_actions::<()>);
15 app.world_mut()
16 .register_component_hooks::<CurrentAction>()
17 .on_remove(CurrentAction::on_remove_hook);
18 app.world_mut()
19 .register_component_hooks::<ActionQueue>()
20 .on_remove(ActionQueue::on_remove_hook);
21 }
22}
23
24impl SequentialActionsPlugin {
25 pub fn check_actions<F: QueryFilter>(
46 action_q: Query<(Entity, &CurrentAction), F>,
47 world: &World,
48 mut commands: Commands,
49 ) {
50 action_q
51 .iter()
52 .filter_map(|(agent, current_action)| {
53 current_action
54 .as_ref()
55 .and_then(|action| action.is_finished(agent, world).then_some(agent))
56 })
57 .for_each(|agent| {
58 commands.queue(move |world: &mut World| {
59 Self::stop_current_action(agent, StopReason::Finished, world);
60 Self::start_next_action(agent, world);
61 });
62 });
63 }
64
65 pub fn add_action(
67 agent: Entity,
68 config: AddConfig,
69 action: impl IntoBoxedAction,
70 world: &mut World,
71 ) {
72 let mut action = action.into_boxed_action();
73
74 if world.get_entity(agent).is_err() {
75 warn!("Cannot add action {action:?} to non-existent agent {agent}.");
76 return;
77 }
78
79 debug!("Adding action {action:?} for agent {agent} with {config:?}.");
80 action.on_add(agent, world);
81
82 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
83 warn!(
84 "Cannot enqueue action {action:?} to non-existent agent {agent}. \
85 Action is therefore dropped immediately."
86 );
87 action.on_remove(None, world);
88 action.on_drop(None, world, DropReason::Skipped);
89 return;
90 };
91
92 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
93 warn!(
94 "Cannot enqueue action {action:?} to agent {agent} due to missing component {}. \
95 Action is therefore dropped immediately.",
96 std::any::type_name::<ActionQueue>()
97 );
98 action.on_remove(agent.into(), world);
99 action.on_drop(agent.into(), world, DropReason::Skipped);
100 return;
101 };
102
103 match config.order {
104 AddOrder::Back => action_queue.push_back(action),
105 AddOrder::Front => action_queue.push_front(action),
106 }
107
108 if config.start {
109 let Some(current_action) = agent_ref.get::<CurrentAction>() else {
110 warn!(
111 "Could not start next action for agent {agent} due to missing component {}.",
112 std::any::type_name::<CurrentAction>()
113 );
114 return;
115 };
116
117 if current_action.is_none() {
118 Self::start_next_action(agent, world);
119 }
120 }
121 }
122
123 pub fn add_actions<I>(agent: Entity, config: AddConfig, actions: I, world: &mut World)
126 where
127 I: DoubleEndedIterator<Item = BoxedAction> + ExactSizeIterator + Debug,
128 {
129 let actions = actions.into_iter();
130 let len = actions.len();
131
132 if len == 0 {
133 return;
134 }
135
136 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
137 warn!("Cannot add actions {actions:?} to non-existent agent {agent}.");
138 return;
139 };
140
141 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
142 warn!(
143 "Cannot add actions {actions:?} to agent {agent} due to missing component {}.",
144 std::any::type_name::<ActionQueue>()
145 );
146 return;
147 };
148
149 debug!("Adding actions {actions:?} for agent {agent} with {config:?}.");
150 action_queue.reserve(len);
151
152 match config.order {
153 AddOrder::Back => {
154 for mut action in actions {
155 action.on_add(agent, world);
156
157 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
158 warn!(
159 "Cannot enqueue action {action:?} to non-existent agent {agent}. \
160 Action is therefore dropped immediately."
161 );
162 action.on_remove(None, world);
163 action.on_drop(None, world, DropReason::Skipped);
164 return;
165 };
166
167 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
168 warn!(
169 "Cannot enqueue action {action:?} to agent {agent} due to missing component {}. \
170 Action is therefore dropped immediately.", std::any::type_name::<ActionQueue>()
171 );
172 action.on_remove(agent.into(), world);
173 action.on_drop(agent.into(), world, DropReason::Skipped);
174 return;
175 };
176
177 action_queue.push_back(action);
178 }
179 }
180 AddOrder::Front => {
181 for mut action in actions.rev() {
182 action.on_add(agent, world);
183
184 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
185 warn!(
186 "Cannot enqueue action {action:?} to non-existent agent {agent}. \
187 Action is therefore dropped immediately."
188 );
189 action.on_remove(None, world);
190 action.on_drop(None, world, DropReason::Skipped);
191 return;
192 };
193
194 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
195 warn!(
196 "Cannot enqueue action {action:?} to agent {agent} due to missing component {}. \
197 Action is therefore dropped immediately.", std::any::type_name::<ActionQueue>()
198 );
199 action.on_remove(agent.into(), world);
200 action.on_drop(agent.into(), world, DropReason::Skipped);
201 return;
202 };
203
204 action_queue.push_front(action);
205 }
206 }
207 }
208
209 if config.start {
210 let Some(current_action) = world.get::<CurrentAction>(agent) else {
211 warn!(
212 "Could not start next action for agent {agent} due to missing component {}.",
213 std::any::type_name::<CurrentAction>()
214 );
215 return;
216 };
217
218 if current_action.is_none() {
219 Self::start_next_action(agent, world);
220 }
221 }
222 }
223
224 pub fn execute_actions(agent: Entity, world: &mut World) {
227 let Ok(agent_ref) = world.get_entity(agent) else {
228 warn!("Cannot execute actions for non-existent agent {agent}.");
229 return;
230 };
231
232 let Some(current_action) = agent_ref.get::<CurrentAction>() else {
233 warn!(
234 "Cannot execute actions for agent {agent} due to missing component {}.",
235 std::any::type_name::<CurrentAction>()
236 );
237 return;
238 };
239
240 if current_action.is_none() {
241 debug!("Executing actions for agent {agent}.");
242 Self::start_next_action(agent, world);
243 }
244 }
245
246 pub fn stop_current_action(agent: Entity, reason: StopReason, world: &mut World) {
248 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
249 warn!(
250 "Cannot stop current action for non-existent agent {agent} with reason {reason:?}."
251 );
252 return;
253 };
254
255 let Some(mut current_action) = agent_ref.get_mut::<CurrentAction>() else {
256 warn!(
257 "Cannot stop current action for agent {agent} with reason {reason:?} \
258 due to missing component {}.",
259 std::any::type_name::<CurrentAction>()
260 );
261 return;
262 };
263
264 if let Some(mut action) = current_action.take() {
265 debug!("Stopping current action {action:?} for agent {agent} with reason {reason:?}.");
266 action.on_stop(agent.into(), world, reason);
267
268 match reason {
269 StopReason::Finished | StopReason::Canceled => {
270 action.on_remove(agent.into(), world);
271 action.on_drop(agent.into(), world, DropReason::Done);
272 }
273 StopReason::Paused => {
274 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
275 warn!(
276 "Cannot enqueue paused action {action:?} to non-existent agent {agent}. \
277 Action is therefore dropped immediately."
278 );
279 action.on_remove(None, world);
280 action.on_drop(None, world, DropReason::Skipped);
281 return;
282 };
283
284 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
285 warn!(
286 "Cannot enqueue paused action {action:?} to agent {agent} due to missing component {}. \
287 Action is therefore dropped immediately.", std::any::type_name::<ActionQueue>()
288 );
289 action.on_remove(agent.into(), world);
290 action.on_drop(agent.into(), world, DropReason::Skipped);
291 return;
292 };
293
294 action_queue.push_front(action);
295 }
296 }
297 }
298 }
299
300 pub fn start_next_action(agent: Entity, world: &mut World) {
309 #[cfg(debug_assertions)]
310 let mut counter: u16 = 0;
311
312 loop {
313 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
314 warn!("Cannot start next action for non-existent agent {agent}.");
315 break;
316 };
317
318 let Some(current_action) = agent_ref.get::<CurrentAction>() else {
319 warn!(
320 "Cannot start next action for agent {agent} due to missing component {}.",
321 std::any::type_name::<CurrentAction>()
322 );
323 break;
324 };
325
326 if let Some(action) = current_action.0.as_ref() {
327 warn!(
328 "Cannot start next action for agent {agent} \
329 as it already has current action {action:?}."
330 );
331 break;
332 }
333
334 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
335 warn!(
336 "Cannot start next action for agent {agent} due to missing component {}.",
337 std::any::type_name::<ActionQueue>()
338 );
339 break;
340 };
341
342 let Some(mut action) = action_queue.pop_front() else {
343 break;
344 };
345
346 debug!("Starting action {action:?} for agent {agent}.");
347 if !action.on_start(agent, world) {
348 match world.get_mut::<CurrentAction>(agent) {
349 Some(mut current_action) => {
350 current_action.0 = Some(action);
351 }
352 None => {
353 debug!("Canceling action {action:?} due to missing agent {agent}.");
354 action.on_stop(None, world, StopReason::Canceled);
355 action.on_remove(None, world);
356 action.on_drop(None, world, DropReason::Done);
357 }
358 }
359 break;
360 };
361
362 debug!("Finishing action {action:?} for agent {agent}.");
363 let agent = world.get_entity(agent).map(|_| agent).ok();
364 action.on_stop(agent, world, StopReason::Finished);
365 action.on_remove(agent, world);
366 action.on_drop(agent, world, DropReason::Done);
367
368 if agent.is_none() {
369 break;
370 }
371
372 #[cfg(debug_assertions)]
373 {
374 counter += 1;
375 if counter == u16::MAX {
376 panic!("infinite loop detected in starting next action");
377 }
378 }
379 }
380 }
381
382 pub fn skip_actions(agent: Entity, mut n: usize, world: &mut World) {
384 loop {
385 if n == 0 {
386 break;
387 }
388
389 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
390 warn!("Cannot skip next action for non-existent agent {agent}.");
391 break;
392 };
393
394 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
395 warn!(
396 "Cannot skip next action for agent {agent} due to missing component {}.",
397 std::any::type_name::<ActionQueue>()
398 );
399 break;
400 };
401
402 let Some(mut action) = action_queue.pop_front() else {
403 break;
404 };
405
406 debug!("Skipping action {action:?} for agent {agent}.");
407 action.on_remove(agent.into(), world);
408 action.on_drop(agent.into(), world, DropReason::Skipped);
409
410 n -= 1;
411 }
412 }
413
414 pub fn clear_actions(agent: Entity, world: &mut World) {
418 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
419 warn!("Cannot clear actions for non-existent agent {agent}.");
420 return;
421 };
422
423 let Some(mut current_action) = agent_ref.get_mut::<CurrentAction>() else {
424 warn!(
425 "Cannot clear current action for agent {agent} due to missing component {}.",
426 std::any::type_name::<CurrentAction>()
427 );
428 return;
429 };
430
431 if let Some(mut action) = current_action.take() {
432 debug!("Clearing current action {action:?} for agent {agent}.");
433 action.on_stop(agent.into(), world, StopReason::Canceled);
434 action.on_remove(agent.into(), world);
435 action.on_drop(agent.into(), world, DropReason::Cleared);
436 }
437
438 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
439 warn!("Cannot clear action queue for non-existent agent {agent}.");
440 return;
441 };
442
443 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
444 warn!(
445 "Cannot clear action queue for agent {agent} due to missing component {}.",
446 std::any::type_name::<ActionQueue>()
447 );
448 return;
449 };
450
451 if action_queue.is_empty() {
452 return;
453 }
454
455 debug!("Clearing action queue {:?} for {agent}.", **action_queue);
456 let actions = std::mem::take(&mut action_queue.0);
457 for mut action in actions {
458 action.on_remove(agent.into(), world);
459 action.on_drop(agent.into(), world, DropReason::Cleared);
460 }
461 }
462}