|  |  |  | ## Controls the flow of execution of the entire behavior tree. | 
					
						
							|  |  |  | @tool | 
					
						
							|  |  |  | @icon("../icons/tree.svg") | 
					
						
							|  |  |  | class_name BeehaveTree extends Node | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum { | 
					
						
							|  |  |  |     SUCCESS, | 
					
						
							|  |  |  |     FAILURE, | 
					
						
							|  |  |  |     RUNNING | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | signal tree_enabled | 
					
						
							|  |  |  | signal tree_disabled | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Wether this behavior tree should be enabled or not. | 
					
						
							|  |  |  | @export var enabled: bool = true: | 
					
						
							|  |  |  |     set(value): | 
					
						
							|  |  |  |         enabled = value | 
					
						
							|  |  |  |         set_physics_process(enabled) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if value: | 
					
						
							|  |  |  |             tree_enabled.emit() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             interrupt() | 
					
						
							|  |  |  |             tree_disabled.emit() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     get: | 
					
						
							|  |  |  |         return enabled | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## An optional node path this behavior tree should apply to. | 
					
						
							|  |  |  | @export_node_path var actor_node_path : NodePath | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Custom blackboard node. An internal blackboard will be used | 
					
						
							|  |  |  | ## if no blackboard is provided explicitly. | 
					
						
							|  |  |  | @export var blackboard:Blackboard: | 
					
						
							|  |  |  |     set(b): | 
					
						
							|  |  |  |         blackboard = b | 
					
						
							|  |  |  |         if blackboard and _internal_blackboard: | 
					
						
							|  |  |  |             remove_child(_internal_blackboard) | 
					
						
							|  |  |  |             _internal_blackboard.free() | 
					
						
							|  |  |  |             _internal_blackboard = null | 
					
						
							|  |  |  |         elif not blackboard and not _internal_blackboard: | 
					
						
							|  |  |  |             _internal_blackboard = Blackboard.new() | 
					
						
							|  |  |  |             add_child(_internal_blackboard, false, Node.INTERNAL_MODE_BACK) | 
					
						
							|  |  |  |     get: | 
					
						
							|  |  |  |         return blackboard if blackboard else _internal_blackboard | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## When enabled, this tree is tracked individually | 
					
						
							|  |  |  | ## as a custom monitor. | 
					
						
							|  |  |  | @export var custom_monitor = false: | 
					
						
							|  |  |  |     set(b): | 
					
						
							|  |  |  |         custom_monitor = b | 
					
						
							|  |  |  |         if custom_monitor and _process_time_metric_name != '': | 
					
						
							|  |  |  |             Performance.add_custom_monitor(_process_time_metric_name, _get_process_time_metric_value) | 
					
						
							|  |  |  |             BeehaveGlobalMetrics.register_tree(self) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if _process_time_metric_name != '': | 
					
						
							|  |  |  |                 # Remove tree metric from the engine | 
					
						
							|  |  |  |                 Performance.remove_custom_monitor(_process_time_metric_name) | 
					
						
							|  |  |  |                 BeehaveGlobalMetrics.unregister_tree(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             BeehaveDebuggerMessages.unregister_tree(get_instance_id()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var actor : Node | 
					
						
							|  |  |  | var status : int = -1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var _internal_blackboard: Blackboard | 
					
						
							|  |  |  | var _process_time_metric_name : String | 
					
						
							|  |  |  | var _process_time_metric_value : float = 0.0 | 
					
						
							|  |  |  | var _can_send_message: bool = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func _ready() -> void: | 
					
						
							|  |  |  |     if Engine.is_editor_hint(): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if self.get_child_count() > 0 and not self.get_child(0) is BeehaveNode: | 
					
						
							|  |  |  |         push_warning("Beehave error: Root %s should have only one child of type BeehaveNode (NodePath: %s)" % [self.name, self.get_path()]) | 
					
						
							|  |  |  |         disable() | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not blackboard: | 
					
						
							|  |  |  |         _internal_blackboard = Blackboard.new() | 
					
						
							|  |  |  |         add_child(_internal_blackboard, false, Node.INTERNAL_MODE_BACK) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     actor = get_parent() | 
					
						
							|  |  |  |     if actor_node_path: | 
					
						
							|  |  |  |         actor = get_node(actor_node_path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Get the name of the parent node name for metric | 
					
						
							|  |  |  |     var parent_name = actor.name | 
					
						
							|  |  |  |     _process_time_metric_name = "beehave [microseconds]/process_time_%s-%s" % [parent_name, get_instance_id()] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Register custom metric to the engine | 
					
						
							|  |  |  |     if custom_monitor: | 
					
						
							|  |  |  |         Performance.add_custom_monitor(_process_time_metric_name, _get_process_time_metric_value) | 
					
						
							|  |  |  |         BeehaveGlobalMetrics.register_tree(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     set_physics_process(enabled) | 
					
						
							|  |  |  |     BeehaveGlobalDebugger.register_tree(self) | 
					
						
							|  |  |  |     BeehaveDebuggerMessages.register_tree(_get_debugger_data(self)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func _physics_process(delta: float) -> void: | 
					
						
							|  |  |  |     if Engine.is_editor_hint(): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Start timing for metric | 
					
						
							|  |  |  |     var start_time = Time.get_ticks_usec() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     blackboard.set_value("can_send_message", _can_send_message) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if _can_send_message: | 
					
						
							|  |  |  |         BeehaveDebuggerMessages.process_begin(get_instance_id()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if self.get_child_count() == 1: | 
					
						
							|  |  |  |         tick() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if _can_send_message: | 
					
						
							|  |  |  |         BeehaveDebuggerMessages.process_end(get_instance_id()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Check the cost for this frame and save it for metric report | 
					
						
							|  |  |  |     _process_time_metric_value = Time.get_ticks_usec() - start_time | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func tick() -> int: | 
					
						
							|  |  |  |     var child := self.get_child(0) | 
					
						
							|  |  |  |     if status != RUNNING: | 
					
						
							|  |  |  |         child.before_run(actor, blackboard) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     status = child.tick(actor, blackboard) | 
					
						
							|  |  |  |     if _can_send_message: | 
					
						
							|  |  |  |         BeehaveDebuggerMessages.process_tick(child.get_instance_id(), status) | 
					
						
							|  |  |  |         BeehaveDebuggerMessages.process_tick(get_instance_id(), status) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Clear running action if nothing is running | 
					
						
							|  |  |  |     if status != RUNNING: | 
					
						
							|  |  |  |         blackboard.set_value("running_action", null, str(actor.get_instance_id())) | 
					
						
							|  |  |  |         child.after_run(actor, blackboard) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return status | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func _get_configuration_warnings() -> PackedStringArray: | 
					
						
							|  |  |  |     var warnings:PackedStringArray = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if get_children().any(func(x): return not (x is BeehaveNode)): | 
					
						
							|  |  |  |         warnings.append("All children of this node should inherit from BeehaveNode class.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if get_child_count() != 1: | 
					
						
							|  |  |  |         warnings.append("BeehaveTree should have exactly one child node.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return warnings | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Returns the currently running action | 
					
						
							|  |  |  | func get_running_action() -> ActionLeaf: | 
					
						
							|  |  |  |     return blackboard.get_value("running_action", null, str(actor.get_instance_id())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Returns the last condition that was executed | 
					
						
							|  |  |  | func get_last_condition() -> ConditionLeaf: | 
					
						
							|  |  |  |     return blackboard.get_value("last_condition", null, str(actor.get_instance_id())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Returns the status of the last executed condition | 
					
						
							|  |  |  | func get_last_condition_status() -> String: | 
					
						
							|  |  |  |     if blackboard.has_value("last_condition_status", str(actor.get_instance_id())): | 
					
						
							|  |  |  |         var status = blackboard.get_value("last_condition_status", null, str(actor.get_instance_id())) | 
					
						
							|  |  |  |         if status == SUCCESS: | 
					
						
							|  |  |  |             return "SUCCESS" | 
					
						
							|  |  |  |         elif status == FAILURE: | 
					
						
							|  |  |  |             return "FAILURE" | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return "RUNNING" | 
					
						
							|  |  |  |     return "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## interrupts this tree if anything was running | 
					
						
							|  |  |  | func interrupt() -> void: | 
					
						
							|  |  |  |     if self.get_child_count() != 0: | 
					
						
							|  |  |  |         var first_child = self.get_child(0) | 
					
						
							|  |  |  |         if "interrupt" in first_child: | 
					
						
							|  |  |  |             first_child.interrupt(actor, blackboard) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Enables this tree. | 
					
						
							|  |  |  | func enable() -> void: | 
					
						
							|  |  |  |     self.enabled = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Disables this tree. | 
					
						
							|  |  |  | func disable() -> void: | 
					
						
							|  |  |  |     self.enabled = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func _exit_tree() -> void: | 
					
						
							|  |  |  |     if custom_monitor: | 
					
						
							|  |  |  |         if _process_time_metric_name != '': | 
					
						
							|  |  |  |             # Remove tree metric from the engine | 
					
						
							|  |  |  |             Performance.remove_custom_monitor(_process_time_metric_name) | 
					
						
							|  |  |  |             BeehaveGlobalMetrics.unregister_tree(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         BeehaveDebuggerMessages.unregister_tree(get_instance_id()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Called by the engine to profile this tree | 
					
						
							|  |  |  | func _get_process_time_metric_value() -> int: | 
					
						
							|  |  |  |     return _process_time_metric_value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func _get_debugger_data(node: Node) -> Dictionary: | 
					
						
							|  |  |  |     if not node is BeehaveTree and not node is BeehaveNode: | 
					
						
							|  |  |  |         return {} | 
					
						
							|  |  |  |     var data := { path = node.get_path(), name = node.name, type = node.get_class_name(), id = str(node.get_instance_id()) } | 
					
						
							|  |  |  |     if node.get_child_count() > 0: | 
					
						
							|  |  |  |         data.children = [] | 
					
						
							|  |  |  |     for child in node.get_children(): | 
					
						
							|  |  |  |         var child_data := _get_debugger_data(child) | 
					
						
							|  |  |  |         if not child_data.is_empty(): | 
					
						
							|  |  |  |             data.children.push_back(child_data) | 
					
						
							|  |  |  |     return data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func get_class_name() -> Array[StringName]: | 
					
						
							|  |  |  |     return [&"BeehaveTree"] |