Merge pull request #11878 from LillyJadeKatrin/retroachievements-achievements-tab

RetroAchievements - Achievement Progress Tab
This commit is contained in:
Admiral H. Curtiss 2023-07-03 03:23:18 +02:00 committed by GitHub
commit da2784a391
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 535 additions and 64 deletions

View File

@ -38,11 +38,24 @@ void AchievementManager::Init()
} }
} }
void AchievementManager::SetUpdateCallback(UpdateCallback callback)
{
m_update_callback = std::move(callback);
m_update_callback();
}
AchievementManager::ResponseType AchievementManager::Login(const std::string& password) AchievementManager::ResponseType AchievementManager::Login(const std::string& password)
{ {
if (!m_is_runtime_initialized) if (!m_is_runtime_initialized)
return AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED; return AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED;
return VerifyCredentials(password); AchievementManager::ResponseType r_type = AchievementManager::ResponseType::UNKNOWN_FAILURE;
{
std::lock_guard lg{m_lock};
r_type = VerifyCredentials(password);
}
if (m_update_callback)
m_update_callback();
return r_type;
} }
void AchievementManager::LoginAsync(const std::string& password, const ResponseCallback& callback) void AchievementManager::LoginAsync(const std::string& password, const ResponseCallback& callback)
@ -52,7 +65,14 @@ void AchievementManager::LoginAsync(const std::string& password, const ResponseC
callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED); callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED);
return; return;
} }
m_queue.EmplaceItem([this, password, callback] { callback(VerifyCredentials(password)); }); m_queue.EmplaceItem([this, password, callback] {
{
std::lock_guard lg{m_lock};
callback(VerifyCredentials(password));
}
if (m_update_callback)
m_update_callback();
});
} }
bool AchievementManager::IsLoggedIn() const bool AchievementManager::IsLoggedIn() const
@ -141,11 +161,11 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
} }
const auto fetch_game_data_response = FetchGameData(); const auto fetch_game_data_response = FetchGameData();
m_is_game_loaded = fetch_game_data_response == ResponseType::SUCCESS; if (fetch_game_data_response != ResponseType::SUCCESS)
if (!m_is_game_loaded)
{ {
OSD::AddMessage("Unable to retrieve data from RetroAchievements server.", OSD::AddMessage("Unable to retrieve data from RetroAchievements server.",
OSD::Duration::VERY_LONG, OSD::Color::RED); OSD::Duration::VERY_LONG, OSD::Color::RED);
return;
} }
// Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in // Claim the lock, then queue the fetch unlock data calls, then initialize the unlock map in
@ -154,6 +174,7 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
// it. // it.
{ {
std::lock_guard lg{m_lock}; std::lock_guard lg{m_lock};
m_is_game_loaded = true;
LoadUnlockData([](ResponseType r_type) {}); LoadUnlockData([](ResponseType r_type) {});
ActivateDeactivateAchievements(); ActivateDeactivateAchievements();
PointSpread spread = TallyScore(); PointSpread spread = TallyScore();
@ -179,10 +200,17 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
// Reset this to zero so that RP immediately triggers on the first frame // Reset this to zero so that RP immediately triggers on the first frame
m_last_ping_time = 0; m_last_ping_time = 0;
if (m_update_callback)
m_update_callback();
callback(fetch_game_data_response); callback(fetch_game_data_response);
}); });
} }
bool AchievementManager::IsGameLoaded() const
{
return m_is_game_loaded;
}
void AchievementManager::LoadUnlockData(const ResponseCallback& callback) void AchievementManager::LoadUnlockData(const ResponseCallback& callback)
{ {
m_queue.EmplaceItem([this, callback] { m_queue.EmplaceItem([this, callback] {
@ -194,6 +222,8 @@ void AchievementManager::LoadUnlockData(const ResponseCallback& callback)
} }
callback(FetchUnlockData(false)); callback(FetchUnlockData(false));
if (m_update_callback)
m_update_callback();
}); });
} }
@ -296,39 +326,114 @@ u32 AchievementManager::MemoryPeeker(u32 address, u32 num_bytes, void* ud)
void AchievementManager::AchievementEventHandler(const rc_runtime_event_t* runtime_event) void AchievementManager::AchievementEventHandler(const rc_runtime_event_t* runtime_event)
{ {
switch (runtime_event->type)
{ {
case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED: std::lock_guard lg{m_lock};
HandleAchievementTriggeredEvent(runtime_event); switch (runtime_event->type)
break; {
case RC_RUNTIME_EVENT_LBOARD_STARTED: case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED:
HandleLeaderboardStartedEvent(runtime_event); HandleAchievementTriggeredEvent(runtime_event);
break; break;
case RC_RUNTIME_EVENT_LBOARD_CANCELED: case RC_RUNTIME_EVENT_LBOARD_STARTED:
HandleLeaderboardCanceledEvent(runtime_event); HandleLeaderboardStartedEvent(runtime_event);
break; break;
case RC_RUNTIME_EVENT_LBOARD_TRIGGERED: case RC_RUNTIME_EVENT_LBOARD_CANCELED:
HandleLeaderboardTriggeredEvent(runtime_event); HandleLeaderboardCanceledEvent(runtime_event);
break; break;
case RC_RUNTIME_EVENT_LBOARD_TRIGGERED:
HandleLeaderboardTriggeredEvent(runtime_event);
break;
}
} }
if (m_update_callback)
m_update_callback();
}
std::recursive_mutex* AchievementManager::GetLock()
{
return &m_lock;
}
std::string AchievementManager::GetPlayerDisplayName() const
{
return IsLoggedIn() ? m_display_name : "";
}
u32 AchievementManager::GetPlayerScore() const
{
return IsLoggedIn() ? m_player_score : 0;
}
std::string AchievementManager::GetGameDisplayName() const
{
return IsGameLoaded() ? m_game_data.title : "";
}
AchievementManager::PointSpread AchievementManager::TallyScore() const
{
PointSpread spread{};
if (!IsGameLoaded())
return spread;
for (const auto& entry : m_unlock_map)
{
u32 points = entry.second.points;
spread.total_count++;
spread.total_points += points;
if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::HARDCORE ||
(hardcore_mode_enabled && entry.second.session_unlock_count > 0))
{
spread.hard_unlocks++;
spread.hard_points += points;
}
else if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE ||
entry.second.session_unlock_count > 0)
{
spread.soft_unlocks++;
spread.soft_points += points;
}
}
return spread;
}
rc_api_fetch_game_data_response_t* AchievementManager::GetGameData()
{
return &m_game_data;
}
AchievementManager::UnlockStatus
AchievementManager::GetUnlockStatus(AchievementId achievement_id) const
{
return m_unlock_map.at(achievement_id);
}
void AchievementManager::GetAchievementProgress(AchievementId achievement_id, u32* value,
u32* target)
{
rc_runtime_get_achievement_measured(&m_runtime, achievement_id, value, target);
} }
void AchievementManager::CloseGame() void AchievementManager::CloseGame()
{ {
m_is_game_loaded = false; {
m_game_id = 0; std::lock_guard lg{m_lock};
m_queue.Cancel(); m_is_game_loaded = false;
m_unlock_map.clear(); m_game_id = 0;
m_system = nullptr; m_queue.Cancel();
ActivateDeactivateAchievements(); m_unlock_map.clear();
ActivateDeactivateLeaderboards(); m_system = nullptr;
ActivateDeactivateRichPresence(); ActivateDeactivateAchievements();
ActivateDeactivateLeaderboards();
ActivateDeactivateRichPresence();
}
if (m_update_callback)
m_update_callback();
} }
void AchievementManager::Logout() void AchievementManager::Logout()
{ {
CloseGame(); CloseGame();
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, ""); Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
if (m_update_callback)
m_update_callback();
} }
void AchievementManager::Shutdown() void AchievementManager::Shutdown()
@ -353,6 +458,7 @@ AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std
{ {
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, login_data.api_token); Config::SetBaseOrCurrent(Config::RA_API_TOKEN, login_data.api_token);
m_display_name = login_data.display_name; m_display_name = login_data.display_name;
m_player_score = login_data.score;
} }
rc_api_destroy_login_response(&login_data); rc_api_destroy_login_response(&login_data);
return r_type; return r_type;
@ -618,30 +724,6 @@ void AchievementManager::HandleLeaderboardTriggeredEvent(const rc_runtime_event_
} }
} }
AchievementManager::PointSpread AchievementManager::TallyScore() const
{
PointSpread spread{};
for (const auto& entry : m_unlock_map)
{
u32 points = entry.second.points;
spread.total_count++;
spread.total_points += points;
if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::HARDCORE ||
(hardcore_mode_enabled && entry.second.session_unlock_count > 0))
{
spread.hard_unlocks++;
spread.hard_points += points;
}
else if (entry.second.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE ||
entry.second.session_unlock_count > 0)
{
spread.soft_unlocks++;
spread.soft_points += points;
}
}
return spread;
}
// Every RetroAchievements API call, with only a partial exception for fetch_image, follows // Every RetroAchievements API call, with only a partial exception for fetch_image, follows
// the same design pattern (here, X is the name of the call): // the same design pattern (here, X is the name of the call):
// Create a specific rc_api_X_request_t struct and populate with the necessary values // Create a specific rc_api_X_request_t struct and populate with the necessary values

View File

@ -41,6 +41,7 @@ public:
UNKNOWN_FAILURE UNKNOWN_FAILURE
}; };
using ResponseCallback = std::function<void(ResponseType)>; using ResponseCallback = std::function<void(ResponseType)>;
using UpdateCallback = std::function<void()>;
struct PointSpread struct PointSpread
{ {
@ -52,12 +53,27 @@ public:
u32 soft_points; u32 soft_points;
}; };
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;
};
static AchievementManager* GetInstance(); static AchievementManager* GetInstance();
void Init(); void Init();
void SetUpdateCallback(UpdateCallback callback);
ResponseType Login(const std::string& password); ResponseType Login(const std::string& password);
void LoginAsync(const std::string& password, const ResponseCallback& callback); void LoginAsync(const std::string& password, const ResponseCallback& callback);
bool IsLoggedIn() const; bool IsLoggedIn() const;
void LoadGameByFilenameAsync(const std::string& iso_path, const ResponseCallback& callback); void LoadGameByFilenameAsync(const std::string& iso_path, const ResponseCallback& callback);
bool IsGameLoaded() const;
void LoadUnlockData(const ResponseCallback& callback); void LoadUnlockData(const ResponseCallback& callback);
void ActivateDeactivateAchievements(); void ActivateDeactivateAchievements();
@ -68,6 +84,15 @@ public:
u32 MemoryPeeker(u32 address, u32 num_bytes, void* ud); u32 MemoryPeeker(u32 address, u32 num_bytes, void* ud);
void AchievementEventHandler(const rc_runtime_event_t* runtime_event); void AchievementEventHandler(const rc_runtime_event_t* runtime_event);
std::recursive_mutex* GetLock();
std::string GetPlayerDisplayName() const;
u32 GetPlayerScore() const;
std::string GetGameDisplayName() const;
PointSpread TallyScore() const;
rc_api_fetch_game_data_response_t* GetGameData();
UnlockStatus GetUnlockStatus(AchievementId achievement_id) const;
void GetAchievementProgress(AchievementId achievement_id, u32* value, u32* target);
void CloseGame(); void CloseGame();
void Logout(); void Logout();
void Shutdown(); void Shutdown();
@ -95,8 +120,6 @@ private:
void HandleLeaderboardCanceledEvent(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); void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event);
PointSpread TallyScore() const;
template <typename RcRequest, typename RcResponse> template <typename RcRequest, typename RcResponse>
ResponseType Request(RcRequest rc_request, RcResponse* rc_response, ResponseType Request(RcRequest rc_request, RcResponse* rc_response,
const std::function<int(rc_api_request_t*, const RcRequest*)>& init_request, const std::function<int(rc_api_request_t*, const RcRequest*)>& init_request,
@ -105,25 +128,15 @@ private:
rc_runtime_t m_runtime{}; rc_runtime_t m_runtime{};
Core::System* m_system{}; Core::System* m_system{};
bool m_is_runtime_initialized = false; bool m_is_runtime_initialized = false;
UpdateCallback m_update_callback;
std::string m_display_name; std::string m_display_name;
u32 m_player_score = 0;
std::array<char, HASH_LENGTH> m_game_hash{}; std::array<char, HASH_LENGTH> m_game_hash{};
u32 m_game_id = 0; u32 m_game_id = 0;
rc_api_fetch_game_data_response_t m_game_data{}; rc_api_fetch_game_data_response_t m_game_data{};
bool m_is_game_loaded = false; bool m_is_game_loaded = false;
time_t m_last_ping_time = 0; time_t m_last_ping_time = 0;
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;
};
std::unordered_map<AchievementId, UnlockStatus> m_unlock_map; std::unordered_map<AchievementId, UnlockStatus> m_unlock_map;
Common::WorkQueueThread<std::function<void()>> m_queue; Common::WorkQueueThread<std::function<void()>> m_queue;

View File

@ -0,0 +1,140 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef USE_RETRO_ACHIEVEMENTS
#include "DolphinQt/Achievements/AchievementHeaderWidget.h"
#include <QCheckBox>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QProgressBar>
#include <QPushButton>
#include <QString>
#include <QVBoxLayout>
#include <fmt/format.h>
#include <rcheevos/include/rc_api_runtime.h>
#include <rcheevos/include/rc_api_user.h>
#include <rcheevos/include/rc_runtime.h>
#include "Core/AchievementManager.h"
#include "Core/Core.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
#include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h"
AchievementHeaderWidget::AchievementHeaderWidget(QWidget* parent) : QWidget(parent)
{
m_user_name = new QLabel();
m_user_points = new QLabel();
m_game_name = new QLabel();
m_game_points = new QLabel();
m_game_progress_hard = new QProgressBar();
m_game_progress_soft = new QProgressBar();
m_rich_presence = new QLabel();
QVBoxLayout* m_user_right_col = new QVBoxLayout();
m_user_right_col->addWidget(m_user_name);
m_user_right_col->addWidget(m_user_points);
QHBoxLayout* m_user_layout = new QHBoxLayout();
// TODO: player badge goes here
m_user_layout->addLayout(m_user_right_col);
m_user_box = new QGroupBox();
m_user_box->setLayout(m_user_layout);
QVBoxLayout* m_game_right_col = new QVBoxLayout();
m_game_right_col->addWidget(m_game_name);
m_game_right_col->addWidget(m_game_points);
m_game_right_col->addWidget(m_game_progress_hard);
m_game_right_col->addWidget(m_game_progress_soft);
QHBoxLayout* m_game_upper_row = new QHBoxLayout();
// TODO: player badge and game badge go here
m_game_upper_row->addLayout(m_game_right_col);
QVBoxLayout* m_game_layout = new QVBoxLayout();
m_game_layout->addLayout(m_game_upper_row);
m_game_layout->addWidget(m_rich_presence);
m_game_box = new QGroupBox();
m_game_box->setLayout(m_game_layout);
QVBoxLayout* m_total = new QVBoxLayout();
m_total->addWidget(m_user_box);
m_total->addWidget(m_game_box);
UpdateData();
m_total->setContentsMargins(0, 0, 0, 0);
m_total->setAlignment(Qt::AlignTop);
setLayout(m_total);
}
void AchievementHeaderWidget::UpdateData()
{
if (!AchievementManager::GetInstance()->IsLoggedIn())
{
m_user_box->setVisible(false);
m_game_box->setVisible(false);
return;
}
QString user_name =
QString::fromStdString(AchievementManager::GetInstance()->GetPlayerDisplayName());
m_user_name->setText(user_name);
m_user_points->setText(tr("%1 points").arg(AchievementManager::GetInstance()->GetPlayerScore()));
if (!AchievementManager::GetInstance()->IsGameLoaded())
{
m_user_box->setVisible(true);
m_game_box->setVisible(false);
return;
}
AchievementManager::PointSpread point_spread = AchievementManager::GetInstance()->TallyScore();
m_game_name->setText(
QString::fromStdString(AchievementManager::GetInstance()->GetGameDisplayName()));
m_game_points->setText(GetPointsString(user_name, point_spread));
m_game_progress_hard = new QProgressBar();
m_game_progress_hard->setRange(0, point_spread.total_count);
m_game_progress_soft->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);
// TODO: RP needs a minor refactor to work here, will be a future PR
// m_rich_presence->setText(QString::fromStdString(AchievementManager::GetInstance()->GenerateRichPresence()));
// m_rich_presence->setVisible(Config::Get(Config::RA_RICH_PRESENCE_ENABLED));
m_rich_presence->setText(QString{});
m_rich_presence->setVisible(false);
m_user_box->setVisible(false);
m_game_box->setVisible(true);
}
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);
}
}
#endif // USE_RETRO_ACHIEVEMENTS

View File

@ -0,0 +1,42 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QWidget>
#include "Core/AchievementManager.h"
class QGroupBox;
class QLabel;
class QProgressBar;
class QVBoxLayout;
class AchievementHeaderWidget final : public QWidget
{
Q_OBJECT
public:
explicit AchievementHeaderWidget(QWidget* parent);
void UpdateData();
private:
QString GetPointsString(const QString& user_name,
const AchievementManager::PointSpread& point_spread) const;
QGroupBox* m_common_box;
QVBoxLayout* m_common_layout;
QLabel* m_user_name;
QLabel* m_user_points;
QLabel* m_game_name;
QLabel* m_game_points;
QProgressBar* m_game_progress_hard;
QProgressBar* m_game_progress_soft;
QLabel* m_rich_presence;
QGroupBox* m_user_box;
QGroupBox* m_game_box;
};
#endif // USE_RETRO_ACHIEVEMENTS

View File

@ -0,0 +1,124 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef USE_RETRO_ACHIEVEMENTS
#include "DolphinQt/Achievements/AchievementProgressWidget.h"
#include <QCheckBox>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QProgressBar>
#include <QPushButton>
#include <QString>
#include <QVBoxLayout>
#include <fmt/format.h>
#include <rcheevos/include/rc_api_runtime.h>
#include <rcheevos/include/rc_api_user.h>
#include <rcheevos/include/rc_runtime.h>
#include "Core/AchievementManager.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "DolphinQt/Config/ControllerInterface/ControllerInterfaceWindow.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
#include "DolphinQt/QtUtils/NonDefaultQPushButton.h"
#include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h"
AchievementProgressWidget::AchievementProgressWidget(QWidget* parent) : QWidget(parent)
{
m_common_box = new QGroupBox();
m_common_layout = new QVBoxLayout();
UpdateData();
m_common_box->setLayout(m_common_layout);
auto* layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setAlignment(Qt::AlignTop);
layout->addWidget(m_common_box);
setLayout(layout);
}
QGroupBox*
AchievementProgressWidget::CreateAchievementBox(const rc_api_achievement_definition_t* achievement)
{
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();
unsigned int value = 0;
unsigned int target = 0;
AchievementManager::GetInstance()->GetAchievementProgress(achievement->id, &value, &target);
if (target > 0)
{
a_progress_bar->setRange(0, target);
a_progress_bar->setValue(value);
}
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();
// TODO: achievement badge goes here
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()
{
QLayoutItem* item;
while ((item = m_common_layout->layout()->takeAt(0)) != nullptr)
{
delete item->widget();
delete item;
}
const auto* game_data = AchievementManager::GetInstance()->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))
{
return tr("Unlocked %1 times this session").arg(unlock_status.session_unlock_count);
}
return tr("Unlocked this session");
}
switch (unlock_status.remote_unlock_status)
{
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");
}
return {};
}
#endif // USE_RETRO_ACHIEVEMENTS

View File

@ -0,0 +1,34 @@
// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef USE_RETRO_ACHIEVEMENTS
#include <QWidget>
#include "Common/CommonTypes.h"
class QCheckBox;
class QGroupBox;
class QLineEdit;
class QPushButton;
class QVBoxLayout;
struct rc_api_achievement_definition_t;
class AchievementProgressWidget final : public QWidget
{
Q_OBJECT
public:
explicit AchievementProgressWidget(QWidget* parent);
void UpdateData();
private:
QGroupBox* CreateAchievementBox(const rc_api_achievement_definition_t* achievement);
QString GetStatusString(u32 achievement_id) const;
QGroupBox* m_common_box;
QVBoxLayout* m_common_layout;
};
#endif // USE_RETRO_ACHIEVEMENTS

View File

@ -4,11 +4,16 @@
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
#include "DolphinQt/Achievements/AchievementsWindow.h" #include "DolphinQt/Achievements/AchievementsWindow.h"
#include <mutex>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QTabWidget> #include <QTabWidget>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "DolphinQt/Achievements/AchievementHeaderWidget.h"
#include "DolphinQt/Achievements/AchievementProgressWidget.h"
#include "DolphinQt/Achievements/AchievementSettingsWidget.h" #include "DolphinQt/Achievements/AchievementSettingsWidget.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
#include "DolphinQt/QtUtils/WrapInScrollArea.h" #include "DolphinQt/QtUtils/WrapInScrollArea.h"
AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent) AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
@ -18,6 +23,8 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
CreateMainLayout(); CreateMainLayout();
ConnectWidgets(); ConnectWidgets();
AchievementManager::GetInstance()->SetUpdateCallback(
[this] { QueueOnObject(this, &AchievementsWindow::UpdateData); });
} }
void AchievementsWindow::showEvent(QShowEvent* event) void AchievementsWindow::showEvent(QShowEvent* event)
@ -30,13 +37,18 @@ void AchievementsWindow::CreateMainLayout()
{ {
auto* layout = new QVBoxLayout(); auto* layout = new QVBoxLayout();
m_header_widget = new AchievementHeaderWidget(this);
m_tab_widget = new QTabWidget(); m_tab_widget = new QTabWidget();
m_progress_widget = new AchievementProgressWidget(m_tab_widget);
m_tab_widget->addTab( m_tab_widget->addTab(
GetWrappedWidget(new AchievementSettingsWidget(m_tab_widget, this), this, 125, 100), GetWrappedWidget(new AchievementSettingsWidget(m_tab_widget, this), this, 125, 100),
tr("Settings")); tr("Settings"));
m_tab_widget->addTab(GetWrappedWidget(m_progress_widget, this, 125, 100), tr("Progress"));
m_tab_widget->setTabVisible(1, AchievementManager::GetInstance()->IsGameLoaded());
m_button_box = new QDialogButtonBox(QDialogButtonBox::Close); m_button_box = new QDialogButtonBox(QDialogButtonBox::Close);
layout->addWidget(m_header_widget);
layout->addWidget(m_tab_widget); layout->addWidget(m_tab_widget);
layout->addWidget(m_button_box); layout->addWidget(m_button_box);
@ -50,6 +62,14 @@ void AchievementsWindow::ConnectWidgets()
void AchievementsWindow::UpdateData() void AchievementsWindow::UpdateData()
{ {
{
std::lock_guard lg{*AchievementManager::GetInstance()->GetLock()};
m_header_widget->UpdateData();
m_header_widget->setVisible(AchievementManager::GetInstance()->IsLoggedIn());
// Settings tab handles its own updates ... indeed, that calls this
m_progress_widget->UpdateData();
m_tab_widget->setTabVisible(1, AchievementManager::GetInstance()->IsGameLoaded());
}
update(); update();
} }

View File

@ -6,8 +6,14 @@
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
#include <QDialog> #include <QDialog>
class QTabWidget; #include "Core/AchievementManager.h"
#include "DolphinQt/QtUtils/QueueOnObject.h"
class AchievementHeaderWidget;
class AchievementProgressWidget;
class QDialogButtonBox; class QDialogButtonBox;
class QTabWidget;
class UpdateCallback;
class AchievementsWindow : public QDialog class AchievementsWindow : public QDialog
{ {
@ -21,7 +27,9 @@ private:
void showEvent(QShowEvent* event); void showEvent(QShowEvent* event);
void ConnectWidgets(); void ConnectWidgets();
AchievementHeaderWidget* m_header_widget;
QTabWidget* m_tab_widget; QTabWidget* m_tab_widget;
AchievementProgressWidget* m_progress_widget;
QDialogButtonBox* m_button_box; QDialogButtonBox* m_button_box;
}; };

View File

@ -27,6 +27,10 @@ add_executable(dolphin-emu
CheatSearchWidget.h CheatSearchWidget.h
CheatsManager.cpp CheatsManager.cpp
CheatsManager.h CheatsManager.h
Achievements/AchievementHeaderWidget.cpp
Achievements/AchievementHeaderWidget.h
Achievements/AchievementProgressWidget.cpp
Achievements/AchievementProgressWidget.h
Achievements/AchievementSettingsWidget.cpp Achievements/AchievementSettingsWidget.cpp
Achievements/AchievementSettingsWidget.h Achievements/AchievementSettingsWidget.h
Achievements/AchievementsWindow.cpp Achievements/AchievementsWindow.cpp

View File

@ -50,6 +50,8 @@
<ClCompile Include="CheatSearchFactoryWidget.cpp" /> <ClCompile Include="CheatSearchFactoryWidget.cpp" />
<ClCompile Include="CheatSearchWidget.cpp" /> <ClCompile Include="CheatSearchWidget.cpp" />
<ClCompile Include="CheatsManager.cpp" /> <ClCompile Include="CheatsManager.cpp" />
<ClCompile Include="Achievements\AchievementHeaderWidget.cpp" />
<ClCompile Include="Achievements\AchievementProgressWidget.cpp" />
<ClCompile Include="Achievements\AchievementSettingsWidget.cpp" /> <ClCompile Include="Achievements\AchievementSettingsWidget.cpp" />
<ClCompile Include="Achievements\AchievementsWindow.cpp" /> <ClCompile Include="Achievements\AchievementsWindow.cpp" />
<ClCompile Include="Config\ARCodeWidget.cpp" /> <ClCompile Include="Config\ARCodeWidget.cpp" />
@ -256,6 +258,8 @@
<QtMoc Include="CheatSearchFactoryWidget.h" /> <QtMoc Include="CheatSearchFactoryWidget.h" />
<QtMoc Include="CheatSearchWidget.h" /> <QtMoc Include="CheatSearchWidget.h" />
<QtMoc Include="CheatsManager.h" /> <QtMoc Include="CheatsManager.h" />
<QtMoc Include="Achievements\AchievementHeaderWidget.h" />
<QtMoc Include="Achievements\AchievementProgressWidget.h" />
<QtMoc Include="Achievements\AchievementSettingsWidget.h" /> <QtMoc Include="Achievements\AchievementSettingsWidget.h" />
<QtMoc Include="Achievements\AchievementsWindow.h" /> <QtMoc Include="Achievements\AchievementsWindow.h" />
<QtMoc Include="Config\ARCodeWidget.h" /> <QtMoc Include="Config\ARCodeWidget.h" />