Make the Controls options menu device-aware
Showing both a keyboard key and a gamepad button on every ability row (and WASD movement rows that never had a gamepad equivalent anyway) cluttered the list once a controller is in play. The rebind list now shows only the relevant set for whichever device the player is actually holding: on a gamepad, movement rows disappear entirely (the D-pad/stick isn't individually remapped, so there's nothing to show) and each ability shows only its gamepad button; on keyboard, the reverse. Uses the same Controls.using_gamepad() last-input heuristic as the HUD's ability keycaps, not mere connection presence, so a controller left plugged in but unused doesn't hide keyboard binds from a mouse-and-keyboard player. The list (and its hint text) also swaps live if the player switches device while the panel is open, skipped while a rebind capture is in progress so it can't be torn out from under them. Verified headlessly: keyboard mode shows all 8 rows, gamepad mode shows only the 4 ability rows, and the hint text swaps between them — confirmed with a real screenshot of both states. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MXBNaEBmrYDSMcv4NgUWtV
This commit is contained in:
+46
-9
@@ -82,6 +82,9 @@ var _listening_action: StringName = &""
|
||||
var _listening_button: Button = null
|
||||
var _listening_joy_action: StringName = &""
|
||||
var _listening_joy_button: Button = null
|
||||
# Cached so the rebind list only rebuilds on an actual keyboard<->gamepad switch, not
|
||||
# every frame the Options panel is open.
|
||||
var _rebind_rows_show_gamepad: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -186,6 +189,7 @@ func _plaque(hot: bool) -> StyleBoxFlat:
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
_update_rebind_rows_for_device()
|
||||
if not main_panel.visible or video.stream == null:
|
||||
return
|
||||
var frame_count := CAPE_POS.size()
|
||||
@@ -294,10 +298,24 @@ func _add_toggle_row(label_text: String, value: bool, setter: Callable, at_index
|
||||
options_vbox.move_child(row, at_index)
|
||||
|
||||
|
||||
## Rebuilds the Controls rebind list for whichever device the player is actually holding
|
||||
## (Controls.using_gamepad(), the same last-input heuristic the HUD's ability keycaps use —
|
||||
## not mere connection presence, so a controller left plugged in but unused doesn't hide
|
||||
## keyboard rebinds from a mouse-and-keyboard player). On a gamepad: movement rows drop
|
||||
## entirely (the D-pad/stick isn't individually remapped, so there's nothing to show), and
|
||||
## each ability shows only its gamepad button. On keyboard: the reverse — WASD-style
|
||||
## keyboard binds only, no gamepad buttons cluttering the list.
|
||||
func _build_rebind_rows() -> void:
|
||||
for child in rebind_list.get_children():
|
||||
child.queue_free()
|
||||
var gamepad := Controls.using_gamepad()
|
||||
_rebind_rows_show_gamepad = gamepad
|
||||
_update_rebind_hint(gamepad)
|
||||
for action: StringName in ACTION_LABELS:
|
||||
var is_joy_rebindable := Controls.JOY_REBINDABLE_ACTIONS.has(String(action))
|
||||
if gamepad and not is_joy_rebindable:
|
||||
continue # movement: nothing to show/rebind on a gamepad
|
||||
|
||||
var row := HBoxContainer.new()
|
||||
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
|
||||
@@ -312,16 +330,15 @@ func _build_rebind_rows() -> void:
|
||||
name_label.add_theme_constant_override("outline_size", 5)
|
||||
row.add_child(name_label)
|
||||
|
||||
var key_button := Button.new()
|
||||
key_button.custom_minimum_size = Vector2(160, 0)
|
||||
key_button.text = Controls.get_key_label(action)
|
||||
_style_button(key_button, false)
|
||||
key_button.pressed.connect(_on_rebind_pressed.bind(action, key_button))
|
||||
row.add_child(key_button)
|
||||
if not gamepad:
|
||||
var key_button := Button.new()
|
||||
key_button.custom_minimum_size = Vector2(160, 0)
|
||||
key_button.text = Controls.get_key_label(action)
|
||||
_style_button(key_button, false)
|
||||
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)):
|
||||
if gamepad and is_joy_rebindable:
|
||||
var joy_button := Button.new()
|
||||
joy_button.custom_minimum_size = Vector2(72, 0)
|
||||
joy_button.text = Controls.get_joy_button_label(action)
|
||||
@@ -332,6 +349,26 @@ func _build_rebind_rows() -> void:
|
||||
rebind_list.add_child(row)
|
||||
|
||||
|
||||
func _update_rebind_hint(gamepad: bool) -> void:
|
||||
var hint := get_node_or_null("OptionsPanel/VBox/Hint") as Label
|
||||
if hint != null:
|
||||
hint.text = "Click a button, then press a new one on your controller. Esc cancels." \
|
||||
if gamepad else "Click a key, then press a new key. Esc cancels."
|
||||
|
||||
|
||||
# Live-swaps the rebind list if the player switches device while the Options panel is open
|
||||
# (e.g. they were clicking through with a mouse, then pick up a controller). Skipped while
|
||||
# actively listening for a rebind so an in-progress capture never gets torn out from under
|
||||
# the player.
|
||||
func _update_rebind_rows_for_device() -> void:
|
||||
if not options_panel.visible:
|
||||
return
|
||||
if _listening_action != &"" or _listening_joy_action != &"":
|
||||
return
|
||||
if Controls.using_gamepad() != _rebind_rows_show_gamepad:
|
||||
_build_rebind_rows()
|
||||
|
||||
|
||||
func _on_play_pressed() -> void:
|
||||
_set_buttons_disabled(true)
|
||||
Run.start_new_run() # fresh map for this run; the arena is the intro fight
|
||||
|
||||
Reference in New Issue
Block a user