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
73
74
75
76
77
78
79
80
81
use std::cmp::Ordering;

use bevy_app::{App, CoreSet, Plugin};
use bevy_ecs::schedule::SystemConfigs;

use crate::*;

/// The [`Plugin`] for this library that must be added to [`App`] in order for everything to work.
///
/// This plugin adds the necessary systems for advancing the action queue for each `agent`.
/// By default, the systems will be added to [`CoreStage::Last`].
/// If you want to schedule the systems yourself, use [`get_systems`](Self::get_systems).
///
/// ```rust,no_run
/// use bevy::prelude::*;
/// use bevy_sequential_actions::*;
///
/// fn main() {
///     App::new()
///         .add_plugins(DefaultPlugins)
///         .add_plugin(SequentialActionsPlugin)
///         .run();
/// }
/// ```
pub struct SequentialActionsPlugin;

impl SequentialActionsPlugin {
    /// Returns the systems used by this plugin.
    /// Useful if you want to schedule the systems yourself.
    ///
    /// ```rust,no_run
    /// use bevy::prelude::*;
    /// use bevy_sequential_actions::*;
    ///
    /// fn main() {
    ///     App::new()
    ///         .add_plugins(DefaultPlugins)
    ///         .add_systems(SequentialActionsPlugin::get_systems().in_base_set(CoreSet::Last))
    ///         .run();
    /// }
    /// ```
    pub fn get_systems() -> SystemConfigs {
        (check_actions,).into_configs()
    }
}

impl Plugin for SequentialActionsPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Self::get_systems().in_base_set(CoreSet::Last));
    }
}

fn check_actions(
    mut action_q: Query<(Entity, &CurrentAction, &mut ActionFinished), Changed<ActionFinished>>,
    mut commands: Commands,
) {
    for (agent, current_action, mut finished) in action_q.iter_mut() {
        if let Some((current_action, _)) = &current_action.0 {
            let finished_count = finished.total();
            let active_count = current_action.len();

            match finished_count.cmp(&active_count) {
                Ordering::Less => {
                    finished.reset_count = 0;
                }
                Ordering::Equal => {
                    commands.add(move |world: &mut World| {
                        world.finish_action(agent);
                    });
                }
                Ordering::Greater => {
                    panic!(
                        "Finished actions exceeds active. \
                        Entity {agent:?} has {active_count} active action(s), \
                        but a total of {finished_count} action(s) have been confirmed finished."
                    );
                }
            }
        }
    }
}