diff options
Diffstat (limited to 'src/game-server/itemmanager.cpp')
-rw-r--r-- | src/game-server/itemmanager.cpp | 397 |
1 files changed, 273 insertions, 124 deletions
diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp index f296225..802197d 100644 --- a/src/game-server/itemmanager.cpp +++ b/src/game-server/itemmanager.cpp @@ -22,6 +22,7 @@ #include "defines.h" #include "common/resourcemanager.hpp" +#include "game-server/attributemanager.hpp" #include "game-server/item.hpp" #include "game-server/skillmanager.hpp" #include "scripting/script.hpp" @@ -32,27 +33,82 @@ #include <set> #include <sstream> -typedef std::map< int, ItemClass * > ItemClasses; -static ItemClasses itemClasses; /**< Item reference */ -static std::string itemReferenceFile; -static unsigned int itemDatabaseVersion = 0; /**< Version of the loaded items database file.*/ - -void ItemManager::initialize(const std::string &file) +void ItemManager::initialize() { - itemReferenceFile = file; + mVisibleEquipSlotCount = 0; reload(); } void ItemManager::reload() { - std::string absPathFile = ResourceManager::resolve(itemReferenceFile); + 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 " << itemReferenceFile << "!"); + LOG_ERROR("Item Manager: Could not find " << mEquipCharSlotReferenceFile << "!"); + return; + } + + XML::Document doc(absPathFile, int()); + rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "equip-slots")) + { + LOG_ERROR("Item Manager: Error while parsing equip slots database (" + << absPathFile << ")!"); return; } - XML::Document doc(absPathFile, false); - xmlNodePtr rootNode = doc.rootNode(); + 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")) + { + std::string name = XML::getProperty(node, "name", ""); + 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, int()); + rootNode = doc2.rootNode(); if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "items")) { @@ -62,150 +118,201 @@ void ItemManager::reload() } LOG_INFO("Loading item reference: " << absPathFile); + unsigned nbItems = 0; for_each_xml_child_node(node, rootNode) { - // Try to load the version of the item database. The version is defined - // as subversion tag embedded as XML attribute. So every modification - // to the items.xml file will increase the revision automatically. - if (xmlStrEqual(node->name, BAD_CAST "version")) - { - std::string revision = XML::getProperty(node, "revision", std::string()); - itemDatabaseVersion = atoi(revision.c_str()); - - LOG_INFO("Loading item database version " << itemDatabaseVersion); - continue; - } - if (!xmlStrEqual(node->name, BAD_CAST "item")) - { continue; - } int id = XML::getProperty(node, "id", 0); - if (id == 0) + if (!id) { - LOG_WARN("Item Manager: An (ignored) item has no ID in " - << itemReferenceFile << "!"); + LOG_WARN("Item Manager: An item has no ID in " + << mItemReferenceFile << ", and so has been ignored!"); continue; } + + // Type is mostly unused, but still serves for + // hairsheets and race sheets. std::string sItemType = XML::getProperty(node, "type", ""); - ItemType itemType = itemTypeFromString(sItemType); + if (sItemType == "hairsprite" || sItemType == "racesprite") + continue; - if (itemType == ITEM_UNKNOWN) - { - LOG_WARN(itemReferenceFile << ": Unknown item type \"" << sItemType - << "\" for item #" << id << - " - treating it as \"generic\"."); - itemType = ITEM_UNUSABLE; - } + ItemClass *item; + ItemClasses::iterator i = itemClasses.find(id); - if (itemType == ITEM_HAIRSPRITE || itemType == ITEM_RACESPRITE) + unsigned int maxPerSlot = XML::getProperty(node, "max-per-slot", 0); + if (!maxPerSlot) { - continue; + LOG_WARN("Item Manager: Missing max-per-slot property for " + "item " << id << " in " << mItemReferenceFile << '.'); + maxPerSlot = 1; } - ItemClass *item; - ItemClasses::iterator i = itemClasses.find(id); if (i == itemClasses.end()) { - item = new ItemClass(id, itemType); + item = new ItemClass(id, maxPerSlot); itemClasses[id] = item; } else { + LOG_WARN("Multiple defintions of item '" << id << "'!"); item = i->second; } - int weight = XML::getProperty(node, "weight", 0); int value = XML::getProperty(node, "value", 0); - int maxPerSlot = XML::getProperty(node, "max-per-slot", 0); - int sprite = XML::getProperty(node, "sprite_id", 0); - std::string scriptFile = XML::getProperty(node, "script", ""); - unsigned attackRange = XML::getProperty(node, "attack-range", 0); - - ItemModifiers modifiers; - if (itemType == ITEM_EQUIPMENT_ONE_HAND_WEAPON || - itemType == ITEM_EQUIPMENT_TWO_HANDS_WEAPON) + // Should have multiple value definitions for multiple currencies? + item->mCost = value; + + for_each_xml_child_node(subnode, node) { - int weaponType = 0; - std::string strWeaponType = XML::getProperty(node, "weapon-type", ""); - if (strWeaponType == "") + if (xmlStrEqual(subnode->name, BAD_CAST "equip")) { - LOG_WARN(itemReferenceFile << ": Empty weapon type \"" - << "\" for item #" << id << - " - treating it as generic item."); + ItemEquipInfo req; + for_each_xml_child_node(equipnode, subnode) + if (xmlStrEqual(equipnode->name, BAD_CAST "slot")) + { + std::string slot = XML::getProperty(equipnode, "type", ""); + 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 - weaponType = SkillManager::getIdFromString(strWeaponType); - - modifiers.setValue(MOD_WEAPON_TYPE, weaponType); - modifiers.setValue(MOD_WEAPON_RANGE, XML::getProperty(node, "range", 0)); - modifiers.setValue(MOD_ELEMENT_TYPE, XML::getProperty(node, "element", 0)); - } - modifiers.setValue(MOD_LIFETIME, XML::getProperty(node, "lifetime", 0) * 10); - //TODO: add child nodes for these modifiers (additive and factor) - modifiers.setAttributeValue(BASE_ATTR_PHY_ATK_MIN, XML::getProperty(node, "attack-min", 0)); - modifiers.setAttributeValue(BASE_ATTR_PHY_ATK_DELTA, XML::getProperty(node, "attack-delta", 0)); - modifiers.setAttributeValue(BASE_ATTR_HP, XML::getProperty(node, "hp", 0)); - modifiers.setAttributeValue(BASE_ATTR_PHY_RES, XML::getProperty(node, "defense", 0)); - modifiers.setAttributeValue(CHAR_ATTR_STRENGTH, XML::getProperty(node, "strength", 0)); - modifiers.setAttributeValue(CHAR_ATTR_AGILITY, XML::getProperty(node, "agility", 0)); - modifiers.setAttributeValue(CHAR_ATTR_DEXTERITY, XML::getProperty(node, "dexterity", 0)); - modifiers.setAttributeValue(CHAR_ATTR_VITALITY, XML::getProperty(node, "vitality", 0)); - modifiers.setAttributeValue(CHAR_ATTR_INTELLIGENCE, XML::getProperty(node, "intelligence", 0)); - modifiers.setAttributeValue(CHAR_ATTR_WILLPOWER, XML::getProperty(node, "willpower", 0)); - - if (maxPerSlot == 0) - { - //LOG_WARN("Item Manager: Missing max-per-slot property for " - // "item " << id << " in " << itemReferenceFile << '.'); - maxPerSlot = 1; - } - - if (itemType > ITEM_USABLE && itemType < ITEM_EQUIPMENT_AMMO && - maxPerSlot != 1) - { - LOG_WARN("Item Manager: Setting max-per-slot property to 1 for " - "equipment " << id << " in " << itemReferenceFile << '.'); - maxPerSlot = 1; - } - - if (weight == 0) - { - LOG_WARN("Item Manager: Missing weight for item " - << id << " in " << itemReferenceFile << '.'); - weight = 1; - } - - // TODO: Clean this up some - if (scriptFile != "") - { - std::stringstream filename; - filename << "scripts/items/" << scriptFile; - if (ResourceManager::exists(filename.str())) // file exists! + else if (xmlStrEqual(subnode->name, BAD_CAST "effect")) { - LOG_INFO("Loading item script: " << filename.str()); - Script *s = Script::create("lua"); - s->loadFile(filename.str()); - item->setScript(s); - } else { - LOG_WARN("Could not find script file \"" << filename.str() << "\" for item #"<<id); + std::pair< ItemTriggerType, ItemTriggerType> triggerTypes; + { + std::string triggerName = XML::getProperty(subnode, "trigger", ""), + dispellTrigger = XML::getProperty(subnode, "dispell", ""); + // 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", ""); + if (tag.empty()) + { + LOG_WARN("Item Manager: Warning, modifier found " + "but no attribute specified!"); + continue; + } + unsigned int duration = XML::getProperty(effectnode, + "duration", + 0); + std::pair<unsigned int, unsigned int> info = attributeManager->getInfoFromTag(tag); + double value = XML::getFloatProperty(effectnode, "value", 0.0); + item->addEffect(new ItemEffectAttrMod(info.first, + info.second, + 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", ""); + if (src.empty()) + { + LOG_WARN("Item Manager: Empty src definition for script effect, skipping!"); + continue; + } + std::string func = XML::getProperty(effectnode, "function", ""); + 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", ""); + // STUB + item->addEffect(new ItemEffectScript(), triggerTypes.first, triggerTypes.second); + } + } } + // More properties go here } - - item->setWeight(weight); - item->setCost(value); - item->setMaxPerSlot(maxPerSlot); - item->setModifiers(modifiers); - item->setSpriteID(sprite ? sprite : id); ++nbItems; - item->setAttackRange(attackRange); - - LOG_DEBUG("Item: ID: " << id << ", itemType: " << itemType - << ", weight: " << weight << ", value: " << value << - ", script: " << scriptFile << ", maxPerSlot: " << maxPerSlot << "."); } LOG_INFO("Loaded " << nbItems << " items from " @@ -221,13 +328,55 @@ void ItemManager::deinitialize() itemClasses.clear(); } -ItemClass *ItemManager::getItem(int itemId) +ItemClass *ItemManager::getItem(int itemId) const { ItemClasses::const_iterator i = itemClasses.find(itemId); return i != itemClasses.end() ? i->second : NULL; } -unsigned ItemManager::getDatabaseVersion() +unsigned int ItemManager::getDatabaseVersion() const +{ + return mItemDatabaseVersion; +} + +const std::string &ItemManager::getEquipNameFromId(unsigned int id) const +{ + return equipSlots.at(id).first; +} + +unsigned int ItemManager::getEquipIdFromName(const std::string &name) const +{ + for (unsigned int i = 0; i < equipSlots.size(); ++i) + if (name == equipSlots.at(i).first) + return i; + LOG_WARN("Item Manager: attempt to find equip id from name \"" << + name << "\" not found, defaulting to 0!"); + return 0; +} + +unsigned int ItemManager::getMaxSlotsFromId(unsigned int id) const +{ + return equipSlots.at(id).second; +} + +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; +} + +bool ItemManager::isEquipSlotVisible(unsigned int id) const { - return itemDatabaseVersion; + for (VisibleEquipSlots::const_iterator it = visibleEquipSlots.begin(), + it_end = visibleEquipSlots.end(); + it != it_end; + ++it) + if (*it == id) + return true; + return false; } |