Guest User

Untitled

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