use crate::NodeStatus;
use async_trait::async_trait;
use mecha10_core::Context;
use std::fmt::Debug;
#[async_trait]
pub trait BehaviorNode: Send + Sync + Debug {
async fn tick(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus>;
fn name(&self) -> &str {
std::any::type_name::<Self>()
}
fn children_mut(&mut self) -> Vec<&mut dyn BehaviorNode> {
Vec::new()
}
fn reset_self(&mut self) {}
async fn reset(&mut self) -> anyhow::Result<()> {
self.reset_self();
for child in self.children_mut() {
child.reset().await?;
}
Ok(())
}
async fn on_init(&mut self, ctx: &Context) -> anyhow::Result<()> {
for child in self.children_mut() {
child.on_init(ctx).await?;
}
Ok(())
}
async fn on_terminate(&mut self, ctx: &Context) -> anyhow::Result<()> {
for child in self.children_mut() {
child.on_terminate(ctx).await?;
}
Ok(())
}
}
pub type BoxedBehavior = Box<dyn BehaviorNode>;
pub fn boxed_children_mut<'a>(children: impl Iterator<Item = &'a mut BoxedBehavior>) -> Vec<&'a mut dyn BehaviorNode> {
children.map(|c| c.as_mut() as &mut dyn BehaviorNode).collect()
}
#[macro_export]
macro_rules! children_mut_via_wrapped_behavior {
($field:ident, $wrapper:ident) => {
fn children_mut(&mut self) -> Vec<&mut dyn $crate::BehaviorNode> {
$crate::boxed_children_mut(self.$field.iter_mut().map(|$wrapper| &mut $wrapper.behavior))
}
};
}
#[macro_export]
macro_rules! reset_child_statuses_field {
($field:ident) => {
fn reset_self(&mut self) {
self.$field
.iter_mut()
.for_each(|s| *s = $crate::NodeStatus::Running);
}
};
}
#[macro_export]
macro_rules! reset_index_field {
($field:ident) => {
fn reset_self(&mut self) {
self.$field = 0;
}
};
}
#[async_trait]
pub trait BehaviorNodeExt: BehaviorNode {
async fn run_until_complete(&mut self, ctx: &Context) -> anyhow::Result<NodeStatus> {
loop {
match self.tick(ctx).await? {
NodeStatus::Success => return Ok(NodeStatus::Success),
NodeStatus::Failure => return Ok(NodeStatus::Failure),
NodeStatus::Running => continue,
}
}
}
async fn run_with_limit(&mut self, ctx: &Context, max_ticks: usize) -> anyhow::Result<NodeStatus> {
for _tick_count in 0..max_ticks {
match self.tick(ctx).await? {
NodeStatus::Success => return Ok(NodeStatus::Success),
NodeStatus::Failure => return Ok(NodeStatus::Failure),
NodeStatus::Running => continue,
}
}
anyhow::bail!("Behavior exceeded maximum ticks ({})", max_ticks)
}
}
impl<T: BehaviorNode + ?Sized> BehaviorNodeExt for T {}