88 lines
2.7 KiB
GDScript
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
|