Advertisement
Guest User

Untitled

a guest
Jan 20th, 2017
224
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 40.93 KB | None | 0 0
  1. /**
  2.  * The Forgotten Server - a free and open-source MMORPG server emulator
  3.  * Copyright (C) 2017  Mark Samman <mark.samman@gmail.com>
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License along
  16.  * with this program; if not, write to the Free Software Foundation, Inc.,
  17.  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18.  */
  19.  
  20. #include "otpch.h"
  21.  
  22. #include "monsters.h"
  23. #include "monster.h"
  24. #include "spells.h"
  25. #include "combat.h"
  26. #include "weapons.h"
  27. #include "configmanager.h"
  28. #include "game.h"
  29.  
  30. #include "pugicast.h"
  31.  
  32. extern Game g_game;
  33. extern Spells* g_spells;
  34. extern Monsters g_monsters;
  35. extern ConfigManager g_config;
  36.  
  37. spellBlock_t::~spellBlock_t()
  38. {
  39.     if (combatSpell) {
  40.         delete spell;
  41.     }
  42. }
  43.  
  44. uint32_t Monsters::getLootRandom()
  45. {
  46.     return uniform_random(0, MAX_LOOTCHANCE) / g_config.getNumber(ConfigManager::RATE_LOOT);
  47. }
  48.  
  49. void MonsterType::createLoot(Container* corpse)
  50. {
  51.     if (g_config.getNumber(ConfigManager::RATE_LOOT) == 0) {
  52.         corpse->startDecaying();
  53.         return;
  54.     }
  55.  
  56.     Player* owner = g_game.getPlayerByID(corpse->getCorpseOwner());
  57.     if (!owner || owner->getStaminaMinutes() > 840) {
  58.         for (auto it = info.lootItems.rbegin(), end = info.lootItems.rend(); it != end; ++it) {
  59.             auto itemList = createLootItem(*it);
  60.             if (itemList.empty()) {
  61.                 continue;
  62.             }
  63.  
  64.             for (Item* item : itemList) {
  65.                 //check containers
  66.                 if (Container* container = item->getContainer()) {
  67.                     if (!createLootContainer(container, *it)) {
  68.                         delete container;
  69.                         continue;
  70.                     }
  71.                 }
  72.  
  73.                 if (g_game.internalAddItem(corpse, item) != RETURNVALUE_NOERROR) {
  74.                     corpse->internalAddThing(item);
  75.                 }
  76.             }
  77.         }
  78.  
  79.         if (owner) {
  80.             std::ostringstream ss;
  81.             ss << "Loot of " << nameDescription << ": " << corpse->getContentDescription();
  82.  
  83.             if (owner->getParty()) {
  84.                 owner->getParty()->broadcastPartyLoot(ss.str());
  85.             } else {
  86.                 owner->sendTextMessage(MESSAGE_LOOT, ss.str());
  87.             }
  88.         }
  89.     } else {
  90.         std::ostringstream ss;
  91.         ss << "Loot of " << nameDescription << ": nothing (due to low stamina)";
  92.  
  93.         if (owner->getParty()) {
  94.             owner->getParty()->broadcastPartyLoot(ss.str());
  95.         } else {
  96.             owner->sendTextMessage(MESSAGE_LOOT, ss.str());
  97.         }
  98.     }
  99.  
  100.     corpse->startDecaying();
  101. }
  102.  
  103. std::vector<Item*> MonsterType::createLootItem(const LootBlock& lootBlock)
  104. {
  105.     int32_t itemCount = 0;
  106.  
  107.     uint32_t randvalue = Monsters::getLootRandom();
  108.     if (randvalue < lootBlock.chance) {
  109.         if (Item::items[lootBlock.id].stackable) {
  110.             itemCount = randvalue % lootBlock.countmax + 1;
  111.         } else {
  112.             itemCount = 1;
  113.         }
  114.     }
  115.  
  116.     std::vector<Item*> itemList;
  117.     while (itemCount > 0) {
  118.         uint16_t n = static_cast<uint16_t>(std::min<int32_t>(itemCount, 100));
  119.         Item* tmpItem = Item::CreateItem(lootBlock.id, n);
  120.         if (!tmpItem) {
  121.             break;
  122.         }
  123.  
  124.         itemCount -= n;
  125.  
  126.         if (lootBlock.subType != -1) {
  127.             tmpItem->setSubType(lootBlock.subType);
  128.         }
  129.  
  130.         if (lootBlock.actionId != -1) {
  131.             tmpItem->setActionId(lootBlock.actionId);
  132.         }
  133.  
  134.         if (!lootBlock.text.empty()) {
  135.             tmpItem->setText(lootBlock.text);
  136.         }
  137.  
  138.         itemList.push_back(tmpItem);
  139.     }
  140.     return itemList;
  141. }
  142.  
  143. bool MonsterType::createLootContainer(Container* parent, const LootBlock& lootblock)
  144. {
  145.     auto it = lootblock.childLoot.begin(), end = lootblock.childLoot.end();
  146.     if (it == end) {
  147.         return true;
  148.     }
  149.  
  150.     for (; it != end && parent->size() < parent->capacity(); ++it) {
  151.         auto itemList = createLootItem(*it);
  152.         for (Item* tmpItem : itemList) {
  153.             if (Container* container = tmpItem->getContainer()) {
  154.                 if (!createLootContainer(container, *it)) {
  155.                     delete container;
  156.                 } else {
  157.                     parent->internalAddThing(container);
  158.                 }
  159.             } else {
  160.                 parent->internalAddThing(tmpItem);
  161.             }
  162.         }
  163.     }
  164.     return !parent->empty();
  165. }
  166.  
  167. bool Monsters::loadFromXml(bool reloading /*= false*/)
  168. {
  169.     pugi::xml_document doc;
  170.     pugi::xml_parse_result result = doc.load_file("data/monster/monsters.xml");
  171.     if (!result) {
  172.         printXMLError("Error - Monsters::loadFromXml", "data/monster/monsters.xml", result);
  173.         return false;
  174.     }
  175.  
  176.     loaded = true;
  177.  
  178.     std::list<std::pair<MonsterType*, std::string>> monsterScriptList;
  179.     for (auto monsterNode : doc.child("monsters").children()) {
  180.         loadMonster("data/monster/" + std::string(monsterNode.attribute("file").as_string()), monsterNode.attribute("name").as_string(), monsterScriptList, reloading);
  181.     }
  182.  
  183.     if (!monsterScriptList.empty()) {
  184.         if (!scriptInterface) {
  185.             scriptInterface.reset(new LuaScriptInterface("Monster Interface"));
  186.             scriptInterface->initState();
  187.         }
  188.  
  189.         for (const auto& scriptEntry : monsterScriptList) {
  190.             MonsterType* mType = scriptEntry.first;
  191.             if (scriptInterface->loadFile("data/monster/scripts/" + scriptEntry.second) == 0) {
  192.                 mType->info.scriptInterface = scriptInterface.get();
  193.                 mType->info.creatureAppearEvent = scriptInterface->getEvent("onCreatureAppear");
  194.                 mType->info.creatureDisappearEvent = scriptInterface->getEvent("onCreatureDisappear");
  195.                 mType->info.creatureMoveEvent = scriptInterface->getEvent("onCreatureMove");
  196.                 mType->info.creatureSayEvent = scriptInterface->getEvent("onCreatureSay");
  197.                 mType->info.thinkEvent = scriptInterface->getEvent("onThink");
  198.             } else {
  199.                 std::cout << "[Warning - Monsters::loadMonster] Can not load script: " << scriptEntry.second << std::endl;
  200.                 std::cout << scriptInterface->getLastLuaError() << std::endl;
  201.             }
  202.         }
  203.     }
  204.     return true;
  205. }
  206.  
  207. bool Monsters::reload()
  208. {
  209.     loaded = false;
  210.  
  211.     scriptInterface.reset();
  212.  
  213.     return loadFromXml(true);
  214. }
  215.  
  216. ConditionDamage* Monsters::getDamageCondition(ConditionType_t conditionType,
  217.         int32_t maxDamage, int32_t minDamage, int32_t startDamage, uint32_t tickInterval)
  218. {
  219.     ConditionDamage* condition = static_cast<ConditionDamage*>(Condition::createCondition(CONDITIONID_COMBAT, conditionType, 0, 0));
  220.     condition->setParam(CONDITION_PARAM_TICKINTERVAL, tickInterval);
  221.     condition->setParam(CONDITION_PARAM_MINVALUE, minDamage);
  222.     condition->setParam(CONDITION_PARAM_MAXVALUE, maxDamage);
  223.     condition->setParam(CONDITION_PARAM_STARTVALUE, startDamage);
  224.     condition->setParam(CONDITION_PARAM_DELAYED, 1);
  225.     return condition;
  226. }
  227.  
  228. bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, const std::string& description)
  229. {
  230.     std::string name;
  231.     std::string scriptName;
  232.     bool isScripted;
  233.  
  234.     pugi::xml_attribute attr;
  235.     if ((attr = node.attribute("script"))) {
  236.         scriptName = attr.as_string();
  237.         isScripted = true;
  238.     } else if ((attr = node.attribute("name"))) {
  239.         name = attr.as_string();
  240.         isScripted = false;
  241.     } else {
  242.         return false;
  243.     }
  244.  
  245.     if ((attr = node.attribute("speed")) || (attr = node.attribute("interval"))) {
  246.         sb.speed = std::max<int32_t>(1, pugi::cast<int32_t>(attr.value()));
  247.     }
  248.  
  249.     if ((attr = node.attribute("chance"))) {
  250.         uint32_t chance = pugi::cast<uint32_t>(attr.value());
  251.         if (chance > 100) {
  252.             chance = 100;
  253.         }
  254.         sb.chance = chance;
  255.     }
  256.  
  257.     if ((attr = node.attribute("range"))) {
  258.         uint32_t range = pugi::cast<uint32_t>(attr.value());
  259.         if (range > (Map::maxViewportX * 2)) {
  260.             range = Map::maxViewportX * 2;
  261.         }
  262.         sb.range = range;
  263.     }
  264.  
  265.     if ((attr = node.attribute("min"))) {
  266.         sb.minCombatValue = pugi::cast<int32_t>(attr.value());
  267.     }
  268.  
  269.     if ((attr = node.attribute("max"))) {
  270.         sb.maxCombatValue = pugi::cast<int32_t>(attr.value());
  271.  
  272.         //normalize values
  273.         if (std::abs(sb.minCombatValue) > std::abs(sb.maxCombatValue)) {
  274.             int32_t value = sb.maxCombatValue;
  275.             sb.maxCombatValue = sb.minCombatValue;
  276.             sb.minCombatValue = value;
  277.         }
  278.     }
  279.  
  280.     if (auto spell = g_spells->getSpellByName(name)) {
  281.         sb.spell = spell;
  282.         return true;
  283.     }
  284.  
  285.     CombatSpell* combatSpell = nullptr;
  286.     bool needTarget = false;
  287.     bool needDirection = false;
  288.  
  289.     if (isScripted) {
  290.         if ((attr = node.attribute("direction"))) {
  291.             needDirection = attr.as_bool();
  292.         }
  293.  
  294.         if ((attr = node.attribute("target"))) {
  295.             needTarget = attr.as_bool();
  296.         }
  297.  
  298.         std::unique_ptr<CombatSpell> combatSpellPtr(new CombatSpell(nullptr, needTarget, needDirection));
  299.         if (!combatSpellPtr->loadScript("data/" + g_spells->getScriptBaseName() + "/scripts/" + scriptName)) {
  300.             return false;
  301.         }
  302.  
  303.         if (!combatSpellPtr->loadScriptCombat()) {
  304.             return false;
  305.         }
  306.  
  307.         combatSpell = combatSpellPtr.release();
  308.         combatSpell->getCombat()->setPlayerCombatValues(COMBAT_FORMULA_DAMAGE, sb.minCombatValue, 0, sb.maxCombatValue, 0);
  309.     } else {
  310.         Combat* combat = new Combat;
  311.         if ((attr = node.attribute("length"))) {
  312.             int32_t length = pugi::cast<int32_t>(attr.value());
  313.             if (length > 0) {
  314.                 int32_t spread = 3;
  315.  
  316.                 //need direction spell
  317.                 if ((attr = node.attribute("spread"))) {
  318.                     spread = std::max<int32_t>(0, pugi::cast<int32_t>(attr.value()));
  319.                 }
  320.  
  321.                 AreaCombat* area = new AreaCombat();
  322.                 area->setupArea(length, spread);
  323.                 combat->setArea(area);
  324.  
  325.                 needDirection = true;
  326.             }
  327.         }
  328.  
  329.         if ((attr = node.attribute("radius"))) {
  330.             int32_t radius = pugi::cast<int32_t>(attr.value());
  331.  
  332.             //target spell
  333.             if ((attr = node.attribute("target"))) {
  334.                 needTarget = attr.as_bool();
  335.             }
  336.  
  337.             AreaCombat* area = new AreaCombat();
  338.             area->setupArea(radius);
  339.             combat->setArea(area);
  340.         }
  341.  
  342.         std::string tmpName = asLowerCaseString(name);
  343.  
  344.         if (tmpName == "melee") {
  345.             sb.isMelee = true;
  346.  
  347.             pugi::xml_attribute attackAttribute, skillAttribute;
  348.             if ((attackAttribute = node.attribute("attack")) && (skillAttribute = node.attribute("skill"))) {
  349.                 sb.minCombatValue = 0;
  350.                 sb.maxCombatValue = -Weapons::getMaxMeleeDamage(pugi::cast<int32_t>(skillAttribute.value()), pugi::cast<int32_t>(attackAttribute.value()));
  351.             }
  352.  
  353.             ConditionType_t conditionType = CONDITION_NONE;
  354.             int32_t minDamage = 0;
  355.             int32_t maxDamage = 0;
  356.             uint32_t tickInterval = 2000;
  357.  
  358.             if ((attr = node.attribute("fire"))) {
  359.                 conditionType = CONDITION_FIRE;
  360.  
  361.                 minDamage = pugi::cast<int32_t>(attr.value());
  362.                 maxDamage = minDamage;
  363.                 tickInterval = 9000;
  364.             } else if ((attr = node.attribute("poison"))) {
  365.                 conditionType = CONDITION_POISON;
  366.  
  367.                 minDamage = pugi::cast<int32_t>(attr.value());
  368.                 maxDamage = minDamage;
  369.                 tickInterval = 5000;
  370.             } else if ((attr = node.attribute("energy"))) {
  371.                 conditionType = CONDITION_ENERGY;
  372.  
  373.                 minDamage = pugi::cast<int32_t>(attr.value());
  374.                 maxDamage = minDamage;
  375.                 tickInterval = 10000;
  376.             } else if ((attr = node.attribute("drown"))) {
  377.                 conditionType = CONDITION_DROWN;
  378.  
  379.                 minDamage = pugi::cast<int32_t>(attr.value());
  380.                 maxDamage = minDamage;
  381.                 tickInterval = 5000;
  382.             } else if ((attr = node.attribute("freeze"))) {
  383.                 conditionType = CONDITION_FREEZING;
  384.  
  385.                 minDamage = pugi::cast<int32_t>(attr.value());
  386.                 maxDamage = minDamage;
  387.                 tickInterval = 8000;
  388.             } else if ((attr = node.attribute("dazzle"))) {
  389.                 conditionType = CONDITION_DAZZLED;
  390.  
  391.                 minDamage = pugi::cast<int32_t>(attr.value());
  392.                 maxDamage = minDamage;
  393.                 tickInterval = 10000;
  394.             } else if ((attr = node.attribute("curse"))) {
  395.                 conditionType = CONDITION_CURSED;
  396.  
  397.                 minDamage = pugi::cast<int32_t>(attr.value());
  398.                 maxDamage = minDamage;
  399.                 tickInterval = 4000;
  400.             } else if ((attr = node.attribute("bleed")) || (attr = node.attribute("physical"))) {
  401.                 conditionType = CONDITION_BLEEDING;
  402.                 tickInterval = 5000;
  403.             }
  404.  
  405.             if ((attr = node.attribute("tick"))) {
  406.                 int32_t value = pugi::cast<int32_t>(attr.value());
  407.                 if (value > 0) {
  408.                     tickInterval = value;
  409.                 }
  410.             }
  411.  
  412.             if (conditionType != CONDITION_NONE) {
  413.                 Condition* condition = getDamageCondition(conditionType, maxDamage, minDamage, 0, tickInterval);
  414.                 combat->setCondition(condition);
  415.             }
  416.  
  417.             sb.range = 1;
  418.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE);
  419.             combat->setParam(COMBAT_PARAM_BLOCKARMOR, 1);
  420.             combat->setParam(COMBAT_PARAM_BLOCKSHIELD, 1);
  421.             combat->setOrigin(ORIGIN_MELEE);
  422.         } else if (tmpName == "physical") {
  423.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE);
  424.             combat->setParam(COMBAT_PARAM_BLOCKARMOR, 1);
  425.             combat->setOrigin(ORIGIN_RANGED);
  426.         } else if (tmpName == "bleed") {
  427.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE);
  428.         } else if (tmpName == "poison" || tmpName == "earth") {
  429.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_EARTHDAMAGE);
  430.         } else if (tmpName == "fire") {
  431.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_FIREDAMAGE);
  432.         } else if (tmpName == "energy") {
  433.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE);
  434.         } else if (tmpName == "drown") {
  435.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_DROWNDAMAGE);
  436.         } else if (tmpName == "ice") {
  437.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE);
  438.         } else if (tmpName == "holy") {
  439.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_HOLYDAMAGE);
  440.         } else if (tmpName == "death") {
  441.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE);
  442.         } else if (tmpName == "lifedrain") {
  443.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_LIFEDRAIN);
  444.         } else if (tmpName == "manadrain") {
  445.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_MANADRAIN);
  446.         } else if (tmpName == "healing") {
  447.             combat->setParam(COMBAT_PARAM_TYPE, COMBAT_HEALING);
  448.             combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0);
  449.         } else if (tmpName == "speed") {
  450.             int32_t speedChange = 0;
  451.             int32_t duration = 10000;
  452.  
  453.             if ((attr = node.attribute("duration"))) {
  454.                 duration = pugi::cast<int32_t>(attr.value());
  455.             }
  456.  
  457.             if ((attr = node.attribute("speedchange"))) {
  458.                 speedChange = pugi::cast<int32_t>(attr.value());
  459.                 if (speedChange < -1000) {
  460.                     //cant be slower than 100%
  461.                     speedChange = -1000;
  462.                 }
  463.             }
  464.  
  465.             ConditionType_t conditionType;
  466.             if (speedChange > 0) {
  467.                 conditionType = CONDITION_HASTE;
  468.                 combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0);
  469.             } else {
  470.                 conditionType = CONDITION_PARALYZE;
  471.             }
  472.  
  473.             ConditionSpeed* condition = static_cast<ConditionSpeed*>(Condition::createCondition(CONDITIONID_COMBAT, conditionType, duration, 0));
  474.             condition->setFormulaVars(speedChange / 1000.0, 0, speedChange / 1000.0, 0);
  475.             combat->setCondition(condition);
  476.         } else if (tmpName == "outfit") {
  477.             int32_t duration = 10000;
  478.  
  479.             if ((attr = node.attribute("duration"))) {
  480.                 duration = pugi::cast<int32_t>(attr.value());
  481.             }
  482.  
  483.             if ((attr = node.attribute("monster"))) {
  484.                 MonsterType* mType = g_monsters.getMonsterType(attr.as_string());
  485.                 if (mType) {
  486.                     ConditionOutfit* condition = static_cast<ConditionOutfit*>(Condition::createCondition(CONDITIONID_COMBAT, CONDITION_OUTFIT, duration, 0));
  487.                     condition->setOutfit(mType->info.outfit);
  488.                     combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0);
  489.                     combat->setCondition(condition);
  490.                 }
  491.             } else if ((attr = node.attribute("item"))) {
  492.                 Outfit_t outfit;
  493.                 outfit.lookTypeEx = pugi::cast<uint16_t>(attr.value());
  494.  
  495.                 ConditionOutfit* condition = static_cast<ConditionOutfit*>(Condition::createCondition(CONDITIONID_COMBAT, CONDITION_OUTFIT, duration, 0));
  496.                 condition->setOutfit(outfit);
  497.                 combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0);
  498.                 combat->setCondition(condition);
  499.             }
  500.         } else if (tmpName == "invisible") {
  501.             int32_t duration = 10000;
  502.  
  503.             if ((attr = node.attribute("duration"))) {
  504.                 duration = pugi::cast<int32_t>(attr.value());
  505.             }
  506.  
  507.             Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_INVISIBLE, duration, 0);
  508.             combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0);
  509.             combat->setCondition(condition);
  510.         } else if (tmpName == "drunk") {
  511.             int32_t duration = 10000;
  512.  
  513.             if ((attr = node.attribute("duration"))) {
  514.                 duration = pugi::cast<int32_t>(attr.value());
  515.             }
  516.  
  517.             Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_DRUNK, duration, 0);
  518.             combat->setCondition(condition);
  519.         } else if (tmpName == "firefield") {
  520.             combat->setParam(COMBAT_PARAM_CREATEITEM, ITEM_FIREFIELD_PVP_FULL);
  521.         } else if (tmpName == "poisonfield") {
  522.             combat->setParam(COMBAT_PARAM_CREATEITEM, ITEM_POISONFIELD_PVP);
  523.         } else if (tmpName == "energyfield") {
  524.             combat->setParam(COMBAT_PARAM_CREATEITEM, ITEM_ENERGYFIELD_PVP);
  525.         } else if (tmpName == "firecondition" || tmpName == "energycondition" ||
  526.                    tmpName == "earthcondition" || tmpName == "poisoncondition" ||
  527.                    tmpName == "icecondition" || tmpName == "freezecondition" ||
  528.                    tmpName == "deathcondition" || tmpName == "cursecondition" ||
  529.                    tmpName == "holycondition" || tmpName == "dazzlecondition" ||
  530.                    tmpName == "drowncondition" || tmpName == "bleedcondition" ||
  531.                    tmpName == "physicalcondition") {
  532.             ConditionType_t conditionType = CONDITION_NONE;
  533.             uint32_t tickInterval = 2000;
  534.  
  535.             if (tmpName == "firecondition") {
  536.                 conditionType = CONDITION_FIRE;
  537.                 tickInterval = 10000;
  538.             } else if (tmpName == "poisoncondition" || tmpName == "earthcondition") {
  539.                 conditionType = CONDITION_POISON;
  540.                 tickInterval = 5000;
  541.             } else if (tmpName == "energycondition") {
  542.                 conditionType = CONDITION_ENERGY;
  543.                 tickInterval = 10000;
  544.             } else if (tmpName == "drowncondition") {
  545.                 conditionType = CONDITION_DROWN;
  546.                 tickInterval = 5000;
  547.             } else if (tmpName == "freezecondition" || tmpName == "icecondition") {
  548.                 conditionType = CONDITION_FREEZING;
  549.                 tickInterval = 10000;
  550.             } else if (tmpName == "cursecondition" || tmpName == "deathcondition") {
  551.                 conditionType = CONDITION_CURSED;
  552.                 tickInterval = 4000;
  553.             } else if (tmpName == "dazzlecondition" || tmpName == "holycondition") {
  554.                 conditionType = CONDITION_DAZZLED;
  555.                 tickInterval = 10000;
  556.             } else if (tmpName == "physicalcondition" || tmpName == "bleedcondition") {
  557.                 conditionType = CONDITION_BLEEDING;
  558.                 tickInterval = 5000;
  559.             }
  560.  
  561.             if ((attr = node.attribute("tick"))) {
  562.                 int32_t value = pugi::cast<int32_t>(attr.value());
  563.                 if (value > 0) {
  564.                     tickInterval = value;
  565.                 }
  566.             }
  567.  
  568.             int32_t minDamage = std::abs(sb.minCombatValue);
  569.             int32_t maxDamage = std::abs(sb.maxCombatValue);
  570.             int32_t startDamage = 0;
  571.  
  572.             if ((attr = node.attribute("start"))) {
  573.                 int32_t value = std::abs(pugi::cast<int32_t>(attr.value()));
  574.                 if (value <= minDamage) {
  575.                     startDamage = value;
  576.                 }
  577.             }
  578.  
  579.             Condition* condition = getDamageCondition(conditionType, maxDamage, minDamage, startDamage, tickInterval);
  580.             combat->setCondition(condition);
  581.         } else if (tmpName == "strength") {
  582.             //
  583.         } else if (tmpName == "effect") {
  584.             //
  585.         } else {
  586.             std::cout << "[Error - Monsters::deserializeSpell] - " << description << " - Unknown spell name: " << name << std::endl;
  587.             delete combat;
  588.             return false;
  589.         }
  590.  
  591.         combat->setPlayerCombatValues(COMBAT_FORMULA_DAMAGE, sb.minCombatValue, 0, sb.maxCombatValue, 0);
  592.         combatSpell = new CombatSpell(combat, needTarget, needDirection);
  593.  
  594.         for (auto attributeNode : node.children()) {
  595.             if ((attr = attributeNode.attribute("key"))) {
  596.                 const char* value = attr.value();
  597.                 if (strcasecmp(value, "shooteffect") == 0) {
  598.                     if ((attr = attributeNode.attribute("value"))) {
  599.                         ShootType_t shoot = getShootType(asLowerCaseString(attr.as_string()));
  600.                         if (shoot != CONST_ANI_NONE) {
  601.                             combat->setParam(COMBAT_PARAM_DISTANCEEFFECT, shoot);
  602.                         } else {
  603.                             std::cout << "[Warning - Monsters::deserializeSpell] " << description << " - Unknown shootEffect: " << attr.as_string() << std::endl;
  604.                         }
  605.                     }
  606.                 } else if (strcasecmp(value, "areaeffect") == 0) {
  607.                     if ((attr = attributeNode.attribute("value"))) {
  608.                         MagicEffectClasses effect = getMagicEffect(asLowerCaseString(attr.as_string()));
  609.                         if (effect != CONST_ME_NONE) {
  610.                             combat->setParam(COMBAT_PARAM_EFFECT, effect);
  611.                         } else {
  612.                             std::cout << "[Warning - Monsters::deserializeSpell] " << description << " - Unknown areaEffect: " << attr.as_string() << std::endl;
  613.                         }
  614.                     }
  615.                 } else {
  616.                     std::cout << "[Warning - Monsters::deserializeSpells] Effect type \"" << attr.as_string() << "\" does not exist." << std::endl;
  617.                 }
  618.             }
  619.         }
  620.     }
  621.  
  622.     sb.spell = combatSpell;
  623.     if (combatSpell) {
  624.         sb.combatSpell = true;
  625.     }
  626.     return true;
  627. }
  628.  
  629. bool Monsters::loadMonster(const std::string& file, const std::string& monsterName, std::list<std::pair<MonsterType*, std::string>>& monsterScriptList, bool reloading /*= false*/)
  630. {
  631.     MonsterType* mType = nullptr;
  632.     bool new_mType = true;
  633.  
  634.     pugi::xml_document doc;
  635.     pugi::xml_parse_result result = doc.load_file(file.c_str());
  636.     if (!result) {
  637.         printXMLError("Error - Monsters::loadMonster", file, result);
  638.         return false;
  639.     }
  640.  
  641.     pugi::xml_node monsterNode = doc.child("monster");
  642.     if (!monsterNode) {
  643.         std::cout << "[Error - Monsters::loadMonster] Missing monster node in: " << file << std::endl;
  644.         return false;
  645.     }
  646.  
  647.     pugi::xml_attribute attr;
  648.     if (!(attr = monsterNode.attribute("name"))) {
  649.         std::cout << "[Error - Monsters::loadMonster] Missing name in: " << file << std::endl;
  650.         return false;
  651.     }
  652.  
  653.     if (reloading) {
  654.         mType = getMonsterType(monsterName);
  655.         if (mType != nullptr) {
  656.             new_mType = false;
  657.             mType->info = {};
  658.         }
  659.     }
  660.  
  661.     if (new_mType) {
  662.         mType = &monsters[asLowerCaseString(monsterName)];
  663.     }
  664.  
  665.     mType->name = attr.as_string();
  666.  
  667.     if ((attr = monsterNode.attribute("nameDescription"))) {
  668.         mType->nameDescription = attr.as_string();
  669.     } else {
  670.         mType->nameDescription = "a " + asLowerCaseString(mType->name);
  671.     }
  672.  
  673.     if ((attr = monsterNode.attribute("race"))) {
  674.         std::string tmpStrValue = asLowerCaseString(attr.as_string());
  675.         uint16_t tmpInt = pugi::cast<uint16_t>(attr.value());
  676.         if (tmpStrValue == "venom" || tmpInt == 1) {
  677.             mType->info.race = RACE_VENOM;
  678.         } else if (tmpStrValue == "blood" || tmpInt == 2) {
  679.             mType->info.race = RACE_BLOOD;
  680.         } else if (tmpStrValue == "undead" || tmpInt == 3) {
  681.             mType->info.race = RACE_UNDEAD;
  682.         } else if (tmpStrValue == "fire" || tmpInt == 4) {
  683.             mType->info.race = RACE_FIRE;
  684.         } else if (tmpStrValue == "energy" || tmpInt == 5) {
  685.             mType->info.race = RACE_ENERGY;
  686.         } else {
  687.             std::cout << "[Warning - Monsters::loadMonster] Unknown race type " << attr.as_string() << ". " << file << std::endl;
  688.         }
  689.     }
  690.  
  691.     if ((attr = monsterNode.attribute("experience"))) {
  692.         mType->info.experience = pugi::cast<uint64_t>(attr.value());
  693.     }
  694.  
  695.     if ((attr = monsterNode.attribute("speed"))) {
  696.         mType->info.baseSpeed = pugi::cast<int32_t>(attr.value());
  697.     }
  698.  
  699.     if ((attr = monsterNode.attribute("manacost"))) {
  700.         mType->info.manaCost = pugi::cast<uint32_t>(attr.value());
  701.     }
  702.  
  703.     if ((attr = monsterNode.attribute("skull"))) {
  704.         mType->info.skull = getSkullType(asLowerCaseString(attr.as_string()));
  705.     }
  706.  
  707.     if ((attr = monsterNode.attribute("script"))) {
  708.         monsterScriptList.emplace_back(mType, attr.as_string());
  709.     }
  710.  
  711.     pugi::xml_node node;
  712.     if ((node = monsterNode.child("health"))) {
  713.         if ((attr = node.attribute("now"))) {
  714.             mType->info.health = pugi::cast<int32_t>(attr.value());
  715.         } else {
  716.             std::cout << "[Error - Monsters::loadMonster] Missing health now. " << file << std::endl;
  717.         }
  718.  
  719.         if ((attr = node.attribute("max"))) {
  720.             mType->info.healthMax = pugi::cast<int32_t>(attr.value());
  721.         } else {
  722.             std::cout << "[Error - Monsters::loadMonster] Missing health max. " << file << std::endl;
  723.         }
  724.     }
  725.  
  726.     if ((node = monsterNode.child("flags"))) {
  727.         for (auto flagNode : node.children()) {
  728.             attr = flagNode.first_attribute();
  729.             const char* attrName = attr.name();
  730.             if (strcasecmp(attrName, "summonable") == 0) {
  731.                 mType->info.isSummonable = attr.as_bool();
  732.             } else if (strcasecmp(attrName, "attackable") == 0) {
  733.                 mType->info.isAttackable = attr.as_bool();
  734.             } else if (strcasecmp(attrName, "hostile") == 0) {
  735.                 mType->info.isHostile = attr.as_bool();
  736.             } else if (strcasecmp(attrName, "illusionable") == 0) {
  737.                 mType->info.isIllusionable = attr.as_bool();
  738.             } else if (strcasecmp(attrName, "convinceable") == 0) {
  739.                 mType->info.isConvinceable = attr.as_bool();
  740.             } else if (strcasecmp(attrName, "pushable") == 0) {
  741.                 mType->info.pushable = attr.as_bool();
  742.             } else if (strcasecmp(attrName, "canpushitems") == 0) {
  743.                 mType->info.canPushItems = attr.as_bool();
  744.             } else if (strcasecmp(attrName, "canpushcreatures") == 0) {
  745.                 mType->info.canPushCreatures = attr.as_bool();
  746.             } else if (strcasecmp(attrName, "staticattack") == 0) {
  747.                 uint32_t staticAttack = pugi::cast<uint32_t>(attr.value());
  748.                 if (staticAttack > 100) {
  749.                     std::cout << "[Warning - Monsters::loadMonster] staticattack greater than 100. " << file << std::endl;
  750.                     staticAttack = 100;
  751.                 }
  752.  
  753.                 mType->info.staticAttackChance = staticAttack;
  754.             } else if (strcasecmp(attrName, "lightlevel") == 0) {
  755.                 mType->info.light.level = pugi::cast<uint16_t>(attr.value());
  756.             } else if (strcasecmp(attrName, "lightcolor") == 0) {
  757.                 mType->info.light.color = pugi::cast<uint16_t>(attr.value());
  758.             } else if (strcasecmp(attrName, "targetdistance") == 0) {
  759.                 mType->info.targetDistance = std::max<int32_t>(1, pugi::cast<int32_t>(attr.value()));
  760.             } else if (strcasecmp(attrName, "runonhealth") == 0) {
  761.                 mType->info.runAwayHealth = pugi::cast<int32_t>(attr.value());
  762.             } else if (strcasecmp(attrName, "hidehealth") == 0) {
  763.                 mType->info.hiddenHealth = attr.as_bool();
  764.             }
  765.             else if (strcasecmp(attrName, "rewardchest") == 0) {
  766.                 mType->rewardChest = attr.as_bool();
  767.             } else {
  768.                 std::cout << "[Warning - Monsters::loadMonster] Unknown flag attribute: " << attrName << ". " << file << std::endl;
  769.             }
  770.         }
  771.  
  772.         //if a monster can push creatures,
  773.         // it should not be pushable
  774.         if (mType->info.canPushCreatures) {
  775.             mType->info.pushable = false;
  776.         }
  777.     }
  778.  
  779.     if ((node = monsterNode.child("targetchange"))) {
  780.         if ((attr = node.attribute("speed")) || (attr = node.attribute("interval"))) {
  781.             mType->info.changeTargetSpeed = pugi::cast<uint32_t>(attr.value());
  782.         } else {
  783.             std::cout << "[Warning - Monsters::loadMonster] Missing targetchange speed. " << file << std::endl;
  784.         }
  785.  
  786.         if ((attr = node.attribute("chance"))) {
  787.             mType->info.changeTargetChance = pugi::cast<int32_t>(attr.value());
  788.         } else {
  789.             std::cout << "[Warning - Monsters::loadMonster] Missing targetchange chance. " << file << std::endl;
  790.         }
  791.     }
  792.  
  793.     if ((node = monsterNode.child("look"))) {
  794.         if ((attr = node.attribute("type"))) {
  795.             mType->info.outfit.lookType = pugi::cast<uint16_t>(attr.value());
  796.  
  797.             if ((attr = node.attribute("head"))) {
  798.                 mType->info.outfit.lookHead = pugi::cast<uint16_t>(attr.value());
  799.             }
  800.  
  801.             if ((attr = node.attribute("body"))) {
  802.                 mType->info.outfit.lookBody = pugi::cast<uint16_t>(attr.value());
  803.             }
  804.  
  805.             if ((attr = node.attribute("legs"))) {
  806.                 mType->info.outfit.lookLegs = pugi::cast<uint16_t>(attr.value());
  807.             }
  808.  
  809.             if ((attr = node.attribute("feet"))) {
  810.                 mType->info.outfit.lookFeet = pugi::cast<uint16_t>(attr.value());
  811.             }
  812.  
  813.             if ((attr = node.attribute("addons"))) {
  814.                 mType->info.outfit.lookAddons = pugi::cast<uint16_t>(attr.value());
  815.             }
  816.         } else if ((attr = node.attribute("typeex"))) {
  817.             mType->info.outfit.lookTypeEx = pugi::cast<uint16_t>(attr.value());
  818.         } else {
  819.             std::cout << "[Warning - Monsters::loadMonster] Missing look type/typeex. " << file << std::endl;
  820.         }
  821.  
  822.         if ((attr = node.attribute("mount"))) {
  823.             mType->info.outfit.lookMount = pugi::cast<uint16_t>(attr.value());
  824.         }
  825.  
  826.         if ((attr = node.attribute("corpse"))) {
  827.             mType->info.lookcorpse = pugi::cast<uint16_t>(attr.value());
  828.         }
  829.     }
  830.  
  831.     if ((node = monsterNode.child("attacks"))) {
  832.         for (auto attackNode : node.children()) {
  833.             spellBlock_t sb;
  834.             if (deserializeSpell(attackNode, sb, monsterName)) {
  835.                 mType->info.attackSpells.emplace_back(std::move(sb));
  836.             } else {
  837.                 std::cout << "[Warning - Monsters::loadMonster] Cant load spell. " << file << std::endl;
  838.             }
  839.         }
  840.     }
  841.  
  842.     if ((node = monsterNode.child("defenses"))) {
  843.         if ((attr = node.attribute("defense"))) {
  844.             mType->info.defense = pugi::cast<int32_t>(attr.value());
  845.         }
  846.  
  847.         if ((attr = node.attribute("armor"))) {
  848.             mType->info.armor = pugi::cast<int32_t>(attr.value());
  849.         }
  850.  
  851.         for (auto defenseNode : node.children()) {
  852.             spellBlock_t sb;
  853.             if (deserializeSpell(defenseNode, sb, monsterName)) {
  854.                 mType->info.defenseSpells.emplace_back(std::move(sb));
  855.             } else {
  856.                 std::cout << "[Warning - Monsters::loadMonster] Cant load spell. " << file << std::endl;
  857.             }
  858.         }
  859.     }
  860.  
  861.     if ((node = monsterNode.child("immunities"))) {
  862.         for (auto immunityNode : node.children()) {
  863.             if ((attr = immunityNode.attribute("name"))) {
  864.                 std::string tmpStrValue = asLowerCaseString(attr.as_string());
  865.                 if (tmpStrValue == "physical") {
  866.                     mType->info.damageImmunities |= COMBAT_PHYSICALDAMAGE;
  867.                     mType->info.conditionImmunities |= CONDITION_BLEEDING;
  868.                 } else if (tmpStrValue == "energy") {
  869.                     mType->info.damageImmunities |= COMBAT_ENERGYDAMAGE;
  870.                     mType->info.conditionImmunities |= CONDITION_ENERGY;
  871.                 } else if (tmpStrValue == "fire") {
  872.                     mType->info.damageImmunities |= COMBAT_FIREDAMAGE;
  873.                     mType->info.conditionImmunities |= CONDITION_FIRE;
  874.                 } else if (tmpStrValue == "poison" ||
  875.                             tmpStrValue == "earth") {
  876.                     mType->info.damageImmunities |= COMBAT_EARTHDAMAGE;
  877.                     mType->info.conditionImmunities |= CONDITION_POISON;
  878.                 } else if (tmpStrValue == "drown") {
  879.                     mType->info.damageImmunities |= COMBAT_DROWNDAMAGE;
  880.                     mType->info.conditionImmunities |= CONDITION_DROWN;
  881.                 } else if (tmpStrValue == "ice") {
  882.                     mType->info.damageImmunities |= COMBAT_ICEDAMAGE;
  883.                     mType->info.conditionImmunities |= CONDITION_FREEZING;
  884.                 } else if (tmpStrValue == "holy") {
  885.                     mType->info.damageImmunities |= COMBAT_HOLYDAMAGE;
  886.                     mType->info.conditionImmunities |= CONDITION_DAZZLED;
  887.                 } else if (tmpStrValue == "death") {
  888.                     mType->info.damageImmunities |= COMBAT_DEATHDAMAGE;
  889.                     mType->info.conditionImmunities |= CONDITION_CURSED;
  890.                 } else if (tmpStrValue == "lifedrain") {
  891.                     mType->info.damageImmunities |= COMBAT_LIFEDRAIN;
  892.                 } else if (tmpStrValue == "manadrain") {
  893.                     mType->info.damageImmunities |= COMBAT_MANADRAIN;
  894.                 } else if (tmpStrValue == "paralyze") {
  895.                     mType->info.conditionImmunities |= CONDITION_PARALYZE;
  896.                 } else if (tmpStrValue == "outfit") {
  897.                     mType->info.conditionImmunities |= CONDITION_OUTFIT;
  898.                 } else if (tmpStrValue == "drunk") {
  899.                     mType->info.conditionImmunities |= CONDITION_DRUNK;
  900.                 } else if (tmpStrValue == "invisible" || tmpStrValue == "invisibility") {
  901.                     mType->info.conditionImmunities |= CONDITION_INVISIBLE;
  902.                 } else if (tmpStrValue == "bleed") {
  903.                     mType->info.conditionImmunities |= CONDITION_BLEEDING;
  904.                 } else {
  905.                     std::cout << "[Warning - Monsters::loadMonster] Unknown immunity name " << attr.as_string() << ". " << file << std::endl;
  906.                 }
  907.             } else if ((attr = immunityNode.attribute("physical"))) {
  908.                 if (attr.as_bool()) {
  909.                     mType->info.damageImmunities |= COMBAT_PHYSICALDAMAGE;
  910.                     mType->info.conditionImmunities |= CONDITION_BLEEDING;
  911.                 }
  912.             } else if ((attr = immunityNode.attribute("energy"))) {
  913.                 if (attr.as_bool()) {
  914.                     mType->info.damageImmunities |= COMBAT_ENERGYDAMAGE;
  915.                     mType->info.conditionImmunities |= CONDITION_ENERGY;
  916.                 }
  917.             } else if ((attr = immunityNode.attribute("fire"))) {
  918.                 if (attr.as_bool()) {
  919.                     mType->info.damageImmunities |= COMBAT_FIREDAMAGE;
  920.                     mType->info.conditionImmunities |= CONDITION_FIRE;
  921.                 }
  922.             } else if ((attr = immunityNode.attribute("poison")) || (attr = immunityNode.attribute("earth"))) {
  923.                 if (attr.as_bool()) {
  924.                     mType->info.damageImmunities |= COMBAT_EARTHDAMAGE;
  925.                     mType->info.conditionImmunities |= CONDITION_POISON;
  926.                 }
  927.             } else if ((attr = immunityNode.attribute("drown"))) {
  928.                 if (attr.as_bool()) {
  929.                     mType->info.damageImmunities |= COMBAT_DROWNDAMAGE;
  930.                     mType->info.conditionImmunities |= CONDITION_DROWN;
  931.                 }
  932.             } else if ((attr = immunityNode.attribute("ice"))) {
  933.                 if (attr.as_bool()) {
  934.                     mType->info.damageImmunities |= COMBAT_ICEDAMAGE;
  935.                     mType->info.conditionImmunities |= CONDITION_FREEZING;
  936.                 }
  937.             } else if ((attr = immunityNode.attribute("holy"))) {
  938.                 if (attr.as_bool()) {
  939.                     mType->info.damageImmunities |= COMBAT_HOLYDAMAGE;
  940.                     mType->info.conditionImmunities |= CONDITION_DAZZLED;
  941.                 }
  942.             } else if ((attr = immunityNode.attribute("death"))) {
  943.                 if (attr.as_bool()) {
  944.                     mType->info.damageImmunities |= COMBAT_DEATHDAMAGE;
  945.                     mType->info.conditionImmunities |= CONDITION_CURSED;
  946.                 }
  947.             } else if ((attr = immunityNode.attribute("lifedrain"))) {
  948.                 if (attr.as_bool()) {
  949.                     mType->info.damageImmunities |= COMBAT_LIFEDRAIN;
  950.                 }
  951.             } else if ((attr = immunityNode.attribute("manadrain"))) {
  952.                 if (attr.as_bool()) {
  953.                     mType->info.damageImmunities |= COMBAT_MANADRAIN;
  954.                 }
  955.             } else if ((attr = immunityNode.attribute("paralyze"))) {
  956.                 if (attr.as_bool()) {
  957.                     mType->info.conditionImmunities |= CONDITION_PARALYZE;
  958.                 }
  959.             } else if ((attr = immunityNode.attribute("outfit"))) {
  960.                 if (attr.as_bool()) {
  961.                     mType->info.conditionImmunities |= CONDITION_OUTFIT;
  962.                 }
  963.             } else if ((attr = immunityNode.attribute("bleed"))) {
  964.                 if (attr.as_bool()) {
  965.                     mType->info.conditionImmunities |= CONDITION_BLEEDING;
  966.                 }
  967.             } else if ((attr = immunityNode.attribute("drunk"))) {
  968.                 if (attr.as_bool()) {
  969.                     mType->info.conditionImmunities |= CONDITION_DRUNK;
  970.                 }
  971.             } else if ((attr = immunityNode.attribute("invisible")) || (attr = immunityNode.attribute("invisibility"))) {
  972.                 if (attr.as_bool()) {
  973.                     mType->info.conditionImmunities |= CONDITION_INVISIBLE;
  974.                 }
  975.             } else {
  976.                 std::cout << "[Warning - Monsters::loadMonster] Unknown immunity. " << file << std::endl;
  977.             }
  978.         }
  979.     }
  980.  
  981.     if ((node = monsterNode.child("voices"))) {
  982.         if ((attr = node.attribute("speed")) || (attr = node.attribute("interval"))) {
  983.             mType->info.yellSpeedTicks = pugi::cast<uint32_t>(attr.value());
  984.         } else {
  985.             std::cout << "[Warning - Monsters::loadMonster] Missing voices speed. " << file << std::endl;
  986.         }
  987.  
  988.         if ((attr = node.attribute("chance"))) {
  989.             mType->info.yellChance = pugi::cast<uint32_t>(attr.value());
  990.         } else {
  991.             std::cout << "[Warning - Monsters::loadMonster] Missing voices chance. " << file << std::endl;
  992.         }
  993.  
  994.         for (auto voiceNode : node.children()) {
  995.             voiceBlock_t vb;
  996.             if ((attr = voiceNode.attribute("sentence"))) {
  997.                 vb.text = attr.as_string();
  998.             } else {
  999.                 std::cout << "[Warning - Monsters::loadMonster] Missing voice sentence. " << file << std::endl;
  1000.             }
  1001.  
  1002.             if ((attr = voiceNode.attribute("yell"))) {
  1003.                 vb.yellText = attr.as_bool();
  1004.             } else {
  1005.                 vb.yellText = false;
  1006.             }
  1007.             mType->info.voiceVector.emplace_back(vb);
  1008.         }
  1009.     }
  1010.  
  1011.     if ((node = monsterNode.child("loot"))) {
  1012.         for (auto lootNode : node.children()) {
  1013.             LootBlock lootBlock;
  1014.             if (loadLootItem(lootNode, lootBlock)) {
  1015.                 mType->info.lootItems.emplace_back(std::move(lootBlock));
  1016.             } else {
  1017.                 std::cout << "[Warning - Monsters::loadMonster] Cant load loot. " << file << std::endl;
  1018.             }
  1019.         }
  1020.     }
  1021.  
  1022.     if ((node = monsterNode.child("elements"))) {
  1023.         for (auto elementNode : node.children()) {
  1024.             if ((attr = elementNode.attribute("physicalPercent"))) {
  1025.                 mType->info.elementMap[COMBAT_PHYSICALDAMAGE] = pugi::cast<int32_t>(attr.value());
  1026.             } else if ((attr = elementNode.attribute("icePercent"))) {
  1027.                 mType->info.elementMap[COMBAT_ICEDAMAGE] = pugi::cast<int32_t>(attr.value());
  1028.             } else if ((attr = elementNode.attribute("poisonPercent")) || (attr = elementNode.attribute("earthPercent"))) {
  1029.                 mType->info.elementMap[COMBAT_EARTHDAMAGE] = pugi::cast<int32_t>(attr.value());
  1030.             } else if ((attr = elementNode.attribute("firePercent"))) {
  1031.                 mType->info.elementMap[COMBAT_FIREDAMAGE] = pugi::cast<int32_t>(attr.value());
  1032.             } else if ((attr = elementNode.attribute("energyPercent"))) {
  1033.                 mType->info.elementMap[COMBAT_ENERGYDAMAGE] = pugi::cast<int32_t>(attr.value());
  1034.             } else if ((attr = elementNode.attribute("holyPercent"))) {
  1035.                 mType->info.elementMap[COMBAT_HOLYDAMAGE] = pugi::cast<int32_t>(attr.value());
  1036.             } else if ((attr = elementNode.attribute("deathPercent"))) {
  1037.                 mType->info.elementMap[COMBAT_DEATHDAMAGE] = pugi::cast<int32_t>(attr.value());
  1038.             } else if ((attr = elementNode.attribute("drownPercent"))) {
  1039.                 mType->info.elementMap[COMBAT_DROWNDAMAGE] = pugi::cast<int32_t>(attr.value());
  1040.             } else if ((attr = elementNode.attribute("lifedrainPercent"))) {
  1041.                 mType->info.elementMap[COMBAT_LIFEDRAIN] = pugi::cast<int32_t>(attr.value());
  1042.             } else if ((attr = elementNode.attribute("manadrainPercent"))) {
  1043.                 mType->info.elementMap[COMBAT_MANADRAIN] = pugi::cast<int32_t>(attr.value());
  1044.             } else {
  1045.                 std::cout << "[Warning - Monsters::loadMonster] Unknown element percent. " << file << std::endl;
  1046.             }
  1047.         }
  1048.     }
  1049.  
  1050.     if ((node = monsterNode.child("summons"))) {
  1051.         if ((attr = node.attribute("maxSummons"))) {
  1052.             mType->info.maxSummons = std::min<uint32_t>(pugi::cast<uint32_t>(attr.value()), 100);
  1053.         } else {
  1054.             std::cout << "[Warning - Monsters::loadMonster] Missing summons maxSummons. " << file << std::endl;
  1055.         }
  1056.  
  1057.         for (auto summonNode : node.children()) {
  1058.             int32_t chance = 100;
  1059.             int32_t speed = 1000;
  1060.             int32_t max = mType->info.maxSummons;
  1061.             bool force = false;
  1062.  
  1063.             if ((attr = summonNode.attribute("speed")) || (attr = summonNode.attribute("interval"))) {
  1064.                 speed = std::max<int32_t>(1, pugi::cast<int32_t>(attr.value()));
  1065.             }
  1066.  
  1067.             if ((attr = summonNode.attribute("chance"))) {
  1068.                 chance = pugi::cast<int32_t>(attr.value());
  1069.             }
  1070.  
  1071.             if ((attr = summonNode.attribute("max"))) {
  1072.                 max = pugi::cast<uint32_t>(attr.value());
  1073.             }
  1074.  
  1075.             if ((attr = summonNode.attribute("force"))) {
  1076.                 force = attr.as_bool();
  1077.             }
  1078.  
  1079.             if ((attr = summonNode.attribute("name"))) {
  1080.                 summonBlock_t sb;
  1081.                 sb.name = attr.as_string();
  1082.                 sb.speed = speed;
  1083.                 sb.chance = chance;
  1084.                 sb.max = max;
  1085.                 sb.force = force;
  1086.                 mType->info.summons.emplace_back(sb);
  1087.             } else {
  1088.                 std::cout << "[Warning - Monsters::loadMonster] Missing summon name. " << file << std::endl;
  1089.             }
  1090.         }
  1091.     }
  1092.  
  1093.     if ((node = monsterNode.child("script"))) {
  1094.         for (auto eventNode : node.children()) {
  1095.             if ((attr = eventNode.attribute("name"))) {
  1096.                 mType->info.scripts.emplace_back(attr.as_string());
  1097.             } else {
  1098.                 std::cout << "[Warning - Monsters::loadMonster] Missing name for script event. " << file << std::endl;
  1099.             }
  1100.         }
  1101.     }
  1102.  
  1103.     mType->info.summons.shrink_to_fit();
  1104.     mType->info.lootItems.shrink_to_fit();
  1105.     mType->info.attackSpells.shrink_to_fit();
  1106.     mType->info.defenseSpells.shrink_to_fit();
  1107.     mType->info.voiceVector.shrink_to_fit();
  1108.     mType->info.scripts.shrink_to_fit();
  1109.     return true;
  1110. }
  1111.  
  1112. bool Monsters::loadLootItem(const pugi::xml_node& node, LootBlock& lootBlock)
  1113. {
  1114.     pugi::xml_attribute attr;
  1115.     if ((attr = node.attribute("id"))) {
  1116.         lootBlock.id = pugi::cast<int32_t>(attr.value());
  1117.     } else if ((attr = node.attribute("name"))) {
  1118.         auto name = attr.as_string();
  1119.         auto ids = Item::items.nameToItems.equal_range(asLowerCaseString(name));
  1120.  
  1121.         if (ids.first == Item::items.nameToItems.cend()) {
  1122.             std::cout << "[Warning - Monsters::loadMonster] Unknown loot item \"" << name << "\". " << std::endl;
  1123.             return false;
  1124.         }
  1125.  
  1126.         uint32_t id = ids.first->second;
  1127.  
  1128.         if (std::next(ids.first) != ids.second) {
  1129.             std::cout << "[Warning - Monsters::loadMonster] Non-unique loot item \"" << name << "\". " << std::endl;
  1130.             return false;
  1131.         }
  1132.  
  1133.         lootBlock.id = id;
  1134.     }
  1135.  
  1136.     if (lootBlock.id == 0) {
  1137.         return false;
  1138.     }
  1139.  
  1140.     if ((attr = node.attribute("countmax"))) {
  1141.         lootBlock.countmax = std::max<int32_t>(1, pugi::cast<int32_t>(attr.value()));
  1142.     } else {
  1143.         lootBlock.countmax = 1;
  1144.     }
  1145.  
  1146.     if ((attr = node.attribute("chance")) || (attr = node.attribute("chance1"))) {
  1147.         lootBlock.chance = std::min<int32_t>(MAX_LOOTCHANCE, pugi::cast<int32_t>(attr.value()));
  1148.     } else {
  1149.         lootBlock.chance = MAX_LOOTCHANCE;
  1150.     }
  1151.  
  1152.     if (Item::items[lootBlock.id].isContainer()) {
  1153.         loadLootContainer(node, lootBlock);
  1154.     }
  1155.  
  1156.     //optional
  1157.     if ((attr = node.attribute("subtype"))) {
  1158.         lootBlock.subType = pugi::cast<int32_t>(attr.value());
  1159.     } else {
  1160.         uint32_t charges = Item::items[lootBlock.id].charges;
  1161.         if (charges != 0) {
  1162.             lootBlock.subType = charges;
  1163.         }
  1164.     }
  1165.     if ((attr = node.attribute("uniquedrop"))) {
  1166.         lootBlock.uniquedrop = attr.as_bool();
  1167.     }
  1168.     if ((attr = node.attribute("actionId"))) {
  1169.         lootBlock.actionId = pugi::cast<int32_t>(attr.value());
  1170.     }
  1171.  
  1172.     if ((attr = node.attribute("text"))) {
  1173.         lootBlock.text = attr.as_string();
  1174.     }
  1175.     return true;
  1176. }
  1177.  
  1178. void Monsters::loadLootContainer(const pugi::xml_node& node, LootBlock& lBlock)
  1179. {
  1180.     for (auto subNode : node.children()) {
  1181.         LootBlock lootBlock;
  1182.         if (loadLootItem(subNode, lootBlock)) {
  1183.             lBlock.childLoot.emplace_back(std::move(lootBlock));
  1184.         }
  1185.     }
  1186. }
  1187.  
  1188. MonsterType* Monsters::getMonsterType(const std::string& name)
  1189. {
  1190.     auto it = monsters.find(asLowerCaseString(name));
  1191.  
  1192.     if (it == monsters.end()) {
  1193.         return nullptr;
  1194.     }
  1195.     return &it->second;
  1196. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement