Show Xbox-style A/B/X/Y glyphs on the ability bar for a gamepad

Controls now tracks whether the player's last deliberate press was a
gamepad button/stick or a keyboard key/mouse click (ignoring plain
mouse motion, since the mouse still drives the camera regardless of
which device moves the bull). The HUD's ability-bar keycaps read from
that: keyboard letters normally, Xbox-style face-button glyphs while
a gamepad is in use, swapping live the moment the player switches
devices.

Godot's joypad button indices are already Xbox-position-normalized
(by SDL natively, by the browser's Standard Gamepad Mapping on web),
so the glyph mapping (button 0/1/2/3 -> A/B/X/Y) reads correctly for
any controller brand with no per-device special-casing.

Verified headlessly: labels start as the bound keyboard letters
(F/G/H/J), switch to X/A/B/Y on a synthetic joypad button press
(matching project.godot's kick/dash/slam/roll bindings), and switch
back on a keyboard press.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MXBNaEBmrYDSMcv4NgUWtV
This commit is contained in:
2026-09-10 19:20:54 +03:00
parent e810d2e75b
commit a34989c49e
2 changed files with 77 additions and 1 deletions
+19 -1
View File
@@ -44,7 +44,11 @@ var _hp_seg_fills: Array[ColorRect] = []
var _bottom_col: VBoxContainer
var _ability_bar: HBoxContainer
var _ability_panels: Array[PanelContainer] = []
var _ability_key_labels: Array[Label] = []
var _bull_bg: TextureRect
# Cached so the ability keycaps only get rewritten on an actual keyboard<->gamepad switch,
# not every frame.
var _showing_gamepad_hints: bool = false
# Lobby barrel-smash reward — tally the barrels present on load; when the last one bursts,
# throw confetti + a "WOooOoW!" and grant the bull two yellow bonus pips. Latched so it
@@ -257,6 +261,7 @@ static func _format_time(seconds: float) -> String:
func _process(_delta: float) -> void:
_update_control_hints()
_update_ability_key_labels()
if _run_timer_label != null:
_run_timer_label.visible = Run.run_timer_started() and not _game_over
if _run_timer_label.visible:
@@ -469,6 +474,18 @@ func _update_control_hints() -> void:
_tab_hint.visible = not _controls_visible
# Swaps the ability-bar keycaps between keyboard letters and Xbox-style face-button glyphs
# (A/B/X/Y) as the player switches between keyboard and gamepad. Only rewrites the labels on
# an actual switch, not every frame — Controls.using_gamepad() itself is cheap either way.
func _update_ability_key_labels() -> void:
var gamepad := Controls.using_gamepad()
if gamepad == _showing_gamepad_hints:
return
_showing_gamepad_hints = gamepad
for i in _ability_key_labels.size():
_ability_key_labels[i].text = Controls.ability_key_label(_ABILITY_ACTIONS[i])
# ── Damage vignette ───────────────────────────────────────────────────────────
# A full-screen ColorRect driven by damage_vignette.gdshader: transparent centre, red
# toward the edges, alpha ("flash") tweened up on a hit and eased back to nothing. Built
@@ -902,7 +919,7 @@ func _build_ability_slot(idx: int) -> Control:
vbox.add_child(keycap)
var key_lbl := Label.new()
key_lbl.text = Controls.get_key_label(_ABILITY_ACTIONS[idx])
key_lbl.text = Controls.ability_key_label(_ABILITY_ACTIONS[idx])
key_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
key_lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
key_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
@@ -912,6 +929,7 @@ func _build_ability_slot(idx: int) -> Control:
key_lbl.add_theme_constant_override("outline_size", 3)
key_lbl.add_theme_font_size_override("font_size", 14)
keycap.add_child(key_lbl)
_ability_key_labels.append(key_lbl)
return vbox