Skip to main content

gizmo_ai/
behavior_tree.rs

1use gizmo_core::World;
2
3/// Return status of a Behavior Tree node
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum BtStatus {
6    Success,
7    Failure,
8    Running,
9}
10
11/// A node in the Behavior Tree
12pub trait BtNode: Send + Sync {
13    /// Executes the node's logic.
14    fn tick(&mut self, entity: u32, world: &mut World, dt: f32) -> BtStatus;
15}
16
17/// Sequence: Runs children in order until one Fails or Runs.
18/// Returns Success if ALL children succeed.
19pub struct Sequence {
20    children: Vec<Box<dyn BtNode>>,
21    current_idx: usize,
22}
23
24impl Sequence {
25    pub fn new(children: Vec<Box<dyn BtNode>>) -> Self {
26        Self {
27            children,
28            current_idx: 0,
29        }
30    }
31}
32
33impl BtNode for Sequence {
34    fn tick(&mut self, entity: u32, world: &mut World, dt: f32) -> BtStatus {
35        while self.current_idx < self.children.len() {
36            let status = self.children[self.current_idx].tick(entity, world, dt);
37            match status {
38                BtStatus::Success => {
39                    self.current_idx += 1; // Move to next child
40                }
41                BtStatus::Failure => {
42                    self.current_idx = 0; // Reset for next tick
43                    return BtStatus::Failure;
44                }
45                BtStatus::Running => {
46                    return BtStatus::Running;
47                }
48            }
49        }
50        self.current_idx = 0; // Reset
51        BtStatus::Success
52    }
53}
54
55/// Selector (Fallback): Runs children in order until one Succeeds or Runs.
56/// Returns Failure if ALL children fail.
57pub struct Selector {
58    children: Vec<Box<dyn BtNode>>,
59    current_idx: usize,
60}
61
62impl Selector {
63    pub fn new(children: Vec<Box<dyn BtNode>>) -> Self {
64        Self {
65            children,
66            current_idx: 0,
67        }
68    }
69}
70
71impl BtNode for Selector {
72    fn tick(&mut self, entity: u32, world: &mut World, dt: f32) -> BtStatus {
73        while self.current_idx < self.children.len() {
74            let status = self.children[self.current_idx].tick(entity, world, dt);
75            match status {
76                BtStatus::Success => {
77                    self.current_idx = 0; // Reset
78                    return BtStatus::Success;
79                }
80                BtStatus::Failure => {
81                    self.current_idx += 1; // Try next child
82                }
83                BtStatus::Running => {
84                    return BtStatus::Running;
85                }
86            }
87        }
88        self.current_idx = 0; // Reset
89        BtStatus::Failure
90    }
91}
92
93/// Inverter: Inverts Success and Failure. Running stays Running.
94pub struct Inverter {
95    child: Box<dyn BtNode>,
96}
97
98impl Inverter {
99    pub fn new(child: Box<dyn BtNode>) -> Self {
100        Self { child }
101    }
102}
103
104impl BtNode for Inverter {
105    fn tick(&mut self, entity: u32, world: &mut World, dt: f32) -> BtStatus {
106        match self.child.tick(entity, world, dt) {
107            BtStatus::Success => BtStatus::Failure,
108            BtStatus::Failure => BtStatus::Success,
109            BtStatus::Running => BtStatus::Running,
110        }
111    }
112}
113
114/// Action Node: A leaf node that performs a concrete action.
115pub struct Action<F>
116where
117    F: FnMut(u32, &mut World, f32) -> BtStatus + Send + Sync,
118{
119    func: F,
120}
121
122impl<F> Action<F>
123where
124    F: FnMut(u32, &mut World, f32) -> BtStatus + Send + Sync,
125{
126    pub fn new(func: F) -> Self {
127        Self { func }
128    }
129}
130
131impl<F> BtNode for Action<F>
132where
133    F: FnMut(u32, &mut World, f32) -> BtStatus + Send + Sync,
134{
135    fn tick(&mut self, entity: u32, world: &mut World, dt: f32) -> BtStatus {
136        (self.func)(entity, world, dt)
137    }
138}
139
140/// Condition Node: A leaf node that checks a condition (returns Success or Failure).
141pub struct Condition<F>
142where
143    F: FnMut(u32, &mut World) -> bool + Send + Sync,
144{
145    func: F,
146}
147
148impl<F> Condition<F>
149where
150    F: FnMut(u32, &mut World) -> bool + Send + Sync,
151{
152    pub fn new(func: F) -> Self {
153        Self { func }
154    }
155}
156
157impl<F> BtNode for Condition<F>
158where
159    F: FnMut(u32, &mut World) -> bool + Send + Sync,
160{
161    fn tick(&mut self, entity: u32, world: &mut World, _dt: f32) -> BtStatus {
162        if (self.func)(entity, world) {
163            BtStatus::Success
164        } else {
165            BtStatus::Failure
166        }
167    }
168}
169
170/// BehaviorTree Component attached to an Entity
171pub struct BehaviorTree {
172    pub root: Option<Box<dyn BtNode>>,
173}
174
175impl gizmo_core::component::Component for BehaviorTree {}
176
177impl Clone for BehaviorTree {
178    fn clone(&self) -> Self {
179        panic!("BehaviorTree cannot be cloned!");
180    }
181}
182
183impl BehaviorTree {
184    pub fn new(root: Box<dyn BtNode>) -> Self {
185        Self { root: Some(root) }
186    }
187
188    pub fn tick(&mut self, entity: u32, world: &mut World, dt: f32) -> BtStatus {
189        if let Some(root) = &mut self.root {
190            root.tick(entity, world, dt)
191        } else {
192            BtStatus::Failure
193        }
194    }
195}
196
197/// The System that ticks all BehaviorTrees
198pub fn behavior_tree_system(world: &mut World, dt: f32) {
199    let entities: Vec<u32> = {
200        let trees = world.borrow_mut::<BehaviorTree>();
201        trees.entities().collect()
202    };
203
204    for entity in entities {
205        let mut root_opt = None;
206        if let Some(mut tree) = world.borrow_mut::<BehaviorTree>().get_mut(entity) {
207            root_opt = tree.root.take();
208        }
209
210        if let Some(mut root) = root_opt {
211            root.tick(entity, world, dt);
212
213            if let Some(mut tree) = world.borrow_mut::<BehaviorTree>().get_mut(entity) {
214                tree.root = Some(root);
215            }
216        }
217    }
218}