mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-04 05:04:33 +00:00
FullscreenUI: Add navigation sound effects
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include "game_list.h"
|
||||
#include "gpu_thread.h"
|
||||
#include "host.h"
|
||||
#include "sound_effect_manager.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "scmversion/scmversion.h"
|
||||
@@ -123,6 +124,11 @@ static void DrawResumeStateSelector();
|
||||
static constexpr std::string_view RESUME_STATE_SELECTOR_DIALOG_NAME = "##resume_state_selector";
|
||||
static constexpr std::string_view ABOUT_DIALOG_NAME = "##about_duckstation";
|
||||
|
||||
const char* SFX_NAV_ACTIVATE = "sounds/nav_activate.wav";
|
||||
const char* SFX_NAV_BACK = "sounds/nav_back.wav";
|
||||
const char* SFX_NAV_MOVE = "sounds/nav_move.wav";
|
||||
const char* SFX_CONTENT_START = "sounds/content_start.wav";
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// State
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -184,6 +190,7 @@ void FullscreenUI::Initialize()
|
||||
UpdateRunIdleState();
|
||||
}
|
||||
|
||||
SoundEffectManager::EnsureInitialized();
|
||||
INFO_LOG("Fullscreen UI initialized.");
|
||||
}
|
||||
|
||||
@@ -303,6 +310,7 @@ void FullscreenUI::OpenPauseMenu()
|
||||
|
||||
PauseForMenuOpen(true);
|
||||
ForceKeyNavEnabled();
|
||||
EnqueueSoundEffect(SFX_NAV_ACTIVATE);
|
||||
|
||||
UpdateAchievementsRecentUnlockAndAlmostThere();
|
||||
BeginTransition(SHORT_TRANSITION_TIME, []() {
|
||||
@@ -324,6 +332,7 @@ void FullscreenUI::OpenCheatsMenu()
|
||||
|
||||
PauseForMenuOpen(false);
|
||||
ForceKeyNavEnabled();
|
||||
EnqueueSoundEffect(SFX_NAV_ACTIVATE);
|
||||
|
||||
BeginTransition(SHORT_TRANSITION_TIME, []() {
|
||||
if (!SwitchToGameSettings(SettingsPage::Cheats))
|
||||
@@ -449,6 +458,8 @@ void FullscreenUI::ReturnToMainWindow(float transition_time)
|
||||
|
||||
void FullscreenUI::Shutdown(bool clear_state)
|
||||
{
|
||||
SoundEffectManager::Shutdown();
|
||||
|
||||
if (clear_state)
|
||||
{
|
||||
s_locals.current_main_window = MainWindowType::None;
|
||||
@@ -617,6 +628,8 @@ void FullscreenUI::DoStartPath(std::string path, std::string state, std::optiona
|
||||
if (GPUThread::HasGPUBackend())
|
||||
return;
|
||||
|
||||
EnqueueSoundEffect(SFX_CONTENT_START);
|
||||
|
||||
// Stop running idle to prevent game list from being redrawn until we know if startup succeeded.
|
||||
GPUThread::SetRunIdleReason(GPUThread::RunIdleReason::FullscreenUIActive, false);
|
||||
|
||||
@@ -1302,11 +1315,19 @@ void FullscreenUI::DrawLandingWindow()
|
||||
if (!AreAnyDialogsOpen())
|
||||
{
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_ACTIVATE);
|
||||
OpenFixedPopupDialog(ABOUT_DIALOG_NAME);
|
||||
}
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_GamepadStart, false) || ImGui::IsKeyPressed(ImGuiKey_F3, false))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_ACTIVATE);
|
||||
DoResume();
|
||||
}
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false) || ImGui::IsKeyPressed(ImGuiKey_F11, false))
|
||||
{
|
||||
DoToggleFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsGamepadInputSource())
|
||||
@@ -1371,7 +1392,10 @@ void FullscreenUI::DrawStartGameWindow()
|
||||
if (!AreAnyDialogsOpen())
|
||||
{
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_ACTIVATE);
|
||||
OpenSaveStateSelector(std::string(), std::string(), true);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsGamepadInputSource())
|
||||
|
||||
@@ -97,6 +97,12 @@ private:
|
||||
std::string m_title;
|
||||
};
|
||||
|
||||
// Sound effect names.
|
||||
extern const char* SFX_NAV_ACTIVATE;
|
||||
extern const char* SFX_NAV_BACK;
|
||||
extern const char* SFX_NAV_MOVE;
|
||||
extern const char* SFX_CONTENT_START;
|
||||
|
||||
} // namespace FullscreenUI
|
||||
|
||||
// Host UI triggers from Big Picture mode.
|
||||
|
||||
@@ -608,6 +608,7 @@ void FullscreenUI::OpenAchievementsWindow()
|
||||
|
||||
PauseForMenuOpen(false);
|
||||
ForceKeyNavEnabled();
|
||||
EnqueueSoundEffect(SFX_NAV_ACTIVATE);
|
||||
|
||||
BeginTransition(SHORT_TRANSITION_TIME, &SwitchToAchievements);
|
||||
});
|
||||
@@ -687,12 +688,14 @@ void FullscreenUI::DrawSubsetSelector()
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_GamepadDpadLeft, true) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_NavGamepadTweakSlow, true) || ImGui::IsKeyPressed(ImGuiKey_LeftArrow, true))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_MOVE);
|
||||
new_subset_id = (i == 0) ? s_achievements_locals.subset_info_list.back().subset_id :
|
||||
s_achievements_locals.subset_info_list[i - 1].subset_id;
|
||||
}
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_GamepadDpadRight, true) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_NavGamepadTweakFast, true) || ImGui::IsKeyPressed(ImGuiKey_RightArrow, true))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_MOVE);
|
||||
new_subset_id = ((i + 1) == s_achievements_locals.subset_info_list.size()) ?
|
||||
s_achievements_locals.subset_info_list.front().subset_id :
|
||||
s_achievements_locals.subset_info_list[i + 1].subset_id;
|
||||
@@ -1313,6 +1316,7 @@ void FullscreenUI::OpenLeaderboardsWindow()
|
||||
|
||||
PauseForMenuOpen(false);
|
||||
ForceKeyNavEnabled();
|
||||
EnqueueSoundEffect(SFX_NAV_ACTIVATE);
|
||||
|
||||
BeginTransition(SHORT_TRANSITION_TIME, &SwitchToLeaderboards);
|
||||
});
|
||||
|
||||
@@ -336,6 +336,7 @@ void FullscreenUI::DrawGameListWindow()
|
||||
{
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false) || ImGui::IsKeyPressed(ImGuiKey_F4, false))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_MOVE);
|
||||
BeginTransition([]() {
|
||||
s_game_list_locals.game_list_view =
|
||||
(s_game_list_locals.game_list_view == GameListView::Grid) ? GameListView::List : GameListView::Grid;
|
||||
@@ -344,10 +345,12 @@ void FullscreenUI::DrawGameListWindow()
|
||||
}
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack, false) || ImGui::IsKeyPressed(ImGuiKey_F2, false))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_BACK);
|
||||
BeginTransition(&SwitchToSettings);
|
||||
}
|
||||
else if (ImGui::IsKeyPressed(ImGuiKey_GamepadStart, false) || ImGui::IsKeyPressed(ImGuiKey_F3, false))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_ACTIVATE);
|
||||
DoResume();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1817,6 +1817,7 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_GamepadDpadLeft, true) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_NavGamepadTweakSlow, true) || ImGui::IsKeyPressed(ImGuiKey_LeftArrow, true))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_MOVE);
|
||||
BeginTransition([page = pages[(index == 0) ? (count - 1) : (index - 1)]]() {
|
||||
s_settings_locals.settings_page = page;
|
||||
QueueResetFocus(FocusResetType::Other);
|
||||
@@ -1826,6 +1827,7 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
ImGui::IsKeyPressed(ImGuiKey_NavGamepadTweakFast, true) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_RightArrow, true))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_MOVE);
|
||||
BeginTransition([page = pages[(index + 1) % count]]() {
|
||||
s_settings_locals.settings_page = page;
|
||||
QueueResetFocus(FocusResetType::Other);
|
||||
@@ -2180,6 +2182,10 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
||||
FSUI_VSTR("Draws a border around the currently-selected item for readability."), "Main",
|
||||
"FullscreenUIMenuBorders", false);
|
||||
|
||||
widgets_settings_changed |= DrawToggleSetting(bsi, FSUI_ICONVSTR(ICON_FA_VOLUME_HIGH, "Sound Effects"),
|
||||
FSUI_VSTR("Plays sound effects when navigating and activating menus."),
|
||||
"Main", "FullscreenUISoundEffects", true);
|
||||
|
||||
// use transition to work around double lock
|
||||
if (widgets_settings_changed)
|
||||
BeginTransition(0.0f, &FullscreenUI::UpdateWidgetsSettings);
|
||||
|
||||
@@ -544,6 +544,7 @@ TRANSLATE_NOOP("FullscreenUI", "Perspective Correct Colors");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Perspective Correct Textures");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pinky Pals");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Plays sound effects for events such as achievement unlocks and leaderboard submissions.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Plays sound effects when navigating and activating menus.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Please enter your user name and password for retroachievements.org below. Your password will not be saved in DuckStation, an access token will be generated and used instead.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Port {} Controller Type");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Post-Processing Settings");
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "gpu_thread.h"
|
||||
#include "host.h"
|
||||
#include "imgui_overlays.h"
|
||||
#include "sound_effect_manager.h"
|
||||
#include "system.h"
|
||||
|
||||
#include "util/gpu_device.h"
|
||||
@@ -344,6 +345,16 @@ struct WidgetsState
|
||||
s32 enum_choice_button_value = 0;
|
||||
bool enum_choice_button_set = false;
|
||||
|
||||
bool had_hovered_menu_item = false;
|
||||
bool has_hovered_menu_item = false;
|
||||
bool rendered_menu_item_border = false;
|
||||
bool had_focus_reset = false;
|
||||
bool sound_effects_enabled = false;
|
||||
bool had_sound_effect = false;
|
||||
|
||||
ImAnimatedVec2 menu_button_frame_min_animated;
|
||||
ImAnimatedVec2 menu_button_frame_max_animated;
|
||||
|
||||
ChoiceDialog choice_dialog;
|
||||
FileSelectorDialog file_selector_dialog;
|
||||
InputStringDialog input_string_dialog;
|
||||
@@ -351,12 +362,6 @@ struct WidgetsState
|
||||
ProgressDialog progress_dialog;
|
||||
MessageDialog message_dialog;
|
||||
|
||||
ImAnimatedVec2 menu_button_frame_min_animated;
|
||||
ImAnimatedVec2 menu_button_frame_max_animated;
|
||||
bool had_hovered_menu_item = false;
|
||||
bool has_hovered_menu_item = false;
|
||||
bool rendered_menu_item_border = false;
|
||||
|
||||
std::vector<Notification> notifications;
|
||||
|
||||
std::string toast_title;
|
||||
@@ -455,6 +460,7 @@ void FullscreenUI::UpdateWidgetsSettings()
|
||||
UIStyle.Animations = Core::GetBaseBoolSettingValue("Main", "FullscreenUIAnimations", true);
|
||||
UIStyle.SmoothScrolling = Core::GetBaseBoolSettingValue("Main", "FullscreenUISmoothScrolling", true);
|
||||
UIStyle.MenuBorders = Core::GetBaseBoolSettingValue("Main", "FullscreenUIMenuBorders", false);
|
||||
s_state.sound_effects_enabled = Core::GetBaseBoolSettingValue("Main", "FullscreenUISoundEffects", true);
|
||||
|
||||
s_state.fullscreen_footer_icon_mapping = Core::GetBaseBoolSettingValue("Main", "FullscreenUIDisplayPSIcons", false) ?
|
||||
s_ps_button_mapping :
|
||||
@@ -1027,6 +1033,27 @@ void FullscreenUI::EndLayout()
|
||||
|
||||
s_state.rendered_menu_item_border = false;
|
||||
s_state.had_hovered_menu_item = std::exchange(s_state.has_hovered_menu_item, false);
|
||||
|
||||
if (!s_state.had_sound_effect)
|
||||
{
|
||||
if (GImGui->NavActivateId != 0)
|
||||
EnqueueSoundEffect(SFX_NAV_ACTIVATE);
|
||||
else if (GImGui->NavJustMovedToId != 0)
|
||||
EnqueueSoundEffect(SFX_NAV_MOVE);
|
||||
}
|
||||
|
||||
// Avoid playing the move sound on focus reset, since it'll also be an active previously.
|
||||
s_state.had_sound_effect = s_state.had_focus_reset;
|
||||
s_state.had_focus_reset = false;
|
||||
}
|
||||
|
||||
void FullscreenUI::EnqueueSoundEffect(std::string_view sound_effect)
|
||||
{
|
||||
if (s_state.had_sound_effect || !s_state.sound_effects_enabled)
|
||||
return;
|
||||
|
||||
SoundEffectManager::EnqueueSoundEffect(sound_effect);
|
||||
s_state.had_sound_effect = true;
|
||||
}
|
||||
|
||||
FullscreenUI::FixedPopupDialog::FixedPopupDialog() = default;
|
||||
@@ -1196,6 +1223,9 @@ bool FullscreenUI::ResetFocusHere()
|
||||
else
|
||||
ImGui::SetNavWindow(window);
|
||||
|
||||
// prevent any sound from playing on the nav change
|
||||
s_state.had_focus_reset = true;
|
||||
|
||||
s_state.focus_reset_queued = FocusResetType::None;
|
||||
ResetMenuButtonFrame();
|
||||
|
||||
@@ -1248,10 +1278,13 @@ bool FullscreenUI::WantsToCloseMenu()
|
||||
s_state.close_button_state = CloseButtonState::GamepadPressed;
|
||||
}
|
||||
else if ((s_state.close_button_state == CloseButtonState::KeyboardPressed && ImGui::IsKeyReleased(ImGuiKey_Escape)) ||
|
||||
(s_state.close_button_state == CloseButtonState::MousePressed &&
|
||||
ImGui::IsKeyReleased(ImGuiKey_MouseRight)) ||
|
||||
(s_state.close_button_state == CloseButtonState::GamepadPressed &&
|
||||
ImGui::IsKeyReleased(ImGuiKey_NavGamepadCancel)))
|
||||
{
|
||||
EnqueueSoundEffect(SFX_NAV_BACK);
|
||||
s_state.close_button_state = CloseButtonState::AnyReleased;
|
||||
}
|
||||
else if ((s_state.close_button_state == CloseButtonState::MousePressed && ImGui::IsKeyReleased(ImGuiKey_MouseRight)))
|
||||
{
|
||||
s_state.close_button_state = CloseButtonState::AnyReleased;
|
||||
}
|
||||
|
||||
@@ -268,6 +268,9 @@ void UpdateTransitionState();
|
||||
void BeginLayout();
|
||||
void EndLayout();
|
||||
|
||||
/// Enqueues a sound effect, and prevents any other sound effects from being started this frame.
|
||||
void EnqueueSoundEffect(std::string_view sound_effect);
|
||||
|
||||
bool IsAnyFixedPopupDialogOpen();
|
||||
bool IsFixedPopupDialogOpen(std::string_view name);
|
||||
void OpenFixedPopupDialog(std::string_view name);
|
||||
|
||||
@@ -2064,7 +2064,9 @@ void System::DestroySystem()
|
||||
|
||||
FreeMemoryStateStorage(true, true, false);
|
||||
|
||||
SoundEffectManager::Shutdown();
|
||||
// unless fsui is running, we don't need sound effects anymore
|
||||
if (!GPUThread::IsFullscreenUIRequested())
|
||||
SoundEffectManager::Shutdown();
|
||||
|
||||
GPUThread::DestroyGPUBackend();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user