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; 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