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

107 lines
2.9 KiB
GDScript

tool
class_name CompoundState, "compound_state.svg"
extends State
## initial state to activate when state is activated
export var initial_state: NodePath setget _set_initial_state
var _active_state: State = null
onready var _initial_state: State = get_node_or_null(initial_state)
func _set_initial_state(value: NodePath) -> void:
initial_state = value
update_configuration_warning()
func _state_init() -> void:
._state_init()
for child in get_children():
if child is State:
child._state_init()
func _state_enter() -> void:
._state_enter()
# activate initial state
if _initial_state != null:
_active_state = _initial_state
_active_state._state_enter()
else:
push_error("no initial state set for state %s" % name)
func _state_exit() -> void:
# deactivate current state
if _active_state != null:
_active_state._state_exit()
_active_state = null
._state_exit()
func _state_event(event: String) -> bool:
if not active:
return false
# forward event to active state
if is_instance_valid(_active_state):
if _active_state._state_event(event):
emit_signal("event_received", event)
return true
# if event not handled by active state, handle here
return ._state_event(event)
func _handle_transition(transition: Transition, source: State) -> void:
var target: State = transition.resolve_target()
if not target is State:
push_error("the target state: %s of transition from state: %s is not a state" % [str(transition.to), source.name])
return
if target.active:
return
# if direct child, just switch active state
if target in get_children():
# deactivate current state
if is_instance_valid(_active_state):
_active_state._state_exit()
# activate target state
_active_state = target
_active_state._state_enter()
return
# if ancestor, activate next state down and let it handle transition further
if self.is_a_parent_of(target):
# find which child is also ancestor
for child in get_children():
if child.is_a_parent_of(target):
# change state if necessary
if _active_state != child:
if is_instance_valid(_active_state):
_active_state._state_exit()
_active_state = child
_active_state._state_enter()
child._handle_transition(transition, source)
return
return
# target is a cousin, defer to mommy
get_parent()._handle_transition(transition, source)
func _get_configuration_warning() -> String:
var warning := ._get_configuration_warning()
if not warning.empty():
return warning
if get_child_count() == 0:
return "compound states must have at least one child state"
var child_state = get_node_or_null(initial_state)
if not is_instance_valid(child_state):
return "initial state not found, is the path correct?"
if child_state.get_parent() != self:
return "initial state must be a direct child of this compound state"
if not child_state is State:
return "initial state must be a State"
return ""