use crate::{
self as behaviortree, Control,
behavior::{Behavior, BehaviorData, BehaviorError, BehaviorResult, BehaviorState},
tree::BehaviorTreeElementList,
};
use alloc::boxed::Box;
use tinyscript::SharedRuntime;
#[derive(Control, Debug)]
#[behavior(groot2)]
pub struct ReactiveFallback {
running_child_idx: i32,
}
impl Default for ReactiveFallback {
fn default() -> Self {
Self { running_child_idx: -1 }
}
}
#[async_trait::async_trait]
impl Behavior for ReactiveFallback {
#[inline]
fn on_halt(&mut self) -> Result<(), BehaviorError> {
self.running_child_idx = -1;
Ok(())
}
#[inline]
fn on_start(
&mut self,
behavior: &mut BehaviorData,
_children: &mut BehaviorTreeElementList,
_runtime: &SharedRuntime,
) -> Result<(), BehaviorError> {
behavior.set_state(BehaviorState::Running);
Ok(())
}
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
#[allow(clippy::cast_sign_loss)]
async fn tick(
&mut self,
_behavior: &mut BehaviorData,
children: &mut BehaviorTreeElementList,
runtime: &SharedRuntime,
) -> BehaviorResult {
let mut all_skipped = true;
self.running_child_idx = -1;
for child_idx in 0..children.len() {
let child = &mut children[child_idx];
let new_state = child.tick(runtime).await?;
all_skipped &= new_state == BehaviorState::Skipped;
match new_state {
BehaviorState::Failure => {
self.running_child_idx = -1;
}
BehaviorState::Idle => {
return Err(BehaviorError::State {
behavior: "ReactiveFallback".into(),
state: new_state,
});
}
BehaviorState::Running => {
if self.running_child_idx != (child_idx as i32) && self.running_child_idx != -1 {
children[self.running_child_idx as usize].halt_children(runtime)?;
}
self.running_child_idx = child_idx as i32;
if self.running_child_idx == -1 {
self.running_child_idx = child_idx as i32;
} else if self.running_child_idx != (child_idx as i32) {
return Err(BehaviorError::Composition {
txt: "[ReactiveFallback]: Only a single child can return Running.".into(),
});
}
return Ok(BehaviorState::Running);
}
BehaviorState::Skipped => {
child.halt_children(runtime)?;
}
BehaviorState::Success => {
children.halt(runtime)?;
self.running_child_idx = -1;
return Ok(BehaviorState::Success);
}
}
}
children.halt(runtime)?;
self.running_child_idx = -1;
if all_skipped {
Ok(BehaviorState::Skipped)
} else {
Ok(BehaviorState::Failure)
}
}
}