diff --git a/src/duckstation-qt/achievementsettingswidget.cpp b/src/duckstation-qt/achievementsettingswidget.cpp index c2a62bb2b..9fa4dcf1f 100644 --- a/src/duckstation-qt/achievementsettingswidget.cpp +++ b/src/duckstation-qt/achievementsettingswidget.cpp @@ -18,6 +18,8 @@ AchievementSettingsWidget::AchievementSettingsWidget(QtHostInterface* host_inter SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.richPresence, "Cheevos", "RichPresence", true); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.testMode, "Cheevos", "TestMode", false); + SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.unofficialTestMode, "Cheevos", + "UnofficialTestMode", false); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.useFirstDiscFromPlaylist, "Cheevos", "UseFirstDiscFromPlaylist", true); m_ui.enable->setChecked(m_host_interface->GetBoolSettingValue("Cheevos", "Enabled", false)); @@ -28,6 +30,10 @@ AchievementSettingsWidget::AchievementSettingsWidget(QtHostInterface* host_inter dialog->registerWidgetHelp(m_ui.testMode, tr("Enable Test Mode"), tr("Unchecked"), tr("When enabled, DuckStation will assume all achievements are locked and not send any " "unlock notifications to the server.")); + dialog->registerWidgetHelp( + m_ui.unofficialTestMode, tr("Test Unofficial Achievements"), tr("Unchecked"), + tr("When enabled, DuckStation will list achievements from unofficial sets. Please note that these achievements are " + "not tracked by RetroAchievements, so they unlock every time.")); dialog->registerWidgetHelp( m_ui.richPresence, tr("Enable Rich Presence"), tr("Unchecked"), tr("When enabled, rich presence information will be collected and sent to the server where supported.")); diff --git a/src/duckstation-qt/achievementsettingswidget.ui b/src/duckstation-qt/achievementsettingswidget.ui index a965c662a..83c646427 100644 --- a/src/duckstation-qt/achievementsettingswidget.ui +++ b/src/duckstation-qt/achievementsettingswidget.ui @@ -67,6 +67,13 @@ + + + + Test Unofficial Achievements + + + diff --git a/src/frontend-common/cheevos.cpp b/src/frontend-common/cheevos.cpp index b0a2ae85b..571c87abf 100644 --- a/src/frontend-common/cheevos.cpp +++ b/src/frontend-common/cheevos.cpp @@ -55,6 +55,7 @@ u32 g_game_id = 0; static bool s_logged_in = false; static bool s_test_mode = false; +static bool s_unofficial_test_mode = false; static bool s_use_first_disc_from_playlist = true; static bool s_rich_presence_enabled = false; @@ -212,7 +213,8 @@ static std::string GetUserAgent() return StringUtil::StdStringFromFormat("DuckStation for %s (%s) %s", SYSTEM_STR, CPU_ARCH_STR, g_scm_tag_str); } -bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode) +bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode, + bool include_unofficial) { s_http_downloader = FrontendCommon::HTTPDownloader::Create(GetUserAgent().c_str()); if (!s_http_downloader) @@ -224,6 +226,7 @@ bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_r g_active = true; g_challenge_mode = challenge_mode; s_test_mode = test_mode; + s_unofficial_test_mode = include_unofficial; s_use_first_disc_from_playlist = use_first_disc_from_playlist; s_rich_presence_enabled = enable_rich_presence; rc_runtime_init(&s_rcheevos_runtime); @@ -298,6 +301,11 @@ bool IsTestModeActive() return s_test_mode; } +bool IsUnofficialTestModeActive() +{ + return s_unofficial_test_mode; +} + bool IsUsingFirstDiscFromPlaylist() { return s_use_first_disc_from_playlist; @@ -672,16 +680,16 @@ static void GetPatchesCallback(s32 status_code, const FrontendCommon::HTTPDownlo } const u32 id = achievement["ID"].GetUint(); - const u32 category = achievement["Flags"].GetUint(); + const AchievementCategory category = static_cast(achievement["Flags"].GetUint()); const char* memaddr = achievement["MemAddr"].GetString(); std::string title = achievement["Title"].GetString(); std::string description = GetOptionalString(achievement, "Description"); std::string badge_name = GetOptionalString(achievement, "BadgeName"); const u32 points = GetOptionalUInt(achievement, "Points"); - // Skip local and unofficial achievements for now. - if (static_cast(category) == AchievementCategory::Local || - static_cast(category) == AchievementCategory::Unofficial) + // Skip local and unofficial achievements for now, unless "Test Unofficial Achievements" is enabled + if (!s_unofficial_test_mode && + (category == AchievementCategory::Local || category == AchievementCategory::Unofficial)) { Log_WarningPrintf("Skipping unofficial achievement %u (%s)", id, title.c_str()); continue; @@ -701,6 +709,7 @@ static void GetPatchesCallback(s32 status_code, const FrontendCommon::HTTPDownlo cheevo.locked = true; cheevo.active = false; cheevo.points = points; + cheevo.category = category; if (!badge_name.empty()) { @@ -1101,6 +1110,13 @@ void UnlockAchievement(u32 achievement_id, bool add_notification /* = true*/) return; } + if (achievement->category != AchievementCategory::Core) + { + Log_WarningPrintf("Skipping sending achievement %u unlock to server because it's not from the core set.", + achievement_id); + return; + } + char url[512]; rc_url_award_cheevo(url, sizeof(url), s_username.c_str(), s_login_token.c_str(), achievement_id, static_cast(g_challenge_mode), s_game_hash.c_str()); diff --git a/src/frontend-common/cheevos.h b/src/frontend-common/cheevos.h index d6c108f24..39231a8cb 100644 --- a/src/frontend-common/cheevos.h +++ b/src/frontend-common/cheevos.h @@ -23,6 +23,7 @@ struct Achievement std::string locked_badge_path; std::string unlocked_badge_path; u32 points; + AchievementCategory category; bool locked; bool active; }; @@ -56,13 +57,15 @@ ALWAYS_INLINE u32 GetGameID() return g_game_id; } -bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode); +bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode, + bool include_unofficial); void Reset(); void Shutdown(); void Update(); bool IsLoggedIn(); bool IsTestModeActive(); +bool IsUnofficialTestModeActive(); bool IsUsingFirstDiscFromPlaylist(); bool IsRichPresenceEnabled(); const std::string& GetUsername(); diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index a6c47140f..f0e178346 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -2807,6 +2807,7 @@ void CommonHostInterface::SetDefaultSettings(SettingsInterface& si) #ifdef WITH_CHEEVOS si.SetBoolValue("Cheevos", "Enabled", false); si.SetBoolValue("Cheevos", "TestMode", false); + si.SetBoolValue("Cheevos", "UnofficialTestMode", false); si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true); si.DeleteValue("Cheevos", "Username"); si.DeleteValue("Cheevos", "Token"); @@ -3818,11 +3819,13 @@ void CommonHostInterface::UpdateCheevosActive() { const bool cheevos_enabled = GetBoolSettingValue("Cheevos", "Enabled", false); const bool cheevos_test_mode = GetBoolSettingValue("Cheevos", "TestMode", false); + const bool cheevos_unofficial_test_mode = GetBoolSettingValue("Cheevos", "UnofficialTestMode", false); const bool cheevos_use_first_disc_from_playlist = GetBoolSettingValue("Cheevos", "UseFirstDiscFromPlaylist", true); const bool cheevos_rich_presence = GetBoolSettingValue("Cheevos", "RichPresence", true); const bool cheevos_hardcore = GetBoolSettingValue("Cheevos", "ChallengeMode", false); if (cheevos_enabled != Cheevos::IsActive() || cheevos_test_mode != Cheevos::IsTestModeActive() || + cheevos_unofficial_test_mode != Cheevos::IsUnofficialTestModeActive() || cheevos_use_first_disc_from_playlist != Cheevos::IsUsingFirstDiscFromPlaylist() || cheevos_rich_presence != Cheevos::IsRichPresenceEnabled() || cheevos_hardcore != Cheevos::IsChallengeModeEnabled()) @@ -3831,7 +3834,7 @@ void CommonHostInterface::UpdateCheevosActive() if (cheevos_enabled) { if (!Cheevos::Initialize(cheevos_test_mode, cheevos_use_first_disc_from_playlist, cheevos_rich_presence, - cheevos_hardcore)) + cheevos_hardcore, cheevos_unofficial_test_mode)) ReportError("Failed to initialize cheevos after settings change."); } } diff --git a/src/frontend-common/fullscreen_ui.cpp b/src/frontend-common/fullscreen_ui.cpp index 0a3ce1211..538be5d28 100644 --- a/src/frontend-common/fullscreen_ui.cpp +++ b/src/frontend-common/fullscreen_ui.cpp @@ -2281,6 +2281,11 @@ void DrawSettingsWindow() "When enabled, DuckStation will assume all achievements are locked and not " "send any unlock notifications to the server.", "Cheevos", "TestMode", false); + settings_changed |= + ToggleButtonForNonSetting(ICON_FA_MEDAL " Test Unofficial Achievements", + "When enabled, DuckStation will list achievements from unofficial sets. These " + "achievements are not tracked by RetroAchievements.", + "Cheevos", "UnofficialTestMode", false); settings_changed |= ToggleButtonForNonSetting(ICON_FA_COMPACT_DISC " Use First Disc From Playlist", "When enabled, the first disc in a playlist will be used for " "achievements, regardless of which disc is active.",