Added ActivateDeactivateAchievement to AchievementManager
ActivateDeactivateAchievement is passed an Achievement ID as returned from the FetchGameData API call and determines whether to activate it, deactivate it, or leave it where it is based on its current known state and what settings are enabled. Activating or deactivating an achievement entails calling a method provided by rcheevos that performs this on the rcheevos runtime. Activating an achievement loads its memory signature into the runtime; now the runtime will process the achievement each time the rc_runtime_do_frame function is called (this will be in a future PR) to determine when the achievement's requirements are met. Deactivating an achievement unloads it from the runtime. The specific logic to determine whether an achievement is active operates over many fields but is documented in detail inside the function. There are multiple settings flags for which achievements are enabled (one flag for all achievements, an "unofficial" flag for enabling achievements marked as unofficial i.e. those that have logic on the site but have not yet been officially approved, and an "encore" flag that enables achievements the player has already unlocked) and this function also evaluates whether the achievement has been unlocked in hardcore mode or softcore mode (though currently every reference to the current hardcore mode state is hardcoded as false).
This commit is contained in:
parent
ae18aa0639
commit
505f40cf9d
|
@ -14,6 +14,8 @@
|
|||
#include "Core/Core.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
|
||||
static constexpr bool hardcore_mode_enabled = false;
|
||||
|
||||
AchievementManager* AchievementManager::GetInstance()
|
||||
{
|
||||
static AchievementManager s_instance;
|
||||
|
@ -138,6 +140,7 @@ void AchievementManager::CloseGame()
|
|||
m_is_game_loaded = false;
|
||||
m_game_id = 0;
|
||||
m_queue.Cancel();
|
||||
m_unlock_map.clear();
|
||||
}
|
||||
|
||||
void AchievementManager::Logout()
|
||||
|
@ -212,6 +215,52 @@ AchievementManager::ResponseType AchievementManager::FetchGameData()
|
|||
rc_api_process_fetch_game_data_response);
|
||||
}
|
||||
|
||||
void AchievementManager::ActivateDeactivateAchievement(AchievementId id, bool enabled,
|
||||
bool unofficial, bool encore)
|
||||
{
|
||||
auto it = m_unlock_map.find(id);
|
||||
if (it == m_unlock_map.end())
|
||||
return;
|
||||
const UnlockStatus& status = it->second;
|
||||
u32 index = status.game_data_index;
|
||||
bool active = (rc_runtime_get_achievement(&m_runtime, id) != nullptr);
|
||||
|
||||
// Deactivate achievements if game is not loaded
|
||||
bool activate = m_is_game_loaded;
|
||||
// Activate achievements only if achievements are enabled
|
||||
if (activate && !enabled)
|
||||
activate = false;
|
||||
// Deactivate if achievement is unofficial, unless unofficial achievements are enabled
|
||||
if (activate && !unofficial &&
|
||||
m_game_data.achievements[index].category == RC_ACHIEVEMENT_CATEGORY_UNOFFICIAL)
|
||||
{
|
||||
activate = false;
|
||||
}
|
||||
// If encore mode is on, activate/deactivate regardless of current unlock status
|
||||
if (activate && !encore)
|
||||
{
|
||||
// Encore is off, achievement has been unlocked in this session, deactivate
|
||||
activate = (status.session_unlock_count == 0);
|
||||
// Encore is off, achievement has been hardcore unlocked on site, deactivate
|
||||
if (activate && status.remote_unlock_status == UnlockStatus::UnlockType::HARDCORE)
|
||||
activate = false;
|
||||
// Encore is off, hardcore is off, achievement has been softcore unlocked on site, deactivate
|
||||
if (activate && !hardcore_mode_enabled &&
|
||||
status.remote_unlock_status == UnlockStatus::UnlockType::SOFTCORE)
|
||||
{
|
||||
activate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!active && activate)
|
||||
{
|
||||
rc_runtime_activate_achievement(&m_runtime, id, m_game_data.achievements[index].definition,
|
||||
nullptr, 0);
|
||||
}
|
||||
if (active && !activate)
|
||||
rc_runtime_deactivate_achievement(&m_runtime, id);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <rcheevos/include/rc_api_runtime.h>
|
||||
#include <rcheevos/include/rc_api_user.h>
|
||||
|
@ -16,6 +17,8 @@
|
|||
#include "Common/Event.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
|
||||
using AchievementId = u32;
|
||||
|
||||
class AchievementManager
|
||||
{
|
||||
public:
|
||||
|
@ -50,6 +53,8 @@ private:
|
|||
ResponseType StartRASession();
|
||||
ResponseType FetchGameData();
|
||||
|
||||
void ActivateDeactivateAchievement(AchievementId id, bool enabled, bool unofficial, bool encore);
|
||||
|
||||
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,
|
||||
|
@ -61,6 +66,19 @@ private:
|
|||
rc_api_fetch_game_data_response_t m_game_data{};
|
||||
bool m_is_game_loaded = false;
|
||||
|
||||
struct UnlockStatus
|
||||
{
|
||||
AchievementId game_data_index = 0;
|
||||
enum class UnlockType
|
||||
{
|
||||
LOCKED,
|
||||
SOFTCORE,
|
||||
HARDCORE
|
||||
} remote_unlock_status = UnlockType::LOCKED;
|
||||
int session_unlock_count = 0;
|
||||
};
|
||||
std::unordered_map<AchievementId, UnlockStatus> m_unlock_map;
|
||||
|
||||
Common::WorkQueueThread<std::function<void()>> m_queue;
|
||||
}; // class AchievementManager
|
||||
|
||||
|
|
|
@ -13,4 +13,9 @@ namespace Config
|
|||
const Info<bool> RA_ENABLED{{System::Achievements, "Achievements", "Enabled"}, false};
|
||||
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_UNOFFICIAL_ENABLED{{System::Achievements, "Achievements", "UnofficialEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_ENCORE_ENABLED{{System::Achievements, "Achievements", "EncoreEnabled"}, false};
|
||||
} // namespace Config
|
||||
|
|
|
@ -11,4 +11,7 @@ namespace Config
|
|||
extern const Info<bool> RA_ENABLED;
|
||||
extern const Info<std::string> RA_USERNAME;
|
||||
extern const Info<std::string> RA_API_TOKEN;
|
||||
extern const Info<bool> RA_ACHIEVEMENTS_ENABLED;
|
||||
extern const Info<bool> RA_UNOFFICIAL_ENABLED;
|
||||
extern const Info<bool> RA_ENCORE_ENABLED;
|
||||
} // namespace Config
|
||||
|
|
Loading…
Reference in New Issue