big_brain/
actions.rs

1//! Defines Action-related functionality. This module includes the
2//! ActionBuilder trait and some Composite Actions for utility.
3use std::sync::Arc;
4
5use bevy::prelude::*;
6#[cfg(feature = "trace")]
7use bevy::utils::tracing::trace;
8
9use crate::thinker::{Action, ActionSpan, Actor};
10
11/// The current state for an Action. These states are changed by a combination
12/// of the Thinker that spawned it, and the actual Action system executing the
13/// Action itself.
14///
15/// Action system implementors should be mindful of taking appropriate action
16/// on all of these states, and be particularly careful when ignoring
17/// variants.
18#[derive(Debug, Clone, Component, Eq, PartialEq, Reflect)]
19#[component(storage = "SparseSet")]
20pub enum ActionState {
21    /// Initial state. No action should be performed.
22    Init,
23
24    /// Action requested. The Action-handling system should start executing
25    /// this Action ASAP and change the status to the next state.
26    Requested,
27
28    /// The action has ongoing execution. The associated Thinker will try to
29    /// keep executing this Action as-is until it changes state or it gets
30    /// Cancelled.
31    Executing,
32
33    /// An ongoing Action has been cancelled. The Thinker might set this
34    /// action for you, so for Actions that execute for longer than a single
35    /// tick, **you must check whether the Cancelled state was set** and
36    /// change do either Success or Failure. Thinkers will wait on Cancelled
37    /// actions to do any necessary cleanup work, so this can hang your AI if
38    /// you don't look for it.
39    Cancelled,
40
41    /// The Action was a success. This is used by Composite Actions to
42    /// determine whether to continue execution.
43    Success,
44
45    /// The Action failed. This is used by Composite Actions to determine
46    /// whether to halt execution.
47    Failure,
48}
49
50impl Default for ActionState {
51    fn default() -> Self {
52        Self::Init
53    }
54}
55
56impl ActionState {
57    pub fn new() -> Self {
58        Self::default()
59    }
60}
61
62#[derive(Debug, Clone, Eq, PartialEq)]
63pub(crate) struct ActionBuilderId;
64
65#[derive(Debug, Clone)]
66pub(crate) struct ActionBuilderWrapper(pub Arc<ActionBuilderId>, pub Arc<dyn ActionBuilder>);
67
68impl ActionBuilderWrapper {
69    pub fn new(builder: Arc<dyn ActionBuilder>) -> Self {
70        ActionBuilderWrapper(Arc::new(ActionBuilderId), builder)
71    }
72}
73
74/// Trait that must be defined by types in order to be `ActionBuilder`s.
75/// `ActionBuilder`s' job is to spawn new `Action` entities on demand. In
76/// general, most of this is already done for you, and the only method you
77/// really have to implement is `.build()`.
78///
79/// The `build()` method MUST be implemented for any `ActionBuilder`s you want
80/// to define.
81#[reflect_trait]
82pub trait ActionBuilder: std::fmt::Debug + Send + Sync {
83    /// MUST insert your concrete Action component into the Scorer [`Entity`],
84    /// using `cmd`. You _may_ use `actor`, but it's perfectly normal to just
85    /// ignore it.
86    ///
87    /// In most cases, your `ActionBuilder` and `Action` can be the same type.
88    /// The only requirement is that your struct implements `Debug`,
89    /// `Component, `Clone`. You can then use the derive macro `ActionBuilder`
90    /// to turn your struct into a `ActionBuilder`
91    ///
92    /// ### Example
93    ///
94    /// Using the derive macro (the easy way):
95    ///
96    /// ```
97    /// # use bevy::prelude::*;
98    /// # use big_brain::prelude::*;
99    /// #[derive(Debug, Clone, Component, ActionBuilder)]
100    /// #[action_label = "MyActionLabel"] // Optional. Defaults to type name.
101    /// struct MyAction;
102    /// ```
103    ///
104    /// Implementing it manually:
105    ///
106    /// ```
107    /// # use bevy::prelude::*;
108    /// # use big_brain::prelude::*;
109    /// #[derive(Debug)]
110    /// struct MyBuilder;
111    /// #[derive(Debug, Component)]
112    /// struct MyAction;
113    ///
114    /// impl ActionBuilder for MyBuilder {
115    ///   fn build(&self, cmd: &mut Commands, action: Entity, actor: Entity) {
116    ///     cmd.entity(action).insert(MyAction);
117    ///   }
118    /// }
119    /// ```
120    fn build(&self, cmd: &mut Commands, action: Entity, actor: Entity);
121
122    /**
123     * A label to display when logging using the Action's tracing span.
124     */
125    fn label(&self) -> Option<&str> {
126        None
127    }
128}
129
130/// Spawns a new Action Component, using the given ActionBuilder. This is
131/// useful when you're doing things like writing composite Actions.
132pub fn spawn_action<T: ActionBuilder + ?Sized>(
133    builder: &T,
134    cmd: &mut Commands,
135    actor: Entity,
136) -> Entity {
137    let action_ent = Action(cmd.spawn_empty().id());
138    let span = ActionSpan::new(action_ent.entity(), ActionBuilder::label(builder));
139    let _guard = span.span().enter();
140    debug!("New Action spawned.");
141    cmd.entity(action_ent.entity())
142        .insert(Name::new("Action"))
143        .insert(ActionState::new())
144        .insert(Actor(actor));
145    builder.build(cmd, action_ent.entity(), actor);
146    std::mem::drop(_guard);
147    cmd.entity(action_ent.entity()).insert(span);
148    action_ent.entity()
149}
150
151/// [`ActionBuilder`] for the [`Steps`] component. Constructed through
152/// `Steps::build()`.
153#[derive(Debug, Reflect)]
154#[reflect(ActionBuilder)]
155pub struct StepsBuilder {
156    label: Option<String>,
157    steps_labels: Vec<String>,
158    #[reflect(ignore)]
159    steps: Vec<Arc<dyn ActionBuilder>>,
160}
161
162impl StepsBuilder {
163    /// Sets the logging label for the Action
164    pub fn label<S: Into<String>>(mut self, label: S) -> Self {
165        self.label = Some(label.into());
166        self
167    }
168
169    /// Adds an action step. Order matters.
170    pub fn step(mut self, action_builder: impl ActionBuilder + 'static) -> Self {
171        if let Some(label) = action_builder.label() {
172            self.steps_labels.push(label.into());
173        } else {
174            self.steps_labels.push("Unlabeled Action".into());
175        }
176        self.steps.push(Arc::new(action_builder));
177        self
178    }
179}
180
181impl ActionBuilder for StepsBuilder {
182    fn label(&self) -> Option<&str> {
183        self.label.as_deref()
184    }
185
186    fn build(&self, cmd: &mut Commands, action: Entity, actor: Entity) {
187        if let Some(step) = self.steps.first() {
188            let child_action = spawn_action(step.as_ref(), cmd, actor);
189            cmd.entity(action)
190                .insert(Name::new("Steps Action"))
191                .insert(Steps {
192                    active_step: 0,
193                    active_ent: Action(child_action),
194                    steps: self.steps.clone(),
195                    steps_labels: self.steps_labels.clone(),
196                })
197                .add_children(&[child_action]);
198        }
199    }
200}
201
202/// Composite Action that executes a series of steps in sequential order, as
203/// long as each step results in a `Success`ful [`ActionState`].
204///
205/// ### Example
206///
207/// ```
208/// # use bevy::prelude::*;
209/// # use big_brain::prelude::*;
210/// # #[derive(Debug, Clone, Component, ScorerBuilder)]
211/// # struct MyScorer;
212/// # #[derive(Debug, Clone, Component, ActionBuilder)]
213/// # struct MyAction;
214/// # #[derive(Debug, Clone, Component, ActionBuilder)]
215/// # struct MyNextAction;
216/// # fn main() {
217/// Thinker::build()
218///     .when(
219///         MyScorer,
220///         Steps::build()
221///             .step(MyAction)
222///             .step(MyNextAction)
223///         )
224/// # ;
225/// # }
226/// ```
227#[derive(Component, Debug, Reflect)]
228pub struct Steps {
229    #[reflect(ignore)]
230    steps: Vec<Arc<dyn ActionBuilder>>,
231    steps_labels: Vec<String>,
232    active_step: usize,
233    active_ent: Action,
234}
235
236impl Steps {
237    /// Construct a new [`StepsBuilder`] to define the steps to take.
238    pub fn build() -> StepsBuilder {
239        StepsBuilder {
240            steps: Vec::new(),
241            steps_labels: Vec::new(),
242            label: None,
243        }
244    }
245}
246
247/// System that takes care of executing any existing [`Steps`] Actions.
248pub fn steps_system(
249    mut cmd: Commands,
250    mut steps_q: Query<(Entity, &Actor, &mut Steps, &ActionSpan)>,
251    mut states: Query<&mut ActionState>,
252) {
253    use ActionState::*;
254    for (seq_ent, Actor(actor), mut steps_action, _span) in steps_q.iter_mut() {
255        let active_ent = steps_action.active_ent.entity();
256        let current_state = states.get_mut(seq_ent).unwrap().clone();
257        #[cfg(feature = "trace")]
258        let _guard = _span.span().enter();
259        match current_state {
260            Requested => {
261                // Begin at the beginning
262                #[cfg(feature = "trace")]
263                trace!(
264                    "Initializing StepsAction and requesting first step: {:?}",
265                    active_ent
266                );
267                *states.get_mut(active_ent).unwrap() = Requested;
268                *states.get_mut(seq_ent).unwrap() = Executing;
269            }
270            Executing => {
271                let mut step_state = states.get_mut(active_ent).unwrap();
272                match *step_state {
273                    Init => {
274                        // Request it! This... should not really happen? But just in case I'm missing something... :)
275                        *step_state = Requested;
276                    }
277                    Executing | Requested => {
278                        // do nothing. Everything's running as it should.
279                    }
280                    Cancelled => {
281                        // Wait for the step to wrap itself up, and we'll decide what to do at that point.
282                    }
283                    Failure => {
284                        // Fail ourselves
285                        #[cfg(feature = "trace")]
286                        trace!("Step {:?} failed. Failing entire StepsAction.", active_ent);
287                        let step_state = step_state.clone();
288                        let mut seq_state = states.get_mut(seq_ent).expect("idk");
289                        *seq_state = step_state;
290                        if let Some(ent) = cmd.get_entity(steps_action.active_ent.entity()) {
291                            ent.despawn_recursive();
292                        }
293                    }
294                    Success if steps_action.active_step == steps_action.steps.len() - 1 => {
295                        // We're done! Let's just be successful
296                        #[cfg(feature = "trace")]
297                        trace!("StepsAction completed all steps successfully.");
298                        let step_state = step_state.clone();
299                        let mut seq_state = states.get_mut(seq_ent).expect("idk");
300                        *seq_state = step_state;
301                        if let Some(ent) = cmd.get_entity(steps_action.active_ent.entity()) {
302                            ent.despawn_recursive();
303                        }
304                    }
305                    Success => {
306                        #[cfg(feature = "trace")]
307                        trace!("Step succeeded, but there's more steps. Spawning next action.");
308                        // Deactivate current step and go to the next step
309                        if let Some(ent) = cmd.get_entity(steps_action.active_ent.entity()) {
310                            ent.despawn_recursive();
311                        }
312
313                        steps_action.active_step += 1;
314                        let step_builder = steps_action.steps[steps_action.active_step].clone();
315                        let step_ent = spawn_action(step_builder.as_ref(), &mut cmd, *actor);
316                        #[cfg(feature = "trace")]
317                        trace!("Spawned next step: {:?}", step_ent);
318                        cmd.entity(seq_ent).add_children(&[step_ent]);
319                        steps_action.active_ent = Action(step_ent);
320                    }
321                }
322            }
323            Cancelled => {
324                // Cancel current action
325                #[cfg(feature = "trace")]
326                trace!("StepsAction has been cancelled. Cancelling current step {:?} before finalizing.", active_ent);
327                let mut step_state = states.get_mut(active_ent).expect("oops");
328                if *step_state == Requested || *step_state == Executing || *step_state == Init {
329                    *step_state = Cancelled;
330                } else if *step_state == Failure || *step_state == Success {
331                    *states.get_mut(seq_ent).unwrap() = step_state.clone();
332                }
333            }
334            Init | Success | Failure => {
335                // Do nothing.
336            }
337        }
338    }
339}
340
341/// Configures what mode the [`Concurrently`] action will run in.
342#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Reflect)]
343pub enum ConcurrentMode {
344    /// Reaches success when any of the concurrent actions reaches [`ActionState::Success`].
345    Race,
346    /// Reaches success when all of the concurrent actions reach [`ActionState::Success`].
347    Join,
348}
349
350/// [`ActionBuilder`] for the [`Concurrently`] component. Constructed through
351/// `Concurrently::build()`.
352#[derive(Debug, Reflect)]
353pub struct ConcurrentlyBuilder {
354    mode: ConcurrentMode,
355    #[reflect(ignore)]
356    actions: Vec<Arc<dyn ActionBuilder>>,
357    action_labels: Vec<String>,
358    label: Option<String>,
359}
360
361impl ConcurrentlyBuilder {
362    /// Sets the logging label for the Action
363    pub fn label<S: Into<String>>(mut self, label: S) -> Self {
364        self.label = Some(label.into());
365        self
366    }
367
368    /// Add an action to execute. Order does not matter.
369    pub fn push(mut self, action_builder: impl ActionBuilder + 'static) -> Self {
370        if let Some(label) = action_builder.label() {
371            self.action_labels.push(label.into());
372        } else {
373            self.action_labels.push("Unnamed Action".into());
374        }
375        self.actions.push(Arc::new(action_builder));
376        self
377    }
378
379    /// Sets the [`ConcurrentMode`] for this action.
380    pub fn mode(mut self, mode: ConcurrentMode) -> Self {
381        self.mode = mode;
382        self
383    }
384}
385
386impl ActionBuilder for ConcurrentlyBuilder {
387    fn label(&self) -> Option<&str> {
388        self.label.as_deref()
389    }
390
391    fn build(&self, cmd: &mut Commands, action: Entity, actor: Entity) {
392        let children: Vec<Entity> = self
393            .actions
394            .iter()
395            .map(|action| spawn_action(action.as_ref(), cmd, actor))
396            .collect();
397        cmd.entity(action)
398            .insert(Name::new("Concurrent Action"))
399            .add_children(&children[..])
400            .insert(Concurrently {
401                actions: children.into_iter().map(Action).collect(),
402                action_labels: self.action_labels.clone(),
403                mode: self.mode,
404            });
405    }
406}
407
408/// Composite Action that executes a number of Actions concurrently. Whether
409/// this action succeeds depends on its [`ConcurrentMode`]:
410///
411/// * [`ConcurrentMode::Join`] (default) succeeds when **all** of the actions
412///   succeed.
413/// * [`ConcurrentMode::Race`] succeeds when **any** of the actions succeed.
414///
415/// ### Example
416///
417/// ```
418/// # use bevy::prelude::*;
419/// # use big_brain::prelude::*;
420/// # #[derive(Debug, Clone, Component, ScorerBuilder)]
421/// # struct MyScorer;
422/// # #[derive(Debug, Clone, Component, ActionBuilder)]
423/// # struct MyAction;
424/// # #[derive(Debug, Clone, Component, ActionBuilder)]
425/// # struct MyOtherAction;
426/// # fn main() {
427/// Thinker::build()
428///     .when(
429///         MyScorer,
430///         Concurrently::build()
431///             .push(MyAction)
432///             .push(MyOtherAction)
433///         )
434/// # ;
435/// # }
436/// ```
437///
438#[derive(Component, Debug, Reflect)]
439pub struct Concurrently {
440    mode: ConcurrentMode,
441    actions: Vec<Action>,
442    action_labels: Vec<String>,
443}
444
445impl Concurrently {
446    /// Construct a new [`ConcurrentlyBuilder`] to define the actions to take.
447    pub fn build() -> ConcurrentlyBuilder {
448        ConcurrentlyBuilder {
449            actions: Vec::new(),
450            action_labels: Vec::new(),
451            mode: ConcurrentMode::Join,
452            label: None,
453        }
454    }
455}
456
457/// System that takes care of executing any existing [`Concurrently`] Actions.
458pub fn concurrent_system(
459    concurrent_q: Query<(Entity, &Concurrently, &ActionSpan)>,
460    mut states_q: Query<&mut ActionState>,
461) {
462    use ActionState::*;
463    for (seq_ent, concurrent_action, _span) in concurrent_q.iter() {
464        let current_state = states_q.get_mut(seq_ent).expect("uh oh").clone();
465        #[cfg(feature = "trace")]
466        let _guard = _span.span.enter();
467        match current_state {
468            Requested => {
469                #[cfg(feature = "trace")]
470                trace!(
471                    "Initializing Concurrently action with {} children.",
472                    concurrent_action.actions.len()
473                );
474                // Begin at the beginning
475                let mut current_state = states_q.get_mut(seq_ent).expect("uh oh");
476                *current_state = Executing;
477                for action in concurrent_action.actions.iter() {
478                    let child_ent = action.entity();
479                    let mut child_state = states_q.get_mut(child_ent).expect("uh oh");
480                    *child_state = Requested;
481                }
482            }
483            Executing => match concurrent_action.mode {
484                ConcurrentMode::Join => {
485                    let mut all_success = true;
486                    let mut failed_idx = None;
487                    for (idx, action) in concurrent_action.actions.iter().enumerate() {
488                        let child_ent = action.entity();
489                        let mut child_state = states_q.get_mut(child_ent).expect("uh oh");
490                        match *child_state {
491                            Failure => {
492                                failed_idx = Some(idx);
493                                all_success = false;
494                                #[cfg(feature = "trace")]
495                                trace!("Join action has failed. Cancelling all other actions that haven't completed yet.");
496                            }
497                            Success => {}
498                            _ => {
499                                all_success = false;
500                                if failed_idx.is_some() {
501                                    *child_state = Cancelled;
502                                }
503                            }
504                        }
505                    }
506                    if all_success {
507                        let mut state_var = states_q.get_mut(seq_ent).expect("uh oh");
508                        *state_var = Success;
509                    } else if let Some(idx) = failed_idx {
510                        for action in concurrent_action.actions.iter().take(idx) {
511                            let child_ent = action.entity();
512                            let mut child_state = states_q.get_mut(child_ent).expect("uh oh");
513                            match *child_state {
514                                Failure | Success => {}
515                                _ => {
516                                    *child_state = Cancelled;
517                                }
518                            }
519                        }
520                        let mut state_var = states_q.get_mut(seq_ent).expect("uh oh");
521                        *state_var = Failure;
522                    }
523                }
524                ConcurrentMode::Race => {
525                    let mut all_failure = true;
526                    let mut succeed_idx = None;
527                    for (idx, action) in concurrent_action.actions.iter().enumerate() {
528                        let child_ent = action.entity();
529                        let mut child_state = states_q.get_mut(child_ent).expect("uh oh");
530                        match *child_state {
531                            Failure => {}
532                            Success => {
533                                succeed_idx = Some(idx);
534                                all_failure = false;
535                                #[cfg(feature = "trace")]
536                                trace!("Race action has succeeded. Cancelling all other actions that haven't completed yet.");
537                            }
538                            _ => {
539                                all_failure = false;
540                                if succeed_idx.is_some() {
541                                    *child_state = Cancelled;
542                                }
543                            }
544                        }
545                    }
546                    if all_failure {
547                        let mut state_var = states_q.get_mut(seq_ent).expect("uh oh");
548                        *state_var = Failure;
549                    } else if let Some(idx) = succeed_idx {
550                        for action in concurrent_action.actions.iter().take(idx) {
551                            let child_ent = action.entity();
552                            let mut child_state = states_q.get_mut(child_ent).expect("uh oh");
553                            match *child_state {
554                                Failure | Success => {}
555                                _ => {
556                                    *child_state = Cancelled;
557                                }
558                            }
559                        }
560                        let mut state_var = states_q.get_mut(seq_ent).expect("uh oh");
561                        *state_var = Success;
562                    }
563                }
564            },
565            Cancelled => {
566                // Cancel all actions
567                let mut all_done = true;
568                let mut any_failed = false;
569                let mut any_success = false;
570                for action in concurrent_action.actions.iter() {
571                    let child_ent = action.entity();
572                    let mut child_state = states_q.get_mut(child_ent).expect("uh oh");
573                    match *child_state {
574                        Init => {}
575                        Success => {
576                            any_success = true;
577                        }
578                        Failure => {
579                            any_failed = true;
580                        }
581                        _ => {
582                            all_done = false;
583                            *child_state = Cancelled;
584                        }
585                    }
586                }
587                if all_done {
588                    let mut state_var = states_q.get_mut(seq_ent).expect("uh oh");
589                    match concurrent_action.mode {
590                        ConcurrentMode::Race => {
591                            if any_success {
592                                #[cfg(feature = "trace")]
593                                trace!("Race action has succeeded due to succeeded children.");
594                                *state_var = Success;
595                            } else {
596                                #[cfg(feature = "trace")]
597                                trace!("No race children has completed Successfully.");
598                                *state_var = Failure;
599                            }
600                        }
601                        ConcurrentMode::Join => {
602                            if any_failed {
603                                #[cfg(feature = "trace")]
604                                trace!("Join action has failed due to failed children.");
605                                *state_var = Failure;
606                            } else {
607                                #[cfg(feature = "trace")]
608                                trace!("All Join children have completed Successfully.");
609                                *state_var = Success;
610                            }
611                        }
612                    }
613                }
614            }
615            Init | Success | Failure => {
616                // Do nothing.
617            }
618        }
619    }
620}