// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License #pragma once #include #include #include #include #include #include #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 * GetRequirements() { return &requirements; } vector * 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 requirements; // List of requirements vector 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 * GetUpdateItems() { return &update_items; } private: int32 id; // Achievement ID this update refers to int32 completed_date; // Timestamp when achievement was completed vector 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 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 * GetAchievements() { return &achievements; } private: map 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 * GetAchievementUpdates() { return &achievement_updates; } private: map 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 *requirements_in; vector *rewards_in; vector::iterator itr; vector::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::iterator itr; vector::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 *items_in; vector::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::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 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 lock(mutex_achievements); if (achievements.count(achievement_id) > 0) achievement = achievements[achievement_id]; return achievement; } void MasterAchievementList::ClearAchievements() { map::iterator itr; // Use unique_lock for write operations std::unique_lock 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 lock(mutex_achievements); return achievements.size(); } void MasterAchievementList::CreateMasterAchievementListPacket() { map::iterator itr; Achievement *achievement; vector *requirements = nullptr; vector::iterator itr2; AchievementRequirements *requirement; vector *rewards = nullptr; vector::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::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::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(); }