diff --git a/data/resources/sounds/achievements/README.txt b/data/resources/sounds/achievements/README.txt new file mode 100644 index 000000000..db96edd8e --- /dev/null +++ b/data/resources/sounds/achievements/README.txt @@ -0,0 +1,2 @@ +lbsubmit.wav: https://freesound.org/people/Eponn/sounds/636656/ +unlock.wav and message.wav are from https://github.com/RetroAchievements/RAInterface diff --git a/data/resources/sounds/achievements/lbsubmit.wav b/data/resources/sounds/achievements/lbsubmit.wav new file mode 100644 index 000000000..218073d02 Binary files /dev/null and b/data/resources/sounds/achievements/lbsubmit.wav differ diff --git a/data/resources/sounds/achievements/message.wav b/data/resources/sounds/achievements/message.wav new file mode 100644 index 000000000..7146107f7 Binary files /dev/null and b/data/resources/sounds/achievements/message.wav differ diff --git a/data/resources/sounds/achievements/unlock.wav b/data/resources/sounds/achievements/unlock.wav new file mode 100644 index 000000000..2b0c3b1ea Binary files /dev/null and b/data/resources/sounds/achievements/unlock.wav differ diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 173b42c3d..3dc251234 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -344,6 +344,7 @@ void Settings::Load(SettingsInterface& si) achievements_use_first_disc_from_playlist = si.GetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true); achievements_rich_presence = si.GetBoolValue("Cheevos", "RichPresence", true); achievements_challenge_mode = si.GetBoolValue("Cheevos", "ChallengeMode", false); + achievements_sound_effects = si.GetBoolValue("Cheevos", "SoundEffects", true); log_level = ParseLogLevelName(si.GetStringValue("Logging", "LogLevel", GetLogLevelName(DEFAULT_LOG_LEVEL)).c_str()) .value_or(DEFAULT_LOG_LEVEL); @@ -528,6 +529,7 @@ void Settings::Save(SettingsInterface& si) const si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", achievements_use_first_disc_from_playlist); si.SetBoolValue("Cheevos", "RichPresence", achievements_rich_presence); si.SetBoolValue("Cheevos", "ChallengeMode", achievements_challenge_mode); + si.SetBoolValue("Cheevos", "SoundEffects", achievements_sound_effects); si.SetStringValue("Logging", "LogLevel", GetLogLevelName(log_level)); si.SetStringValue("Logging", "LogFilter", log_filter.c_str()); diff --git a/src/core/settings.h b/src/core/settings.h index 199bf805a..5d91fa9c3 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -173,6 +173,7 @@ struct Settings bool achievements_use_first_disc_from_playlist : 1; bool achievements_rich_presence : 1; bool achievements_challenge_mode : 1; + bool achievements_sound_effects : 1; #endif struct DebugSettings diff --git a/src/duckstation-qt/achievementsettingswidget.cpp b/src/duckstation-qt/achievementsettingswidget.cpp index 68878f205..816fb8d6e 100644 --- a/src/duckstation-qt/achievementsettingswidget.cpp +++ b/src/duckstation-qt/achievementsettingswidget.cpp @@ -24,6 +24,7 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.unofficialTestMode, "Cheevos", "UnofficialTestMode", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useFirstDiscFromPlaylist, "Cheevos", "UseFirstDiscFromPlaylist", true); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.soundEffects, "Cheevos", "SoundEffects", true); dialog->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"), tr("When enabled and logged in, DuckStation will scan for achievements on startup.")); @@ -44,6 +45,9 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi dialog->registerWidgetHelp(m_ui.challengeMode, tr("Enable Hardcore Mode"), tr("Unchecked"), tr("\"Challenge\" mode for achievements. Disables save state, cheats, and slowdown " "functions, but you receive double the achievement points.")); + dialog->registerWidgetHelp( + m_ui.challengeMode, tr("Enable Sound Effects"), tr("Checked"), + tr("Plays sound effects for events such as achievement unlocks and leaderboard submissions.")); connect(m_ui.enable, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState); diff --git a/src/duckstation-qt/achievementsettingswidget.ui b/src/duckstation-qt/achievementsettingswidget.ui index 8b15cd68a..b61a4c3af 100644 --- a/src/duckstation-qt/achievementsettingswidget.ui +++ b/src/duckstation-qt/achievementsettingswidget.ui @@ -39,17 +39,10 @@ - - + + - Enable Rich Presence - - - - - - - Enable Test Mode + Enable Hardcore Mode @@ -60,10 +53,10 @@ - - + + - Enable Hardcore Mode + Enable Test Mode @@ -74,6 +67,20 @@ + + + + Enable Rich Presence + + + + + + + Enable Sound Effects + + + diff --git a/src/frontend-common/achievements.cpp b/src/frontend-common/achievements.cpp index ba1fe3250..42faf69b2 100644 --- a/src/frontend-common/achievements.cpp +++ b/src/frontend-common/achievements.cpp @@ -16,6 +16,7 @@ #include "core/system.h" #include "fullscreen_ui.h" #include "imgui_fullscreen.h" +#include "platform_misc.h" #include "rapidjson/document.h" #include "rc_api_info.h" #include "rc_api_request.h" @@ -53,6 +54,10 @@ enum : s32 NO_RICH_PRESENCE_PING_FREQUENCY = RICH_PRESENCE_PING_FREQUENCY * 2, }; +static constexpr const char* INFO_SOUND_NAME = "sounds/achievements/message.wav"; +static constexpr const char* UNLOCK_SOUND_NAME = "sounds/achievements/unlock.wav"; +static constexpr const char* LBSUBMIT_SOUND_NAME = "sounds/achievements/lbsubmit.wav"; + static void FormattedError(const char* format, ...); static void LogFailedResponseJSON(const Common::HTTPDownloader::Request::Data& data); static void EnsureCacheDirectoriesExist(); @@ -1021,6 +1026,10 @@ void Achievements::DisplayAchievementSummary() Host::RunOnCPUThread([title = std::move(title), summary = std::move(summary), icon = s_game_icon]() { if (FullscreenUI::IsInitialized()) ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon)); + + // Technically not going through the resource API, but since we're passing this to something else, we can't. + if (g_settings.achievements_sound_effects) + FrontendCommon::PlaySoundAsync(Path::Combine(EmuFolders::Resources, INFO_SOUND_NAME).c_str()); }); } @@ -1729,6 +1738,10 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, std::string conten submitted_score, best_score, response.new_rank, response.num_entries); ImGuiFullscreen::AddNotification(10.0f, lb->title, std::move(summary), s_game_icon); + + // Technically not going through the resource API, but since we're passing this to something else, we can't. + if (g_settings.achievements_sound_effects) + FrontendCommon::PlaySoundAsync(Path::Combine(EmuFolders::Resources, LBSUBMIT_SOUND_NAME).c_str()); } void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /* = true*/) @@ -1769,6 +1782,8 @@ void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification / ImGuiFullscreen::AddNotification(15.0f, std::move(title), achievement->description, GetAchievementBadgePath(*achievement)); + if (g_settings.achievements_sound_effects) + FrontendCommon::PlaySoundAsync(Path::Combine(EmuFolders::Resources, UNLOCK_SOUND_NAME).c_str()); if (IsTestModeActive()) {