Skip to main content

behaviortree_core/tree/
tree_element.rs

1// Copyright © 2025 Stephan Kunz
2//! A [`BehaviorTree`](crate::tree::tree::BehaviorTree) element.
3
4use 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)]
21/// The different kinds of a [`BehaviorTreeElement`]
22pub enum TreeElementKind {
23	/// A behavior tree leaf.
24	Leaf,
25	/// A behavior tree node.
26	Node,
27	/// A behavior subtree.
28	SubTree,
29}
30
31/// A tree elements.
32pub struct BehaviorTreeElement {
33	/// Kind of the element.
34	kind: TreeElementKind,
35	/// The behavior of that element.
36	behavior: BehaviorPtr,
37	/// Data of the Behavior.
38	data: BehaviorData,
39	/// Children of the element.
40	children: BehaviorTreeElementList,
41	/// Tuple of pre- and post-conditions, checked before and after a tick.
42	conditions: Conditions,
43}
44
45impl BehaviorTreeElement {
46	/// Construct a [`BehaviorTreeElement`].
47	#[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	/// Construct a [`BehaviorTreeElement`].
59	#[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	/// Get the uid.
77	#[must_use]
78	pub const fn uid(&self) -> u16 {
79		self.data.uid()
80	}
81
82	/// Get the id.
83	#[must_use]
84	pub const fn id(&self) -> &ConstString {
85		self.data.description().id()
86	}
87
88	/// Returns the name of the behavior.
89	#[must_use]
90	pub const fn name(&self) -> &ConstString {
91		self.data.description().name()
92	}
93
94	/// Returns a reference to the [`BehaviorData`].
95	#[must_use]
96	pub const fn data(&self) -> &BehaviorData {
97		&self.data
98	}
99
100	/// Returns a mutable reference to the [`BehaviorData`].
101	#[must_use]
102	pub const fn data_mut(&mut self) -> &mut BehaviorData {
103		&mut self.data
104	}
105
106	/// Returns a reference to the behavior.
107	#[must_use]
108	pub const fn behavior(&self) -> &BehaviorPtr {
109		&self.behavior
110	}
111
112	/// Returns a mutable reference to the behavior.
113	#[must_use]
114	pub const fn behavior_mut(&mut self) -> &mut BehaviorPtr {
115		&mut self.behavior
116	}
117
118	/// Returns a reference to the blackboard.
119	#[must_use]
120	pub const fn blackboard(&self) -> &Databoard {
121		self.data().blackboard()
122	}
123
124	/// Returns a reference to the blackboard.
125	#[must_use]
126	pub const fn blackboard_mut(&mut self) -> &mut Databoard {
127		self.data_mut().blackboard_mut()
128	}
129
130	/// Returns the children.
131	#[must_use]
132	pub const fn children(&self) -> &BehaviorTreeElementList {
133		&self.children
134	}
135
136	/// Returns the children mutable.
137	#[must_use]
138	pub const fn children_mut(&mut self) -> &mut BehaviorTreeElementList {
139		&mut self.children
140	}
141
142	/// Returns the pre conditions.
143	#[must_use]
144	pub const fn pre_conditions(&self) -> &PreConditions {
145		&self.conditions.pre
146	}
147
148	/// Returns the pre condition mutables.
149	#[must_use]
150	pub const fn pre_conditions_mut(&mut self) -> &mut PreConditions {
151		&mut self.conditions.pre
152	}
153
154	/// Returns the post conditions.
155	#[must_use]
156	pub const fn post_conditions(&self) -> &PostConditions {
157		&self.conditions.post
158	}
159
160	/// Returns the post conditions mutable.
161	#[must_use]
162	pub const fn post_conditions_mut(&mut self) -> &mut PostConditions {
163		&mut self.conditions.post
164	}
165
166	/// Halts the element and all its children considering postconditions.
167	/// # Errors
168	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	/// Ticks the element considering pre- and postconditions.
184	/// # Errors
185	pub async fn tick(&mut self, runtime: &SharedRuntime) -> BehaviorResult {
186		// A pre-condition may return the next state which will override the current tick().
187		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		// Preserve the last state if skipped, but communicate `Skipped` to parent
203		if state != BehaviorState::Skipped {
204			self.data.set_state(state);
205			// } else {
206			//     self.data.set_state(old_state);
207		}
208
209		Ok(state)
210	}
211
212	/// Halts child at `index`.
213	/// # Errors
214	/// - if index is out of childrens bounds.
215	pub fn halt_child_at(&mut self, index: usize, runtime: &SharedRuntime) -> Result<(), Error> {
216		self.children.halt_at(index, runtime)
217	}
218
219	/// Halt all children at and beyond `index`.
220	/// # Errors
221	/// - if index is out of childrens bounds.
222	pub fn halt_children_from(&mut self, index: usize, runtime: &SharedRuntime) -> Result<(), Error> {
223		self.children.halt_from(index, runtime)
224	}
225
226	/// Halt all children at and beyond `index`.
227	/// # Errors
228	/// - if index is out of childrens bounds.
229	pub fn halt_children(&mut self, runtime: &SharedRuntime) -> Result<(), Error> {
230		self.children.halt(runtime)
231	}
232
233	/// Reset state of element.
234	pub fn reset_state(&mut self) {
235		// let prev_state = self.data.state();
236		self.data.set_state(BehaviorState::Idle);
237		// if prev_state != BehaviorState::Idle {
238		// 	// @TODO: tree_node.cpp TreeNode::resetStatus()
239		// 	todo!()
240		// }
241	}
242
243	/// Add a pre state change callback with the given name.
244	/// The name is not unique, which is important when removing callback.
245	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	/// Remove any pre state change callback with the given name.
254	pub fn remove_pre_state_change_callback(&mut self, name: &str) {
255		self.data.remove_pre_state_change_callback(name);
256	}
257
258	/// Return an iterator over the children.
259	#[must_use]
260	pub fn children_iter(&self) -> impl DoubleEndedIterator<Item = &Self> {
261		self.children().iter()
262	}
263
264	/// Return a mutable iterator over the children.
265	#[must_use]
266	pub fn children_iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Self> {
267		self.children_mut().iter_mut()
268	}
269
270	/// Get an iterator over the tree element.
271	pub fn iter(&self) -> impl Iterator<Item = &Self> {
272		TreeIter::new(self)
273	}
274
275	/// Get a mutable iterator over the tree element.
276	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			// Preconditions only applied when the node state is `Idle` or `Skipped`
287			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			// Preconditions only applied when the node state is `Running`
322			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 not true halt element and return `Skipped`
329				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				// rest is ignored
356				_ => {}
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	/// Returns the Groot2 style 'path' of the element.
368	#[must_use]
369	pub const fn groot2_path(&self) -> &ConstString {
370		self.data.description().groot2_path()
371	}
372
373	/// Returns the current state of the element.
374	#[must_use]
375	pub const fn state(&self) -> BehaviorState {
376		self.data.state()
377	}
378
379	/// Adds a child to the elements children list.
380	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	/// Covers `BehaviorTreeElement::new()` constructor (lines 60-74).
398	/// Uses internal types (`Conditions`, `BehaviorTreeElementList`) since we are in the crate.
399	#[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}