add beehave

This commit is contained in:
2025-01-28 13:53:59 +08:00
parent d6b69c14ac
commit 144a01556d
112 changed files with 6075 additions and 205 deletions
@@ -0,0 +1,49 @@
@tool
@icon("../../icons/cooldown.svg")
extends Decorator
class_name CooldownDecorator
## The Cooldown Decorator will return 'FAILURE' for a set amount of time
## after executing its child.
## The timer resets the next time its child is executed and it is not `RUNNING`
## The wait time in seconds
@export var wait_time := 0.0
@onready var cache_key = "cooldown_%s" % self.get_instance_id()
func tick(actor: Node, blackboard: Blackboard) -> int:
var c: BeehaveNode = get_child(0)
var remaining_time: float = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id()))
var response: int
if c != running_child:
c.before_run(actor, blackboard)
if remaining_time > 0:
response = FAILURE
remaining_time -= get_physics_process_delta_time()
blackboard.set_value(cache_key, remaining_time, str(actor.get_instance_id()))
if can_send_message(blackboard):
BeehaveDebuggerMessages.process_tick(self.get_instance_id(), response, blackboard.get_debug_data())
else:
response = 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()))
if response == RUNNING and c is ActionLeaf:
running_child = c
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
if response != RUNNING:
blackboard.set_value(cache_key, wait_time, str(actor.get_instance_id()))
return response
@@ -0,0 +1,33 @@
@tool
@icon("../../icons/category_decorator.svg")
class_name Decorator extends BeehaveNode
## Decorator nodes are used to transform the result received by its child.
## Must only have one child.
var running_child: BeehaveNode = null
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
@@ -0,0 +1,49 @@
@tool
@icon("../../icons/delayer.svg")
extends Decorator
class_name DelayDecorator
## The Delay Decorator will return 'RUNNING' for a set amount of time
## before executing its child.
## The timer resets when both it and its child are not `RUNNING`
## The wait time in seconds
@export var wait_time := 0.0
@onready var cache_key = "time_limiter_%s" % self.get_instance_id()
func tick(actor: Node, blackboard: Blackboard) -> int:
var c: BeehaveNode = get_child(0)
var total_time: float = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id()))
var response: int
if c != running_child:
c.before_run(actor, blackboard)
if total_time < wait_time:
response = RUNNING
total_time += get_physics_process_delta_time()
blackboard.set_value(cache_key, total_time, str(actor.get_instance_id()))
if can_send_message(blackboard):
BeehaveDebuggerMessages.process_tick(self.get_instance_id(), response, blackboard.get_debug_data())
else:
response = 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()))
if response == RUNNING and c is ActionLeaf:
running_child = c
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
if response != RUNNING:
blackboard.set_value(cache_key, 0.0, str(actor.get_instance_id()))
return response
+35
View File
@@ -0,0 +1,35 @@
@tool
@icon("../../icons/failer.svg")
class_name AlwaysFailDecorator extends Decorator
## A Failer node will always return a `FAILURE` status code.
func tick(actor: Node, blackboard: Blackboard) -> int:
var c: BeehaveNode = get_child(0)
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()))
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
@@ -0,0 +1,43 @@
@tool
@icon("../../icons/inverter.svg")
class_name InverterDecorator extends Decorator
## 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.
func tick(actor: Node, blackboard: Blackboard) -> int:
var c: BeehaveNode = get_child(0)
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:
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
@@ -0,0 +1,60 @@
@tool
@icon("../../icons/limiter.svg")
class_name LimiterDecorator extends Decorator
## The limiter will execute its `RUNNING` child `x` amount of times. When the number of
## maximum ticks is reached, it will return a `FAILURE` status code.
## The count resets the next time that a child is not `RUNNING`
@onready var cache_key = "limiter_%s" % self.get_instance_id()
@export var max_count: int = 0
func tick(actor: Node, blackboard: Blackboard) -> int:
if not get_child_count() == 1:
return FAILURE
var child: BeehaveNode = get_child(0)
var current_count: int = blackboard.get_value(cache_key, 0, str(actor.get_instance_id()))
if current_count < max_count:
blackboard.set_value(cache_key, current_count + 1, str(actor.get_instance_id()))
var response: int = child.tick(actor, blackboard)
if can_send_message(blackboard):
BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response, blackboard.get_debug_data())
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()))
if response != RUNNING:
child.after_run(actor, blackboard)
return response
else:
interrupt(actor, blackboard)
child.after_run(actor, blackboard)
return FAILURE
func before_run(actor: Node, blackboard: Blackboard) -> void:
blackboard.set_value(cache_key, 0, str(actor.get_instance_id()))
if get_child_count() > 0:
get_child(0).before_run(actor, blackboard)
func get_class_name() -> Array[StringName]:
var classes := super()
classes.push_back(&"LimiterDecorator")
return classes
func _get_configuration_warnings() -> PackedStringArray:
if not get_child_count() == 1:
return ["Requires exactly one child node"]
return []
@@ -0,0 +1,58 @@
## The repeater will execute its child until it returns `SUCCESS` a certain amount of times.
## When the number of maximum ticks is reached, it will return a `SUCCESS` status code.
## If the child returns `FAILURE`, the repeater will return `FAILURE` immediately.
@tool
@icon("../../icons/repeater.svg")
class_name RepeaterDecorator extends Decorator
@export var repetitions: int = 1
var current_count: int = 0
func before_run(actor: Node, blackboard: Blackboard):
current_count = 0
func tick(actor: Node, blackboard: Blackboard) -> int:
var child: BeehaveNode = get_child(0)
if current_count < repetitions:
if running_child == null:
child.before_run(actor, blackboard)
var response: int = child.tick(actor, blackboard)
if can_send_message(blackboard):
BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response, blackboard.get_debug_data())
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 RUNNING
current_count += 1
child.after_run(actor, blackboard)
if running_child != null:
running_child = null
if response == FAILURE:
return FAILURE
if current_count >= repetitions:
return SUCCESS
return RUNNING
else:
return SUCCESS
func get_class_name() -> Array[StringName]:
var classes := super()
classes.push_back(&"LimiterDecorator")
return classes
@@ -0,0 +1,35 @@
@tool
@icon("../../icons/succeeder.svg")
class_name AlwaysSucceedDecorator extends Decorator
## A succeeder node will always return a `SUCCESS` status code.
func tick(actor: Node, blackboard: Blackboard) -> int:
var c: BeehaveNode = get_child(0)
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()))
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
@@ -0,0 +1,60 @@
@tool
@icon("../../icons/limiter.svg")
class_name TimeLimiterDecorator extends Decorator
## The Time Limit Decorator will give its `RUNNING` child a set amount of time to finish
## before interrupting it and return a `FAILURE` status code.
## The timer resets the next time that a child is not `RUNNING`
@export var wait_time := 0.0
@onready var cache_key: String = "time_limiter_%s" % self.get_instance_id()
func tick(actor: Node, blackboard: Blackboard) -> int:
if not get_child_count() == 1:
return FAILURE
var child: BeehaveNode = self.get_child(0)
var time_left: float = blackboard.get_value(cache_key, 0.0, str(actor.get_instance_id()))
if time_left < wait_time:
time_left += get_physics_process_delta_time()
blackboard.set_value(cache_key, time_left, str(actor.get_instance_id()))
var response: int = child.tick(actor, blackboard)
if can_send_message(blackboard):
BeehaveDebuggerMessages.process_tick(child.get_instance_id(), response, blackboard.get_debug_data())
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()))
else:
child.after_run(actor, blackboard)
return response
else:
interrupt(actor, blackboard)
child.after_run(actor, blackboard)
return FAILURE
func before_run(actor: Node, blackboard: Blackboard) -> void:
blackboard.set_value(cache_key, 0.0, str(actor.get_instance_id()))
if get_child_count() > 0:
get_child(0).before_run(actor, blackboard)
func get_class_name() -> Array[StringName]:
var classes := super()
classes.push_back(&"TimeLimiterDecorator")
return classes
func _get_configuration_warnings() -> PackedStringArray:
if not get_child_count() == 1:
return ["Requires exactly one child node"]
return []
@@ -0,0 +1,33 @@
@tool
@icon("../../icons/until_fail.svg")
class_name UntilFailDecorator
extends Decorator
## The UntilFail Decorator will return `RUNNING` if its child returns
## `SUCCESS` or `RUNNING` or it will return `SUCCESS` if its child returns
## `FAILURE`
func tick(actor: Node, blackboard: Blackboard) -> int:
var c: BeehaveNode = get_child(0)
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()))
if response == RUNNING:
running_child = c
if c is ActionLeaf:
blackboard.set_value("running_action", c, str(actor.get_instance_id()))
return RUNNING
if response == SUCCESS:
return RUNNING
return SUCCESS