Skip to content

[FEATURE] Implement Selection and Back Audio Triggers #492

Description

@ikostan

Sub-Issue 2: UI Accept & Cancel SFX


Description

Implement a robust audio feedback system to trigger distinct sound effects when confirming selections or backing out of menus across the UI framework. To avoid Godot 4 viewport/sub-window isolation issues and eliminate double-triggering conflicts on standard buttons, this uses a hybrid architecture: global interception for menu navigation/cancellation, and localized, signal-driven logic for modal confirmations.

Note: Automated GUT unit testing is explicitly out of scope for this requirement and is handled separately under its own dedicated task (#495).

Audio Mappings

  • Accept Action: Plays res://files/sounds/sfx/ui_accept.wav.
  • Cancel Action: Plays res://files/sounds/sfx/ui_cancel.wav.

Technical Architectural Requirements

1. Centralized Cancellation Interception (res://scripts/core/globals.gd)

  • Intercept ui_cancel (the Escape key or gamepad back action) globally inside the _input(event: InputEvent) loop of the Globals autoload singleton.
  • Event-Driven Guard: Evaluate incoming events directly via event.is_action_pressed("ui_cancel", false). Explicitly passing false to the allow_echo parameter ensures the sound triggers exactly once per physical press cycle, eliminating key-repeat spam.
  • Context Gate: The global cancellation trigger must only evaluate when a menu context is active (get_tree().paused or options_open or not hidden_menus.is_empty() or ui_has_focus).

2. Localized Dialog Confirmation & Platform-Specific Delay (res://scripts/ui/menus/main_menu.gd)

  • Due to Godot 4's Window and ConfirmationDialog isolation rules, capture the ui_accept confirmation action locally via native scene signals (_on_quit_dialog_confirmed() and _on_quit_dialog_canceled()).
  • Audio Buffer Protection (Configurable Flush Delay): Implement a configurable execution delay using an export variable (audio_flush_delay) to let the audio stream finish flushing to system hardware before termination.
  • Platform Adaptation: On desktop native platforms, use a non-blocking engine timer delay (await get_tree().create_timer(audio_flush_delay).timeout) before calling get_tree().quit(). On Web export platforms, format and offload this delay directly to a JavaScript timeout function (setTimeout) via JavaScriptBridge.eval to preserve the browser's transient user activation state and prevent the redirect from being blocked as an unsolicited script navigation.

3. Value-Editing, Toggle, & Selection Component Gate

  • Inside globals.gd, explicitly bypass global interface triggers if a control node whose primary purpose is text entry, numerical value configuration, state toggling, selection lists, or input remapping currently holds focus (gui_get_focus_owner()). This protects input elements from false-positive ticks:
    • Text Entry: LineEdit, TextEdit
    • Numerical/Range Selection: Range (covers Slider and SpinBox nodes)
    • State Toggling: CheckButton
    • Selection Dropdowns: OptionButton
    • Input Configuration: InputRemapButton

4. Encapsulation & Resource Management

  • Route all requests through the audio manager singleton using AudioManager.play_sfx("ui_accept") and AudioManager.play_sfx("ui_cancel"). This utilizes the pre-allocated audio player pool and routes to the designated menu audio bus without introducing disk I/O stutter or web export latency spikes.

Verification Criteria

Functional Manual Verification

  • Keyboard Confirmation: Pressing ui_accept (Enter/Space) inside a modal popup (like QuitDialog) triggers ui_accept.wav completely and smoothly before the application exits or redirects.
  • Gamepad Confirmation: Pressing a controller button mapped to ui_accept on a modal dialog choice triggers ui_accept.wav exactly once.
  • Flat Button Protection: Pressing ui_accept while highlighting standard flat main menu or options menu buttons does not superimpose a global confirmation sound over the button's native theme audio.
  • Keyboard Cancellation: Pressing ui_cancel (Escape) inside any valid menu panel layer or dialog box triggers ui_cancel.wav exactly once.
  • Gamepad Cancellation: Pressing a controller button mapped to ui_cancel to close a submenu panel triggers ui_cancel.wav exactly once.
  • Echo Mitigation: Holding down a bound keyboard key or gamepad button does not generate rapid, machine-gun repeating audio loops.
  • LineEdit Gate: Typing or pressing Enter to submit data inside a focused text field box completely suppresses interface navigation/confirmation sound events.
  • Slider Control Gate: Modifying values using horizontal directional inputs while a volume or difficulty Slider node has focus does not emit an accidental navigation tick sound.
  • CheckButton Gate: Toggling or interacting with a focused CheckButton suppresses global cancellation audio triggers.
  • OptionButton Gate: Interacting with or opening a focused dropdown menu selection box suppresses global cancellation audio triggers.
  • InputRemapButton Gate: Pressing Escape or interacting with a focused control remapping button suppresses the global cancellation sound effect to avoid overlap with remapping states.

Metadata

Metadata

Labels

audioSound/Audio related issueenhancementNew feature or request

Projects

Status
Done

Relationships

None yet

Development

No branches or pull requests

Issue actions