use std::collections::HashMap;
use std::rc::Rc;
use arora_behavior::graph::{Graph, GraphDiff};
use arora_behavior::{BehaviorContext, BehaviorError, BehaviorInterpreter, BehaviorStatus};
use arora_types::data::{DataStore, Key, Slot};
use uuid::Uuid;
use crate::error::BehaviorTreeError;
use crate::graph::build_behavior_tree;
use crate::{run_behavior_tree, schema_groot, BehaviorTree, ModuleFunction};
pub struct BehaviorTreeInterpreter {
tree: Option<BehaviorTree>,
function_index: Rc<HashMap<Uuid, ModuleFunction>>,
graph: Option<Graph>,
dirty: bool,
}
fn direct_resolver(store: &dyn DataStore) -> impl Fn(&str) -> Option<Box<dyn Slot>> + '_ {
move |name: &str| Some(store.slot(&Key::from(name)))
}
impl BehaviorTreeInterpreter {
pub fn new(function_index: Rc<HashMap<Uuid, ModuleFunction>>) -> Self {
Self {
tree: None,
function_index,
graph: None,
dirty: false,
}
}
pub fn load(&mut self, behavior: BehaviorTree) {
self.tree = Some(behavior);
self.graph = None;
self.dirty = false;
}
pub fn load_graph(
&mut self,
graph: Graph,
store: &dyn DataStore,
) -> Result<(), BehaviorTreeError> {
let tree = build_behavior_tree(&graph, &direct_resolver(store))?;
self.tree = Some(tree);
self.graph = Some(graph);
self.dirty = false;
Ok(())
}
pub fn load_groot(
&mut self,
xml: &str,
store: &dyn DataStore,
) -> Result<(), BehaviorTreeError> {
let groot = schema_groot::BehaviorTree::try_from_groot_xml(xml)?;
let graph = groot.into_graph(self.function_index.as_ref())?;
self.load_graph(graph, store)
}
pub fn graph(&self) -> Option<&Graph> {
self.graph.as_ref()
}
}
impl BehaviorInterpreter for BehaviorTreeInterpreter {
fn tick(&mut self, ctx: &mut BehaviorContext) -> Result<BehaviorStatus, BehaviorError> {
if self.dirty {
let graph = self.graph.as_ref().expect("dirty implies a graph");
self.tree = Some(
build_behavior_tree(graph, &direct_resolver(ctx.store)).map_err(|e| {
BehaviorError {
message: format!("rebuild after apply: {e:?}"),
}
})?,
);
self.dirty = false;
}
let Some(tree) = self.tree.as_ref() else {
return Ok(BehaviorStatus::Running);
};
run_behavior_tree(tree, self.function_index.clone(), ctx.call_bridge, false).map_err(
|e| BehaviorError {
message: format!("behavior tree: {e:?}"),
},
)?;
Ok(BehaviorStatus::Done)
}
fn apply(&mut self, diff: GraphDiff) -> Result<(), BehaviorError> {
let graph = self.graph.as_mut().ok_or_else(|| BehaviorError {
message: "the loaded behavior has no editable graph; load one with load_graph or \
load_groot to edit it"
.to_string(),
})?;
graph.apply(diff).map_err(|e| BehaviorError {
message: format!("graph diff: {e}"),
})?;
self.dirty = true;
Ok(())
}
}