1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use bevy_ecs::{query::ReadOnlyWorldQuery, system::BoxedSystem};

use crate::*;

/// The [`Plugin`] for this library that must be added to [`App`] in order for everything to work.
///
/// This plugin adds a system that advances the action queue for each `agent`.
/// By default, the system is added to [`CoreSet::Last`].
/// For custom scheduling, see [`ActionHandler::check_actions`].
///
/// # Example
///
/// ```rust,no_run
/// # use bevy_ecs::prelude::*;
/// # use bevy_app::prelude::*;
/// # use bevy_sequential_actions::*;
/// # fn main() {
/// App::new()
///     .add_plugin(SequentialActionsPlugin)
///     .run();
/// # }
/// ```
pub struct SequentialActionsPlugin;

impl Plugin for SequentialActionsPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Last, check_actions::<()>);
    }
}

impl ActionHandler {
    /// The [`System`] used by [`SequentialActionsPlugin`].
    /// It is responsible for checking all agents for finished actions
    /// and advancing the action queue.
    ///
    /// The query filter `F` is used for filtering agents.
    /// Use the unit type `()` for no filtering.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # use bevy_ecs::prelude::*;
    /// # use bevy_app::prelude::*;
    /// # use bevy_sequential_actions::*;
    /// #
    /// # fn main() {
    /// App::new()
    ///     .add_systems(Last, ActionHandler::check_actions::<()>())
    ///     .run();
    /// # }
    /// ```
    pub fn check_actions<F: ReadOnlyWorldQuery + 'static>() -> BoxedSystem {
        Box::new(IntoSystem::into_system(check_actions::<F>))
    }
}

fn check_actions<F: ReadOnlyWorldQuery>(
    action_q: Query<(Entity, &CurrentAction), F>,
    world: &World,
    mut commands: Commands,
) {
    action_q.for_each(|(agent, current_action)| {
        if let Some(action) = current_action.as_ref() {
            if action.is_finished(agent, world) {
                commands.add(move |world: &mut World| {
                    ActionHandler::stop_current(agent, StopReason::Finished, world);
                    ActionHandler::start_next(agent, world);
                });
            }
        }
    });
}