From 414ddf78cce926b2ee9e60a8124b220a62670a65 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 16 Aug 2025 13:29:40 +1000 Subject: [PATCH] Achievements: Display warning for unsupported achievements --- dep/imgui/include/IconsEmoji.h | 1 + src/core/achievements.cpp | 57 ++++++++++++--- .../translations/duckstation-qt_en.ts | 73 +++++++++++++------ 3 files changed, 98 insertions(+), 33 deletions(-) diff --git a/dep/imgui/include/IconsEmoji.h b/dep/imgui/include/IconsEmoji.h index 1e1e77973..38b48583b 100644 --- a/dep/imgui/include/IconsEmoji.h +++ b/dep/imgui/include/IconsEmoji.h @@ -7,6 +7,7 @@ // List to use: https://www.freecodecamp.org/news/all-emojis-emoji-list-for-copy-and-paste/ #define ICON_EMOJI_WARNING "\xe2\x9a\xa0" +#define ICON_EMOJI_CHECKMARK_BUTTON "\xe2\x9c\x85" #define ICON_EMOJI_OPTICAL_DISK "\xf0\x9f\x92\xbf" #define ICON_EMOJI_FLOPPY_DISK "\xf0\x9f\x92\xbe" #define ICON_EMOJI_INFORMATION "\xe2\x84\xb9" diff --git a/src/core/achievements.cpp b/src/core/achievements.cpp index 50d157a6d..d939cc8f7 100644 --- a/src/core/achievements.cpp +++ b/src/core/achievements.cpp @@ -77,6 +77,7 @@ static constexpr u32 LEADERBOARD_ALL_FETCH_SIZE = 20; static constexpr float LOGIN_NOTIFICATION_TIME = 5.0f; static constexpr float ACHIEVEMENT_SUMMARY_NOTIFICATION_TIME = 5.0f; static constexpr float ACHIEVEMENT_SUMMARY_NOTIFICATION_TIME_HC = 10.0f; +static constexpr float ACHIEVEMENT_SUMMARY_UNSUPPORTED_TIME = 12.0f; static constexpr float GAME_COMPLETE_NOTIFICATION_TIME = 20.0f; static constexpr float LEADERBOARD_STARTED_NOTIFICATION_TIME = 3.0f; static constexpr float LEADERBOARD_FAILED_NOTIFICATION_TIME = 3.0f; @@ -1385,9 +1386,24 @@ void Achievements::DisplayAchievementSummary() if (!FullscreenUI::Initialize()) return; - ImGuiFullscreen::AddNotification("achievement_summary", time, std::move(title), std::move(summary), + ImGuiFullscreen::AddNotification("AchievementsSummary", time, std::move(title), std::move(summary), std::move(icon)); }); + + if (s_state.game_summary.num_unsupported_achievements > 0) + { + GPUThread::RunOnThread([num_unsupported = s_state.game_summary.num_unsupported_achievements]() mutable { + if (!FullscreenUI::Initialize()) + return; + + ImGuiFullscreen::AddNotification("UnsupportedAchievements", ACHIEVEMENT_SUMMARY_UNSUPPORTED_TIME, + TRANSLATE_STR("Achievements", "Unsupported Achievements"), + TRANSLATE_PLURAL_STR("Achievements", + "%n achievements are not supported by DuckStation.", + "Achievement popup", num_unsupported), + "images/warning.svg"); + }); + } } // Technically not going through the resource API, but since we're passing this to something else, we can't. @@ -2770,7 +2786,8 @@ void Achievements::DrawAchievementsWindow() static constexpr float alpha = 0.8f; static constexpr float heading_alpha = 0.95f; const float heading_height_unscaled = - (s_state.game_summary.beaten_time > 0 || s_state.game_summary.completed_time) ? 122.0f : 102.0f; + ((s_state.game_summary.beaten_time > 0 || s_state.game_summary.completed_time) ? 122.0f : 102.0f) + + ((s_state.game_summary.num_unsupported_achievements > 0) ? 20.0f : 0.0f); const ImVec4 background = ImGuiFullscreen::ModAlpha(UIStyle.BackgroundColor, alpha); const ImVec4 heading_background = ImGuiFullscreen::ModAlpha(UIStyle.BackgroundColor, heading_alpha); @@ -2821,14 +2838,15 @@ void Achievements::DrawAchievementsWindow() const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFontSize)); if (s_state.game_summary.num_core_achievements > 0) { + text.assign(ICON_EMOJI_UNLOCKED " "); if (s_state.game_summary.num_unlocked_achievements == s_state.game_summary.num_core_achievements) { - text = TRANSLATE_PLURAL_SSTR("Achievements", "You have unlocked all achievements and earned %n points!", - "Point count", s_state.game_summary.points_unlocked); + text.append(TRANSLATE_PLURAL_SSTR("Achievements", "You have unlocked all achievements and earned %n points!", + "Point count", s_state.game_summary.points_unlocked)); } else { - text.format(TRANSLATE_FS("Achievements", + text.append_format(TRANSLATE_FS("Achievements", "You have unlocked {0} of {1} achievements, earning {2} of {3} possible points."), s_state.game_summary.num_unlocked_achievements, s_state.game_summary.num_core_achievements, s_state.game_summary.points_unlocked, s_state.game_summary.points_core); @@ -2836,7 +2854,7 @@ void Achievements::DrawAchievementsWindow() } else { - text.assign(TRANSLATE_SV("Achievements", "This game has no achievements.")); + text.format(ICON_FA_BAN " {}", TRANSLATE_SV("Achievements", "This game has no achievements.")); } top += UIStyle.MediumFontSize + spacing; @@ -2846,9 +2864,26 @@ void Achievements::DrawAchievementsWindow() ImGui::GetColorU32(ImGuiFullscreen::DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text])), text, nullptr, ImVec2(0.0f, 0.0f), 0.0f, &summary_bb); + if (s_state.game_summary.num_unsupported_achievements) + { + text.format("{} {}", ICON_EMOJI_WARNING, + TRANSLATE_PLURAL_SSTR( + "Achievements", "%n achievements are not supported by DuckStation and cannot be unlocked.", + "Unsupported achievement count", s_state.game_summary.num_unsupported_achievements)); + + const ImRect unsupported_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFontSize)); + RenderShadowedTextClipped( + UIStyle.Font, UIStyle.MediumFontSize, UIStyle.BoldFontWeight, unsupported_bb.Min, unsupported_bb.Max, + ImGui::GetColorU32(ImGuiFullscreen::DarkerColor(ImGui::GetStyle().Colors[ImGuiCol_Text])), text, nullptr, + ImVec2(0.0f, 0.0f), 0.0f, &unsupported_bb); + + top += UIStyle.MediumFontSize + spacing; + } + if (s_state.game_summary.beaten_time > 0 || s_state.game_summary.completed_time > 0) { - text.clear(); + text.assign(ICON_EMOJI_CHECKMARK_BUTTON " "); + if (s_state.game_summary.beaten_time > 0) { const std::string beaten_time = @@ -2857,19 +2892,19 @@ void Achievements::DrawAchievementsWindow() { const std::string completion_time = Host::FormatNumber(Host::NumberFormatType::ShortDate, static_cast(s_state.game_summary.beaten_time)); - text.format(TRANSLATE_FS("Achievements", "Game was beaten on {0}, and completed on {1}."), beaten_time, - completion_time); + text.append_format(TRANSLATE_FS("Achievements", "Game was beaten on {0}, and completed on {1}."), beaten_time, + completion_time); } else { - text.format(TRANSLATE_FS("Achievements", "Game was beaten on {0}."), beaten_time); + text.append_format(TRANSLATE_FS("Achievements", "Game was beaten on {0}."), beaten_time); } } else { const std::string completion_time = Host::FormatNumber(Host::NumberFormatType::ShortDate, static_cast(s_state.game_summary.completed_time)); - text.format(TRANSLATE_FS("Achievements", "Game was completed on {0}."), completion_time); + text.append_format(TRANSLATE_FS("Achievements", "Game was completed on {0}."), completion_time); } const ImRect beaten_bb(ImVec2(left, top), ImVec2(right, top + UIStyle.MediumFontSize)); diff --git a/src/duckstation-qt/translations/duckstation-qt_en.ts b/src/duckstation-qt/translations/duckstation-qt_en.ts index a9003b91f..ae1aa1a79 100644 --- a/src/duckstation-qt/translations/duckstation-qt_en.ts +++ b/src/duckstation-qt/translations/duckstation-qt_en.ts @@ -4,8 +4,8 @@ AchievementSettingsWidget - - + + %n seconds %n second @@ -16,7 +16,7 @@ Achievements - + You have unlocked {} of %n achievements Achievement popup @@ -25,7 +25,7 @@ - + and earned {} of %n points Achievement popup @@ -34,7 +34,17 @@ - + + %n achievements are not supported by DuckStation. + Achievement popup + + %n achievement is not supported by DuckStation. + %n achievements are not supported by DuckStation. + + + + + %n achievements Mastery popup @@ -43,8 +53,9 @@ - - + + + %n points Achievement points @@ -53,7 +64,7 @@ - + %n unlocks have not been confirmed by the server. Pause Menu @@ -62,7 +73,7 @@ - + You have unlocked all achievements and earned %n points! Point count @@ -71,7 +82,16 @@ - + + %n achievements are not supported by DuckStation and cannot be unlocked. + Unsupported achievement count + + %n achievement is not supported by DuckStation and cannot be unlocked. + %n achievements are not supported by DuckStation and cannot be unlocked. + + + + This game has %n leaderboards. Leaderboard count @@ -83,7 +103,7 @@ Cheats - + %n game patches are active. OSD Message @@ -92,7 +112,7 @@ - + %n cheats are enabled. This may crash games. OSD Message @@ -101,7 +121,7 @@ - + %n cheats Cheats blocked by hardcore mode @@ -110,7 +130,7 @@ - + %n patches Patches blocked by hardcore mode @@ -122,7 +142,7 @@ EmulationSettingsWidget - + Rewind for %n frame(s), lasting %1 second(s) will require up to %2MB of RAM and %3MB of VRAM. Rewind for %n frame, lasting %1 second(s) will require up to %2MB of RAM and %3MB of VRAM. @@ -133,7 +153,7 @@ GPU_HW - + %n replacement textures found. Replacement texture count @@ -145,8 +165,17 @@ GameList - - + + + %n seconds + + %n second + %n seconds + + + + + %n hours %n hour @@ -154,8 +183,8 @@ - - + + %n minutes %n minute @@ -166,7 +195,7 @@ InputBindingWidget - + %n bindings %n binding @@ -177,7 +206,7 @@ MemoryCardEditorWindow - + %n block(s) free%1 %n block free%1