arora_behavior/lib.rs
1//! The behavior abstraction: anything the Arora runtime can tick.
2//!
3//! A [`Behavior`] is the runtime's "what to do each step". The behavior tree is
4//! one (`arora-behavior-tree`'s `TreeBehavior`); a Vizij node graph is another.
5//! The runtime accepts any of them interchangeably — it just swaps the
6//! interpreter.
7//!
8//! Each tick a behavior gets a [`BehaviorContext`]: the shared
9//! [`DataStore`](arora_types::data::DataStore) (read inputs, write intent /
10//! outputs), a [`CallBridge`](arora_types::call::CallBridge) (so module-calling
11//! behaviors like the behavior tree can reach the engine), and the frame delta.
12//! A behavior uses whichever it needs — a graph reads/writes the store; the tree
13//! drives the caller.
14
15use arora_types::call::CallBridge;
16use arora_types::data::DataStore;
17
18/// Whether a behavior wants to be ticked again.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum BehaviorStatus {
21 /// Tick me again next step (a node graph; a tree still running).
22 Running,
23 /// I reached a terminal state; the runtime may drop me.
24 Done,
25}
26
27/// What a [`Behavior`] receives each [`tick`](Behavior::tick).
28pub struct BehaviorContext<'a> {
29 /// The shared blackboard: read inputs, write intent / outputs.
30 pub store: &'a dyn DataStore,
31 /// The module-call bridge (the engine), for behaviors that call modules.
32 pub caller: &'a mut dyn CallBridge,
33 /// Seconds elapsed since the previous tick.
34 pub dt: f32,
35}
36
37/// A behavior failed to tick.
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct BehaviorError {
40 /// Human-readable description.
41 pub message: String,
42}
43
44impl std::fmt::Display for BehaviorError {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.write_str(&self.message)
47 }
48}
49
50impl std::error::Error for BehaviorError {}
51
52/// Something the Arora runtime ticks once per step.
53///
54/// Implemented by the behavior tree (`arora-behavior-tree`'s `TreeBehavior`) and
55/// by external interpreters such as a Vizij node graph. The runtime holds a
56/// queue of `Box<dyn Behavior>` and ticks them without knowing which is which.
57///
58/// Not `Send`: the runtime is a single-owner, single-thread step loop.
59pub trait Behavior {
60 /// Advance one step. Return [`BehaviorStatus::Running`] to be ticked again,
61 /// or [`BehaviorStatus::Done`] to be dropped.
62 fn tick(&mut self, ctx: &mut BehaviorContext) -> Result<BehaviorStatus, BehaviorError>;
63}