132 lines
3.5 KiB
GDScript
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 ""
|