1use databoard::Databoard;
5use tinyscript::SharedRuntime;
6
7use crate::{
8 BehaviorPtr, BehaviorResult, BehaviorState, ConstString, FAILURE_IF, ON_FAILURE, ON_SUCCESS, POST, SKIP_IF, SUCCESS_IF,
9 WHILE,
10 behavior_data::BehaviorData,
11 error::Error,
12 pre_post_conditions::{Conditions, PostConditions, PreConditions},
13 tree::{
14 tree_element_list::BehaviorTreeElementList,
15 tree_iter::{TreeIter, TreeIterMut},
16 },
17};
18
19#[repr(u8)]
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum TreeElementKind {
23 Leaf,
25 Node,
27 SubTree,
29}
30
31pub struct BehaviorTreeElement {
33 kind: TreeElementKind,
35 behavior: BehaviorPtr,
37 data: BehaviorData,
39 children: BehaviorTreeElementList,
41 conditions: Conditions,
43}
44
45impl BehaviorTreeElement {
46 #[must_use]
48 pub fn create(kind: TreeElementKind, behavior: BehaviorPtr, data: BehaviorData) -> Self {
49 Self {
50 kind,
51 behavior,
52 data,
53 children: BehaviorTreeElementList::default(),
54 conditions: Conditions::default(),
55 }
56 }
57
58 #[must_use]
60 pub fn new(
61 kind: TreeElementKind,
62 behavior: BehaviorPtr,
63 data: BehaviorData,
64 children: BehaviorTreeElementList,
65 conditions: Conditions,
66 ) -> Self {
67 Self {
68 kind,
69 behavior,
70 data,
71 children,
72 conditions,
73 }
74 }
75
76 #[must_use]
78 pub const fn uid(&self) -> u16 {
79 self.data.uid()
80 }
81
82 #[must_use]
84 pub const fn id(&self) -> &ConstString {
85 self.data.description().id()
86 }
87
88 #[must_use]
90 pub const fn name(&self) -> &ConstString {
91 self.data.description().name()
92 }
93
94 #[must_use]
96 pub const fn data(&self) -> &BehaviorData {
97 &self.data
98 }
99
100 #[must_use]
102 pub const fn data_mut(&mut self) -> &mut BehaviorData {
103 &mut self.data
104 }
105
106 #[must_use]
108 pub const fn behavior(&self) -> &BehaviorPtr {
109 &self.behavior
110 }
111
112 #[must_use]
114 pub const fn behavior_mut(&mut self) -> &mut BehaviorPtr {
115 &mut self.behavior
116 }
117
118 #[must_use]
120 pub const fn blackboard(&self) -> &Databoard {
121 self.data().blackboard()
122 }
123
124 #[must_use]
126 pub const fn blackboard_mut(&mut self) -> &mut Databoard {
127 self.data_mut().blackboard_mut()
128 }
129
130 #[must_use]
132 pub const fn children(&self) -> &BehaviorTreeElementList {
133 &self.children
134 }
135
136 #[must_use]
138 pub const fn children_mut(&mut self) -> &mut BehaviorTreeElementList {
139 &mut self.children
140 }
141
142 #[must_use]
144 pub const fn pre_conditions(&self) -> &PreConditions {
145 &self.conditions.pre
146 }
147
148 #[must_use]
150 pub const fn pre_conditions_mut(&mut self) -> &mut PreConditions {
151 &mut self.conditions.pre
152 }
153
154 #[must_use]
156 pub const fn post_conditions(&self) -> &PostConditions {
157 &self.conditions.post
158 }
159
160 #[must_use]
162 pub const fn post_conditions_mut(&mut self) -> &mut PostConditions {
163 &mut self.conditions.post
164 }
165
166 pub fn halt(&mut self, runtime: &SharedRuntime) -> Result<(), Error> {
169 if self.data.state() != BehaviorState::Idle {
170 let state = self
171 .behavior
172 .halt(&mut self.data, &mut self.children, runtime)?;
173 self.data.set_state(state);
174 if let Some(script) = self.conditions.post.get("_onHalted") {
175 let _ = runtime
176 .lock()
177 .run(script, self.data.blackboard_mut())?;
178 }
179 }
180 Ok(())
181 }
182
183 pub async fn tick(&mut self, runtime: &SharedRuntime) -> BehaviorResult {
186 let old_state = self.data.state();
188 let state = if let Some(result) = self.check_pre_conditions(runtime)? {
189 result
190 } else if old_state == BehaviorState::Idle {
191 self.behavior
192 .start(&mut self.data, &mut self.children, runtime)
193 .await?
194 } else {
195 self.behavior
196 .tick(&mut self.data, &mut self.children, runtime)
197 .await?
198 };
199
200 self.check_post_conditions(state, runtime)?;
201
202 if state != BehaviorState::Skipped {
204 self.data.set_state(state);
205 }
208
209 Ok(state)
210 }
211
212 pub fn halt_child_at(&mut self, index: usize, runtime: &SharedRuntime) -> Result<(), Error> {
216 self.children.halt_at(index, runtime)
217 }
218
219 pub fn halt_children_from(&mut self, index: usize, runtime: &SharedRuntime) -> Result<(), Error> {
223 self.children.halt_from(index, runtime)
224 }
225
226 pub fn halt_children(&mut self, runtime: &SharedRuntime) -> Result<(), Error> {
230 self.children.halt(runtime)
231 }
232
233 pub fn reset_state(&mut self) {
235 self.data.set_state(BehaviorState::Idle);
237 }
242
243 pub fn add_pre_state_change_callback<T>(&mut self, name: ConstString, callback: T)
246 where
247 T: Fn(&BehaviorData, &mut BehaviorState) + Send + Sync + 'static,
248 {
249 self.data
250 .add_pre_state_change_callback(name, callback);
251 }
252
253 pub fn remove_pre_state_change_callback(&mut self, name: &str) {
255 self.data.remove_pre_state_change_callback(name);
256 }
257
258 #[must_use]
260 pub fn children_iter(&self) -> impl DoubleEndedIterator<Item = &Self> {
261 self.children().iter()
262 }
263
264 #[must_use]
266 pub fn children_iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Self> {
267 self.children_mut().iter_mut()
268 }
269
270 pub fn iter(&self) -> impl Iterator<Item = &Self> {
272 TreeIter::new(self)
273 }
274
275 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self> {
277 TreeIterMut::new(self)
278 }
279
280 pub const fn kind(&self) -> TreeElementKind {
281 self.kind
282 }
283
284 fn check_pre_conditions(&mut self, runtime: &SharedRuntime) -> Result<Option<BehaviorState>, Error> {
285 if self.conditions.pre.is_some() {
286 if self.data.state() == BehaviorState::Idle || self.data.state() == BehaviorState::Skipped {
288 if let Some(script) = self.conditions.pre.get(FAILURE_IF) {
289 let res = runtime
290 .lock()
291 .run(script, self.data.blackboard_mut())?;
292 if bool::try_from(res)? {
293 return Ok(Some(BehaviorState::Failure));
294 }
295 }
296 if let Some(script) = self.conditions.pre.get(SUCCESS_IF) {
297 let res = runtime
298 .lock()
299 .run(script, self.data.blackboard_mut())?;
300 if bool::try_from(res)? {
301 return Ok(Some(BehaviorState::Success));
302 }
303 }
304 if let Some(script) = self.conditions.pre.get(SKIP_IF) {
305 let res = runtime
306 .lock()
307 .run(script, self.data.blackboard_mut())?;
308 if bool::try_from(res)? {
309 return Ok(Some(BehaviorState::Skipped));
310 }
311 }
312 if let Some(script) = self.conditions.pre.get(WHILE) {
313 let res = runtime
314 .lock()
315 .run(script, self.data.blackboard_mut())?;
316 if bool::try_from(res)? {
317 return Ok(Some(BehaviorState::Skipped));
318 }
319 }
320 } else
321 if self.data.state() == BehaviorState::Running
323 && let Some(script) = self.conditions.pre.get(WHILE)
324 {
325 let res = runtime
326 .lock()
327 .run(script, self.data.blackboard_mut())?;
328 if bool::try_from(res)? {
330 let _res = self.halt(runtime);
331 return Ok(Some(BehaviorState::Skipped));
332 }
333 }
334 }
335 Ok(None)
336 }
337
338 fn check_post_conditions(&mut self, state: BehaviorState, runtime: &SharedRuntime) -> Result<(), Error> {
339 if self.conditions.post.is_some() {
340 match state {
341 BehaviorState::Failure => {
342 if let Some(script) = self.conditions.post.get(ON_FAILURE) {
343 let _ = runtime
344 .lock()
345 .run(script, self.data.blackboard_mut())?;
346 }
347 }
348 BehaviorState::Success => {
349 if let Some(script) = self.conditions.post.get(ON_SUCCESS) {
350 let _ = runtime
351 .lock()
352 .run(script, self.data.blackboard_mut())?;
353 }
354 }
355 _ => {}
357 }
358 if let Some(script) = self.conditions.post.get(POST) {
359 let _ = runtime
360 .lock()
361 .run(script, self.data.blackboard_mut())?;
362 }
363 }
364 Ok(())
365 }
366
367 #[must_use]
369 pub const fn groot2_path(&self) -> &ConstString {
370 self.data.description().groot2_path()
371 }
372
373 #[must_use]
375 pub const fn state(&self) -> BehaviorState {
376 self.data.state()
377 }
378
379 pub fn add_child(&mut self, child: Self) {
381 self.children.push(child);
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use crate::{
389 behavior_data::BehaviorData,
390 behavior_description::BehaviorDescription,
391 behavior_state::BehaviorState,
392 behaviors::mock_behavior::{MockBehavior, MockBehaviorConfig},
393 pre_post_conditions::Conditions,
394 };
395 use databoard::Databoard;
396
397 #[test]
400 fn new_constructor_covers_all_lines() {
401 let behavior: BehaviorPtr = alloc::boxed::Box::new(MockBehavior::new(MockBehaviorConfig {
402 return_state: BehaviorState::Success,
403 ..Default::default()
404 }));
405 let data = BehaviorData::create(
406 42,
407 Databoard::default(),
408 BehaviorDescription::new("test_node", "test_node", false),
409 );
410 let children = BehaviorTreeElementList::default();
411 let conditions = Conditions::default();
412
413 let element = BehaviorTreeElement::new(TreeElementKind::Node, behavior, data, children, conditions);
414
415 assert_eq!(element.uid(), 42);
416 assert_eq!(element.name().as_ref(), "test_node");
417 assert!(matches!(element.kind(), TreeElementKind::Node));
418 assert_eq!(element.children().len(), 0);
419 }
420}