From 8fbc5960e7b14babe5eaa113e59814451c1486c6 Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Fri, 20 Oct 2023 08:35:41 -0400 Subject: [PATCH 1/2] Handle Achievement Un/Primed Events When an achievement is "primed", a challenge is active, for example completing a portion of the game in under a time limit or without taking damage or using certain items. This is stored in a map in the Achievement Manager (and removed when the achievement is unprimed) so a later commit can display it on screen. --- Source/Core/Core/AchievementManager.cpp | 39 +++++++++++++++++++++++++ Source/Core/Core/AchievementManager.h | 11 +++++++ 2 files changed, 50 insertions(+) diff --git a/Source/Core/Core/AchievementManager.cpp b/Source/Core/Core/AchievementManager.cpp index ebb83ac801..2862fbdac3 100644 --- a/Source/Core/Core/AchievementManager.cpp +++ b/Source/Core/Core/AchievementManager.cpp @@ -710,6 +710,12 @@ void AchievementManager::AchievementEventHandler(const rc_runtime_event_t* runti case RC_RUNTIME_EVENT_ACHIEVEMENT_PROGRESS_UPDATED: HandleAchievementProgressUpdatedEvent(runtime_event); break; + case RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED: + HandleAchievementPrimedEvent(runtime_event); + break; + case RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED: + HandleAchievementUnprimedEvent(runtime_event); + break; case RC_RUNTIME_EVENT_LBOARD_STARTED: HandleLeaderboardStartedEvent(runtime_event); break; @@ -857,6 +863,11 @@ void AchievementManager::SetDisabled(bool disable) } }; +const AchievementManager::NamedIconMap& AchievementManager::GetChallengeIcons() const +{ + return m_active_challenges; +} + void AchievementManager::CloseGame() { { @@ -1467,6 +1478,34 @@ void AchievementManager::HandleAchievementProgressUpdatedEvent( nullptr); } +void AchievementManager::HandleAchievementPrimedEvent(const rc_runtime_event_t* runtime_event) +{ + if (!Config::Get(Config::RA_BADGES_ENABLED)) + return; + auto it = m_unlock_map.find(runtime_event->id); + if (it == m_unlock_map.end()) + { + ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid achievement primed event with id {}.", runtime_event->id); + return; + } + m_active_challenges[it->second.unlocked_badge.name] = + DecodeBadgeToOSDIcon(it->second.unlocked_badge.badge); +} + +void AchievementManager::HandleAchievementUnprimedEvent(const rc_runtime_event_t* runtime_event) +{ + if (!Config::Get(Config::RA_BADGES_ENABLED)) + return; + auto it = m_unlock_map.find(runtime_event->id); + if (it == m_unlock_map.end()) + { + ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid achievement unprimed event with id {}.", + runtime_event->id); + return; + } + m_active_challenges.erase(it->second.unlocked_badge.name); +} + void AchievementManager::HandleLeaderboardStartedEvent(const rc_runtime_event_t* runtime_event) { for (u32 ix = 0; ix < m_game_data.num_leaderboards; ix++) diff --git a/Source/Core/Core/AchievementManager.h b/Source/Core/Core/AchievementManager.h index 7d93b7d781..3a260f9e8b 100644 --- a/Source/Core/Core/AchievementManager.h +++ b/Source/Core/Core/AchievementManager.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,11 @@ namespace Core class System; } +namespace OSD +{ +struct Icon; +} + class AchievementManager { public: @@ -61,6 +67,7 @@ public: static constexpr size_t RP_SIZE = 256; using RichPresence = std::array; using Badge = std::vector; + using NamedIconMap = std::map, std::less<>>; struct BadgeStatus { @@ -138,6 +145,7 @@ public: RichPresence GetRichPresence(); bool IsDisabled() const { return m_disabled; }; void SetDisabled(bool disabled); + const NamedIconMap& GetChallengeIcons() const; void CloseGame(); void Logout(); @@ -180,6 +188,8 @@ private: void HandleAchievementTriggeredEvent(const rc_runtime_event_t* runtime_event); void HandleAchievementProgressUpdatedEvent(const rc_runtime_event_t* runtime_event); + void HandleAchievementPrimedEvent(const rc_runtime_event_t* runtime_event); + void HandleAchievementUnprimedEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardStartedEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event); void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event); @@ -210,6 +220,7 @@ private: std::unordered_map m_unlock_map; std::unordered_map m_leaderboard_map; + NamedIconMap m_active_challenges; Common::WorkQueueThread> m_queue; Common::WorkQueueThread> m_image_queue; From caa729f84a90b562f4b93e30344d2dced4d4f85a Mon Sep 17 00:00:00 2001 From: LillyJadeKatrin Date: Fri, 20 Oct 2023 08:38:20 -0400 Subject: [PATCH 2/2] Display Active Challenges On Screen The active challenges, aka the primed achievements, are displayed on screen as a series of icons in the bottom right corner of the screen via OnScreenUI. --- Source/Core/VideoCommon/OnScreenUI.cpp | 63 ++++++++++++++++++++++++++ Source/Core/VideoCommon/OnScreenUI.h | 7 +++ 2 files changed, 70 insertions(+) diff --git a/Source/Core/VideoCommon/OnScreenUI.cpp b/Source/Core/VideoCommon/OnScreenUI.cpp index 1919149629..b2f3e86682 100644 --- a/Source/Core/VideoCommon/OnScreenUI.cpp +++ b/Source/Core/VideoCommon/OnScreenUI.cpp @@ -7,6 +7,7 @@ #include "Common/Profiler.h" #include "Common/Timer.h" +#include "Core/AchievementManager.h" #include "Core/Config/MainSettings.h" #include "Core/Config/NetplaySettings.h" #include "Core/Movie.h" @@ -326,6 +327,65 @@ void OnScreenUI::DrawDebugText() ImGui::TextUnformatted(profile_output.c_str()); } +#ifdef USE_RETRO_ACHIEVEMENTS +void OnScreenUI::DrawChallenges() +{ + std::lock_guard lg{*AchievementManager::GetInstance()->GetLock()}; + const AchievementManager::NamedIconMap& challenge_icons = + AchievementManager::GetInstance()->GetChallengeIcons(); + + const std::string window_name = "Challenges"; + + u32 sum_of_icon_heights = 0; + u32 max_icon_width = 0; + for (const auto& [name, icon] : challenge_icons) + { + // These *should* all be the same square size but you never know. + if (icon->width > max_icon_width) + max_icon_width = icon->width; + sum_of_icon_heights += icon->height; + } + ImGui::SetNextWindowPos( + ImVec2(ImGui::GetIO().DisplaySize.x - 20.f * m_backbuffer_scale - max_icon_width, + ImGui::GetIO().DisplaySize.y - 20.f * m_backbuffer_scale - sum_of_icon_heights)); + ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); + if (ImGui::Begin(window_name.c_str(), nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) + { + for (const auto& [name, icon] : challenge_icons) + { + if (m_challenge_texture_map.find(name) != m_challenge_texture_map.end()) + continue; + const u32 width = icon->width; + const u32 height = icon->height; + TextureConfig tex_config(width, height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0); + auto res = m_challenge_texture_map.insert_or_assign(name, g_gfx->CreateTexture(tex_config)); + res.first->second->Load(0, width, height, width, icon->rgba_data.data(), + sizeof(u32) * width * height); + } + for (auto& [name, texture] : m_challenge_texture_map) + { + auto icon_itr = challenge_icons.find(name); + if (icon_itr == challenge_icons.end()) + { + m_challenge_texture_map.erase(name); + continue; + } + if (texture) + { + ImGui::Image(texture.get(), ImVec2(static_cast(icon_itr->second->width), + static_cast(icon_itr->second->height))); + } + } + } + + ImGui::End(); +} +#endif // USE_RETRO_ACHIEVEMENTS + void OnScreenUI::Finalize() { auto lock = GetImGuiLock(); @@ -333,6 +393,9 @@ void OnScreenUI::Finalize() g_perf_metrics.DrawImGuiStats(m_backbuffer_scale); DrawDebugText(); OSD::DrawMessages(); +#ifdef USE_RETRO_ACHIEVEMENTS + DrawChallenges(); +#endif // USE_RETRO_ACHIEVEMENTS ImGui::Render(); } diff --git a/Source/Core/VideoCommon/OnScreenUI.h b/Source/Core/VideoCommon/OnScreenUI.h index f937027403..9b7aa0d3f8 100644 --- a/Source/Core/VideoCommon/OnScreenUI.h +++ b/Source/Core/VideoCommon/OnScreenUI.h @@ -61,6 +61,9 @@ public: private: void DrawDebugText(); +#ifdef USE_RETRO_ACHIEVEMENTS + void DrawChallenges(); +#endif // USE_RETRO_ACHIEVEMENTS // ImGui resources. std::unique_ptr m_imgui_vertex_format; @@ -74,6 +77,10 @@ private: u32 m_backbuffer_height = 1; float m_backbuffer_scale = 1.0; +#ifdef USE_RETRO_ACHIEVEMENTS + std::map, std::less<>> m_challenge_texture_map; +#endif // USE_RETRO_ACHIEVEMENTS + bool m_ready = false; }; } // namespace VideoCommon