Add a behavior tree to the enemy
Add the Beehave addon. Make the enemy do the same thing as before but with a behavior tree.
This commit is contained in:
parent
09f6925a00
commit
1aed988149
92 changed files with 4025 additions and 25 deletions
41
addons/beehave/nodes/decorators/decorator.gd
Normal file
41
addons/beehave/nodes/decorators/decorator.gd
Normal file
|
@ -0,0 +1,41 @@
|
|||
## Decorator nodes are used to transform the result received by its child.
|
||||
## Must only have one child.
|
||||
@tool
|
||||
@icon("../../icons/category_decorator.svg")
|
||||
class_name Decorator extends BeehaveNode
|
||||
|
||||
|
||||
var running_child: BeehaveNode = null
|
||||
|
||||
|
||||
func _ready():
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if self.get_child_count() != 1:
|
||||
push_warning("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
|
||||
|
||||
|
||||
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(&"Decorator")
|
||||
return classes
|
34
addons/beehave/nodes/decorators/failer.gd
Normal file
34
addons/beehave/nodes/decorators/failer.gd
Normal file
|
@ -0,0 +1,34 @@
|
|||
## A Failer node will always return a `FAILURE` status code.
|
||||
@tool
|
||||
@icon("../../icons/failer.svg")
|
||||
class_name AlwaysFailDecorator extends Decorator
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var c = get_child(0)
|
||||
|
||||
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)
|
||||
|
||||
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()))
|
||||
|
||||
if response == RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
else:
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"AlwaysFailDecorator")
|
||||
return classes
|
42
addons/beehave/nodes/decorators/inverter.gd
Normal file
42
addons/beehave/nodes/decorators/inverter.gd
Normal file
|
@ -0,0 +1,42 @@
|
|||
## 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
|
||||
@icon("../../icons/inverter.svg")
|
||||
class_name InverterDecorator extends Decorator
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var c = get_child(0)
|
||||
|
||||
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)
|
||||
|
||||
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:
|
||||
c.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
FAILURE:
|
||||
c.after_run(actor, blackboard)
|
||||
return SUCCESS
|
||||
RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
_:
|
||||
push_error("This should be unreachable")
|
||||
return -1
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"InverterDecorator")
|
||||
return classes
|
41
addons/beehave/nodes/decorators/limiter.gd
Normal file
41
addons/beehave/nodes/decorators/limiter.gd
Normal file
|
@ -0,0 +1,41 @@
|
|||
## 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
|
||||
@icon("../../icons/limiter.svg")
|
||||
class_name LimiterDecorator extends Decorator
|
||||
|
||||
@onready var cache_key = 'limiter_%s' % self.get_instance_id()
|
||||
|
||||
@export var max_count : float = 0
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var child = self.get_child(0)
|
||||
var current_count = blackboard.get_value(cache_key, 0, str(actor.get_instance_id()))
|
||||
|
||||
if current_count == 0:
|
||||
child.before_run(actor, blackboard)
|
||||
|
||||
if current_count < max_count:
|
||||
blackboard.set_value(cache_key, current_count + 1, str(actor.get_instance_id()))
|
||||
var response = child.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response)
|
||||
|
||||
if child is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", child, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
if child is ActionLeaf and response == RUNNING:
|
||||
running_child = child
|
||||
blackboard.set_value("running_action", child, str(actor.get_instance_id()))
|
||||
|
||||
return response
|
||||
else:
|
||||
child.after_run(actor, blackboard)
|
||||
return FAILURE
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"LimiterDecorator")
|
||||
return classes
|
34
addons/beehave/nodes/decorators/succeeder.gd
Normal file
34
addons/beehave/nodes/decorators/succeeder.gd
Normal file
|
@ -0,0 +1,34 @@
|
|||
## A succeeder node will always return a `SUCCESS` status code.
|
||||
@tool
|
||||
@icon("../../icons/succeeder.svg")
|
||||
class_name AlwaysSucceedDecorator extends Decorator
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
var c = get_child(0)
|
||||
|
||||
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)
|
||||
|
||||
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()))
|
||||
|
||||
if response == RUNNING:
|
||||
running_child = c
|
||||
if c is ActionLeaf:
|
||||
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
|
||||
return RUNNING
|
||||
else:
|
||||
c.after_run(actor, blackboard)
|
||||
return SUCCESS
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"AlwaysSucceedDecorator")
|
||||
return classes
|
46
addons/beehave/nodes/decorators/time_limiter.gd
Normal file
46
addons/beehave/nodes/decorators/time_limiter.gd
Normal file
|
@ -0,0 +1,46 @@
|
|||
## The Time Limit Decorator will give its child a set amount of time to finish
|
||||
## before interrupting it and return a `FAILURE` status code. The timer is reset
|
||||
## every time before the node runs.
|
||||
@tool
|
||||
@icon("../../icons/limiter.svg")
|
||||
class_name TimeLimiterDecorator extends Decorator
|
||||
|
||||
@export var wait_time: = 0.0
|
||||
|
||||
var time_left: = 0.0
|
||||
|
||||
@onready var child: BeehaveNode = get_child(0)
|
||||
|
||||
|
||||
func tick(actor: Node, blackboard: Blackboard) -> int:
|
||||
if time_left < wait_time:
|
||||
time_left += get_physics_process_delta_time()
|
||||
var response = child.tick(actor, blackboard)
|
||||
if can_send_message(blackboard):
|
||||
BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response)
|
||||
|
||||
if child is ConditionLeaf:
|
||||
blackboard.set_value("last_condition", child, str(actor.get_instance_id()))
|
||||
blackboard.set_value("last_condition_status", response, str(actor.get_instance_id()))
|
||||
|
||||
if response == RUNNING:
|
||||
running_child = child
|
||||
if child is ActionLeaf:
|
||||
blackboard.set_value("running_action", child, str(actor.get_instance_id()))
|
||||
|
||||
return response
|
||||
else:
|
||||
child.after_run(actor, blackboard)
|
||||
interrupt(actor, blackboard)
|
||||
return FAILURE
|
||||
|
||||
|
||||
func before_run(actor: Node, blackboard: Blackboard) -> void:
|
||||
time_left = 0.0
|
||||
child.before_run(actor, blackboard)
|
||||
|
||||
|
||||
func get_class_name() -> Array[StringName]:
|
||||
var classes := super()
|
||||
classes.push_back(&"TimeLimiterDecorator")
|
||||
return classes
|
Loading…
Add table
Add a link
Reference in a new issue