1extends Node
2# Base interface for a generic state machine.
3# It handles initializing, setting the machine active or not
4# delegating _physics_process, _input calls to the State nodes,
5# and changing the current/active state.
6# See the PlayerV2 scene for an example on how to use it.
7
8signal state_changed(current_state)
9
10# You should set a starting node from the inspector or on the node that inherits
11# from this state machine interface. If you don't, the game will default to
12# the first state in the state machine's children.
13export(NodePath) var start_state
14var states_map = {}
15
16var states_stack = []
17var current_state = null
18var _active = false setget set_active
19
20func _ready():
21	if not start_state:
22		start_state = get_child(0).get_path()
23	for child in get_children():
24		var err = child.connect("finished", self, "_change_state")
25		if err:
26			printerr(err)
27	initialize(start_state)
28
29
30func initialize(initial_state):
31	set_active(true)
32	states_stack.push_front(get_node(initial_state))
33	current_state = states_stack[0]
34	current_state.enter()
35
36
37func set_active(value):
38	_active = value
39	set_physics_process(value)
40	set_process_input(value)
41	if not _active:
42		states_stack = []
43		current_state = null
44
45
46func _unhandled_input(event):
47	current_state.handle_input(event)
48
49
50func _physics_process(delta):
51	current_state.update(delta)
52
53
54func _on_animation_finished(anim_name):
55	if not _active:
56		return
57	current_state._on_animation_finished(anim_name)
58
59
60func _change_state(state_name):
61	if not _active:
62		return
63	current_state.exit()
64
65	if state_name == "previous":
66		states_stack.pop_front()
67	else:
68		states_stack[0] = states_map[state_name]
69
70	current_state = states_stack[0]
71	emit_signal("state_changed", current_state)
72
73	if state_name != "previous":
74		current_state.enter()
75