Add addons
This commit is contained in:
parent
2c862b2235
commit
3367bad24f
527 changed files with 36197 additions and 0 deletions
14
Sprout/addons/beehave/nodes/beehave_node.gd
Normal file
14
Sprout/addons/beehave/nodes/beehave_node.gd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
## A node in the behaviour tree. Every node must return `SUCCESS`, `FAILURE` or
|
||||
## `RUNNING` when ticked.
|
||||
@tool
|
||||
class_name BeehaveNode extends BeehaveTree
|
||||
|
||||
## Executes this node and returns a status code.
|
||||
## This method must be overwritten.
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
return SUCCESS
|
||||
|
||||
|
||||
## Called when this node needs to be interrupted before it can return FAILURE or SUCCESS.
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
pass
|
||||
93
Sprout/addons/beehave/nodes/beehave_root.gd
Normal file
93
Sprout/addons/beehave/nodes/beehave_root.gd
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
## Controls the flow of execution of the entire behaviour tree.
|
||||
@tool
|
||||
class_name BeehaveRoot extends BeehaveTree
|
||||
@icon("../icons/tree.svg")
|
||||
|
||||
|
||||
@export var enabled: bool = true:
|
||||
set(value):
|
||||
enabled = value
|
||||
set_physics_process(enabled)
|
||||
get:
|
||||
return enabled
|
||||
|
||||
@export_node_path var actor_node_path : NodePath
|
||||
|
||||
var actor : Node
|
||||
|
||||
@onready var blackboard: Blackboard = Blackboard.new()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if self.get_child_count() != 1:
|
||||
push_error("Beehave error: Root %s should have one child (NodePath: %s)" % [self.name, self.get_path()])
|
||||
disable()
|
||||
return
|
||||
|
||||
actor = get_parent()
|
||||
if actor_node_path:
|
||||
actor = get_node(actor_node_path)
|
||||
|
||||
set_physics_process(enabled)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
blackboard.set_value("delta", delta)
|
||||
var status = self.get_child(0).tick(actor, blackboard)
|
||||
|
||||
# Updates the current running action.
|
||||
var running_action = get_running_action() if status == RUNNING else null
|
||||
blackboard.set_value("running_action", running_action)
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = super._get_configuration_warnings()
|
||||
|
||||
if get_child_count() != 1:
|
||||
warnings.append("BeehaveRoot should have exactly one child node.")
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
func get_running_action() -> ActionLeaf:
|
||||
var node = get_child(0)
|
||||
while node != null:
|
||||
if node is Composite:
|
||||
node = (node as Composite).running_child
|
||||
elif node is ActionLeaf:
|
||||
return node
|
||||
|
||||
push_error("Beehave error: Could not find running action in tree root '%s'." % name)
|
||||
return null
|
||||
|
||||
|
||||
func get_last_condition() -> Variant:
|
||||
if blackboard.has_value("last_condition"):
|
||||
return blackboard.get_value("last_condition")
|
||||
return null
|
||||
|
||||
|
||||
func get_last_condition_status() -> String:
|
||||
if blackboard.has_value("last_condition_status"):
|
||||
var status = blackboard.get_value("last_condition_status")
|
||||
if status == SUCCESS:
|
||||
return "SUCCESS"
|
||||
elif status == FAILURE:
|
||||
return "FAILURE"
|
||||
else:
|
||||
return "RUNNING"
|
||||
return ""
|
||||
|
||||
|
||||
func enable() -> void:
|
||||
enabled = true
|
||||
|
||||
|
||||
func disable() -> void:
|
||||
enabled = false
|
||||
20
Sprout/addons/beehave/nodes/beehave_tree.gd
Normal file
20
Sprout/addons/beehave/nodes/beehave_tree.gd
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
## Base class for all parts of the behaviour tree.
|
||||
@tool
|
||||
class_name BeehaveTree extends Node
|
||||
@icon("../icons/category_bt.svg")
|
||||
|
||||
|
||||
enum {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
RUNNING
|
||||
}
|
||||
|
||||
|
||||
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.")
|
||||
|
||||
return warnings
|
||||
30
Sprout/addons/beehave/nodes/composites/composite.gd
Normal file
30
Sprout/addons/beehave/nodes/composites/composite.gd
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
## A Composite node controls the flow of execution of its children in a specific manner.
|
||||
@tool
|
||||
class_name Composite extends BeehaveNode
|
||||
@icon("../../icons/category_composite.svg")
|
||||
|
||||
|
||||
var running_child: BeehaveNode = null
|
||||
|
||||
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if self.get_child_count() < 1:
|
||||
push_error("BehaviorTree Error: Composite %s should have at least one child (NodePath: %s)" % [self.name, self.get_path()])
|
||||
|
||||
|
||||
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
|
||||
25
Sprout/addons/beehave/nodes/composites/selector.gd
Normal file
25
Sprout/addons/beehave/nodes/composites/selector.gd
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
## 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`. This node will attempt to process all its children every
|
||||
## single tick, even if one of them is currently `RUNNING` already.
|
||||
@tool
|
||||
class_name SelectorComposite extends Composite
|
||||
@icon("../../icons/selector.svg")
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response != FAILURE:
|
||||
if response == SUCCESS:
|
||||
# Interrupt any child that was RUNNING before.
|
||||
interrupt(actor, blackboard)
|
||||
else: # RUNNING
|
||||
running_child = c
|
||||
return response
|
||||
|
||||
return FAILURE
|
||||
54
Sprout/addons/beehave/nodes/composites/selector_random.gd
Normal file
54
Sprout/addons/beehave/nodes/composites/selector_random.gd
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
## 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.
|
||||
@tool
|
||||
class_name SelectorRandomComposite extends Composite
|
||||
@icon("../../icons/selector_random.svg")
|
||||
|
||||
|
||||
## A shuffled list of the children that will be executed in reverse order.
|
||||
var _children_bag: Array[Node] = []
|
||||
var c: Node
|
||||
|
||||
|
||||
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]
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response == RUNNING:
|
||||
running_child = c
|
||||
else:
|
||||
_children_bag.erase(c)
|
||||
|
||||
if response != FAILURE:
|
||||
if response == SUCCESS:
|
||||
_reset()
|
||||
return response
|
||||
|
||||
return FAILURE
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _get_reversed_indexes() -> Array[int]:
|
||||
var reversed = range(_children_bag.size())
|
||||
reversed.reverse()
|
||||
return reversed
|
||||
|
||||
|
||||
## Generates a new shuffled list of the children.
|
||||
func _reset() -> void:
|
||||
_children_bag = get_children().duplicate()
|
||||
_children_bag.shuffle()
|
||||
32
Sprout/addons/beehave/nodes/composites/selector_star.gd
Normal file
32
Sprout/addons/beehave/nodes/composites/selector_star.gd
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
## Selector Star 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`. This node will skip all previous child nodes that were
|
||||
## executed prior, in case one of the children is currently in `RUNNING` state.
|
||||
@tool
|
||||
class_name SelectorStarComposite extends Composite
|
||||
@icon("../../icons/selector_reactive.svg")
|
||||
|
||||
var last_execution_index = 0
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < last_execution_index:
|
||||
continue
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response != FAILURE:
|
||||
if response == SUCCESS:
|
||||
last_execution_index = 0
|
||||
else: # RUNNING
|
||||
running_child = c
|
||||
return response
|
||||
else:
|
||||
last_execution_index += 1
|
||||
|
||||
last_execution_index = 0
|
||||
return FAILURE
|
||||
26
Sprout/addons/beehave/nodes/composites/sequence.gd
Normal file
26
Sprout/addons/beehave/nodes/composites/sequence.gd
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
## 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`. This node will attempt to process all its children every
|
||||
## single tick, even if one of them is currently `RUNNING` already.
|
||||
@tool
|
||||
class_name SequenceComposite extends Composite
|
||||
@icon("../../icons/sequence.svg")
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response != SUCCESS:
|
||||
if response == FAILURE:
|
||||
# Interrupt any child that was RUNNING before.
|
||||
interrupt(actor, blackboard)
|
||||
else: # RUNNING
|
||||
running_child = c
|
||||
return response
|
||||
|
||||
return SUCCESS
|
||||
60
Sprout/addons/beehave/nodes/composites/sequence_random.gd
Normal file
60
Sprout/addons/beehave/nodes/composites/sequence_random.gd
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
## 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.
|
||||
@tool
|
||||
class_name SequenceRandomComposite extends Composite
|
||||
@icon("../../icons/sequence_random.svg")
|
||||
|
||||
|
||||
## 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 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]
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response == RUNNING:
|
||||
running_child = c
|
||||
else:
|
||||
_children_bag.erase(c)
|
||||
|
||||
if response != SUCCESS:
|
||||
if not resume_on_failure and response == FAILURE:
|
||||
_reset()
|
||||
return response
|
||||
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func interrupt(actor: Node, blackboard: Blackboard) -> void:
|
||||
if not resume_on_interrupt:
|
||||
_reset()
|
||||
super(actor, blackboard)
|
||||
|
||||
|
||||
func _get_reversed_indexes() -> Array[int]:
|
||||
var reversed = range(_children_bag.size())
|
||||
reversed.reverse()
|
||||
return reversed
|
||||
|
||||
|
||||
## Generates a new shuffled list of the children.
|
||||
func _reset() -> void:
|
||||
_children_bag = get_children().duplicate()
|
||||
_children_bag.shuffle()
|
||||
37
Sprout/addons/beehave/nodes/composites/sequence_star.gd
Normal file
37
Sprout/addons/beehave/nodes/composites/sequence_star.gd
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
## 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`. This node will skip all previous child nodes that succeeded
|
||||
## prior, in case one of the children is currently in `RUNNING` state
|
||||
@tool
|
||||
class_name SequenceStarComposite extends Composite
|
||||
@icon("../../icons/sequence_reactive.svg")
|
||||
|
||||
var successful_index = 0
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
if c.get_index() < successful_index:
|
||||
continue
|
||||
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if c is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", c)
|
||||
blackboard.set_value("last_condition_status", response)
|
||||
|
||||
if response != SUCCESS:
|
||||
if response == FAILURE:
|
||||
successful_index = 0
|
||||
else: # RUNNING
|
||||
running_child = c
|
||||
return response
|
||||
else:
|
||||
successful_index += 1
|
||||
|
||||
if successful_index == get_child_count():
|
||||
successful_index = 0
|
||||
return SUCCESS
|
||||
else:
|
||||
successful_index = 0
|
||||
return FAILURE
|
||||
22
Sprout/addons/beehave/nodes/decorators/decorator.gd
Normal file
22
Sprout/addons/beehave/nodes/decorators/decorator.gd
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
## Decorator nodes are used to transform the result received by its child.
|
||||
## Must only have one child.
|
||||
@tool
|
||||
class_name Decorator extends BeehaveNode
|
||||
@icon("../../icons/category_decorator.svg")
|
||||
|
||||
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if self.get_child_count() != 1:
|
||||
push_error("Beehave Error: Decorator %s should have only one child (NodePath: %s)" % [self.name, self.get_path()])
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = super._get_configuration_warnings()
|
||||
|
||||
if get_child_count() != 1:
|
||||
warnings.append("Decorator should have exactly one child node.")
|
||||
|
||||
return warnings
|
||||
12
Sprout/addons/beehave/nodes/decorators/failer.gd
Normal file
12
Sprout/addons/beehave/nodes/decorators/failer.gd
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
## A Failer node will always return a `FAILURE` status code.
|
||||
@tool
|
||||
class_name AlwaysFailDecorator extends Decorator
|
||||
@icon("../../icons/failer.svg")
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
if response == RUNNING:
|
||||
return RUNNING
|
||||
return FAILURE
|
||||
21
Sprout/addons/beehave/nodes/decorators/inverter.gd
Normal file
21
Sprout/addons/beehave/nodes/decorators/inverter.gd
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
## An inverter will return `FAILURE` in case it's child returns a `SUCCESS` status
|
||||
## code or `SUCCESS` in case its child returns a `FAILURE` status code.
|
||||
@tool
|
||||
class_name InverterDecorator extends Decorator
|
||||
@icon("../../icons/inverter.svg")
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
|
||||
if response == SUCCESS:
|
||||
return FAILURE
|
||||
if response == FAILURE:
|
||||
return SUCCESS
|
||||
|
||||
if c is Leaf and response == RUNNING:
|
||||
blackboard.set_value("running_action", c)
|
||||
return RUNNING
|
||||
|
||||
# Decorators must have a child. This should be unreachable code.
|
||||
return FAILURE
|
||||
21
Sprout/addons/beehave/nodes/decorators/limiter.gd
Normal file
21
Sprout/addons/beehave/nodes/decorators/limiter.gd
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
## The limiter will execute its child `x` amount of times. When the number of
|
||||
## maximum ticks is reached, it will return a `FAILURE` status code.
|
||||
@tool
|
||||
class_name LimiterDecorator extends Decorator
|
||||
@icon("../../icons/limiter.svg")
|
||||
|
||||
@onready var cache_key = 'limiter_%s' % self.get_instance_id()
|
||||
|
||||
@export var max_count : float = 0
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var current_count = blackboard.get_value(cache_key)
|
||||
|
||||
if current_count == null:
|
||||
current_count = 0
|
||||
|
||||
if current_count <= max_count:
|
||||
blackboard.set_value(cache_key, current_count + 1)
|
||||
return self.get_child(0).tick(actor, blackboard)
|
||||
else:
|
||||
return FAILURE
|
||||
12
Sprout/addons/beehave/nodes/decorators/succeeder.gd
Normal file
12
Sprout/addons/beehave/nodes/decorators/succeeder.gd
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
## A succeeder node will always return a `SUCCESS` status code.
|
||||
@tool
|
||||
class_name AlwaysSucceedDecorator extends Decorator
|
||||
@icon("../../icons/succeeder.svg")
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
for c in get_children():
|
||||
var response = c.tick(actor, blackboard)
|
||||
if response == RUNNING:
|
||||
return RUNNING
|
||||
return SUCCESS
|
||||
7
Sprout/addons/beehave/nodes/leaves/action.gd
Normal file
7
Sprout/addons/beehave/nodes/leaves/action.gd
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
## Actions are leaf nodes that define a task to be performed by an actor.
|
||||
## Their execution can be long running, potentially being called across multiple
|
||||
## frame executions. In this case, the node should return `RUNNING` until the
|
||||
## action is completed.
|
||||
@tool
|
||||
class_name ActionLeaf extends Leaf
|
||||
@icon("../../icons/action.svg")
|
||||
5
Sprout/addons/beehave/nodes/leaves/condition.gd
Normal file
5
Sprout/addons/beehave/nodes/leaves/condition.gd
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
## Conditions are leaf nodes that either return SUCCESS or FAILURE depending on
|
||||
## a single simple condition. They should never return `RUNNING`.
|
||||
@tool
|
||||
class_name ConditionLeaf extends Leaf
|
||||
@icon("../../icons/condition.svg")
|
||||
15
Sprout/addons/beehave/nodes/leaves/leaf.gd
Normal file
15
Sprout/addons/beehave/nodes/leaves/leaf.gd
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
## Base class for all leaf nodes of the tree.
|
||||
@tool
|
||||
class_name Leaf extends BeehaveNode
|
||||
@icon("../../icons/category_leaf.svg")
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings: PackedStringArray = []
|
||||
|
||||
var children: Array[Node] = get_children()
|
||||
|
||||
if children.any(func(x): return x is BeehaveNode):
|
||||
warnings.append("Leaf nodes should not have any child nodes. They won't be ticked.")
|
||||
|
||||
return warnings
|
||||
Loading…
Add table
Add a link
Reference in a new issue