1 extends DialogicSubsystem
2 ## Subsystem that handles input, Auto-Advance, and skipping.
4 ## This subsystem can be accessed via GDScript: `Dialogic.Inputs`.
7 signal dialogic_action_priority
10 ## Whenever the Auto-Skip timer finishes, this signal is emitted.
11 ## Configure Auto-Skip settings via [member auto_skip].
12 signal autoskip_timer_finished
15 const _SETTING_INPUT_ACTION := "dialogic/text/input_action"
16 const _SETTING_INPUT_ACTION_DEFAULT := "dialogic_default_action"
18 var input_block_timer := Timer.new()
19 var _auto_skip_timer_left: float = 0.0
20 var action_was_consumed := false
21 var input_was_mouse_input := false
23 var auto_skip: DialogicAutoSkip = null
24 var auto_advance: DialogicAutoAdvance = null
25 var manual_advance: DialogicManualAdvance = null
28 #region SUBSYSTEM METHODS
29 ################################################################################
31 func clear_game_state(_clear_flag := DialogicGameHandler.ClearFlags.FULL_CLEAR) -> void:
32 if not is_node_ready():
35 manual_advance.disabled_until_next_event = false
36 manual_advance.system_enabled = true
40 auto_advance.autoadvance_timer.paused = true
41 input_block_timer.paused = true
45 func resume() -> void:
46 auto_advance.autoadvance_timer.paused = false
47 input_block_timer.paused = false
48 var is_autoskip_timer_done := _auto_skip_timer_left > 0.0
49 set_process(!is_autoskip_timer_done)
52 func post_install() -> void:
53 dialogic.Settings.connect_to_change('autoadvance_delay_modifier', auto_advance._update_autoadvance_delay_modifier)
54 auto_skip.toggled.connect(_on_autoskip_toggled)
56 add_child(input_block_timer)
57 input_block_timer.one_shot = true
64 ################################################################################
66 func handle_input() -> void:
67 if dialogic.paused or is_input_blocked():
70 if not action_was_consumed:
71 # We want to stop auto-advancing that cancels on user inputs.
72 if (auto_advance.is_enabled()
73 and auto_advance.enabled_until_user_input):
74 auto_advance.enabled_until_user_input = false
75 action_was_consumed = true
77 # We want to stop auto-skipping if it's enabled, we are listening
78 # to user inputs, and it's not instant skipping.
79 if (auto_skip.disable_on_user_input
80 and auto_skip.enabled):
81 auto_skip.enabled = false
82 action_was_consumed = true
85 dialogic_action_priority.emit()
87 if action_was_consumed:
88 action_was_consumed = false
91 dialogic_action.emit()
92 input_was_mouse_input = false
95 ## Unhandled Input is used for all NON-Mouse based inputs.
96 func _unhandled_input(event:InputEvent) -> void:
97 if is_input_pressed(event, true):
98 if event is InputEventMouse or event is InputEventScreenTouch:
100 input_was_mouse_input = false
104 ## Input is used for all mouse based inputs.
105 ## If any DialogicInputNode is present this won't do anything (because that node handles MouseInput then).
106 func _input(event:InputEvent) -> void:
107 if is_input_pressed(event):
108 if not event is InputEventMouse:
110 if get_tree().get_nodes_in_group('dialogic_input').any(func(node):return node.is_visible_in_tree()):
112 input_was_mouse_input = true
116 func is_input_pressed(event: InputEvent, exact := false) -> bool:
117 var action: String = ProjectSettings.get_setting(_SETTING_INPUT_ACTION, _SETTING_INPUT_ACTION_DEFAULT)
118 return (event is InputEventAction and event.action == action) or Input.is_action_just_pressed(action, exact)
121 ## This is called from the gui_input of the InputCatcher and DialogText nodes
122 func handle_node_gui_input(event:InputEvent) -> void:
123 if Input.is_action_just_pressed(ProjectSettings.get_setting(_SETTING_INPUT_ACTION, _SETTING_INPUT_ACTION_DEFAULT)):
124 if event is InputEventMouseButton and event.pressed:
125 input_was_mouse_input = true
129 func is_input_blocked() -> bool:
130 return input_block_timer.time_left > 0.0
133 func block_input(time:=0.1) -> void:
135 input_block_timer.wait_time = max(time, input_block_timer.time_left)
136 input_block_timer.start()
139 func _ready() -> void:
140 auto_skip = DialogicAutoSkip.new()
141 auto_advance = DialogicAutoAdvance.new()
142 manual_advance = DialogicManualAdvance.new()
144 # We use the process method to count down the auto-start_autoskip_timer timer.
148 func stop_timers() -> void:
149 auto_advance.autoadvance_timer.stop()
150 input_block_timer.stop()
151 _auto_skip_timer_left = 0.0
157 ################################################################################
159 ## This method will advance the timeline based on Auto-Skip settings.
160 ## The state, whether Auto-Skip is enabled, is ignored.
161 func start_autoskip_timer() -> void:
162 _auto_skip_timer_left = auto_skip.time_per_event
164 await autoskip_timer_finished
167 ## If Auto-Skip disables, we want to stop the timer.
168 func _on_autoskip_toggled(enabled: bool) -> void:
170 _auto_skip_timer_left = 0.0
173 ## Handles fine-grained Auto-Skip logic.
174 ## The [method _process] method allows for a more precise timer than the
176 func _process(delta: float) -> void:
177 if _auto_skip_timer_left > 0:
178 _auto_skip_timer_left -= delta
180 if _auto_skip_timer_left <= 0:
181 autoskip_timer_finished.emit()
184 autoskip_timer_finished.emit()
190 ################################################################################
193 func effect_input(_text_node:Control, skipped:bool, _argument:String) -> void:
196 dialogic.Text.show_next_indicators()
197 await dialogic.Inputs.dialogic_action_priority
198 dialogic.Text.hide_next_indicators()
199 dialogic.Inputs.action_was_consumed = true
202 func effect_noskip(text_node:Control, skipped:bool, argument:String) -> void:
203 dialogic.Text.set_text_reveal_skippable(false, true)
204 manual_advance.disabled_until_next_event = true
205 effect_autoadvance(text_node, skipped, argument)
208 func effect_autoadvance(_text_node: Control, _skipped:bool, argument:String) -> void:
209 if argument.ends_with('?'):
210 argument = argument.trim_suffix('?')
212 auto_advance.enabled_until_next_event = true
214 if argument.is_valid_float():
215 auto_advance.override_delay_for_current_event = float(argument)