heliostat/addons/beehave/nodes/composites/simple_parallel.gd

123 lines
3.9 KiB
GDScript3
Raw Normal View History

2024-07-29 05:10:46 +00:00
@tool
@icon("../../icons/simple_parallel.svg")
class_name SimpleParallelComposite extends Composite
## Simple Parallel nodes will attampt to execute all chidren at same time and
## can only have exactly two children. First child as primary node, second
## child as secondary node.
## This node will always report primary node's state, and continue tick while
## primary node return 'RUNNING'. The state of secondary node will be ignored
## and executed like a subtree.
## If primary node return 'SUCCESS' or 'FAILURE', this node will interrupt
## secondary node and return primary node's result.
## If this node is running under delay mode, it will wait seconday node
## finish its action after primary node terminates.
#how many times should secondary node repeat, zero means loop forever
@export var secondary_node_repeat_count: int = 0
#wether to wait secondary node finish its current action after primary node finished
@export var delay_mode: bool = false
var delayed_result := SUCCESS
var main_task_finished: bool = false
var secondary_node_running: bool = false
var secondary_node_repeat_left: int = 0
func _get_configuration_warnings() -> PackedStringArray:
var warnings: PackedStringArray = super._get_configuration_warnings()
if get_child_count() != 2:
warnings.append("SimpleParallel should have exactly two child nodes.")
if not get_child(0) is ActionLeaf:
warnings.append("SimpleParallel should have an action leaf node as first child node.")
return warnings
func tick(actor, blackboard: Blackboard):
for c in get_children():
var node_index = c.get_index()
if node_index == 0 and not main_task_finished:
if c != running_child:
c.before_run(actor, blackboard)
var response = c.tick(actor, blackboard)
if can_send_message(blackboard):
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response)
delayed_result = response
match response:
SUCCESS, FAILURE:
_cleanup_running_task(c, actor, blackboard)
c.after_run(actor, blackboard)
main_task_finished = true
if not delay_mode:
if secondary_node_running:
get_child(1).interrupt(actor, blackboard)
_reset()
return delayed_result
RUNNING:
running_child = c
if c is ActionLeaf:
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
elif node_index == 1:
if secondary_node_repeat_count == 0 or secondary_node_repeat_left > 0:
if not secondary_node_running:
c.before_run(actor, blackboard)
var subtree_response = c.tick(actor, blackboard)
if subtree_response != RUNNING:
secondary_node_running = false
c.after_run(actor, blackboard)
if delay_mode and main_task_finished:
_reset()
return delayed_result
elif secondary_node_repeat_left > 0:
secondary_node_repeat_left -= 1
else:
secondary_node_running = true
return RUNNING
func before_run(actor: Node, blackboard: Blackboard) -> void:
secondary_node_repeat_left = secondary_node_repeat_count
super(actor, blackboard)
func interrupt(actor: Node, blackboard: Blackboard) -> void:
if not main_task_finished:
get_child(0).interrupt(actor, blackboard)
if secondary_node_running:
get_child(1).interrupt(actor, blackboard)
_reset()
super(actor, blackboard)
func after_run(actor: Node, blackboard: Blackboard) -> void:
_reset()
super(actor, blackboard)
func _reset() -> void:
main_task_finished = false
secondary_node_running = false
## Changes `running_action` and `running_child` after the node finishes executing.
func _cleanup_running_task(finished_action: Node, actor: Node, blackboard: Blackboard):
var blackboard_name = str(actor.get_instance_id())
if finished_action == running_child:
running_child = null
if finished_action == blackboard.get_value("running_action", null, blackboard_name):
blackboard.set_value("running_action", null, blackboard_name)
func get_class_name() -> Array[StringName]:
var classes := super()
classes.push_back(&"SimpleParallelComposite")
return classes