From a0f7a0f7999f30e35bd09746fa8a68eb267e9ef1 Mon Sep 17 00:00:00 2001 From: cdemeyer-teachx Date: Wed, 12 Nov 2025 08:40:55 +0900 Subject: [PATCH] HarvestableInfo.xml, Achievements.xml, Loot.xml, NPCInfo.xml and Shops.xml. --- .gitignore | 1 + include/configs/achievements.hpp | 67 +++++++++++++++++ include/configs/harvestables.hpp | 65 ++++++++++++++++ include/configs/loot.hpp | 61 ++++++++++++++++ include/configs/npcs.hpp | 68 +++++++++++++++++ include/configs/shops.hpp | 61 ++++++++++++++++ src/configs/achievements.cpp | 122 +++++++++++++++++++++++++++++++ src/configs/harvestables.cpp | 91 +++++++++++++++++++++++ src/configs/loot.cpp | 86 ++++++++++++++++++++++ src/configs/npcs.cpp | 111 ++++++++++++++++++++++++++++ src/configs/shops.cpp | 86 ++++++++++++++++++++++ 11 files changed, 819 insertions(+) create mode 100644 include/configs/achievements.hpp create mode 100644 include/configs/harvestables.hpp create mode 100644 include/configs/loot.hpp create mode 100644 include/configs/npcs.hpp create mode 100644 include/configs/shops.hpp create mode 100644 src/configs/achievements.cpp create mode 100644 src/configs/harvestables.cpp create mode 100644 src/configs/loot.cpp create mode 100644 src/configs/npcs.cpp create mode 100644 src/configs/shops.cpp diff --git a/.gitignore b/.gitignore index 8bc3772..439e37c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ CBAssets +build \ No newline at end of file diff --git a/include/configs/achievements.hpp b/include/configs/achievements.hpp new file mode 100644 index 0000000..467624d --- /dev/null +++ b/include/configs/achievements.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include + +#include + +#include "constants.h" + +namespace cursebreaker { + +struct AchievementRequirement +{ + ActionType action{}; + uint16_t targetId{}; // e.g., item id, npc id, etc. + uint16_t count{}; + std::string description; +}; + +struct Achievement +{ + uint16_t id{}; + uint16_t points{}; + uint16_t category{}; + + std::string name; + std::string description; + std::string icon; + + std::vector requirements; + + Achievement() = default; +}; + +// Achievements configuration manager +class AchievementsConfig { +public: + static AchievementsConfig& getInstance() { + static AchievementsConfig instance; + return instance; + } + + bool loadFromXML(const std::string& filepath); + const Achievement* getAchievementById(int id) const; + const std::unordered_map& getAllAchievements() const { return m_achievements; } + +private: + AchievementsConfig() = default; + ~AchievementsConfig() = default; + AchievementsConfig(const AchievementsConfig&) = delete; + AchievementsConfig& operator=(const AchievementsConfig&) = delete; + + std::unordered_map m_achievements; + + // Helper methods for parsing + void parseAchievement(tinyxml2::XMLElement* achievementElement); + void parseRequirements(tinyxml2::XMLElement* achievementElement, Achievement& achievement); + std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = ""); + int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0); + bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false); +}; + +// Helper functions for enum conversions +ActionType stringToActionType(const std::string& str); + +} // namespace cursebreaker \ No newline at end of file diff --git a/include/configs/harvestables.hpp b/include/configs/harvestables.hpp new file mode 100644 index 0000000..c945cc3 --- /dev/null +++ b/include/configs/harvestables.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +#include + +#include "constants.h" +#include "items.hpp" // for stringToSkillType + +namespace cursebreaker { + +struct HarvestableLootEntry +{ + uint16_t itemId{}; + uint16_t amount{}; + uint16_t chance{}; // percentage chance +}; + +struct Harvestable +{ + uint16_t id{}; + uint16_t level{}; + uint16_t respawnTime{}; // in seconds or minutes, TBD + + SkillType skill{}; + + std::string name; + std::string description; + + std::vector loot; + + Harvestable() = default; +}; + +// Harvestables configuration manager +class HarvestablesConfig { +public: + static HarvestablesConfig& getInstance() { + static HarvestablesConfig instance; + return instance; + } + + bool loadFromXML(const std::string& filepath); + const Harvestable* getHarvestableById(int id) const; + const std::unordered_map& getAllHarvestables() const { return m_harvestables; } + +private: + HarvestablesConfig() = default; + ~HarvestablesConfig() = default; + HarvestablesConfig(const HarvestablesConfig&) = delete; + HarvestablesConfig& operator=(const HarvestablesConfig&) = delete; + + std::unordered_map m_harvestables; + + // Helper methods for parsing + void parseHarvestable(tinyxml2::XMLElement* harvestableElement); + void parseLoot(tinyxml2::XMLElement* harvestableElement, Harvestable& harvestable); + std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = ""); + int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0); + bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false); +}; + +} // namespace cursebreaker \ No newline at end of file diff --git a/include/configs/loot.hpp b/include/configs/loot.hpp new file mode 100644 index 0000000..af59fa7 --- /dev/null +++ b/include/configs/loot.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include + +#include "constants.h" + +namespace cursebreaker { + +struct LootEntry +{ + uint16_t itemId{}; + uint16_t minAmount{}; + uint16_t maxAmount{}; + uint16_t chance{}; // percentage chance + uint16_t level{}; // minimum level required +}; + +struct LootTable +{ + uint16_t id{}; + std::string name; + std::string description; + + std::vector entries; + + LootTable() = default; +}; + +// Loot configuration manager +class LootConfig { +public: + static LootConfig& getInstance() { + static LootConfig instance; + return instance; + } + + bool loadFromXML(const std::string& filepath); + const LootTable* getLootTableById(int id) const; + const std::unordered_map& getAllLootTables() const { return m_lootTables; } + +private: + LootConfig() = default; + ~LootConfig() = default; + LootConfig(const LootConfig&) = delete; + LootConfig& operator=(const LootConfig&) = delete; + + std::unordered_map m_lootTables; + + // Helper methods for parsing + void parseLootTable(tinyxml2::XMLElement* lootTableElement); + void parseEntries(tinyxml2::XMLElement* lootTableElement, LootTable& lootTable); + std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = ""); + int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0); + bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false); +}; + +} // namespace cursebreaker \ No newline at end of file diff --git a/include/configs/npcs.hpp b/include/configs/npcs.hpp new file mode 100644 index 0000000..f0e9512 --- /dev/null +++ b/include/configs/npcs.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include + +#include + +#include "constants.h" +#include "items.hpp" // for stringToStatType + +namespace cursebreaker { + +struct NPCStat +{ + StatType type{}; + uint16_t value{}; +}; + +struct NPC +{ + uint16_t id{}; + uint16_t level{}; + uint16_t health{}; + uint16_t mana{}; + uint16_t experience{}; + uint16_t lootTableId{}; + + std::string name; + std::string description; + std::string faction; + + std::vector stats; + std::vector lootItems; // alternative simple loot + + NPC() = default; +}; + +// NPCs configuration manager +class NPCsConfig { +public: + static NPCsConfig& getInstance() { + static NPCsConfig instance; + return instance; + } + + bool loadFromXML(const std::string& filepath); + const NPC* getNPCById(int id) const; + const std::unordered_map& getAllNPCs() const { return m_npcs; } + +private: + NPCsConfig() = default; + ~NPCsConfig() = default; + NPCsConfig(const NPCsConfig&) = delete; + NPCsConfig& operator=(const NPCsConfig&) = delete; + + std::unordered_map m_npcs; + + // Helper methods for parsing + void parseNPC(tinyxml2::XMLElement* npcElement); + void parseStats(tinyxml2::XMLElement* npcElement, NPC& npc); + void parseLoot(tinyxml2::XMLElement* npcElement, NPC& npc); + std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = ""); + int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0); + bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false); +}; + +} // namespace cursebreaker \ No newline at end of file diff --git a/include/configs/shops.hpp b/include/configs/shops.hpp new file mode 100644 index 0000000..dee38df --- /dev/null +++ b/include/configs/shops.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#include + +#include "constants.h" + +namespace cursebreaker { + +struct ShopItem +{ + uint16_t itemId{}; + uint16_t price{}; + uint16_t stock{}; // -1 for unlimited + uint16_t levelRequired{}; +}; + +struct Shop +{ + uint16_t id{}; + std::string name; + std::string description; + std::string location; + + std::vector inventory; + + Shop() = default; +}; + +// Shops configuration manager +class ShopsConfig { +public: + static ShopsConfig& getInstance() { + static ShopsConfig instance; + return instance; + } + + bool loadFromXML(const std::string& filepath); + const Shop* getShopById(int id) const; + const std::unordered_map& getAllShops() const { return m_shops; } + +private: + ShopsConfig() = default; + ~ShopsConfig() = default; + ShopsConfig(const ShopsConfig&) = delete; + ShopsConfig& operator=(const ShopsConfig&) = delete; + + std::unordered_map m_shops; + + // Helper methods for parsing + void parseShop(tinyxml2::XMLElement* shopElement); + void parseInventory(tinyxml2::XMLElement* shopElement, Shop& shop); + std::string getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue = ""); + int getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue = 0); + bool getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue = false); +}; + +} // namespace cursebreaker \ No newline at end of file diff --git a/src/configs/achievements.cpp b/src/configs/achievements.cpp new file mode 100644 index 0000000..3b0c32d --- /dev/null +++ b/src/configs/achievements.cpp @@ -0,0 +1,122 @@ +#include "configs/achievements.hpp" +#include +#include + +namespace cursebreaker { + +ActionType stringToActionType(const std::string& str) { + static const std::unordered_map actionMap = { + {"NpcDeath", ActionType::NpcDeath}, + {"PlayerDeath", ActionType::PlayerDeath}, + {"PlayerRespawn", ActionType::PlayerRespawn}, + {"NpcInteract", ActionType::NpcInteract}, + {"QuestUpdate", ActionType::QuestUpdate}, + {"QuestTimerEnd", ActionType::QuestTimerEnd}, + {"UseItemOnItem", ActionType::UseItemOnItem}, + {"UseItemOnNpc", ActionType::UseItemOnNpc}, + {"ConsumeItem", ActionType::ConsumeItem}, + {"NpcCombatInteract", ActionType::NpcCombatInteract}, + {"NpcKilledByPlayer", ActionType::NpcKilledByPlayer}, + {"EnterMap", ActionType::EnterMap}, + {"VarUpdated", ActionType::VarUpdated}, + {"GrantAchievement", ActionType::GrantAchievement}, + {"PlayerKilledByNpc", ActionType::PlayerKilledByNpc}, + {"UseItem", ActionType::UseItem}, + {"CraftItem", ActionType::CraftItem}, + {"HarvestItem", ActionType::HarvestItem}, + {"BuyItem", ActionType::BuyItem}, + {"NpcTagKilledByPlayer", ActionType::NpcTagKilledByPlayer}, + {"UseAbility", ActionType::UseAbility}, + {"LootItem", ActionType::LootItem}, + {"UseItemCategoryOnItem", ActionType::UseItemCategoryOnItem} + }; + + auto it = actionMap.find(str); + return (it != actionMap.end()) ? it->second : ActionType::none; +} + +bool AchievementsConfig::loadFromXML(const std::string& filepath) { + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filepath.c_str()); + + if (result != tinyxml2::XML_SUCCESS) { + std::cerr << "Failed to load achievements XML file: " << filepath << std::endl; + return false; + } + + tinyxml2::XMLElement* root = doc.FirstChildElement("achievements"); + if (!root) { + std::cerr << "Invalid XML structure: missing 'achievements' root element" << std::endl; + return false; + } + + m_achievements.clear(); + + // Parse all achievements + for (tinyxml2::XMLElement* achievementElement = root->FirstChildElement("achievement"); + achievementElement != nullptr; + achievementElement = achievementElement->NextSiblingElement("achievement")) { + parseAchievement(achievementElement); + } + + std::cout << "Loaded " << m_achievements.size() << " achievements from XML" << std::endl; + return true; +} + +void AchievementsConfig::parseAchievement(tinyxml2::XMLElement* achievementElement) { + Achievement achievement; + + // Parse basic attributes + achievement.id = static_cast(getAttributeValueInt(achievementElement, "id")); + achievement.name = getAttributeValue(achievementElement, "name"); + achievement.description = getAttributeValue(achievementElement, "description"); + achievement.icon = getAttributeValue(achievementElement, "icon"); + + // Parse numeric attributes + achievement.points = static_cast(getAttributeValueInt(achievementElement, "points")); + achievement.category = static_cast(getAttributeValueInt(achievementElement, "category")); + + // Parse child elements + parseRequirements(achievementElement, achievement); + + m_achievements[achievement.id] = std::move(achievement); +} + +void AchievementsConfig::parseRequirements(tinyxml2::XMLElement* achievementElement, Achievement& achievement) { + for (tinyxml2::XMLElement* reqElement = achievementElement->FirstChildElement("requirement"); + reqElement != nullptr; + reqElement = reqElement->NextSiblingElement("requirement")) { + + AchievementRequirement req; + req.action = stringToActionType(getAttributeValue(reqElement, "action")); + req.targetId = static_cast(getAttributeValueInt(reqElement, "targetid")); + req.count = static_cast(getAttributeValueInt(reqElement, "count")); + req.description = getAttributeValue(reqElement, "description"); + + achievement.requirements.push_back(req); + } +} + +const Achievement* AchievementsConfig::getAchievementById(int id) const { + auto it = m_achievements.find(id); + return (it != m_achievements.end()) ? &it->second : nullptr; +} + +std::string AchievementsConfig::getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) { + const char* value = element->Attribute(attribute); + return value ? std::string(value) : defaultValue; +} + +int AchievementsConfig::getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue) { + int value = defaultValue; + element->QueryIntAttribute(attribute, &value); + return value; +} + +bool AchievementsConfig::getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue) { + bool value = defaultValue; + element->QueryBoolAttribute(attribute, &value); + return value; +} + +} // namespace cursebreaker \ No newline at end of file diff --git a/src/configs/harvestables.cpp b/src/configs/harvestables.cpp new file mode 100644 index 0000000..39696ef --- /dev/null +++ b/src/configs/harvestables.cpp @@ -0,0 +1,91 @@ +#include "configs/harvestables.hpp" +#include +#include +#include + +namespace cursebreaker { + +bool HarvestablesConfig::loadFromXML(const std::string& filepath) { + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filepath.c_str()); + + if (result != tinyxml2::XML_SUCCESS) { + std::cerr << "Failed to load harvestables XML file: " << filepath << std::endl; + return false; + } + + tinyxml2::XMLElement* root = doc.FirstChildElement("harvestables"); + if (!root) { + std::cerr << "Invalid XML structure: missing 'harvestables' root element" << std::endl; + return false; + } + + m_harvestables.clear(); + + // Parse all harvestables + for (tinyxml2::XMLElement* harvestableElement = root->FirstChildElement("harvestable"); + harvestableElement != nullptr; + harvestableElement = harvestableElement->NextSiblingElement("harvestable")) { + parseHarvestable(harvestableElement); + } + + std::cout << "Loaded " << m_harvestables.size() << " harvestables from XML" << std::endl; + return true; +} + +void HarvestablesConfig::parseHarvestable(tinyxml2::XMLElement* harvestableElement) { + Harvestable harvestable; + + // Parse basic attributes + harvestable.id = static_cast(getAttributeValueInt(harvestableElement, "id")); + harvestable.name = getAttributeValue(harvestableElement, "name"); + harvestable.description = getAttributeValue(harvestableElement, "description"); + harvestable.skill = stringToSkillType(getAttributeValue(harvestableElement, "skill")); + + // Parse numeric attributes + harvestable.level = static_cast(getAttributeValueInt(harvestableElement, "level")); + harvestable.respawnTime = static_cast(getAttributeValueInt(harvestableElement, "respawntime")); + + // Parse child elements + parseLoot(harvestableElement, harvestable); + + m_harvestables[harvestable.id] = std::move(harvestable); +} + +void HarvestablesConfig::parseLoot(tinyxml2::XMLElement* harvestableElement, Harvestable& harvestable) { + for (tinyxml2::XMLElement* lootElement = harvestableElement->FirstChildElement("loot"); + lootElement != nullptr; + lootElement = lootElement->NextSiblingElement("loot")) { + + HarvestableLootEntry lootEntry; + lootEntry.itemId = static_cast(getAttributeValueInt(lootElement, "itemid")); + lootEntry.amount = static_cast(getAttributeValueInt(lootElement, "amount")); + lootEntry.chance = static_cast(getAttributeValueInt(lootElement, "chance")); + + harvestable.loot.push_back(lootEntry); + } +} + +const Harvestable* HarvestablesConfig::getHarvestableById(int id) const { + auto it = m_harvestables.find(id); + return (it != m_harvestables.end()) ? &it->second : nullptr; +} + +std::string HarvestablesConfig::getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) { + const char* value = element->Attribute(attribute); + return value ? std::string(value) : defaultValue; +} + +int HarvestablesConfig::getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue) { + int value = defaultValue; + element->QueryIntAttribute(attribute, &value); + return value; +} + +bool HarvestablesConfig::getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue) { + bool value = defaultValue; + element->QueryBoolAttribute(attribute, &value); + return value; +} + +} // namespace cursebreaker \ No newline at end of file diff --git a/src/configs/loot.cpp b/src/configs/loot.cpp new file mode 100644 index 0000000..7c58d30 --- /dev/null +++ b/src/configs/loot.cpp @@ -0,0 +1,86 @@ +#include "configs/loot.hpp" +#include + +namespace cursebreaker { + +bool LootConfig::loadFromXML(const std::string& filepath) { + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filepath.c_str()); + + if (result != tinyxml2::XML_SUCCESS) { + std::cerr << "Failed to load loot XML file: " << filepath << std::endl; + return false; + } + + tinyxml2::XMLElement* root = doc.FirstChildElement("lootTables"); + if (!root) { + std::cerr << "Invalid XML structure: missing 'lootTables' root element" << std::endl; + return false; + } + + m_lootTables.clear(); + + // Parse all loot tables + for (tinyxml2::XMLElement* lootTableElement = root->FirstChildElement("lootTable"); + lootTableElement != nullptr; + lootTableElement = lootTableElement->NextSiblingElement("lootTable")) { + parseLootTable(lootTableElement); + } + + std::cout << "Loaded " << m_lootTables.size() << " loot tables from XML" << std::endl; + return true; +} + +void LootConfig::parseLootTable(tinyxml2::XMLElement* lootTableElement) { + LootTable lootTable; + + // Parse basic attributes + lootTable.id = static_cast(getAttributeValueInt(lootTableElement, "id")); + lootTable.name = getAttributeValue(lootTableElement, "name"); + lootTable.description = getAttributeValue(lootTableElement, "description"); + + // Parse child elements + parseEntries(lootTableElement, lootTable); + + m_lootTables[lootTable.id] = std::move(lootTable); +} + +void LootConfig::parseEntries(tinyxml2::XMLElement* lootTableElement, LootTable& lootTable) { + for (tinyxml2::XMLElement* entryElement = lootTableElement->FirstChildElement("entry"); + entryElement != nullptr; + entryElement = entryElement->NextSiblingElement("entry")) { + + LootEntry entry; + entry.itemId = static_cast(getAttributeValueInt(entryElement, "itemid")); + entry.minAmount = static_cast(getAttributeValueInt(entryElement, "minamount")); + entry.maxAmount = static_cast(getAttributeValueInt(entryElement, "maxamount")); + entry.chance = static_cast(getAttributeValueInt(entryElement, "chance")); + entry.level = static_cast(getAttributeValueInt(entryElement, "level")); + + lootTable.entries.push_back(entry); + } +} + +const LootTable* LootConfig::getLootTableById(int id) const { + auto it = m_lootTables.find(id); + return (it != m_lootTables.end()) ? &it->second : nullptr; +} + +std::string LootConfig::getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) { + const char* value = element->Attribute(attribute); + return value ? std::string(value) : defaultValue; +} + +int LootConfig::getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue) { + int value = defaultValue; + element->QueryIntAttribute(attribute, &value); + return value; +} + +bool LootConfig::getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue) { + bool value = defaultValue; + element->QueryBoolAttribute(attribute, &value); + return value; +} + +} // namespace cursebreaker \ No newline at end of file diff --git a/src/configs/npcs.cpp b/src/configs/npcs.cpp new file mode 100644 index 0000000..c3097b6 --- /dev/null +++ b/src/configs/npcs.cpp @@ -0,0 +1,111 @@ +#include "configs/npcs.hpp" +#include +#include +#include + +namespace cursebreaker { + +bool NPCsConfig::loadFromXML(const std::string& filepath) { + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filepath.c_str()); + + if (result != tinyxml2::XML_SUCCESS) { + std::cerr << "Failed to load NPCs XML file: " << filepath << std::endl; + return false; + } + + tinyxml2::XMLElement* root = doc.FirstChildElement("npcs"); + if (!root) { + std::cerr << "Invalid XML structure: missing 'npcs' root element" << std::endl; + return false; + } + + m_npcs.clear(); + + // Parse all NPCs + for (tinyxml2::XMLElement* npcElement = root->FirstChildElement("npc"); + npcElement != nullptr; + npcElement = npcElement->NextSiblingElement("npc")) { + parseNPC(npcElement); + } + + std::cout << "Loaded " << m_npcs.size() << " NPCs from XML" << std::endl; + return true; +} + +void NPCsConfig::parseNPC(tinyxml2::XMLElement* npcElement) { + NPC npc; + + // Parse basic attributes + npc.id = static_cast(getAttributeValueInt(npcElement, "id")); + npc.name = getAttributeValue(npcElement, "name"); + npc.description = getAttributeValue(npcElement, "description"); + npc.faction = getAttributeValue(npcElement, "faction"); + + // Parse numeric attributes + npc.level = static_cast(getAttributeValueInt(npcElement, "level")); + npc.health = static_cast(getAttributeValueInt(npcElement, "health")); + npc.mana = static_cast(getAttributeValueInt(npcElement, "mana")); + npc.experience = static_cast(getAttributeValueInt(npcElement, "experience")); + npc.lootTableId = static_cast(getAttributeValueInt(npcElement, "loottableid")); + + // Parse child elements + parseStats(npcElement, npc); + parseLoot(npcElement, npc); + + m_npcs[npc.id] = std::move(npc); +} + +void NPCsConfig::parseStats(tinyxml2::XMLElement* npcElement, NPC& npc) { + for (tinyxml2::XMLElement* statElement = npcElement->FirstChildElement("stat"); + statElement != nullptr; + statElement = statElement->NextSiblingElement("stat")) { + + NPCStat stat; + std::string statTypeStr = getAttributeValue(statElement, "type"); + stat.type = stringToStatType(statTypeStr); + stat.value = static_cast(getAttributeValueInt(statElement, "value")); + + npc.stats.push_back(stat); + } +} + +void NPCsConfig::parseLoot(tinyxml2::XMLElement* npcElement, NPC& npc) { + std::string lootItemsStr = getAttributeValue(npcElement, "lootitems"); + if (!lootItemsStr.empty()) { + std::stringstream ss(lootItemsStr); + std::string itemStr; + while (std::getline(ss, itemStr, ',')) { + try { + uint16_t itemId = static_cast(std::stoi(itemStr)); + npc.lootItems.push_back(itemId); + } catch (const std::exception&) { + // Skip invalid items + } + } + } +} + +const NPC* NPCsConfig::getNPCById(int id) const { + auto it = m_npcs.find(id); + return (it != m_npcs.end()) ? &it->second : nullptr; +} + +std::string NPCsConfig::getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) { + const char* value = element->Attribute(attribute); + return value ? std::string(value) : defaultValue; +} + +int NPCsConfig::getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue) { + int value = defaultValue; + element->QueryIntAttribute(attribute, &value); + return value; +} + +bool NPCsConfig::getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue) { + bool value = defaultValue; + element->QueryBoolAttribute(attribute, &value); + return value; +} + +} // namespace cursebreaker \ No newline at end of file diff --git a/src/configs/shops.cpp b/src/configs/shops.cpp new file mode 100644 index 0000000..1cf8de7 --- /dev/null +++ b/src/configs/shops.cpp @@ -0,0 +1,86 @@ +#include "configs/shops.hpp" +#include + +namespace cursebreaker { + +bool ShopsConfig::loadFromXML(const std::string& filepath) { + tinyxml2::XMLDocument doc; + tinyxml2::XMLError result = doc.LoadFile(filepath.c_str()); + + if (result != tinyxml2::XML_SUCCESS) { + std::cerr << "Failed to load shops XML file: " << filepath << std::endl; + return false; + } + + tinyxml2::XMLElement* root = doc.FirstChildElement("shops"); + if (!root) { + std::cerr << "Invalid XML structure: missing 'shops' root element" << std::endl; + return false; + } + + m_shops.clear(); + + // Parse all shops + for (tinyxml2::XMLElement* shopElement = root->FirstChildElement("shop"); + shopElement != nullptr; + shopElement = shopElement->NextSiblingElement("shop")) { + parseShop(shopElement); + } + + std::cout << "Loaded " << m_shops.size() << " shops from XML" << std::endl; + return true; +} + +void ShopsConfig::parseShop(tinyxml2::XMLElement* shopElement) { + Shop shop; + + // Parse basic attributes + shop.id = static_cast(getAttributeValueInt(shopElement, "id")); + shop.name = getAttributeValue(shopElement, "name"); + shop.description = getAttributeValue(shopElement, "description"); + shop.location = getAttributeValue(shopElement, "location"); + + // Parse child elements + parseInventory(shopElement, shop); + + m_shops[shop.id] = std::move(shop); +} + +void ShopsConfig::parseInventory(tinyxml2::XMLElement* shopElement, Shop& shop) { + for (tinyxml2::XMLElement* itemElement = shopElement->FirstChildElement("item"); + itemElement != nullptr; + itemElement = itemElement->NextSiblingElement("item")) { + + ShopItem item; + item.itemId = static_cast(getAttributeValueInt(itemElement, "itemid")); + item.price = static_cast(getAttributeValueInt(itemElement, "price")); + item.stock = static_cast(getAttributeValueInt(itemElement, "stock")); + item.levelRequired = static_cast(getAttributeValueInt(itemElement, "levelrequired")); + + shop.inventory.push_back(item); + } +} + +const Shop* ShopsConfig::getShopById(int id) const { + auto it = m_shops.find(id); + return (it != m_shops.end()) ? &it->second : nullptr; +} + +std::string ShopsConfig::getAttributeValue(tinyxml2::XMLElement* element, const char* attribute, const std::string& defaultValue) { + const char* value = element->Attribute(attribute); + return value ? std::string(value) : defaultValue; +} + +int ShopsConfig::getAttributeValueInt(tinyxml2::XMLElement* element, const char* attribute, int defaultValue) { + int value = defaultValue; + element->QueryIntAttribute(attribute, &value); + return value; +} + +bool ShopsConfig::getAttributeValueBool(tinyxml2::XMLElement* element, const char* attribute, bool defaultValue) { + bool value = defaultValue; + element->QueryBoolAttribute(attribute, &value); + return value; +} + +} // namespace cursebreaker \ No newline at end of file