Advertisement
Rellyx

weapons.cpp

Sep 22nd, 2018
220
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 24.88 KB | None | 0 0
  1. /**
  2.  * The Forgotten Server - a free and open-source MMORPG server emulator
  3.  * Copyright (C) 2018  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 "combat.h"
  23. #include "configmanager.h"
  24. #include "game.h"
  25. #include "pugicast.h"
  26. #include "weapons.h"
  27.  
  28. extern Game g_game;
  29. extern Vocations g_vocations;
  30. extern ConfigManager g_config;
  31. extern Weapons* g_weapons;
  32.  
  33. Weapons::Weapons()
  34. {
  35.     scriptInterface.initState();
  36. }
  37.  
  38. Weapons::~Weapons()
  39. {
  40.     clear();
  41. }
  42.  
  43. const Weapon* Weapons::getWeapon(const Item* item) const
  44. {
  45.     if (!item) {
  46.         return nullptr;
  47.     }
  48.  
  49.     auto it = weapons.find(item->getID());
  50.     if (it == weapons.end()) {
  51.         return nullptr;
  52.     }
  53.     return it->second;
  54. }
  55.  
  56. void Weapons::clear()
  57. {
  58.     for (const auto& it : weapons) {
  59.         delete it.second;
  60.     }
  61.     weapons.clear();
  62.  
  63.     scriptInterface.reInitState();
  64. }
  65.  
  66. LuaScriptInterface& Weapons::getScriptInterface()
  67. {
  68.     return scriptInterface;
  69. }
  70.  
  71. std::string Weapons::getScriptBaseName() const
  72. {
  73.     return "weapons";
  74. }
  75.  
  76. void Weapons::loadDefaults()
  77. {
  78.     for (size_t i = 100, size = Item::items.size(); i < size; ++i) {
  79.         const ItemType& it = Item::items.getItemType(i);
  80.         if (it.id == 0 || weapons.find(i) != weapons.end()) {
  81.             continue;
  82.         }
  83.  
  84.         switch (it.weaponType) {
  85.             case WEAPON_AXE:
  86.             case WEAPON_SWORD:
  87.             case WEAPON_CLUB: {
  88.                 WeaponMelee* weapon = new WeaponMelee(&scriptInterface);
  89.                 weapon->configureWeapon(it);
  90.                 weapons[i] = weapon;
  91.                 break;
  92.             }
  93.  
  94.             case WEAPON_AMMO:
  95.             case WEAPON_DISTANCE: {
  96.                 if (it.weaponType == WEAPON_DISTANCE && it.ammoType != AMMO_NONE) {
  97.                     continue;
  98.                 }
  99.  
  100.                 WeaponDistance* weapon = new WeaponDistance(&scriptInterface);
  101.                 weapon->configureWeapon(it);
  102.                 weapons[i] = weapon;
  103.                 break;
  104.             }
  105.  
  106.             default:
  107.                 break;
  108.         }
  109.     }
  110. }
  111.  
  112. Event_ptr Weapons::getEvent(const std::string& nodeName)
  113. {
  114.     if (strcasecmp(nodeName.c_str(), "melee") == 0) {
  115.         return Event_ptr(new WeaponMelee(&scriptInterface));
  116.     } else if (strcasecmp(nodeName.c_str(), "distance") == 0) {
  117.         return Event_ptr(new WeaponDistance(&scriptInterface));
  118.     } else if (strcasecmp(nodeName.c_str(), "wand") == 0) {
  119.         return Event_ptr(new WeaponWand(&scriptInterface));
  120.     }
  121.     return nullptr;
  122. }
  123.  
  124. bool Weapons::registerEvent(Event_ptr event, const pugi::xml_node&)
  125. {
  126.     Weapon* weapon = static_cast<Weapon*>(event.release()); //event is guaranteed to be a Weapon
  127.  
  128.     auto result = weapons.emplace(weapon->getID(), weapon);
  129.     if (!result.second) {
  130.         std::cout << "[Warning - Weapons::registerEvent] Duplicate registered item with id: " << weapon->getID() << std::endl;
  131.     }
  132.     return result.second;
  133. }
  134.  
  135. //monsters
  136. int32_t Weapons::getMaxMeleeDamage(int32_t attackSkill, int32_t attackValue)
  137. {
  138.     return static_cast<int32_t>(std::ceil((attackSkill * (attackValue * 0.05)) + (attackValue * 0.5)));
  139. }
  140.  
  141. //players
  142. int32_t Weapons::getMaxWeaponDamage(uint32_t level, int32_t attackSkill, int32_t attackValue, float attackFactor)
  143. {
  144.     return static_cast<int32_t>(std::round((level / 5) + (((((attackSkill / 4.) + 1) * (attackValue / 3.)) * 1.03) / attackFactor)));
  145. }
  146.  
  147. bool Weapon::configureEvent(const pugi::xml_node& node)
  148. {
  149.     pugi::xml_attribute attr;
  150.     if (!(attr = node.attribute("id"))) {
  151.         std::cout << "[Error - Weapon::configureEvent] Weapon without id." << std::endl;
  152.         return false;
  153.     }
  154.     id = pugi::cast<uint16_t>(attr.value());
  155.  
  156.     if ((attr = node.attribute("level"))) {
  157.         level = pugi::cast<uint32_t>(attr.value());
  158.     }
  159.  
  160.     if ((attr = node.attribute("maglv")) || (attr = node.attribute("maglevel"))) {
  161.         magLevel = pugi::cast<uint32_t>(attr.value());
  162.     }
  163.  
  164.     if ((attr = node.attribute("mana"))) {
  165.         mana = pugi::cast<uint32_t>(attr.value());
  166.     }
  167.  
  168.     if ((attr = node.attribute("manapercent"))) {
  169.         manaPercent = pugi::cast<uint32_t>(attr.value());
  170.     }
  171.  
  172.     if ((attr = node.attribute("soul"))) {
  173.         soul = pugi::cast<uint32_t>(attr.value());
  174.     }
  175.  
  176.     if ((attr = node.attribute("prem"))) {
  177.         premium = attr.as_bool();
  178.     }
  179.  
  180.     if ((attr = node.attribute("breakchance"))) {
  181.         breakChance = std::min<uint8_t>(100, pugi::cast<uint16_t>(attr.value()));
  182.     }
  183.  
  184.     if ((attr = node.attribute("action"))) {
  185.         action = getWeaponAction(asLowerCaseString(attr.as_string()));
  186.         if (action == WEAPONACTION_NONE) {
  187.             std::cout << "[Warning - Weapon::configureEvent] Unknown action " << attr.as_string() << std::endl;
  188.         }
  189.     }
  190.  
  191.     if ((attr = node.attribute("enabled"))) {
  192.         enabled = attr.as_bool();
  193.     }
  194.  
  195.     if ((attr = node.attribute("unproperly"))) {
  196.         wieldUnproperly = attr.as_bool();
  197.     }
  198.  
  199.     std::list<std::string> vocStringList;
  200.     for (auto vocationNode : node.children()) {
  201.         if (!(attr = vocationNode.attribute("name"))) {
  202.             continue;
  203.         }
  204.  
  205.         int32_t vocationId = g_vocations.getVocationId(attr.as_string());
  206.         if (vocationId != -1) {
  207.             vocWeaponMap[vocationId] = true;
  208.             int32_t promotedVocation = g_vocations.getPromotedVocation(vocationId);
  209.             if (promotedVocation != VOCATION_NONE) {
  210.                 vocWeaponMap[promotedVocation] = true;
  211.             }
  212.  
  213.             if (vocationNode.attribute("showInDescription").as_bool(true)) {
  214.                 vocStringList.push_back(asLowerCaseString(attr.as_string()));
  215.             }
  216.         }
  217.     }
  218.  
  219.     std::string vocationString;
  220.     for (const std::string& str : vocStringList) {
  221.         if (!vocationString.empty()) {
  222.             if (str != vocStringList.back()) {
  223.                 vocationString.push_back(',');
  224.                 vocationString.push_back(' ');
  225.             } else {
  226.                 vocationString += " and ";
  227.             }
  228.         }
  229.  
  230.         vocationString += str;
  231.         vocationString.push_back('s');
  232.     }
  233.  
  234.     uint32_t wieldInfo = 0;
  235.     if (getReqLevel() > 0) {
  236.         wieldInfo |= WIELDINFO_LEVEL;
  237.     }
  238.  
  239.     if (getReqMagLv() > 0) {
  240.         wieldInfo |= WIELDINFO_MAGLV;
  241.     }
  242.  
  243.     if (!vocationString.empty()) {
  244.         wieldInfo |= WIELDINFO_VOCREQ;
  245.     }
  246.  
  247.     if (isPremium()) {
  248.         wieldInfo |= WIELDINFO_PREMIUM;
  249.     }
  250.  
  251.     if (wieldInfo != 0) {
  252.         ItemType& it = Item::items.getItemType(id);
  253.         it.wieldInfo = wieldInfo;
  254.         it.vocationString = vocationString;
  255.         it.minReqLevel = getReqLevel();
  256.         it.minReqMagicLevel = getReqMagLv();
  257.     }
  258.  
  259.     configureWeapon(Item::items[id]);
  260.     return true;
  261. }
  262.  
  263. void Weapon::configureWeapon(const ItemType& it)
  264. {
  265.     id = it.id;
  266. }
  267.  
  268. std::string Weapon::getScriptEventName() const
  269. {
  270.     return "onUseWeapon";
  271. }
  272.  
  273. int32_t Weapon::playerWeaponCheck(Player* player, Creature* target, uint8_t shootRange) const
  274. {
  275.     const Position& playerPos = player->getPosition();
  276.     const Position& targetPos = target->getPosition();
  277.     if (playerPos.z != targetPos.z) {
  278.         return 0;
  279.     }
  280.  
  281.     if (std::max<uint32_t>(Position::getDistanceX(playerPos, targetPos), Position::getDistanceY(playerPos, targetPos)) > shootRange) {
  282.         return 0;
  283.     }
  284.  
  285.     if (!player->hasFlag(PlayerFlag_IgnoreWeaponCheck)) {
  286.         if (!enabled) {
  287.             return 0;
  288.         }
  289.  
  290.         if (player->getMana() < getManaCost(player)) {
  291.             return 0;
  292.         }
  293.  
  294.         if (player->getSoul() < soul) {
  295.             return 0;
  296.         }
  297.  
  298.         if (isPremium() && !player->isPremium()) {
  299.             return 0;
  300.         }
  301.  
  302.         if (!vocWeaponMap.empty()) {
  303.             if (vocWeaponMap.find(player->getVocationId()) == vocWeaponMap.end()) {
  304.                 return 0;
  305.             }
  306.         }
  307.  
  308.         int32_t damageModifier = 100;
  309.         if (player->isDualWielding()) {
  310.             damageModifier = g_config.getNumber(ConfigManager::DUAL_WIELDING_DAMAGE_RATE);
  311.         }
  312.  
  313.         if (player->getLevel() < getReqLevel()) {
  314.             damageModifier = (isWieldedUnproperly() ? damageModifier / 2 : 0);
  315.         }
  316.  
  317.         if (player->getMagicLevel() < getReqMagLv()) {
  318.             damageModifier = (isWieldedUnproperly() ? damageModifier / 2 : 0);
  319.         }
  320.         return damageModifier;
  321.     }
  322.  
  323.     return 100;
  324. }
  325.  
  326. bool Weapon::useWeapon(Player* player, Item* item, Creature* target) const
  327. {
  328.     int32_t damageModifier = playerWeaponCheck(player, target, item->getShootRange());
  329.     if (damageModifier == 0) {
  330.         return false;
  331.     }
  332.  
  333.     internalUseWeapon(player, item, target, damageModifier);
  334.     return true;
  335. }
  336.  
  337. bool Weapon::useFist(Player* player, Creature* target)
  338. {
  339.     if (!Position::areInRange<1, 1>(player->getPosition(), target->getPosition())) {
  340.         return false;
  341.     }
  342.  
  343.     float attackFactor = player->getAttackFactor();
  344.     int32_t attackSkill = player->getSkillLevel(SKILL_FIST);
  345.     int32_t attackValue = 7;
  346.  
  347.     int32_t maxDamage = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor);
  348.  
  349.     CombatParams params;
  350.     params.combatType = COMBAT_PHYSICALDAMAGE;
  351.     params.blockedByArmor = true;
  352.     params.blockedByShield = true;
  353.  
  354.     CombatDamage damage;
  355.     damage.origin = ORIGIN_MELEE;
  356.     damage.primary.type = params.combatType;
  357.     damage.primary.value = -normal_random(0, maxDamage);
  358.  
  359.     Combat::doCombatHealth(player, target, damage, params);
  360.     if (!player->hasFlag(PlayerFlag_NotGainSkill) && player->getAddAttackSkill()) {
  361.         player->addSkillAdvance(SKILL_FIST, 1);
  362.     }
  363.  
  364.     return true;
  365. }
  366.  
  367. void Weapon::internalUseWeapon(Player* player, Item* item, Creature* target, int32_t damageModifier) const
  368. {
  369.     if (scripted) {
  370.         LuaVariant var;
  371.         var.type = VARIANT_NUMBER;
  372.         var.number = target->getID();
  373.         executeUseWeapon(player, var);
  374.     } else {
  375.         CombatDamage damage;
  376.         WeaponType_t weaponType = item->getWeaponType();
  377.         if (weaponType == WEAPON_AMMO || weaponType == WEAPON_DISTANCE) {
  378.             damage.origin = ORIGIN_RANGED;
  379.         } else {
  380.             damage.origin = ORIGIN_MELEE;
  381.         }
  382.         damage.primary.type = params.combatType;
  383.         damage.primary.value = (getWeaponDamage(player, target, item) * damageModifier) / 100;
  384.         damage.secondary.type = getElementType();
  385.         damage.secondary.value = getElementDamage(player, target, item);
  386.         Combat::doCombatHealth(player, target, damage, params);
  387.     }
  388.  
  389.     onUsedWeapon(player, item, target->getTile());
  390. }
  391.  
  392. void Weapon::internalUseWeapon(Player* player, Item* item, Tile* tile) const
  393. {
  394.     if (scripted) {
  395.         LuaVariant var;
  396.         var.type = VARIANT_TARGETPOSITION;
  397.         var.pos = tile->getPosition();
  398.         executeUseWeapon(player, var);
  399.     } else {
  400.         Combat::postCombatEffects(player, tile->getPosition(), params);
  401.         g_game.addMagicEffect(tile->getPosition(), CONST_ME_POFF);
  402.     }
  403.  
  404.     onUsedWeapon(player, item, tile);
  405. }
  406.  
  407. void Weapon::onUsedWeapon(Player* player, Item* item, Tile* destTile) const
  408. {
  409.     if (!player->hasFlag(PlayerFlag_NotGainSkill)) {
  410.         skills_t skillType;
  411.         uint32_t skillPoint;
  412.         if (getSkillType(player, item, skillType, skillPoint)) {
  413.             //player->addSkillAdvance(skillType, skillPoint);
  414.               /* Advance one point for every single-wielding hit OR one point for every two hit for each hand */
  415.             if (!player->isDualWielding() || !player->getBlockSkillAdvance()) {
  416.                 player->addSkillAdvance(skillType, skillPoint);
  417.             }
  418.  
  419.             /* For every dual-wielding turn (one hit for each hand), flip the block skill bit */
  420.             if (player->getAttackHand() == CONST_SLOT_LEFT) {
  421.                 player->switchBlockSkillAdvance();
  422.             }
  423.         }
  424.     }
  425.  
  426.     /* There's not even the need to check if player is dual wielding: the variable is ignored otherwise. */
  427.     player->switchAttackHand();
  428.  
  429.     uint32_t manaCost = getManaCost(player);
  430.     if (manaCost != 0) {
  431.         player->addManaSpent(manaCost);
  432.         player->changeMana(-static_cast<int32_t>(manaCost));
  433.     }
  434.  
  435.     if (!player->hasFlag(PlayerFlag_HasInfiniteSoul) && soul > 0) {
  436.         player->changeSoul(-static_cast<int32_t>(soul));
  437.     }
  438.  
  439.     if (breakChance != 0 && uniform_random(1, 100) <= breakChance) {
  440.         Weapon::decrementItemCount(item);
  441.         return;
  442.     }
  443.  
  444.     switch (action) {
  445.         case WEAPONACTION_REMOVECOUNT:
  446.             Weapon::decrementItemCount(item);
  447.             break;
  448.  
  449.         case WEAPONACTION_REMOVECHARGE: {
  450.             uint16_t charges = item->getCharges();
  451.             if (charges != 0) {
  452.                 g_game.transformItem(item, item->getID(), charges - 1);
  453.             }
  454.             break;
  455.         }
  456.  
  457.         case WEAPONACTION_MOVE:
  458.             g_game.internalMoveItem(item->getParent(), destTile, INDEX_WHEREEVER, item, 1, nullptr, FLAG_NOLIMIT);
  459.             break;
  460.  
  461.         default:
  462.             break;
  463.     }
  464. }
  465.  
  466. uint32_t Weapon::getManaCost(const Player* player) const
  467. {
  468.     if (mana != 0) {
  469.         return mana;
  470.     }
  471.  
  472.     if (manaPercent == 0) {
  473.         return 0;
  474.     }
  475.  
  476.     return (player->getMaxMana() * manaPercent) / 100;
  477. }
  478.  
  479. bool Weapon::executeUseWeapon(Player* player, const LuaVariant& var) const
  480. {
  481.     //onUseWeapon(player, var)
  482.     if (!scriptInterface->reserveScriptEnv()) {
  483.         std::cout << "[Error - Weapon::executeUseWeapon] Call stack overflow" << std::endl;
  484.         return false;
  485.     }
  486.  
  487.     ScriptEnvironment* env = scriptInterface->getScriptEnv();
  488.     env->setScriptId(scriptId, scriptInterface);
  489.  
  490.     lua_State* L = scriptInterface->getLuaState();
  491.  
  492.     scriptInterface->pushFunction(scriptId);
  493.     LuaScriptInterface::pushUserdata<Player>(L, player);
  494.     LuaScriptInterface::setMetatable(L, -1, "Player");
  495.     scriptInterface->pushVariant(L, var);
  496.  
  497.     return scriptInterface->callFunction(2);
  498. }
  499.  
  500. void Weapon::decrementItemCount(Item* item)
  501. {
  502.     uint16_t count = item->getItemCount();
  503.     if (count > 1) {
  504.         g_game.transformItem(item, item->getID(), count - 1);
  505.     } else {
  506.         g_game.internalRemoveItem(item);
  507.     }
  508. }
  509.  
  510. WeaponMelee::WeaponMelee(LuaScriptInterface* interface) :
  511.     Weapon(interface)
  512. {
  513.     params.blockedByArmor = true;
  514.     params.blockedByShield = true;
  515.     params.combatType = COMBAT_PHYSICALDAMAGE;
  516. }
  517.  
  518. void WeaponMelee::configureWeapon(const ItemType& it)
  519. {
  520.     if (it.abilities) {
  521.         elementType = it.abilities->elementType;
  522.         elementDamage = it.abilities->elementDamage;
  523.         params.aggressive = true;
  524.         params.useCharges = true;
  525.     } else {
  526.         elementType = COMBAT_NONE;
  527.         elementDamage = 0;
  528.     }
  529.     Weapon::configureWeapon(it);
  530. }
  531.  
  532. bool WeaponMelee::useWeapon(Player* player, Item* item, Creature* target) const
  533. {
  534.     int32_t damageModifier = playerWeaponCheck(player, target, item->getShootRange());
  535.     if (damageModifier == 0) {
  536.         return false;
  537.     }
  538.  
  539.     internalUseWeapon(player, item, target, damageModifier);
  540.     return true;
  541. }
  542.  
  543. bool WeaponMelee::getSkillType(const Player* player, const Item* item,
  544.                                skills_t& skill, uint32_t& skillpoint) const
  545. {
  546.     if (player->getAddAttackSkill() && player->getLastAttackBlockType() != BLOCK_IMMUNITY) {
  547.         skillpoint = 1;
  548.     } else {
  549.         skillpoint = 0;
  550.     }
  551.  
  552.     WeaponType_t weaponType = item->getWeaponType();
  553.     switch (weaponType) {
  554.         case WEAPON_SWORD: {
  555.             skill = SKILL_SWORD;
  556.             return true;
  557.         }
  558.  
  559.         case WEAPON_CLUB: {
  560.             skill = SKILL_CLUB;
  561.             return true;
  562.         }
  563.  
  564.         case WEAPON_AXE: {
  565.             skill = SKILL_AXE;
  566.             return true;
  567.         }
  568.  
  569.         default:
  570.             break;
  571.     }
  572.     return false;
  573. }
  574.  
  575. int32_t WeaponMelee::getElementDamage(const Player* player, const Creature*, const Item* item) const
  576. {
  577.     if (elementType == COMBAT_NONE) {
  578.         return 0;
  579.     }
  580.  
  581.     int32_t attackSkill = player->getWeaponSkill(item);
  582.     int32_t attackValue = elementDamage;
  583.     float attackFactor = player->getAttackFactor();
  584.  
  585.     int32_t maxValue = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor);
  586.     return -normal_random(0, static_cast<int32_t>(maxValue * player->getVocation()->meleeDamageMultiplier));
  587. }
  588.  
  589. int32_t WeaponMelee::getWeaponDamage(const Player* player, const Creature*, const Item* item, bool maxDamage /*= false*/) const
  590. {
  591.     int32_t attackSkill = player->getWeaponSkill(item);
  592.     int32_t attackValue = std::max<int32_t>(0, item->getAttack());
  593.     float attackFactor = player->getAttackFactor();
  594.  
  595.     int32_t maxValue = static_cast<int32_t>(Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor) * player->getVocation()->meleeDamageMultiplier);
  596.     if (maxDamage) {
  597.         return -maxValue;
  598.     }
  599.  
  600.     return -normal_random(0, maxValue);
  601. }
  602.  
  603. WeaponDistance::WeaponDistance(LuaScriptInterface* interface) :
  604.     Weapon(interface)
  605. {
  606.     params.blockedByArmor = true;
  607.     params.combatType = COMBAT_PHYSICALDAMAGE;
  608. }
  609.  
  610. void WeaponDistance::configureWeapon(const ItemType& it)
  611. {
  612.     params.distanceEffect = it.shootType;
  613.  
  614.     if (it.abilities) {
  615.         elementType = it.abilities->elementType;
  616.         elementDamage = it.abilities->elementDamage;
  617.         params.aggressive = true;
  618.         params.useCharges = true;
  619.     } else {
  620.         elementType = COMBAT_NONE;
  621.         elementDamage = 0;
  622.     }
  623.  
  624.     Weapon::configureWeapon(it);
  625. }
  626.  
  627. bool WeaponDistance::useWeapon(Player* player, Item* item, Creature* target) const
  628. {
  629.     int32_t damageModifier;
  630.     const ItemType& it = Item::items[id];
  631.     if (it.weaponType == WEAPON_AMMO) {
  632.         Item* mainWeaponItem = player->getWeapon(true);
  633.         const Weapon* mainWeapon = g_weapons->getWeapon(mainWeaponItem);
  634.         if (mainWeapon) {
  635.             damageModifier = mainWeapon->playerWeaponCheck(player, target, mainWeaponItem->getShootRange());
  636.         } else {
  637.             damageModifier = playerWeaponCheck(player, target, mainWeaponItem->getShootRange());
  638.         }
  639.     } else {
  640.         damageModifier = playerWeaponCheck(player, target, item->getShootRange());
  641.     }
  642.  
  643.     if (damageModifier == 0) {
  644.         return false;
  645.     }
  646.  
  647.     int32_t chance;
  648.     if (it.hitChance == 0) {
  649.         //hit chance is based on distance to target and distance skill
  650.         uint32_t skill = player->getSkillLevel(SKILL_DISTANCE);
  651.         const Position& playerPos = player->getPosition();
  652.         const Position& targetPos = target->getPosition();
  653.         uint32_t distance = std::max<uint32_t>(Position::getDistanceX(playerPos, targetPos), Position::getDistanceY(playerPos, targetPos));
  654.  
  655.         uint32_t maxHitChance;
  656.         if (it.maxHitChance != -1) {
  657.             maxHitChance = it.maxHitChance;
  658.         } else if (it.ammoType != AMMO_NONE) {
  659.             //hit chance on two-handed weapons is limited to 90%
  660.             maxHitChance = 90;
  661.         } else {
  662.             //one-handed is set to 75%
  663.             maxHitChance = 75;
  664.         }
  665.  
  666.         if (maxHitChance == 75) {
  667.             //chance for one-handed weapons
  668.             switch (distance) {
  669.                 case 1:
  670.                 case 5:
  671.                     chance = std::min<uint32_t>(skill, 74) + 1;
  672.                     break;
  673.                 case 2:
  674.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 28) * 2.40f) + 8;
  675.                     break;
  676.                 case 3:
  677.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 45) * 1.55f) + 6;
  678.                     break;
  679.                 case 4:
  680.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 58) * 1.25f) + 3;
  681.                     break;
  682.                 case 6:
  683.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 90) * 0.80f) + 3;
  684.                     break;
  685.                 case 7:
  686.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 104) * 0.70f) + 2;
  687.                     break;
  688.                 default:
  689.                     chance = it.hitChance;
  690.                     break;
  691.             }
  692.         } else if (maxHitChance == 90) {
  693.             //formula for two-handed weapons
  694.             switch (distance) {
  695.                 case 1:
  696.                 case 5:
  697.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 74) * 1.20f) + 1;
  698.                     break;
  699.                 case 2:
  700.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 28) * 3.20f);
  701.                     break;
  702.                 case 3:
  703.                     chance = std::min<uint32_t>(skill, 45) * 2;
  704.                     break;
  705.                 case 4:
  706.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 58) * 1.55f);
  707.                     break;
  708.                 case 6:
  709.                 case 7:
  710.                     chance = std::min<uint32_t>(skill, 90);
  711.                     break;
  712.                 default:
  713.                     chance = it.hitChance;
  714.                     break;
  715.             }
  716.         } else if (maxHitChance == 100) {
  717.             switch (distance) {
  718.                 case 1:
  719.                 case 5:
  720.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 73) * 1.35f) + 1;
  721.                     break;
  722.                 case 2:
  723.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 30) * 3.20f) + 4;
  724.                     break;
  725.                 case 3:
  726.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 48) * 2.05f) + 2;
  727.                     break;
  728.                 case 4:
  729.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 65) * 1.50f) + 2;
  730.                     break;
  731.                 case 6:
  732.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 87) * 1.20f) - 4;
  733.                     break;
  734.                 case 7:
  735.                     chance = static_cast<uint32_t>(std::min<uint32_t>(skill, 90) * 1.10f) + 1;
  736.                     break;
  737.                 default:
  738.                     chance = it.hitChance;
  739.                     break;
  740.             }
  741.         } else {
  742.             chance = maxHitChance;
  743.         }
  744.     } else {
  745.         chance = it.hitChance;
  746.     }
  747.  
  748.     if (item->getWeaponType() == WEAPON_AMMO) {
  749.         Item* bow = player->getWeapon(true);
  750.         if (bow && bow->getHitChance() != 0) {
  751.             chance += bow->getHitChance();
  752.         }
  753.     }
  754.  
  755.     if (chance >= uniform_random(1, 100)) {
  756.         Weapon::internalUseWeapon(player, item, target, damageModifier);
  757.     } else {
  758.         //miss target
  759.         Tile* destTile = target->getTile();
  760.  
  761.         if (!Position::areInRange<1, 1, 0>(player->getPosition(), target->getPosition())) {
  762.             static std::vector<std::pair<int32_t, int32_t>> destList {
  763.                 {-1, -1}, {0, -1}, {1, -1},
  764.                 {-1,  0}, {0,  0}, {1,  0},
  765.                 {-1,  1}, {0,  1}, {1,  1}
  766.             };
  767.             std::shuffle(destList.begin(), destList.end(), getRandomGenerator());
  768.  
  769.             Position destPos = target->getPosition();
  770.  
  771.             for (const auto& dir : destList) {
  772.                 // Blocking tiles or tiles without ground ain't valid targets for spears
  773.                 Tile* tmpTile = g_game.map.getTile(destPos.x + dir.first, destPos.y + dir.second, destPos.z);
  774.                 if (tmpTile && !tmpTile->hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID) && tmpTile->getGround() != nullptr) {
  775.                     destTile = tmpTile;
  776.                     break;
  777.                 }
  778.             }
  779.         }
  780.  
  781.         Weapon::internalUseWeapon(player, item, destTile);
  782.     }
  783.     return true;
  784. }
  785.  
  786. int32_t WeaponDistance::getElementDamage(const Player* player, const Creature* target, const Item* item) const
  787. {
  788.     if (elementType == COMBAT_NONE) {
  789.         return 0;
  790.     }
  791.  
  792.     int32_t attackValue = elementDamage;
  793.     if (item->getWeaponType() == WEAPON_AMMO) {
  794.         Item* weapon = player->getWeapon(true);
  795.         if (weapon) {
  796.             attackValue += weapon->getAttack();
  797.         }
  798.     }
  799.  
  800.     int32_t attackSkill = player->getSkillLevel(SKILL_DISTANCE);
  801.     float attackFactor = player->getAttackFactor();
  802.  
  803.     int32_t minValue = 0;
  804.     int32_t maxValue = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor);
  805.     if (target) {
  806.         if (target->getPlayer()) {
  807.             minValue = static_cast<int32_t>(std::ceil(player->getLevel() * 0.1));
  808.         } else {
  809.             minValue = static_cast<int32_t>(std::ceil(player->getLevel() * 0.2));
  810.         }
  811.     }
  812.  
  813.     return -normal_random(minValue, static_cast<int32_t>(maxValue * player->getVocation()->distDamageMultiplier));
  814. }
  815.  
  816. int32_t WeaponDistance::getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage /*= false*/) const
  817. {
  818.     int32_t attackValue = item->getAttack();
  819.  
  820.     if (item->getWeaponType() == WEAPON_AMMO) {
  821.         Item* weapon = player->getWeapon(true);
  822.         if (weapon) {
  823.             attackValue += weapon->getAttack();
  824.         }
  825.     }
  826.  
  827.     int32_t attackSkill = player->getSkillLevel(SKILL_DISTANCE);
  828.     float attackFactor = player->getAttackFactor();
  829.  
  830.     int32_t maxValue = static_cast<int32_t>(Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor) * player->getVocation()->distDamageMultiplier);
  831.     if (maxDamage) {
  832.         return -maxValue;
  833.     }
  834.  
  835.     int32_t minValue;
  836.     if (target) {
  837.         if (target->getPlayer()) {
  838.             minValue = static_cast<int32_t>(std::ceil(player->getLevel() * 0.1));
  839.         } else {
  840.             minValue = static_cast<int32_t>(std::ceil(player->getLevel() * 0.2));
  841.         }
  842.     } else {
  843.         minValue = 0;
  844.     }
  845.     return -normal_random(minValue, maxValue);
  846. }
  847.  
  848. bool WeaponDistance::getSkillType(const Player* player, const Item*, skills_t& skill, uint32_t& skillpoint) const
  849. {
  850.     skill = SKILL_DISTANCE;
  851.  
  852.     if (player->getAddAttackSkill()) {
  853.         switch (player->getLastAttackBlockType()) {
  854.             case BLOCK_NONE: {
  855.                 skillpoint = 2;
  856.                 break;
  857.             }
  858.  
  859.             case BLOCK_DEFENSE:
  860.             case BLOCK_ARMOR: {
  861.                 skillpoint = 1;
  862.                 break;
  863.             }
  864.  
  865.             default:
  866.                 skillpoint = 0;
  867.                 break;
  868.         }
  869.     } else {
  870.         skillpoint = 0;
  871.     }
  872.     return true;
  873. }
  874.  
  875. bool WeaponWand::configureEvent(const pugi::xml_node& node)
  876. {
  877.     if (!Weapon::configureEvent(node)) {
  878.         return false;
  879.     }
  880.  
  881.     pugi::xml_attribute attr;
  882.     if ((attr = node.attribute("min"))) {
  883.         minChange = pugi::cast<int32_t>(attr.value());
  884.     }
  885.  
  886.     if ((attr = node.attribute("max"))) {
  887.         maxChange = pugi::cast<int32_t>(attr.value());
  888.     }
  889.  
  890.     attr = node.attribute("type");
  891.     if (!attr) {
  892.         return true;
  893.     }
  894.  
  895.     std::string tmpStrValue = asLowerCaseString(attr.as_string());
  896.     if (tmpStrValue == "earth") {
  897.         params.combatType = COMBAT_EARTHDAMAGE;
  898.     } else if (tmpStrValue == "ice") {
  899.         params.combatType = COMBAT_ICEDAMAGE;
  900.     } else if (tmpStrValue == "energy") {
  901.         params.combatType = COMBAT_ENERGYDAMAGE;
  902.     } else if (tmpStrValue == "fire") {
  903.         params.combatType = COMBAT_FIREDAMAGE;
  904.     } else if (tmpStrValue == "death") {
  905.         params.combatType = COMBAT_DEATHDAMAGE;
  906.     } else if (tmpStrValue == "holy") {
  907.         params.combatType = COMBAT_HOLYDAMAGE;
  908.     } else {
  909.         std::cout << "[Warning - WeaponWand::configureEvent] Type \"" << attr.as_string() << "\" does not exist." << std::endl;
  910.     }
  911.     return true;
  912. }
  913.  
  914. void WeaponWand::configureWeapon(const ItemType& it)
  915. {
  916.     params.distanceEffect = it.shootType;
  917.  
  918.     Weapon::configureWeapon(it);
  919. }
  920.  
  921. int32_t WeaponWand::getWeaponDamage(const Player*, const Creature*, const Item*, bool maxDamage /*= false*/) const
  922. {
  923.     if (maxDamage) {
  924.         return -maxChange;
  925.     }
  926.     return -normal_random(minChange, maxChange);
  927. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement