Achievements: Show primed/challenge-in-progress achievements

This commit is contained in:
Connor McLaughlin 2022-09-21 22:02:54 +10:00
parent a55ca69b87
commit cba4bb4ab2
6 changed files with 143 additions and 28 deletions

View File

@ -9,7 +9,8 @@ namespace Achievements {
#ifdef WITH_CHEEVOS
// Implemented in Host.
extern bool Reset();
extern bool ConfirmSystemReset();
extern void ResetRuntime();
extern bool DoState(StateWrapper& sw);
extern void GameChanged(const std::string& path, CDImage* image);
@ -28,10 +29,11 @@ extern bool ChallengeModeActive();
#else
// Make noops when compiling without cheevos.
static inline bool Reset()
static inline bool ConfirmSystemReset()
{
return true;
}
static inline void ResetRuntime() {}
static inline bool DoState(StateWrapper& sw)
{
return true;

View File

@ -898,6 +898,11 @@ void System::ResetSystem()
if (!IsValid())
return;
#ifdef WITH_CHEEVOS
if (!Achievements::ConfirmSystemReset())
return;
#endif
InternalReset();
ResetPerformanceCounters();
ResetThrottler();
@ -1650,7 +1655,7 @@ bool System::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool u
{
#ifdef WITH_CHEEVOS
// loading an old state without cheevos, so reset the runtime
Achievements::Reset();
Achievements::ResetRuntime();
#endif
}
}
@ -1686,7 +1691,7 @@ void System::InternalReset()
ResetPerformanceCounters();
#ifdef WITH_CHEEVOS
Achievements::Reset();
Achievements::ResetRuntime();
#endif
g_gpu->ResetGraphicsAPIState();

View File

@ -27,6 +27,7 @@
#include "util/cd_image.h"
#include "util/state_wrapper.h"
#include <algorithm>
#include <atomic>
#include <cstdarg>
#include <cstdlib>
#include <ctime>
@ -60,6 +61,10 @@ static unsigned PeekMemory(unsigned address, unsigned num_bytes, void* ud);
static void ActivateLockedAchievements();
static bool ActivateAchievement(Achievement* achievement);
static void DeactivateAchievement(Achievement* achievement);
static void UnlockAchievement(u32 achievement_id, bool add_notification = true);
static void AchievementPrimed(u32 achievement_id);
static void AchievementUnprimed(u32 achievement_id);
static void SubmitLeaderboard(u32 leaderboard_id, int value);
static void SendPing();
static void SendPlaying();
static void UpdateRichPresence();
@ -115,6 +120,7 @@ static std::string s_game_title;
static std::string s_game_icon;
static std::vector<Achievements::Achievement> s_achievements;
static std::vector<Achievements::Leaderboard> s_leaderboards;
static std::atomic<u32> s_primed_achievement_count{0};
static bool s_has_rich_presence = false;
static std::string s_rich_presence_string;
@ -328,6 +334,7 @@ void Achievements::ClearGameInfo(bool clear_achievements, bool clear_leaderboard
DeactivateAchievement(&ach);
s_achievements.pop_back();
}
s_primed_achievement_count.store(0, std::memory_order_release);
}
if (clear_leaderboards)
{
@ -459,7 +466,7 @@ void Achievements::UpdateSettings(const Settings& old_config)
if (!g_settings.achievements_enabled)
{
// we're done here
Shutdown();
OnSystemShutdown();
return;
}
@ -491,7 +498,7 @@ void Achievements::UpdateSettings(const Settings& old_config)
g_settings.achievements_use_first_disc_from_playlist != old_config.achievements_use_first_disc_from_playlist ||
g_settings.achievements_rich_presence != old_config.achievements_rich_presence)
{
Shutdown();
OnSystemShutdown();
Initialize();
return;
}
@ -584,7 +591,7 @@ void Achievements::SetChallengeMode(bool enabled)
GetUserUnlocks();
}
bool Achievements::Shutdown()
bool Achievements::OnSystemShutdown()
{
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
@ -619,29 +626,35 @@ bool Achievements::Shutdown()
return true;
}
bool Achievements::Reset()
bool Achievements::ConfirmSystemReset()
{
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
return RA_ConfirmLoadNewRom(false);
#endif
return true;
}
void Achievements::ResetRuntime()
{
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
{
if (!RA_ConfirmLoadNewRom(false))
return false;
RA_OnReset();
return true;
return;
}
#endif
if (!s_active)
return true;
return;
std::unique_lock lock(s_achievements_mutex);
Log_DevPrint("Resetting rcheevos state...");
rc_runtime_reset(&s_rcheevos_runtime);
return true;
}
void Achievements::OnPaused(bool paused)
void Achievements::OnSystemPaused(bool paused)
{
#ifdef WITH_RAINTEGRATION
if (IsUsingRAIntegration())
@ -1131,6 +1144,7 @@ void Achievements::GetPatchesCallback(s32 status_code, std::string content_type,
cheevo.badge_name = defn.badge_name;
cheevo.locked = true;
cheevo.active = false;
cheevo.primed = false;
cheevo.points = defn.points;
cheevo.category = static_cast<AchievementCategory>(defn.category);
s_achievements.push_back(std::move(cheevo));
@ -1352,7 +1366,9 @@ void Achievements::GameChanged(const std::string& path, CDImage* image)
if (s_http_downloader->HasAnyRequests())
{
Host::DisplayLoadingScreen("Downloading achievements data...");
if (image && System::IsValid())
Host::DisplayLoadingScreen("Downloading achievements data...");
s_http_downloader->WaitForAllRequests();
}
@ -1655,6 +1671,12 @@ void Achievements::DeactivateAchievement(Achievement* achievement)
rc_runtime_deactivate_achievement(&s_rcheevos_runtime, achievement->id);
achievement->active = false;
if (achievement->primed)
{
achievement->primed = false;
s_primed_achievement_count.fetch_sub(std::memory_order_acq_rel);
}
Log_DevPrintf("Deactivated achievement %s (%u)", achievement->title.c_str(), achievement->id);
}
@ -1711,6 +1733,8 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, std::string conten
void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /* = true*/)
{
std::unique_lock lock(s_achievements_mutex);
Achievement* achievement = GetMutableAchievementByID(achievement_id);
if (!achievement)
{
@ -1783,6 +1807,8 @@ void Achievements::SubmitLeaderboard(u32 leaderboard_id, int value)
return;
}
std::unique_lock lock(s_achievements_mutex);
s_submitting_lboard_id = leaderboard_id;
RAPIRequest<rc_api_submit_lboard_entry_request_t, rc_api_init_submit_lboard_entry_request> request;
@ -1794,6 +1820,28 @@ void Achievements::SubmitLeaderboard(u32 leaderboard_id, int value)
request.Send(SubmitLeaderboardCallback);
}
void Achievements::AchievementPrimed(u32 achievement_id)
{
std::unique_lock lock(s_achievements_mutex);
Achievement* achievement = GetMutableAchievementByID(achievement_id);
if (!achievement || achievement->primed)
return;
achievement->primed = true;
s_primed_achievement_count.fetch_add(std::memory_order_acq_rel);
}
void Achievements::AchievementUnprimed(u32 achievement_id)
{
std::unique_lock lock(s_achievements_mutex);
Achievement* achievement = GetMutableAchievementByID(achievement_id);
if (!achievement || !achievement->primed)
return;
achievement->primed = false;
s_primed_achievement_count.fetch_sub(std::memory_order_acq_rel);
}
std::pair<u32, u32> Achievements::GetAchievementProgress(const Achievement& achievement)
{
std::pair<u32, u32> result;
@ -1841,6 +1889,12 @@ std::string Achievements::GetAchievementBadgeURL(const Achievement& achievement)
return request.GetURL();
}
u32 Achievements::GetPrimedAchievementCount()
{
// Relaxed is fine here, worst that happens is we draw the triggers one frame late.
return s_primed_achievement_count.load(std::memory_order_relaxed);
}
void Achievements::CheevosEventHandler(const rc_runtime_event_t* runtime_event)
{
static const char* events[] = {"RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED", "RC_RUNTIME_EVENT_ACHIEVEMENT_PAUSED",
@ -1853,10 +1907,27 @@ void Achievements::CheevosEventHandler(const rc_runtime_event_t* runtime_event)
((unsigned)runtime_event->type >= countof(events)) ? "unknown" : events[(unsigned)runtime_event->type];
Log_DevPrintf("Cheevos Event %s for %u", event_text, runtime_event->id);
if (runtime_event->type == RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED)
UnlockAchievement(runtime_event->id);
else if (runtime_event->type == RC_RUNTIME_EVENT_LBOARD_TRIGGERED)
SubmitLeaderboard(runtime_event->id, runtime_event->value);
switch (runtime_event->type)
{
case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED:
UnlockAchievement(runtime_event->id);
break;
case RC_RUNTIME_EVENT_ACHIEVEMENT_PRIMED:
AchievementPrimed(runtime_event->id);
break;
case RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED:
AchievementUnprimed(runtime_event->id);
break;
case RC_RUNTIME_EVENT_LBOARD_TRIGGERED:
SubmitLeaderboard(runtime_event->id, runtime_event->value);
break;
default:
break;
}
}
unsigned Achievements::PeekMemory(unsigned address, unsigned num_bytes, void* ud)

View File

@ -13,7 +13,7 @@ class CDImage;
class StateWrapper;
namespace Achievements {
enum class AchievementCategory : u32
enum class AchievementCategory : u8
{
Local = 0,
Core = 3,
@ -36,6 +36,7 @@ struct Achievement
AchievementCategory category;
bool locked;
bool active;
bool primed;
};
struct Leaderboard
@ -85,13 +86,13 @@ void Initialize();
void UpdateSettings(const Settings& old_config);
/// Called when the system is being reset. If it returns false, the reset should be aborted.
bool Reset();
bool ConfirmSystemReset();
/// Called when the system is being shut down. If Shutdown() returns false, the shutdown should be aborted.
bool Shutdown();
bool OnSystemShutdown();
/// Called when the system is being paused and resumed.
void OnPaused(bool paused);
void OnSystemPaused(bool paused);
/// Called once a frame at vsync time on the CPU thread.
void FrameUpdate();
@ -141,6 +142,7 @@ std::optional<bool> TryEnumerateLeaderboardEntries(u32 id, std::function<bool(co
const Leaderboard* GetLeaderboardByID(u32 id);
u32 GetLeaderboardCount();
bool IsLeaderboardTimeType(const Leaderboard& leaderboard);
u32 GetPrimedAchievementCount();
const Achievement* GetAchievementByID(u32 id);
std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement);
@ -148,9 +150,6 @@ std::string GetAchievementProgressText(const Achievement& achievement);
const std::string& GetAchievementBadgePath(const Achievement& achievement, bool download_if_missing = true);
std::string GetAchievementBadgeURL(const Achievement& achievement);
void UnlockAchievement(u32 achievement_id, bool add_notification = true);
void SubmitLeaderboard(u32 leaderboard_id, int value);
#ifdef WITH_RAINTEGRATION
void SwitchToRAIntegration();

View File

@ -110,7 +110,7 @@ void CommonHost::Shutdown()
#endif
#ifdef WITH_CHEEVOS
Achievements::Shutdown();
Achievements::OnSystemShutdown();
#endif
InputManager::CloseSources();

View File

@ -430,6 +430,7 @@ static GameListPage s_game_list_page = GameListPage::Grid;
//////////////////////////////////////////////////////////////////////////
static void DrawAchievementsWindow();
static void DrawAchievement(const Achievements::Achievement& cheevo);
static void DrawPrimedAchievements();
static void DrawLeaderboardsWindow();
static void DrawLeaderboardListEntry(const Achievements::Leaderboard& lboard);
static void DrawLeaderboardEntry(const Achievements::LeaderboardEntry& lbEntry, float rank_column_width,
@ -710,6 +711,12 @@ void FullscreenUI::Render()
ImGuiFullscreen::BeginLayout();
#ifdef WITH_CHEEVOS
// Primed achievements must come first, because we don't want the pause screen to be behind them.
if (Achievements::GetPrimedAchievementCount() > 0)
DrawPrimedAchievements();
#endif
switch (s_current_main_window)
{
case MainWindowType::Landing:
@ -5556,6 +5563,37 @@ void FullscreenUI::DrawAchievementsWindow()
EndFullscreenWindow();
}
void FullscreenUI::DrawPrimedAchievements()
{
const ImVec2 image_size(LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT, LAYOUT_MENU_BUTTON_HEIGHT));
const float spacing = LayoutScale(10.0f);
const float padding = LayoutScale(10.0f);
const ImGuiIO& io = ImGui::GetIO();
const float x_advance = image_size.x + spacing;
ImVec2 position(io.DisplaySize.x - padding - image_size.x, io.DisplaySize.y - padding - image_size.y);
auto lock = Achievements::GetLock();
Achievements::EnumerateAchievements(
[&image_size, &x_advance, &position](const Achievements::Achievement& achievement) {
if (!achievement.primed)
return true;
const std::string& badge_path = Achievements::GetAchievementBadgePath(achievement);
if (badge_path.empty())
return true;
HostDisplayTexture* badge = GetCachedTextureAsync(badge_path.c_str());
if (!badge)
return true;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
dl->AddImage(badge->GetHandle(), position, position + image_size);
position.x -= x_advance;
return true;
});
}
bool FullscreenUI::OpenLeaderboardsWindow()
{
if (!System::IsValid() || !Achievements::HasActiveGame() || Achievements::GetLeaderboardCount() == 0 || !Initialize())