Skip to content

Commit 783701b

Browse files
Fix: dynamic on-screen control hints (#1177)
Previously, on-screen control hints always displayed keyboard icons, even when the player was using a gamepad. This confused testing, since players assumed only the keyboard was supported. Gamepad input was already functional, but hints did not update dynamically. This change integrates the godot_input_helper addon and refactors input handling to centralize device detection. On-screen hint scenes now listen to InputHelper.device_changed and update automatically to show the correct icons for the last active device (keyboard, PlayStation, Xbox, Switch, etc.). The mapping API is used so that each action consistently displays the proper button symbol. As part of this work: - shaker.gd was updated to remove redundant _last_controller_id logic. - input_key.gd was simplified to handle only gamepad input. - Two new scripts were added: - keyboard_input.gd for handling keyboard hints. - interact_input.gd for managing the interaction button across both keyboard and gamepad. - Hint scene scripts were extended to react to device changes and swap textures dynamically. - New assets for the main device families were added and wired into the mapping system. Success criteria - Control hints reflect the last active input device (keyboard or gamepad). - Switching between devices updates hints immediately. - Input handling is centralized through InputHelper, with no duplication. - No regressions in normal gameplay or input handling. Fixes #403 --------- Co-authored-by: AlealarcViva <U22200142@utp.edu.pe>
1 parent 9c6f6c5 commit 783701b

253 files changed

Lines changed: 5968 additions & 55 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,6 @@ repos:
5555
exclude: |
5656
(?x)^(
5757
addons/dialogue_manager/(.*)|
58+
addons/input_helper/(.*)|
5859
addons/git_describe/(.*)
5960
)$

REUSE.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ path = "addons/git_describe/**"
4343
SPDX-FileCopyrightText = "Joseph Michael Ware (zibetnu)"
4444
SPDX-License-Identifier = "MIT"
4545

46+
[[annotations]]
47+
path = "addons/input_helper/**"
48+
SPDX-FileCopyrightText = "2022-present Nathan Hoad"
49+
SPDX-License-Identifier = "MIT"
50+
4651
[[annotations]]
4752
path = "assets/third_party/xylophone-sampler-pack/*.ogg"
4853
SPDX-FileCopyrightText = "2013 juancamiloorjuela"
@@ -90,9 +95,7 @@ SPDX-License-Identifier = "CC-BY-SA-4.0"
9095

9196
[[annotations]]
9297
path = [
93-
"assets/third_party/inputs/keyboard-and-mouse/Blanks/*.png",
94-
"assets/third_party/inputs/keyboard-and-mouse/Dark/*.png",
95-
"assets/third_party/inputs/keyboard-and-mouse/Light/*.png",
98+
"assets/third_party/inputs/**/*.png",
9699
]
97100
SPDX-FileCopyrightText = "Nicolae (XELU) Berbece"
98101
SPDX-License-Identifier = "CC0-1.0"

addons/input_helper/InputHelper.cs

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
using Godot;
2+
using Godot.Collections;
3+
4+
namespace NathanHoad
5+
{
6+
public static class InputHelper
7+
{
8+
public delegate void DeviceChangedEventHandler(string device, int deviceIndex);
9+
public delegate void KeyboardInputChangedEventHandler(string action, InputEvent input);
10+
public delegate void JoypadInputChangedEventHandler(string action, InputEvent input);
11+
public delegate void JoypadChangedEventHandler(int deviceIndex, bool isConnected);
12+
13+
public static DeviceChangedEventHandler? DeviceChanged;
14+
public static KeyboardInputChangedEventHandler? KeyboardInputChanged;
15+
public static JoypadInputChangedEventHandler? JoypadInputChanged;
16+
public static JoypadChangedEventHandler? JoypadChanged;
17+
18+
19+
public const string DEVICE_KEYBOARD = "keyboard";
20+
public const string DEVICE_XBOX_CONTROLLER = "xbox";
21+
public const string DEVICE_SWITCH_CONTROLLER = "switch";
22+
public const string DEVICE_PLAYSTATION_CONTROLLER = "playstation";
23+
public const string DEVICE_STEAMDECK_CONTROLLER = "steamdeck";
24+
public const string DEVICE_GENERIC = "generic";
25+
26+
public const string SUB_DEVICE_XBOX_ONE_CONTROLLER = "xbox_one";
27+
public const string SUB_DEVICE_XBOX_SERIES_CONTROLLER = "xbox_series";
28+
public const string SUB_DEVICE_PLAYSTATION3_CONTROLLER = "playstation3";
29+
public const string SUB_DEVICE_PLAYSTATION4_CONTROLLER = "playstation4";
30+
public const string SUB_DEVICE_PLAYSTATION5_CONTROLLER = "playstation5";
31+
public const string SUB_DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER = "switch_left_joycon";
32+
public const string SUB_DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER = "switch_right_joycon";
33+
34+
35+
private static Node instance;
36+
public static Node Instance
37+
{
38+
get
39+
{
40+
if (instance == null)
41+
{
42+
instance = (Node)Engine.GetSingleton("InputHelper");
43+
instance.Connect("device_changed", Callable.From((string device, int deviceIndex) => DeviceChanged?.Invoke(device, deviceIndex)));
44+
instance.Connect("keyboard_input_changed", Callable.From((string action, InputEvent input) => KeyboardInputChanged?.Invoke(action, input)));
45+
instance.Connect("joypad_input_changed", Callable.From((string action, InputEvent input) => JoypadInputChanged?.Invoke(action, input)));
46+
instance.Connect("joypad_changed", Callable.From((int deviceIndex, bool isConnected) => JoypadChanged?.Invoke(deviceIndex, isConnected)));
47+
}
48+
return instance;
49+
}
50+
}
51+
52+
53+
public static string Device
54+
{
55+
get => (string)Instance.Get("device");
56+
}
57+
58+
59+
public static int DeviceIndex
60+
{
61+
get => (int)Instance.Get("device_index");
62+
}
63+
64+
65+
public static string LastKnownJoypadDevice
66+
{
67+
get => (string)Instance.Get("last_known_joypad_device");
68+
}
69+
70+
71+
public static string LastKnownJoypadIndex
72+
{
73+
get => (string)Instance.Get("last_known_joypad_index");
74+
}
75+
76+
77+
public static float Deadzone
78+
{
79+
get => (float)Instance.Get("deadzone");
80+
set => Instance.Set("deadzone", value);
81+
}
82+
83+
84+
public static int MouseMotionThreshold
85+
{
86+
get => (int)Instance.Get("mouse_motion_threshold");
87+
set => Instance.Set("mouse_motion_threshold", value);
88+
}
89+
90+
91+
public static string GetSimplifiedDeviceName(string rawName)
92+
{
93+
return (string)Instance.Call("get_simplified_device_name", rawName);
94+
}
95+
96+
97+
public static string GetDeviceFromEvent(InputEvent @event)
98+
{
99+
return (string)Instance.Call("get_device_from_event", @event);
100+
}
101+
102+
103+
public static int GetDeviceIndexFromEvent(InputEvent @event)
104+
{
105+
return (int)Instance.Call("get_device_index_from_event", @event);
106+
}
107+
108+
109+
public static bool HasJoypad()
110+
{
111+
return (bool)Instance.Call("has_joypad");
112+
}
113+
114+
115+
public static string GuessDeviceName()
116+
{
117+
return (string)Instance.Call("guess_device_name");
118+
}
119+
120+
121+
public static void ResetAllActions()
122+
{
123+
Instance.Call("reset_all_actions");
124+
}
125+
126+
127+
public static void SetKeyboardOrJoypadInputForAction(string action, InputEvent input, bool swapIfTaken = true)
128+
{
129+
Instance.Call("set_keyboard_or_joypad_input_for_action", action, input, swapIfTaken);
130+
}
131+
132+
133+
public static InputEvent GetKeyboardOrJoypadInputForAction(string action, InputEvent input, bool swapIfTaken = true)
134+
{
135+
return (InputEvent)Instance.Call("get_keyboard_or_joypad_input_for_action", action, input, swapIfTaken);
136+
}
137+
138+
139+
public static Array<InputEvent> GetKeyboardOrJoypadInputsForAction(string action)
140+
{
141+
return (Array<InputEvent>)Instance.Call("get_keyboard_or_joypad_inputs_for_action", action);
142+
}
143+
144+
145+
public static string GetLabelForInput(InputEvent input)
146+
{
147+
return (string)Instance.Call("get_label_for_input", input);
148+
}
149+
150+
151+
public static string SerializeInputsForAction(string action)
152+
{
153+
return (string)Instance.Call("serialize_inputs_for_action", action);
154+
}
155+
156+
157+
public static string SerializeInputsForActions(Array<string> actions = null)
158+
{
159+
if (actions == null)
160+
{
161+
actions = new Array<string>();
162+
}
163+
return (string)Instance.Call("serialize_inputs_for_actions", actions);
164+
}
165+
166+
167+
public static void DeserializeInputsForAction(string action, string serializedInputs)
168+
{
169+
Instance.Call("desserialize_inputs_for_action", action, serializedInputs);
170+
}
171+
172+
public static void DeserializeInputsForActions(string serializedInputs)
173+
{
174+
Instance.Call("deserialize_inputs_for_actions", serializedInputs);
175+
}
176+
177+
178+
#region Keyboard/Mouse
179+
180+
public static Array<InputEvent> GetKeyboardInputsForAction(string action)
181+
{
182+
return (Array<InputEvent>)Instance.Call("get_keyboard_inputs_for_action", action);
183+
}
184+
185+
186+
public static InputEvent GetKeyboardInputForAction(string action)
187+
{
188+
return (InputEvent)Instance.Call("get_keyboard_input_for_action", action);
189+
}
190+
191+
192+
public static void SetKeyboardInputForAction(string action, InputEvent input, bool swapIfTaken = true)
193+
{
194+
Instance.Call("set_keyboard_input_for_action", action, input, swapIfTaken);
195+
}
196+
197+
198+
public static void ReplaceKeyboardInputForAction(string action, InputEvent currentInput, InputEvent input, bool swapIfTaken = true)
199+
{
200+
Instance.Call("replace_keyboard_input_for_action", action, currentInput, input, swapIfTaken);
201+
}
202+
203+
204+
public static void ReplaceKeyboardInputAtIndex(string action, int index, InputEvent input, bool swapIfTaken = true)
205+
{
206+
Instance.Call("replace_keyboard_input_at_index", action, index, input, swapIfTaken);
207+
}
208+
209+
#endregion
210+
211+
212+
#region Joypad
213+
214+
public static Array<InputEvent> GetJoypadInputsForAction(string action)
215+
{
216+
return (Array<InputEvent>)Instance.Call("get_joypad_inputs_for_action", action);
217+
}
218+
219+
220+
public static InputEvent GetJoypadInputForAction(string action)
221+
{
222+
return (InputEvent)Instance.Call("get_joypad_input_for_action", action);
223+
}
224+
225+
226+
public static void SetJoypadInputForAction(string action, InputEvent input, bool swapIfTaken = true)
227+
{
228+
Instance.Call("set_joypad_input_for_action", action, input, swapIfTaken);
229+
}
230+
231+
232+
public static void ReplaceJoypadInputForAction(string action, InputEvent currentInput, InputEvent input, bool swapIfTaken = true)
233+
{
234+
Instance.Call("replace_joypad_input_for_action", action, currentInput, input, swapIfTaken);
235+
}
236+
237+
238+
public static void ReplaceJoypadInputAtIndex(string action, int index, InputEvent input, bool swapIfTaken = true)
239+
{
240+
Instance.Call("replace_joypad_input_at_index", action, index, input, swapIfTaken);
241+
}
242+
243+
244+
public static void RumbleSmall(int targetDevice = 0)
245+
{
246+
Instance.Call("rumble_small", targetDevice);
247+
}
248+
249+
250+
public static void RumbleMedium(int targetDevice = 0)
251+
{
252+
Instance.Call("rumble_medium", targetDevice);
253+
}
254+
255+
256+
public static void RumbleLarge(int targetDevice = 0)
257+
{
258+
Instance.Call("rumble_large", targetDevice);
259+
}
260+
261+
262+
public static void StartRumbleSmall(int targetDevice = 0)
263+
{
264+
Instance.Call("start_rumble_small", targetDevice);
265+
}
266+
267+
268+
public static void StartRumbleMedium(int targetDevice = 0)
269+
{
270+
Instance.Call("start_rumble_medium", targetDevice);
271+
}
272+
273+
274+
public static void StartRumbleLarge(int targetDevice = 0)
275+
{
276+
Instance.Call("start_rumble_large", targetDevice);
277+
}
278+
279+
280+
public static void StopRumble(int targetDevice = 0)
281+
{
282+
Instance.Call("stop_rumble", targetDevice);
283+
}
284+
285+
#endregion
286+
}
287+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://cfs4dgwrfvb11

addons/input_helper/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022-present Nathan Hoad
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)