add beehave
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
@tool
|
||||
@icon("../../icons/category_composite.svg")
|
||||
class_name Composite extends BeehaveNode
|
||||
|
||||
## A Composite node controls the flow of execution of its children in a specific manner.
|
||||
|
||||
var running_child: BeehaveNode = null
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = super._get_configuration_warnings()
|
||||
|
||||
if get_children().filter(func(x): return x is BeehaveNode).size() < 2:
|
||||
warnings.append(
|
||||
"Any composite node should have at least two children. Otherwise it is not useful."
|
||||
)
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
if running_child != null:
|
||||
running_child.interrupt(actor, blackboard)
|
||||
running_child = null
|
||||
|
||||
|
||||
func after_run(actor: Node, blackboard: Blackboard) -> void:
|
||||
running_child = null
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"Composite")
|
||||
return classes
|
||||
@@ -0,0 +1,176 @@
|
||||
@tool
|
||||
class_name RandomizedComposite extends Composite
|
||||
|
||||
const WEIGHTS_PREFIX = "Weights/"
|
||||
|
||||
## Sets a predicable seed
|
||||
@export var random_seed: int = 0:
|
||||
set(rs):
|
||||
random_seed = rs
|
||||
if random_seed != 0:
|
||||
seed(random_seed)
|
||||
else:
|
||||
randomize()
|
||||
|
||||
## Wether to use weights for every child or not.
|
||||
@export var use_weights: bool:
|
||||
set(value):
|
||||
use_weights = value
|
||||
if use_weights:
|
||||
_update_weights(get_children())
|
||||
_connect_children_changing_signals()
|
||||
notify_property_list_changed()
|
||||
|
||||
var _weights: Dictionary
|
||||
var _exiting_tree: bool
|
||||
|
||||
|
||||
func _ready():
|
||||
_connect_children_changing_signals()
|
||||
|
||||
|
||||
func _connect_children_changing_signals():
|
||||
if not child_entered_tree.is_connected(_on_child_entered_tree):
|
||||
child_entered_tree.connect(_on_child_entered_tree)
|
||||
|
||||
if not child_exiting_tree.is_connected(_on_child_exiting_tree):
|
||||
child_exiting_tree.connect(_on_child_exiting_tree)
|
||||
|
||||
|
||||
func get_shuffled_children() -> Array[Node]:
|
||||
var children_bag: Array[Node] = get_children().duplicate()
|
||||
if use_weights:
|
||||
var weights: Array[int]
|
||||
weights.assign(children_bag.map(func(child): return _weights[child.name]))
|
||||
children_bag.assign(_weighted_shuffle(children_bag, weights))
|
||||
else:
|
||||
children_bag.shuffle()
|
||||
return children_bag
|
||||
|
||||
|
||||
## Returns a shuffled version of a given array using the supplied array of weights.
|
||||
## Think of weights as the chance of a given item being the first in the array.
|
||||
func _weighted_shuffle(items: Array, weights: Array[int]) -> Array:
|
||||
if len(items) != len(weights):
|
||||
push_error(
|
||||
(
|
||||
"items and weights size mismatch: expected %d weights, got %d instead."
|
||||
% [len(items), len(weights)]
|
||||
)
|
||||
)
|
||||
return items
|
||||
|
||||
# This method is based on the weighted random sampling algorithm
|
||||
# by Efraimidis, Spirakis; 2005. This runs in O(n log(n)).
|
||||
|
||||
# For each index, it will calculate random_value^(1/weight).
|
||||
var chance_calc = func(i): return [i, randf() ** (1.0 / weights[i])]
|
||||
var random_distribuition = range(len(items)).map(chance_calc)
|
||||
|
||||
# Now we just have to order by the calculated value, descending.
|
||||
random_distribuition.sort_custom(func(a, b): return a[1] > b[1])
|
||||
|
||||
return random_distribuition.map(func(dist): return items[dist[0]])
|
||||
|
||||
|
||||
func _get_property_list():
|
||||
var properties = []
|
||||
|
||||
if use_weights:
|
||||
for key in _weights.keys():
|
||||
properties.append(
|
||||
{
|
||||
"name": WEIGHTS_PREFIX + key,
|
||||
"type": TYPE_INT,
|
||||
"usage": PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "1,100"
|
||||
}
|
||||
)
|
||||
|
||||
return properties
|
||||
|
||||
|
||||
func _set(property: StringName, value: Variant) -> bool:
|
||||
if property.begins_with(WEIGHTS_PREFIX):
|
||||
var weight_name = property.trim_prefix(WEIGHTS_PREFIX)
|
||||
_weights[weight_name] = value
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func _get(property: StringName):
|
||||
if property.begins_with(WEIGHTS_PREFIX):
|
||||
var weight_name = property.trim_prefix(WEIGHTS_PREFIX)
|
||||
return _weights[weight_name]
|
||||
|
||||
return null
|
||||
|
||||
|
||||
func _update_weights(children: Array[Node]) -> void:
|
||||
var new_weights = {}
|
||||
for c in children:
|
||||
if _weights.has(c.name):
|
||||
new_weights[c.name] = _weights[c.name]
|
||||
else:
|
||||
new_weights[c.name] = 1
|
||||
_weights = new_weights
|
||||
notify_property_list_changed()
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
_exiting_tree = true
|
||||
|
||||
|
||||
func _enter_tree() -> void:
|
||||
_exiting_tree = false
|
||||
|
||||
|
||||
func _on_child_entered_tree(node: Node):
|
||||
_update_weights(get_children())
|
||||
|
||||
var renamed_callable = _on_child_renamed.bind(node.name, node)
|
||||
if not node.renamed.is_connected(renamed_callable):
|
||||
node.renamed.connect(renamed_callable)
|
||||
|
||||
if not node.tree_exited.is_connected(_on_child_tree_exited):
|
||||
node.tree_exited.connect(_on_child_tree_exited.bind(node))
|
||||
|
||||
|
||||
func _on_child_exiting_tree(node: Node):
|
||||
var renamed_callable = _on_child_renamed.bind(node.name, node)
|
||||
if node.renamed.is_connected(renamed_callable):
|
||||
node.renamed.disconnect(renamed_callable)
|
||||
|
||||
|
||||
func _on_child_tree_exited(node: Node) -> void:
|
||||
# don't erase the individual child if the whole tree is exiting together
|
||||
if not _exiting_tree:
|
||||
var children = get_children()
|
||||
children.erase(node)
|
||||
_update_weights(children)
|
||||
|
||||
if node.tree_exited.is_connected(_on_child_tree_exited):
|
||||
node.tree_exited.disconnect(_on_child_tree_exited)
|
||||
|
||||
|
||||
func _on_child_renamed(old_name: String, renamed_child: Node):
|
||||
if old_name == renamed_child.name:
|
||||
return # No need to update the weights.
|
||||
|
||||
# Disconnect signal with old name...
|
||||
renamed_child.renamed.disconnect(_on_child_renamed.bind(old_name, renamed_child))
|
||||
# ...and connect with the new name.
|
||||
renamed_child.renamed.connect(_on_child_renamed.bind(renamed_child.name, renamed_child))
|
||||
|
||||
var original_weight = _weights[old_name]
|
||||
_weights.erase(old_name)
|
||||
_weights[renamed_child.name] = original_weight
|
||||
notify_property_list_changed()
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"RandomizedComposite")
|
||||
return classes
|
||||
@@ -0,0 +1,69 @@
|
||||
@tool
|
||||
@icon("../../icons/selector.svg")
|
||||
class_name SelectorComposite extends Composite
|
||||
|
||||
## Selector nodes will attempt to execute each of its children until one of
|
||||
## them return `SUCCESS`. If all children return `FAILURE`, this node will also
|
||||
## return `FAILURE`.
|
||||
## If a child returns `RUNNING` it will tick again.
|
||||
|
||||
var last_execution_index: int = 0
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < last_execution_index:
|
||||
continue
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response: int = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data())
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
_cleanup_running_task(c, actor, blackboard)
|
||||
c.after_run(actor, blackboard)
|
||||
return SUCCESS
|
||||
FAILURE:
|
||||
_cleanup_running_task(c, actor, blackboard)
|
||||
last_execution_index += 1
|
||||
c.after_run(actor, blackboard)
|
||||
RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
return FAILURE
|
||||
|
||||
|
||||
func after_run(actor: Node, blackboard: Blackboard) -> void:
|
||||
last_execution_index = 0
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
last_execution_index = 0
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
## 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: String = 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(&"SelectorComposite")
|
||||
return classes
|
||||
@@ -0,0 +1,82 @@
|
||||
@tool
|
||||
@icon("../../icons/selector_random.svg")
|
||||
class_name SelectorRandomComposite extends RandomizedComposite
|
||||
|
||||
## This node will attempt to execute all of its children just like a
|
||||
## [code]SelectorStar[/code] would, with the exception that the children
|
||||
## will be executed in a random order.
|
||||
|
||||
## A shuffled list of the children that will be executed in reverse order.
|
||||
var _children_bag: Array[Node] = []
|
||||
var c: Node
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
super()
|
||||
if random_seed == 0:
|
||||
randomize()
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
if _children_bag.is_empty():
|
||||
_reset()
|
||||
|
||||
# We need to traverse the array in reverse since we will be manipulating it.
|
||||
for i in _get_reversed_indexes():
|
||||
c = _children_bag[i]
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response: int = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data())
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
_children_bag.erase(c)
|
||||
c.after_run(actor, blackboard)
|
||||
return SUCCESS
|
||||
FAILURE:
|
||||
_children_bag.erase(c)
|
||||
c.after_run(actor, blackboard)
|
||||
RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
return FAILURE
|
||||
|
||||
|
||||
func after_run(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _get_reversed_indexes() -> Array[int]:
|
||||
var reversed: Array[int]
|
||||
reversed.assign(range(_children_bag.size()))
|
||||
reversed.reverse()
|
||||
return reversed
|
||||
|
||||
|
||||
func _reset() -> void:
|
||||
var new_order = get_shuffled_children()
|
||||
_children_bag = new_order.duplicate()
|
||||
_children_bag.reverse() # It needs to run the children in reverse order.
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SelectorRandomComposite")
|
||||
return classes
|
||||
@@ -0,0 +1,47 @@
|
||||
@tool
|
||||
@icon("../../icons/selector_reactive.svg")
|
||||
class_name SelectorReactiveComposite extends Composite
|
||||
|
||||
## Selector Reactive nodes will attempt to execute each of its children until one of
|
||||
## them return `SUCCESS`. If all children return `FAILURE`, this node will also
|
||||
## return `FAILURE`.
|
||||
## If a child returns `RUNNING` it will restart.
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response: int = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data())
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
# Interrupt any child that was RUNNING before.
|
||||
if c != running_child:
|
||||
interrupt(actor, blackboard)
|
||||
c.after_run(actor, blackboard)
|
||||
return SUCCESS
|
||||
FAILURE:
|
||||
c.after_run(actor, blackboard)
|
||||
RUNNING:
|
||||
if c != running_child:
|
||||
interrupt(actor, blackboard)
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
return FAILURE
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SelectorReactiveComposite")
|
||||
return classes
|
||||
@@ -0,0 +1,75 @@
|
||||
@tool
|
||||
@icon("../../icons/sequence.svg")
|
||||
class_name SequenceComposite extends Composite
|
||||
|
||||
## Sequence nodes will attempt to execute all of its children and report
|
||||
## `SUCCESS` in case all of the children report a `SUCCESS` status code.
|
||||
## If at least one child reports a `FAILURE` status code, this node will also
|
||||
## return `FAILURE` and restart.
|
||||
## In case a child returns `RUNNING` this node will tick again.
|
||||
|
||||
var successful_index: int = 0
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < successful_index:
|
||||
continue
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response: int = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data())
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
_cleanup_running_task(c, actor, blackboard)
|
||||
successful_index += 1
|
||||
c.after_run(actor, blackboard)
|
||||
FAILURE:
|
||||
_cleanup_running_task(c, actor, blackboard)
|
||||
# Interrupt any child that was RUNNING before.
|
||||
interrupt(actor, blackboard)
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
RUNNING:
|
||||
if c != running_child:
|
||||
if running_child != null:
|
||||
running_child.interrupt(actor, blackboard)
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
_reset()
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _reset() -> void:
|
||||
successful_index = 0
|
||||
|
||||
|
||||
## 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: String = 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(&"SequenceComposite")
|
||||
return classes
|
||||
@@ -0,0 +1,96 @@
|
||||
@tool
|
||||
@icon("../../icons/sequence_random.svg")
|
||||
class_name SequenceRandomComposite extends RandomizedComposite
|
||||
|
||||
## This node will attempt to execute all of its children just like a
|
||||
## [code]SequenceStar[/code] would, with the exception that the children
|
||||
## will be executed in a random order.
|
||||
|
||||
# Emitted whenever the children are shuffled.
|
||||
signal reset(new_order: Array[Node])
|
||||
|
||||
## Whether the sequence should start where it left off after a previous failure.
|
||||
@export var resume_on_failure: bool = false
|
||||
## Whether the sequence should start where it left off after a previous interruption.
|
||||
@export var resume_on_interrupt: bool = false
|
||||
|
||||
## A shuffled list of the children that will be executed in reverse order.
|
||||
var _children_bag: Array[Node] = []
|
||||
var c: Node
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
super()
|
||||
if random_seed == 0:
|
||||
randomize()
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
if _children_bag.is_empty():
|
||||
_reset()
|
||||
|
||||
# We need to traverse the array in reverse since we will be manipulating it.
|
||||
for i in _get_reversed_indexes():
|
||||
c = _children_bag[i]
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response: int = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data())
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
_children_bag.erase(c)
|
||||
c.after_run(actor, blackboard)
|
||||
FAILURE:
|
||||
_children_bag.erase(c)
|
||||
# Interrupt any child that was RUNNING before
|
||||
# but do not reset!
|
||||
super.interrupt(actor, blackboard)
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func after_run(actor: Node, blackboard: Blackboard) -> void:
|
||||
if not resume_on_failure:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
if not resume_on_interrupt:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _get_reversed_indexes() -> Array[int]:
|
||||
var reversed: Array[int]
|
||||
reversed.assign(range(_children_bag.size()))
|
||||
reversed.reverse()
|
||||
return reversed
|
||||
|
||||
|
||||
func _reset() -> void:
|
||||
var new_order: Array[Node] = get_shuffled_children()
|
||||
_children_bag = new_order.duplicate()
|
||||
_children_bag.reverse() # It needs to run the children in reverse order.
|
||||
reset.emit(new_order)
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SequenceRandomComposite")
|
||||
return classes
|
||||
@@ -0,0 +1,63 @@
|
||||
@tool
|
||||
@icon("../../icons/sequence_reactive.svg")
|
||||
class_name SequenceReactiveComposite extends Composite
|
||||
|
||||
## Reactive Sequence nodes will attempt to execute all of its children and report
|
||||
## `SUCCESS` in case all of the children report a `SUCCESS` status code.
|
||||
## If at least one child reports a `FAILURE` status code, this node will also
|
||||
## return `FAILURE` and restart.
|
||||
## In case a child returns `RUNNING` this node will restart.
|
||||
|
||||
var successful_index: int = 0
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < successful_index:
|
||||
continue
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response: int = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data())
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
successful_index += 1
|
||||
c.after_run(actor, blackboard)
|
||||
FAILURE:
|
||||
# Interrupt any child that was RUNNING before.
|
||||
interrupt(actor, blackboard)
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
RUNNING:
|
||||
_reset()
|
||||
if running_child != c:
|
||||
interrupt(actor, blackboard)
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
_reset()
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _reset() -> void:
|
||||
successful_index = 0
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SequenceReactiveComposite")
|
||||
return classes
|
||||
@@ -0,0 +1,61 @@
|
||||
@tool
|
||||
@icon("../../icons/sequence_reactive.svg")
|
||||
class_name SequenceStarComposite extends Composite
|
||||
|
||||
## Sequence Star nodes will attempt to execute all of its children and report
|
||||
## `SUCCESS` in case all of the children report a `SUCCESS` status code.
|
||||
## If at least one child reports a `FAILURE` status code, this node will also
|
||||
## return `FAILURE` and tick again.
|
||||
## In case a child returns `RUNNING` this node will tick again.
|
||||
|
||||
var successful_index: int = 0
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < successful_index:
|
||||
continue
|
||||
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response: int = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data())
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
match response:
|
||||
SUCCESS:
|
||||
successful_index += 1
|
||||
c.after_run(actor, blackboard)
|
||||
FAILURE:
|
||||
# Interrupt any child that was RUNNING before
|
||||
# but do not reset!
|
||||
super.interrupt(actor, blackboard)
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
_reset()
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _reset() -> void:
|
||||
successful_index = 0
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"SequenceStarComposite")
|
||||
return classes
|
||||
@@ -0,0 +1,122 @@
|
||||
@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: int = c.get_index()
|
||||
if node_index == 0 and not main_task_finished:
|
||||
if c != running_child:
|
||||
c.before_run(actor, blackboard)
|
||||
|
||||
var response: int = c.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(c.get_instance_id(), response, blackboard.get_debug_data())
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user