SHARE
TWEET

Untitled

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