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 ""