Merge pull request #11878 from LillyJadeKatrin/retroachievements-achievements-tab
RetroAchievements - Achievement Progress Tab
This commit is contained in:
commit
da2784a391
|
@ -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)
|
||||
{
|
||||
if (!m_is_runtime_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)
|
||||
|
@ -52,7 +65,14 @@ void AchievementManager::LoginAsync(const std::string& password, const ResponseC
|
|||
callback(AchievementManager::ResponseType::MANAGER_NOT_INITIALIZED);
|
||||
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
|
||||
|
@ -141,11 +161,11 @@ void AchievementManager::LoadGameByFilenameAsync(const std::string& iso_path,
|
|||
}
|
||||
|
||||
const auto fetch_game_data_response = FetchGameData();
|
||||
m_is_game_loaded = fetch_game_data_response == ResponseType::SUCCESS;
|
||||
if (!m_is_game_loaded)
|
||||
if (fetch_game_data_response != ResponseType::SUCCESS)
|
||||
{
|
||||
OSD::AddMessage("Unable to retrieve data from RetroAchievements server.",
|
||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
m_is_game_loaded = true;
|
||||
LoadUnlockData([](ResponseType r_type) {});
|
||||
ActivateDeactivateAchievements();
|
||||
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
|
||||
m_last_ping_time = 0;
|
||||
|
||||
if (m_update_callback)
|
||||
m_update_callback();
|
||||
callback(fetch_game_data_response);
|
||||
});
|
||||
}
|
||||
|
||||
bool AchievementManager::IsGameLoaded() const
|
||||
{
|
||||
return m_is_game_loaded;
|
||||
}
|
||||
|
||||
void AchievementManager::LoadUnlockData(const ResponseCallback& callback)
|
||||
{
|
||||
m_queue.EmplaceItem([this, callback] {
|
||||
|
@ -194,6 +222,8 @@ void AchievementManager::LoadUnlockData(const ResponseCallback& callback)
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
switch (runtime_event->type)
|
||||
{
|
||||
case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED:
|
||||
HandleAchievementTriggeredEvent(runtime_event);
|
||||
break;
|
||||
case RC_RUNTIME_EVENT_LBOARD_STARTED:
|
||||
HandleLeaderboardStartedEvent(runtime_event);
|
||||
break;
|
||||
case RC_RUNTIME_EVENT_LBOARD_CANCELED:
|
||||
HandleLeaderboardCanceledEvent(runtime_event);
|
||||
break;
|
||||
case RC_RUNTIME_EVENT_LBOARD_TRIGGERED:
|
||||
HandleLeaderboardTriggeredEvent(runtime_event);
|
||||
break;
|
||||
std::lock_guard lg{m_lock};
|
||||
switch (runtime_event->type)
|
||||
{
|
||||
case RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED:
|
||||
HandleAchievementTriggeredEvent(runtime_event);
|
||||
break;
|
||||
case RC_RUNTIME_EVENT_LBOARD_STARTED:
|
||||
HandleLeaderboardStartedEvent(runtime_event);
|
||||
break;
|
||||
case RC_RUNTIME_EVENT_LBOARD_CANCELED:
|
||||
HandleLeaderboardCanceledEvent(runtime_event);
|
||||
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()
|
||||
{
|
||||
m_is_game_loaded = false;
|
||||
m_game_id = 0;
|
||||
m_queue.Cancel();
|
||||
m_unlock_map.clear();
|
||||
m_system = nullptr;
|
||||
ActivateDeactivateAchievements();
|
||||
ActivateDeactivateLeaderboards();
|
||||
ActivateDeactivateRichPresence();
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
m_is_game_loaded = false;
|
||||
m_game_id = 0;
|
||||
m_queue.Cancel();
|
||||
m_unlock_map.clear();
|
||||
m_system = nullptr;
|
||||
ActivateDeactivateAchievements();
|
||||
ActivateDeactivateLeaderboards();
|
||||
ActivateDeactivateRichPresence();
|
||||
}
|
||||
if (m_update_callback)
|
||||
m_update_callback();
|
||||
}
|
||||
|
||||
void AchievementManager::Logout()
|
||||
{
|
||||
CloseGame();
|
||||
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
|
||||
if (m_update_callback)
|
||||
m_update_callback();
|
||||
}
|
||||
|
||||
void AchievementManager::Shutdown()
|
||||
|
@ -353,6 +458,7 @@ AchievementManager::ResponseType AchievementManager::VerifyCredentials(const std
|
|||
{
|
||||
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, login_data.api_token);
|
||||
m_display_name = login_data.display_name;
|
||||
m_player_score = login_data.score;
|
||||
}
|
||||
rc_api_destroy_login_response(&login_data);
|
||||
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
|
||||
// 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
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
UNKNOWN_FAILURE
|
||||
};
|
||||
using ResponseCallback = std::function<void(ResponseType)>;
|
||||
using UpdateCallback = std::function<void()>;
|
||||
|
||||
struct PointSpread
|
||||
{
|
||||
|
@ -52,12 +53,27 @@ public:
|
|||
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();
|
||||
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 LoadGameByFilenameAsync(const std::string& iso_path, const ResponseCallback& callback);
|
||||
bool IsGameLoaded() const;
|
||||
|
||||
void LoadUnlockData(const ResponseCallback& callback);
|
||||
void ActivateDeactivateAchievements();
|
||||
|
@ -68,6 +84,15 @@ public:
|
|||
u32 MemoryPeeker(u32 address, u32 num_bytes, void* ud);
|
||||
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 Logout();
|
||||
void Shutdown();
|
||||
|
@ -95,8 +120,6 @@ private:
|
|||
void HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event);
|
||||
void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event);
|
||||
|
||||
PointSpread TallyScore() const;
|
||||
|
||||
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,
|
||||
|
@ -105,25 +128,15 @@ private:
|
|||
rc_runtime_t m_runtime{};
|
||||
Core::System* m_system{};
|
||||
bool m_is_runtime_initialized = false;
|
||||
UpdateCallback m_update_callback;
|
||||
std::string m_display_name;
|
||||
u32 m_player_score = 0;
|
||||
std::array<char, HASH_LENGTH> m_game_hash{};
|
||||
u32 m_game_id = 0;
|
||||
rc_api_fetch_game_data_response_t m_game_data{};
|
||||
bool m_is_game_loaded = false;
|
||||
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;
|
||||
|
||||
Common::WorkQueueThread<std::function<void()>> m_queue;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -4,11 +4,16 @@
|
|||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
#include "DolphinQt/Achievements/AchievementsWindow.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QTabWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "DolphinQt/Achievements/AchievementHeaderWidget.h"
|
||||
#include "DolphinQt/Achievements/AchievementProgressWidget.h"
|
||||
#include "DolphinQt/Achievements/AchievementSettingsWidget.h"
|
||||
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||
#include "DolphinQt/QtUtils/WrapInScrollArea.h"
|
||||
|
||||
AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
|
||||
|
@ -18,6 +23,8 @@ AchievementsWindow::AchievementsWindow(QWidget* parent) : QDialog(parent)
|
|||
|
||||
CreateMainLayout();
|
||||
ConnectWidgets();
|
||||
AchievementManager::GetInstance()->SetUpdateCallback(
|
||||
[this] { QueueOnObject(this, &AchievementsWindow::UpdateData); });
|
||||
}
|
||||
|
||||
void AchievementsWindow::showEvent(QShowEvent* event)
|
||||
|
@ -30,13 +37,18 @@ void AchievementsWindow::CreateMainLayout()
|
|||
{
|
||||
auto* layout = new QVBoxLayout();
|
||||
|
||||
m_header_widget = new AchievementHeaderWidget(this);
|
||||
m_tab_widget = new QTabWidget();
|
||||
m_progress_widget = new AchievementProgressWidget(m_tab_widget);
|
||||
m_tab_widget->addTab(
|
||||
GetWrappedWidget(new AchievementSettingsWidget(m_tab_widget, this), this, 125, 100),
|
||||
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);
|
||||
|
||||
layout->addWidget(m_header_widget);
|
||||
layout->addWidget(m_tab_widget);
|
||||
layout->addWidget(m_button_box);
|
||||
|
||||
|
@ -50,6 +62,14 @@ void AchievementsWindow::ConnectWidgets()
|
|||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,14 @@
|
|||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
#include <QDialog>
|
||||
|
||||
class QTabWidget;
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "DolphinQt/QtUtils/QueueOnObject.h"
|
||||
|
||||
class AchievementHeaderWidget;
|
||||
class AchievementProgressWidget;
|
||||
class QDialogButtonBox;
|
||||
class QTabWidget;
|
||||
class UpdateCallback;
|
||||
|
||||
class AchievementsWindow : public QDialog
|
||||
{
|
||||
|
@ -21,7 +27,9 @@ private:
|
|||
void showEvent(QShowEvent* event);
|
||||
void ConnectWidgets();
|
||||
|
||||
AchievementHeaderWidget* m_header_widget;
|
||||
QTabWidget* m_tab_widget;
|
||||
AchievementProgressWidget* m_progress_widget;
|
||||
QDialogButtonBox* m_button_box;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@ add_executable(dolphin-emu
|
|||
CheatSearchWidget.h
|
||||
CheatsManager.cpp
|
||||
CheatsManager.h
|
||||
Achievements/AchievementHeaderWidget.cpp
|
||||
Achievements/AchievementHeaderWidget.h
|
||||
Achievements/AchievementProgressWidget.cpp
|
||||
Achievements/AchievementProgressWidget.h
|
||||
Achievements/AchievementSettingsWidget.cpp
|
||||
Achievements/AchievementSettingsWidget.h
|
||||
Achievements/AchievementsWindow.cpp
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
<ClCompile Include="CheatSearchFactoryWidget.cpp" />
|
||||
<ClCompile Include="CheatSearchWidget.cpp" />
|
||||
<ClCompile Include="CheatsManager.cpp" />
|
||||
<ClCompile Include="Achievements\AchievementHeaderWidget.cpp" />
|
||||
<ClCompile Include="Achievements\AchievementProgressWidget.cpp" />
|
||||
<ClCompile Include="Achievements\AchievementSettingsWidget.cpp" />
|
||||
<ClCompile Include="Achievements\AchievementsWindow.cpp" />
|
||||
<ClCompile Include="Config\ARCodeWidget.cpp" />
|
||||
|
@ -256,6 +258,8 @@
|
|||
<QtMoc Include="CheatSearchFactoryWidget.h" />
|
||||
<QtMoc Include="CheatSearchWidget.h" />
|
||||
<QtMoc Include="CheatsManager.h" />
|
||||
<QtMoc Include="Achievements\AchievementHeaderWidget.h" />
|
||||
<QtMoc Include="Achievements\AchievementProgressWidget.h" />
|
||||
<QtMoc Include="Achievements\AchievementSettingsWidget.h" />
|
||||
<QtMoc Include="Achievements\AchievementsWindow.h" />
|
||||
<QtMoc Include="Config\ARCodeWidget.h" />
|
||||
|
|
Loading…
Reference in New Issue