Merge pull request #12639 from LillyJadeKatrin/retroachievements-client
Refactor AchievementManager from rc_runtime to rc_client
This commit is contained in:
commit
b71fdef356
File diff suppressed because it is too large
Load Diff
|
@ -15,9 +15,11 @@
|
|||
|
||||
#include <rcheevos/include/rc_api_runtime.h>
|
||||
#include <rcheevos/include/rc_api_user.h>
|
||||
#include <rcheevos/include/rc_client.h>
|
||||
#include <rcheevos/include/rc_runtime.h>
|
||||
|
||||
#include "Common/Event.h"
|
||||
#include "Common/HttpRequest.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
|
||||
|
@ -35,30 +37,7 @@ struct Icon;
|
|||
class AchievementManager
|
||||
{
|
||||
public:
|
||||
enum class ResponseType
|
||||
{
|
||||
SUCCESS,
|
||||
NOT_ENABLED,
|
||||
MANAGER_NOT_INITIALIZED,
|
||||
INVALID_REQUEST,
|
||||
INVALID_CREDENTIALS,
|
||||
CONNECTION_FAILED,
|
||||
MALFORMED_OBJECT,
|
||||
EXPIRED_CONTEXT,
|
||||
UNKNOWN_FAILURE
|
||||
};
|
||||
using ResponseCallback = std::function<void(ResponseType)>;
|
||||
using UpdateCallback = std::function<void()>;
|
||||
|
||||
struct PointSpread
|
||||
{
|
||||
u32 total_count;
|
||||
u32 total_points;
|
||||
u32 hard_unlocks;
|
||||
u32 hard_points;
|
||||
u32 soft_unlocks;
|
||||
u32 soft_points;
|
||||
};
|
||||
using BadgeNameFunction = std::function<std::string(const AchievementManager&)>;
|
||||
|
||||
static constexpr size_t HASH_SIZE = 33;
|
||||
using Hash = std::array<char, HASH_SIZE>;
|
||||
|
@ -70,6 +49,7 @@ public:
|
|||
using RichPresence = std::array<char, RP_SIZE>;
|
||||
using Badge = std::vector<u8>;
|
||||
using NamedIconMap = std::map<std::string, std::unique_ptr<OSD::Icon>, std::less<>>;
|
||||
static constexpr size_t MAX_DISPLAYED_LBOARDS = 4;
|
||||
|
||||
struct BadgeStatus
|
||||
{
|
||||
|
@ -77,22 +57,6 @@ public:
|
|||
Badge badge{};
|
||||
};
|
||||
|
||||
struct UnlockStatus
|
||||
{
|
||||
AchievementId game_data_index = 0;
|
||||
enum class UnlockType
|
||||
{
|
||||
LOCKED,
|
||||
SOFTCORE,
|
||||
HARDCORE
|
||||
} remote_unlock_status = UnlockType::LOCKED;
|
||||
u32 session_unlock_count = 0;
|
||||
u32 points = 0;
|
||||
BadgeStatus locked_badge;
|
||||
BadgeStatus unlocked_badge;
|
||||
u32 category = RC_ACHIEVEMENT_CATEGORY_CORE;
|
||||
};
|
||||
|
||||
static constexpr std::string_view GRAY = "transparent";
|
||||
static constexpr std::string_view GOLD = "#FFD700";
|
||||
static constexpr std::string_view BLUE = "#0B71C1";
|
||||
|
@ -112,43 +76,50 @@ public:
|
|||
std::unordered_map<u32, LeaderboardEntry> entries;
|
||||
};
|
||||
|
||||
struct UpdatedItems
|
||||
{
|
||||
bool all = false;
|
||||
bool player_icon = false;
|
||||
bool game_icon = false;
|
||||
bool all_achievements = false;
|
||||
std::set<AchievementId> achievements{};
|
||||
bool all_leaderboards = false;
|
||||
std::set<AchievementId> leaderboards{};
|
||||
bool rich_presence = false;
|
||||
};
|
||||
using UpdateCallback = std::function<void(const UpdatedItems&)>;
|
||||
|
||||
static AchievementManager& GetInstance();
|
||||
void Init();
|
||||
void SetUpdateCallback(UpdateCallback callback);
|
||||
ResponseType Login(const std::string& password);
|
||||
void LoginAsync(const std::string& password, const ResponseCallback& callback);
|
||||
bool IsLoggedIn() const;
|
||||
void HashGame(const std::string& file_path, const ResponseCallback& callback);
|
||||
void HashGame(const DiscIO::Volume* volume, const ResponseCallback& callback);
|
||||
void Login(const std::string& password);
|
||||
bool HasAPIToken() const;
|
||||
void LoadGame(const std::string& file_path, const DiscIO::Volume* volume);
|
||||
bool IsGameLoaded() const;
|
||||
|
||||
void LoadUnlockData(const ResponseCallback& callback);
|
||||
void ActivateDeactivateAchievements();
|
||||
void ActivateDeactivateLeaderboards();
|
||||
void ActivateDeactivateRichPresence();
|
||||
void FetchBadges();
|
||||
void FetchPlayerBadge();
|
||||
void FetchGameBadges();
|
||||
|
||||
void DoFrame();
|
||||
u32 MemoryPeeker(u32 address, u32 num_bytes, void* ud);
|
||||
void AchievementEventHandler(const rc_runtime_event_t* runtime_event);
|
||||
|
||||
std::recursive_mutex& GetLock();
|
||||
void SetHardcoreMode();
|
||||
bool IsHardcoreModeActive() const;
|
||||
std::string GetPlayerDisplayName() const;
|
||||
void SetSpectatorMode();
|
||||
std::string_view GetPlayerDisplayName() const;
|
||||
u32 GetPlayerScore() const;
|
||||
const BadgeStatus& GetPlayerBadge() const;
|
||||
std::string GetGameDisplayName() const;
|
||||
PointSpread TallyScore() const;
|
||||
std::string_view GetGameDisplayName() const;
|
||||
rc_client_t* GetClient();
|
||||
rc_api_fetch_game_data_response_t* GetGameData();
|
||||
const BadgeStatus& GetGameBadge() const;
|
||||
const UnlockStatus& GetUnlockStatus(AchievementId achievement_id) const;
|
||||
AchievementManager::ResponseType GetAchievementProgress(AchievementId achievement_id, u32* value,
|
||||
u32* target);
|
||||
const std::unordered_map<AchievementId, LeaderboardStatus>& GetLeaderboardsInfo() const;
|
||||
const BadgeStatus& GetAchievementBadge(AchievementId id, bool locked) const;
|
||||
const LeaderboardStatus* GetLeaderboardInfo(AchievementId leaderboard_id);
|
||||
RichPresence GetRichPresence() const;
|
||||
bool IsDisabled() const { return m_disabled; };
|
||||
void SetDisabled(bool disabled);
|
||||
const NamedIconMap& GetChallengeIcons() const;
|
||||
std::vector<std::string> GetActiveLeaderboards() const;
|
||||
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
void CloseGame();
|
||||
void Logout();
|
||||
|
@ -163,6 +134,8 @@ private:
|
|||
std::unique_ptr<DiscIO::Volume> volume;
|
||||
};
|
||||
|
||||
const BadgeStatus m_default_badge;
|
||||
|
||||
static void* FilereaderOpenByFilepath(const char* path_utf8);
|
||||
static void* FilereaderOpenByVolume(const char* path_utf8);
|
||||
static void FilereaderSeek(void* file_handle, int64_t offset, int origin);
|
||||
|
@ -170,47 +143,50 @@ private:
|
|||
static size_t FilereaderRead(void* file_handle, void* buffer, size_t requested_bytes);
|
||||
static void FilereaderClose(void* file_handle);
|
||||
|
||||
ResponseType VerifyCredentials(const std::string& password);
|
||||
ResponseType ResolveHash(const Hash& game_hash, u32* game_id);
|
||||
void LoadGameSync(const ResponseCallback& callback);
|
||||
ResponseType StartRASession();
|
||||
ResponseType FetchGameData();
|
||||
ResponseType FetchUnlockData(bool hardcore);
|
||||
ResponseType FetchBoardInfo(AchievementId leaderboard_id);
|
||||
static void LoginCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata);
|
||||
|
||||
void FetchBoardInfo(AchievementId leaderboard_id);
|
||||
|
||||
std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; };
|
||||
|
||||
void ActivateDeactivateAchievement(AchievementId id, bool enabled, bool unofficial, bool encore);
|
||||
void GenerateRichPresence(const Core::CPUThreadGuard& guard);
|
||||
|
||||
ResponseType AwardAchievement(AchievementId achievement_id);
|
||||
ResponseType SubmitLeaderboard(AchievementId leaderboard_id, int value);
|
||||
ResponseType PingRichPresence(const RichPresence& rich_presence);
|
||||
|
||||
static void LoadGameCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata);
|
||||
static void ChangeMediaCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata);
|
||||
void DisplayWelcomeMessage();
|
||||
|
||||
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);
|
||||
static void LeaderboardEntriesCallback(int result, const char* error_message,
|
||||
rc_client_leaderboard_entry_list_t* list,
|
||||
rc_client_t* client, void* userdata);
|
||||
|
||||
template <typename RcRequest, typename RcResponse>
|
||||
ResponseType Request(RcRequest rc_request, RcResponse* rc_response,
|
||||
const std::function<int(rc_api_request_t*, const RcRequest*)>& init_request,
|
||||
const std::function<int(RcResponse*, const char*)>& process_response);
|
||||
ResponseType RequestImage(rc_api_fetch_image_request_t rc_request, Badge* rc_response);
|
||||
static void HandleAchievementTriggeredEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardStartedEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardFailedEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardSubmittedEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardTrackerShowEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardTrackerHideEvent(const rc_client_event_t* client_event);
|
||||
static void HandleAchievementChallengeIndicatorShowEvent(const rc_client_event_t* client_event);
|
||||
static void HandleAchievementChallengeIndicatorHideEvent(const rc_client_event_t* client_event);
|
||||
static void HandleAchievementProgressIndicatorShowEvent(const rc_client_event_t* client_event);
|
||||
static void HandleGameCompletedEvent(const rc_client_event_t* client_event, rc_client_t* client);
|
||||
static void HandleResetEvent(const rc_client_event_t* client_event);
|
||||
static void HandleServerErrorEvent(const rc_client_event_t* client_event);
|
||||
|
||||
static void Request(const rc_api_request_t* request, rc_client_server_callback_t callback,
|
||||
void* callback_data, rc_client_t* client);
|
||||
static u32 MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
|
||||
void FetchBadge(BadgeStatus* badge, u32 badge_type, const BadgeNameFunction function,
|
||||
const UpdatedItems callback_data);
|
||||
static void EventHandler(const rc_client_event_t* event, rc_client_t* client);
|
||||
|
||||
rc_runtime_t m_runtime{};
|
||||
rc_client_t* m_client{};
|
||||
Core::System* m_system{};
|
||||
bool m_is_runtime_initialized = false;
|
||||
UpdateCallback m_update_callback = [] {};
|
||||
UpdateCallback m_update_callback = [](const UpdatedItems&) {};
|
||||
std::unique_ptr<DiscIO::Volume> m_loading_volume;
|
||||
bool m_disabled = false;
|
||||
std::string m_display_name;
|
||||
u32 m_player_score = 0;
|
||||
BadgeStatus m_player_badge;
|
||||
Hash m_game_hash{};
|
||||
u32 m_game_id = 0;
|
||||
|
@ -218,12 +194,14 @@ private:
|
|||
bool m_is_game_loaded = false;
|
||||
u32 m_framecount = 0;
|
||||
BadgeStatus m_game_badge;
|
||||
std::unordered_map<AchievementId, BadgeStatus> m_unlocked_badges;
|
||||
std::unordered_map<AchievementId, BadgeStatus> m_locked_badges;
|
||||
RichPresence m_rich_presence;
|
||||
time_t m_last_ping_time = 0;
|
||||
std::chrono::steady_clock::time_point m_last_rp_time = std::chrono::steady_clock::now();
|
||||
|
||||
std::unordered_map<AchievementId, UnlockStatus> m_unlock_map;
|
||||
std::unordered_map<AchievementId, LeaderboardStatus> m_leaderboard_map;
|
||||
NamedIconMap m_active_challenges;
|
||||
std::vector<rc_client_leaderboard_tracker_t> m_active_leaderboards;
|
||||
|
||||
Common::WorkQueueThread<std::function<void()>> m_queue;
|
||||
Common::WorkQueueThread<std::function<void()>> m_image_queue;
|
||||
|
|
|
@ -576,8 +576,7 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
|
|||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().HashGame(executable.path,
|
||||
[](AchievementManager::ResponseType r_type) {});
|
||||
AchievementManager::GetInstance().LoadGame(executable.path, nullptr);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (!executable.reader->LoadIntoMemory(system))
|
||||
|
|
|
@ -167,7 +167,7 @@ bool BootCore(Core::System& system, std::unique_ptr<BootParameters> boot,
|
|||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().SetDisabled(false);
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
const bool load_ipl = !system.IsWii() && !Config::Get(Config::MAIN_SKIP_IPL) &&
|
||||
|
|
|
@ -15,20 +15,16 @@ const Info<bool> RA_ENABLED{{System::Achievements, "Achievements", "Enabled"}, f
|
|||
const Info<std::string> RA_HOST_URL{{System::Achievements, "Achievements", "HostUrl"}, ""};
|
||||
const Info<std::string> RA_USERNAME{{System::Achievements, "Achievements", "Username"}, ""};
|
||||
const Info<std::string> RA_API_TOKEN{{System::Achievements, "Achievements", "ApiToken"}, ""};
|
||||
const Info<bool> RA_ACHIEVEMENTS_ENABLED{
|
||||
{System::Achievements, "Achievements", "AchievementsEnabled"}, false};
|
||||
const Info<bool> RA_LEADERBOARDS_ENABLED{
|
||||
{System::Achievements, "Achievements", "LeaderboardsEnabled"}, false};
|
||||
const Info<bool> RA_RICH_PRESENCE_ENABLED{
|
||||
{System::Achievements, "Achievements", "RichPresenceEnabled"}, false};
|
||||
const Info<bool> RA_HARDCORE_ENABLED{{System::Achievements, "Achievements", "HardcoreEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_PROGRESS_ENABLED{{System::Achievements, "Achievements", "ProgressEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_BADGES_ENABLED{{System::Achievements, "Achievements", "BadgesEnabled"}, false};
|
||||
true};
|
||||
const Info<bool> RA_UNOFFICIAL_ENABLED{{System::Achievements, "Achievements", "UnofficialEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_ENCORE_ENABLED{{System::Achievements, "Achievements", "EncoreEnabled"}, false};
|
||||
const Info<bool> RA_SPECTATOR_ENABLED{{System::Achievements, "Achievements", "SpectatorEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_PROGRESS_ENABLED{{System::Achievements, "Achievements", "ProgressEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_BADGES_ENABLED{{System::Achievements, "Achievements", "BadgesEnabled"}, false};
|
||||
} // namespace Config
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -14,14 +14,12 @@ extern const Info<bool> RA_ENABLED;
|
|||
extern const Info<std::string> RA_USERNAME;
|
||||
extern const Info<std::string> RA_HOST_URL;
|
||||
extern const Info<std::string> RA_API_TOKEN;
|
||||
extern const Info<bool> RA_ACHIEVEMENTS_ENABLED;
|
||||
extern const Info<bool> RA_LEADERBOARDS_ENABLED;
|
||||
extern const Info<bool> RA_RICH_PRESENCE_ENABLED;
|
||||
extern const Info<bool> RA_HARDCORE_ENABLED;
|
||||
extern const Info<bool> RA_PROGRESS_ENABLED;
|
||||
extern const Info<bool> RA_BADGES_ENABLED;
|
||||
extern const Info<bool> RA_UNOFFICIAL_ENABLED;
|
||||
extern const Info<bool> RA_ENCORE_ENABLED;
|
||||
extern const Info<bool> RA_SPECTATOR_ENABLED;
|
||||
extern const Info<bool> RA_PROGRESS_ENABLED;
|
||||
extern const Info<bool> RA_BADGES_ENABLED;
|
||||
} // namespace Config
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -171,7 +171,7 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
|
|||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (game_id != "00000000")
|
||||
AchievementManager::GetInstance().SetDisabled(true);
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (game_id == "00000000")
|
||||
|
|
|
@ -290,7 +290,6 @@ void Stop(Core::System& system) // - Hammertime!
|
|||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
AchievementManager::GetInstance().SetDisabled(false);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
s_is_stopping = true;
|
||||
|
|
|
@ -399,8 +399,7 @@ void DVDInterface::SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
|
|||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().HashGame(disc.get(),
|
||||
[](AchievementManager::ResponseType r_type) {});
|
||||
AchievementManager::GetInstance().LoadGame("", disc.get());
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
// Assume that inserting a disc requires having an empty disc before
|
||||
|
|
|
@ -481,7 +481,7 @@ bool ESDevice::LaunchPPCTitle(u64 title_id)
|
|||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
INFO_LOG_FMT(ACHIEVEMENTS,
|
||||
"WAD and NAND formats not currently supported by Achievement Manager.");
|
||||
AchievementManager::GetInstance().SetDisabled(true);
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
core_timing.RemoveEvent(s_bootstrap_ppc_for_launch_event);
|
||||
|
|
|
@ -98,7 +98,7 @@ static size_t s_state_writes_in_queue;
|
|||
static std::condition_variable s_state_write_queue_is_empty;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
constexpr u32 STATE_VERSION = 167; // Last changed in PR 12494
|
||||
constexpr u32 STATE_VERSION = 168; // Last changed in PR 12639
|
||||
|
||||
// Increase this if the StateExtendedHeader definition changes
|
||||
constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217
|
||||
|
@ -198,6 +198,10 @@ static void DoState(Core::System& system, PointerWrap& p)
|
|||
p.DoMarker("Wiimote");
|
||||
Gecko::DoState(p);
|
||||
p.DoMarker("Gecko");
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().DoState(p);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
}
|
||||
|
||||
void LoadFromBuffer(Core::System& system, std::vector<u8>& buffer)
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
#include "DolphinQt/Achievements/AchievementBox.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QProgressBar>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include <rcheevos/include/rc_api_runtime.h>
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
|
||||
#include "DolphinQt/QtUtils/FromStdString.h"
|
||||
|
||||
AchievementBox::AchievementBox(QWidget* parent, rc_client_achievement_t* achievement)
|
||||
: QGroupBox(parent), m_achievement(achievement)
|
||||
{
|
||||
const auto& instance = AchievementManager::GetInstance();
|
||||
if (!instance.IsGameLoaded())
|
||||
return;
|
||||
|
||||
m_badge = new QLabel();
|
||||
QLabel* title = new QLabel(QString::fromUtf8(achievement->title, strlen(achievement->title)));
|
||||
QLabel* description =
|
||||
new QLabel(QString::fromUtf8(achievement->description, strlen(achievement->description)));
|
||||
QLabel* points = new QLabel(tr("%1 points").arg(achievement->points));
|
||||
m_status = new QLabel();
|
||||
m_progress_bar = new QProgressBar();
|
||||
QSizePolicy sp_retain = m_progress_bar->sizePolicy();
|
||||
sp_retain.setRetainSizeWhenHidden(true);
|
||||
m_progress_bar->setSizePolicy(sp_retain);
|
||||
|
||||
QVBoxLayout* a_col_right = new QVBoxLayout();
|
||||
a_col_right->addWidget(title);
|
||||
a_col_right->addWidget(description);
|
||||
a_col_right->addWidget(points);
|
||||
a_col_right->addWidget(m_status);
|
||||
a_col_right->addWidget(m_progress_bar);
|
||||
QHBoxLayout* a_total = new QHBoxLayout();
|
||||
a_total->addWidget(m_badge);
|
||||
a_total->addLayout(a_col_right);
|
||||
setLayout(a_total);
|
||||
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
void AchievementBox::UpdateData()
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
|
||||
const auto& badge = AchievementManager::GetInstance().GetAchievementBadge(
|
||||
m_achievement->id, m_achievement->state != RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED);
|
||||
std::string_view color = AchievementManager::GRAY;
|
||||
if (m_achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE)
|
||||
color = AchievementManager::GOLD;
|
||||
else if (m_achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE)
|
||||
color = AchievementManager::BLUE;
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED) && badge.name != "")
|
||||
{
|
||||
QImage i_badge{};
|
||||
if (i_badge.loadFromData(&badge.badge.front(), static_cast<int>(badge.badge.size())))
|
||||
{
|
||||
m_badge->setPixmap(QPixmap::fromImage(i_badge).scaled(64, 64, Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
m_badge->adjustSize();
|
||||
m_badge->setStyleSheet(
|
||||
QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_badge->setText({});
|
||||
}
|
||||
|
||||
if (m_achievement->state == RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED)
|
||||
{
|
||||
m_status->setText(
|
||||
tr("Unlocked at %1")
|
||||
.arg(QDateTime::fromSecsSinceEpoch(m_achievement->unlock_time).toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_status->setText(tr("Locked"));
|
||||
}
|
||||
|
||||
if (m_achievement->measured_percent > 0.000)
|
||||
{
|
||||
m_progress_bar->setRange(0, 100);
|
||||
m_progress_bar->setValue(m_achievement->measured_percent);
|
||||
m_progress_bar->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_progress_bar->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
#include <QGroupBox>
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
|
||||
class QLabel;
|
||||
class QProgressBar;
|
||||
class QWidget;
|
||||
|
||||
struct rc_api_achievement_definition_t;
|
||||
|
||||
class AchievementBox final : public QGroupBox
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AchievementBox(QWidget* parent, rc_client_achievement_t* achievement);
|
||||
void UpdateData();
|
||||
|
||||
private:
|
||||
QLabel* m_badge;
|
||||
QLabel* m_status;
|
||||
QProgressBar* m_progress_bar;
|
||||
|
||||
rc_client_achievement_t* m_achievement;
|
||||
};
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
|
@ -11,10 +11,13 @@
|
|||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <rcheevos/include/rc_client.h>
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/Core.h"
|
||||
|
||||
#include "DolphinQt/QtUtils/FromStdString.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(parent)
|
||||
|
@ -23,21 +26,12 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
|
|||
m_game_icon = new QLabel();
|
||||
m_name = new QLabel();
|
||||
m_points = new QLabel();
|
||||
m_game_progress_hard = new QProgressBar();
|
||||
m_game_progress_soft = new QProgressBar();
|
||||
m_game_progress = new QProgressBar();
|
||||
m_rich_presence = new QLabel();
|
||||
m_locked_warning = new QLabel();
|
||||
|
||||
m_locked_warning->setText(tr("Achievements have been disabled.<br>Please close all running "
|
||||
"games to re-enable achievements."));
|
||||
m_locked_warning->setStyleSheet(QStringLiteral("QLabel { color : red; }"));
|
||||
|
||||
QSizePolicy sp_retain = m_game_progress_hard->sizePolicy();
|
||||
QSizePolicy sp_retain = m_game_progress->sizePolicy();
|
||||
sp_retain.setRetainSizeWhenHidden(true);
|
||||
m_game_progress_hard->setSizePolicy(sp_retain);
|
||||
sp_retain = m_game_progress_soft->sizePolicy();
|
||||
sp_retain.setRetainSizeWhenHidden(true);
|
||||
m_game_progress_soft->setSizePolicy(sp_retain);
|
||||
m_game_progress->setSizePolicy(sp_retain);
|
||||
|
||||
QVBoxLayout* icon_col = new QVBoxLayout();
|
||||
icon_col->addWidget(m_user_icon);
|
||||
|
@ -45,10 +39,8 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
|
|||
QVBoxLayout* text_col = new QVBoxLayout();
|
||||
text_col->addWidget(m_name);
|
||||
text_col->addWidget(m_points);
|
||||
text_col->addWidget(m_game_progress_hard);
|
||||
text_col->addWidget(m_game_progress_soft);
|
||||
text_col->addWidget(m_game_progress);
|
||||
text_col->addWidget(m_rich_presence);
|
||||
text_col->addWidget(m_locked_warning);
|
||||
QHBoxLayout* header_layout = new QHBoxLayout();
|
||||
header_layout->addLayout(icon_col);
|
||||
header_layout->addLayout(text_col);
|
||||
|
@ -61,50 +53,48 @@ AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(pare
|
|||
m_total->setContentsMargins(0, 0, 0, 0);
|
||||
m_total->setAlignment(Qt::AlignTop);
|
||||
setLayout(m_total);
|
||||
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
void AchievementHeaderWidget::UpdateData()
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
if (!instance.IsLoggedIn())
|
||||
if (!instance.HasAPIToken())
|
||||
{
|
||||
m_header_box->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
AchievementManager::PointSpread point_spread = instance.TallyScore();
|
||||
QString user_name = QString::fromStdString(instance.GetPlayerDisplayName());
|
||||
QString game_name = QString::fromStdString(instance.GetGameDisplayName());
|
||||
QString user_name = QtUtils::FromStdString(instance.GetPlayerDisplayName());
|
||||
QString game_name = QtUtils::FromStdString(instance.GetGameDisplayName());
|
||||
AchievementManager::BadgeStatus player_badge = instance.GetPlayerBadge();
|
||||
AchievementManager::BadgeStatus game_badge = instance.GetGameBadge();
|
||||
|
||||
m_user_icon->setVisible(false);
|
||||
m_user_icon->clear();
|
||||
m_user_icon->setText({});
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED))
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED) && !player_badge.name.empty())
|
||||
{
|
||||
if (!player_badge.name.empty())
|
||||
QImage i_user_icon{};
|
||||
if (i_user_icon.loadFromData(&player_badge.badge.front(), (int)player_badge.badge.size()))
|
||||
{
|
||||
QImage i_user_icon{};
|
||||
if (i_user_icon.loadFromData(&player_badge.badge.front(), (int)player_badge.badge.size()))
|
||||
{
|
||||
m_user_icon->setPixmap(QPixmap::fromImage(i_user_icon)
|
||||
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
m_user_icon->adjustSize();
|
||||
m_user_icon->setStyleSheet(QStringLiteral("border: 4px solid transparent"));
|
||||
m_user_icon->setVisible(true);
|
||||
}
|
||||
m_user_icon->setPixmap(QPixmap::fromImage(i_user_icon)
|
||||
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
m_user_icon->adjustSize();
|
||||
m_user_icon->setStyleSheet(QStringLiteral("border: 4px solid transparent"));
|
||||
m_user_icon->setVisible(true);
|
||||
}
|
||||
}
|
||||
m_game_icon->setVisible(false);
|
||||
m_game_icon->clear();
|
||||
m_game_icon->setText({});
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED))
|
||||
|
||||
if (instance.IsGameLoaded())
|
||||
{
|
||||
if (!game_badge.name.empty())
|
||||
rc_client_user_game_summary_t game_summary;
|
||||
rc_client_get_user_game_summary(instance.GetClient(), &game_summary);
|
||||
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED) && !game_badge.name.empty())
|
||||
{
|
||||
QImage i_game_icon{};
|
||||
if (i_game_icon.loadFromData(&game_badge.badge.front(), (int)game_badge.badge.size()))
|
||||
|
@ -113,70 +103,39 @@ void AchievementHeaderWidget::UpdateData()
|
|||
.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||
m_game_icon->adjustSize();
|
||||
std::string_view color = AchievementManager::GRAY;
|
||||
if (point_spread.hard_unlocks == point_spread.total_count)
|
||||
color = AchievementManager::GOLD;
|
||||
else if (point_spread.hard_unlocks + point_spread.soft_unlocks == point_spread.total_count)
|
||||
color = AchievementManager::BLUE;
|
||||
if (game_summary.num_core_achievements == game_summary.num_unlocked_achievements)
|
||||
{
|
||||
color =
|
||||
instance.IsHardcoreModeActive() ? AchievementManager::GOLD : AchievementManager::BLUE;
|
||||
}
|
||||
m_game_icon->setStyleSheet(
|
||||
QStringLiteral("border: 4px solid %1").arg(QString::fromStdString(std::string(color))));
|
||||
QStringLiteral("border: 4px solid %1").arg(QtUtils::FromStdString(color)));
|
||||
m_game_icon->setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!game_name.isEmpty())
|
||||
{
|
||||
m_name->setText(tr("%1 is playing %2").arg(user_name).arg(game_name));
|
||||
m_points->setText(GetPointsString(user_name, point_spread));
|
||||
m_points->setText(tr("%1 has unlocked %2/%3 achievements worth %4/%5 points")
|
||||
.arg(user_name)
|
||||
.arg(game_summary.num_unlocked_achievements)
|
||||
.arg(game_summary.num_core_achievements)
|
||||
.arg(game_summary.points_unlocked)
|
||||
.arg(game_summary.points_core));
|
||||
|
||||
m_game_progress_hard->setRange(0, point_spread.total_count);
|
||||
if (!m_game_progress_hard->isVisible())
|
||||
m_game_progress_hard->setVisible(true);
|
||||
m_game_progress_hard->setValue(point_spread.hard_unlocks);
|
||||
m_game_progress_soft->setRange(0, point_spread.total_count);
|
||||
m_game_progress_soft->setValue(point_spread.hard_unlocks + point_spread.soft_unlocks);
|
||||
if (!m_game_progress_soft->isVisible())
|
||||
m_game_progress_soft->setVisible(true);
|
||||
m_game_progress->setRange(0, game_summary.num_core_achievements);
|
||||
if (!m_game_progress->isVisible())
|
||||
m_game_progress->setVisible(true);
|
||||
m_game_progress->setValue(game_summary.num_unlocked_achievements);
|
||||
m_rich_presence->setText(QString::fromUtf8(instance.GetRichPresence().data()));
|
||||
if (!m_rich_presence->isVisible())
|
||||
m_rich_presence->setVisible(Config::Get(Config::RA_RICH_PRESENCE_ENABLED));
|
||||
m_locked_warning->setVisible(false);
|
||||
m_rich_presence->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_name->setText(user_name);
|
||||
m_points->setText(tr("%1 points").arg(instance.GetPlayerScore()));
|
||||
|
||||
m_game_progress_hard->setVisible(false);
|
||||
m_game_progress_soft->setVisible(false);
|
||||
m_game_progress->setVisible(false);
|
||||
m_rich_presence->setVisible(false);
|
||||
m_locked_warning->setVisible(instance.IsDisabled());
|
||||
}
|
||||
}
|
||||
|
||||
QString
|
||||
AchievementHeaderWidget::GetPointsString(const QString& user_name,
|
||||
const AchievementManager::PointSpread& point_spread) const
|
||||
{
|
||||
if (point_spread.soft_points > 0)
|
||||
{
|
||||
return tr("%1 has unlocked %2/%3 achievements (%4 hardcore) worth %5/%6 points (%7 hardcore)")
|
||||
.arg(user_name)
|
||||
.arg(point_spread.hard_unlocks + point_spread.soft_unlocks)
|
||||
.arg(point_spread.total_count)
|
||||
.arg(point_spread.hard_unlocks)
|
||||
.arg(point_spread.hard_points + point_spread.soft_points)
|
||||
.arg(point_spread.total_points)
|
||||
.arg(point_spread.hard_points);
|
||||
}
|
||||
else
|
||||
{
|
||||
return tr("%1 has unlocked %2/%3 achievements worth %4/%5 points")
|
||||
.arg(user_name)
|
||||
.arg(point_spread.hard_unlocks)
|
||||
.arg(point_spread.total_count)
|
||||
.arg(point_spread.hard_points)
|
||||
.arg(point_spread.total_points);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,17 +20,12 @@ public:
|
|||
void UpdateData();
|
||||
|
||||
private:
|
||||
QString GetPointsString(const QString& user_name,
|
||||
const AchievementManager::PointSpread& point_spread) const;
|
||||
|
||||
QLabel* m_user_icon;
|
||||
QLabel* m_game_icon;
|
||||
QLabel* m_name;
|
||||
QLabel* m_points;
|
||||
QProgressBar* m_game_progress_hard;
|
||||
QProgressBar* m_game_progress_soft;
|
||||
QProgressBar* m_game_progress;
|
||||
QLabel* m_rich_presence;
|
||||
QLabel* m_locked_warning;
|
||||
QGroupBox* m_header_box;
|
||||
};
|
||||
|
||||
|
|
|
@ -24,11 +24,6 @@ AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QW
|
|||
m_common_box = new QGroupBox();
|
||||
m_common_layout = new QGridLayout();
|
||||
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
m_common_box->setLayout(m_common_layout);
|
||||
|
||||
auto* layout = new QVBoxLayout;
|
||||
|
@ -38,77 +33,126 @@ AchievementLeaderboardWidget::AchievementLeaderboardWidget(QWidget* parent) : QW
|
|||
setLayout(layout);
|
||||
}
|
||||
|
||||
void AchievementLeaderboardWidget::UpdateData()
|
||||
void AchievementLeaderboardWidget::UpdateData(bool clean_all)
|
||||
{
|
||||
ClearLayoutRecursively(m_common_layout);
|
||||
|
||||
if (!AchievementManager::GetInstance().IsGameLoaded())
|
||||
return;
|
||||
const auto& leaderboards = AchievementManager::GetInstance().GetLeaderboardsInfo();
|
||||
int row = 0;
|
||||
for (const auto& board_row : leaderboards)
|
||||
if (clean_all)
|
||||
{
|
||||
const AchievementManager::LeaderboardStatus& board = board_row.second;
|
||||
QLabel* a_title = new QLabel(QString::fromStdString(board.name));
|
||||
QLabel* a_description = new QLabel(QString::fromStdString(board.description));
|
||||
QVBoxLayout* a_col_left = new QVBoxLayout();
|
||||
a_col_left->addWidget(a_title);
|
||||
a_col_left->addWidget(a_description);
|
||||
if (row > 0)
|
||||
ClearLayoutRecursively(m_common_layout);
|
||||
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
if (!instance.IsGameLoaded())
|
||||
return;
|
||||
auto* client = instance.GetClient();
|
||||
auto* leaderboard_list =
|
||||
rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
|
||||
|
||||
u32 row = 0;
|
||||
for (u32 bucket = 0; bucket < leaderboard_list->num_buckets; bucket++)
|
||||
{
|
||||
QFrame* a_divider = new QFrame();
|
||||
a_divider->setFrameShape(QFrame::HLine);
|
||||
m_common_layout->addWidget(a_divider, row - 1, 0);
|
||||
}
|
||||
m_common_layout->addLayout(a_col_left, row, 0);
|
||||
// Each leaderboard entry is displayed with four values. These are *generally* intended to be,
|
||||
// in order, the first place entry, the entry one above the player, the player's entry, and
|
||||
// the entry one below the player.
|
||||
// Edge cases:
|
||||
// * If there are fewer than four entries in the leaderboard, all entries will be shown in
|
||||
// order and the remainder of the list will be padded with empty values.
|
||||
// * If the player does not currently have a score in the leaderboard, or is in the top 3,
|
||||
// the four slots will be the top four players in order.
|
||||
// * If the player is last place, the player will be in the fourth slot, and the second and
|
||||
// third slots will be the two players above them. The first slot will always be first place.
|
||||
std::array<u32, 4> to_display{1, 2, 3, 4};
|
||||
if (board.player_index > to_display.size() - 1)
|
||||
{
|
||||
// If the rank one below than the player is found, offset = 1.
|
||||
u32 offset = static_cast<u32>(board.entries.count(board.player_index + 1));
|
||||
// Example: player is 10th place but not last
|
||||
// to_display = {1, 10-3+1+1, 10-3+1+2, 10-3+1+3} = {1, 9, 10, 11}
|
||||
// Example: player is 15th place and is last
|
||||
// to_display = {1, 15-3+0+1, 15-3+0+2, 15-3+0+3} = {1, 13, 14, 15}
|
||||
for (size_t i = 1; i < to_display.size(); ++i)
|
||||
to_display[i] = board.player_index - 3 + offset + static_cast<u32>(i);
|
||||
}
|
||||
for (size_t i = 0; i < to_display.size(); ++i)
|
||||
{
|
||||
u32 index = to_display[i];
|
||||
QLabel* a_rank = new QLabel(QStringLiteral("---"));
|
||||
QLabel* a_username = new QLabel(QStringLiteral("---"));
|
||||
QLabel* a_score = new QLabel(QStringLiteral("---"));
|
||||
const auto it = board.entries.find(index);
|
||||
if (it != board.entries.end())
|
||||
const auto& leaderboard_bucket = leaderboard_list->buckets[bucket];
|
||||
for (u32 board = 0; board < leaderboard_bucket.num_leaderboards; board++)
|
||||
{
|
||||
a_rank->setText(tr("Rank %1").arg(it->second.rank));
|
||||
a_username->setText(QString::fromStdString(it->second.username));
|
||||
a_score->setText(QString::fromUtf8(it->second.score.data()));
|
||||
const auto* leaderboard = leaderboard_bucket.leaderboards[board];
|
||||
m_leaderboard_order[leaderboard->id] = row;
|
||||
QLabel* a_title = new QLabel(QString::fromUtf8(leaderboard->title));
|
||||
QLabel* a_description = new QLabel(QString::fromUtf8(leaderboard->description));
|
||||
QVBoxLayout* a_col_left = new QVBoxLayout();
|
||||
a_col_left->addWidget(a_title);
|
||||
a_col_left->addWidget(a_description);
|
||||
if (row > 0)
|
||||
{
|
||||
QFrame* a_divider = new QFrame();
|
||||
a_divider->setFrameShape(QFrame::HLine);
|
||||
m_common_layout->addWidget(a_divider, row - 1, 0);
|
||||
}
|
||||
m_common_layout->addLayout(a_col_left, row, 0);
|
||||
for (size_t ix = 0; ix < 4; ix++)
|
||||
{
|
||||
QVBoxLayout* a_col = new QVBoxLayout();
|
||||
for (size_t jx = 0; jx < 3; jx++)
|
||||
a_col->addWidget(new QLabel(QStringLiteral("---")));
|
||||
if (row > 0)
|
||||
{
|
||||
QFrame* a_divider = new QFrame();
|
||||
a_divider->setFrameShape(QFrame::HLine);
|
||||
m_common_layout->addWidget(a_divider, row - 1, static_cast<int>(ix) + 1);
|
||||
}
|
||||
m_common_layout->addLayout(a_col, row, static_cast<int>(ix) + 1);
|
||||
}
|
||||
row += 2;
|
||||
}
|
||||
}
|
||||
rc_client_destroy_leaderboard_list(leaderboard_list);
|
||||
}
|
||||
for (auto row : m_leaderboard_order)
|
||||
{
|
||||
UpdateRow(row.second);
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementLeaderboardWidget::UpdateData(
|
||||
const std::set<AchievementManager::AchievementId>& update_ids)
|
||||
{
|
||||
for (auto row : m_leaderboard_order)
|
||||
{
|
||||
if (update_ids.contains(row.first))
|
||||
{
|
||||
UpdateRow(row.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementLeaderboardWidget::UpdateRow(AchievementManager::AchievementId leaderboard_id)
|
||||
{
|
||||
const auto leaderboard_itr = m_leaderboard_order.find(leaderboard_id);
|
||||
if (leaderboard_itr == m_leaderboard_order.end())
|
||||
return;
|
||||
const int row = leaderboard_itr->second;
|
||||
|
||||
const AchievementManager::LeaderboardStatus* board;
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
board = AchievementManager::GetInstance().GetLeaderboardInfo(leaderboard_id);
|
||||
}
|
||||
if (!board)
|
||||
return;
|
||||
|
||||
// Each leaderboard entry is displayed with four values. These are *generally* intended to be,
|
||||
// in order, the first place entry, the entry one above the player, the player's entry, and
|
||||
// the entry one below the player.
|
||||
// Edge cases:
|
||||
// * If there are fewer than four entries in the leaderboard, all entries will be shown in
|
||||
// order and the remainder of the list will be padded with empty values.
|
||||
// * If the player does not currently have a score in the leaderboard, or is in the top 3,
|
||||
// the four slots will be the top four players in order.
|
||||
// * If the player is last place, the player will be in the fourth slot, and the second and
|
||||
// third slots will be the two players above them. The first slot will always be first place.
|
||||
std::array<u32, 4> to_display{1, 2, 3, 4};
|
||||
if (board->player_index > to_display.size() - 1)
|
||||
{
|
||||
// If the rank one below than the player is found, offset = 1.
|
||||
u32 offset = static_cast<u32>(board->entries.count(board->player_index + 1));
|
||||
// Example: player is 10th place but not last
|
||||
// to_display = {1, 10-3+1+1, 10-3+1+2, 10-3+1+3} = {1, 9, 10, 11}
|
||||
// Example: player is 15th place and is last
|
||||
// to_display = {1, 15-3+0+1, 15-3+0+2, 15-3+0+3} = {1, 13, 14, 15}
|
||||
for (size_t ix = 1; ix < to_display.size(); ++ix)
|
||||
to_display[ix] = board->player_index - 3 + offset + static_cast<u32>(ix);
|
||||
}
|
||||
for (size_t ix = 0; ix < to_display.size(); ++ix)
|
||||
{
|
||||
const auto it = board->entries.find(to_display[ix]);
|
||||
if (it != board->entries.end())
|
||||
{
|
||||
QVBoxLayout* a_col = new QVBoxLayout();
|
||||
a_col->addWidget(a_rank);
|
||||
a_col->addWidget(a_username);
|
||||
a_col->addWidget(a_score);
|
||||
if (row > 0)
|
||||
{
|
||||
QFrame* a_divider = new QFrame();
|
||||
a_divider->setFrameShape(QFrame::HLine);
|
||||
m_common_layout->addWidget(a_divider, row - 1, static_cast<int>(i) + 1);
|
||||
}
|
||||
m_common_layout->addLayout(a_col, row, static_cast<int>(i) + 1);
|
||||
a_col->addWidget(new QLabel(tr("Rank %1").arg(it->second.rank)));
|
||||
a_col->addWidget(new QLabel(QString::fromStdString(it->second.username)));
|
||||
a_col->addWidget(new QLabel(QString::fromUtf8(it->second.score.data())));
|
||||
auto old_item = m_common_layout->itemAtPosition(row, static_cast<int>(ix) + 1);
|
||||
m_common_layout->removeItem(old_item);
|
||||
ClearLayoutRecursively(static_cast<QLayout*>(old_item));
|
||||
m_common_layout->addLayout(a_col, row, static_cast<int>(ix) + 1);
|
||||
}
|
||||
row += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
#include <QWidget>
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
|
||||
class QGroupBox;
|
||||
class QGridLayout;
|
||||
|
||||
|
@ -14,11 +16,14 @@ class AchievementLeaderboardWidget final : public QWidget
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit AchievementLeaderboardWidget(QWidget* parent);
|
||||
void UpdateData();
|
||||
void UpdateData(bool clean_all);
|
||||
void UpdateData(const std::set<AchievementManager::AchievementId>& update_ids);
|
||||
void UpdateRow(AchievementManager::AchievementId leaderboard_id);
|
||||
|
||||
private:
|
||||
QGroupBox* m_common_box;
|
||||
QGridLayout* m_common_layout;
|
||||
std::map<AchievementManager::AchievementId, int> m_leaderboard_order;
|
||||
};
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -18,21 +18,15 @@
|
|||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Core.h"
|
||||
|
||||
#include "DolphinQt/Achievements/AchievementBox.h"
|
||||
#include "DolphinQt/QtUtils/ClearLayoutRecursively.h"
|
||||
#include "DolphinQt/Settings.h"
|
||||
|
||||
static constexpr bool hardcore_mode_enabled = false;
|
||||
|
||||
AchievementProgressWidget::AchievementProgressWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
m_common_box = new QGroupBox();
|
||||
m_common_layout = new QVBoxLayout();
|
||||
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
UpdateData();
|
||||
}
|
||||
|
||||
m_common_box->setLayout(m_common_layout);
|
||||
|
||||
auto* layout = new QVBoxLayout;
|
||||
|
@ -42,124 +36,50 @@ AchievementProgressWidget::AchievementProgressWidget(QWidget* parent) : QWidget(
|
|||
setLayout(layout);
|
||||
}
|
||||
|
||||
QGroupBox*
|
||||
AchievementProgressWidget::CreateAchievementBox(const rc_api_achievement_definition_t* achievement)
|
||||
void AchievementProgressWidget::UpdateData(bool clean_all)
|
||||
{
|
||||
const auto& instance = AchievementManager::GetInstance();
|
||||
if (!instance.IsGameLoaded())
|
||||
return new QGroupBox();
|
||||
if (clean_all)
|
||||
{
|
||||
m_achievement_boxes.clear();
|
||||
ClearLayoutRecursively(m_common_layout);
|
||||
|
||||
QLabel* a_badge = new QLabel();
|
||||
const auto unlock_status = instance.GetUnlockStatus(achievement->id);
|
||||
const AchievementManager::BadgeStatus* badge = &unlock_status.locked_badge;
|
||||
std::string_view color = AchievementManager::GRAY;
|
||||
if (unlock_status.remote_unlock_status == AchievementManager::UnlockStatus::UnlockType::HARDCORE)
|
||||
{
|
||||
badge = &unlock_status.unlocked_badge;
|
||||
color = AchievementManager::GOLD;
|
||||
}
|
||||
else if (hardcore_mode_enabled && unlock_status.session_unlock_count > 1)
|
||||
{
|
||||
badge = &unlock_status.unlocked_badge;
|
||||
color = AchievementManager::GOLD;
|
||||
}
|
||||
else if (unlock_status.remote_unlock_status ==
|
||||
AchievementManager::UnlockStatus::UnlockType::SOFTCORE)
|
||||
{
|
||||
badge = &unlock_status.unlocked_badge;
|
||||
color = AchievementManager::BLUE;
|
||||
}
|
||||
else if (unlock_status.session_unlock_count > 1)
|
||||
{
|
||||
badge = &unlock_status.unlocked_badge;
|
||||
color = AchievementManager::BLUE;
|
||||
}
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED) && badge->name != "")
|
||||
{
|
||||
QImage i_badge{};
|
||||
if (i_badge.loadFromData(&badge->badge.front(), (int)badge->badge.size()))
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
if (!instance.IsGameLoaded())
|
||||
return;
|
||||
auto* client = instance.GetClient();
|
||||
auto* achievement_list = rc_client_create_achievement_list(
|
||||
client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL,
|
||||
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_LOCK_STATE);
|
||||
for (u32 ix = 0; ix < achievement_list->num_buckets; ix++)
|
||||
{
|
||||
a_badge->setPixmap(QPixmap::fromImage(i_badge).scaled(64, 64, Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
a_badge->adjustSize();
|
||||
a_badge->setStyleSheet(
|
||||
QStringLiteral("border: 4px solid %1").arg(QString::fromStdString(std::string(color))));
|
||||
for (u32 jx = 0; jx < achievement_list->buckets[ix].num_achievements; jx++)
|
||||
{
|
||||
auto* achievement = achievement_list->buckets[ix].achievements[jx];
|
||||
m_achievement_boxes[achievement->id] = std::make_shared<AchievementBox>(this, achievement);
|
||||
m_common_layout->addWidget(m_achievement_boxes[achievement->id].get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QLabel* a_title = new QLabel(QString::fromUtf8(achievement->title, strlen(achievement->title)));
|
||||
QLabel* a_description =
|
||||
new QLabel(QString::fromUtf8(achievement->description, strlen(achievement->description)));
|
||||
QLabel* a_points = new QLabel(tr("%1 points").arg(achievement->points));
|
||||
QLabel* a_status = new QLabel(GetStatusString(achievement->id));
|
||||
QProgressBar* a_progress_bar = new QProgressBar();
|
||||
QSizePolicy sp_retain = a_progress_bar->sizePolicy();
|
||||
sp_retain.setRetainSizeWhenHidden(true);
|
||||
a_progress_bar->setSizePolicy(sp_retain);
|
||||
unsigned int value = 0;
|
||||
unsigned int target = 0;
|
||||
if (AchievementManager::GetInstance().GetAchievementProgress(achievement->id, &value, &target) ==
|
||||
AchievementManager::ResponseType::SUCCESS &&
|
||||
target > 0)
|
||||
{
|
||||
a_progress_bar->setRange(0, target);
|
||||
a_progress_bar->setValue(value);
|
||||
rc_client_destroy_achievement_list(achievement_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
a_progress_bar->setVisible(false);
|
||||
}
|
||||
|
||||
QVBoxLayout* a_col_right = new QVBoxLayout();
|
||||
a_col_right->addWidget(a_title);
|
||||
a_col_right->addWidget(a_description);
|
||||
a_col_right->addWidget(a_points);
|
||||
a_col_right->addWidget(a_status);
|
||||
a_col_right->addWidget(a_progress_bar);
|
||||
QHBoxLayout* a_total = new QHBoxLayout();
|
||||
a_total->addWidget(a_badge);
|
||||
a_total->addLayout(a_col_right);
|
||||
QGroupBox* a_group_box = new QGroupBox();
|
||||
a_group_box->setLayout(a_total);
|
||||
return a_group_box;
|
||||
}
|
||||
|
||||
void AchievementProgressWidget::UpdateData()
|
||||
{
|
||||
ClearLayoutRecursively(m_common_layout);
|
||||
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
if (!instance.IsGameLoaded())
|
||||
return;
|
||||
|
||||
const auto* game_data = instance.GetGameData();
|
||||
for (u32 ix = 0; ix < game_data->num_achievements; ix++)
|
||||
{
|
||||
m_common_layout->addWidget(CreateAchievementBox(game_data->achievements + ix));
|
||||
}
|
||||
}
|
||||
|
||||
QString AchievementProgressWidget::GetStatusString(u32 achievement_id) const
|
||||
{
|
||||
const auto unlock_status = AchievementManager::GetInstance().GetUnlockStatus(achievement_id);
|
||||
if (unlock_status.session_unlock_count > 0)
|
||||
{
|
||||
if (Config::Get(Config::RA_ENCORE_ENABLED))
|
||||
for (auto box : m_achievement_boxes)
|
||||
{
|
||||
return tr("Unlocked %1 times this session").arg(unlock_status.session_unlock_count);
|
||||
box.second->UpdateData();
|
||||
}
|
||||
return tr("Unlocked this session");
|
||||
}
|
||||
switch (unlock_status.remote_unlock_status)
|
||||
}
|
||||
|
||||
void AchievementProgressWidget::UpdateData(
|
||||
const std::set<AchievementManager::AchievementId>& update_ids)
|
||||
{
|
||||
for (auto& [id, box] : m_achievement_boxes)
|
||||
{
|
||||
case AchievementManager::UnlockStatus::UnlockType::LOCKED:
|
||||
return tr("Locked");
|
||||
case AchievementManager::UnlockStatus::UnlockType::SOFTCORE:
|
||||
return tr("Unlocked (Casual)");
|
||||
case AchievementManager::UnlockStatus::UnlockType::HARDCORE:
|
||||
return tr("Unlocked");
|
||||
if (update_ids.contains(id))
|
||||
{
|
||||
box->UpdateData();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#include <QWidget>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/AchievementManager.h"
|
||||
|
||||
class AchievementBox;
|
||||
class QCheckBox;
|
||||
class QGroupBox;
|
||||
class QLineEdit;
|
||||
|
@ -21,14 +23,13 @@ class AchievementProgressWidget final : public QWidget
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit AchievementProgressWidget(QWidget* parent);
|
||||
void UpdateData();
|
||||
void UpdateData(bool clean_all);
|
||||
void UpdateData(const std::set<AchievementManager::AchievementId>& update_ids);
|
||||
|
||||
private:
|
||||
QGroupBox* CreateAchievementBox(const rc_api_achievement_definition_t* achievement);
|
||||
QString GetStatusString(u32 achievement_id) const;
|
||||
|
||||
QGroupBox* m_common_box;
|
||||
QVBoxLayout* m_common_layout;
|
||||
std::map<AchievementManager::AchievementId, std::shared_ptr<AchievementBox>> m_achievement_boxes;
|
||||
};
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -61,24 +61,6 @@ void AchievementSettingsWidget::CreateLayout()
|
|||
m_common_login_failed = new QLabel(tr("Login Failed"));
|
||||
m_common_login_failed->setStyleSheet(QStringLiteral("QLabel { color : red; }"));
|
||||
m_common_login_failed->setVisible(false);
|
||||
m_common_achievements_enabled_input = new ToolTipCheckBox(tr("Enable Achievements"));
|
||||
m_common_achievements_enabled_input->SetDescription(tr("Enable unlocking achievements.<br>"));
|
||||
m_common_leaderboards_enabled_input = new ToolTipCheckBox(tr("Enable Leaderboards"));
|
||||
m_common_leaderboards_enabled_input->SetDescription(
|
||||
tr("Enable competing in RetroAchievements leaderboards.<br><br>Hardcore Mode must be enabled "
|
||||
"to use."));
|
||||
m_common_rich_presence_enabled_input = new ToolTipCheckBox(tr("Enable Rich Presence"));
|
||||
m_common_rich_presence_enabled_input->SetDescription(
|
||||
tr("Enable detailed rich presence on the RetroAchievements website.<br><br>This provides a "
|
||||
"detailed description of what the player is doing in game to the website. If this is "
|
||||
"disabled, the website will only report what game is being played.<br><br>This has no "
|
||||
"bearing on Discord rich presence."));
|
||||
m_common_unofficial_enabled_input = new ToolTipCheckBox(tr("Enable Unofficial Achievements"));
|
||||
m_common_unofficial_enabled_input->SetDescription(
|
||||
tr("Enable unlocking unofficial achievements as well as official "
|
||||
"achievements.<br><br>Unofficial achievements may be optional or unfinished achievements "
|
||||
"that have not been deemed official by RetroAchievements and may be useful for testing or "
|
||||
"simply for fun."));
|
||||
m_common_hardcore_enabled_input = new ToolTipCheckBox(tr("Enable Hardcore Mode"));
|
||||
m_common_hardcore_enabled_input->SetDescription(
|
||||
tr("Enable Hardcore Mode on RetroAchievements.<br><br>Hardcore Mode is intended to provide "
|
||||
|
@ -93,6 +75,25 @@ void AchievementSettingsWidget::CreateLayout()
|
|||
"playing.</dolphin_emphasis><br>Close your current game before enabling.<br>Be aware that "
|
||||
"turning Hardcore Mode off while a game is running requires the game to be closed before "
|
||||
"re-enabling."));
|
||||
m_common_unofficial_enabled_input = new ToolTipCheckBox(tr("Enable Unofficial Achievements"));
|
||||
m_common_unofficial_enabled_input->SetDescription(
|
||||
tr("Enable unlocking unofficial achievements as well as official "
|
||||
"achievements.<br><br>Unofficial achievements may be optional or unfinished achievements "
|
||||
"that have not been deemed official by RetroAchievements and may be useful for testing or "
|
||||
"simply for fun.<br><br>Setting takes effect on next game load."));
|
||||
m_common_encore_enabled_input = new ToolTipCheckBox(tr("Enable Encore Achievements"));
|
||||
m_common_encore_enabled_input->SetDescription(
|
||||
tr("Enable unlocking achievements in Encore Mode.<br><br>Encore Mode re-enables achievements "
|
||||
"the player has already unlocked on the site so that the player will be notified if they "
|
||||
"meet the unlock conditions again, useful for custom speedrun criteria or simply for fun."
|
||||
"<br><br>Setting takes effect on next game load."));
|
||||
m_common_spectator_enabled_input = new ToolTipCheckBox(tr("Enable Spectator Mode"));
|
||||
m_common_spectator_enabled_input->SetDescription(
|
||||
tr("Enable unlocking achievements in Spectator Mode.<br><br>While in Spectator Mode, "
|
||||
"achievements and leaderboards will be processed and displayed on screen, but will not be "
|
||||
"submitted to the server.<br><br>If this is on at game launch, it will not be turned off "
|
||||
"until game close, because a RetroAchievements session will not be created.<br><br>If "
|
||||
"this is off at game launch, it can be toggled freely while the game is running."));
|
||||
m_common_progress_enabled_input = new ToolTipCheckBox(tr("Enable Progress Notifications"));
|
||||
m_common_progress_enabled_input->SetDescription(
|
||||
tr("Enable progress notifications on achievements.<br><br>Displays a brief popup message "
|
||||
|
@ -103,11 +104,6 @@ void AchievementSettingsWidget::CreateLayout()
|
|||
tr("Enable achievement badges.<br><br>Displays icons for the player, game, and achievements. "
|
||||
"Simple visual option, but will require a small amount of extra memory and time to "
|
||||
"download the images."));
|
||||
m_common_encore_enabled_input = new ToolTipCheckBox(tr("Enable Encore Achievements"));
|
||||
m_common_encore_enabled_input->SetDescription(tr(
|
||||
"Enable unlocking achievements in Encore Mode.<br><br>Encore Mode re-enables achievements "
|
||||
"the player has already unlocked on the site so that the player will be notified if they "
|
||||
"meet the unlock conditions again, useful for custom speedrun criteria or simply for fun."));
|
||||
|
||||
m_common_layout->addWidget(m_common_integration_enabled_input);
|
||||
m_common_layout->addWidget(m_common_username_label);
|
||||
|
@ -117,14 +113,14 @@ void AchievementSettingsWidget::CreateLayout()
|
|||
m_common_layout->addWidget(m_common_login_button);
|
||||
m_common_layout->addWidget(m_common_logout_button);
|
||||
m_common_layout->addWidget(m_common_login_failed);
|
||||
m_common_layout->addWidget(m_common_achievements_enabled_input);
|
||||
m_common_layout->addWidget(m_common_leaderboards_enabled_input);
|
||||
m_common_layout->addWidget(m_common_rich_presence_enabled_input);
|
||||
m_common_layout->addWidget(new QLabel(tr("Function Settings")));
|
||||
m_common_layout->addWidget(m_common_hardcore_enabled_input);
|
||||
m_common_layout->addWidget(m_common_progress_enabled_input);
|
||||
m_common_layout->addWidget(m_common_badges_enabled_input);
|
||||
m_common_layout->addWidget(m_common_unofficial_enabled_input);
|
||||
m_common_layout->addWidget(m_common_encore_enabled_input);
|
||||
m_common_layout->addWidget(m_common_spectator_enabled_input);
|
||||
m_common_layout->addWidget(new QLabel(tr("Display Settings")));
|
||||
m_common_layout->addWidget(m_common_progress_enabled_input);
|
||||
m_common_layout->addWidget(m_common_badges_enabled_input);
|
||||
|
||||
m_common_layout->setAlignment(Qt::AlignTop);
|
||||
setLayout(m_common_layout);
|
||||
|
@ -136,22 +132,18 @@ void AchievementSettingsWidget::ConnectWidgets()
|
|||
&AchievementSettingsWidget::ToggleRAIntegration);
|
||||
connect(m_common_login_button, &QPushButton::pressed, this, &AchievementSettingsWidget::Login);
|
||||
connect(m_common_logout_button, &QPushButton::pressed, this, &AchievementSettingsWidget::Logout);
|
||||
connect(m_common_achievements_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleAchievements);
|
||||
connect(m_common_leaderboards_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleLeaderboards);
|
||||
connect(m_common_rich_presence_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleRichPresence);
|
||||
connect(m_common_hardcore_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleHardcore);
|
||||
connect(m_common_progress_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleProgress);
|
||||
connect(m_common_badges_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleBadges);
|
||||
connect(m_common_unofficial_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleUnofficial);
|
||||
connect(m_common_encore_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleEncore);
|
||||
connect(m_common_spectator_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleSpectator);
|
||||
connect(m_common_progress_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleProgress);
|
||||
connect(m_common_badges_enabled_input, &QCheckBox::toggled, this,
|
||||
&AchievementSettingsWidget::ToggleBadges);
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::OnControllerInterfaceConfigure()
|
||||
|
@ -165,7 +157,6 @@ void AchievementSettingsWidget::OnControllerInterfaceConfigure()
|
|||
void AchievementSettingsWidget::LoadSettings()
|
||||
{
|
||||
bool enabled = Config::Get(Config::RA_ENABLED);
|
||||
bool achievements_enabled = Config::Get(Config::RA_ACHIEVEMENTS_ENABLED);
|
||||
bool hardcore_enabled = Config::Get(Config::RA_HARDCORE_ENABLED);
|
||||
bool logged_out = Config::Get(Config::RA_API_TOKEN).empty();
|
||||
std::string username = Config::Get(Config::RA_USERNAME);
|
||||
|
@ -184,17 +175,6 @@ void AchievementSettingsWidget::LoadSettings()
|
|||
SignalBlocking(m_common_logout_button)->setVisible(!logged_out);
|
||||
SignalBlocking(m_common_logout_button)->setEnabled(enabled);
|
||||
|
||||
SignalBlocking(m_common_achievements_enabled_input)->setChecked(achievements_enabled);
|
||||
SignalBlocking(m_common_achievements_enabled_input)->setEnabled(enabled);
|
||||
|
||||
SignalBlocking(m_common_leaderboards_enabled_input)
|
||||
->setChecked(Config::Get(Config::RA_LEADERBOARDS_ENABLED));
|
||||
SignalBlocking(m_common_leaderboards_enabled_input)->setEnabled(enabled && hardcore_enabled);
|
||||
|
||||
SignalBlocking(m_common_rich_presence_enabled_input)
|
||||
->setChecked(Config::Get(Config::RA_RICH_PRESENCE_ENABLED));
|
||||
SignalBlocking(m_common_rich_presence_enabled_input)->setEnabled(enabled);
|
||||
|
||||
SignalBlocking(m_common_hardcore_enabled_input)
|
||||
->setChecked(Config::Get(Config::RA_HARDCORE_ENABLED));
|
||||
auto& system = Core::System::GetInstance();
|
||||
|
@ -203,19 +183,23 @@ void AchievementSettingsWidget::LoadSettings()
|
|||
(hardcore_enabled || (Core::GetState(system) == Core::State::Uninitialized &&
|
||||
!system.GetMovie().IsPlayingInput())));
|
||||
|
||||
SignalBlocking(m_common_unofficial_enabled_input)
|
||||
->setChecked(Config::Get(Config::RA_UNOFFICIAL_ENABLED));
|
||||
SignalBlocking(m_common_unofficial_enabled_input)->setEnabled(enabled);
|
||||
|
||||
SignalBlocking(m_common_encore_enabled_input)->setChecked(Config::Get(Config::RA_ENCORE_ENABLED));
|
||||
SignalBlocking(m_common_encore_enabled_input)->setEnabled(enabled);
|
||||
|
||||
SignalBlocking(m_common_spectator_enabled_input)
|
||||
->setChecked(Config::Get(Config::RA_SPECTATOR_ENABLED));
|
||||
SignalBlocking(m_common_spectator_enabled_input)->setEnabled(enabled);
|
||||
|
||||
SignalBlocking(m_common_progress_enabled_input)
|
||||
->setChecked(Config::Get(Config::RA_PROGRESS_ENABLED));
|
||||
SignalBlocking(m_common_progress_enabled_input)->setEnabled(enabled && achievements_enabled);
|
||||
SignalBlocking(m_common_progress_enabled_input)->setEnabled(enabled);
|
||||
|
||||
SignalBlocking(m_common_badges_enabled_input)->setChecked(Config::Get(Config::RA_BADGES_ENABLED));
|
||||
SignalBlocking(m_common_badges_enabled_input)->setEnabled(enabled);
|
||||
|
||||
SignalBlocking(m_common_unofficial_enabled_input)
|
||||
->setChecked(Config::Get(Config::RA_UNOFFICIAL_ENABLED));
|
||||
SignalBlocking(m_common_unofficial_enabled_input)->setEnabled(enabled && achievements_enabled);
|
||||
|
||||
SignalBlocking(m_common_encore_enabled_input)->setChecked(Config::Get(Config::RA_ENCORE_ENABLED));
|
||||
SignalBlocking(m_common_encore_enabled_input)->setEnabled(enabled && achievements_enabled);
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::SaveSettings()
|
||||
|
@ -223,20 +207,16 @@ void AchievementSettingsWidget::SaveSettings()
|
|||
Config::ConfigChangeCallbackGuard config_guard;
|
||||
|
||||
Config::SetBaseOrCurrent(Config::RA_ENABLED, m_common_integration_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_ACHIEVEMENTS_ENABLED,
|
||||
m_common_achievements_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_LEADERBOARDS_ENABLED,
|
||||
m_common_leaderboards_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_RICH_PRESENCE_ENABLED,
|
||||
m_common_rich_presence_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_HARDCORE_ENABLED,
|
||||
m_common_hardcore_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_PROGRESS_ENABLED,
|
||||
m_common_unofficial_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_BADGES_ENABLED, m_common_badges_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_UNOFFICIAL_ENABLED,
|
||||
m_common_unofficial_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_ENCORE_ENABLED, m_common_encore_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_SPECTATOR_ENABLED,
|
||||
m_common_spectator_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_PROGRESS_ENABLED,
|
||||
m_common_progress_enabled_input->isChecked());
|
||||
Config::SetBaseOrCurrent(Config::RA_BADGES_ENABLED, m_common_badges_enabled_input->isChecked());
|
||||
Config::Save();
|
||||
}
|
||||
|
||||
|
@ -256,7 +236,6 @@ void AchievementSettingsWidget::Login()
|
|||
Config::SetBaseOrCurrent(Config::RA_USERNAME, m_common_username_input->text().toStdString());
|
||||
AchievementManager::GetInstance().Login(m_common_password_input->text().toStdString());
|
||||
m_common_password_input->setText(QString());
|
||||
m_common_login_failed->setVisible(Config::Get(Config::RA_API_TOKEN).empty());
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
|
@ -266,27 +245,10 @@ void AchievementSettingsWidget::Logout()
|
|||
SaveSettings();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleAchievements()
|
||||
{
|
||||
SaveSettings();
|
||||
AchievementManager::GetInstance().ActivateDeactivateAchievements();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleLeaderboards()
|
||||
{
|
||||
SaveSettings();
|
||||
AchievementManager::GetInstance().ActivateDeactivateLeaderboards();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleRichPresence()
|
||||
{
|
||||
SaveSettings();
|
||||
AchievementManager::GetInstance().ActivateDeactivateRichPresence();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleHardcore()
|
||||
{
|
||||
SaveSettings();
|
||||
AchievementManager::GetInstance().SetHardcoreMode();
|
||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||
{
|
||||
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f)
|
||||
|
@ -298,6 +260,22 @@ void AchievementSettingsWidget::ToggleHardcore()
|
|||
emit Settings::Instance().EmulationStateChanged(Core::GetState(Core::System::GetInstance()));
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleUnofficial()
|
||||
{
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleEncore()
|
||||
{
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleSpectator()
|
||||
{
|
||||
SaveSettings();
|
||||
AchievementManager::GetInstance().SetSpectatorMode();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleProgress()
|
||||
{
|
||||
SaveSettings();
|
||||
|
@ -306,19 +284,8 @@ void AchievementSettingsWidget::ToggleProgress()
|
|||
void AchievementSettingsWidget::ToggleBadges()
|
||||
{
|
||||
SaveSettings();
|
||||
AchievementManager::GetInstance().FetchBadges();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleUnofficial()
|
||||
{
|
||||
SaveSettings();
|
||||
AchievementManager::GetInstance().ActivateDeactivateAchievements();
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::ToggleEncore()
|
||||
{
|
||||
SaveSettings();
|
||||
AchievementManager::GetInstance().ActivateDeactivateAchievements();
|
||||
AchievementManager::GetInstance().FetchPlayerBadge();
|
||||
AchievementManager::GetInstance().FetchGameBadges();
|
||||
}
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -32,14 +32,12 @@ private:
|
|||
void ToggleRAIntegration();
|
||||
void Login();
|
||||
void Logout();
|
||||
void ToggleAchievements();
|
||||
void ToggleLeaderboards();
|
||||
void ToggleRichPresence();
|
||||
void ToggleHardcore();
|
||||
void ToggleProgress();
|
||||
void ToggleBadges();
|
||||
void ToggleUnofficial();
|
||||
void ToggleEncore();
|
||||
void ToggleSpectator();
|
||||
void ToggleProgress();
|
||||
void ToggleBadges();
|
||||
|
||||
QGroupBox* m_common_box;
|
||||
QVBoxLayout* m_common_layout;
|
||||
|
@ -51,14 +49,12 @@ private:
|
|||
QLineEdit* m_common_password_input;
|
||||
QPushButton* m_common_login_button;
|
||||
QPushButton* m_common_logout_button;
|
||||
ToolTipCheckBox* m_common_achievements_enabled_input;
|
||||
ToolTipCheckBox* m_common_leaderboards_enabled_input;
|
||||
ToolTipCheckBox* m_common_rich_presence_enabled_input;
|
||||
ToolTipCheckBox* m_common_hardcore_enabled_input;
|
||||
ToolTipCheckBox* m_common_progress_enabled_input;
|
||||
ToolTipCheckBox* m_common_badges_enabled_input;
|
||||
ToolTipCheckBox* m_common_unofficial_enabled_input;
|
||||
ToolTipCheckBox* m_common_encore_enabled_input;
|
||||
ToolTipCheckBox* m_common_spectator_enabled_input;
|
||||
ToolTipCheckBox* m_common_progress_enabled_input;
|
||||
ToolTipCheckBox* m_common_badges_enabled_input;
|
||||
};
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -28,11 +28,13 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
|
|||
CreateMainLayout();
|
||||
ConnectWidgets();
|
||||
AchievementManager::GetInstance().SetUpdateCallback(
|
||||
[this] { QueueOnObject(this, &AchievementsWindow::UpdateData); });
|
||||
[this](AchievementManager::UpdatedItems updated_items) {
|
||||
QueueOnObject(this, [this, updated_items = std::move(updated_items)] {
|
||||
AchievementsWindow::UpdateData(std::move(updated_items));
|
||||
});
|
||||
});
|
||||
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
|
||||
&AchievementsWindow::UpdateData);
|
||||
|
||||
UpdateData();
|
||||
[this] { AchievementsWindow::UpdateData({.all = true}); });
|
||||
}
|
||||
|
||||
void AchievementsWindow::showEvent(QShowEvent* event)
|
||||
|
@ -71,19 +73,38 @@ void AchievementsWindow::ConnectWidgets()
|
|||
connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
void AchievementsWindow::UpdateData()
|
||||
void AchievementsWindow::UpdateData(AchievementManager::UpdatedItems updated_items)
|
||||
{
|
||||
m_settings_widget->UpdateData();
|
||||
if (updated_items.all)
|
||||
{
|
||||
m_header_widget->UpdateData();
|
||||
m_progress_widget->UpdateData(true);
|
||||
m_leaderboard_widget->UpdateData(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (updated_items.player_icon || updated_items.game_icon || updated_items.rich_presence ||
|
||||
updated_items.all_achievements || updated_items.achievements.size() > 0)
|
||||
{
|
||||
m_header_widget->UpdateData();
|
||||
}
|
||||
if (updated_items.all_achievements)
|
||||
m_progress_widget->UpdateData(false);
|
||||
else if (updated_items.achievements.size() > 0)
|
||||
m_progress_widget->UpdateData(updated_items.achievements);
|
||||
if (updated_items.all_leaderboards)
|
||||
m_leaderboard_widget->UpdateData(false);
|
||||
else if (updated_items.leaderboards.size() > 0)
|
||||
m_leaderboard_widget->UpdateData(updated_items.leaderboards);
|
||||
}
|
||||
|
||||
{
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
std::lock_guard lg{instance.GetLock()};
|
||||
const bool is_game_loaded = instance.IsGameLoaded();
|
||||
|
||||
m_header_widget->UpdateData();
|
||||
m_header_widget->setVisible(instance.IsLoggedIn());
|
||||
m_settings_widget->UpdateData();
|
||||
m_progress_widget->UpdateData();
|
||||
m_header_widget->setVisible(instance.HasAPIToken());
|
||||
m_tab_widget->setTabVisible(1, is_game_loaded);
|
||||
m_leaderboard_widget->UpdateData();
|
||||
m_tab_widget->setTabVisible(2, is_game_loaded);
|
||||
}
|
||||
update();
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
#include <QDialog>
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
|
||||
class AchievementHeaderWidget;
|
||||
class AchievementLeaderboardWidget;
|
||||
class AchievementSettingsWidget;
|
||||
|
@ -19,7 +21,7 @@ class AchievementsWindow : public QDialog
|
|||
Q_OBJECT
|
||||
public:
|
||||
explicit AchievementsWindow(QWidget* parent);
|
||||
void UpdateData();
|
||||
void UpdateData(AchievementManager::UpdatedItems updated_items);
|
||||
void ForceSettingsTab();
|
||||
|
||||
private:
|
||||
|
|
|
@ -28,6 +28,8 @@ add_executable(dolphin-emu
|
|||
CheatSearchWidget.h
|
||||
CheatsManager.cpp
|
||||
CheatsManager.h
|
||||
Achievements/AchievementBox.cpp
|
||||
Achievements/AchievementBox.h
|
||||
Achievements/AchievementHeaderWidget.cpp
|
||||
Achievements/AchievementHeaderWidget.h
|
||||
Achievements/AchievementLeaderboardWidget.cpp
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
<ClCompile Include="CheatSearchFactoryWidget.cpp" />
|
||||
<ClCompile Include="CheatSearchWidget.cpp" />
|
||||
<ClCompile Include="CheatsManager.cpp" />
|
||||
<ClCompile Include="Achievements\AchievementBox.cpp" />
|
||||
<ClCompile Include="Achievements\AchievementHeaderWidget.cpp" />
|
||||
<ClCompile Include="Achievements\AchievementLeaderboardWidget.cpp" />
|
||||
<ClCompile Include="Achievements\AchievementProgressWidget.cpp" />
|
||||
|
@ -267,6 +268,7 @@
|
|||
<QtMoc Include="CheatSearchFactoryWidget.h" />
|
||||
<QtMoc Include="CheatSearchWidget.h" />
|
||||
<QtMoc Include="CheatsManager.h" />
|
||||
<QtMoc Include="Achievements\AchievementBox.h" />
|
||||
<QtMoc Include="Achievements\AchievementHeaderWidget.h" />
|
||||
<QtMoc Include="Achievements\AchievementLeaderboardWidget.h" />
|
||||
<QtMoc Include="Achievements\AchievementProgressWidget.h" />
|
||||
|
|
|
@ -2005,6 +2005,7 @@ void MainWindow::ShowAchievementsWindow()
|
|||
m_achievements_window->show();
|
||||
m_achievements_window->raise();
|
||||
m_achievements_window->activateWindow();
|
||||
m_achievements_window->UpdateData(AchievementManager::UpdatedItems{.all = true});
|
||||
}
|
||||
|
||||
void MainWindow::ShowAchievementSettings()
|
||||
|
|
|
@ -332,63 +332,70 @@ void OnScreenUI::DrawDebugText()
|
|||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
void OnScreenUI::DrawChallenges()
|
||||
void OnScreenUI::DrawChallengesAndLeaderboards()
|
||||
{
|
||||
std::lock_guard lg{AchievementManager::GetInstance().GetLock()};
|
||||
const auto& challenge_icons = AchievementManager::GetInstance().GetChallengeIcons();
|
||||
if (challenge_icons.empty())
|
||||
return;
|
||||
|
||||
const std::string window_name = "Challenges";
|
||||
|
||||
u32 sum_of_icon_heights = 0;
|
||||
u32 max_icon_width = 0;
|
||||
for (const auto& [name, icon] : challenge_icons)
|
||||
const auto& leaderboard_progress = AchievementManager::GetInstance().GetActiveLeaderboards();
|
||||
float leaderboard_y = ImGui::GetIO().DisplaySize.y;
|
||||
if (!challenge_icons.empty())
|
||||
{
|
||||
// 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)
|
||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y), 0,
|
||||
ImVec2(1.0, 1.0));
|
||||
ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f));
|
||||
if (ImGui::Begin("Challenges", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing))
|
||||
{
|
||||
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,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
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())
|
||||
for (const auto& [name, icon] : challenge_icons)
|
||||
{
|
||||
m_challenge_texture_map.erase(name);
|
||||
continue;
|
||||
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,
|
||||
AbstractTextureType::Texture_2DArray);
|
||||
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);
|
||||
}
|
||||
if (texture)
|
||||
for (auto& [name, texture] : m_challenge_texture_map)
|
||||
{
|
||||
ImGui::Image(texture.get(), ImVec2(static_cast<float>(icon_itr->second->width),
|
||||
static_cast<float>(icon_itr->second->height)));
|
||||
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<float>(icon_itr->second->width),
|
||||
static_cast<float>(icon_itr->second->height)));
|
||||
}
|
||||
}
|
||||
leaderboard_y -= ImGui::GetWindowHeight();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
if (!leaderboard_progress.empty())
|
||||
{
|
||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x, leaderboard_y), 0,
|
||||
ImVec2(1.0, 1.0));
|
||||
ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f));
|
||||
if (ImGui::Begin("Leaderboards", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav |
|
||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing))
|
||||
{
|
||||
for (const auto& value : leaderboard_progress)
|
||||
ImGui::Text(value.data());
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
|
@ -400,7 +407,7 @@ void OnScreenUI::Finalize()
|
|||
DrawDebugText();
|
||||
OSD::DrawMessages();
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
DrawChallenges();
|
||||
DrawChallengesAndLeaderboards();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
ImGui::Render();
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
private:
|
||||
void DrawDebugText();
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
void DrawChallenges();
|
||||
void DrawChallengesAndLeaderboards();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
// ImGui resources.
|
||||
|
|
Loading…
Reference in New Issue