Advertisement
Rellyx

player.cpp

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