hero-mark-2/addons/godot_state_charts/transition.gd

88 lines
2.7 KiB
GDScript

tool
class_name Transition, "transition.svg"
extends Node
## cyclic reference workarounds :/
var _State = load("res://addons/godot_state_charts/state.gd")
var _StateChart = load("res://addons/godot_state_charts/state_chart.gd")
## state to transition to
export var to: NodePath
## event to react to for transition
export var event: String
## delay before taking transition
export var delay: float = 0.0
## expression to determine whether to take transition
## if it returns true the transition will be taken
## expression properties added via the StateChart are available here
## if states are listed in `monitored_states`, they will be available as
## boolean properties named "[state]_active"
export (String, MULTILINE) var guard_expression: String
## states to check if active
## they are available in the guard expression as bools named "[state]_active"
export (Array, NodePath) var checked_states: Array = []
## returns true if the transition has an event to react to
func has_event() -> bool:
return event != null and not event.empty()
## returns the target state
func resolve_target():
if to == null or to.is_empty():
return null
var target = get_node_or_null(to)
if target is _State:
return target
return null
## returns true if the transition should be taken at this time
func evaluate_guard() -> bool:
# return true if there is no guard
if guard_expression == null or guard_expression.empty():
return true
# find root StateChart
var root = get_parent()
while is_instance_valid(root) and not root is _StateChart:
root = root.get_parent()
if not is_instance_valid(root):
push_error("could not find root StateChart, cannot evaluate expression")
return false
# combine monitored states with expression properties
var properties: Dictionary = root._guard_properties.duplicate()
for node in checked_states:
var state = get_node_or_null(node)
if is_instance_valid(state) and state is _State:
properties["in_" + state.name] = state.active
# construct expression and set up properties
var expression := Expression.new()
var input_names = properties.keys()
# attempt to parse expression
var parse_result = expression.parse(guard_expression, input_names)
if parse_result != OK:
push_error("error parsing expression: " + expression.get_error_text())
return false
# create array of input values
var input_values = []
for input_name in input_names:
input_values.append(properties[input_name])
# execute expression and validate result
var result = expression.execute(input_values)
if expression.has_execute_failed():
push_error("failed to execute expression: " + expression.get_error_text())
return false
if typeof(result) != TYPE_BOOL:
push_error("result is not a boolean value. returning false")
return false
return result