behaviortree 0.7.4

A #![no_std] compatible behavior tree library similar to 'BehaviorTree.CPP'.
Documentation
// Copyright © 2025 Stephan Kunz
//! [`WasEntryUpdated`] [`Condition`] implementation.

// region:      --- modules
use crate::{
	self as behaviortree, Condition, ConstString, EMPTY_STR,
	behavior::{Behavior, BehaviorData, BehaviorError, BehaviorResult, BehaviorState},
	input_port,
	port::PortList,
	port_list,
	tree::BehaviorTreeElementList,
};
use alloc::{
	boxed::Box,
	string::{String, ToString},
};
use tinyscript::SharedRuntime;
// endregion:   --- modules

// region:		--- globals
/// Port name literals
const ENTRY: &str = "entry";
// endregion:	--- globals

// region:      --- WasEntryUpdated
/// The `WasEntryUpdated` condition returns Success if a blackboard entry was updated otherwise Failure.
/// # Errors
/// - if the entry does not exist
///
/// The behavior is gated behind feature `was_entry_updated` and it is a Groot2 behavior.
#[derive(Condition, Debug, Default)]
#[behavior(groot2)]
pub struct WasEntryUpdated {
	/// ID of the last checked update
	sequence_id: usize,
	/// The entry to monitor
	entry_key: ConstString,
}

#[async_trait::async_trait]
impl Behavior for WasEntryUpdated {
	fn on_start(
		&mut self,
		behavior: &mut BehaviorData,
		_children: &mut BehaviorTreeElementList,
		_runtime: &SharedRuntime,
	) -> Result<(), BehaviorError> {
		self.sequence_id = 0;
		match behavior.remappings().find(ENTRY) {
			databoard::RemappingTarget::BoardPointer(board_pointer)
			| databoard::RemappingTarget::LocalPointer(board_pointer)
			| databoard::RemappingTarget::RootPointer(board_pointer)
			| databoard::RemappingTarget::StringAssignment(board_pointer) => {
				self.entry_key = board_pointer;
				Ok(())
			}
			databoard::RemappingTarget::None(_) => Err(BehaviorError::PortNotDeclared {
				port: "entry".into(),
				behavior: behavior.name().clone(),
			}),
		}
	}

	async fn tick(
		&mut self,
		behavior: &mut BehaviorData,
		_children: &mut BehaviorTreeElementList,
		_runtime: &SharedRuntime,
	) -> BehaviorResult {
		let sequence_id = behavior.sequence_id(&self.entry_key)?;
		if sequence_id == self.sequence_id {
			Ok(BehaviorState::Failure)
		} else {
			self.sequence_id = sequence_id;
			Ok(BehaviorState::Success)
		}
	}

	fn provided_ports() -> PortList {
		port_list![input_port!(
			String,
			ENTRY,
			EMPTY_STR,
			"The blackboard entry to check."
		)]
	}
}
// endregion:   --- WasEntryUpdated