Achievements: Show primed/challenge-in-progress achievements
This commit is contained in:
parent
a55ca69b87
commit
cba4bb4ab2
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
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)
|
||||
switch (runtime_event->type)
|
||||
{
|
||||
case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED:
|
||||
UnlockAchievement(runtime_event->id);
|
||||
else if (runtime_event->type == RC_RUNTIME_EVENT_LBOARD_TRIGGERED)
|
||||
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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ void CommonHost::Shutdown()
|
|||
#endif
|
||||
|
||||
#ifdef WITH_CHEEVOS
|
||||
Achievements::Shutdown();
|
||||
Achievements::OnSystemShutdown();
|
||||
#endif
|
||||
|
||||
InputManager::CloseSources();
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in New Issue