summaryrefslogtreecommitdiff
path: root/addons/escoria-core/patterns/state_machine
diff options
context:
space:
mode:
Diffstat (limited to 'addons/escoria-core/patterns/state_machine')
-rw-r--r--addons/escoria-core/patterns/state_machine/state.gd81
-rw-r--r--addons/escoria-core/patterns/state_machine/state.gd.uid1
-rw-r--r--addons/escoria-core/patterns/state_machine/state_machine.gd147
-rw-r--r--addons/escoria-core/patterns/state_machine/state_machine.gd.uid1
4 files changed, 230 insertions, 0 deletions
diff --git a/addons/escoria-core/patterns/state_machine/state.gd b/addons/escoria-core/patterns/state_machine/state.gd
new file mode 100644
index 0000000..cfa9e0f
--- /dev/null
+++ b/addons/escoria-core/patterns/state_machine/state.gd
@@ -0,0 +1,81 @@
+extends Node
+class_name State
+## Base interface for all states.
+##
+## This class doesn't do anything in itself but forces us to pass the right
+## arguments to the methods below and makes sure every State object had all of
+## these methods.
+
+## Signal sent when the state just changed. Parameter is the new state value.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |next_state_name|`Variant`|Name of the state that should become active after this state finishes.|yes|[br]
+## [br]
+signal finished(next_state_name)
+
+
+## Initialize the state. E.g. change the animation[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func enter():
+ return
+
+
+## Clean up the state. Reinitialize values like a timer.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func exit():
+ return
+
+
+## Manage an input event while this state is active.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |_event|`InputEvent`|InputEvent to process|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func handle_input(_event: InputEvent):
+ return
+
+
+## Perform an update while this state is active.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |_delta|`float`|float value obtained from a _process() call|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func update(_delta: float):
+ return
+
+
+# Callback called when an animation is finished.
+# [br]
+# #### Parameters[br]
+# - _anim_name: the animation name that just finished.
+func _on_animation_finished(_anim_name: String):
+ return
diff --git a/addons/escoria-core/patterns/state_machine/state.gd.uid b/addons/escoria-core/patterns/state_machine/state.gd.uid
new file mode 100644
index 0000000..6ac0bde
--- /dev/null
+++ b/addons/escoria-core/patterns/state_machine/state.gd.uid
@@ -0,0 +1 @@
+uid://c2huw3nfouhce
diff --git a/addons/escoria-core/patterns/state_machine/state_machine.gd b/addons/escoria-core/patterns/state_machine/state_machine.gd
new file mode 100644
index 0000000..2fa5d96
--- /dev/null
+++ b/addons/escoria-core/patterns/state_machine/state_machine.gd
@@ -0,0 +1,147 @@
+extends Node
+class_name StateMachine
+## Base interface for a generic state machine
+## It handles initializing, setting the machine active or not
+## delegating _physics_process, _input calls to the State nodes,
+## and changing the current/active state.
+
+## Signal emitted when the state has changed. Parameter is the new current state.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |current_state|`Variant`|Instance of the state that became active.|yes|[br]
+## [br]
+signal state_changed(current_state)
+
+## Starting state. This node has to be set *before* the initialize(START_STATE)
+## command is called.
+var START_STATE: Node
+
+## List of states
+var states_map = {}
+
+## Stack of states
+var states_stack = [] # can also be used as a pushdown automaton
+
+## Value of the current state
+var current_state = null
+
+## Name of the current state
+var current_state_name = ""
+
+## Whether the state machine is currently enabled or not.
+var _active = false:
+ set = set_active
+
+
+## Initialize the state machine with the start_state parameter.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |start_state|`State`|State value to use as starting state for the state machine.|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func initialize(start_state: State):
+ if START_STATE == null:
+ escoria.logger.error(
+ self,
+ "Starting state is required to be initialized with a defined state,
+ but it is null. Escoria cannot determine which of the defined states
+ (in states_map dictionary) is supposed to be the starting one.
+ Please assign a state to START_STATE in your implementation of the StateMachine class.")
+ for child in get_children():
+ child.connect("finished", Callable(self, "_change_state"))
+
+ set_active(true)
+ states_stack.push_front(start_state)
+ current_state = states_stack[0]
+ current_state.enter()
+
+
+## Enable or disable the state machine.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |value|`bool`|if true, enables the state machine. If false, disables it.|yes|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func set_active(value: bool):
+ _active = value
+ set_physics_process(value)
+ set_process_input(value)
+ if not _active:
+ states_stack = []
+ current_state = null
+
+
+# Manage an input event by the state machine's current state.
+#
+# #### Parameters
+#
+# - event: InputEvent to manage.
+func _input(event: InputEvent):
+ current_state.handle_input(event)
+
+
+# Lets the state machine's current state perform an update during
+# _physics_process() phase.
+#
+# #### Parameters
+#
+# - delta: float value corresponding to the elapsed time since last frame update.
+func _physics_process(delta: float):
+ current_state.update(delta)
+
+
+# Lets the state machine's current state perform an action on animation_finished
+# signal.
+#
+# #### Parameters
+#
+# - anim_name: name of the animation that finished.
+func _on_animation_finished(anim_name: String):
+ if not _active:
+ return
+ current_state._on_animation_finished(anim_name)
+
+
+# Change the current state of the state machine using its name. The value of
+# the state to be set is obtained in states_map dictionary.
+#
+# #### Parameters
+#
+# - state_name: name of the state to set.
+func _change_state(state_name: String):
+ if not _active:
+ return
+
+ escoria.logger.trace(
+ self,
+ "Dialog State Machine: Changing state from '%s' to '%s'." % [current_state_name, state_name]
+ )
+
+ current_state.exit()
+
+ if state_name == "previous":
+ states_stack.pop_front()
+ else:
+ states_stack[0] = states_map[state_name]
+
+ current_state = states_stack[0]
+
+ state_changed.emit(current_state)
+
+ #if state_name != "previous":
+ current_state.enter()
+
+ current_state_name = state_name
diff --git a/addons/escoria-core/patterns/state_machine/state_machine.gd.uid b/addons/escoria-core/patterns/state_machine/state_machine.gd.uid
new file mode 100644
index 0000000..5b8e593
--- /dev/null
+++ b/addons/escoria-core/patterns/state_machine/state_machine.gd.uid
@@ -0,0 +1 @@
+uid://bti0tjqa05fo8