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

132 lines
3.5 KiB
GDScript

tool
class_name State, "state.svg"
extends Node
## called when state is entered
signal state_entered()
## called when state is exited
signal state_exited()
## called when state recieves an event while active
signal event_received(event)
## called when the state is processing
signal state_processing(delta)
## called when the state is physics_processing
signal state_physics_processing(delta)
## processing mode
enum ProcessMode {IDLE, PHYSICS}
## whether to process transition delays during physics or idle frames
export (ProcessMode) var transition_process_mode: int = ProcessMode.PHYSICS
## events to consume so that parent state can not
export (Array, String) var consumed_events: Array = []
## whether the current state is active
var active: bool setget _set_active
func _set_active(value: bool):
active = value
set_process(value)
set_physics_process(value)
## all of the state's transitions
var _transitions: Array = []
## queued transition to take
var _queued_transition: Transition = null
## time until queued transition is taken
var _queued_transition_time: float = 0.0
## called when building the state chart
func _state_init() -> void:
_set_active(false)
# get references to transitions
_transitions.clear()
for child in get_children():
if child is Transition:
_transitions.append(child as Transition)
## called when state is entered
func _state_enter() -> void:
_set_active(true)
emit_signal("state_entered")
# check eventless transitions
for transition in _transitions:
if not transition.has_event() and transition.evaluate_guard():
# first match is taken
_queue_transition(transition)
## called when state is exited
func _state_exit() -> void:
_set_active(false)
emit_signal("state_exited")
## handles given event. returns true if it is consumed
func _state_event(event: String) -> bool:
if not active:
return false
# emit event received signal
emit_signal("event_received", event)
#check for transitions reacting to event
for transition in _transitions:
if transition.event == event and transition.evaluate_guard():
# first match is taken
_queue_transition(transition)
return true
if event in consumed_events:
return true
return false
func _process(delta: float) -> void:
if Engine.editor_hint:
return
# emit processing signal
emit_signal("state_processing", delta)
# process transitions if mode is IDLE
if transition_process_mode == ProcessMode.IDLE:
_process_transition(delta)
func _physics_process(delta: float) -> void:
if Engine.editor_hint:
return
# emit physics processing signal
emit_signal("state_physics_processing", delta)
# process transitions if mode is PHYSICS
if transition_process_mode == ProcessMode.PHYSICS:
_process_transition(delta)
## queues a transition to be taken
func _queue_transition(transition: Transition) -> void:
_queued_transition = transition
_queued_transition_time = transition.delay
## checks for and processes queued transition
func _process_transition(delta: float) -> void:
# check for queued transition
if _queued_transition != null:
_queued_transition_time -= delta
# if ready, handle transition and clear queue
if _queued_transition_time <= 0.0:
var transition = _queued_transition
_queued_transition = null
_queued_transition_time = 0.0
_handle_transition(transition, self)
## attempts to take transition
func _handle_transition(transition: Transition, source: State) -> void:
push_error("state %s cannot handle transitions" % name)
func _get_configuration_warning() -> String:
return ""