575 lines
18 KiB
C++
575 lines
18 KiB
C++
// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License
|
|
|
|
#pragma once
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <shared_mutex>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include "../../common/types.hpp"
|
|
#include "../../common/log.hpp"
|
|
#include "../../common/config_reader.hpp"
|
|
#include "../Items/Items.h"
|
|
|
|
using namespace std;
|
|
|
|
// Forward declarations
|
|
extern ConfigReader configReader;
|
|
extern class MasterAchievementList master_achievement_list;
|
|
|
|
// Structure representing achievement reward data
|
|
struct AchievementRewards
|
|
{
|
|
int32 achievement_id; // ID of the associated achievement
|
|
string reward; // Description of the reward
|
|
};
|
|
|
|
// Structure representing achievement requirement data
|
|
struct AchievementRequirements
|
|
{
|
|
int32 achievement_id; // ID of the associated achievement
|
|
string name; // Name/description of the requirement
|
|
int32 qty_req; // Quantity required to fulfill this requirement
|
|
};
|
|
|
|
// Structure representing achievement update item data
|
|
struct AchievementUpdateItems
|
|
{
|
|
int32 achievement_id; // ID of the associated achievement
|
|
int32 item_update; // Update value for the item
|
|
};
|
|
|
|
// Main achievement class representing a single achievement with all its data
|
|
class Achievement
|
|
{
|
|
public:
|
|
// Default constructor - initializes all members to default values
|
|
Achievement();
|
|
|
|
// Copy constructor - creates deep copy of another achievement
|
|
Achievement(Achievement *in);
|
|
|
|
// Destructor - cleans up dynamically allocated requirement and reward structures
|
|
virtual ~Achievement();
|
|
|
|
// Setters for achievement properties
|
|
void SetID(int32 id) { this->id = id; }
|
|
void SetTitle(const char *title) { strncpy(this->title, title, sizeof(this->title)); }
|
|
void SetUncompletedText(const char *uncompleted_text) { strncpy(this->uncompleted_text, uncompleted_text, sizeof(this->uncompleted_text)); }
|
|
void SetCompletedText(const char *completed_text) { strncpy(this->completed_text, completed_text, sizeof(this->completed_text)); }
|
|
void SetCategory(const char *category) { strncpy(this->category, category, sizeof(this->category)); }
|
|
void SetExpansion(const char *expansion) { strncpy(this->expansion, expansion, sizeof(this->expansion)); }
|
|
void SetIcon(int16 icon) { this->icon = icon; }
|
|
void SetPointValue(int32 point_value) { this->point_value = point_value; }
|
|
void SetQtyReq(int32 qty_req) { this->qty_req = qty_req; }
|
|
void SetHide(bool hide) { this->hide = hide; }
|
|
void SetUnknown3a(int32 unknown3a) { this->unknown3a = unknown3a; }
|
|
void SetUnknown3b(int32 unknown3b) { this->unknown3b = unknown3b; }
|
|
|
|
// Adds a requirement structure to this achievement
|
|
void AddAchievementRequirement(struct AchievementRequirements *requirement);
|
|
|
|
// Adds a reward structure to this achievement
|
|
void AddAchievementReward(struct AchievementRewards *reward);
|
|
|
|
// Getters for achievement properties
|
|
int32 GetID() { return id; }
|
|
const char * GetTitle() { return title; }
|
|
const char * GetUncompletedText() { return uncompleted_text; }
|
|
const char * GetCompletedText() { return completed_text; }
|
|
const char * GetCategory() { return category; }
|
|
const char * GetExpansion() { return expansion; }
|
|
int16 GetIcon() { return icon; }
|
|
int32 GetPointValue() { return point_value; }
|
|
int32 GetQtyReq() { return qty_req; }
|
|
bool GetHide() { return hide; }
|
|
int32 GetUnknown3a() { return unknown3a; }
|
|
int32 GetUnknown3b() { return unknown3b; }
|
|
vector<struct AchievementRequirements *> * GetRequirements() { return &requirements; }
|
|
vector<struct AchievementRewards *> * GetRewards() { return &rewards; }
|
|
|
|
private:
|
|
int32 id; // Unique identifier for this achievement
|
|
char title[512]; // Display title of the achievement
|
|
char uncompleted_text[512]; // Text shown when achievement is not completed
|
|
char completed_text[512]; // Text shown when achievement is completed
|
|
char category[32]; // Category this achievement belongs to
|
|
char expansion[32]; // Expansion pack this achievement is from
|
|
int16 icon; // Icon ID for display
|
|
int32 point_value; // Point value awarded for completion
|
|
int32 qty_req; // Base quantity requirement
|
|
bool hide; // Whether this achievement should be hidden
|
|
int32 unknown3a; // Unknown field for future use
|
|
int32 unknown3b; // Unknown field for future use
|
|
vector<struct AchievementRequirements *> requirements; // List of requirements
|
|
vector<struct AchievementRewards *> rewards; // List of rewards
|
|
};
|
|
|
|
// Class representing an achievement update with completion data
|
|
class AchievementUpdate
|
|
{
|
|
public:
|
|
// Default constructor - initializes members to default values
|
|
AchievementUpdate();
|
|
|
|
// Copy constructor - creates deep copy of another achievement update
|
|
AchievementUpdate(AchievementUpdate *in);
|
|
|
|
// Destructor - cleans up dynamically allocated update item structures
|
|
virtual ~AchievementUpdate();
|
|
|
|
// Setters for update properties
|
|
void SetID(int32 id) { this->id = id; }
|
|
void SetCompletedDate(int32 completed_date) { this->completed_date = completed_date; }
|
|
|
|
// Adds an update item structure to this achievement update
|
|
void AddAchievementUpdateItems(struct AchievementUpdateItems *update_item);
|
|
|
|
// Getters for update properties
|
|
int32 GetID() { return id; }
|
|
int32 GetCompletedDate() { return completed_date; }
|
|
vector<struct AchievementUpdateItems *> * GetUpdateItems() { return &update_items; }
|
|
|
|
private:
|
|
int32 id; // Achievement ID this update refers to
|
|
int32 completed_date; // Timestamp when achievement was completed
|
|
vector<struct AchievementUpdateItems *> update_items; // List of update items
|
|
};
|
|
|
|
// Master list managing all achievements in the system with thread safety
|
|
class MasterAchievementList
|
|
{
|
|
public:
|
|
// Constructor - initializes member variables and mutex
|
|
MasterAchievementList();
|
|
|
|
// Destructor - cleans up all achievements and resources
|
|
virtual ~MasterAchievementList();
|
|
|
|
// Adds an achievement to the master list (thread-safe)
|
|
bool AddAchievement(Achievement *achievement);
|
|
|
|
// Retrieves an achievement by ID (thread-safe)
|
|
Achievement * GetAchievement(int32 achievement_id);
|
|
|
|
// Removes all achievements from the list (thread-safe)
|
|
void ClearAchievements();
|
|
|
|
// Returns the number of achievements in the list (thread-safe)
|
|
int32 Size();
|
|
|
|
// Creates the master achievement list packet for client communication
|
|
void CreateMasterAchievementListPacket();
|
|
|
|
// Returns the serialized achievement packet, null if not created
|
|
EQ2Packet * GetAchievementPacket() { return m_packetsCreated ? masterPacket : nullptr; }
|
|
|
|
EQ2Packet *masterPacket; // Serialized packet data for client
|
|
|
|
private:
|
|
std::shared_mutex mutex_achievements; // Thread safety for achievement operations
|
|
map<int32, Achievement *> achievements; // Map of achievement ID to achievement object
|
|
bool m_packetsCreated; // Flag indicating if packet has been created
|
|
};
|
|
|
|
// Player-specific achievement list without thread safety requirements
|
|
class PlayerAchievementList
|
|
{
|
|
public:
|
|
// Constructor - no special initialization needed
|
|
PlayerAchievementList();
|
|
|
|
// Destructor - cleans up all player achievements
|
|
virtual ~PlayerAchievementList();
|
|
|
|
// Adds an achievement to the player's list
|
|
bool AddAchievement(Achievement *achievement);
|
|
|
|
// Retrieves an achievement by ID from player's list
|
|
Achievement * GetAchievement(int32 achievement_id);
|
|
|
|
// Removes all achievements from player's list
|
|
void ClearAchievements();
|
|
|
|
// Returns the number of achievements in player's list
|
|
int32 Size();
|
|
|
|
// Returns direct access to the achievements map
|
|
map<int32, Achievement *> * GetAchievements() { return &achievements; }
|
|
|
|
private:
|
|
map<int32, Achievement *> achievements; // Map of player's achievements
|
|
};
|
|
|
|
// Player-specific achievement update list for tracking completion progress
|
|
class PlayerAchievementUpdateList
|
|
{
|
|
public:
|
|
// Constructor - no special initialization needed
|
|
PlayerAchievementUpdateList();
|
|
|
|
// Destructor - cleans up all achievement updates
|
|
virtual ~PlayerAchievementUpdateList();
|
|
|
|
// Adds an achievement update to the player's list
|
|
bool AddAchievementUpdate(AchievementUpdate *achievement_update);
|
|
|
|
// Removes all achievement updates from player's list
|
|
void ClearAchievementUpdates();
|
|
|
|
// Returns the number of achievement updates in player's list
|
|
int32 Size();
|
|
|
|
// Returns direct access to the achievement updates map
|
|
map<int32, AchievementUpdate *> * GetAchievementUpdates() { return &achievement_updates; }
|
|
|
|
private:
|
|
map<int32, AchievementUpdate *> achievement_updates; // Map of player's achievement updates
|
|
};
|
|
|
|
// Implementation of Achievement class methods
|
|
|
|
Achievement::Achievement()
|
|
{
|
|
id = 0;
|
|
memset(title, 0, sizeof(title));
|
|
memset(uncompleted_text, 0, sizeof(uncompleted_text));
|
|
memset(completed_text, 0, sizeof(completed_text));
|
|
memset(category, 0, sizeof(category));
|
|
memset(expansion, 0, sizeof(expansion));
|
|
icon = 0;
|
|
point_value = 0;
|
|
qty_req = 0;
|
|
hide = false;
|
|
unknown3a = 0;
|
|
unknown3b = 0;
|
|
}
|
|
|
|
Achievement::Achievement(Achievement *in)
|
|
{
|
|
vector<struct AchievementRequirements *> *requirements_in;
|
|
vector<struct AchievementRewards *> *rewards_in;
|
|
vector<struct AchievementRequirements *>::iterator itr;
|
|
vector<struct AchievementRewards *>::iterator itr2;
|
|
struct AchievementRequirements *achievement_requirement;
|
|
struct AchievementRewards *achievement_reward;
|
|
|
|
assert(in);
|
|
|
|
id = in->GetID();
|
|
strncpy(title, in->GetTitle(), sizeof(title));
|
|
strncpy(uncompleted_text, in->GetUncompletedText(), sizeof(uncompleted_text));
|
|
strncpy(completed_text, in->GetCompletedText(), sizeof(completed_text));
|
|
strncpy(category, in->GetCategory(), sizeof(category));
|
|
strncpy(expansion, in->GetExpansion(), sizeof(expansion));
|
|
icon = in->GetIcon();
|
|
point_value = in->GetPointValue();
|
|
qty_req = in->GetQtyReq();
|
|
hide = in->GetHide();
|
|
unknown3a = in->GetUnknown3a();
|
|
unknown3b = in->GetUnknown3b();
|
|
|
|
// Deep copy requirements
|
|
requirements_in = in->GetRequirements();
|
|
for (itr = requirements_in->begin(); itr != requirements_in->end(); itr++) {
|
|
achievement_requirement = new struct AchievementRequirements;
|
|
achievement_requirement->achievement_id = (*itr)->achievement_id;
|
|
achievement_requirement->name = (*itr)->name;
|
|
achievement_requirement->qty_req = (*itr)->qty_req;
|
|
requirements.push_back(achievement_requirement);
|
|
}
|
|
|
|
// Deep copy rewards
|
|
rewards_in = in->GetRewards();
|
|
for (itr2 = rewards_in->begin(); itr2 != rewards_in->end(); itr2++) {
|
|
achievement_reward = new struct AchievementRewards;
|
|
achievement_reward->achievement_id = (*itr2)->achievement_id;
|
|
achievement_reward->reward = (*itr2)->reward;
|
|
rewards.push_back(achievement_reward);
|
|
}
|
|
}
|
|
|
|
Achievement::~Achievement()
|
|
{
|
|
vector<struct AchievementRequirements *>::iterator itr;
|
|
vector<struct AchievementRewards *>::iterator itr2;
|
|
|
|
// Clean up all requirements
|
|
for (itr = requirements.begin(); itr != requirements.end(); itr++)
|
|
delete *itr;
|
|
|
|
// Clean up all rewards
|
|
for (itr2 = rewards.begin(); itr2 != rewards.end(); itr2++)
|
|
delete *itr2;
|
|
}
|
|
|
|
void Achievement::AddAchievementRequirement(struct AchievementRequirements *requirement)
|
|
{
|
|
assert(requirement);
|
|
requirements.push_back(requirement);
|
|
}
|
|
|
|
void Achievement::AddAchievementReward(struct AchievementRewards *reward)
|
|
{
|
|
assert(reward);
|
|
rewards.push_back(reward);
|
|
}
|
|
|
|
// Implementation of AchievementUpdate class methods
|
|
|
|
AchievementUpdate::AchievementUpdate()
|
|
{
|
|
id = 0;
|
|
completed_date = 0;
|
|
}
|
|
|
|
AchievementUpdate::AchievementUpdate(AchievementUpdate *in)
|
|
{
|
|
vector<struct AchievementUpdateItems *> *items_in;
|
|
vector<struct AchievementUpdateItems *>::iterator itr;
|
|
struct AchievementUpdateItems *items;
|
|
|
|
assert(in);
|
|
|
|
id = in->GetID();
|
|
completed_date = in->GetCompletedDate();
|
|
|
|
// Deep copy update items
|
|
items_in = in->GetUpdateItems();
|
|
for (itr = items_in->begin(); itr != items_in->end(); itr++) {
|
|
items = new struct AchievementUpdateItems;
|
|
items->achievement_id = (*itr)->achievement_id;
|
|
items->item_update = (*itr)->item_update;
|
|
update_items.push_back(items);
|
|
}
|
|
}
|
|
|
|
AchievementUpdate::~AchievementUpdate()
|
|
{
|
|
vector<struct AchievementUpdateItems *>::iterator itr;
|
|
|
|
// Clean up all update items
|
|
for (itr = update_items.begin(); itr != update_items.end(); itr++)
|
|
delete *itr;
|
|
}
|
|
|
|
void AchievementUpdate::AddAchievementUpdateItems(struct AchievementUpdateItems *update_item)
|
|
{
|
|
assert(update_item);
|
|
update_items.push_back(update_item);
|
|
}
|
|
|
|
// Implementation of MasterAchievementList class methods
|
|
|
|
MasterAchievementList::MasterAchievementList()
|
|
{
|
|
m_packetsCreated = false;
|
|
masterPacket = nullptr;
|
|
}
|
|
|
|
MasterAchievementList::~MasterAchievementList()
|
|
{
|
|
ClearAchievements();
|
|
}
|
|
|
|
bool MasterAchievementList::AddAchievement(Achievement *achievement)
|
|
{
|
|
bool ret = false;
|
|
|
|
assert(achievement);
|
|
|
|
// Use unique_lock for write operations
|
|
std::unique_lock<std::shared_mutex> lock(mutex_achievements);
|
|
if (achievements.count(achievement->GetID()) == 0) {
|
|
achievements[achievement->GetID()] = achievement;
|
|
ret = true;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
Achievement * MasterAchievementList::GetAchievement(int32 achievement_id)
|
|
{
|
|
Achievement *achievement = nullptr;
|
|
|
|
// Use shared_lock for read operations
|
|
std::shared_lock<std::shared_mutex> lock(mutex_achievements);
|
|
if (achievements.count(achievement_id) > 0)
|
|
achievement = achievements[achievement_id];
|
|
|
|
return achievement;
|
|
}
|
|
|
|
void MasterAchievementList::ClearAchievements()
|
|
{
|
|
map<int32, Achievement *>::iterator itr;
|
|
|
|
// Use unique_lock for write operations
|
|
std::unique_lock<std::shared_mutex> lock(mutex_achievements);
|
|
for (itr = achievements.begin(); itr != achievements.end(); itr++)
|
|
delete itr->second;
|
|
achievements.clear();
|
|
}
|
|
|
|
int32 MasterAchievementList::Size()
|
|
{
|
|
// Use shared_lock for read operations
|
|
std::shared_lock<std::shared_mutex> lock(mutex_achievements);
|
|
return achievements.size();
|
|
}
|
|
|
|
void MasterAchievementList::CreateMasterAchievementListPacket()
|
|
{
|
|
map<int32, Achievement *>::iterator itr;
|
|
Achievement *achievement;
|
|
vector<AchievementRequirements *> *requirements = nullptr;
|
|
vector<AchievementRequirements *>::iterator itr2;
|
|
AchievementRequirements *requirement;
|
|
vector<AchievementRewards *> *rewards = nullptr;
|
|
vector<AchievementRewards *>::iterator itr3;
|
|
AchievementRewards *reward;
|
|
PacketStruct *packet;
|
|
int16 i = 0;
|
|
int16 j = 0;
|
|
int16 k = 0;
|
|
int16 version = 1096;
|
|
|
|
// Get packet structure for achievement data
|
|
if (!(packet = configReader.getStruct("WS_CharacterAchievements", version))) {
|
|
return;
|
|
}
|
|
|
|
// Set up packet array for all achievements
|
|
packet->setArrayLengthByName("num_achievements", achievements.size());
|
|
for (itr = achievements.begin(); itr != achievements.end(); itr++) {
|
|
achievement = itr->second;
|
|
|
|
// Set basic achievement data
|
|
packet->setArrayDataByName("achievement_id", achievement->GetID(), i);
|
|
packet->setArrayDataByName("title", achievement->GetTitle(), i);
|
|
packet->setArrayDataByName("uncompleted_text", achievement->GetUncompletedText(), i);
|
|
packet->setArrayDataByName("completed_text", achievement->GetCompletedText(), i);
|
|
packet->setArrayDataByName("category", achievement->GetCategory(), i);
|
|
packet->setArrayDataByName("expansion", achievement->GetExpansion(), i);
|
|
packet->setArrayDataByName("icon", achievement->GetIcon(), i);
|
|
packet->setArrayDataByName("point_value", achievement->GetPointValue(), i);
|
|
packet->setArrayDataByName("qty_req", achievement->GetQtyReq(), i);
|
|
packet->setArrayDataByName("hide_achievement", achievement->GetHide(), i);
|
|
packet->setArrayDataByName("unknown3", achievement->GetUnknown3a(), i);
|
|
packet->setArrayDataByName("unknown3", achievement->GetUnknown3b(), i);
|
|
|
|
requirements = achievement->GetRequirements();
|
|
rewards = achievement->GetRewards();
|
|
j = 0;
|
|
k = 0;
|
|
|
|
// Set achievement requirements
|
|
packet->setSubArrayLengthByName("num_items", requirements->size(), i, j);
|
|
for (itr2 = requirements->begin(); itr2 != requirements->end(); itr2++) {
|
|
requirement = *itr2;
|
|
packet->setSubArrayDataByName("item_name", requirement->name.c_str(), i, j);
|
|
packet->setSubArrayDataByName("item_qty_req", requirement->qty_req, i, j);
|
|
j++;
|
|
}
|
|
|
|
// Set achievement rewards
|
|
packet->setSubArrayLengthByName("num_rewards", achievement->GetRewards()->size(), i, k);
|
|
for (itr3 = rewards->begin(); itr3 != rewards->end(); itr3++) {
|
|
reward = *itr3;
|
|
packet->setSubArrayDataByName("reward_item", reward->reward.c_str(), i, k);
|
|
k++;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
// Serialize and create final packet
|
|
EQ2Packet* data = packet->serialize();
|
|
masterPacket = new EQ2Packet(OP_ClientCmdMsg, data->pBuffer, data->size);
|
|
delete packet;
|
|
delete data;
|
|
|
|
m_packetsCreated = true;
|
|
}
|
|
|
|
// Implementation of PlayerAchievementList class methods
|
|
|
|
PlayerAchievementList::PlayerAchievementList()
|
|
{
|
|
}
|
|
|
|
PlayerAchievementList::~PlayerAchievementList()
|
|
{
|
|
ClearAchievements();
|
|
}
|
|
|
|
bool PlayerAchievementList::AddAchievement(Achievement *achievement)
|
|
{
|
|
assert(achievement);
|
|
|
|
if (achievements.count(achievement->GetID()) == 0) {
|
|
achievements[achievement->GetID()] = achievement;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Achievement * PlayerAchievementList::GetAchievement(int32 achievement_id)
|
|
{
|
|
if (achievements.count(achievement_id) > 0)
|
|
return achievements[achievement_id];
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void PlayerAchievementList::ClearAchievements()
|
|
{
|
|
map<int32, Achievement *>::iterator itr;
|
|
|
|
for (itr = achievements.begin(); itr != achievements.end(); itr++)
|
|
delete itr->second;
|
|
achievements.clear();
|
|
}
|
|
|
|
int32 PlayerAchievementList::Size()
|
|
{
|
|
return achievements.size();
|
|
}
|
|
|
|
// Implementation of PlayerAchievementUpdateList class methods
|
|
|
|
PlayerAchievementUpdateList::PlayerAchievementUpdateList()
|
|
{
|
|
}
|
|
|
|
PlayerAchievementUpdateList::~PlayerAchievementUpdateList()
|
|
{
|
|
ClearAchievementUpdates();
|
|
}
|
|
|
|
bool PlayerAchievementUpdateList::AddAchievementUpdate(AchievementUpdate *update)
|
|
{
|
|
assert(update);
|
|
|
|
if (achievement_updates.count(update->GetID()) == 0) {
|
|
achievement_updates[update->GetID()] = update;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PlayerAchievementUpdateList::ClearAchievementUpdates()
|
|
{
|
|
map<int32, AchievementUpdate *>::iterator itr;
|
|
|
|
for (itr = achievement_updates.begin(); itr != achievement_updates.end(); itr++)
|
|
delete itr->second;
|
|
achievement_updates.clear();
|
|
}
|
|
|
|
int32 PlayerAchievementUpdateList::Size()
|
|
{
|
|
return achievement_updates.size();
|
|
} |