Skip to content
Snippets Groups Projects
Unverified Commit 6c736374 authored by Kat Marchán's avatar Kat Marchán
Browse files

feat(actions): Add new Concurrently composite action

Fixes: https://github.com/zkat/big-brain/issues/22
parent 63bad1fd
No related branches found
No related tags found
No related merge requests found
......@@ -232,3 +232,144 @@ pub fn steps_system(
}
}
}
/**
[`ActionBuilder`] for the [`Concurrent`] component. Constructed through `Concurrent::build()`.
*/
#[derive(Debug)]
pub struct ConcurrentlyBuilder {
actions: Vec<Arc<dyn ActionBuilder>>,
}
impl ConcurrentlyBuilder {
/**
Add an action to execute. Order does not matter.
*/
pub fn push(mut self, action_builder: impl ActionBuilder + 'static) -> Self {
self.actions.push(Arc::new(action_builder));
self
}
}
impl ActionBuilder for ConcurrentlyBuilder {
fn build(&self, cmd: &mut Commands, action: Entity, actor: Entity) {
let children: Vec<Entity> = self
.actions
.iter()
.map(|action| action.attach(cmd, actor))
.collect();
cmd.entity(action)
.insert(Name::new("Concurrent Action"))
.push_children(&children[..])
.insert(Concurrently {
actions: children.into_iter().map(ActionEnt).collect(),
});
}
}
/**
Composite Action that executes a number of Actions concurrently, as long as they all result in a `Success`ful [`ActionState`].
### Example
```ignore
Thinker::build()
.when(
MyScorer,
Concurrent::build()
.push(MyAction::build())
.push(MyOtherAction::build())
)
```
*/
#[derive(Debug)]
pub struct Concurrently {
actions: Vec<ActionEnt>,
}
impl Concurrently {
/**
Construct a new [`ConcurrentBuilder`] to define the actions to take.
*/
pub fn build() -> ConcurrentlyBuilder {
ConcurrentlyBuilder {
actions: Vec::new(),
}
}
}
/**
System that takes care of executing any existing [`Concurrent`] Actions.
*/
pub fn concurrent_system(
concurrent_q: Query<(Entity, &Concurrently)>,
mut states_q: Query<&mut ActionState>,
) {
use ActionState::*;
for (seq_ent, concurrent_action) in concurrent_q.iter() {
let current_state = states_q.get_mut(seq_ent).expect("uh oh").clone();
match current_state {
Requested => {
// Begin at the beginning
let mut current_state = states_q.get_mut(seq_ent).expect("uh oh");
*current_state = Executing;
for ActionEnt(child_ent) in concurrent_action.actions.iter() {
let mut child_state = states_q.get_mut(*child_ent).expect("uh oh");
*child_state = Requested;
}
}
Executing => {
let mut all_success = true;
let mut failed_idx = None;
for (idx, ActionEnt(child_ent)) in concurrent_action.actions.iter().enumerate() {
let mut child_state = states_q.get_mut(*child_ent).expect("uh oh");
match *child_state {
Failure => {
failed_idx = Some(idx);
all_success = false;
}
Success => {}
_ => {
all_success = false;
if failed_idx.is_some() {
*child_state = Cancelled;
}
}
}
}
if all_success {
let mut state_var = states_q.get_mut(seq_ent).expect("uh oh");
*state_var = Success;
} else if let Some(idx) = failed_idx {
for ActionEnt(child_ent) in concurrent_action.actions.iter().take(idx) {
let mut child_state = states_q.get_mut(*child_ent).expect("uh oh");
match *child_state {
Failure | Success => {}
_ => {
*child_state = Cancelled;
}
}
}
}
}
Cancelled => {
// Cancel all actions
for ActionEnt(child_ent) in concurrent_action.actions.iter() {
let mut child_state = states_q.get_mut(*child_ent).expect("uh oh");
match *child_state {
Init | Success | Failure => {
// Do nothing
}
_ => {
*child_state = Cancelled;
}
}
}
}
Init | Success | Failure => {
// Do nothing.
}
}
}
}
......@@ -163,7 +163,7 @@ pub mod prelude {
use super::*;
pub use super::BigBrainPlugin;
pub use actions::{ActionBuilder, ActionState, Steps};
pub use actions::{ActionBuilder, ActionState, Steps, Concurrently};
pub use pickers::{FirstToScore, Picker};
pub use scorers::{AllOrNothing, FixedScore, Score, ScorerBuilder, SumOfScorers, WinningScorer};
pub use thinker::{Actor, Thinker, ThinkerBuilder};
......@@ -196,6 +196,7 @@ impl Plugin for BigBrainPlugin {
app.add_system(thinker::thinker_component_detach_system.system());
app.add_system(thinker::actor_gone_cleanup.system());
app.add_system(actions::steps_system.system());
app.add_system(actions::concurrent_system.system());
app.add_system(scorers::fixed_score_system.system());
app.add_system(scorers::all_or_nothing_system.system());
app.add_system(scorers::sum_of_scorers_system.system());
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment