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
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.
Audio Mappings
res://files/sounds/sfx/ui_accept.wav.res://files/sounds/sfx/ui_cancel.wav.Technical Architectural Requirements
1. Centralized Cancellation Interception (
res://scripts/core/globals.gd)ui_cancel(the Escape key or gamepad back action) globally inside the_input(event: InputEvent)loop of theGlobalsautoload singleton.event.is_action_pressed("ui_cancel", false). Explicitly passingfalseto theallow_echoparameter ensures the sound triggers exactly once per physical press cycle, eliminating key-repeat spam.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)WindowandConfirmationDialogisolation rules, capture theui_acceptconfirmation action locally via native scene signals (_on_quit_dialog_confirmed()and_on_quit_dialog_canceled()).audio_flush_delay) to let the audio stream finish flushing to system hardware before termination.await get_tree().create_timer(audio_flush_delay).timeout) before callingget_tree().quit(). On Web export platforms, format and offload this delay directly to a JavaScript timeout function (setTimeout) viaJavaScriptBridge.evalto 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
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:LineEdit,TextEditRange(coversSliderandSpinBoxnodes)CheckButtonOptionButtonInputRemapButton4. Encapsulation & Resource Management
AudioManager.play_sfx("ui_accept")andAudioManager.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
ui_accept(Enter/Space) inside a modal popup (likeQuitDialog) triggersui_accept.wavcompletely and smoothly before the application exits or redirects.ui_accepton a modal dialog choice triggersui_accept.wavexactly once.ui_acceptwhile highlighting standard flat main menu or options menu buttons does not superimpose a global confirmation sound over the button's native theme audio.ui_cancel(Escape) inside any valid menu panel layer or dialog box triggersui_cancel.wavexactly once.ui_cancelto close a submenu panel triggersui_cancel.wavexactly once.