SHARE
TWEET

Untitled

a guest Sep 21st, 2018 119 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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 <bitset>
  23.  
  24. #include "bed.h"
  25. #include "chat.h"
  26. #include "combat.h"
  27. #include "configmanager.h"
  28. #include "creatureevent.h"
  29. #include "events.h"
  30. #include "game.h"
  31. #include "iologindata.h"
  32. #include "monster.h"
  33. #include "movement.h"
  34. #include "scheduler.h"
  35. #include "weapons.h"
  36.  
  37. extern ConfigManager g_config;
  38. extern Game g_game;
  39. extern Chat* g_chat;
  40. extern Vocations g_vocations;
  41. extern MoveEvents* g_moveEvents;
  42. extern Weapons* g_weapons;
  43. extern CreatureEvents* g_creatureEvents;
  44. extern Events* g_events;
  45.  
  46. MuteCountMap Player::muteCountMap;
  47.  
  48. uint32_t Player::playerAutoID = 0x10000000;
  49.  
  50. Player::Player(ProtocolGame_ptr p) :
  51.     Creature(), lastPing(OTSYS_TIME()), lastPong(lastPing), inbox(new Inbox(ITEM_INBOX)), client(std::move(p))
  52. {
  53.     lastAttackHand = HAND_LEFT;
  54.     inbox->incrementReferenceCounter();
  55. }
  56.  
  57. Player::~Player()
  58. {
  59.     for (Item* item : inventory) {
  60.         if (item) {
  61.             item->setParent(nullptr);
  62.             item->decrementReferenceCounter();
  63.         }
  64.     }
  65.  
  66.     for (const auto& it : depotLockerMap) {
  67.         it.second->removeInbox(inbox);
  68.         it.second->decrementReferenceCounter();
  69.     }
  70.  
  71.     inbox->decrementReferenceCounter();
  72.  
  73.     setWriteItem(nullptr);
  74.     setEditHouse(nullptr);
  75. }
  76.  
  77. bool Player::setVocation(uint16_t vocId)
  78. {
  79.     Vocation* voc = g_vocations.getVocation(vocId);
  80.     if (!voc) {
  81.         return false;
  82.     }
  83.     vocation = voc;
  84.  
  85.     Condition* condition = getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT);
  86.     if (condition) {
  87.         condition->setParam(CONDITION_PARAM_HEALTHGAIN, vocation->getHealthGainAmount());
  88.         condition->setParam(CONDITION_PARAM_HEALTHTICKS, vocation->getHealthGainTicks() * 1000);
  89.         condition->setParam(CONDITION_PARAM_MANAGAIN, vocation->getManaGainAmount());
  90.         condition->setParam(CONDITION_PARAM_MANATICKS, vocation->getManaGainTicks() * 1000);
  91.     }
  92.     return true;
  93. }
  94.  
  95. bool Player::isPushable() const
  96. {
  97.     if (hasFlag(PlayerFlag_CannotBePushed)) {
  98.         return false;
  99.     }
  100.     return Creature::isPushable();
  101. }
  102.  
  103. std::string Player::getDescription(int32_t lookDistance) const
  104. {
  105.     std::ostringstream s;
  106.  
  107.     if (lookDistance == -1) {
  108.         s << "yourself.";
  109.     } else {
  110.         s << name;
  111.         if (!group->access) {
  112.             s << " (Level " << level << ')';
  113.         }
  114.         s << '.';
  115.  
  116.         if (sex == PLAYERSEX_FEMALE) {
  117.             //s << " She";
  118.         } else {
  119.             //s << " He";
  120.         }
  121.     }
  122.  
  123.     if (party) {
  124.         if (lookDistance == -1) {
  125.             s << " Your party has ";
  126.         } else if (sex == PLAYERSEX_FEMALE) {
  127.             s << " She is in a party with ";
  128.         } else {
  129.             s << " He is in a party with ";
  130.         }
  131.  
  132.         size_t memberCount = party->getMemberCount() + 1;
  133.         if (memberCount == 1) {
  134.             s << "1 member and ";
  135.         } else {
  136.             s << memberCount << " members and ";
  137.         }
  138.  
  139.         size_t invitationCount = party->getInvitationCount();
  140.         if (invitationCount == 1) {
  141.             s << "1 pending invitation.";
  142.         } else {
  143.             s << invitationCount << " pending invitations.";
  144.         }
  145.     }
  146.  
  147.     if (!guild || !guildRank) {
  148.         return s.str();
  149.     }
  150.  
  151.     if (lookDistance == -1) {
  152.         s << " You are ";
  153.     } else if (sex == PLAYERSEX_FEMALE) {
  154.         s << " She is ";
  155.     } else {
  156.         s << " He is ";
  157.     }
  158.  
  159.     s << guildRank->name << " of the " << guild->getName();
  160.     if (!guildNick.empty()) {
  161.         s << " (" << guildNick << ')';
  162.     }
  163.  
  164.     size_t memberCount = guild->getMemberCount();
  165.     if (memberCount == 1) {
  166.         s << ", which has 1 member, " << guild->getMembersOnline().size() << " of them online.";
  167.     } else {
  168.         s << ", which has " << memberCount << " members, " << guild->getMembersOnline().size() << " of them online.";
  169.     }
  170.     return s.str();
  171. }
  172.  
  173. Item* Player::getInventoryItem(slots_t slot) const
  174. {
  175.     if (slot < CONST_SLOT_FIRST || slot > CONST_SLOT_LAST) {
  176.         return nullptr;
  177.     }
  178.     return inventory[slot];
  179. }
  180.  
  181. void Player::addConditionSuppressions(uint32_t conditions)
  182. {
  183.     conditionSuppressions |= conditions;
  184. }
  185.  
  186. void Player::removeConditionSuppressions(uint32_t conditions)
  187. {
  188.     conditionSuppressions &= ~conditions;
  189. }
  190.  
  191. //CHANGED! QUIVER SYSTEM
  192. Item* Player::getWeapon(slots_t slot, bool ignoreAmmo) const
  193. {
  194.     Item* item = inventory[slot];
  195.     if (!item) {
  196.         return nullptr;
  197.     }
  198.  
  199.     WeaponType_t weaponType = item->getWeaponType();
  200.     if (weaponType == WEAPON_NONE || weaponType == WEAPON_SHIELD || weaponType == WEAPON_AMMO) {
  201.         return nullptr;
  202.     }
  203.  
  204.     if (!ignoreAmmo && weaponType == WEAPON_DISTANCE) {
  205.         const ItemType& it = Item::items[item->getID()];
  206.         if (it.ammoType != AMMO_NONE) {
  207.             Item* ammoItem = inventory[CONST_SLOT_AMMO];
  208.  
  209.             // NEW!
  210.             if (!ammoItem) {
  211.                 return nullptr;
  212.             }
  213.  
  214.             if (Container* container = ammoItem->getContainer()) {
  215.                 for (ContainerIterator iter = container->iterator(); iter.hasNext(); iter.advance()) {
  216.                     const ItemType& itr = Item::items[(*iter)->getID()];
  217.                     if (itr.ammoType == it.ammoType) {
  218.                         item = (*iter);
  219.                         return item;
  220.                     }
  221.                 }
  222.             }
  223.             // end new
  224.  
  225.             if (!ammoItem || ammoItem->getAmmoType() != it.ammoType) {
  226.                 return nullptr;
  227.             }
  228.             item = ammoItem;
  229.         }
  230.     }
  231.     return item;
  232. }
  233.  
  234. //CHANGED! DUAL WIELD SYSTEM
  235. Item* Player::getWeapon(bool ignoreAmmo/* = false*/) const
  236. {
  237.     // NEW! if player is dual wielding, we already assured he has weapons in both hands
  238.     if (isDualWielding()) {
  239.         Item* item = getWeapon(getAttackHand(), ignoreAmmo);
  240.         if (item) {
  241.             return item; //NEW!
  242.         }
  243.     }
  244.  
  245.     Item* item = getWeapon(CONST_SLOT_LEFT, ignoreAmmo);
  246.     if (item) {
  247.         return item;
  248.     }
  249.  
  250.     item = getWeapon(CONST_SLOT_RIGHT, ignoreAmmo);
  251.     if (item) {
  252.         return item;
  253.     }
  254.     return nullptr;
  255. }
  256.  
  257. WeaponType_t Player::getWeaponType() const
  258. {
  259.     Item* item = getWeapon();
  260.     if (!item) {
  261.         return WEAPON_NONE;
  262.     }
  263.     return item->getWeaponType();
  264. }
  265.  
  266. //CHANGED! SKILL POINTS SYSTEM
  267. int32_t Player::getWeaponSkill(const Item* item) const
  268. {
  269.     if (!item) { //STRENGHT
  270.         return getSkillLevel(SKILL_STRENGHT); //* g_config.getNumber(ConfigManager::MELEE_STRENGHTFACTOR) / 100 + getSkillLevel(SKILL_DEXTERITY) * g_config.getNumber(ConfigManager::MELEE_DEXTERITYFACTOR) / 100;
  271.     }
  272.  
  273.     int32_t attackSkill;
  274.  
  275.     WeaponType_t weaponType = item->getWeaponType();
  276.     switch (weaponType) {
  277.         case WEAPON_SWORD: { //STRENGHT
  278.             attackSkill = getSkillLevel(SKILL_STRENGHT); //* g_config.getNumber(ConfigManager::MELEE_STRENGHTFACTOR) / 100 + getSkillLevel(SKILL_DEXTERITY) * g_config.getNumber(ConfigManager::MELEE_DEXTERITYFACTOR) / 100;
  279.             break;
  280.         }
  281.  
  282.         case WEAPON_CLUB: { //STRENGHT
  283.             attackSkill = getSkillLevel(SKILL_STRENGHT); //* g_config.getNumber(ConfigManager::MELEE_STRENGHTFACTOR) / 100 + getSkillLevel(SKILL_DEXTERITY) * g_config.getNumber(ConfigManager::MELEE_DEXTERITYFACTOR) / 100;
  284.             break;
  285.         }
  286.  
  287.         case WEAPON_AXE: { //STRENGHT
  288.             attackSkill = getSkillLevel(SKILL_STRENGHT); //* g_config.getNumber(ConfigManager::MELEE_STRENGHTFACTOR) / 100 + getSkillLevel(SKILL_DEXTERITY) * g_config.getNumber(ConfigManager::MELEE_DEXTERITYFACTOR) / 100;
  289.             break;
  290.         }
  291.  
  292.         case WEAPON_DISTANCE: { //DEXTERITY
  293.             attackSkill = getSkillLevel(SKILL_DEXTERITY);
  294.             break;
  295.         }
  296.  
  297.         default: {
  298.             attackSkill = 0;
  299.             break;
  300.         }
  301.     }
  302.     return attackSkill;
  303. }
  304.  
  305. int32_t Player::getArmor() const
  306. {
  307.     int32_t armor = 0;
  308.  
  309.     static const slots_t armorSlots[] = {CONST_SLOT_HEAD, CONST_SLOT_NECKLACE, CONST_SLOT_ARMOR, CONST_SLOT_LEGS, CONST_SLOT_FEET, CONST_SLOT_RING};
  310.     for (slots_t slot : armorSlots) {
  311.         Item* inventoryItem = inventory[slot];
  312.         if (inventoryItem) {
  313.             armor += inventoryItem->getArmor();
  314.         }
  315.     }
  316.     return static_cast<int32_t>(armor * vocation->armorMultiplier);
  317. }
  318.  
  319. //CHANGED! DUAL WIELD SYSTEM
  320. void Player::getShieldAndWeapon(const Item*& shield, const Item*& weapon) const
  321. {
  322.     shield = nullptr;
  323.     weapon = nullptr;
  324.  
  325. /*  for (uint32_t slot = CONST_SLOT_RIGHT; slot <= CONST_SLOT_LEFT; slot++) {
  326.         Item* item = inventory[slot];
  327.         if (!item) {
  328.             continue;
  329.         }*/
  330.  
  331.     if (isDualWielding()) {
  332.         if (lastAttackHand == HAND_LEFT) {
  333.             shield = inventory[CONST_SLOT_RIGHT];
  334.             weapon = inventory[CONST_SLOT_LEFT];
  335.         }
  336.         else {
  337.             shield = inventory[CONST_SLOT_LEFT];
  338.             weapon = inventory[CONST_SLOT_RIGHT];
  339.         }
  340.     }
  341.     else {
  342.         for (uint32_t slot = CONST_SLOT_RIGHT; slot <= CONST_SLOT_LEFT; slot++) {
  343.             Item * item = inventory[slot];
  344.             if (!item) {
  345.                 continue;
  346.             }
  347.  
  348.             switch (item->getWeaponType()) {
  349.                 case WEAPON_NONE:
  350.                     break;
  351.  
  352.                 case WEAPON_SHIELD: {
  353.                     if (!shield || item->getDefense() > shield->getDefense()) {
  354.                         shield = item;
  355.                     }
  356.                     break;
  357.                 }
  358.  
  359.                 default: { // weapons that are not shields
  360.                     weapon = item;
  361.                     break;
  362.                 }
  363.             }
  364.         }
  365.     }
  366. }
  367.  
  368. //NEW! DUAL WIELD SYSTEM
  369. bool Player::isDualWielding() const
  370. {
  371.     /* Not checking dual wield because the player can't wear two weapons worn without it */
  372.     if (this->getWeapon(CONST_SLOT_LEFT, true) && this->getWeapon(CONST_SLOT_RIGHT, true)) {
  373.         return true;
  374.     }
  375.     return false;
  376. }
  377.  
  378. //uint32_t Player::getAttackSpeed(Player* player) const
  379. /*{
  380.     if (player->isDualWielding()) {
  381.         return 1000;
  382.     }
  383.     else {
  384.         return 2000 + 8 * 10 - player->skills[SKILL_DEXTERITY].level * 5;
  385.     }
  386. }*/
  387.  
  388. int32_t Player::getDefense() const
  389. {
  390.     int32_t defenseSkill = (getSkillLevel(SKILL_RESISTANCE) * g_config.getNumber(ConfigManager::SHIELD_RESISTANCEFACTOR) / 100) + (getSkillLevel(SKILL_DEXTERITY) * g_config.getNumber(ConfigManager::SHIELD_DEXTERITYFACTOR) / 100);
  391.     int32_t defenseValue = 7;
  392.     const Item* weapon;
  393.     const Item* shield;
  394.     getShieldAndWeapon(shield, weapon);
  395.  
  396.     if (weapon) {
  397.         defenseValue = weapon->getDefense() + weapon->getExtraDefense();
  398.         defenseSkill = getWeaponSkill(weapon);
  399.     }
  400.  
  401.     if (shield) {
  402.         defenseValue = weapon != nullptr ? shield->getDefense() + weapon->getExtraDefense() : shield->getDefense();
  403.         defenseSkill = (getSkillLevel(SKILL_RESISTANCE) * g_config.getNumber(ConfigManager::SHIELD_RESISTANCEFACTOR) / 100) + (getSkillLevel(SKILL_DEXTERITY) * g_config.getNumber(ConfigManager::SHIELD_DEXTERITYFACTOR) / 100);
  404.     }
  405.  
  406.     if (defenseSkill == 0) {
  407.         switch (fightMode) {
  408.             case FIGHTMODE_ATTACK:
  409.             case FIGHTMODE_BALANCED:
  410.                 return 1;
  411.  
  412.             case FIGHTMODE_DEFENSE:
  413.                 return 2;
  414.         }
  415.     }
  416.  
  417.     return (defenseSkill / 4. + 2.23) * defenseValue * 0.15 * getDefenseFactor() * vocation->defenseMultiplier;
  418. }
  419.  
  420. float Player::getAttackFactor() const
  421. {
  422.     switch (fightMode) {
  423.         case FIGHTMODE_ATTACK: return 1.0f;
  424.         case FIGHTMODE_BALANCED: return 1.2f;
  425.         case FIGHTMODE_DEFENSE: return 2.0f;
  426.         default: return 1.0f;
  427.     }
  428. }
  429.  
  430. float Player::getDefenseFactor() const
  431. {
  432.     switch (fightMode) {
  433.         case FIGHTMODE_ATTACK: return (OTSYS_TIME() - lastAttack) < getAttackSpeed() ? 0.5f : 1.0f;
  434.         case FIGHTMODE_BALANCED: return (OTSYS_TIME() - lastAttack) < getAttackSpeed() ? 0.75f : 1.0f;
  435.         case FIGHTMODE_DEFENSE: return 1.0f;
  436.         default: return 1.0f;
  437.     }
  438. }
  439.  
  440. uint16_t Player::getClientIcons() const
  441. {
  442.     uint16_t icons = 0;
  443.     for (Condition* condition : conditions) {
  444.         if (!isSuppress(condition->getType())) {
  445.             icons |= condition->getIcons();
  446.         }
  447.     }
  448.  
  449.     if (pzLocked) {
  450.         icons |= ICON_REDSWORDS;
  451.     }
  452.  
  453.     if (tile->hasFlag(TILESTATE_PROTECTIONZONE)) {
  454.         icons |= ICON_PIGEON;
  455.  
  456.         // Don't show ICON_SWORDS if player is in protection zone.
  457.         if (hasBitSet(ICON_SWORDS, icons)) {
  458.             icons &= ~ICON_SWORDS;
  459.         }
  460.     }
  461.  
  462.     // Game client debugs with 10 or more icons
  463.     // so let's prevent that from happening.
  464.     std::bitset<20> icon_bitset(static_cast<uint64_t>(icons));
  465.     for (size_t pos = 0, bits_set = icon_bitset.count(); bits_set >= 10; ++pos) {
  466.         if (icon_bitset[pos]) {
  467.             icon_bitset.reset(pos);
  468.             --bits_set;
  469.         }
  470.     }
  471.     return icon_bitset.to_ulong();
  472. }
  473.  
  474. void Player::updateInventoryWeight()
  475. {
  476.     if (hasFlag(PlayerFlag_HasInfiniteCapacity)) {
  477.         return;
  478.     }
  479.  
  480.     inventoryWeight = 0;
  481.     for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
  482.         const Item* item = inventory[i];
  483.         if (item) {
  484.             inventoryWeight += item->getWeight();
  485.         }
  486.     }
  487. }
  488.  
  489. //CHANGED! SKILL POINTS SYSTEM - STATS GAIN
  490. void Player::addSkillAdvance(skills_t skill, uint64_t count)
  491. {
  492.     uint64_t currReqTries = vocation->getReqSkillTries(skill, skills[skill].level);
  493.     uint64_t nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
  494.     if (currReqTries >= nextReqTries) {
  495.         //player has reached max skill
  496.         return;
  497.     }
  498.  
  499.     g_events->eventPlayerOnGainSkillTries(this, skill, count);
  500.     if (count == 0) {
  501.         return;
  502.     }
  503.  
  504.     bool sendUpdateSkills = false;
  505.     while ((skills[skill].tries + count) >= nextReqTries) {
  506.         count -= nextReqTries - skills[skill].tries;
  507.         skills[skill].level++;
  508.         skills[skill].tries = 0;
  509.         skills[skill].percent = 0;
  510.  
  511.         //std::ostringstream ss;
  512.         //ss << "You advanced to " << getSkillName(skill) << " level " << skills[skill].level << '.';
  513.         //sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  514.  
  515.         if (skill == SKILL_VITALITY) {
  516.             healthMax += g_config.getNumber(ConfigManager::VITALITY_HEALTHGAIN);
  517.             health += g_config.getNumber(ConfigManager::VITALITY_HEALTHGAIN);
  518.             g_game.addCreatureHealth(this);
  519.         }
  520.  
  521.         if (skill == SKILL_STRENGHT) {
  522.             healthMax += g_config.getNumber(ConfigManager::STRENGHT_HEALTHGAIN);
  523.             health += g_config.getNumber(ConfigManager::STRENGHT_HEALTHGAIN);
  524.             capacity += g_config.getNumber(ConfigManager::STRENGHT_CAPGAIN);
  525.             g_game.addCreatureHealth(this);
  526.         }
  527.  
  528.         if (skill == SKILL_RESISTANCE) {
  529.             capacity += g_config.getNumber(ConfigManager::RESISTANCE_CAPGAIN);
  530.             healthMax += g_config.getNumber(ConfigManager::RESISTANCE_HEALTHGAIN);
  531.             health += g_config.getNumber(ConfigManager::RESISTANCE_HEALTHGAIN);
  532.             g_game.addCreatureHealth(this);
  533.         }
  534.  
  535.         if (skill == SKILL_DEXTERITY) {
  536.             updateBaseSpeed();
  537.             setBaseSpeed(getBaseSpeed());
  538.             g_game.changeSpeed(this, 0);
  539.         }
  540.  
  541.         if (skill == SKILL_INTELLIGENCE) {
  542.             manaMax += g_config.getNumber(ConfigManager::INTELLIGENCE_MANAGAIN);
  543.             mana += g_config.getNumber(ConfigManager::INTELLIGENCE_MANAGAIN);
  544.         }
  545.  
  546.         if (skill == SKILL_FAITH) {
  547.             manaMax += g_config.getNumber(ConfigManager::FAITH_MANAGAIN);
  548.             mana += g_config.getNumber(ConfigManager::INTELLIGENCE_MANAGAIN);
  549.         }
  550.  
  551.         if (skill == SKILL_ENDURANCE) {
  552.             healthMax += g_config.getNumber(ConfigManager::ENDURANCE_HEALTHGAIN);
  553.             health += g_config.getNumber(ConfigManager::ENDURANCE_HEALTHGAIN);
  554.             capacity += g_config.getNumber(ConfigManager::ENDURANCE_CAPGAIN);
  555.             g_game.addCreatureHealth(this);
  556.         }
  557.  
  558.         sendStats();
  559.         sendSkills();
  560.         g_creatureEvents->playerAdvance(this, skill, (skills[skill].level - 1), skills[skill].level);
  561.  
  562.         sendUpdateSkills = true;
  563.         currReqTries = nextReqTries;
  564.         nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
  565.         if (currReqTries >= nextReqTries) {
  566.             count = 0;
  567.             break;
  568.         }
  569.     }
  570.  
  571.     skills[skill].tries += count;
  572.  
  573.     uint32_t newPercent;
  574.     if (nextReqTries > currReqTries) {
  575.         newPercent = Player::getPercentLevel(skills[skill].tries, nextReqTries);
  576.     } else {
  577.         newPercent = 0;
  578.     }
  579.  
  580.     if (skills[skill].percent != newPercent) {
  581.         skills[skill].percent = newPercent;
  582.         sendUpdateSkills = true;
  583.     }
  584.  
  585.     if (sendUpdateSkills) {
  586.         sendSkills();
  587.     }
  588. }
  589.  
  590. void Player::setVarStats(stats_t stat, int32_t modifier)
  591. {
  592.     varStats[stat] += modifier;
  593.  
  594.     switch (stat) {
  595.         case STAT_MAXHITPOINTS: {
  596.             if (getHealth() > getMaxHealth()) {
  597.                 Creature::changeHealth(getMaxHealth() - getHealth());
  598.             } else {
  599.                 g_game.addCreatureHealth(this);
  600.             }
  601.             break;
  602.         }
  603.  
  604.         case STAT_MAXMANAPOINTS: {
  605.             if (getMana() > getMaxMana()) {
  606.                 changeMana(getMaxMana() - getMana());
  607.             }
  608.             break;
  609.         }
  610.  
  611.         default: {
  612.             break;
  613.         }
  614.     }
  615. }
  616.  
  617. int32_t Player::getDefaultStats(stats_t stat) const
  618. {
  619.     switch (stat) {
  620.         case STAT_MAXHITPOINTS: return healthMax;
  621.         case STAT_MAXMANAPOINTS: return manaMax;
  622.         case STAT_MAGICPOINTS: return getBaseMagicLevel();
  623.         default: return 0;
  624.     }
  625. }
  626.  
  627. void Player::addContainer(uint8_t cid, Container* container)
  628. {
  629.     if (cid > 0xF) {
  630.         return;
  631.     }
  632.  
  633.     if (container->getID() == ITEM_BROWSEFIELD) {
  634.         container->incrementReferenceCounter();
  635.     }
  636.  
  637.     auto it = openContainers.find(cid);
  638.     if (it != openContainers.end()) {
  639.         OpenContainer& openContainer = it->second;
  640.         Container* oldContainer = openContainer.container;
  641.         if (oldContainer->getID() == ITEM_BROWSEFIELD) {
  642.             oldContainer->decrementReferenceCounter();
  643.         }
  644.  
  645.         openContainer.container = container;
  646.         openContainer.index = 0;
  647.     } else {
  648.         OpenContainer openContainer;
  649.         openContainer.container = container;
  650.         openContainer.index = 0;
  651.         openContainers[cid] = openContainer;
  652.     }
  653. }
  654.  
  655. void Player::closeContainer(uint8_t cid)
  656. {
  657.     auto it = openContainers.find(cid);
  658.     if (it == openContainers.end()) {
  659.         return;
  660.     }
  661.  
  662.     OpenContainer openContainer = it->second;
  663.     Container* container = openContainer.container;
  664.     openContainers.erase(it);
  665.  
  666.     if (container && container->getID() == ITEM_BROWSEFIELD) {
  667.         container->decrementReferenceCounter();
  668.     }
  669. }
  670.  
  671. void Player::setContainerIndex(uint8_t cid, uint16_t index)
  672. {
  673.     auto it = openContainers.find(cid);
  674.     if (it == openContainers.end()) {
  675.         return;
  676.     }
  677.     it->second.index = index;
  678. }
  679.  
  680. Container* Player::getContainerByID(uint8_t cid)
  681. {
  682.     auto it = openContainers.find(cid);
  683.     if (it == openContainers.end()) {
  684.         return nullptr;
  685.     }
  686.     return it->second.container;
  687. }
  688.  
  689. int8_t Player::getContainerID(const Container* container) const
  690. {
  691.     for (const auto& it : openContainers) {
  692.         if (it.second.container == container) {
  693.             return it.first;
  694.         }
  695.     }
  696.     return -1;
  697. }
  698.  
  699. uint16_t Player::getContainerIndex(uint8_t cid) const
  700. {
  701.     auto it = openContainers.find(cid);
  702.     if (it == openContainers.end()) {
  703.         return 0;
  704.     }
  705.     return it->second.index;
  706. }
  707.  
  708. bool Player::canOpenCorpse(uint32_t ownerId) const
  709. {
  710.     return getID() == ownerId || (party && party->canOpenCorpse(ownerId));
  711. }
  712.  
  713. uint16_t Player::getLookCorpse() const
  714. {
  715.     if (sex == PLAYERSEX_FEMALE) {
  716.         return ITEM_FEMALE_CORPSE;
  717.     } else {
  718.         return ITEM_MALE_CORPSE;
  719.     }
  720. }
  721.  
  722. void Player::addStorageValue(const uint32_t key, const int32_t value, const bool isLogin/* = false*/)
  723. {
  724.     if (IS_IN_KEYRANGE(key, RESERVED_RANGE)) {
  725.         if (IS_IN_KEYRANGE(key, OUTFITS_RANGE)) {
  726.             outfits.emplace_back(
  727.                 value >> 16,
  728.                 value & 0xFF
  729.             );
  730.             return;
  731.         } else if (IS_IN_KEYRANGE(key, MOUNTS_RANGE)) {
  732.             // do nothing
  733.         } else {
  734.             std::cout << "Warning: unknown reserved key: " << key << " player: " << getName() << std::endl;
  735.             return;
  736.         }
  737.     }
  738.  
  739.     if (value != -1) {
  740.         int32_t oldValue;
  741.         getStorageValue(key, oldValue);
  742.  
  743.         storageMap[key] = value;
  744.  
  745.         if (!isLogin) {
  746.             auto currentFrameTime = g_dispatcher.getDispatcherCycle();
  747.             if (lastQuestlogUpdate != currentFrameTime && g_game.quests.isQuestStorage(key, value, oldValue)) {
  748.                 lastQuestlogUpdate = currentFrameTime;
  749.                 sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your questlog has been updated.");
  750.             }
  751.         }
  752.     } else {
  753.         storageMap.erase(key);
  754.     }
  755. }
  756.  
  757. bool Player::getStorageValue(const uint32_t key, int32_t& value) const
  758. {
  759.     auto it = storageMap.find(key);
  760.     if (it == storageMap.end()) {
  761.         value = -1;
  762.         return false;
  763.     }
  764.  
  765.     value = it->second;
  766.     return true;
  767. }
  768.  
  769. bool Player::canSee(const Position& pos) const
  770. {
  771.     if (!client) {
  772.         return false;
  773.     }
  774.     return client->canSee(pos);
  775. }
  776.  
  777. bool Player::canSeeCreature(const Creature* creature) const
  778. {
  779.     if (creature == this) {
  780.         return true;
  781.     }
  782.  
  783.     if (creature->isInGhostMode() && !group->access) {
  784.         return false;
  785.     }
  786.  
  787.     if (!creature->getPlayer() && !canSeeInvisibility() && creature->isInvisible()) {
  788.         return false;
  789.     }
  790.     return true;
  791. }
  792.  
  793. bool Player::canWalkthrough(const Creature* creature) const
  794. {
  795.     if (group->access || creature->isInGhostMode()) {
  796.         return true;
  797.     }
  798.  
  799.     const Player* player = creature->getPlayer();
  800.     if (!player) {
  801.         return false;
  802.     }
  803.  
  804.     const Tile* playerTile = player->getTile();
  805.     if (!playerTile || (!playerTile->hasFlag(TILESTATE_PROTECTIONZONE) && player->getLevel() > static_cast<uint32_t>(g_config.getNumber(ConfigManager::PROTECTION_LEVEL)))) {
  806.         return false;
  807.     }
  808.  
  809.     const Item* playerTileGround = playerTile->getGround();
  810.     if (!playerTileGround || !playerTileGround->hasWalkStack()) {
  811.         return false;
  812.     }
  813.  
  814.     Player* thisPlayer = const_cast<Player*>(this);
  815.     if ((OTSYS_TIME() - lastWalkthroughAttempt) > 2000) {
  816.         thisPlayer->setLastWalkthroughAttempt(OTSYS_TIME());
  817.         return false;
  818.     }
  819.  
  820.     if (creature->getPosition() != lastWalkthroughPosition) {
  821.         thisPlayer->setLastWalkthroughPosition(creature->getPosition());
  822.         return false;
  823.     }
  824.  
  825.     thisPlayer->setLastWalkthroughPosition(creature->getPosition());
  826.     return true;
  827. }
  828.  
  829. bool Player::canWalkthroughEx(const Creature* creature) const
  830. {
  831.     if (group->access) {
  832.         return true;
  833.     }
  834.  
  835.     const Player* player = creature->getPlayer();
  836.     if (!player) {
  837.         return false;
  838.     }
  839.  
  840.     const Tile* playerTile = player->getTile();
  841.     return playerTile && (playerTile->hasFlag(TILESTATE_PROTECTIONZONE) || player->getLevel() <= static_cast<uint32_t>(g_config.getNumber(ConfigManager::PROTECTION_LEVEL)));
  842. }
  843.  
  844. void Player::onReceiveMail() const
  845. {
  846.     if (isNearDepotBox()) {
  847.         sendTextMessage(MESSAGE_EVENT_ADVANCE, "New mail has arrived.");
  848.     }
  849. }
  850.  
  851. bool Player::isNearDepotBox() const
  852. {
  853.     const Position& pos = getPosition();
  854.     for (int32_t cx = -1; cx <= 1; ++cx) {
  855.         for (int32_t cy = -1; cy <= 1; ++cy) {
  856.             Tile* tile = g_game.map.getTile(pos.x + cx, pos.y + cy, pos.z);
  857.             if (!tile) {
  858.                 continue;
  859.             }
  860.  
  861.             if (tile->hasFlag(TILESTATE_DEPOT)) {
  862.                 return true;
  863.             }
  864.         }
  865.     }
  866.     return false;
  867. }
  868.  
  869. DepotChest* Player::getDepotChest(uint32_t depotId, bool autoCreate)
  870. {
  871.     auto it = depotChests.find(depotId);
  872.     if (it != depotChests.end()) {
  873.         return it->second;
  874.     }
  875.  
  876.     if (!autoCreate) {
  877.         return nullptr;
  878.     }
  879.  
  880.     DepotChest* depotChest = new DepotChest(ITEM_DEPOT);
  881.     depotChest->incrementReferenceCounter();
  882.     depotChest->setMaxDepotItems(getMaxDepotItems());
  883.     depotChests[depotId] = depotChest;
  884.     return depotChest;
  885. }
  886.  
  887. DepotLocker* Player::getDepotLocker(uint32_t depotId)
  888. {
  889.     auto it = depotLockerMap.find(depotId);
  890.     if (it != depotLockerMap.end()) {
  891.         inbox->setParent(it->second);
  892.         return it->second;
  893.     }
  894.  
  895.     DepotLocker* depotLocker = new DepotLocker(ITEM_LOCKER1);
  896.     depotLocker->setDepotId(depotId);
  897.     depotLocker->internalAddThing(Item::CreateItem(ITEM_MARKET));
  898.     depotLocker->internalAddThing(inbox);
  899.     depotLocker->internalAddThing(getDepotChest(depotId, true));
  900.     depotLockerMap[depotId] = depotLocker;
  901.     return depotLocker;
  902. }
  903.  
  904. void Player::sendCancelMessage(ReturnValue message) const
  905. {
  906.     sendCancelMessage(getReturnMessage(message));
  907. }
  908.  
  909. void Player::sendStats()
  910. {
  911.     if (client) {
  912.         client->sendStats();
  913.         lastStatsTrainingTime = getOfflineTrainingTime() / 60 / 1000;
  914.     }
  915. }
  916.  
  917. void Player::sendPing()
  918. {
  919.     int64_t timeNow = OTSYS_TIME();
  920.  
  921.     bool hasLostConnection = false;
  922.     if ((timeNow - lastPing) >= 5000) {
  923.         lastPing = timeNow;
  924.         if (client) {
  925.             client->sendPing();
  926.         } else {
  927.             hasLostConnection = true;
  928.         }
  929.     }
  930.  
  931.     int64_t noPongTime = timeNow - lastPong;
  932.     if ((hasLostConnection || noPongTime >= 7000) && attackedCreature && attackedCreature->getPlayer()) {
  933.         setAttackedCreature(nullptr);
  934.     }
  935.  
  936.     if (noPongTime >= 60000 && canLogout()) {
  937.         if (g_creatureEvents->playerLogout(this)) {
  938.             if (client) {
  939.                 client->logout(true, true);
  940.             } else {
  941.                 g_game.removeCreature(this, true);
  942.             }
  943.         }
  944.     }
  945. }
  946.  
  947. Item* Player::getWriteItem(uint32_t& windowTextId, uint16_t& maxWriteLen)
  948. {
  949.     windowTextId = this->windowTextId;
  950.     maxWriteLen = this->maxWriteLen;
  951.     return writeItem;
  952. }
  953.  
  954. void Player::setWriteItem(Item* item, uint16_t maxWriteLen /*= 0*/)
  955. {
  956.     windowTextId++;
  957.  
  958.     if (writeItem) {
  959.         writeItem->decrementReferenceCounter();
  960.     }
  961.  
  962.     if (item) {
  963.         writeItem = item;
  964.         this->maxWriteLen = maxWriteLen;
  965.         writeItem->incrementReferenceCounter();
  966.     } else {
  967.         writeItem = nullptr;
  968.         this->maxWriteLen = 0;
  969.     }
  970. }
  971.  
  972. House* Player::getEditHouse(uint32_t& windowTextId, uint32_t& listId)
  973. {
  974.     windowTextId = this->windowTextId;
  975.     listId = this->editListId;
  976.     return editHouse;
  977. }
  978.  
  979. void Player::setEditHouse(House* house, uint32_t listId /*= 0*/)
  980. {
  981.     windowTextId++;
  982.     editHouse = house;
  983.     editListId = listId;
  984. }
  985.  
  986. void Player::sendHouseWindow(House* house, uint32_t listId) const
  987. {
  988.     if (!client) {
  989.         return;
  990.     }
  991.  
  992.     std::string text;
  993.     if (house->getAccessList(listId, text)) {
  994.         client->sendHouseWindow(windowTextId, text);
  995.     }
  996. }
  997.  
  998. //container
  999. void Player::sendAddContainerItem(const Container* container, const Item* item)
  1000. {
  1001.     if (!client) {
  1002.         return;
  1003.     }
  1004.  
  1005.     for (const auto& it : openContainers) {
  1006.         const OpenContainer& openContainer = it.second;
  1007.         if (openContainer.container != container) {
  1008.             continue;
  1009.         }
  1010.  
  1011.         uint16_t slot = openContainer.index;
  1012.         if (container->getID() == ITEM_BROWSEFIELD) {
  1013.             uint16_t containerSize = container->size() - 1;
  1014.             uint16_t pageEnd = openContainer.index + container->capacity() - 1;
  1015.             if (containerSize > pageEnd) {
  1016.                 slot = pageEnd;
  1017.                 item = container->getItemByIndex(pageEnd);
  1018.             } else {
  1019.                 slot = containerSize;
  1020.             }
  1021.         } else if (openContainer.index >= container->capacity()) {
  1022.             item = container->getItemByIndex(openContainer.index - 1);
  1023.         }
  1024.         client->sendAddContainerItem(it.first, slot, item);
  1025.     }
  1026. }
  1027.  
  1028. void Player::sendUpdateContainerItem(const Container* container, uint16_t slot, const Item* newItem)
  1029. {
  1030.     if (!client) {
  1031.         return;
  1032.     }
  1033.  
  1034.     for (const auto& it : openContainers) {
  1035.         const OpenContainer& openContainer = it.second;
  1036.         if (openContainer.container != container) {
  1037.             continue;
  1038.         }
  1039.  
  1040.         if (slot < openContainer.index) {
  1041.             continue;
  1042.         }
  1043.  
  1044.         uint16_t pageEnd = openContainer.index + container->capacity();
  1045.         if (slot >= pageEnd) {
  1046.             continue;
  1047.         }
  1048.  
  1049.         client->sendUpdateContainerItem(it.first, slot, newItem);
  1050.     }
  1051. }
  1052.  
  1053. void Player::sendRemoveContainerItem(const Container* container, uint16_t slot)
  1054. {
  1055.     if (!client) {
  1056.         return;
  1057.     }
  1058.  
  1059.     for (auto& it : openContainers) {
  1060.         OpenContainer& openContainer = it.second;
  1061.         if (openContainer.container != container) {
  1062.             continue;
  1063.         }
  1064.  
  1065.         uint16_t& firstIndex = openContainer.index;
  1066.         if (firstIndex > 0 && firstIndex >= container->size() - 1) {
  1067.             firstIndex -= container->capacity();
  1068.             sendContainer(it.first, container, false, firstIndex);
  1069.         }
  1070.  
  1071.         client->sendRemoveContainerItem(it.first, std::max<uint16_t>(slot, firstIndex), container->getItemByIndex(container->capacity() + firstIndex));
  1072.     }
  1073. }
  1074.  
  1075. void Player::onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem,
  1076.                               const ItemType& oldType, const Item* newItem, const ItemType& newType)
  1077. {
  1078.     Creature::onUpdateTileItem(tile, pos, oldItem, oldType, newItem, newType);
  1079.  
  1080.     if (oldItem != newItem) {
  1081.         onRemoveTileItem(tile, pos, oldType, oldItem);
  1082.     }
  1083.  
  1084.     if (tradeState != TRADE_TRANSFER) {
  1085.         if (tradeItem && oldItem == tradeItem) {
  1086.             g_game.internalCloseTrade(this);
  1087.         }
  1088.     }
  1089. }
  1090.  
  1091. void Player::onRemoveTileItem(const Tile* tile, const Position& pos, const ItemType& iType,
  1092.                               const Item* item)
  1093. {
  1094.     Creature::onRemoveTileItem(tile, pos, iType, item);
  1095.  
  1096.     if (tradeState != TRADE_TRANSFER) {
  1097.         checkTradeState(item);
  1098.  
  1099.         if (tradeItem) {
  1100.             const Container* container = item->getContainer();
  1101.             if (container && container->isHoldingItem(tradeItem)) {
  1102.                 g_game.internalCloseTrade(this);
  1103.             }
  1104.         }
  1105.     }
  1106. }
  1107.  
  1108. void Player::onCreatureAppear(Creature* creature, bool isLogin)
  1109. {
  1110.     Creature::onCreatureAppear(creature, isLogin);
  1111.  
  1112.     if (isLogin && creature == this) {
  1113.         sendItems();
  1114.  
  1115.         for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
  1116.             Item* item = inventory[slot];
  1117.             if (item) {
  1118.                 item->startDecaying();
  1119.                 g_moveEvents->onPlayerEquip(this, item, static_cast<slots_t>(slot), false);
  1120.             }
  1121.         }
  1122.  
  1123.         for (Condition* condition : storedConditionList) {
  1124.             addCondition(condition);
  1125.         }
  1126.         storedConditionList.clear();
  1127.  
  1128.         BedItem* bed = g_game.getBedBySleeper(guid);
  1129.         if (bed) {
  1130.             bed->wakeUp(this);
  1131.         }
  1132.        
  1133.         Account account = IOLoginData::loadAccount(accountNumber);
  1134.         Game::updatePremium(account);
  1135.  
  1136.         std::cout << name << " has logged in." << std::endl;
  1137.  
  1138.         if (guild) {
  1139.             guild->addMember(this);
  1140.         }
  1141.  
  1142.         int32_t offlineTime;
  1143.         if (getLastLogout() != 0) {
  1144.             // Not counting more than 21 days to prevent overflow when multiplying with 1000 (for milliseconds).
  1145.             offlineTime = std::min<int32_t>(time(nullptr) - getLastLogout(), 86400 * 21);
  1146.         } else {
  1147.             offlineTime = 0;
  1148.         }
  1149.  
  1150.         for (Condition* condition : getMuteConditions()) {
  1151.             condition->setTicks(condition->getTicks() - (offlineTime * 1000));
  1152.             if (condition->getTicks() <= 0) {
  1153.                 removeCondition(condition);
  1154.             }
  1155.         }
  1156.  
  1157.         g_game.checkPlayersRecord();
  1158.         IOLoginData::updateOnlineStatus(guid, true);
  1159.     }
  1160. }
  1161.  
  1162. void Player::onAttackedCreatureDisappear(bool isLogout)
  1163. {
  1164.     sendCancelTarget();
  1165.  
  1166.     if (!isLogout) {
  1167.         sendTextMessage(MESSAGE_STATUS_SMALL, "Target lost.");
  1168.     }
  1169. }
  1170.  
  1171. void Player::onFollowCreatureDisappear(bool isLogout)
  1172. {
  1173.     sendCancelTarget();
  1174.  
  1175.     if (!isLogout) {
  1176.         sendTextMessage(MESSAGE_STATUS_SMALL, "Target lost.");
  1177.     }
  1178. }
  1179.  
  1180. void Player::onChangeZone(ZoneType_t zone)
  1181. {
  1182.     if (zone == ZONE_PROTECTION) {
  1183.         if (attackedCreature && !hasFlag(PlayerFlag_IgnoreProtectionZone)) {
  1184.             setAttackedCreature(nullptr);
  1185.             onAttackedCreatureDisappear(false);
  1186.         }
  1187.  
  1188.         if (!group->access && isMounted()) {
  1189.             dismount();
  1190.             g_game.internalCreatureChangeOutfit(this, defaultOutfit);
  1191.             wasMounted = true;
  1192.         }
  1193.     } else {
  1194.         if (wasMounted) {
  1195.             toggleMount(true);
  1196.             wasMounted = false;
  1197.         }
  1198.     }
  1199.  
  1200.     g_game.updateCreatureWalkthrough(this);
  1201.     sendIcons();
  1202. }
  1203.  
  1204. void Player::onAttackedCreatureChangeZone(ZoneType_t zone)
  1205. {
  1206.     if (zone == ZONE_PROTECTION) {
  1207.         if (!hasFlag(PlayerFlag_IgnoreProtectionZone)) {
  1208.             setAttackedCreature(nullptr);
  1209.             onAttackedCreatureDisappear(false);
  1210.         }
  1211.     } else if (zone == ZONE_NOPVP) {
  1212.         if (attackedCreature->getPlayer()) {
  1213.             if (!hasFlag(PlayerFlag_IgnoreProtectionZone)) {
  1214.                 setAttackedCreature(nullptr);
  1215.                 onAttackedCreatureDisappear(false);
  1216.             }
  1217.         }
  1218.     } else if (zone == ZONE_NORMAL) {
  1219.         //attackedCreature can leave a pvp zone if not pzlocked
  1220.         if (g_game.getWorldType() == WORLD_TYPE_NO_PVP) {
  1221.             if (attackedCreature->getPlayer()) {
  1222.                 setAttackedCreature(nullptr);
  1223.                 onAttackedCreatureDisappear(false);
  1224.             }
  1225.         }
  1226.     }
  1227. }
  1228.  
  1229. void Player::onRemoveCreature(Creature* creature, bool isLogout)
  1230. {
  1231.     Creature::onRemoveCreature(creature, isLogout);
  1232.  
  1233.     if (creature == this) {
  1234.         if (isLogout) {
  1235.             loginPosition = getPosition();
  1236.         }
  1237.  
  1238.         lastLogout = time(nullptr);
  1239.  
  1240.         if (eventWalk != 0) {
  1241.             setFollowCreature(nullptr);
  1242.         }
  1243.  
  1244.         if (tradePartner) {
  1245.             g_game.internalCloseTrade(this);
  1246.         }
  1247.  
  1248.         closeShopWindow();
  1249.  
  1250.         clearPartyInvitations();
  1251.  
  1252.         if (party) {
  1253.             party->leaveParty(this);
  1254.         }
  1255.  
  1256.         g_chat->removeUserFromAllChannels(*this);
  1257.  
  1258.         std::cout << getName() << " has logged out." << std::endl;
  1259.  
  1260.         if (guild) {
  1261.             guild->removeMember(this);
  1262.         }
  1263.  
  1264.         IOLoginData::updateOnlineStatus(guid, false);
  1265.  
  1266.         bool saved = false;
  1267.         for (uint32_t tries = 0; tries < 3; ++tries) {
  1268.             if (IOLoginData::savePlayer(this)) {
  1269.                 saved = true;
  1270.                 break;
  1271.             }
  1272.         }
  1273.  
  1274.         if (!saved) {
  1275.             std::cout << "Error while saving player: " << getName() << std::endl;
  1276.         }
  1277.     }
  1278. }
  1279.  
  1280. void Player::openShopWindow(Npc* npc, const std::list<ShopInfo>& shop)
  1281. {
  1282.     shopItemList = shop;
  1283.     sendShop(npc);
  1284.     sendSaleItemList();
  1285. }
  1286.  
  1287. bool Player::closeShopWindow(bool sendCloseShopWindow /*= true*/)
  1288. {
  1289.     //unreference callbacks
  1290.     int32_t onBuy;
  1291.     int32_t onSell;
  1292.  
  1293.     Npc* npc = getShopOwner(onBuy, onSell);
  1294.     if (!npc) {
  1295.         shopItemList.clear();
  1296.         return false;
  1297.     }
  1298.  
  1299.     setShopOwner(nullptr, -1, -1);
  1300.     npc->onPlayerEndTrade(this, onBuy, onSell);
  1301.  
  1302.     if (sendCloseShopWindow) {
  1303.         sendCloseShop();
  1304.     }
  1305.  
  1306.     shopItemList.clear();
  1307.     return true;
  1308. }
  1309.  
  1310. void Player::onWalk(Direction& dir)
  1311. {
  1312.     Creature::onWalk(dir);
  1313.     setNextActionTask(nullptr);
  1314.     setNextAction(OTSYS_TIME() + getStepDuration(dir));
  1315. }
  1316.  
  1317. void Player::onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos,
  1318.                             const Tile* oldTile, const Position& oldPos, bool teleport)
  1319. {
  1320.     Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport);
  1321.  
  1322.     if (hasFollowPath && (creature == followCreature || (creature == this && followCreature))) {
  1323.         isUpdatingPath = false;
  1324.         g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, &g_game, getID())));
  1325.     }
  1326.  
  1327.     if (creature != this) {
  1328.         return;
  1329.     }
  1330.  
  1331.     if (tradeState != TRADE_TRANSFER) {
  1332.         //check if we should close trade
  1333.         if (tradeItem && !Position::areInRange<1, 1, 0>(tradeItem->getPosition(), getPosition())) {
  1334.             g_game.internalCloseTrade(this);
  1335.         }
  1336.  
  1337.         if (tradePartner && !Position::areInRange<2, 2, 0>(tradePartner->getPosition(), getPosition())) {
  1338.             g_game.internalCloseTrade(this);
  1339.         }
  1340.     }
  1341.  
  1342.     // close modal windows
  1343.     if (!modalWindows.empty()) {
  1344.         // TODO: This shouldn't be hardcoded
  1345.         for (uint32_t modalWindowId : modalWindows) {
  1346.             if (modalWindowId == std::numeric_limits<uint32_t>::max()) {
  1347.                 sendTextMessage(MESSAGE_EVENT_ADVANCE, "Offline training aborted.");
  1348.                 break;
  1349.             }
  1350.         }
  1351.         modalWindows.clear();
  1352.     }
  1353.  
  1354.     // leave market
  1355.     if (inMarket) {
  1356.         inMarket = false;
  1357.     }
  1358.  
  1359.     if (party) {
  1360.         party->updateSharedExperience();
  1361.     }
  1362.  
  1363.     if (teleport || oldPos.z != newPos.z) {
  1364.         int32_t ticks = g_config.getNumber(ConfigManager::STAIRHOP_DELAY);
  1365.         if (ticks > 0) {
  1366.             if (Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks, 0)) {
  1367.                 addCondition(condition);
  1368.             }
  1369.         }
  1370.     }
  1371. }
  1372.  
  1373. //container
  1374. void Player::onAddContainerItem(const Item* item)
  1375. {
  1376.     checkTradeState(item);
  1377. }
  1378.  
  1379. void Player::onUpdateContainerItem(const Container* container, const Item* oldItem, const Item* newItem)
  1380. {
  1381.     if (oldItem != newItem) {
  1382.         onRemoveContainerItem(container, oldItem);
  1383.     }
  1384.  
  1385.     if (tradeState != TRADE_TRANSFER) {
  1386.         checkTradeState(oldItem);
  1387.     }
  1388. }
  1389.  
  1390. void Player::onRemoveContainerItem(const Container* container, const Item* item)
  1391. {
  1392.     if (tradeState != TRADE_TRANSFER) {
  1393.         checkTradeState(item);
  1394.  
  1395.         if (tradeItem) {
  1396.             if (tradeItem->getParent() != container && container->isHoldingItem(tradeItem)) {
  1397.                 g_game.internalCloseTrade(this);
  1398.             }
  1399.         }
  1400.     }
  1401. }
  1402.  
  1403. void Player::onCloseContainer(const Container* container)
  1404. {
  1405.     if (!client) {
  1406.         return;
  1407.     }
  1408.  
  1409.     for (const auto& it : openContainers) {
  1410.         if (it.second.container == container) {
  1411.             client->sendCloseContainer(it.first);
  1412.         }
  1413.     }
  1414. }
  1415.  
  1416. void Player::onSendContainer(const Container* container)
  1417. {
  1418.     if (!client) {
  1419.         return;
  1420.     }
  1421.  
  1422.     bool hasParent = container->hasParent();
  1423.     for (const auto& it : openContainers) {
  1424.         const OpenContainer& openContainer = it.second;
  1425.         if (openContainer.container == container) {
  1426.             client->sendContainer(it.first, container, hasParent, openContainer.index);
  1427.         }
  1428.     }
  1429. }
  1430.  
  1431. //inventory
  1432. void Player::onUpdateInventoryItem(Item* oldItem, Item* newItem)
  1433. {
  1434.     if (oldItem != newItem) {
  1435.         onRemoveInventoryItem(oldItem);
  1436.     }
  1437.  
  1438.     if (tradeState != TRADE_TRANSFER) {
  1439.         checkTradeState(oldItem);
  1440.     }
  1441. }
  1442.  
  1443. void Player::onRemoveInventoryItem(Item* item)
  1444. {
  1445.     if (tradeState != TRADE_TRANSFER) {
  1446.         checkTradeState(item);
  1447.  
  1448.         if (tradeItem) {
  1449.             const Container* container = item->getContainer();
  1450.             if (container && container->isHoldingItem(tradeItem)) {
  1451.                 g_game.internalCloseTrade(this);
  1452.             }
  1453.         }
  1454.     }
  1455. }
  1456.  
  1457. void Player::checkTradeState(const Item* item)
  1458. {
  1459.     if (!tradeItem || tradeState == TRADE_TRANSFER) {
  1460.         return;
  1461.     }
  1462.  
  1463.     if (tradeItem == item) {
  1464.         g_game.internalCloseTrade(this);
  1465.     } else {
  1466.         const Container* container = dynamic_cast<const Container*>(item->getParent());
  1467.         while (container) {
  1468.             if (container == tradeItem) {
  1469.                 g_game.internalCloseTrade(this);
  1470.                 break;
  1471.             }
  1472.  
  1473.             container = dynamic_cast<const Container*>(container->getParent());
  1474.         }
  1475.     }
  1476. }
  1477.  
  1478. void Player::setNextWalkActionTask(SchedulerTask* task)
  1479. {
  1480.     if (walkTaskEvent != 0) {
  1481.         g_scheduler.stopEvent(walkTaskEvent);
  1482.         walkTaskEvent = 0;
  1483.     }
  1484.  
  1485.     delete walkTask;
  1486.     walkTask = task;
  1487. }
  1488.  
  1489. void Player::setNextWalkTask(SchedulerTask* task)
  1490. {
  1491.     if (nextStepEvent != 0) {
  1492.         g_scheduler.stopEvent(nextStepEvent);
  1493.         nextStepEvent = 0;
  1494.     }
  1495.  
  1496.     if (task) {
  1497.         nextStepEvent = g_scheduler.addEvent(task);
  1498.         resetIdleTime();
  1499.     }
  1500. }
  1501.  
  1502. void Player::setNextActionTask(SchedulerTask* task)
  1503. {
  1504.     if (actionTaskEvent != 0) {
  1505.         g_scheduler.stopEvent(actionTaskEvent);
  1506.         actionTaskEvent = 0;
  1507.     }
  1508.  
  1509.     if (task) {
  1510.         actionTaskEvent = g_scheduler.addEvent(task);
  1511.         resetIdleTime();
  1512.     }
  1513. }
  1514.  
  1515. uint32_t Player::getNextActionTime() const
  1516. {
  1517.     return std::max<int64_t>(SCHEDULER_MINTICKS, nextAction - OTSYS_TIME());
  1518. }
  1519.  
  1520. void Player::onThink(uint32_t interval)
  1521. {
  1522.     Creature::onThink(interval);
  1523.  
  1524.     sendPing();
  1525.  
  1526.     MessageBufferTicks += interval;
  1527.     if (MessageBufferTicks >= 1500) {
  1528.         MessageBufferTicks = 0;
  1529.         addMessageBuffer();
  1530.     }
  1531.  
  1532.     if (!getTile()->hasFlag(TILESTATE_NOLOGOUT) && !isAccessPlayer()) {
  1533.         idleTime += interval;
  1534.         const int32_t kickAfterMinutes = g_config.getNumber(ConfigManager::KICK_AFTER_MINUTES);
  1535.         if (idleTime > (kickAfterMinutes * 60000) + 60000) {
  1536.             kickPlayer(true);
  1537.         } else if (client && idleTime == 60000 * kickAfterMinutes) {
  1538.             std::ostringstream ss;
  1539.             ss << "You have been idle for " << kickAfterMinutes << " minutes. You will be disconnected in one minute if you are still idle then.";
  1540.             client->sendTextMessage(TextMessage(MESSAGE_STATUS_WARNING, ss.str()));
  1541.         }
  1542.     }
  1543.  
  1544.     if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
  1545.         checkSkullTicks(interval);
  1546.     }
  1547.  
  1548.     addOfflineTrainingTime(interval);
  1549.     if (lastStatsTrainingTime != getOfflineTrainingTime() / 60 / 1000) {
  1550.         sendStats();
  1551.     }
  1552. }
  1553.  
  1554. uint32_t Player::isMuted() const
  1555. {
  1556.     if (hasFlag(PlayerFlag_CannotBeMuted)) {
  1557.         return 0;
  1558.     }
  1559.  
  1560.     int32_t muteTicks = 0;
  1561.     for (Condition* condition : conditions) {
  1562.         if (condition->getType() == CONDITION_MUTED && condition->getTicks() > muteTicks) {
  1563.             muteTicks = condition->getTicks();
  1564.         }
  1565.     }
  1566.     return static_cast<uint32_t>(muteTicks) / 1000;
  1567. }
  1568.  
  1569. void Player::addMessageBuffer()
  1570. {
  1571.     if (MessageBufferCount > 0 && g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER) != 0 && !hasFlag(PlayerFlag_CannotBeMuted)) {
  1572.         --MessageBufferCount;
  1573.     }
  1574. }
  1575.  
  1576. void Player::removeMessageBuffer()
  1577. {
  1578.     if (hasFlag(PlayerFlag_CannotBeMuted)) {
  1579.         return;
  1580.     }
  1581.  
  1582.     const int32_t maxMessageBuffer = g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER);
  1583.     if (maxMessageBuffer != 0 && MessageBufferCount <= maxMessageBuffer + 1) {
  1584.         if (++MessageBufferCount > maxMessageBuffer) {
  1585.             uint32_t muteCount = 1;
  1586.             auto it = muteCountMap.find(guid);
  1587.             if (it != muteCountMap.end()) {
  1588.                 muteCount = it->second;
  1589.             }
  1590.  
  1591.             uint32_t muteTime = 5 * muteCount * muteCount;
  1592.             muteCountMap[guid] = muteCount + 1;
  1593.             Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, muteTime * 1000, 0);
  1594.             addCondition(condition);
  1595.  
  1596.             std::ostringstream ss;
  1597.             ss << "You are muted for " << muteTime << " seconds.";
  1598.             sendTextMessage(MESSAGE_STATUS_SMALL, ss.str());
  1599.         }
  1600.     }
  1601. }
  1602.  
  1603. void Player::drainHealth(Creature* attacker, int32_t damage)
  1604. {
  1605.     Creature::drainHealth(attacker, damage);
  1606.     sendStats();
  1607. }
  1608.  
  1609. void Player::drainMana(Creature* attacker, int32_t manaLoss)
  1610. {
  1611.     onAttacked();
  1612.     changeMana(-manaLoss);
  1613.  
  1614.     if (attacker) {
  1615.         addDamagePoints(attacker, manaLoss);
  1616.     }
  1617.  
  1618.     sendStats();
  1619. }
  1620.  
  1621. void Player::addManaSpent(uint64_t amount)
  1622. {
  1623.     if (hasFlag(PlayerFlag_NotGainMana)) {
  1624.         return;
  1625.     }
  1626.  
  1627.     uint64_t currReqMana = vocation->getReqMana(magLevel);
  1628.     uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
  1629.     if (currReqMana >= nextReqMana) {
  1630.         //player has reached max magic level
  1631.         return;
  1632.     }
  1633.  
  1634.     g_events->eventPlayerOnGainSkillTries(this, SKILL_MAGLEVEL, amount);
  1635.     if (amount == 0) {
  1636.         return;
  1637.     }
  1638.  
  1639.     bool sendUpdateStats = false;
  1640.     while ((manaSpent + amount) >= nextReqMana) {
  1641.         amount -= nextReqMana - manaSpent;
  1642.  
  1643.         magLevel++;
  1644.         manaMax += g_config.getNumber(ConfigManager::MAGIC_MANAGAIN);
  1645.         mana += g_config.getNumber(ConfigManager::MAGIC_MANAGAIN);
  1646.         manaSpent = 0;
  1647.  
  1648.         std::ostringstream ss;
  1649.         ss << "You advanced to magic level " << magLevel << '.';
  1650.         //sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  1651.  
  1652.         g_creatureEvents->playerAdvance(this, SKILL_MAGLEVEL, magLevel - 1, magLevel);
  1653.  
  1654.         sendUpdateStats = true;
  1655.         currReqMana = nextReqMana;
  1656.         nextReqMana = vocation->getReqMana(magLevel + 1);
  1657.         if (currReqMana >= nextReqMana) {
  1658.             return;
  1659.         }
  1660.     }
  1661.  
  1662.     manaSpent += amount;
  1663.  
  1664.     uint8_t oldPercent = magLevelPercent;
  1665.     if (nextReqMana > currReqMana) {
  1666.         magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
  1667.     } else {
  1668.         magLevelPercent = 0;
  1669.     }
  1670.  
  1671.     if (oldPercent != magLevelPercent) {
  1672.         sendUpdateStats = true;
  1673.     }
  1674.  
  1675.     if (sendUpdateStats) {
  1676.         sendStats();
  1677.     }
  1678. }
  1679.  
  1680. void Player::addExperience(Creature* source, uint64_t exp, bool sendText/* = false*/)
  1681. {
  1682.     uint64_t currLevelExp = Player::getExpForLevel(level);
  1683.     uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
  1684.     uint64_t rawExp = exp;
  1685.     if (currLevelExp >= nextLevelExp) {
  1686.         //player has reached max level
  1687.         levelPercent = 0;
  1688.         sendStats();
  1689.         return;
  1690.     }
  1691.  
  1692.     g_events->eventPlayerOnGainExperience(this, source, exp, rawExp);
  1693.     if (exp == 0) {
  1694.         return;
  1695.     }
  1696.  
  1697.     experience += exp;
  1698.  
  1699.     if (sendText) {
  1700.         std::string expString = std::to_string(exp) + (exp != 1 ? " experience points." : " experience point.");
  1701.  
  1702.         TextMessage message(MESSAGE_EXPERIENCE, "You gained " + expString);
  1703.         message.position = position;
  1704.         message.primary.value = exp;
  1705.         message.primary.color = TEXTCOLOR_WHITE_EXP;
  1706.         sendTextMessage(message);
  1707.  
  1708.         SpectatorHashSet spectators;
  1709.         g_game.map.getSpectators(spectators, position, false, true);
  1710.         spectators.erase(this);
  1711.         if (!spectators.empty()) {
  1712.             message.type = MESSAGE_EXPERIENCE_OTHERS;
  1713.             message.text = getName() + " gained " + expString;
  1714.             for (Creature* spectator : spectators) {
  1715.                 spectator->getPlayer()->sendTextMessage(message);
  1716.             }
  1717.         }
  1718.     }
  1719.  
  1720.     uint32_t prevLevel = level;
  1721.     while (experience >= nextLevelExp) {
  1722.         ++level;
  1723.         healthMax += vocation->getHPGain();
  1724.         health += vocation->getHPGain();
  1725.         manaMax += vocation->getManaGain();
  1726.         mana += vocation->getManaGain();
  1727.         capacity += vocation->getCapGain();
  1728.  
  1729.         currLevelExp = nextLevelExp;
  1730.         nextLevelExp = Player::getExpForLevel(level + 1);
  1731.         if (currLevelExp >= nextLevelExp) {
  1732.             //player has reached max level
  1733.             break;
  1734.         }
  1735.     }
  1736.  
  1737.     if (prevLevel != level) {
  1738.         //health = healthMax;
  1739.         //mana = manaMax;
  1740.  
  1741.         updateBaseSpeed();
  1742.         setBaseSpeed(getBaseSpeed());
  1743.  
  1744.         g_game.changeSpeed(this, 0);
  1745.         //g_game.addCreatureHealth(this);
  1746.  
  1747.         if (party) {
  1748.             party->updateSharedExperience();
  1749.         }
  1750.  
  1751.         g_creatureEvents->playerAdvance(this, SKILL_LEVEL, prevLevel, level);
  1752.  
  1753.         std::ostringstream ss;
  1754.         ss << "You advanced from Level " << prevLevel << " to Level " << level << '.';
  1755.         sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  1756.     }
  1757.  
  1758.     if (nextLevelExp > currLevelExp) {
  1759.         levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
  1760.     } else {
  1761.         levelPercent = 0;
  1762.     }
  1763.     sendStats();
  1764. }
  1765.  
  1766. void Player::removeExperience(uint64_t exp, bool sendText/* = false*/)
  1767. {
  1768.     if (experience == 0 || exp == 0) {
  1769.         return;
  1770.     }
  1771.  
  1772.     g_events->eventPlayerOnLoseExperience(this, exp);
  1773.     if (exp == 0) {
  1774.         return;
  1775.     }
  1776.  
  1777.     uint64_t lostExp = experience;
  1778.     experience = std::max<int64_t>(0, experience - exp);
  1779.  
  1780.     if (sendText) {
  1781.         lostExp -= experience;
  1782.  
  1783.         std::string expString = std::to_string(lostExp) + (lostExp != 1 ? " experience points." : " experience point.");
  1784.  
  1785.         TextMessage message(MESSAGE_EXPERIENCE, "You lost " + expString);
  1786.         message.position = position;
  1787.         message.primary.value = lostExp;
  1788.         message.primary.color = TEXTCOLOR_RED;
  1789.         sendTextMessage(message);
  1790.  
  1791.         SpectatorHashSet spectators;
  1792.         g_game.map.getSpectators(spectators, position, false, true);
  1793.         spectators.erase(this);
  1794.         if (!spectators.empty()) {
  1795.             message.type = MESSAGE_EXPERIENCE_OTHERS;
  1796.             message.text = getName() + " lost " + expString;
  1797.             for (Creature* spectator : spectators) {
  1798.                 spectator->getPlayer()->sendTextMessage(message);
  1799.             }
  1800.         }
  1801.     }
  1802.  
  1803.     uint32_t oldLevel = level;
  1804.     uint64_t currLevelExp = Player::getExpForLevel(level);
  1805.  
  1806.     while (level > 1 && experience < currLevelExp) {
  1807.         --level;
  1808.         healthMax = std::max<int32_t>(0, healthMax - vocation->getHPGain());
  1809.         manaMax = std::max<int32_t>(0, manaMax - vocation->getManaGain());
  1810.         capacity = std::max<int32_t>(0, capacity - vocation->getCapGain());
  1811.         currLevelExp = Player::getExpForLevel(level);
  1812.     }
  1813.  
  1814.     if (oldLevel != level) {
  1815.         health = healthMax;
  1816.         mana = manaMax;
  1817.  
  1818.         updateBaseSpeed();
  1819.         setBaseSpeed(getBaseSpeed());
  1820.  
  1821.         g_game.changeSpeed(this, 0);
  1822.         g_game.addCreatureHealth(this);
  1823.  
  1824.         if (party) {
  1825.             party->updateSharedExperience();
  1826.         }
  1827.  
  1828.         std::ostringstream ss;
  1829.         ss << "You were downgraded from Level " << oldLevel << " to Level " << level << '.';
  1830.         sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  1831.     }
  1832.  
  1833.     uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
  1834.     if (nextLevelExp > currLevelExp) {
  1835.         levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
  1836.     } else {
  1837.         levelPercent = 0;
  1838.     }
  1839.     sendStats();
  1840. }
  1841.  
  1842. uint8_t Player::getPercentLevel(uint64_t count, uint64_t nextLevelCount)
  1843. {
  1844.     if (nextLevelCount == 0) {
  1845.         return 0;
  1846.     }
  1847.  
  1848.     uint8_t result = (count * 100) / nextLevelCount;
  1849.     if (result > 100) {
  1850.         return 0;
  1851.     }
  1852.     return result;
  1853. }
  1854.  
  1855. void Player::onBlockHit()
  1856. {
  1857.     if (shieldBlockCount > 0) {
  1858.         --shieldBlockCount;
  1859.  
  1860.         if (hasShield()) {
  1861.             //addSkillAdvance(SKILL_SHIELD, 1);
  1862.         }
  1863.     }
  1864. }
  1865.  
  1866. void Player::onAttackedCreatureBlockHit(BlockType_t blockType)
  1867. {
  1868.     lastAttackBlockType = blockType;
  1869.  
  1870.     switch (blockType) {
  1871.         case BLOCK_NONE: {
  1872.             addAttackSkillPoint = true;
  1873.             bloodHitCount = 30;
  1874.             shieldBlockCount = 30;
  1875.             break;
  1876.         }
  1877.  
  1878.         case BLOCK_DEFENSE:
  1879.         case BLOCK_ARMOR: {
  1880.             //need to draw blood every 30 hits
  1881.             if (bloodHitCount > 0) {
  1882.                 addAttackSkillPoint = true;
  1883.                 --bloodHitCount;
  1884.             } else {
  1885.                 addAttackSkillPoint = false;
  1886.             }
  1887.             break;
  1888.         }
  1889.  
  1890.         default: {
  1891.             addAttackSkillPoint = false;
  1892.             break;
  1893.         }
  1894.     }
  1895. }
  1896.  
  1897. bool Player::hasShield() const
  1898. {
  1899.     Item* item = inventory[CONST_SLOT_LEFT];
  1900.     if (item && item->getWeaponType() == WEAPON_SHIELD) {
  1901.         return true;
  1902.     }
  1903.  
  1904.     item = inventory[CONST_SLOT_RIGHT];
  1905.     if (item && item->getWeaponType() == WEAPON_SHIELD) {
  1906.         return true;
  1907.     }
  1908.     return false;
  1909. }
  1910.  
  1911. BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
  1912.                              bool checkDefense /* = false*/, bool checkArmor /* = false*/, bool field /* = false*/)
  1913. {
  1914.     BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor, field);
  1915.  
  1916.     if (attacker) {
  1917.         sendCreatureSquare(attacker, SQ_COLOR_BLACK);
  1918.     }
  1919.  
  1920.     if (blockType != BLOCK_NONE) {
  1921.         return blockType;
  1922.     }
  1923.  
  1924.     if (damage <= 0) {
  1925.         damage = 0;
  1926.         return BLOCK_ARMOR;
  1927.     }
  1928.  
  1929.     for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
  1930.         if (!isItemAbilityEnabled(static_cast<slots_t>(slot))) {
  1931.             continue;
  1932.         }
  1933.  
  1934.         Item* item = inventory[slot];
  1935.         if (!item) {
  1936.             continue;
  1937.         }
  1938.  
  1939.         const ItemType& it = Item::items[item->getID()];
  1940.         if (!it.abilities) {
  1941.             if (damage <= 0) {
  1942.                 damage = 0;
  1943.                 return BLOCK_ARMOR;
  1944.             }
  1945.  
  1946.             continue;
  1947.         }
  1948.  
  1949.         const int16_t& absorbPercent = it.abilities->absorbPercent[combatTypeToIndex(combatType)];
  1950.         if (absorbPercent != 0) {
  1951.             damage -= std::round(damage * (absorbPercent / 100.));
  1952.  
  1953.             uint16_t charges = item->getCharges();
  1954.             if (charges != 0) {
  1955.                 g_game.transformItem(item, item->getID(), charges - 1);
  1956.             }
  1957.         }
  1958.  
  1959.         if (field) {
  1960.             const int16_t& fieldAbsorbPercent = it.abilities->fieldAbsorbPercent[combatTypeToIndex(combatType)];
  1961.             if (fieldAbsorbPercent != 0) {
  1962.                 damage -= std::round(damage * (fieldAbsorbPercent / 100.));
  1963.  
  1964.                 uint16_t charges = item->getCharges();
  1965.                 if (charges != 0) {
  1966.                     g_game.transformItem(item, item->getID(), charges - 1);
  1967.                 }
  1968.             }
  1969.         }
  1970.     }
  1971.  
  1972.     if (damage <= 0) {
  1973.         damage = 0;
  1974.         blockType = BLOCK_ARMOR;
  1975.     }
  1976.     return blockType;
  1977. }
  1978.  
  1979. uint32_t Player::getIP() const
  1980. {
  1981.     if (client) {
  1982.         return client->getIP();
  1983.     }
  1984.  
  1985.     return 0;
  1986. }
  1987.  
  1988. void Player::death(Creature* lastHitCreature)
  1989. {
  1990.     loginPosition = town->getTemplePosition();
  1991.  
  1992.     if (skillLoss) {
  1993.         uint8_t unfairFightReduction = 100;
  1994.         bool lastHitPlayer = Player::lastHitIsPlayer(lastHitCreature);
  1995.  
  1996.         if (lastHitPlayer) {
  1997.             uint32_t sumLevels = 0;
  1998.             uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
  1999.             for (const auto& it : damageMap) {
  2000.                 CountBlock_t cb = it.second;
  2001.                 if ((OTSYS_TIME() - cb.ticks) <= inFightTicks) {
  2002.                     Player* damageDealer = g_game.getPlayerByID(it.first);
  2003.                     if (damageDealer) {
  2004.                         sumLevels += damageDealer->getLevel();
  2005.                     }
  2006.                 }
  2007.             }
  2008.  
  2009.             if (sumLevels > level) {
  2010.                 double reduce = level / static_cast<double>(sumLevels);
  2011.                 unfairFightReduction = std::max<uint8_t>(20, std::floor((reduce * 100) + 0.5));
  2012.             }
  2013.         }
  2014.  
  2015.         //Magic level loss
  2016.         uint64_t sumMana = 0;
  2017.         uint64_t lostMana = 0;
  2018.  
  2019.         //sum up all the mana
  2020.         for (uint32_t i = 1; i <= magLevel; ++i) {
  2021.             sumMana += vocation->getReqMana(i);
  2022.         }
  2023.  
  2024.         sumMana += manaSpent;
  2025.  
  2026.         double deathLossPercent = getLostPercent() * (unfairFightReduction / 100.);
  2027.  
  2028.         lostMana = static_cast<uint64_t>(sumMana * deathLossPercent);
  2029.  
  2030.         while (lostMana > manaSpent && magLevel > 0) {
  2031.             lostMana -= manaSpent;
  2032.             manaSpent = vocation->getReqMana(magLevel);
  2033.             //magLevel--;
  2034.         }
  2035.  
  2036.         //manaSpent -= lostMana;
  2037.  
  2038.         uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
  2039.         if (nextReqMana > vocation->getReqMana(magLevel)) {
  2040.             magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
  2041.         } else {
  2042.             magLevelPercent = 0;
  2043.         }
  2044.  
  2045.         //Skill loss
  2046.         for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { //for each skill
  2047.             uint64_t sumSkillTries = 0;
  2048.             for (uint16_t c = 11; c <= skills[i].level; ++c) { //sum up all required tries for all skill levels
  2049.                 sumSkillTries += vocation->getReqSkillTries(i, c);
  2050.             }
  2051.  
  2052.             sumSkillTries += skills[i].tries;
  2053.  
  2054.             uint32_t lostSkillTries = static_cast<uint32_t>(sumSkillTries * deathLossPercent);
  2055.             while (lostSkillTries > skills[i].tries) {
  2056.                 lostSkillTries -= skills[i].tries;
  2057.  
  2058.                 if (skills[i].level <= 10) {
  2059.                     //skills[i].level = 10;
  2060.                     skills[i].tries = 0;
  2061.                     lostSkillTries = 0;
  2062.                     break;
  2063.                 }
  2064.  
  2065.                 skills[i].tries = vocation->getReqSkillTries(i, skills[i].level);
  2066.                 //skills[i].level--;
  2067.             }
  2068.  
  2069.             skills[i].tries = std::max<int32_t>(0, skills[i].tries - lostSkillTries);
  2070.             skills[i].percent = Player::getPercentLevel(skills[i].tries, vocation->getReqSkillTries(i, skills[i].level));
  2071.         }
  2072.  
  2073.         //Level loss
  2074.         uint64_t expLoss = static_cast<uint64_t>(experience * deathLossPercent);
  2075.         g_events->eventPlayerOnLoseExperience(this, expLoss);
  2076.  
  2077.         if (expLoss != 0) {
  2078.             uint32_t oldLevel = level;
  2079.  
  2080.             if (vocation->getId() == VOCATION_NONE || level > 7) {
  2081.                 experience -= expLoss;
  2082.             }
  2083.  
  2084.             while (level > 1 && experience < Player::getExpForLevel(level)) {
  2085.                 --level;
  2086.                 healthMax = std::max<int32_t>(0, healthMax - vocation->getHPGain());
  2087.                 manaMax = std::max<int32_t>(0, manaMax - vocation->getManaGain());
  2088.                 capacity = std::max<int32_t>(0, capacity - vocation->getCapGain());
  2089.             }
  2090.  
  2091.             if (oldLevel != level) {
  2092.                 std::ostringstream ss;
  2093.                 ss << "You were downgraded from Level " << oldLevel << " to Level " << level << '.';
  2094.                 //sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  2095.             }
  2096.  
  2097.             uint64_t currLevelExp = Player::getExpForLevel(level);
  2098.             uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
  2099.             if (nextLevelExp > currLevelExp) {
  2100.                 levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
  2101.             } else {
  2102.                 levelPercent = 0;
  2103.             }
  2104.         }
  2105.  
  2106.         std::bitset<6> bitset(blessings);
  2107.         if (bitset[5]) {
  2108.             if (lastHitPlayer) {
  2109.                 bitset.reset(5);
  2110.                 blessings = bitset.to_ulong();
  2111.             } else {
  2112.                 blessings = 32;
  2113.             }
  2114.         } else {
  2115.             blessings = 0;
  2116.         }
  2117.  
  2118.         sendStats();
  2119.         sendSkills();
  2120.         //sendReLoginWindow(unfairFightReduction);
  2121.  
  2122.         if (getSkull() == SKULL_BLACK) {
  2123.             health = 40;
  2124.             mana = 0;
  2125.         } else {
  2126.             health = healthMax;
  2127.             mana = manaMax;
  2128.         }
  2129.  
  2130.         auto it = conditions.begin(), end = conditions.end();
  2131.         while (it != end) {
  2132.             Condition* condition = *it;
  2133.             if (condition->isPersistent()) {
  2134.                 it = conditions.erase(it);
  2135.  
  2136.                 condition->endCondition(this);
  2137.                 onEndCondition(condition->getType());
  2138.                 delete condition;
  2139.             } else {
  2140.                 ++it;
  2141.             }
  2142.         }
  2143.     } else {
  2144.         setSkillLoss(true);
  2145.  
  2146.         auto it = conditions.begin(), end = conditions.end();
  2147.         while (it != end) {
  2148.             Condition* condition = *it;
  2149.             if (condition->isPersistent()) {
  2150.                 it = conditions.erase(it);
  2151.  
  2152.                 condition->endCondition(this);
  2153.                 onEndCondition(condition->getType());
  2154.                 delete condition;
  2155.             } else {
  2156.                 ++it;
  2157.             }
  2158.         }
  2159.  
  2160.         health = healthMax;
  2161.         g_game.internalTeleport(this, getTemplePosition(), true);
  2162.         g_game.addCreatureHealth(this);
  2163.         onThink(EVENT_CREATURE_THINK_INTERVAL);
  2164.         onIdleStatus();
  2165.         sendStats();
  2166.     }
  2167. }
  2168.  
  2169. bool Player::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified)
  2170. {
  2171.     if (getZone() != ZONE_PVP || !Player::lastHitIsPlayer(lastHitCreature)) {
  2172.         return Creature::dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
  2173.     }
  2174.  
  2175.     setDropLoot(true);
  2176.     return false;
  2177. }
  2178.  
  2179. Item* Player::getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature)
  2180. {
  2181.     Item* corpse = Creature::getCorpse(lastHitCreature, mostDamageCreature);
  2182.     if (corpse && corpse->getContainer()) {
  2183.         std::ostringstream ss;
  2184.         if (lastHitCreature) {
  2185.             ss << "You recognize " << getNameDescription() << ". " << (getSex() == PLAYERSEX_FEMALE ? "She" : "He") << " was killed by " << lastHitCreature->getNameDescription() << '.';
  2186.         } else {
  2187.             ss << "You recognize " << getNameDescription() << '.';
  2188.         }
  2189.  
  2190.         corpse->setSpecialDescription(ss.str());
  2191.     }
  2192.     return corpse;
  2193. }
  2194.  
  2195. void Player::addInFightTicks(bool pzlock /*= false*/)
  2196. {
  2197.     if (hasFlag(PlayerFlag_NotGainInFight)) {
  2198.         return;
  2199.     }
  2200.  
  2201.     if (pzlock) {
  2202.         pzLocked = true;
  2203.     }
  2204.  
  2205.     Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_config.getNumber(ConfigManager::PZ_LOCKED), 0);
  2206.     addCondition(condition);
  2207. }
  2208.  
  2209. void Player::removeList()
  2210. {
  2211.     g_game.removePlayer(this);
  2212.  
  2213.     for (const auto& it : g_game.getPlayers()) {
  2214.         it.second->notifyStatusChange(this, VIPSTATUS_OFFLINE);
  2215.     }
  2216. }
  2217.  
  2218. void Player::addList()
  2219. {
  2220.     for (const auto& it : g_game.getPlayers()) {
  2221.         it.second->notifyStatusChange(this, VIPSTATUS_ONLINE);
  2222.     }
  2223.  
  2224.     g_game.addPlayer(this);
  2225. }
  2226.  
  2227. void Player::kickPlayer(bool displayEffect)
  2228. {
  2229.     g_creatureEvents->playerLogout(this);
  2230.     if (client) {
  2231.         client->logout(displayEffect, true);
  2232.     } else {
  2233.         g_game.removeCreature(this);
  2234.     }
  2235. }
  2236.  
  2237. void Player::notifyStatusChange(Player* loginPlayer, VipStatus_t status)
  2238. {
  2239.     if (!client) {
  2240.         return;
  2241.     }
  2242.  
  2243.     auto it = VIPList.find(loginPlayer->guid);
  2244.     if (it == VIPList.end()) {
  2245.         return;
  2246.     }
  2247.  
  2248.     client->sendUpdatedVIPStatus(loginPlayer->guid, status);
  2249.  
  2250.     if (status == VIPSTATUS_ONLINE) {
  2251.         client->sendTextMessage(TextMessage(MESSAGE_STATUS_SMALL, loginPlayer->getName() + " has logged in."));
  2252.     } else if (status == VIPSTATUS_OFFLINE) {
  2253.         client->sendTextMessage(TextMessage(MESSAGE_STATUS_SMALL, loginPlayer->getName() + " has logged out."));
  2254.     }
  2255. }
  2256.  
  2257. bool Player::removeVIP(uint32_t vipGuid)
  2258. {
  2259.     if (VIPList.erase(vipGuid) == 0) {
  2260.         return false;
  2261.     }
  2262.  
  2263.     IOLoginData::removeVIPEntry(accountNumber, vipGuid);
  2264.     return true;
  2265. }
  2266.  
  2267. bool Player::addVIP(uint32_t vipGuid, const std::string& vipName, VipStatus_t status)
  2268. {
  2269.     if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53
  2270.         sendTextMessage(MESSAGE_STATUS_SMALL, "You cannot add more buddies.");
  2271.         return false;
  2272.     }
  2273.  
  2274.     auto result = VIPList.insert(vipGuid);
  2275.     if (!result.second) {
  2276.         sendTextMessage(MESSAGE_STATUS_SMALL, "This player is already in your list.");
  2277.         return false;
  2278.     }
  2279.  
  2280.     IOLoginData::addVIPEntry(accountNumber, vipGuid, "", 0, false);
  2281.     if (client) {
  2282.         client->sendVIP(vipGuid, vipName, "", 0, false, status);
  2283.     }
  2284.     return true;
  2285. }
  2286.  
  2287. bool Player::addVIPInternal(uint32_t vipGuid)
  2288. {
  2289.     if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53
  2290.         return false;
  2291.     }
  2292.  
  2293.     return VIPList.insert(vipGuid).second;
  2294. }
  2295.  
  2296. bool Player::editVIP(uint32_t vipGuid, const std::string& description, uint32_t icon, bool notify)
  2297. {
  2298.     auto it = VIPList.find(vipGuid);
  2299.     if (it == VIPList.end()) {
  2300.         return false; // player is not in VIP
  2301.     }
  2302.  
  2303.     IOLoginData::editVIPEntry(accountNumber, vipGuid, description, icon, notify);
  2304.     return true;
  2305. }
  2306.  
  2307. //close container and its child containers
  2308. void Player::autoCloseContainers(const Container* container)
  2309. {
  2310.     std::vector<uint32_t> closeList;
  2311.     for (const auto& it : openContainers) {
  2312.         Container* tmpContainer = it.second.container;
  2313.         while (tmpContainer) {
  2314.             if (tmpContainer->isRemoved() || tmpContainer == container) {
  2315.                 closeList.push_back(it.first);
  2316.                 break;
  2317.             }
  2318.  
  2319.             tmpContainer = dynamic_cast<Container*>(tmpContainer->getParent());
  2320.         }
  2321.     }
  2322.  
  2323.     for (uint32_t containerId : closeList) {
  2324.         closeContainer(containerId);
  2325.         if (client) {
  2326.             client->sendCloseContainer(containerId);
  2327.         }
  2328.     }
  2329. }
  2330.  
  2331. bool Player::hasCapacity(const Item* item, uint32_t count) const
  2332. {
  2333.     if (hasFlag(PlayerFlag_CannotPickupItem)) {
  2334.         return false;
  2335.     }
  2336.  
  2337.     if (hasFlag(PlayerFlag_HasInfiniteCapacity) || item->getTopParent() == this) {
  2338.         return true;
  2339.     }
  2340.  
  2341.     uint32_t itemWeight = item->getContainer() != nullptr ? item->getWeight() : item->getBaseWeight();
  2342.     if (item->isStackable()) {
  2343.         itemWeight *= count;
  2344.     }
  2345.     return itemWeight <= getFreeCapacity();
  2346. }
  2347.  
  2348. //CHANGED!
  2349. ReturnValue Player::queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature*) const
  2350. {
  2351.     const Item* item = thing.getItem();
  2352.  
  2353.     if (item == nullptr) {
  2354.         return RETURNVALUE_NOTPOSSIBLE;
  2355.     }
  2356.  
  2357.     bool childIsOwner = hasBitSet(FLAG_CHILDISOWNER, flags);
  2358.     if (childIsOwner) {
  2359.         //a child container is querying the player, just check if enough capacity
  2360.         bool skipLimit = hasBitSet(FLAG_NOLIMIT, flags);
  2361.         if (skipLimit || hasCapacity(item, count)) {
  2362.             return RETURNVALUE_NOERROR;
  2363.         }
  2364.         return RETURNVALUE_NOTENOUGHCAPACITY;
  2365.     }
  2366.  
  2367.     if (!item->isPickupable()) {
  2368.         return RETURNVALUE_CANNOTPICKUP;
  2369.     }
  2370.  
  2371.     ReturnValue ret = RETURNVALUE_NOERROR;
  2372.  
  2373.     const int32_t& slotPosition = item->getSlotPosition();
  2374.     if ((slotPosition & SLOTP_HEAD) || (slotPosition & SLOTP_NECKLACE) ||
  2375.             (slotPosition & SLOTP_BACKPACK) || (slotPosition & SLOTP_ARMOR) ||
  2376.             (slotPosition & SLOTP_LEGS) || (slotPosition & SLOTP_FEET) ||
  2377.             (slotPosition & SLOTP_RING)) {
  2378.         ret = RETURNVALUE_CANNOTBEDRESSED;
  2379.     } else if (slotPosition & SLOTP_TWO_HAND) {
  2380.         ret = RETURNVALUE_PUTTHISOBJECTINBOTHHANDS;
  2381.     } else if ((slotPosition & SLOTP_RIGHT) || (slotPosition & SLOTP_LEFT)) {
  2382.         if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
  2383.             ret = RETURNVALUE_CANNOTBEDRESSED;
  2384.         } else {
  2385.             ret = RETURNVALUE_PUTTHISOBJECTINYOURHAND;
  2386.         }
  2387.     }
  2388.  
  2389.     switch (index) {
  2390.         case CONST_SLOT_HEAD: {
  2391.             if (slotPosition & SLOTP_HEAD) {
  2392.                 ret = RETURNVALUE_NOERROR;
  2393.             }
  2394.             break;
  2395.         }
  2396.  
  2397.         case CONST_SLOT_NECKLACE: {
  2398.             if (slotPosition & SLOTP_NECKLACE) {
  2399.                 ret = RETURNVALUE_NOERROR;
  2400.             }
  2401.             break;
  2402.         }
  2403.  
  2404.         case CONST_SLOT_BACKPACK: {
  2405.             if (slotPosition & SLOTP_BACKPACK) {
  2406.                 ret = RETURNVALUE_NOERROR;
  2407.             }
  2408.             break;
  2409.         }
  2410.  
  2411.         case CONST_SLOT_ARMOR: {
  2412.             if (slotPosition & SLOTP_ARMOR) {
  2413.                 ret = RETURNVALUE_NOERROR;
  2414.             }
  2415.             break;
  2416.         }
  2417.  
  2418.         case CONST_SLOT_RIGHT: {
  2419.             if (slotPosition & SLOTP_RIGHT) {
  2420.                 if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
  2421.                     if (item->getWeaponType() != WEAPON_SHIELD) {
  2422.                         ret = RETURNVALUE_CANNOTBEDRESSED;
  2423.                     } else {
  2424.                         const Item* leftItem = inventory[CONST_SLOT_LEFT];
  2425.                         if (leftItem) {
  2426.                             if ((leftItem->getSlotPosition() | slotPosition) & SLOTP_TWO_HAND) {
  2427.                                 ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
  2428.                             } else {
  2429.                                 ret = RETURNVALUE_NOERROR;
  2430.                             }
  2431.                         } else {
  2432.                             ret = RETURNVALUE_NOERROR;
  2433.                         }
  2434.                     }
  2435.                 } else if (slotPosition & SLOTP_TWO_HAND) {
  2436.                     if (inventory[CONST_SLOT_LEFT] && inventory[CONST_SLOT_LEFT] != item) {
  2437.                         ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
  2438.                     } else {
  2439.                         ret = RETURNVALUE_NOERROR;
  2440.                     }
  2441.                 } else if (inventory[CONST_SLOT_LEFT]) {
  2442.                     const Item* leftItem = inventory[CONST_SLOT_LEFT];
  2443.                     WeaponType_t type = item->getWeaponType(), leftType = leftItem->getWeaponType();
  2444.  
  2445.                     if (leftItem->getSlotPosition() & SLOTP_TWO_HAND) {
  2446.                         ret = RETURNVALUE_DROPTWOHANDEDITEM;
  2447.                     } else if (item == leftItem && count == item->getItemCount()) {
  2448.                         ret = RETURNVALUE_NOERROR;
  2449.                     } else if (leftType == WEAPON_SHIELD && type == WEAPON_SHIELD) {
  2450.                         ret = RETURNVALUE_CANONLYUSEONESHIELD;
  2451.                     } else if (leftType == WEAPON_NONE || type == WEAPON_NONE ||
  2452.                                leftType == WEAPON_SHIELD || leftType == WEAPON_AMMO
  2453.                                || type == WEAPON_SHIELD || type == WEAPON_AMMO) {
  2454.                         ret = RETURNVALUE_NOERROR;
  2455.                     } else if (type != WEAPON_DISTANCE && type != WEAPON_WAND &&
  2456.                         g_config.getBoolean(ConfigManager::ALLOW_DUAL_WIELDING) &&
  2457.                         vocation->canDualWield()) {
  2458.                         ret = RETURNVALUE_NOERROR;
  2459.                     } else {
  2460.                         ret = RETURNVALUE_CANONLYUSEONEWEAPON;
  2461.                     }
  2462.                 } else {
  2463.                     ret = RETURNVALUE_NOERROR;
  2464.                 }
  2465.             }
  2466.             break;
  2467.         }
  2468.  
  2469.         case CONST_SLOT_LEFT: {
  2470.             if (slotPosition & SLOTP_LEFT) {
  2471.                 if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
  2472.                     WeaponType_t type = item->getWeaponType();
  2473.                     if (type == WEAPON_NONE || type == WEAPON_SHIELD) {
  2474.                         ret = RETURNVALUE_CANNOTBEDRESSED;
  2475.                     } else if (inventory[CONST_SLOT_RIGHT] && (slotPosition & SLOTP_TWO_HAND)) {
  2476.                         ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
  2477.                     } else {
  2478.                         ret = RETURNVALUE_NOERROR;
  2479.                     }
  2480.                 } else if (slotPosition & SLOTP_TWO_HAND) {
  2481.                     if (inventory[CONST_SLOT_RIGHT] && inventory[CONST_SLOT_RIGHT] != item) {
  2482.                         ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
  2483.                     } else {
  2484.                         ret = RETURNVALUE_NOERROR;
  2485.                     }
  2486.                 } else if (inventory[CONST_SLOT_RIGHT]) {
  2487.                     const Item* rightItem = inventory[CONST_SLOT_RIGHT];
  2488.                     WeaponType_t type = item->getWeaponType(), rightType = rightItem->getWeaponType();
  2489.  
  2490.                     if (rightItem->getSlotPosition() & SLOTP_TWO_HAND) {
  2491.                         ret = RETURNVALUE_DROPTWOHANDEDITEM;
  2492.                     } else if (item == rightItem && count == item->getItemCount()) {
  2493.                         ret = RETURNVALUE_NOERROR;
  2494.                     } else if (rightType == WEAPON_SHIELD && type == WEAPON_SHIELD) {
  2495.                         ret = RETURNVALUE_CANONLYUSEONESHIELD;
  2496.                     } else if (rightType == WEAPON_NONE || type == WEAPON_NONE ||
  2497.                                rightType == WEAPON_SHIELD || rightType == WEAPON_AMMO
  2498.                                || type == WEAPON_SHIELD || type == WEAPON_AMMO) {
  2499.                         ret = RETURNVALUE_NOERROR;
  2500.                     } else if (type != WEAPON_DISTANCE && type != WEAPON_WAND &&
  2501.                         g_config.getBoolean(ConfigManager::ALLOW_DUAL_WIELDING) &&
  2502.                         vocation->canDualWield()) {
  2503.                         ret = RETURNVALUE_NOERROR;
  2504.                     } else {
  2505.                         ret = RETURNVALUE_CANONLYUSEONEWEAPON;
  2506.                     }
  2507.                 } else {
  2508.                     ret = RETURNVALUE_NOERROR;
  2509.                 }
  2510.             }
  2511.             break;
  2512.         }
  2513.  
  2514.         case CONST_SLOT_LEGS: {
  2515.             if (slotPosition & SLOTP_LEGS) {
  2516.                 ret = RETURNVALUE_NOERROR;
  2517.             }
  2518.             break;
  2519.         }
  2520.  
  2521.         case CONST_SLOT_FEET: {
  2522.             if (slotPosition & SLOTP_FEET) {
  2523.                 ret = RETURNVALUE_NOERROR;
  2524.             }
  2525.             break;
  2526.         }
  2527.  
  2528.         case CONST_SLOT_RING: {
  2529.             if (slotPosition & SLOTP_RING) {
  2530.                 ret = RETURNVALUE_NOERROR;
  2531.             }
  2532.             break;
  2533.         }
  2534.  
  2535.         case CONST_SLOT_AMMO: {
  2536.             if ((slotPosition & SLOTP_AMMO) || g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
  2537.                 ret = RETURNVALUE_NOERROR;
  2538.             }
  2539.             break;
  2540.         }
  2541.  
  2542.         case CONST_SLOT_WHEREEVER:
  2543.         case -1:
  2544.             ret = RETURNVALUE_NOTENOUGHROOM;
  2545.             break;
  2546.  
  2547.         default:
  2548.             ret = RETURNVALUE_NOTPOSSIBLE;
  2549.             break;
  2550.     }
  2551.  
  2552.  
  2553.     if (ret != RETURNVALUE_NOERROR && ret != RETURNVALUE_NOTENOUGHROOM) {
  2554.         return ret;
  2555.     }
  2556.  
  2557.     //need an exchange with source?
  2558.     const Item* inventoryItem = getInventoryItem(static_cast<slots_t>(index));
  2559.     if (inventoryItem && (!inventoryItem->isStackable() || inventoryItem->getID() != item->getID())) {
  2560.         return RETURNVALUE_NEEDEXCHANGE;
  2561.     }
  2562.  
  2563.     //check if enough capacity
  2564.     if (!hasCapacity(item, count)) {
  2565.         return RETURNVALUE_NOTENOUGHCAPACITY;
  2566.     }
  2567.  
  2568.     if (!g_moveEvents->onPlayerEquip(const_cast<Player*>(this), const_cast<Item*>(item), static_cast<slots_t>(index), true)) {
  2569.         return RETURNVALUE_CANNOTBEDRESSED;
  2570.     }
  2571.  
  2572.     return ret;
  2573. }
  2574.  
  2575. ReturnValue Player::queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount,
  2576.         uint32_t flags) const
  2577. {
  2578.     const Item* item = thing.getItem();
  2579.     if (item == nullptr) {
  2580.         maxQueryCount = 0;
  2581.         return RETURNVALUE_NOTPOSSIBLE;
  2582.     }
  2583.  
  2584.     if (index == INDEX_WHEREEVER) {
  2585.         uint32_t n = 0;
  2586.         for (int32_t slotIndex = CONST_SLOT_FIRST; slotIndex <= CONST_SLOT_LAST; ++slotIndex) {
  2587.             Item* inventoryItem = inventory[slotIndex];
  2588.             if (inventoryItem) {
  2589.                 if (Container* subContainer = inventoryItem->getContainer()) {
  2590.                     uint32_t queryCount = 0;
  2591.                     subContainer->queryMaxCount(INDEX_WHEREEVER, *item, item->getItemCount(), queryCount, flags);
  2592.                     n += queryCount;
  2593.  
  2594.                     //iterate through all items, including sub-containers (deep search)
  2595.                     for (ContainerIterator it = subContainer->iterator(); it.hasNext(); it.advance()) {
  2596.                         if (Container* tmpContainer = (*it)->getContainer()) {
  2597.                             queryCount = 0;
  2598.                             tmpContainer->queryMaxCount(INDEX_WHEREEVER, *item, item->getItemCount(), queryCount, flags);
  2599.                             n += queryCount;
  2600.                         }
  2601.                     }
  2602.                 } else if (inventoryItem->isStackable() && item->equals(inventoryItem) && inventoryItem->getItemCount() < 100) {
  2603.                     uint32_t remainder = (100 - inventoryItem->getItemCount());
  2604.  
  2605.                     if (queryAdd(slotIndex, *item, remainder, flags) == RETURNVALUE_NOERROR) {
  2606.                         n += remainder;
  2607.                     }
  2608.                 }
  2609.             } else if (queryAdd(slotIndex, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) { //empty slot
  2610.                 if (item->isStackable()) {
  2611.                     n += 100;
  2612.                 } else {
  2613.                     ++n;
  2614.                 }
  2615.             }
  2616.         }
  2617.  
  2618.         maxQueryCount = n;
  2619.     } else {
  2620.         const Item* destItem = nullptr;
  2621.  
  2622.         const Thing* destThing = getThing(index);
  2623.         if (destThing) {
  2624.             destItem = destThing->getItem();
  2625.         }
  2626.  
  2627.         if (destItem) {
  2628.             if (destItem->isStackable() && item->equals(destItem) && destItem->getItemCount() < 100) {
  2629.                 maxQueryCount = 100 - destItem->getItemCount();
  2630.             } else {
  2631.                 maxQueryCount = 0;
  2632.             }
  2633.         } else if (queryAdd(index, *item, count, flags) == RETURNVALUE_NOERROR) { //empty slot
  2634.             if (item->isStackable()) {
  2635.                 maxQueryCount = 100;
  2636.             } else {
  2637.                 maxQueryCount = 1;
  2638.             }
  2639.  
  2640.             return RETURNVALUE_NOERROR;
  2641.         }
  2642.     }
  2643.  
  2644.     if (maxQueryCount < count) {
  2645.         return RETURNVALUE_NOTENOUGHROOM;
  2646.     } else {
  2647.         return RETURNVALUE_NOERROR;
  2648.     }
  2649. }
  2650.  
  2651. ReturnValue Player::queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const
  2652. {
  2653.     int32_t index = getThingIndex(&thing);
  2654.     if (index == -1) {
  2655.         return RETURNVALUE_NOTPOSSIBLE;
  2656.     }
  2657.  
  2658.     const Item* item = thing.getItem();
  2659.     if (item == nullptr) {
  2660.         return RETURNVALUE_NOTPOSSIBLE;
  2661.     }
  2662.  
  2663.     if (count == 0 || (item->isStackable() && count > item->getItemCount())) {
  2664.         return RETURNVALUE_NOTPOSSIBLE;
  2665.     }
  2666.  
  2667.     if (!item->isMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags)) {
  2668.         return RETURNVALUE_NOTMOVEABLE;
  2669.     }
  2670.  
  2671.     return RETURNVALUE_NOERROR;
  2672. }
  2673.  
  2674. Cylinder* Player::queryDestination(int32_t& index, const Thing& thing, Item** destItem,
  2675.         uint32_t& flags)
  2676. {
  2677.     if (index == 0 /*drop to capacity window*/ || index == INDEX_WHEREEVER) {
  2678.         *destItem = nullptr;
  2679.  
  2680.         const Item* item = thing.getItem();
  2681.         if (item == nullptr) {
  2682.             return this;
  2683.         }
  2684.  
  2685.         bool autoStack = !((flags & FLAG_IGNOREAUTOSTACK) == FLAG_IGNOREAUTOSTACK);
  2686.         bool isStackable = item->isStackable();
  2687.  
  2688.         std::vector<Container*> containers;
  2689.  
  2690.         for (uint32_t slotIndex = CONST_SLOT_FIRST; slotIndex <= CONST_SLOT_LAST; ++slotIndex) {
  2691.             Item* inventoryItem = inventory[slotIndex];
  2692.             if (inventoryItem) {
  2693.                 if (inventoryItem == tradeItem) {
  2694.                     continue;
  2695.                 }
  2696.  
  2697.                 if (inventoryItem == item) {
  2698.                     continue;
  2699.                 }
  2700.  
  2701.                 if (autoStack && isStackable) {
  2702.                     //try find an already existing item to stack with
  2703.                     if (queryAdd(slotIndex, *item, item->getItemCount(), 0) == RETURNVALUE_NOERROR) {
  2704.                         if (inventoryItem->equals(item) && inventoryItem->getItemCount() < 100) {
  2705.                             index = slotIndex;
  2706.                             *destItem = inventoryItem;
  2707.                             return this;
  2708.                         }
  2709.                     }
  2710.  
  2711.                     if (Container* subContainer = inventoryItem->getContainer()) {
  2712.                         containers.push_back(subContainer);
  2713.                     }
  2714.                 } else if (Container* subContainer = inventoryItem->getContainer()) {
  2715.                     containers.push_back(subContainer);
  2716.                 }
  2717.             } else if (queryAdd(slotIndex, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) { //empty slot
  2718.                 index = slotIndex;
  2719.                 *destItem = nullptr;
  2720.                 return this;
  2721.             }
  2722.         }
  2723.  
  2724.         size_t i = 0;
  2725.         while (i < containers.size()) {
  2726.             Container* tmpContainer = containers[i++];
  2727.             if (!autoStack || !isStackable) {
  2728.                 //we need to find first empty container as fast as we can for non-stackable items
  2729.                 uint32_t n = tmpContainer->capacity() - tmpContainer->size();
  2730.                 while (n) {
  2731.                     if (tmpContainer->queryAdd(tmpContainer->capacity() - n, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) {
  2732.                         index = tmpContainer->capacity() - n;
  2733.                         *destItem = nullptr;
  2734.                         return tmpContainer;
  2735.                     }
  2736.  
  2737.                     n--;
  2738.                 }
  2739.  
  2740.                 for (Item* tmpContainerItem : tmpContainer->getItemList()) {
  2741.                     if (Container* subContainer = tmpContainerItem->getContainer()) {
  2742.                         containers.push_back(subContainer);
  2743.                     }
  2744.                 }
  2745.  
  2746.                 continue;
  2747.             }
  2748.  
  2749.             uint32_t n = 0;
  2750.  
  2751.             for (Item* tmpItem : tmpContainer->getItemList()) {
  2752.                 if (tmpItem == tradeItem) {
  2753.                     continue;
  2754.                 }
  2755.  
  2756.                 if (tmpItem == item) {
  2757.                     continue;
  2758.                 }
  2759.  
  2760.                 //try find an already existing item to stack with
  2761.                 if (tmpItem->equals(item) && tmpItem->getItemCount() < 100) {
  2762.                     index = n;
  2763.                     *destItem = tmpItem;
  2764.                     return tmpContainer;
  2765.                 }
  2766.  
  2767.                 if (Container* subContainer = tmpItem->getContainer()) {
  2768.                     containers.push_back(subContainer);
  2769.                 }
  2770.  
  2771.                 n++;
  2772.             }
  2773.  
  2774.             if (n < tmpContainer->capacity() && tmpContainer->queryAdd(n, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) {
  2775.                 index = n;
  2776.                 *destItem = nullptr;
  2777.                 return tmpContainer;
  2778.             }
  2779.         }
  2780.  
  2781.         return this;
  2782.     }
  2783.  
  2784.     Thing* destThing = getThing(index);
  2785.     if (destThing) {
  2786.         *destItem = destThing->getItem();
  2787.     }
  2788.  
  2789.     Cylinder* subCylinder = dynamic_cast<Cylinder*>(destThing);
  2790.     if (subCylinder) {
  2791.         index = INDEX_WHEREEVER;
  2792.         *destItem = nullptr;
  2793.         return subCylinder;
  2794.     } else {
  2795.         return this;
  2796.     }
  2797. }
  2798.  
  2799. void Player::addThing(int32_t index, Thing* thing)
  2800. {
  2801.     if (index < CONST_SLOT_FIRST || index > CONST_SLOT_LAST) {
  2802.         return /*RETURNVALUE_NOTPOSSIBLE*/;
  2803.     }
  2804.  
  2805.     Item* item = thing->getItem();
  2806.     if (!item) {
  2807.         return /*RETURNVALUE_NOTPOSSIBLE*/;
  2808.     }
  2809.  
  2810.     item->setParent(this);
  2811.     inventory[index] = item;
  2812.  
  2813.     //send to client
  2814.     sendInventoryItem(static_cast<slots_t>(index), item);
  2815. }
  2816.  
  2817. void Player::updateThing(Thing* thing, uint16_t itemId, uint32_t count)
  2818. {
  2819.     int32_t index = getThingIndex(thing);
  2820.     if (index == -1) {
  2821.         return /*RETURNVALUE_NOTPOSSIBLE*/;
  2822.     }
  2823.  
  2824.     Item* item = thing->getItem();
  2825.     if (!item) {
  2826.         return /*RETURNVALUE_NOTPOSSIBLE*/;
  2827.     }
  2828.  
  2829.     item->setID(itemId);
  2830.     item->setSubType(count);
  2831.  
  2832.     //send to client
  2833.     sendInventoryItem(static_cast<slots_t>(index), item);
  2834.  
  2835.     //event methods
  2836.     onUpdateInventoryItem(item, item);
  2837. }
  2838.  
  2839. void Player::replaceThing(uint32_t index, Thing* thing)
  2840. {
  2841.     if (index > CONST_SLOT_LAST) {
  2842.         return /*RETURNVALUE_NOTPOSSIBLE*/;
  2843.     }
  2844.  
  2845.     Item* oldItem = getInventoryItem(static_cast<slots_t>(index));
  2846.     if (!oldItem) {
  2847.         return /*RETURNVALUE_NOTPOSSIBLE*/;
  2848.     }
  2849.  
  2850.     Item* item = thing->getItem();
  2851.     if (!item) {
  2852.         return /*RETURNVALUE_NOTPOSSIBLE*/;
  2853.     }
  2854.  
  2855.     //send to client
  2856.     sendInventoryItem(static_cast<slots_t>(index), item);
  2857.  
  2858.     //event methods
  2859.     onUpdateInventoryItem(oldItem, item);
  2860.  
  2861.     item->setParent(this);
  2862.  
  2863.     inventory[index] = item;
  2864. }
  2865.  
  2866. void Player::removeThing(Thing* thing, uint32_t count)
  2867. {
  2868.     Item* item = thing->getItem();
  2869.     if (!item) {
  2870.         return /*RETURNVALUE_NOTPOSSIBLE*/;
  2871.     }
  2872.  
  2873.     int32_t index = getThingIndex(thing);
  2874.     if (index == -1) {
  2875.         return /*RETURNVALUE_NOTPOSSIBLE*/;
  2876.     }
  2877.  
  2878.     if (item->isStackable()) {
  2879.         if (count == item->getItemCount()) {
  2880.             //send change to client
  2881.             sendInventoryItem(static_cast<slots_t>(index), nullptr);
  2882.  
  2883.             //event methods
  2884.             onRemoveInventoryItem(item);
  2885.  
  2886.             item->setParent(nullptr);
  2887.             inventory[index] = nullptr;
  2888.         } else {
  2889.             uint8_t newCount = static_cast<uint8_t>(std::max<int32_t>(0, item->getItemCount() - count));
  2890.             item->setItemCount(newCount);
  2891.  
  2892.             //send change to client
  2893.             sendInventoryItem(static_cast<slots_t>(index), item);
  2894.  
  2895.             //event methods
  2896.             onUpdateInventoryItem(item, item);
  2897.         }
  2898.     } else {
  2899.         //send change to client
  2900.         sendInventoryItem(static_cast<slots_t>(index), nullptr);
  2901.  
  2902.         //event methods
  2903.         onRemoveInventoryItem(item);
  2904.  
  2905.         item->setParent(nullptr);
  2906.         inventory[index] = nullptr;
  2907.     }
  2908. }
  2909.  
  2910. int32_t Player::getThingIndex(const Thing* thing) const
  2911. {
  2912.     for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
  2913.         if (inventory[i] == thing) {
  2914.             return i;
  2915.         }
  2916.     }
  2917.     return -1;
  2918. }
  2919.  
  2920. size_t Player::getFirstIndex() const
  2921. {
  2922.     return CONST_SLOT_FIRST;
  2923. }
  2924.  
  2925. size_t Player::getLastIndex() const
  2926. {
  2927.     return CONST_SLOT_LAST + 1;
  2928. }
  2929.  
  2930. uint32_t Player::getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) const
  2931. {
  2932.     uint32_t count = 0;
  2933.     for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
  2934.         Item* item = inventory[i];
  2935.         if (!item) {
  2936.             continue;
  2937.         }
  2938.  
  2939.         if (item->getID() == itemId) {
  2940.             count += Item::countByType(item, subType);
  2941.         }
  2942.  
  2943.         if (Container* container = item->getContainer()) {
  2944.             for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
  2945.                 if ((*it)->getID() == itemId) {
  2946.                     count += Item::countByType(*it, subType);
  2947.                 }
  2948.             }
  2949.         }
  2950.     }
  2951.     return count;
  2952. }
  2953.  
  2954. bool Player::removeItemOfType(uint16_t itemId, uint32_t amount, int32_t subType, bool ignoreEquipped/* = false*/) const
  2955. {
  2956.     if (amount == 0) {
  2957.         return true;
  2958.     }
  2959.  
  2960.     std::vector<Item*> itemList;
  2961.  
  2962.     uint32_t count = 0;
  2963.     for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
  2964.         Item* item = inventory[i];
  2965.         if (!item) {
  2966.             continue;
  2967.         }
  2968.  
  2969.         if (!ignoreEquipped && item->getID() == itemId) {
  2970.             uint32_t itemCount = Item::countByType(item, subType);
  2971.             if (itemCount == 0) {
  2972.                 continue;
  2973.             }
  2974.  
  2975.             itemList.push_back(item);
  2976.  
  2977.             count += itemCount;
  2978.             if (count >= amount) {
  2979.                 g_game.internalRemoveItems(std::move(itemList), amount, Item::items[itemId].stackable);
  2980.                 return true;
  2981.             }
  2982.         } else if (Container* container = item->getContainer()) {
  2983.             for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
  2984.                 Item* containerItem = *it;
  2985.                 if (containerItem->getID() == itemId) {
  2986.                     uint32_t itemCount = Item::countByType(containerItem, subType);
  2987.                     if (itemCount == 0) {
  2988.                         continue;
  2989.                     }
  2990.  
  2991.                     itemList.push_back(containerItem);
  2992.  
  2993.                     count += itemCount;
  2994.                     if (count >= amount) {
  2995.                         g_game.internalRemoveItems(std::move(itemList), amount, Item::items[itemId].stackable);
  2996.                         return true;
  2997.                     }
  2998.                 }
  2999.             }
  3000.         }
  3001.     }
  3002.     return false;
  3003. }
  3004.  
  3005. std::map<uint32_t, uint32_t>& Player::getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const
  3006. {
  3007.     for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
  3008.         Item* item = inventory[i];
  3009.         if (!item) {
  3010.             continue;
  3011.         }
  3012.  
  3013.         countMap[item->getID()] += Item::countByType(item, -1);
  3014.  
  3015.         if (Container* container = item->getContainer()) {
  3016.             for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
  3017.                 countMap[(*it)->getID()] += Item::countByType(*it, -1);
  3018.             }
  3019.         }
  3020.     }
  3021.     return countMap;
  3022. }
  3023.  
  3024. Thing* Player::getThing(size_t index) const
  3025. {
  3026.     if (index >= CONST_SLOT_FIRST && index <= CONST_SLOT_LAST) {
  3027.         return inventory[index];
  3028.     }
  3029.     return nullptr;
  3030. }
  3031.  
  3032. void Player::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link /*= LINK_OWNER*/)
  3033. {
  3034.     if (link == LINK_OWNER) {
  3035.         //calling movement scripts
  3036.         g_moveEvents->onPlayerEquip(this, thing->getItem(), static_cast<slots_t>(index), false);
  3037.     }
  3038.  
  3039.     bool requireListUpdate = false;
  3040.  
  3041.     if (link == LINK_OWNER || link == LINK_TOPPARENT) {
  3042.         const Item* i = (oldParent ? oldParent->getItem() : nullptr);
  3043.  
  3044.         // Check if we owned the old container too, so we don't need to do anything,
  3045.         // as the list was updated in postRemoveNotification
  3046.         assert(i ? i->getContainer() != nullptr : true);
  3047.  
  3048.         if (i) {
  3049.             requireListUpdate = i->getContainer()->getHoldingPlayer() != this;
  3050.         } else {
  3051.             requireListUpdate = oldParent != this;
  3052.         }
  3053.  
  3054.         updateInventoryWeight();
  3055.         updateItemsLight();
  3056.         sendStats();
  3057.     }
  3058.  
  3059.     if (const Item* item = thing->getItem()) {
  3060.         if (const Container* container = item->getContainer()) {
  3061.             onSendContainer(container);
  3062.         }
  3063.  
  3064.         if (shopOwner && requireListUpdate) {
  3065.             updateSaleShopList(item);
  3066.         }
  3067.     } else if (const Creature* creature = thing->getCreature()) {
  3068.         if (creature == this) {
  3069.             //check containers
  3070.             std::vector<Container*> containers;
  3071.  
  3072.             for (const auto& it : openContainers) {
  3073.                 Container* container = it.second.container;
  3074.                 if (!Position::areInRange<1, 1, 0>(container->getPosition(), getPosition())) {
  3075.                     containers.push_back(container);
  3076.                 }
  3077.             }
  3078.  
  3079.             for (const Container* container : containers) {
  3080.                 autoCloseContainers(container);
  3081.             }
  3082.         }
  3083.     }
  3084. }
  3085.  
  3086. void Player::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link /*= LINK_OWNER*/)
  3087. {
  3088.     if (link == LINK_OWNER) {
  3089.         //calling movement scripts
  3090.         g_moveEvents->onPlayerDeEquip(this, thing->getItem(), static_cast<slots_t>(index));
  3091.     }
  3092.  
  3093.     bool requireListUpdate = false;
  3094.  
  3095.     if (link == LINK_OWNER || link == LINK_TOPPARENT) {
  3096.         const Item* i = (newParent ? newParent->getItem() : nullptr);
  3097.  
  3098.         // Check if we owned the old container too, so we don't need to do anything,
  3099.         // as the list was updated in postRemoveNotification
  3100.         assert(i ? i->getContainer() != nullptr : true);
  3101.  
  3102.         if (i) {
  3103.             requireListUpdate = i->getContainer()->getHoldingPlayer() != this;
  3104.         } else {
  3105.             requireListUpdate = newParent != this;
  3106.         }
  3107.  
  3108.         updateInventoryWeight();
  3109.         updateItemsLight();
  3110.         sendStats();
  3111.     }
  3112.  
  3113.     if (const Item* item = thing->getItem()) {
  3114.         if (const Container* container = item->getContainer()) {
  3115.             if (container->isRemoved() || !Position::areInRange<1, 1, 0>(getPosition(), container->getPosition())) {
  3116.                 autoCloseContainers(container);
  3117.             } else if (container->getTopParent() == this) {
  3118.                 onSendContainer(container);
  3119.             } else if (const Container* topContainer = dynamic_cast<const Container*>(container->getTopParent())) {
  3120.                 if (const DepotChest* depotChest = dynamic_cast<const DepotChest*>(topContainer)) {
  3121.                     bool isOwner = false;
  3122.  
  3123.                     for (const auto& it : depotChests) {
  3124.                         if (it.second == depotChest) {
  3125.                             isOwner = true;
  3126.                             onSendContainer(container);
  3127.                         }
  3128.                     }
  3129.  
  3130.                     if (!isOwner) {
  3131.                         autoCloseContainers(container);
  3132.                     }
  3133.                 } else {
  3134.                     onSendContainer(container);
  3135.                 }
  3136.             } else {
  3137.                 autoCloseContainers(container);
  3138.             }
  3139.         }
  3140.  
  3141.         if (shopOwner && requireListUpdate) {
  3142.             updateSaleShopList(item);
  3143.         }
  3144.     }
  3145. }
  3146.  
  3147. bool Player::updateSaleShopList(const Item* item)
  3148. {
  3149.     uint16_t itemId = item->getID();
  3150.     if (itemId != ITEM_GOLD_COIN && itemId != ITEM_PLATINUM_COIN && itemId != ITEM_CRYSTAL_COIN) {
  3151.         auto it = std::find_if(shopItemList.begin(), shopItemList.end(), [itemId](const ShopInfo& shopInfo) { return shopInfo.itemId == itemId && shopInfo.sellPrice != 0; });
  3152.         if (it == shopItemList.end()) {
  3153.             const Container* container = item->getContainer();
  3154.             if (!container) {
  3155.                 return false;
  3156.             }
  3157.  
  3158.             const auto& items = container->getItemList();
  3159.             return std::any_of(items.begin(), items.end(), [this](const Item* containerItem) {
  3160.                 return updateSaleShopList(containerItem);
  3161.             });
  3162.         }
  3163.     }
  3164.  
  3165.     if (client) {
  3166.         client->sendSaleItemList(shopItemList);
  3167.     }
  3168.     return true;
  3169. }
  3170.  
  3171. bool Player::hasShopItemForSale(uint32_t itemId, uint8_t subType) const
  3172. {
  3173.     const ItemType& itemType = Item::items[itemId];
  3174.     return std::any_of(shopItemList.begin(), shopItemList.end(), [&](const ShopInfo& shopInfo) {
  3175.         return shopInfo.itemId == itemId && shopInfo.buyPrice != 0 && (!itemType.isFluidContainer() || shopInfo.subType == subType);
  3176.     });
  3177. }
  3178.  
  3179. void Player::internalAddThing(Thing* thing)
  3180. {
  3181.     internalAddThing(0, thing);
  3182. }
  3183.  
  3184. void Player::internalAddThing(uint32_t index, Thing* thing)
  3185. {
  3186.     Item* item = thing->getItem();
  3187.     if (!item) {
  3188.         return;
  3189.     }
  3190.  
  3191.     //index == 0 means we should equip this item at the most appropiate slot (no action required here)
  3192.     if (index > 0 && index < 11) {
  3193.         if (inventory[index]) {
  3194.             return;
  3195.         }
  3196.  
  3197.         inventory[index] = item;
  3198.         item->setParent(this);
  3199.     }
  3200. }
  3201.  
  3202. bool Player::setFollowCreature(Creature* creature)
  3203. {
  3204.     if (!Creature::setFollowCreature(creature)) {
  3205.         setFollowCreature(nullptr);
  3206.         setAttackedCreature(nullptr);
  3207.  
  3208.         sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  3209.         sendCancelTarget();
  3210.         stopWalk();
  3211.         return false;
  3212.     }
  3213.     return true;
  3214. }
  3215.  
  3216. bool Player::setAttackedCreature(Creature* creature)
  3217. {
  3218.     if (!Creature::setAttackedCreature(creature)) {
  3219.         sendCancelTarget();
  3220.         return false;
  3221.     }
  3222.  
  3223.     if (chaseMode && creature) {
  3224.         if (followCreature != creature) {
  3225.             //chase opponent
  3226.             setFollowCreature(creature);
  3227.         }
  3228.     } else if (followCreature) {
  3229.         setFollowCreature(nullptr);
  3230.     }
  3231.  
  3232.     if (creature) {
  3233.         g_dispatcher.addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game, getID())));
  3234.     }
  3235.     return true;
  3236. }
  3237.  
  3238. void Player::goToFollowCreature()
  3239. {
  3240.     if (!walkTask) {
  3241.         if ((OTSYS_TIME() - lastFailedFollow) < 2000) {
  3242.             return;
  3243.         }
  3244.  
  3245.         Creature::goToFollowCreature();
  3246.  
  3247.         if (followCreature && !hasFollowPath) {
  3248.             lastFailedFollow = OTSYS_TIME();
  3249.         }
  3250.     }
  3251. }
  3252.  
  3253. void Player::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const
  3254. {
  3255.     Creature::getPathSearchParams(creature, fpp);
  3256.     fpp.fullPathSearch = true;
  3257. }
  3258.  
  3259. void Player::doAttacking(uint32_t)
  3260. {
  3261.     if (lastAttack == 0) {
  3262.         lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
  3263.     }
  3264.  
  3265.     if (hasCondition(CONDITION_PACIFIED)) {
  3266.         return;
  3267.     }
  3268.  
  3269.     if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
  3270.         bool result = false;
  3271.  
  3272.         Item* tool = getWeapon();
  3273.         const Weapon* weapon = g_weapons->getWeapon(tool);
  3274.         uint32_t delay = getAttackSpeed();
  3275.         bool classicSpeed = g_config.getBoolean(ConfigManager::CLASSIC_ATTACK_SPEED);
  3276.  
  3277.         if (weapon) {
  3278.             if (!weapon->interruptSwing()) {
  3279.                 result = weapon->useWeapon(this, tool, attackedCreature);
  3280.             } else if (!classicSpeed && !canDoAction()) {
  3281.                 delay = getNextActionTime();
  3282.             } else {
  3283.                 result = weapon->useWeapon(this, tool, attackedCreature);
  3284.             }
  3285.         } else {
  3286.             result = Weapon::useFist(this, attackedCreature);
  3287.         }
  3288.  
  3289.         SchedulerTask* task = createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID()));
  3290.         if (!classicSpeed) {
  3291.             setNextActionTask(task);
  3292.         } else {
  3293.             g_scheduler.addEvent(task);
  3294.         }
  3295.  
  3296.         if (result) {
  3297.             lastAttack = OTSYS_TIME();
  3298.         }
  3299.     }
  3300. }
  3301.  
  3302. uint64_t Player::getGainedExperience(Creature* attacker) const
  3303. {
  3304.     if (g_config.getBoolean(ConfigManager::EXPERIENCE_FROM_PLAYERS)) {
  3305.         Player* attackerPlayer = attacker->getPlayer();
  3306.         if (attackerPlayer && attackerPlayer != this && skillLoss && std::abs(static_cast<int32_t>(attackerPlayer->getLevel() - level)) <= g_config.getNumber(ConfigManager::EXP_FROM_PLAYERS_LEVEL_RANGE)) {
  3307.             return std::max<uint64_t>(0, std::floor(getLostExperience() * getDamageRatio(attacker) * 0.75));
  3308.         }
  3309.     }
  3310.     return 0;
  3311. }
  3312.  
  3313. void Player::onFollowCreature(const Creature* creature)
  3314. {
  3315.     if (!creature) {
  3316.         stopWalk();
  3317.     }
  3318. }
  3319.  
  3320. void Player::setChaseMode(bool mode)
  3321. {
  3322.     bool prevChaseMode = chaseMode;
  3323.     chaseMode = mode;
  3324.  
  3325.     if (prevChaseMode != chaseMode) {
  3326.         if (chaseMode) {
  3327.             if (!followCreature && attackedCreature) {
  3328.                 //chase opponent
  3329.                 setFollowCreature(attackedCreature);
  3330.             }
  3331.         } else if (attackedCreature) {
  3332.             setFollowCreature(nullptr);
  3333.             cancelNextWalk = true;
  3334.         }
  3335.     }
  3336. }
  3337.  
  3338. void Player::onWalkAborted()
  3339. {
  3340.     setNextWalkActionTask(nullptr);
  3341.     sendCancelWalk();
  3342. }
  3343.  
  3344. void Player::onWalkComplete()
  3345. {
  3346.     if (walkTask) {
  3347.         walkTaskEvent = g_scheduler.addEvent(walkTask);
  3348.         walkTask = nullptr;
  3349.     }
  3350. }
  3351.  
  3352. void Player::stopWalk()
  3353. {
  3354.     cancelNextWalk = true;
  3355. }
  3356.  
  3357. LightInfo Player::getCreatureLight() const
  3358. {
  3359.     if (internalLight.level > itemsLight.level) {
  3360.         return internalLight;
  3361.     }
  3362.     return itemsLight;
  3363. }
  3364.  
  3365. void Player::updateItemsLight(bool internal /*=false*/)
  3366. {
  3367.     LightInfo maxLight;
  3368.  
  3369.     for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
  3370.         Item* item = inventory[i];
  3371.         if (item) {
  3372.             LightInfo curLight = item->getLightInfo();
  3373.  
  3374.             if (curLight.level > maxLight.level) {
  3375.                 maxLight = std::move(curLight);
  3376.             }
  3377.         }
  3378.     }
  3379.  
  3380.     if (itemsLight.level != maxLight.level || itemsLight.color != maxLight.color) {
  3381.         itemsLight = maxLight;
  3382.  
  3383.         if (!internal) {
  3384.             g_game.changeLight(this);
  3385.         }
  3386.     }
  3387. }
  3388.  
  3389. void Player::onAddCondition(ConditionType_t type)
  3390. {
  3391.     Creature::onAddCondition(type);
  3392.  
  3393.     if (type == CONDITION_OUTFIT && isMounted()) {
  3394.         dismount();
  3395.     }
  3396.  
  3397.     sendIcons();
  3398. }
  3399.  
  3400. void Player::onAddCombatCondition(ConditionType_t type)
  3401. {
  3402.     switch (type) {
  3403.         case CONDITION_POISON:
  3404.             sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are poisoned.");
  3405.             break;
  3406.  
  3407.         case CONDITION_DROWN:
  3408.             sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are drowning.");
  3409.             break;
  3410.  
  3411.         case CONDITION_PARALYZE:
  3412.             sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are paralyzed.");
  3413.             break;
  3414.  
  3415.         case CONDITION_DRUNK:
  3416.             sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are drunk.");
  3417.             break;
  3418.  
  3419.         case CONDITION_CURSED:
  3420.             sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are cursed.");
  3421.             break;
  3422.  
  3423.         case CONDITION_FREEZING:
  3424.             sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are freezing.");
  3425.             break;
  3426.  
  3427.         case CONDITION_DAZZLED:
  3428.             sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are dazzled.");
  3429.             break;
  3430.  
  3431.         case CONDITION_BLEEDING:
  3432.             sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are bleeding.");
  3433.             break;
  3434.  
  3435.         default:
  3436.             break;
  3437.     }
  3438. }
  3439.  
  3440. void Player::onEndCondition(ConditionType_t type)
  3441. {
  3442.     Creature::onEndCondition(type);
  3443.  
  3444.     if (type == CONDITION_INFIGHT) {
  3445.         onIdleStatus();
  3446.         pzLocked = false;
  3447.         clearAttacked();
  3448.  
  3449.         if (getSkull() != SKULL_RED && getSkull() != SKULL_BLACK) {
  3450.             setSkull(SKULL_NONE);
  3451.         }
  3452.     }
  3453.  
  3454.     sendIcons();
  3455. }
  3456.  
  3457. void Player::onCombatRemoveCondition(Condition* condition)
  3458. {
  3459.     //Creature::onCombatRemoveCondition(condition);
  3460.     if (condition->getId() > 0) {
  3461.         //Means the condition is from an item, id == slot
  3462.         if (g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) {
  3463.             Item* item = getInventoryItem(static_cast<slots_t>(condition->getId()));
  3464.             if (item) {
  3465.                 //25% chance to destroy the item
  3466.                 if (25 >= uniform_random(1, 100)) {
  3467.                     g_game.internalRemoveItem(item);
  3468.                 }
  3469.             }
  3470.         }
  3471.     } else {
  3472.         if (!canDoAction()) {
  3473.             const uint32_t delay = getNextActionTime();
  3474.             const int32_t ticks = delay - (delay % EVENT_CREATURE_THINK_INTERVAL);
  3475.             if (ticks < 0) {
  3476.                 removeCondition(condition);
  3477.             } else {
  3478.                 condition->setTicks(ticks);
  3479.             }
  3480.         } else {
  3481.             removeCondition(condition);
  3482.         }
  3483.     }
  3484. }
  3485.  
  3486. void Player::onAttackedCreature(Creature* target)
  3487. {
  3488.     Creature::onAttackedCreature(target);
  3489.  
  3490.     if (target->getZone() == ZONE_PVP) {
  3491.         return;
  3492.     }
  3493.  
  3494.     if (target == this) {
  3495.         addInFightTicks();
  3496.         return;
  3497.     }
  3498.  
  3499.     if (hasFlag(PlayerFlag_NotGainInFight)) {
  3500.         return;
  3501.     }
  3502.  
  3503.     Player* targetPlayer = target->getPlayer();
  3504.     if (targetPlayer && !isPartner(targetPlayer) && !isGuildMate(targetPlayer)) {
  3505.         if (!pzLocked && g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) {
  3506.             pzLocked = true;
  3507.             sendIcons();
  3508.         }
  3509.  
  3510.         if (getSkull() == SKULL_NONE && getSkullClient(targetPlayer) == SKULL_YELLOW) {
  3511.             addAttacked(targetPlayer);
  3512.             targetPlayer->sendCreatureSkull(this);
  3513.         } else if (!targetPlayer->hasAttacked(this)) {
  3514.             if (!pzLocked) {
  3515.                 pzLocked = true;
  3516.                 sendIcons();
  3517.             }
  3518.  
  3519.             if (!Combat::isInPvpZone(this, targetPlayer) && !isInWar(targetPlayer)) {
  3520.                 addAttacked(targetPlayer);
  3521.  
  3522.                 if (targetPlayer->getSkull() == SKULL_NONE && getSkull() == SKULL_NONE) {
  3523.                     setSkull(SKULL_WHITE);
  3524.                 }
  3525.  
  3526.                 if (getSkull() == SKULL_NONE) {
  3527.                     targetPlayer->sendCreatureSkull(this);
  3528.                 }
  3529.             }
  3530.         }
  3531.     }
  3532.  
  3533.     addInFightTicks();
  3534. }
  3535.  
  3536. void Player::onAttacked()
  3537. {
  3538.     Creature::onAttacked();
  3539.  
  3540.     addInFightTicks();
  3541. }
  3542.  
  3543. void Player::onIdleStatus()
  3544. {
  3545.     Creature::onIdleStatus();
  3546.  
  3547.     if (party) {
  3548.         party->clearPlayerPoints(this);
  3549.     }
  3550. }
  3551.  
  3552. void Player::onPlacedCreature()
  3553. {
  3554.     //scripting event - onLogin
  3555.     if (!g_creatureEvents->playerLogin(this)) {
  3556.         kickPlayer(true);
  3557.     }
  3558. }
  3559.  
  3560. void Player::onAttackedCreatureDrainHealth(Creature* target, int32_t points)
  3561. {
  3562.     Creature::onAttackedCreatureDrainHealth(target, points);
  3563.  
  3564.     if (target) {
  3565.         if (party && !Combat::isPlayerCombat(target)) {
  3566.             Monster* tmpMonster = target->getMonster();
  3567.             if (tmpMonster && tmpMonster->isHostile()) {
  3568.                 //We have fulfilled a requirement for shared experience
  3569.                 party->updatePlayerTicks(this, points);
  3570.             }
  3571.         }
  3572.     }
  3573. }
  3574.  
  3575. void Player::onTargetCreatureGainHealth(Creature* target, int32_t points)
  3576. {
  3577.     if (target && party) {
  3578.         Player* tmpPlayer = nullptr;
  3579.  
  3580.         if (target->getPlayer()) {
  3581.             tmpPlayer = target->getPlayer();
  3582.         } else if (Creature* targetMaster = target->getMaster()) {
  3583.             if (Player* targetMasterPlayer = targetMaster->getPlayer()) {
  3584.                 tmpPlayer = targetMasterPlayer;
  3585.             }
  3586.         }
  3587.  
  3588.         if (isPartner(tmpPlayer)) {
  3589.             party->updatePlayerTicks(this, points);
  3590.         }
  3591.     }
  3592. }
  3593.  
  3594. bool Player::onKilledCreature(Creature* target, bool lastHit/* = true*/)
  3595. {
  3596.     bool unjustified = false;
  3597.  
  3598.     if (hasFlag(PlayerFlag_NotGenerateLoot)) {
  3599.         target->setDropLoot(false);
  3600.     }
  3601.  
  3602.     Creature::onKilledCreature(target, lastHit);
  3603.  
  3604.     Player* targetPlayer = target->getPlayer();
  3605.     if (!targetPlayer) {
  3606.         return false;
  3607.     }
  3608.  
  3609.     if (targetPlayer->getZone() == ZONE_PVP) {
  3610.         targetPlayer->setDropLoot(false);
  3611.         targetPlayer->setSkillLoss(false);
  3612.     } else if (!hasFlag(PlayerFlag_NotGainInFight) && !isPartner(targetPlayer)) {
  3613.         if (!Combat::isInPvpZone(this, targetPlayer) && hasAttacked(targetPlayer) && !targetPlayer->hasAttacked(this) && !isGuildMate(targetPlayer) && targetPlayer != this) {
  3614.             if (targetPlayer->getSkull() == SKULL_NONE && !isInWar(targetPlayer)) {
  3615.                 unjustified = true;
  3616.                 addUnjustifiedDead(targetPlayer);
  3617.             }
  3618.  
  3619.             if (lastHit && hasCondition(CONDITION_INFIGHT)) {
  3620.                 pzLocked = true;
  3621.                 Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_config.getNumber(ConfigManager::WHITE_SKULL_TIME), 0);
  3622.                 addCondition(condition);
  3623.             }
  3624.         }
  3625.     }
  3626.  
  3627.     return unjustified;
  3628. }
  3629.  
  3630. void Player::gainExperience(uint64_t gainExp, Creature* source)
  3631. {
  3632.     if (hasFlag(PlayerFlag_NotGainExperience) || gainExp == 0 || staminaMinutes == 0) {
  3633.         return;
  3634.     }
  3635.  
  3636.     addExperience(source, gainExp, true);
  3637. }
  3638.  
  3639. void Player::onGainExperience(uint64_t gainExp, Creature* target)
  3640. {
  3641.     if (hasFlag(PlayerFlag_NotGainExperience)) {
  3642.         return;
  3643.     }
  3644.  
  3645.     if (target && !target->getPlayer() && party && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
  3646.         party->shareExperience(gainExp, target);
  3647.         //We will get a share of the experience through the sharing mechanism
  3648.         return;
  3649.     }
  3650.  
  3651.     Creature::onGainExperience(gainExp, target);
  3652.     gainExperience(gainExp, target);
  3653. }
  3654.  
  3655. void Player::onGainSharedExperience(uint64_t gainExp, Creature* source)
  3656. {
  3657.     gainExperience(gainExp, source);
  3658. }
  3659.  
  3660. bool Player::isImmune(CombatType_t type) const
  3661. {
  3662.     if (hasFlag(PlayerFlag_CannotBeAttacked)) {
  3663.         return true;
  3664.     }
  3665.     return Creature::isImmune(type);
  3666. }
  3667.  
  3668. bool Player::isImmune(ConditionType_t type) const
  3669. {
  3670.     if (hasFlag(PlayerFlag_CannotBeAttacked)) {
  3671.         return true;
  3672.     }
  3673.     return Creature::isImmune(type);
  3674. }
  3675.  
  3676. bool Player::isAttackable() const
  3677. {
  3678.     return !hasFlag(PlayerFlag_CannotBeAttacked);
  3679. }
  3680.  
  3681. bool Player::lastHitIsPlayer(Creature* lastHitCreature)
  3682. {
  3683.     if (!lastHitCreature) {
  3684.         return false;
  3685.     }
  3686.  
  3687.     if (lastHitCreature->getPlayer()) {
  3688.         return true;
  3689.     }
  3690.  
  3691.     Creature* lastHitMaster = lastHitCreature->getMaster();
  3692.     return lastHitMaster && lastHitMaster->getPlayer();
  3693. }
  3694.  
  3695. void Player::changeHealth(int32_t healthChange, bool sendHealthChange/* = true*/)
  3696. {
  3697.     Creature::changeHealth(healthChange, sendHealthChange);
  3698.     sendStats();
  3699. }
  3700.  
  3701. void Player::changeMana(int32_t manaChange)
  3702. {
  3703.     if (!hasFlag(PlayerFlag_HasInfiniteMana)) {
  3704.         if (manaChange > 0) {
  3705.             mana += std::min<int32_t>(manaChange, getMaxMana() - mana);
  3706.         } else {
  3707.             mana = std::max<int32_t>(0, mana + manaChange);
  3708.         }
  3709.     }
  3710.  
  3711.     sendStats();
  3712. }
  3713.  
  3714. void Player::changeSoul(int32_t soulChange)
  3715. {
  3716.     if (soulChange > 0) {
  3717.         soul += std::min<int32_t>(soulChange, vocation->getSoulMax() - soul);
  3718.     } else {
  3719.         soul = std::max<int32_t>(0, soul + soulChange);
  3720.     }
  3721.  
  3722.     sendStats();
  3723. }
  3724.  
  3725. bool Player::canWear(uint32_t lookType, uint8_t addons) const
  3726. {
  3727.     if (group->access) {
  3728.         return true;
  3729.     }
  3730.  
  3731.     const Outfit* outfit = Outfits::getInstance().getOutfitByLookType(sex, lookType);
  3732.     if (!outfit) {
  3733.         return false;
  3734.     }
  3735.  
  3736.     if (outfit->premium && !isPremium()) {
  3737.         return false;
  3738.     }
  3739.  
  3740.     if (outfit->unlocked && addons == 0) {
  3741.         return true;
  3742.     }
  3743.  
  3744.     for (const OutfitEntry& outfitEntry : outfits) {
  3745.         if (outfitEntry.lookType != lookType) {
  3746.             continue;
  3747.         }
  3748.         return (outfitEntry.addons & addons) == addons;
  3749.     }
  3750.     return false;
  3751. }
  3752.  
  3753. bool Player::canLogout()
  3754. {
  3755.     if (isConnecting) {
  3756.         return false;
  3757.     }
  3758.  
  3759.     if (getTile()->hasFlag(TILESTATE_NOLOGOUT)) {
  3760.         return false;
  3761.     }
  3762.  
  3763.     if (getTile()->hasFlag(TILESTATE_PROTECTIONZONE)) {
  3764.         return true;
  3765.     }
  3766.  
  3767.     return !isPzLocked() && !hasCondition(CONDITION_INFIGHT);
  3768. }
  3769.  
  3770. void Player::genReservedStorageRange()
  3771. {
  3772.     //generate outfits range
  3773.     uint32_t base_key = PSTRG_OUTFITS_RANGE_START;
  3774.     for (const OutfitEntry& entry : outfits) {
  3775.         storageMap[++base_key] = (entry.lookType << 16) | entry.addons;
  3776.     }
  3777. }
  3778.  
  3779. void Player::addOutfit(uint16_t lookType, uint8_t addons)
  3780. {
  3781.     for (OutfitEntry& outfitEntry : outfits) {
  3782.         if (outfitEntry.lookType == lookType) {
  3783.             outfitEntry.addons |= addons;
  3784.             return;
  3785.         }
  3786.     }
  3787.     outfits.emplace_back(lookType, addons);
  3788. }
  3789.  
  3790. bool Player::removeOutfit(uint16_t lookType)
  3791. {
  3792.     for (auto it = outfits.begin(), end = outfits.end(); it != end; ++it) {
  3793.         OutfitEntry& entry = *it;
  3794.         if (entry.lookType == lookType) {
  3795.             outfits.erase(it);
  3796.             return true;
  3797.         }
  3798.     }
  3799.     return false;
  3800. }
  3801.  
  3802. bool Player::removeOutfitAddon(uint16_t lookType, uint8_t addons)
  3803. {
  3804.     for (OutfitEntry& outfitEntry : outfits) {
  3805.         if (outfitEntry.lookType == lookType) {
  3806.             outfitEntry.addons &= ~addons;
  3807.             return true;
  3808.         }
  3809.     }
  3810.     return false;
  3811. }
  3812.  
  3813. bool Player::getOutfitAddons(const Outfit& outfit, uint8_t& addons) const
  3814. {
  3815.     if (group->access) {
  3816.         addons = 3;
  3817.         return true;
  3818.     }
  3819.  
  3820.     if (outfit.premium && !isPremium()) {
  3821.         return false;
  3822.     }
  3823.  
  3824.     for (const OutfitEntry& outfitEntry : outfits) {
  3825.         if (outfitEntry.lookType != outfit.lookType) {
  3826.             continue;
  3827.         }
  3828.  
  3829.         addons = outfitEntry.addons;
  3830.         return true;
  3831.     }
  3832.  
  3833.     if (!outfit.unlocked) {
  3834.         return false;
  3835.     }
  3836.  
  3837.     addons = 0;
  3838.     return true;
  3839. }
  3840.  
  3841. void Player::setSex(PlayerSex_t newSex)
  3842. {
  3843.     sex = newSex;
  3844. }
  3845.  
  3846. Skulls_t Player::getSkull() const
  3847. {
  3848.     if (hasFlag(PlayerFlag_NotGainInFight)) {
  3849.         return SKULL_NONE;
  3850.     }
  3851.     return skull;
  3852. }
  3853.  
  3854. Skulls_t Player::getSkullClient(const Creature* creature) const
  3855. {
  3856.     if (!creature || g_game.getWorldType() != WORLD_TYPE_PVP) {
  3857.         return SKULL_NONE;
  3858.     }
  3859.  
  3860.     const Player* player = creature->getPlayer();
  3861.     if (!player || player->getSkull() != SKULL_NONE) {
  3862.         return Creature::getSkullClient(creature);
  3863.     }
  3864.  
  3865.     if (isInWar(player)) {
  3866.         return SKULL_GREEN;
  3867.     }
  3868.  
  3869.     if (!player->getGuildWarVector().empty() && guild == player->getGuild()) {
  3870.         return SKULL_GREEN;
  3871.     }
  3872.  
  3873.     if (player->hasAttacked(this)) {
  3874.         return SKULL_YELLOW;
  3875.     }
  3876.  
  3877.     if (isPartner(player)) {
  3878.         return SKULL_GREEN;
  3879.     }
  3880.     return Creature::getSkullClient(creature);
  3881. }
  3882.  
  3883. bool Player::hasAttacked(const Player* attacked) const
  3884. {
  3885.     if (hasFlag(PlayerFlag_NotGainInFight) || !attacked) {
  3886.         return false;
  3887.     }
  3888.  
  3889.     return attackedSet.find(attacked->guid) != attackedSet.end();
  3890. }
  3891.  
  3892. void Player::addAttacked(const Player* attacked)
  3893. {
  3894.     if (hasFlag(PlayerFlag_NotGainInFight) || !attacked || attacked == this) {
  3895.         return;
  3896.     }
  3897.  
  3898.     attackedSet.insert(attacked->guid);
  3899. }
  3900.  
  3901. void Player::removeAttacked(const Player* attacked)
  3902. {
  3903.     if (!attacked || attacked == this) {
  3904.         return;
  3905.     }
  3906.  
  3907.     auto it = attackedSet.find(attacked->guid);
  3908.     if (it != attackedSet.end()) {
  3909.         attackedSet.erase(it);
  3910.     }
  3911. }
  3912.  
  3913. void Player::clearAttacked()
  3914. {
  3915.     attackedSet.clear();
  3916. }
  3917.  
  3918. void Player::addUnjustifiedDead(const Player* attacked)
  3919. {
  3920.     if (hasFlag(PlayerFlag_NotGainInFight) || attacked == this || g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) {
  3921.         return;
  3922.     }
  3923.  
  3924.     sendTextMessage(MESSAGE_EVENT_ADVANCE, "Warning! The murder of " + attacked->getName() + " was not justified.");
  3925.  
  3926.     skullTicks += g_config.getNumber(ConfigManager::FRAG_TIME);
  3927.  
  3928.     if (getSkull() != SKULL_BLACK) {
  3929.         if (g_config.getNumber(ConfigManager::KILLS_TO_BLACK) != 0 && skullTicks > (g_config.getNumber(ConfigManager::KILLS_TO_BLACK) - 1) * static_cast<int64_t>(g_config.getNumber(ConfigManager::FRAG_TIME))) {
  3930.             setSkull(SKULL_BLACK);
  3931.         } else if (getSkull() != SKULL_RED && g_config.getNumber(ConfigManager::KILLS_TO_RED) != 0 && skullTicks > (g_config.getNumber(ConfigManager::KILLS_TO_RED) - 1) * static_cast<int64_t>(g_config.getNumber(ConfigManager::FRAG_TIME))) {
  3932.             setSkull(SKULL_RED);
  3933.         }
  3934.     }
  3935. }
  3936.  
  3937. void Player::checkSkullTicks(int32_t ticks)
  3938. {
  3939.     int32_t newTicks = skullTicks - ticks;
  3940.     if (newTicks < 0) {
  3941.         skullTicks = 0;
  3942.     } else {
  3943.         skullTicks = newTicks;
  3944.     }
  3945.  
  3946.     if ((skull == SKULL_RED || skull == SKULL_BLACK) && skullTicks < 1000 && !hasCondition(CONDITION_INFIGHT)) {
  3947.         setSkull(SKULL_NONE);
  3948.     }
  3949. }
  3950.  
  3951. bool Player::isPromoted() const
  3952. {
  3953.     uint16_t promotedVocation = g_vocations.getPromotedVocation(vocation->getId());
  3954.     return promotedVocation == VOCATION_NONE && vocation->getId() != promotedVocation;
  3955. }
  3956.  
  3957. double Player::getLostPercent() const
  3958. {
  3959.     int32_t blessingCount = std::bitset<5>(blessings).count();
  3960.  
  3961.     int32_t deathLosePercent = g_config.getNumber(ConfigManager::DEATH_LOSE_PERCENT);
  3962.     if (deathLosePercent != -1) {
  3963.         if (isPromoted()) {
  3964.             deathLosePercent -= 3;
  3965.         }
  3966.  
  3967.         deathLosePercent -= blessingCount;
  3968.         return std::max<int32_t>(0, deathLosePercent) / 100.;
  3969.     }
  3970.  
  3971.     double lossPercent;
  3972.     if (level >= 25) {
  3973.         double tmpLevel = level + (levelPercent / 100.);
  3974.         lossPercent = static_cast<double>((tmpLevel + 50) * 50 * ((tmpLevel * tmpLevel) - (5 * tmpLevel) + 8)) / experience;
  3975.     } else {
  3976.         lossPercent = 10;
  3977.     }
  3978.  
  3979.     if (isPromoted()) {
  3980.         lossPercent *= 0.7;
  3981.     }
  3982.  
  3983.     return lossPercent * pow(0.92, blessingCount) / 100;
  3984. }
  3985.  
  3986. void Player::learnInstantSpell(const std::string& spellName)
  3987. {
  3988.     if (!hasLearnedInstantSpell(spellName)) {
  3989.         learnedInstantSpellList.push_front(spellName);
  3990.     }
  3991. }
  3992.  
  3993. void Player::forgetInstantSpell(const std::string& spellName)
  3994. {
  3995.     learnedInstantSpellList.remove(spellName);
  3996. }
  3997.  
  3998. bool Player::hasLearnedInstantSpell(const std::string& spellName) const
  3999. {
  4000.     if (hasFlag(PlayerFlag_CannotUseSpells)) {
  4001.         return false;
  4002.     }
  4003.  
  4004.     if (hasFlag(PlayerFlag_IgnoreSpellCheck)) {
  4005.         return true;
  4006.     }
  4007.  
  4008.     for (const auto& learnedSpellName : learnedInstantSpellList) {
  4009.         if (strcasecmp(learnedSpellName.c_str(), spellName.c_str()) == 0) {
  4010.             //player->sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "New spell available!!");
  4011.             return true;
  4012.         }
  4013.     }
  4014.     return false;
  4015. }
  4016.  
  4017. bool Player::isInWar(const Player* player) const
  4018. {
  4019.     if (!player || !guild) {
  4020.         return false;
  4021.     }
  4022.  
  4023.     const Guild* playerGuild = player->getGuild();
  4024.     if (!playerGuild) {
  4025.         return false;
  4026.     }
  4027.  
  4028.     return isInWarList(playerGuild->getId()) && player->isInWarList(guild->getId());
  4029. }
  4030.  
  4031. bool Player::isInWarList(uint32_t guildId) const
  4032. {
  4033.     return std::find(guildWarVector.begin(), guildWarVector.end(), guildId) != guildWarVector.end();
  4034. }
  4035.  
  4036. bool Player::isPremium() const
  4037. {
  4038.     if (g_config.getBoolean(ConfigManager::FREE_PREMIUM) || hasFlag(PlayerFlag_IsAlwaysPremium)) {
  4039.         return true;
  4040.     }
  4041.  
  4042.     return premiumDays > 0;
  4043. }
  4044.  
  4045. void Player::setPremiumDays(int32_t v)
  4046. {
  4047.     premiumDays = v;
  4048.     sendBasicData();
  4049. }
  4050.  
  4051. PartyShields_t Player::getPartyShield(const Player* player) const
  4052. {
  4053.     if (!player) {
  4054.         return SHIELD_NONE;
  4055.     }
  4056.  
  4057.     if (party) {
  4058.         if (party->getLeader() == player) {
  4059.             if (party->isSharedExperienceActive()) {
  4060.                 if (party->isSharedExperienceEnabled()) {
  4061.                     return SHIELD_YELLOW_SHAREDEXP;
  4062.                 }
  4063.  
  4064.                 if (party->canUseSharedExperience(player)) {
  4065.                     return SHIELD_YELLOW_NOSHAREDEXP;
  4066.                 }
  4067.  
  4068.                 return SHIELD_YELLOW_NOSHAREDEXP_BLINK;
  4069.             }
  4070.  
  4071.             return SHIELD_YELLOW;
  4072.         }
  4073.  
  4074.         if (player->party == party) {
  4075.             if (party->isSharedExperienceActive()) {
  4076.                 if (party->isSharedExperienceEnabled()) {
  4077.                     return SHIELD_BLUE_SHAREDEXP;
  4078.                 }
  4079.  
  4080.                 if (party->canUseSharedExperience(player)) {
  4081.                     return SHIELD_BLUE_NOSHAREDEXP;
  4082.                 }
  4083.  
  4084.                 return SHIELD_BLUE_NOSHAREDEXP_BLINK;
  4085.             }
  4086.  
  4087.             return SHIELD_BLUE;
  4088.         }
  4089.  
  4090.         if (isInviting(player)) {
  4091.             return SHIELD_WHITEBLUE;
  4092.         }
  4093.     }
  4094.  
  4095.     if (player->isInviting(this)) {
  4096.         return SHIELD_WHITEYELLOW;
  4097.     }
  4098.  
  4099.     if (player->party) {
  4100.         return SHIELD_GRAY;
  4101.     }
  4102.  
  4103.     return SHIELD_NONE;
  4104. }
  4105.  
  4106. bool Player::isInviting(const Player* player) const
  4107. {
  4108.     if (!player || !party || party->getLeader() != this) {
  4109.         return false;
  4110.     }
  4111.     return party->isPlayerInvited(player);
  4112. }
  4113.  
  4114. bool Player::isPartner(const Player* player) const
  4115. {
  4116.     if (!player || !party || player == this) {
  4117.         return false;
  4118.     }
  4119.     return party == player->party;
  4120. }
  4121.  
  4122. bool Player::isGuildMate(const Player* player) const
  4123. {
  4124.     if (!player || !guild) {
  4125.         return false;
  4126.     }
  4127.     return guild == player->guild;
  4128. }
  4129.  
  4130. void Player::sendPlayerPartyIcons(Player* player)
  4131. {
  4132.     sendCreatureShield(player);
  4133.     sendCreatureSkull(player);
  4134. }
  4135.  
  4136. bool Player::addPartyInvitation(Party* party)
  4137. {
  4138.     auto it = std::find(invitePartyList.begin(), invitePartyList.end(), party);
  4139.     if (it != invitePartyList.end()) {
  4140.         return false;
  4141.     }
  4142.  
  4143.     invitePartyList.push_front(party);
  4144.     return true;
  4145. }
  4146.  
  4147. void Player::removePartyInvitation(Party* party)
  4148. {
  4149.     invitePartyList.remove(party);
  4150. }
  4151.  
  4152. void Player::clearPartyInvitations()
  4153. {
  4154.     for (Party* invitingParty : invitePartyList) {
  4155.         invitingParty->removeInvite(*this, false);
  4156.     }
  4157.     invitePartyList.clear();
  4158. }
  4159.  
  4160. GuildEmblems_t Player::getGuildEmblem(const Player* player) const
  4161. {
  4162.     if (!player) {
  4163.         return GUILDEMBLEM_NONE;
  4164.     }
  4165.  
  4166.     const Guild* playerGuild = player->getGuild();
  4167.     if (!playerGuild) {
  4168.         return GUILDEMBLEM_NONE;
  4169.     }
  4170.  
  4171.     if (player->getGuildWarVector().empty()) {
  4172.         if (guild == playerGuild) {
  4173.             return GUILDEMBLEM_MEMBER;
  4174.         } else {
  4175.             return GUILDEMBLEM_OTHER;
  4176.         }
  4177.     } else if (guild == playerGuild) {
  4178.         return GUILDEMBLEM_ALLY;
  4179.     } else if (isInWar(player)) {
  4180.         return GUILDEMBLEM_ENEMY;
  4181.     }
  4182.  
  4183.     return GUILDEMBLEM_NEUTRAL;
  4184. }
  4185.  
  4186. uint8_t Player::getCurrentMount() const
  4187. {
  4188.     int32_t value;
  4189.     if (getStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, value)) {
  4190.         return value;
  4191.     }
  4192.     return 0;
  4193. }
  4194.  
  4195. void Player::setCurrentMount(uint8_t mountId)
  4196. {
  4197.     addStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, mountId);
  4198. }
  4199.  
  4200. bool Player::toggleMount(bool mount)
  4201. {
  4202.     if ((OTSYS_TIME() - lastToggleMount) < 3000 && !wasMounted) {
  4203.         sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED);
  4204.         return false;
  4205.     }
  4206.  
  4207.     if (mount) {
  4208.         if (isMounted()) {
  4209.             return false;
  4210.         }
  4211.  
  4212.         if (!group->access && tile->hasFlag(TILESTATE_PROTECTIONZONE)) {
  4213.             sendCancelMessage(RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE);
  4214.             return false;
  4215.         }
  4216.  
  4217.         const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(getSex(), defaultOutfit.lookType);
  4218.         if (!playerOutfit) {
  4219.             return false;
  4220.         }
  4221.  
  4222.         uint8_t currentMountId = getCurrentMount();
  4223.         if (currentMountId == 0) {
  4224.             sendOutfitWindow();
  4225.             return false;
  4226.         }
  4227.  
  4228.         Mount* currentMount = g_game.mounts.getMountByID(currentMountId);
  4229.         if (!currentMount) {
  4230.             return false;
  4231.         }
  4232.  
  4233.         if (!hasMount(currentMount)) {
  4234.             setCurrentMount(0);
  4235.             sendOutfitWindow();
  4236.             return false;
  4237.         }
  4238.  
  4239.         if (currentMount->premium && !isPremium()) {
  4240.             sendCancelMessage(RETURNVALUE_YOUNEEDPREMIUMACCOUNT);
  4241.             return false;
  4242.         }
  4243.  
  4244.         if (hasCondition(CONDITION_OUTFIT)) {
  4245.             sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  4246.             return false;
  4247.         }
  4248.  
  4249.         defaultOutfit.lookMount = currentMount->clientId;
  4250.  
  4251.         if (currentMount->speed != 0) {
  4252.             g_game.changeSpeed(this, currentMount->speed);
  4253.         }
  4254.     } else {
  4255.         if (!isMounted()) {
  4256.             return false;
  4257.         }
  4258.  
  4259.         dismount();
  4260.     }
  4261.  
  4262.     g_game.internalCreatureChangeOutfit(this, defaultOutfit);
  4263.     lastToggleMount = OTSYS_TIME();
  4264.     return true;
  4265. }
  4266.  
  4267. bool Player::tameMount(uint8_t mountId)
  4268. {
  4269.     if (!g_game.mounts.getMountByID(mountId)) {
  4270.         return false;
  4271.     }
  4272.  
  4273.     const uint8_t tmpMountId = mountId - 1;
  4274.     const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31);
  4275.  
  4276.     int32_t value;
  4277.     if (getStorageValue(key, value)) {
  4278.         value |= (1 << (tmpMountId % 31));
  4279.     } else {
  4280.         value = (1 << (tmpMountId % 31));
  4281.     }
  4282.  
  4283.     addStorageValue(key, value);
  4284.     return true;
  4285. }
  4286.  
  4287. bool Player::untameMount(uint8_t mountId)
  4288. {
  4289.     if (!g_game.mounts.getMountByID(mountId)) {
  4290.         return false;
  4291.     }
  4292.  
  4293.     const uint8_t tmpMountId = mountId - 1;
  4294.     const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31);
  4295.  
  4296.     int32_t value;
  4297.     if (!getStorageValue(key, value)) {
  4298.         return true;
  4299.     }
  4300.  
  4301.     value &= ~(1 << (tmpMountId % 31));
  4302.     addStorageValue(key, value);
  4303.  
  4304.     if (getCurrentMount() == mountId) {
  4305.         if (isMounted()) {
  4306.             dismount();
  4307.             g_game.internalCreatureChangeOutfit(this, defaultOutfit);
  4308.         }
  4309.  
  4310.         setCurrentMount(0);
  4311.     }
  4312.  
  4313.     return true;
  4314. }
  4315.  
  4316. bool Player::hasMount(const Mount* mount) const
  4317. {
  4318.     if (isAccessPlayer()) {
  4319.         return true;
  4320.     }
  4321.  
  4322.     if (mount->premium && !isPremium()) {
  4323.         return false;
  4324.     }
  4325.  
  4326.     const uint8_t tmpMountId = mount->id - 1;
  4327.  
  4328.     int32_t value;
  4329.     if (!getStorageValue(PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31), value)) {
  4330.         return false;
  4331.     }
  4332.  
  4333.     return ((1 << (tmpMountId % 31)) & value) != 0;
  4334. }
  4335.  
  4336. void Player::dismount()
  4337. {
  4338.     Mount* mount = g_game.mounts.getMountByID(getCurrentMount());
  4339.     if (mount && mount->speed > 0) {
  4340.         g_game.changeSpeed(this, -mount->speed);
  4341.     }
  4342.  
  4343.     defaultOutfit.lookMount = 0;
  4344. }
  4345.  
  4346. bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries)
  4347. {
  4348.     if (tries == 0 || skill == SKILL_LEVEL) {
  4349.         return false;
  4350.     }
  4351.  
  4352.     bool sendUpdate = false;
  4353.     uint32_t oldSkillValue, newSkillValue;
  4354.     long double oldPercentToNextLevel, newPercentToNextLevel;
  4355.  
  4356.     if (skill == SKILL_MAGLEVEL) {
  4357.         uint64_t currReqMana = vocation->getReqMana(magLevel);
  4358.         uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
  4359.  
  4360.         if (currReqMana >= nextReqMana) {
  4361.             return false;
  4362.         }
  4363.  
  4364.         oldSkillValue = magLevel;
  4365.         oldPercentToNextLevel = static_cast<long double>(manaSpent * 100) / nextReqMana;
  4366.  
  4367.         g_events->eventPlayerOnGainSkillTries(this, SKILL_MAGLEVEL, tries);
  4368.         uint32_t currMagLevel = magLevel;
  4369.  
  4370.         while ((manaSpent + tries) >= nextReqMana) {
  4371.             tries -= nextReqMana - manaSpent;
  4372.  
  4373.             magLevel++;
  4374.             manaSpent = 0;
  4375.  
  4376.             g_creatureEvents->playerAdvance(this, SKILL_MAGLEVEL, magLevel - 1, magLevel);
  4377.  
  4378.             sendUpdate = true;
  4379.             currReqMana = nextReqMana;
  4380.             nextReqMana = vocation->getReqMana(magLevel + 1);
  4381.  
  4382.             if (currReqMana >= nextReqMana) {
  4383.                 tries = 0;
  4384.                 break;
  4385.             }
  4386.         }
  4387.  
  4388.         manaSpent += tries;
  4389.  
  4390.         if (magLevel != currMagLevel) {
  4391.             std::ostringstream ss;
  4392.             ss << "You advanced to magic level " << magLevel << '.';
  4393.             sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  4394.         }
  4395.  
  4396.         uint8_t newPercent;
  4397.         if (nextReqMana > currReqMana) {
  4398.             newPercent = Player::getPercentLevel(manaSpent, nextReqMana);
  4399.             newPercentToNextLevel = static_cast<long double>(manaSpent * 100) / nextReqMana;
  4400.         } else {
  4401.             newPercent = 0;
  4402.             newPercentToNextLevel = 0;
  4403.         }
  4404.  
  4405.         if (newPercent != magLevelPercent) {
  4406.             magLevelPercent = newPercent;
  4407.             sendUpdate = true;
  4408.         }
  4409.  
  4410.         newSkillValue = magLevel;
  4411.     } else {
  4412.         uint64_t currReqTries = vocation->getReqSkillTries(skill, skills[skill].level);
  4413.         uint64_t nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
  4414.         if (currReqTries >= nextReqTries) {
  4415.             return false;
  4416.         }
  4417.  
  4418.         oldSkillValue = skills[skill].level;
  4419.         oldPercentToNextLevel = static_cast<long double>(skills[skill].tries * 100) / nextReqTries;
  4420.  
  4421.         g_events->eventPlayerOnGainSkillTries(this, skill, tries);
  4422.         uint32_t currSkillLevel = skills[skill].level;
  4423.  
  4424.         while ((skills[skill].tries + tries) >= nextReqTries) {
  4425.             tries -= nextReqTries - skills[skill].tries;
  4426.  
  4427.             //skills[skill].level++;
  4428.             skills[skill].tries = 0;
  4429.             skills[skill].percent = 0;
  4430.  
  4431.             g_creatureEvents->playerAdvance(this, skill, (skills[skill].level - 1), skills[skill].level);
  4432.  
  4433.             sendUpdate = true;
  4434.             currReqTries = nextReqTries;
  4435.             nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
  4436.  
  4437.             if (currReqTries >= nextReqTries) {
  4438.                 tries = 0;
  4439.                 break;
  4440.             }
  4441.         }
  4442.  
  4443.         skills[skill].tries += tries;
  4444.  
  4445.         if (currSkillLevel != skills[skill].level) {
  4446.             std::ostringstream ss;
  4447.             ss << "You advanced to " << getSkillName(skill) << " level " << skills[skill].level << '.';
  4448.             //sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  4449.         }
  4450.  
  4451.         uint8_t newPercent;
  4452.         if (nextReqTries > currReqTries) {
  4453.             newPercent = Player::getPercentLevel(skills[skill].tries, nextReqTries);
  4454.             newPercentToNextLevel = static_cast<long double>(skills[skill].tries * 100) / nextReqTries;
  4455.         } else {
  4456.             newPercent = 0;
  4457.             newPercentToNextLevel = 0;
  4458.         }
  4459.  
  4460.         if (skills[skill].percent != newPercent) {
  4461.             skills[skill].percent = newPercent;
  4462.             sendUpdate = true;
  4463.         }
  4464.  
  4465.         newSkillValue = skills[skill].level;
  4466.     }
  4467.  
  4468.     if (sendUpdate) {
  4469.         sendSkills();
  4470.     }
  4471.  
  4472.     std::ostringstream ss;
  4473.     ss << std::fixed << std::setprecision(2) << "Your " << ucwords(getSkillName(skill)) << " skill changed from level " << oldSkillValue << " (with " << oldPercentToNextLevel << "% progress towards level " << (oldSkillValue + 1) << ") to level " << newSkillValue << " (with " << newPercentToNextLevel << "% progress towards level " << (newSkillValue + 1) << ')';
  4474.     //sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  4475.     return sendUpdate;
  4476. }
  4477.  
  4478. bool Player::hasModalWindowOpen(uint32_t modalWindowId) const
  4479. {
  4480.     return find(modalWindows.begin(), modalWindows.end(), modalWindowId) != modalWindows.end();
  4481. }
  4482.  
  4483. void Player::onModalWindowHandled(uint32_t modalWindowId)
  4484. {
  4485.     modalWindows.remove(modalWindowId);
  4486. }
  4487.  
  4488. void Player::sendModalWindow(const ModalWindow& modalWindow)
  4489. {
  4490.     if (!client) {
  4491.         return;
  4492.     }
  4493.  
  4494.     modalWindows.push_front(modalWindow.id);
  4495.     client->sendModalWindow(modalWindow);
  4496. }
  4497.  
  4498. void Player::clearModalWindows()
  4499. {
  4500.     modalWindows.clear();
  4501. }
  4502.  
  4503. uint16_t Player::getHelpers() const
  4504. {
  4505.     uint16_t helpers;
  4506.  
  4507.     if (guild && party) {
  4508.         std::unordered_set<Player*> helperSet;
  4509.  
  4510.         const auto& guildMembers = guild->getMembersOnline();
  4511.         helperSet.insert(guildMembers.begin(), guildMembers.end());
  4512.  
  4513.         const auto& partyMembers = party->getMembers();
  4514.         helperSet.insert(partyMembers.begin(), partyMembers.end());
  4515.  
  4516.         const auto& partyInvitees = party->getInvitees();
  4517.         helperSet.insert(partyInvitees.begin(), partyInvitees.end());
  4518.  
  4519.         helperSet.insert(party->getLeader());
  4520.  
  4521.         helpers = helperSet.size();
  4522.     } else if (guild) {
  4523.         helpers = guild->getMembersOnline().size();
  4524.     } else if (party) {
  4525.         helpers = party->getMemberCount() + party->getInvitationCount() + 1;
  4526.     } else {
  4527.         helpers = 0;
  4528.     }
  4529.  
  4530.     return helpers;
  4531. }
  4532.  
  4533. void Player::sendClosePrivate(uint16_t channelId)
  4534. {
  4535.     if (channelId == CHANNEL_GUILD || channelId == CHANNEL_PARTY) {
  4536.         g_chat->removeUserFromChannel(*this, channelId);
  4537.     }
  4538.  
  4539.     if (client) {
  4540.         client->sendClosePrivate(channelId);
  4541.     }
  4542. }
  4543.  
  4544. uint64_t Player::getMoney() const
  4545. {
  4546.     std::vector<const Container*> containers;
  4547.     uint64_t moneyCount = 0;
  4548.  
  4549.     for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
  4550.         Item* item = inventory[i];
  4551.         if (!item) {
  4552.             continue;
  4553.         }
  4554.  
  4555.         const Container* container = item->getContainer();
  4556.         if (container) {
  4557.             containers.push_back(container);
  4558.         } else {
  4559.             moneyCount += item->getWorth();
  4560.         }
  4561.     }
  4562.  
  4563.     size_t i = 0;
  4564.     while (i < containers.size()) {
  4565.         const Container* container = containers[i++];
  4566.         for (const Item* item : container->getItemList()) {
  4567.             const Container* tmpContainer = item->getContainer();
  4568.             if (tmpContainer) {
  4569.                 containers.push_back(tmpContainer);
  4570.             } else {
  4571.                 moneyCount += item->getWorth();
  4572.             }
  4573.         }
  4574.     }
  4575.     return moneyCount;
  4576. }
  4577.  
  4578. size_t Player::getMaxVIPEntries() const
  4579. {
  4580.     if (group->maxVipEntries != 0) {
  4581.         return group->maxVipEntries;
  4582.     } else if (isPremium()) {
  4583.         return 100;
  4584.     }
  4585.     return 20;
  4586. }
  4587.  
  4588. size_t Player::getMaxDepotItems() const
  4589. {
  4590.     if (group->maxDepotItems != 0) {
  4591.         return group->maxDepotItems;
  4592.     } else if (isPremium()) {
  4593.         return 2000;
  4594.     }
  4595.     return 1000;
  4596. }
  4597.  
  4598. std::forward_list<Condition*> Player::getMuteConditions() const
  4599. {
  4600.     std::forward_list<Condition*> muteConditions;
  4601.     for (Condition* condition : conditions) {
  4602.         if (condition->getTicks() <= 0) {
  4603.             continue;
  4604.         }
  4605.  
  4606.         ConditionType_t type = condition->getType();
  4607.         if (type != CONDITION_MUTED && type != CONDITION_CHANNELMUTEDTICKS && type != CONDITION_YELLTICKS) {
  4608.             continue;
  4609.         }
  4610.  
  4611.         muteConditions.push_front(condition);
  4612.     }
  4613.     return muteConditions;
  4614. }
  4615.  
  4616. void Player::setGuild(Guild* guild)
  4617. {
  4618.     if (guild == this->guild) {
  4619.         return;
  4620.     }
  4621.  
  4622.     Guild* oldGuild = this->guild;
  4623.  
  4624.     this->guildNick.clear();
  4625.     this->guild = nullptr;
  4626.     this->guildRank = nullptr;
  4627.  
  4628.     if (guild) {
  4629.         const GuildRank* rank = guild->getRankByLevel(1);
  4630.         if (!rank) {
  4631.             return;
  4632.         }
  4633.  
  4634.         this->guild = guild;
  4635.         this->guildRank = rank;
  4636.         guild->addMember(this);
  4637.     }
  4638.  
  4639.     if (oldGuild) {
  4640.         oldGuild->removeMember(this);
  4641.     }
  4642. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top