TheLastEmpire

The Last Empire - Player.cpp

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