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.",
171 std::any::type_name::<ActionQueue>()
172 );
173 action.on_remove(agent.into(), world);
174 action.on_drop(agent.into(), world, DropReason::Skipped);
175 return;
176 };
177
178 action_queue.push_back(action);
179 }
180 }
181 AddOrder::Front => {
182 for mut action in actions.rev() {
183 action.on_add(agent, world);
184
185 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
186 warn!(
187 "Cannot enqueue action {action:?} to non-existent agent {agent}. \
188 Action is therefore dropped immediately."
189 );
190 action.on_remove(None, world);
191 action.on_drop(None, world, DropReason::Skipped);
192 return;
193 };
194
195 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
196 warn!(
197 "Cannot enqueue action {action:?} to agent {agent} due to missing component {}. \
198 Action is therefore dropped immediately.",
199 std::any::type_name::<ActionQueue>()
200 );
201 action.on_remove(agent.into(), world);
202 action.on_drop(agent.into(), world, DropReason::Skipped);
203 return;
204 };
205
206 action_queue.push_front(action);
207 }
208 }
209 }
210
211 if config.start {
212 let Some(current_action) = world.get::<CurrentAction>(agent) else {
213 warn!(
214 "Could not start next action for agent {agent} due to missing component {}.",
215 std::any::type_name::<CurrentAction>()
216 );
217 return;
218 };
219
220 if current_action.is_none() {
221 Self::start_next_action(agent, world);
222 }
223 }
224 }
225
226 pub fn execute_actions(agent: Entity, world: &mut World) {
229 let Ok(agent_ref) = world.get_entity(agent) else {
230 warn!("Cannot execute actions for non-existent agent {agent}.");
231 return;
232 };
233
234 let Some(current_action) = agent_ref.get::<CurrentAction>() else {
235 warn!(
236 "Cannot execute actions for agent {agent} due to missing component {}.",
237 std::any::type_name::<CurrentAction>()
238 );
239 return;
240 };
241
242 if current_action.is_none() {
243 debug!("Executing actions for agent {agent}.");
244 Self::start_next_action(agent, world);
245 }
246 }
247
248 pub fn stop_current_action(agent: Entity, reason: StopReason, world: &mut World) {
250 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
251 warn!(
252 "Cannot stop current action for non-existent agent {agent} with reason {reason:?}."
253 );
254 return;
255 };
256
257 let Some(mut current_action) = agent_ref.get_mut::<CurrentAction>() else {
258 warn!(
259 "Cannot stop current action for agent {agent} with reason {reason:?} \
260 due to missing component {}.",
261 std::any::type_name::<CurrentAction>()
262 );
263 return;
264 };
265
266 if let Some(mut action) = current_action.take() {
267 debug!("Stopping current action {action:?} for agent {agent} with reason {reason:?}.");
268 action.on_stop(agent.into(), world, reason);
269
270 match reason {
271 StopReason::Finished | StopReason::Canceled => {
272 action.on_remove(agent.into(), world);
273 action.on_drop(agent.into(), world, DropReason::Done);
274 }
275 StopReason::Paused => {
276 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
277 warn!(
278 "Cannot enqueue paused action {action:?} to non-existent agent {agent}. \
279 Action is therefore dropped immediately."
280 );
281 action.on_remove(None, world);
282 action.on_drop(None, world, DropReason::Skipped);
283 return;
284 };
285
286 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
287 warn!(
288 "Cannot enqueue paused action {action:?} to agent {agent} due to missing component {}. \
289 Action is therefore dropped immediately.",
290 std::any::type_name::<ActionQueue>()
291 );
292 action.on_remove(agent.into(), world);
293 action.on_drop(agent.into(), world, DropReason::Skipped);
294 return;
295 };
296
297 action_queue.push_front(action);
298 }
299 }
300 }
301 }
302
303 pub fn start_next_action(agent: Entity, world: &mut World) {
312 #[cfg(debug_assertions)]
313 let mut counter: u16 = 0;
314
315 loop {
316 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
317 warn!("Cannot start next action for non-existent agent {agent}.");
318 break;
319 };
320
321 let Some(current_action) = agent_ref.get::<CurrentAction>() else {
322 warn!(
323 "Cannot start next action for agent {agent} due to missing component {}.",
324 std::any::type_name::<CurrentAction>()
325 );
326 break;
327 };
328
329 if let Some(action) = current_action.0.as_ref() {
330 warn!(
331 "Cannot start next action for agent {agent} \
332 as it already has current action {action:?}."
333 );
334 break;
335 }
336
337 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
338 warn!(
339 "Cannot start next action for agent {agent} due to missing component {}.",
340 std::any::type_name::<ActionQueue>()
341 );
342 break;
343 };
344
345 let Some(mut action) = action_queue.pop_front() else {
346 break;
347 };
348
349 debug!("Starting action {action:?} for agent {agent}.");
350 if !action.on_start(agent, world) {
351 match world.get_mut::<CurrentAction>(agent) {
352 Some(mut current_action) => {
353 current_action.0 = Some(action);
354 }
355 None => {
356 debug!("Canceling action {action:?} due to missing agent {agent}.");
357 action.on_stop(None, world, StopReason::Canceled);
358 action.on_remove(None, world);
359 action.on_drop(None, world, DropReason::Done);
360 }
361 }
362 break;
363 };
364
365 debug!("Finishing action {action:?} for agent {agent}.");
366 let agent = world.get_entity(agent).map(|_| agent).ok();
367 action.on_stop(agent, world, StopReason::Finished);
368 action.on_remove(agent, world);
369 action.on_drop(agent, world, DropReason::Done);
370
371 if agent.is_none() {
372 break;
373 }
374
375 #[cfg(debug_assertions)]
376 {
377 counter += 1;
378 if counter == u16::MAX {
379 panic!("infinite loop detected in starting next action");
380 }
381 }
382 }
383 }
384
385 pub fn skip_actions(agent: Entity, mut n: usize, world: &mut World) {
387 loop {
388 if n == 0 {
389 break;
390 }
391
392 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
393 warn!("Cannot skip next action for non-existent agent {agent}.");
394 break;
395 };
396
397 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
398 warn!(
399 "Cannot skip next action for agent {agent} due to missing component {}.",
400 std::any::type_name::<ActionQueue>()
401 );
402 break;
403 };
404
405 let Some(mut action) = action_queue.pop_front() else {
406 break;
407 };
408
409 debug!("Skipping action {action:?} for agent {agent}.");
410 action.on_remove(agent.into(), world);
411 action.on_drop(agent.into(), world, DropReason::Skipped);
412
413 n -= 1;
414 }
415 }
416
417 pub fn clear_actions(agent: Entity, world: &mut World) {
421 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
422 warn!("Cannot clear actions for non-existent agent {agent}.");
423 return;
424 };
425
426 let Some(mut current_action) = agent_ref.get_mut::<CurrentAction>() else {
427 warn!(
428 "Cannot clear current action for agent {agent} due to missing component {}.",
429 std::any::type_name::<CurrentAction>()
430 );
431 return;
432 };
433
434 if let Some(mut action) = current_action.take() {
435 debug!("Clearing current action {action:?} for agent {agent}.");
436 action.on_stop(agent.into(), world, StopReason::Canceled);
437 action.on_remove(agent.into(), world);
438 action.on_drop(agent.into(), world, DropReason::Cleared);
439 }
440
441 let Ok(mut agent_ref) = world.get_entity_mut(agent) else {
442 warn!("Cannot clear action queue for non-existent agent {agent}.");
443 return;
444 };
445
446 let Some(mut action_queue) = agent_ref.get_mut::<ActionQueue>() else {
447 warn!(
448 "Cannot clear action queue for agent {agent} due to missing component {}.",
449 std::any::type_name::<ActionQueue>()
450 );
451 return;
452 };
453
454 if action_queue.is_empty() {
455 return;
456 }
457
458 debug!("Clearing action queue {:?} for {agent}.", **action_queue);
459 let actions = std::mem::take(&mut action_queue.0);
460 for mut action in actions {
461 action.on_remove(agent.into(), world);
462 action.on_drop(agent.into(), world, DropReason::Cleared);
463 }
464 }
465}