Full Xbox/PlayStation controller support: glyphs, remapping, vibration toggle
Extends the earlier Xbox-only ability glyphs into full brand-aware controller support, following standard practice (position-based button mapping, safe defaults for unrecognised pads, no per-device special-casing): - Controls detects PlayStation pads (DualShock/DualSense) from Input.get_joy_name() via a name-substring heuristic, checking for "xbox" first so a Bluetooth-connected Xbox pad (reports as "Xbox Wireless Controller") can't be misread as PlayStation through the generic "wireless controller" hint. Unrecognised/generic pads read as Xbox-style, matching the browser's Standard Gamepad Mapping. - Ability-bar keycaps and the options menu now show ✕/○/□/△ for a detected PlayStation pad instead of A/B/X/Y — no index remapping needed, since PlayStation's face buttons sit in the exact same four positions as Xbox's. - The options menu's Controls section gains a gamepad rebind button next to each ability's keyboard one (movement stays on the D-pad/ stick, not individually remapped, matching convention). Clicking it captures the next joypad button press, mirroring the existing keyboard-rebind flow exactly (Esc cancels either). Gamepad rebinds persist to controls.cfg alongside keyboard ones and survive a reload. - Added a player-facing "Controller Vibration" toggle (Settings... Gameplay section) — Controls.vibrate() now checks it alongside the existing DP debug flag, so either can silence rumble. Verified headlessly end-to-end: default bindings show correct brand-agnostic glyphs (kick=X, dash=A, ...), rebinding kick to a different button through the real options-menu flow changes only that action's glyph, leaves its keyboard binding and other abilities' gamepad bindings untouched, and survives a reload from disk. Sanity- checked the PlayStation name heuristic against realistic controller name strings from Xbox, PlayStation (Windows/macOS/Bluetooth variants), and other brands. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MXBNaEBmrYDSMcv4NgUWtV
This commit is contained in:
+40
-3
@@ -80,6 +80,8 @@ const ACTION_LABELS: Dictionary = {
|
||||
|
||||
var _listening_action: StringName = &""
|
||||
var _listening_button: Button = null
|
||||
var _listening_joy_action: StringName = &""
|
||||
var _listening_joy_button: Button = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -207,6 +209,7 @@ func _build_options_toggles() -> void:
|
||||
_add_section_label("── Gameplay ──", idx); idx += 1
|
||||
_add_toggle_row("Screen Shake", Settings.screen_shake, Settings.set_screen_shake, idx); idx += 1
|
||||
_add_toggle_row("Blood & Gore", Settings.gore, Settings.set_gore, idx); idx += 1
|
||||
_add_toggle_row("Controller Vibration", Settings.vibration, Settings.set_vibration, idx); idx += 1
|
||||
_add_section_label("── Controls ──", idx)
|
||||
|
||||
|
||||
@@ -316,6 +319,16 @@ func _build_rebind_rows() -> void:
|
||||
key_button.pressed.connect(_on_rebind_pressed.bind(action, key_button))
|
||||
row.add_child(key_button)
|
||||
|
||||
# Movement stays on the D-pad/stick (standard, not individually remapped) — only the
|
||||
# face-button abilities get a gamepad rebind button, alongside the keyboard one.
|
||||
if Controls.JOY_REBINDABLE_ACTIONS.has(String(action)):
|
||||
var joy_button := Button.new()
|
||||
joy_button.custom_minimum_size = Vector2(72, 0)
|
||||
joy_button.text = Controls.get_joy_button_label(action)
|
||||
_style_button(joy_button, false)
|
||||
joy_button.pressed.connect(_on_rebind_joy_pressed.bind(action, joy_button))
|
||||
row.add_child(joy_button)
|
||||
|
||||
rebind_list.add_child(row)
|
||||
|
||||
|
||||
@@ -369,17 +382,26 @@ func _on_rebind_pressed(action: StringName, button: Button) -> void:
|
||||
button.text = "Press a key…"
|
||||
|
||||
|
||||
func _on_rebind_joy_pressed(action: StringName, button: Button) -> void:
|
||||
_cancel_listening()
|
||||
_listening_joy_action = action
|
||||
_listening_joy_button = button
|
||||
button.text = "Press…"
|
||||
|
||||
|
||||
func _cancel_listening() -> void:
|
||||
if _listening_button != null:
|
||||
_listening_button.text = Controls.get_key_label(_listening_action)
|
||||
if _listening_joy_button != null:
|
||||
_listening_joy_button.text = Controls.get_joy_button_label(_listening_joy_action)
|
||||
_listening_action = &""
|
||||
_listening_button = null
|
||||
_listening_joy_action = &""
|
||||
_listening_joy_button = null
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if _listening_action == &"":
|
||||
return
|
||||
if event is InputEventKey and event.pressed and not event.echo:
|
||||
if _listening_action != &"" and event is InputEventKey and event.pressed and not event.echo:
|
||||
if event.physical_keycode == KEY_ESCAPE:
|
||||
_cancel_listening()
|
||||
else:
|
||||
@@ -390,3 +412,18 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
_listening_action = &""
|
||||
_listening_button = null
|
||||
get_viewport().set_input_as_handled()
|
||||
return
|
||||
|
||||
if _listening_joy_action != &"":
|
||||
if event is InputEventKey and event.pressed and not event.echo \
|
||||
and event.physical_keycode == KEY_ESCAPE:
|
||||
_cancel_listening()
|
||||
get_viewport().set_input_as_handled()
|
||||
elif event is InputEventJoypadButton and event.pressed:
|
||||
var new_joy_event := InputEventJoypadButton.new()
|
||||
new_joy_event.button_index = event.button_index
|
||||
Controls.rebind_joy(_listening_joy_action, new_joy_event)
|
||||
_listening_joy_button.text = Controls.get_joy_button_label(_listening_joy_action)
|
||||
_listening_joy_action = &""
|
||||
_listening_joy_button = null
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
Reference in New Issue
Block a user