diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/game-server/item.h | 8 | ||||
-rw-r--r-- | src/game-server/itemmanager.cpp | 601 | ||||
-rw-r--r-- | src/game-server/itemmanager.h | 27 |
3 files changed, 337 insertions, 299 deletions
diff --git a/src/game-server/item.h b/src/game-server/item.h index f03e951a..d779e1f0 100644 --- a/src/game-server/item.h +++ b/src/game-server/item.h @@ -212,7 +212,6 @@ class ItemClass private: - /** * Add an effect to a trigger * @param effect The effect to be run when the trigger is hit. @@ -267,9 +266,8 @@ class ItemClass }; /** -* Class for an item stack laying on the floor in the game world -*/ - + * Class for an item stack laying on the floor in the game world + */ class Item : public Actor { public: @@ -289,4 +287,4 @@ class Item : public Actor int mLifetime; }; -#endif +#endif // ITEM_H diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp index 3e7c673a..3aa98b55 100644 --- a/src/game-server/itemmanager.cpp +++ b/src/game-server/itemmanager.cpp @@ -28,7 +28,6 @@ #include "scripting/script.h" #include "utils/logger.h" #include "utils/string.h" -#include "utils/xml.h" #include <map> #include <set> @@ -36,300 +35,20 @@ void ItemManager::initialize() { - mVisibleEquipSlotCount = 0; reload(); } void ItemManager::reload() { - std::string absPathFile; - xmlNodePtr rootNode; - - // #################################################################### - // ### Load the equip slots that a character has available to them. ### - // #################################################################### - - absPathFile = ResourceManager::resolve(mEquipCharSlotReferenceFile); - if (absPathFile.empty()) { - LOG_ERROR("Item Manager: Could not find " << mEquipCharSlotReferenceFile << "!"); - return; - } - - XML::Document doc(absPathFile, false); - rootNode = doc.rootNode(); - - if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "equip-slots")) - { - LOG_ERROR("Item Manager: Error while parsing equip slots database (" - << absPathFile << ")!"); - return; - } - - LOG_INFO("Loading equip slots: " << absPathFile); - - { - unsigned int totalCount = 0, slotCount = 0, visibleSlotCount = 0; - for_each_xml_child_node(node, rootNode) - { - if (xmlStrEqual(node->name, BAD_CAST "slot")) - { - const std::string name = XML::getProperty(node, "name", - std::string()); - const int count = XML::getProperty(node, "count", 0); - if (name.empty() || !count || count < 0) - LOG_WARN("Item Manager: equip slot has no name or zero count"); - else - { - bool visible = XML::getProperty(node, "visible", "false") != "false"; - if (visible) - { - visibleEquipSlots.push_back(equipSlots.size()); - if (++visibleSlotCount > 7) - LOG_WARN("Item Manager: More than 7 visible equip slot!" - "This will not work with current netcode!"); - } - equipSlots.push_back(std::pair<std::string, unsigned int> - (name, count)); - totalCount += count; - ++slotCount; - } - } - } - LOG_INFO("Loaded '" << slotCount << "' slot types with '" - << totalCount << "' slots."); - } - - // #################################### - // ### Load the main item database. ### - // #################################### - - absPathFile = ResourceManager::resolve(mItemReferenceFile); - if (absPathFile.empty()) { - LOG_ERROR("Item Manager: Could not find " << mItemReferenceFile << "!"); - return; - } - - XML::Document doc2(absPathFile, false); - rootNode = doc2.rootNode(); - - if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items")) - { - LOG_ERROR("Item Manager: Error while parsing item database (" - << absPathFile << ")!"); - return; - } - - LOG_INFO("Loading item reference: " << absPathFile); - - unsigned nbItems = 0; - for_each_xml_child_node(node, rootNode) - { - if (!xmlStrEqual(node->name, BAD_CAST "item")) - continue; - - int id = XML::getProperty(node, "id", 0); - if (id < 1) - { - LOG_WARN("Item Manager: Item ID: " << id << " is invalid in " - << mItemReferenceFile << ", and will be ignored."); - continue; - } - - - // Type is mostly unused, but still serves for - // hairsheets and race sheets. - std::string sItemType = XML::getProperty(node, "type", std::string()); - if (sItemType == "hairsprite" || sItemType == "racesprite") - continue; - - ItemClass *item; - ItemClasses::iterator i = itemClasses.find(id); - - unsigned int maxPerSlot = XML::getProperty(node, "max-per-slot", 0); - if (!maxPerSlot) - { - LOG_WARN("Item Manager: Missing max-per-slot property for " - "item " << id << " in " << mItemReferenceFile << '.'); - maxPerSlot = 1; - } - - if (i == itemClasses.end()) - { - item = new ItemClass(id, maxPerSlot); - itemClasses[id] = item; - } - else - { - LOG_WARN("Multiple defintions of item '" << id << "'!"); - item = i->second; - } - - std::string name = XML::getProperty(node, "name", "unnamed"); - item->setName(name); - - int value = XML::getProperty(node, "value", 0); - // Should have multiple value definitions for multiple currencies? - item->mCost = value; - - for_each_xml_child_node(subnode, node) - { - if (xmlStrEqual(subnode->name, BAD_CAST "equip")) - { - ItemEquipInfo req; - for_each_xml_child_node(equipnode, subnode) - if (xmlStrEqual(equipnode->name, BAD_CAST "slot")) - { - std::string slot = XML::getProperty(equipnode, "type", - std::string()); - if (slot.empty()) - { - LOG_WARN("Item Manager: empty equip slot definition!"); - continue; - } - req.push_back(std::make_pair(getEquipIdFromName(slot), - XML::getProperty(equipnode, "required", - 1))); - } - if (req.empty()) - { - LOG_WARN("Item Manager: empty equip requirement " - "definition for item " << id << "!"); - continue; - } - item->mEquip.push_back(req); - } - else if (xmlStrEqual(subnode->name, BAD_CAST "effect")) - { - std::pair< ItemTriggerType, ItemTriggerType> triggerTypes; - { - const std::string triggerName = XML::getProperty( - subnode, "trigger", std::string()); - const std::string dispellTrigger = XML::getProperty( - subnode, "dispell", std::string()); - // label -> { trigger (apply), trigger (cancel (default)) } - // The latter can be overridden. - static std::map<const std::string, - std::pair<ItemTriggerType, ItemTriggerType> > - triggerTable; - if (triggerTable.empty()) - { - /* - * The following is a table of all triggers for item - * effects. - * The first element defines the trigger used for this - * trigger, and the second defines the default - * trigger to use for dispelling. - */ - triggerTable["existence"].first = ITT_IN_INVY; - triggerTable["existence"].second = ITT_LEAVE_INVY; - triggerTable["activation"].first = ITT_ACTIVATE; - triggerTable["activation"].second = ITT_NULL; - triggerTable["equip"].first = ITT_EQUIP; - triggerTable["equip"].second = ITT_UNEQUIP; - triggerTable["leave-inventory"].first = ITT_LEAVE_INVY; - triggerTable["leave-inventory"].second = ITT_NULL; - triggerTable["unequip"].first = ITT_UNEQUIP; - triggerTable["unequip"].second = ITT_NULL; - triggerTable["equip-change"].first = ITT_EQUIPCHG; - triggerTable["equip-change"].second = ITT_NULL; - triggerTable["null"].first = ITT_NULL; - triggerTable["null"].second = ITT_NULL; - } - std::map<const std::string, std::pair<ItemTriggerType, - ItemTriggerType> >::iterator - it = triggerTable.find(triggerName); - - if (it == triggerTable.end()) { - LOG_WARN("Item Manager: Unable to find effect trigger type \"" - << triggerName << "\", skipping!"); - continue; - } - triggerTypes = it->second; - if (!dispellTrigger.empty()) - { - if ((it = triggerTable.find(dispellTrigger)) - == triggerTable.end()) - LOG_WARN("Item Manager: Unable to find dispell effect " - "trigger type \"" << dispellTrigger << "\"!"); - else - triggerTypes.second = it->second.first; - } - } - for_each_xml_child_node(effectnode, subnode) - { - if (xmlStrEqual(effectnode->name, BAD_CAST "modifier")) - { - std::string tag = XML::getProperty(effectnode, "attribute", std::string()); - if (tag.empty()) - { - LOG_WARN("Item Manager: Warning, modifier found " - "but no attribute specified!"); - continue; - } - unsigned int duration = XML::getProperty(effectnode, - "duration", - 0); - ModifierLocation location = attributeManager->getLocation(tag); - double value = XML::getFloatProperty(effectnode, "value", 0.0); - item->addEffect(new ItemEffectAttrMod(location.attributeId, - location.layer, - value, id, - duration), - triggerTypes.first, triggerTypes.second); - } - else if (xmlStrEqual(effectnode->name, BAD_CAST "autoattack")) - { - // TODO - URGENT - } - // Having a dispell for the next three is nonsensical. - else if (xmlStrEqual(effectnode->name, BAD_CAST "cooldown")) - { - LOG_WARN("Item Manager: Cooldown property not implemented yet!"); - // TODO: Also needs unique items before this action will work - } - else if (xmlStrEqual(effectnode->name, BAD_CAST "g-cooldown")) - { - LOG_WARN("Item Manager: G-Cooldown property not implemented yet!"); - // TODO - } - else if (xmlStrEqual(effectnode->name, BAD_CAST "consumes")) - item->addEffect(new ItemEffectConsumes(), triggerTypes.first); - else if (xmlStrEqual(effectnode->name, BAD_CAST "script")) - { - std::string src = XML::getProperty(effectnode, "src", std::string()); - if (src.empty()) - { - LOG_WARN("Item Manager: Empty src definition for script effect, skipping!"); - continue; - } - std::string func = XML::getProperty(effectnode, "function", std::string()); - if (func.empty()) - { - LOG_WARN ("Item Manager: Empty func definition for script effect, skipping!"); - continue; - } - for_each_xml_child_node(scriptnode, effectnode) - { - // TODO: Load variables from variable subnodes - } - std::string dfunc = XML::getProperty(effectnode, "dispell-function", std::string()); - // STUB - item->addEffect(new ItemEffectScript(), triggerTypes.first, triggerTypes.second); - } - } - } - // More properties go here - } - ++nbItems; - } - - LOG_INFO("Loaded " << nbItems << " items from " - << absPathFile << "."); + mVisibleEquipSlotCount = 0; + readEquipSlotsFile(); + readItemsFile(); } void ItemManager::deinitialize() { - for (ItemClasses::iterator i = itemClasses.begin(), i_end = itemClasses.end(); i != i_end; ++i) + for (ItemClasses::iterator i = itemClasses.begin(), + i_end = itemClasses.end(); i != i_end; ++i) { delete i->second; } @@ -339,7 +58,7 @@ void ItemManager::deinitialize() ItemClass *ItemManager::getItem(int itemId) const { ItemClasses::const_iterator i = itemClasses.find(itemId); - return i != itemClasses.end() ? i->second : NULL; + return i != itemClasses.end() ? i->second : 0; } ItemClass *ItemManager::getItemByName(std::string name) const @@ -348,7 +67,7 @@ ItemClass *ItemManager::getItemByName(std::string name) const for (ItemClasses::const_iterator i = itemClasses.begin(), i_end = itemClasses.end(); i != i_end; ++i) { - if(utils::toLower(i->second->getName()) == name) + if (utils::toLower(i->second->getName()) == name) { return i->second; } @@ -384,11 +103,15 @@ unsigned int ItemManager::getMaxSlotsFromId(unsigned int id) const unsigned int ItemManager::getVisibleSlotCount() const { if (!mVisibleEquipSlotCount) + { for (VisibleEquipSlots::const_iterator it = visibleEquipSlots.begin(), it_end = visibleEquipSlots.end(); it != it_end; ++it) + { mVisibleEquipSlotCount += equipSlots.at(*it).second; + } + } return mVisibleEquipSlotCount; } @@ -398,7 +121,309 @@ bool ItemManager::isEquipSlotVisible(unsigned int id) const it_end = visibleEquipSlots.end(); it != it_end; ++it) + { if (*it == id) return true; + } return false; } + +void ItemManager::readEquipSlotsFile() +{ + std::string absPathFile = ResourceManager::resolve(mEquipSlotsFile); + if (absPathFile.empty()) { + LOG_ERROR("Item Manager: Could not find " << mEquipSlotsFile << "!"); + return; + } + + XML::Document doc(absPathFile, false); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "equip-slots")) + { + LOG_ERROR("Item Manager: Error while parsing equip slots database (" + << absPathFile << ")!"); + return; + } + + LOG_INFO("Loading equip slots: " << absPathFile); + + unsigned totalCount = 0; + unsigned slotCount = 0; + unsigned visibleSlotCount = 0; + + for_each_xml_child_node(node, rootNode) + { + if (xmlStrEqual(node->name, BAD_CAST "slot")) + { + const std::string name = XML::getProperty(node, "name", + std::string()); + const int count = XML::getProperty(node, "count", 0); + + if (name.empty() || count <= 0) + { + LOG_WARN("Item Manager: equip slot has no name or zero count"); + } + else + { + bool visible = XML::getProperty(node, "visible", "false") != "false"; + if (visible) + { + visibleEquipSlots.push_back(equipSlots.size()); + if (++visibleSlotCount > 7) + LOG_WARN("Item Manager: More than 7 visible equip slot!" + "This will not work with current netcode!"); + } + equipSlots.push_back(std::pair<std::string, unsigned int> + (name, count)); + totalCount += count; + ++slotCount; + } + } + } + + LOG_INFO("Loaded '" << slotCount << "' slot types with '" + << totalCount << "' slots."); +} + +void ItemManager::readItemsFile() +{ + std::string absPathFile = ResourceManager::resolve(mItemsFile); + if (absPathFile.empty()) + { + LOG_ERROR("Item Manager: Could not find " << mItemsFile << "!"); + return; + } + + XML::Document doc2(absPathFile, false); + xmlNodePtr rootNode = doc2.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items")) + { + LOG_ERROR("Item Manager: Error while parsing item database (" + << absPathFile << ")!"); + return; + } + + LOG_INFO("Loading item reference: " << absPathFile); + + for_each_xml_child_node(node, rootNode) + { + if (xmlStrEqual(node->name, BAD_CAST "item")) + { + readItemNode(node); + } + } + + LOG_INFO("Loaded " << itemClasses.size() << " items from " + << absPathFile << "."); +} + +void ItemManager::readItemNode(xmlNodePtr itemNode) +{ + const int id = XML::getProperty(itemNode, "id", 0); + if (id < 1) + { + LOG_WARN("Item Manager: Item ID: " << id << " is invalid in " + << mItemsFile << ", and will be ignored."); + return; + } + + // Type is mostly unused, but still serves for + // hairsheets and race sheets. + std::string sItemType = XML::getProperty(itemNode, "type", std::string()); + if (sItemType == "hairsprite" || sItemType == "racesprite") + return; + + ItemClass *item; + ItemClasses::iterator i = itemClasses.find(id); + + unsigned int maxPerSlot = XML::getProperty(itemNode, "max-per-slot", 0); + if (!maxPerSlot) + { + LOG_WARN("Item Manager: Missing max-per-slot property for " + "item " << id << " in " << mItemsFile << '.'); + maxPerSlot = 1; + } + + if (i == itemClasses.end()) + { + item = new ItemClass(id, maxPerSlot); + itemClasses[id] = item; + } + else + { + LOG_WARN("Multiple defintions of item '" << id << "'!"); + item = i->second; + } + + std::string name = XML::getProperty(itemNode, "name", "unnamed"); + item->setName(name); + + int value = XML::getProperty(itemNode, "value", 0); + // Should have multiple value definitions for multiple currencies? + item->mCost = value; + + for_each_xml_child_node(subNode, itemNode) + { + if (xmlStrEqual(subNode->name, BAD_CAST "equip")) + { + readEquipNode(subNode, item); + } + else if (xmlStrEqual(subNode->name, BAD_CAST "effect")) + { + readEffectNode(subNode, item); + } + // More properties go here + } +} + +void ItemManager::readEquipNode(xmlNodePtr equipNode, ItemClass *item) +{ + ItemEquipInfo req; + for_each_xml_child_node(subNode, equipNode) + { + if (xmlStrEqual(subNode->name, BAD_CAST "slot")) + { + std::string slot = XML::getProperty(subNode, "type", std::string()); + if (slot.empty()) + { + LOG_WARN("Item Manager: empty equip slot definition!"); + continue; + } + req.push_back(std::make_pair(getEquipIdFromName(slot), + XML::getProperty(subNode, "required", 1))); + } + } + if (req.empty()) + { + LOG_WARN("Item Manager: empty equip requirement " + "definition for item " << item->getDatabaseID() << "!"); + return; + } + item->mEquip.push_back(req); +} + +void ItemManager::readEffectNode(xmlNodePtr effectNode, ItemClass *item) +{ + std::pair<ItemTriggerType, ItemTriggerType> triggerTypes; + { + const std::string triggerName = XML::getProperty( + effectNode, "trigger", std::string()); + const std::string dispellTrigger = XML::getProperty( + effectNode, "dispell", std::string()); + // label -> { trigger (apply), trigger (cancel (default)) } + // The latter can be overridden. + static std::map<const std::string, + std::pair<ItemTriggerType, ItemTriggerType> > + triggerTable; + if (triggerTable.empty()) + { + /* + * The following is a table of all triggers for item + * effects. + * The first element defines the trigger used for this + * trigger, and the second defines the default + * trigger to use for dispelling. + */ + triggerTable["existence"].first = ITT_IN_INVY; + triggerTable["existence"].second = ITT_LEAVE_INVY; + triggerTable["activation"].first = ITT_ACTIVATE; + triggerTable["activation"].second = ITT_NULL; + triggerTable["equip"].first = ITT_EQUIP; + triggerTable["equip"].second = ITT_UNEQUIP; + triggerTable["leave-inventory"].first = ITT_LEAVE_INVY; + triggerTable["leave-inventory"].second = ITT_NULL; + triggerTable["unequip"].first = ITT_UNEQUIP; + triggerTable["unequip"].second = ITT_NULL; + triggerTable["equip-change"].first = ITT_EQUIPCHG; + triggerTable["equip-change"].second = ITT_NULL; + triggerTable["null"].first = ITT_NULL; + triggerTable["null"].second = ITT_NULL; + } + std::map<const std::string, std::pair<ItemTriggerType, + ItemTriggerType> >::iterator + it = triggerTable.find(triggerName); + + if (it == triggerTable.end()) { + LOG_WARN("Item Manager: Unable to find effect trigger type \"" + << triggerName << "\", skipping!"); + return; + } + triggerTypes = it->second; + if (!dispellTrigger.empty()) + { + if ((it = triggerTable.find(dispellTrigger)) == triggerTable.end()) + LOG_WARN("Item Manager: Unable to find dispell effect " + "trigger type \"" << dispellTrigger << "\"!"); + else + triggerTypes.second = it->second.first; + } + } + + for_each_xml_child_node(subNode, effectNode) + { + if (xmlStrEqual(subNode->name, BAD_CAST "modifier")) + { + std::string tag = XML::getProperty(subNode, "attribute", std::string()); + if (tag.empty()) + { + LOG_WARN("Item Manager: Warning, modifier found " + "but no attribute specified!"); + continue; + } + unsigned int duration = XML::getProperty(subNode, + "duration", + 0); + ModifierLocation location = attributeManager->getLocation(tag); + double value = XML::getFloatProperty(subNode, "value", 0.0); + item->addEffect(new ItemEffectAttrMod(location.attributeId, + location.layer, + value, + item->getDatabaseID(), + duration), + triggerTypes.first, triggerTypes.second); + } + else if (xmlStrEqual(subNode->name, BAD_CAST "autoattack")) + { + // TODO - URGENT + } + // Having a dispell for the next three is nonsensical. + else if (xmlStrEqual(subNode->name, BAD_CAST "cooldown")) + { + LOG_WARN("Item Manager: Cooldown property not implemented yet!"); + // TODO: Also needs unique items before this action will work + } + else if (xmlStrEqual(subNode->name, BAD_CAST "g-cooldown")) + { + LOG_WARN("Item Manager: G-Cooldown property not implemented yet!"); + // TODO + } + else if (xmlStrEqual(subNode->name, BAD_CAST "consumes")) + { + item->addEffect(new ItemEffectConsumes(), triggerTypes.first); + } + else if (xmlStrEqual(subNode->name, BAD_CAST "script")) + { + std::string src = XML::getProperty(subNode, "src", std::string()); + if (src.empty()) + { + LOG_WARN("Item Manager: Empty src definition for script effect, skipping!"); + continue; + } + std::string func = XML::getProperty(subNode, "function", std::string()); + if (func.empty()) + { + LOG_WARN("Item Manager: Empty func definition for script effect, skipping!"); + continue; + } + for_each_xml_child_node(scriptSubNode, subNode) + { + // TODO: Load variables from variable subnodes + } + std::string dfunc = XML::getProperty(subNode, "dispell-function", std::string()); + // STUB + item->addEffect(new ItemEffectScript(), triggerTypes.first, triggerTypes.second); + } + } +} diff --git a/src/game-server/itemmanager.h b/src/game-server/itemmanager.h index 00739785..c7033047 100644 --- a/src/game-server/itemmanager.h +++ b/src/game-server/itemmanager.h @@ -21,6 +21,8 @@ #ifndef ITEMMANAGER_H #define ITEMMANAGER_H +#include "utils/xml.h" + #include <string> #include <map> #include <vector> @@ -31,9 +33,12 @@ class ItemManager { public: ItemManager(const std::string &itemFile, const std::string &equipFile) : - mItemReferenceFile(itemFile), - mEquipCharSlotReferenceFile(equipFile), - mItemDatabaseVersion(0) {} + mItemsFile(itemFile), + mEquipSlotsFile(equipFile), + mVisibleEquipSlotCount(0), + mItemDatabaseVersion(0) + {} + /** * Loads item reference file. */ @@ -78,6 +83,15 @@ class ItemManager bool isEquipSlotVisible(unsigned int id) const; private: + /** Loads the equip slots that a character has available to them. */ + void readEquipSlotsFile(); + + /** Loads the main item database. */ + void readItemsFile(); + void readItemNode(xmlNodePtr itemNode); + void readEquipNode(xmlNodePtr equipNode, ItemClass *item); + void readEffectNode(xmlNodePtr effectNode, ItemClass *item); + typedef std::map< int, ItemClass * > ItemClasses; // Map a string (name of slot) with (str-id, max-per-equip-slot) typedef std::vector< std::pair< std::string, unsigned int > > EquipSlots; @@ -88,11 +102,12 @@ class ItemManager EquipSlots equipSlots; VisibleEquipSlots visibleEquipSlots; - std::string mItemReferenceFile; - std::string mEquipCharSlotReferenceFile; + std::string mItemsFile; + std::string mEquipSlotsFile; mutable unsigned int mVisibleEquipSlotCount; // Cache - unsigned int mItemDatabaseVersion; /**< Version of the loaded items database file.*/ + /** Version of the loaded items database file.*/ + unsigned int mItemDatabaseVersion; }; extern ItemManager *itemManager; |