SHARE
TWEET

Untitled

a guest May 20th, 2019 83 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * The Forgotten Server - a free and open-source MMORPG server emulator
  3.  * Copyright (C) 2016  Mark Samman <mark.samman@gmail.com>
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License along
  16.  * with this program; if not, write to the Free Software Foundation, Inc.,
  17.  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18.  */
  19.  
  20. #include "otpch.h"
  21.  
  22. #include "pugicast.h"
  23.  
  24. #include "items.h"
  25. #include "commands.h"
  26. #include "creature.h"
  27. #include "monster.h"
  28. #include "game.h"
  29. #include "actions.h"
  30. #include "iologindata.h"
  31. #include "talkaction.h"
  32. #include "spells.h"
  33. #include "configmanager.h"
  34. #include "server.h"
  35. #include "globalevent.h"
  36. #include "bed.h"
  37. #include "scheduler.h"
  38. #include "events.h"
  39. #include "databasetasks.h"
  40.  
  41. extern ConfigManager g_config;
  42. extern Actions* g_actions;
  43. extern Chat* g_chat;
  44. extern TalkActions* g_talkActions;
  45. extern Spells* g_spells;
  46. extern Vocations g_vocations;
  47. extern GlobalEvents* g_globalEvents;
  48. extern Events* g_events;
  49.  
  50. Game::Game() :
  51.     wildcardTree(false)
  52. {
  53.     gameState = GAME_STATE_NORMAL;
  54.     worldType = WORLD_TYPE_PVP;
  55.  
  56.     serviceManager = nullptr;
  57.     lastStageLevel = 0;
  58.     playersRecord = 0;
  59.     motdNum = 0;
  60.     useLastStageLevel = false;
  61.     stagesEnabled = false;
  62.  
  63.     lastBucket = 0;
  64.  
  65.     //(1440 minutes/day)/(3600 seconds/day)*10 seconds event interval
  66.     int32_t dayCycle = 3600;
  67.     lightHourDelta = 1440 * 10 / dayCycle;
  68.     lightHour = SUNRISE + (SUNSET - SUNRISE) / 2;
  69.     lightLevel = LIGHT_LEVEL_DAY;
  70.     lightState = LIGHT_STATE_DAY;
  71. }
  72.  
  73. Game::~Game()
  74. {
  75.     for (const auto& it : guilds) {
  76.         delete it.second;
  77.     }
  78. }
  79.  
  80. void Game::start(ServiceManager* manager)
  81. {
  82.     serviceManager = manager;
  83.  
  84.     g_scheduler.addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL, std::bind(&Game::checkLight, this)));
  85.     g_scheduler.addEvent(createSchedulerTask(EVENT_CREATURE_THINK_INTERVAL, std::bind(&Game::checkCreatures, this, 0)));
  86.     g_scheduler.addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, std::bind(&Game::checkDecay, this)));
  87. }
  88.  
  89. GameState_t Game::getGameState() const
  90. {
  91.     return gameState;
  92. }
  93.  
  94. void Game::setWorldType(WorldType_t type)
  95. {
  96.     worldType = type;
  97. }
  98.  
  99. void Game::setGameState(GameState_t newState)
  100. {
  101.     if (gameState == GAME_STATE_SHUTDOWN) {
  102.         return;    //this cannot be stopped
  103.     }
  104.  
  105.     if (gameState == newState) {
  106.         return;
  107.     }
  108.  
  109.     gameState = newState;
  110.     switch (newState) {
  111.         case GAME_STATE_INIT: {
  112.             commands.loadFromXml();
  113.  
  114.             loadExperienceStages();
  115.  
  116.             groups.load();
  117.             g_chat->load();
  118.  
  119.             map.spawns.startup();
  120.  
  121.             raids.loadFromXml();
  122.             raids.startup();
  123.  
  124.             quests.loadFromXml();
  125.  
  126.             loadMotdNum();
  127.             loadPlayersRecord();
  128.  
  129.             g_globalEvents->startup();
  130.             break;
  131.         }
  132.  
  133.         case GAME_STATE_SHUTDOWN: {
  134.             g_globalEvents->execute(GLOBALEVENT_SHUTDOWN);
  135.  
  136.             //kick all players that are still online
  137.             auto it = players.begin();
  138.             while (it != players.end()) {
  139.                 it->second->kickPlayer(true);
  140.                 it = players.begin();
  141.             }
  142.  
  143.             saveMotdNum();
  144.             saveGameState();
  145.  
  146.             g_dispatcher.addTask(
  147.                 createTask(std::bind(&Game::shutdown, this)));
  148.  
  149.             g_scheduler.stop();
  150.             g_databaseTasks.stop();
  151.             g_dispatcher.stop();
  152.             break;
  153.         }
  154.  
  155.         case GAME_STATE_CLOSED: {
  156.             /* kick all players without the CanAlwaysLogin flag */
  157.             auto it = players.begin();
  158.             while (it != players.end()) {
  159.                 if (!it->second->hasFlag(PlayerFlag_CanAlwaysLogin)) {
  160.                     it->second->kickPlayer(true);
  161.                     it = players.begin();
  162.                 } else {
  163.                     ++it;
  164.                 }
  165.             }
  166.  
  167.             saveGameState();
  168.             break;
  169.         }
  170.  
  171.         default:
  172.             break;
  173.     }
  174. }
  175.  
  176. void Game::saveGameState()
  177. {
  178.     if (gameState == GAME_STATE_NORMAL) {
  179.         setGameState(GAME_STATE_MAINTAIN);
  180.     }
  181.  
  182.     std::cout << "Saving server..." << std::endl;
  183.  
  184.     for (const auto& it : players) {
  185.         it.second->loginPosition = it.second->getPosition();
  186.         IOLoginData::savePlayer(it.second);
  187.     }
  188.  
  189.     Map::save();
  190.  
  191.     if (gameState == GAME_STATE_MAINTAIN) {
  192.         setGameState(GAME_STATE_NORMAL);
  193.     }
  194. }
  195.  
  196. bool Game::loadMainMap(const std::string& filename)
  197. {
  198.     Monster::despawnRange = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRANGE);
  199.     Monster::despawnRadius = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRADIUS);
  200.     return map.loadMap("data/world/" + filename + ".otbm", true);
  201. }
  202.  
  203. void Game::loadMap(const std::string& path)
  204. {
  205.     map.loadMap(path, false);
  206. }
  207.  
  208. Cylinder* Game::internalGetCylinder(Player* player, const Position& pos) const
  209. {
  210.     if (pos.x != 0xFFFF) {
  211.         return map.getTile(pos);
  212.     }
  213.  
  214.     //container
  215.     if (pos.y & 0x40) {
  216.         uint8_t from_cid = pos.y & 0x0F;
  217.         return player->getContainerByID(from_cid);
  218.     }
  219.  
  220.     //inventory
  221.     return player;
  222. }
  223.  
  224. Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index, uint32_t spriteId, stackPosType_t type) const
  225. {
  226.     if (pos.x != 0xFFFF) {
  227.         Tile* tile = map.getTile(pos);
  228.         if (!tile) {
  229.             return nullptr;
  230.         }
  231.  
  232.         Thing* thing;
  233.         switch (type) {
  234.             case STACKPOS_LOOK: {
  235.                 return tile->getTopVisibleThing(player);
  236.             }
  237.  
  238.             case STACKPOS_MOVE: {
  239.                 Item* item = tile->getTopDownItem();
  240.                 if (item && item->isMoveable()) {
  241.                     thing = item;
  242.                 } else {
  243.                     thing = tile->getTopVisibleCreature(player);
  244.                 }
  245.                 break;
  246.             }
  247.  
  248.             case STACKPOS_USEITEM: {
  249.                 thing = tile->getUseItem();
  250.                 break;
  251.             }
  252.  
  253.             case STACKPOS_TOPDOWN_ITEM: {
  254.                 thing = tile->getTopDownItem();
  255.                 break;
  256.             }
  257.  
  258.             case STACKPOS_USETARGET: {
  259.                 thing = tile->getTopVisibleCreature(player);
  260.                 if (!thing) {
  261.                     thing = tile->getUseItem();
  262.                 }
  263.                 break;
  264.             }
  265.  
  266.             default: {
  267.                 thing = nullptr;
  268.                 break;
  269.             }
  270.         }
  271.  
  272.         if (player && tile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) {
  273.             //do extra checks here if the thing is accessable
  274.             if (thing && thing->getItem()) {
  275.                 if (tile->hasProperty(CONST_PROP_ISVERTICAL)) {
  276.                     if (player->getPosition().x + 1 == tile->getPosition().x) {
  277.                         thing = nullptr;
  278.                     }
  279.                 } else { // horizontal
  280.                     if (player->getPosition().y + 1 == tile->getPosition().y) {
  281.                         thing = nullptr;
  282.                     }
  283.                 }
  284.             }
  285.         }
  286.         return thing;
  287.     }
  288.  
  289.     //container
  290.     if (pos.y & 0x40) {
  291.         uint8_t fromCid = pos.y & 0x0F;
  292.  
  293.         Container* parentContainer = player->getContainerByID(fromCid);
  294.         if (!parentContainer) {
  295.             return nullptr;
  296.         }
  297.  
  298.         uint8_t slot = pos.z;
  299.         return parentContainer->getItemByIndex(player->getContainerIndex(fromCid) + slot);
  300.     } else if (pos.y == 0 && pos.z == 0) {
  301.         const ItemType& it = Item::items.getItemIdByClientId(spriteId);
  302.         if (it.id == 0) {
  303.             return nullptr;
  304.         }
  305.  
  306.         int32_t subType;
  307.         if (it.isFluidContainer() && index < static_cast<int32_t>(sizeof(reverseFluidMap) / sizeof(uint8_t))) {
  308.             subType = reverseFluidMap[index];
  309.         } else {
  310.             subType = -1;
  311.         }
  312.  
  313.         return findItemOfType(player, it.id, true, subType);
  314.     }
  315.  
  316.     //inventory
  317.     slots_t slot = static_cast<slots_t>(pos.y);
  318.     return player->getInventoryItem(slot);
  319. }
  320.  
  321. void Game::internalGetPosition(Item* item, Position& pos, uint8_t& stackpos)
  322. {
  323.     pos.x = 0;
  324.     pos.y = 0;
  325.     pos.z = 0;
  326.     stackpos = 0;
  327.  
  328.     Cylinder* topParent = item->getTopParent();
  329.     if (topParent) {
  330.         if (Player* player = dynamic_cast<Player*>(topParent)) {
  331.             pos.x = 0xFFFF;
  332.  
  333.             Container* container = dynamic_cast<Container*>(item->getParent());
  334.             if (container) {
  335.                 pos.y = static_cast<uint16_t>(0x40) | static_cast<uint16_t>(player->getContainerID(container));
  336.                 pos.z = container->getThingIndex(item);
  337.                 stackpos = pos.z;
  338.             } else {
  339.                 pos.y = player->getThingIndex(item);
  340.                 stackpos = pos.y;
  341.             }
  342.         } else if (Tile* tile = topParent->getTile()) {
  343.             pos = tile->getPosition();
  344.             stackpos = tile->getThingIndex(item);
  345.         }
  346.     }
  347. }
  348.  
  349. Creature* Game::getCreatureByID(uint32_t id)
  350. {
  351.     if (id <= Player::playerAutoID) {
  352.         return getPlayerByID(id);
  353.     } else if (id <= Monster::monsterAutoID) {
  354.         return getMonsterByID(id);
  355.     } else if (id <= Npc::npcAutoID) {
  356.         return getNpcByID(id);
  357.     }
  358.     return nullptr;
  359. }
  360.  
  361. Monster* Game::getMonsterByID(uint32_t id)
  362. {
  363.     if (id == 0) {
  364.         return nullptr;
  365.     }
  366.  
  367.     auto it = monsters.find(id);
  368.     if (it == monsters.end()) {
  369.         return nullptr;
  370.     }
  371.     return it->second;
  372. }
  373.  
  374. Npc* Game::getNpcByID(uint32_t id)
  375. {
  376.     if (id == 0) {
  377.         return nullptr;
  378.     }
  379.  
  380.     auto it = npcs.find(id);
  381.     if (it == npcs.end()) {
  382.         return nullptr;
  383.     }
  384.     return it->second;
  385. }
  386.  
  387. Player* Game::getPlayerByID(uint32_t id)
  388. {
  389.     if (id == 0) {
  390.         return nullptr;
  391.     }
  392.  
  393.     auto it = players.find(id);
  394.     if (it == players.end()) {
  395.         return nullptr;
  396.     }
  397.     return it->second;
  398. }
  399.  
  400. Creature* Game::getCreatureByName(const std::string& s)
  401. {
  402.     if (s.empty()) {
  403.         return nullptr;
  404.     }
  405.  
  406.     const std::string& lowerCaseName = asLowerCaseString(s);
  407.  
  408.     auto m_it = mappedPlayerNames.find(lowerCaseName);
  409.     if (m_it != mappedPlayerNames.end()) {
  410.         return m_it->second;
  411.     }
  412.  
  413.     for (const auto& it : npcs) {
  414.         if (lowerCaseName == asLowerCaseString(it.second->getName())) {
  415.             return it.second;
  416.         }
  417.     }
  418.  
  419.     for (const auto& it : monsters) {
  420.         if (lowerCaseName == asLowerCaseString(it.second->getName())) {
  421.             return it.second;
  422.         }
  423.     }
  424.     return nullptr;
  425. }
  426.  
  427. Npc* Game::getNpcByName(const std::string& s)
  428. {
  429.     if (s.empty()) {
  430.         return nullptr;
  431.     }
  432.  
  433.     const char* npcName = s.c_str();
  434.     for (const auto& it : npcs) {
  435.         if (strcasecmp(npcName, it.second->getName().c_str()) == 0) {
  436.             return it.second;
  437.         }
  438.     }
  439.     return nullptr;
  440. }
  441.  
  442. Player* Game::getPlayerByName(const std::string& s)
  443. {
  444.     if (s.empty()) {
  445.         return nullptr;
  446.     }
  447.  
  448.     auto it = mappedPlayerNames.find(asLowerCaseString(s));
  449.     if (it == mappedPlayerNames.end()) {
  450.         return nullptr;
  451.     }
  452.     return it->second;
  453. }
  454.  
  455. Player* Game::getPlayerByGUID(const uint32_t& guid)
  456. {
  457.     if (guid == 0) {
  458.         return nullptr;
  459.     }
  460.  
  461.     for (const auto& it : players) {
  462.         if (guid == it.second->getGUID()) {
  463.             return it.second;
  464.         }
  465.     }
  466.     return nullptr;
  467. }
  468.  
  469. ReturnValue Game::getPlayerByNameWildcard(const std::string& s, Player*& player)
  470. {
  471.     size_t strlen = s.length();
  472.     if (strlen == 0 || strlen > 20) {
  473.         return RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE;
  474.     }
  475.  
  476.     if (s.back() == '~') {
  477.         const std::string& query = asLowerCaseString(s.substr(0, strlen - 1));
  478.         std::string result;
  479.         ReturnValue ret = wildcardTree.findOne(query, result);
  480.         if (ret != RETURNVALUE_NOERROR) {
  481.             return ret;
  482.         }
  483.  
  484.         player = getPlayerByName(result);
  485.     } else {
  486.         player = getPlayerByName(s);
  487.     }
  488.  
  489.     if (!player) {
  490.         return RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE;
  491.     }
  492.  
  493.     return RETURNVALUE_NOERROR;
  494. }
  495.  
  496. Player* Game::getPlayerByAccount(uint32_t acc)
  497. {
  498.     for (const auto& it : players) {
  499.         if (it.second->getAccount() == acc) {
  500.             return it.second;
  501.         }
  502.     }
  503.     return nullptr;
  504. }
  505.  
  506. bool Game::internalPlaceCreature(Creature* creature, const Position& pos, bool extendedPos /*=false*/, bool forced /*= false*/)
  507. {
  508.     if (creature->getParent() != nullptr) {
  509.         return false;
  510.     }
  511.  
  512.     if (!map.placeCreature(pos, creature, extendedPos, forced)) {
  513.         return false;
  514.     }
  515.  
  516.     creature->incrementReferenceCounter();
  517.     creature->setID();
  518.     creature->addList();
  519.     return true;
  520. }
  521.  
  522. bool Game::placeCreature(Creature* creature, const Position& pos, bool extendedPos /*=false*/, bool forced /*= false*/)
  523. {
  524.     if (!internalPlaceCreature(creature, pos, extendedPos, forced)) {
  525.         return false;
  526.     }
  527.  
  528.     SpectatorVec list;
  529.     map.getSpectators(list, creature->getPosition(), true);
  530.     for (Creature* spectator : list) {
  531.         if (Player* tmpPlayer = spectator->getPlayer()) {
  532.             tmpPlayer->sendCreatureAppear(creature, creature->getPosition(), true);
  533.         }
  534.     }
  535.  
  536.     for (Creature* spectator : list) {
  537.         spectator->onCreatureAppear(creature, true);
  538.     }
  539.  
  540.     creature->getParent()->postAddNotification(creature, nullptr, 0);
  541.  
  542.     addCreatureCheck(creature);
  543.     creature->onPlacedCreature();
  544.     return true;
  545. }
  546.  
  547. bool Game::removeCreature(Creature* creature, bool isLogout/* = true*/)
  548. {
  549.     if (creature->isRemoved()) {
  550.         return false;
  551.     }
  552.  
  553.     Tile* tile = creature->getTile();
  554.  
  555.     std::vector<int32_t> oldStackPosVector;
  556.  
  557.     SpectatorVec list;
  558.     map.getSpectators(list, tile->getPosition(), true);
  559.     for (Creature* spectator : list) {
  560.         if (Player* player = spectator->getPlayer()) {
  561.             oldStackPosVector.push_back(player->canSeeCreature(creature) ? tile->getStackposOfCreature(player, creature) : -1);
  562.         }
  563.     }
  564.  
  565.     tile->removeCreature(creature);
  566.  
  567.     const Position& tilePosition = tile->getPosition();
  568.  
  569.     //send to client
  570.     size_t i = 0;
  571.     for (Creature* spectator : list) {
  572.         if (Player* player = spectator->getPlayer()) {
  573.             player->sendRemoveTileThing(tilePosition, oldStackPosVector[i++]);
  574.         }
  575.     }
  576.  
  577.     //event method
  578.     for (Creature* spectator : list) {
  579.         spectator->onRemoveCreature(creature, isLogout);
  580.     }
  581.  
  582.     creature->getParent()->postRemoveNotification(creature, nullptr, 0);
  583.  
  584.     creature->removeList();
  585.     creature->setRemoved();
  586.     ReleaseCreature(creature);
  587.  
  588.     removeCreatureCheck(creature);
  589.  
  590.     for (Creature* summon : creature->summons) {
  591.         summon->setLossSkill(false);
  592.         removeCreature(summon);
  593.     }
  594.     return true;
  595. }
  596.  
  597. void Game::playerMoveThing(uint32_t playerId, const Position& fromPos,
  598.                            uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count)
  599. {
  600.     Player* player = getPlayerByID(playerId);
  601.     if (!player) {
  602.         return;
  603.     }
  604.  
  605.     uint8_t fromIndex = 0;
  606.     if (fromPos.x == 0xFFFF) {
  607.         if (fromPos.y & 0x40) {
  608.             fromIndex = fromPos.z;
  609.         } else {
  610.             fromIndex = static_cast<uint8_t>(fromPos.y);
  611.         }
  612.     } else {
  613.         fromIndex = fromStackPos;
  614.     }
  615.  
  616.     Thing* thing = internalGetThing(player, fromPos, fromIndex, 0, STACKPOS_MOVE);
  617.     if (!thing) {
  618.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  619.         return;
  620.     }
  621.  
  622.     if (Creature* movingCreature = thing->getCreature()) {
  623.         Tile* tile = map.getTile(toPos);
  624.         if (!tile) {
  625.             player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  626.             return;
  627.         }
  628.  
  629.         if (Position::areInRange<1, 1, 0>(movingCreature->getPosition(), player->getPosition())) {
  630.             SchedulerTask* task = createSchedulerTask(1000,
  631.                                   std::bind(&Game::playerMoveCreatureByID, this, player->getID(),
  632.                                               movingCreature->getID(), movingCreature->getPosition(), tile->getPosition()));
  633.             player->setNextActionTask(task);
  634.         } else {
  635.             playerMoveCreature(player, movingCreature, movingCreature->getPosition(), tile);
  636.         }
  637.     } else if (thing->getItem()) {
  638.         Cylinder* toCylinder = internalGetCylinder(player, toPos);
  639.         if (!toCylinder) {
  640.             player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  641.             return;
  642.         }
  643.  
  644.         playerMoveItem(player, fromPos, spriteId, fromStackPos, toPos, count, thing->getItem(), toCylinder);
  645.     }
  646. }
  647.  
  648. void Game::playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, const Position& movingCreatureOrigPos, const Position& toPos)
  649. {
  650.     Player* player = getPlayerByID(playerId);
  651.     if (!player) {
  652.         return;
  653.     }
  654.  
  655.     Creature* movingCreature = getCreatureByID(movingCreatureId);
  656.     if (!movingCreature) {
  657.         return;
  658.     }
  659.  
  660.     Tile* toTile = map.getTile(toPos);
  661.     if (!toTile) {
  662.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  663.         return;
  664.     }
  665.  
  666.     playerMoveCreature(player, movingCreature, movingCreatureOrigPos, toTile);
  667. }
  668.  
  669. void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile)
  670. {
  671.     if (!player->canDoAction()) {
  672.         uint32_t delay = player->getNextActionTime();
  673.         SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()));
  674.         player->setNextActionTask(task);
  675.         return;
  676.     }
  677.  
  678.     player->setNextActionTask(nullptr);
  679.  
  680.     if (!Position::areInRange<1, 1, 0>(movingCreatureOrigPos, player->getPosition())) {
  681.         //need to walk to the creature first before moving it
  682.         std::forward_list<Direction> listDir;
  683.         if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) {
  684.             g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)));
  685.             SchedulerTask* task = createSchedulerTask(1500, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition())); //1500 push
  686.             player->setNextWalkActionTask(task);
  687.         } else {
  688.             player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  689.         }
  690.         return;
  691.     }
  692.  
  693.     if ((!movingCreature->isPushable() && !player->hasFlag(PlayerFlag_CanPushAllCreatures)) ||
  694.             (movingCreature->isInGhostMode() && !player->isAccessPlayer())) {
  695.         player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE);
  696.         return;
  697.     }
  698.  
  699.     //check throw distance
  700.     const Position& movingCreaturePos = movingCreature->getPosition();
  701.     const Position& toPos = toTile->getPosition();
  702.     if ((Position::getDistanceX(movingCreaturePos, toPos) > movingCreature->getThrowRange()) || (Position::getDistanceY(movingCreaturePos, toPos) > movingCreature->getThrowRange()) || (Position::getDistanceZ(movingCreaturePos, toPos) * 4 > movingCreature->getThrowRange())) {
  703.         player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
  704.         return;
  705.     }
  706.  
  707.     if (player != movingCreature) {
  708.         if (toTile->hasFlag(TILESTATE_BLOCKPATH)) {
  709.             player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
  710.             return;
  711.         } else if ((movingCreature->getZone() == ZONE_PROTECTION && !toTile->hasFlag(TILESTATE_PROTECTIONZONE)) || (movingCreature->getZone() == ZONE_NOPVP && !toTile->hasFlag(TILESTATE_NOPVPZONE))) {
  712.             player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  713.             return;
  714.         } else {
  715.             if (CreatureVector* tileCreatures = toTile->getCreatures()) {
  716.                 for (Creature* tileCreature : *tileCreatures) {
  717.                     if (!tileCreature->isInGhostMode()) {
  718.                         player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
  719.                         return;
  720.                     }
  721.                 }
  722.             }
  723.  
  724.             Npc* movingNpc = movingCreature->getNpc();
  725.             if (movingNpc && !Spawns::isInZone(movingNpc->getMasterPos(), movingNpc->getMasterRadius(), toPos)) {
  726.                 player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
  727.                 return;
  728.             }
  729.         }
  730.     }
  731.  
  732.     if (!g_events->eventPlayerOnMoveCreature(player, movingCreature, movingCreaturePos, toPos)) {
  733.         return;
  734.     }
  735.  
  736.     ReturnValue ret = internalMoveCreature(*movingCreature, *toTile);
  737.     if (ret != RETURNVALUE_NOERROR) {
  738.         player->sendCancelMessage(ret);
  739.     }
  740. }
  741.  
  742. ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, uint32_t flags /*= 0*/)
  743. {
  744.     const Position& currentPos = creature->getPosition();
  745.     Position destPos = getNextPosition(direction, currentPos);
  746.  
  747.     bool diagonalMovement = (direction & DIRECTION_DIAGONAL_MASK) != 0;
  748.     if (creature->getPlayer() && !diagonalMovement) {
  749.         //try go up
  750.         if (currentPos.z != 8 && creature->getTile()->hasHeight(3)) {
  751.             Tile* tmpTile = map.getTile(currentPos.x, currentPos.y, currentPos.getZ() - 1);
  752.             if (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID))) {
  753.                 tmpTile = map.getTile(destPos.x, destPos.y, destPos.getZ() - 1);
  754.                 if (tmpTile && tmpTile->getGround() && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)) {
  755.                     flags |= FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
  756.  
  757.                     if (!tmpTile->hasFlag(TILESTATE_FLOORCHANGE)) {
  758.                         destPos.z--;
  759.                     }
  760.                 }
  761.             }
  762.         } else {
  763.             //try go down
  764.             Tile* tmpTile = map.getTile(destPos.x, destPos.y, destPos.z);
  765.             if (currentPos.z != 7 && (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)))) {
  766.                 tmpTile = map.getTile(destPos.x, destPos.y, destPos.z + 1);
  767.                 if (tmpTile && tmpTile->hasHeight(3)) {
  768.                     flags |= FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
  769.                     destPos.z++;
  770.                 }
  771.             }
  772.         }
  773.     }
  774.  
  775.     Tile* toTile = map.getTile(destPos);
  776.     if (!toTile) {
  777.         return RETURNVALUE_NOTPOSSIBLE;
  778.     }
  779.     return internalMoveCreature(*creature, *toTile, flags);
  780. }
  781.  
  782. ReturnValue Game::internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags /*= 0*/)
  783. {
  784.     //check if we can move the creature to the destination
  785.     ReturnValue ret = toTile.queryAdd(0, creature, 1, flags);
  786.     if (ret != RETURNVALUE_NOERROR) {
  787.         return ret;
  788.     }
  789.  
  790.     map.moveCreature(creature, toTile);
  791.     if (creature.getParent() != &toTile) {
  792.         return RETURNVALUE_NOERROR;
  793.     }
  794.  
  795.     int32_t index = 0;
  796.     Item* toItem = nullptr;
  797.     Tile* subCylinder = nullptr;
  798.     Tile* toCylinder = &toTile;
  799.     Tile* fromCylinder = nullptr;
  800.     uint32_t n = 0;
  801.  
  802.     while ((subCylinder = toCylinder->queryDestination(index, creature, &toItem, flags)) != toCylinder) {
  803.         map.moveCreature(creature, *subCylinder);
  804.  
  805.         if (creature.getParent() != subCylinder) {
  806.             //could happen if a script move the creature
  807.             fromCylinder = nullptr;
  808.             break;
  809.         }
  810.  
  811.         fromCylinder = toCylinder;
  812.         toCylinder = subCylinder;
  813.         flags = 0;
  814.  
  815.         //to prevent infinite loop
  816.         if (++n >= MAP_MAX_LAYERS) {
  817.             break;
  818.         }
  819.     }
  820.  
  821.     if (fromCylinder) {
  822.         const Position& fromPosition = fromCylinder->getPosition();
  823.         const Position& toPosition = toCylinder->getPosition();
  824.         if (fromPosition.z != toPosition.z && (fromPosition.x != toPosition.x || fromPosition.y != toPosition.y)) {
  825.             Direction dir = getDirectionTo(fromPosition, toPosition);
  826.             if ((dir & DIRECTION_DIAGONAL_MASK) == 0) {
  827.                 internalCreatureTurn(&creature, dir);
  828.             }
  829.         }
  830.     }
  831.  
  832.     return RETURNVALUE_NOERROR;
  833. }
  834.  
  835. void Game::playerMoveItemByPlayerID(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count)
  836. {
  837.     Player* player = getPlayerByID(playerId);
  838.     if (!player) {
  839.         return;
  840.     }
  841.     playerMoveItem(player, fromPos, spriteId, fromStackPos, toPos, count, nullptr, nullptr);
  842. }
  843.  
  844. void Game::playerMoveItem(Player* player, const Position& fromPos,
  845.                           uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count, Item* item, Cylinder* toCylinder)
  846. {
  847.     if (!player->canDoAction()) {
  848.         uint32_t delay = player->getNextActionTime();
  849.         SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerMoveItemByPlayerID, this,
  850.                               player->getID(), fromPos, spriteId, fromStackPos, toPos, count));
  851.         player->setNextActionTask(task);
  852.         return;
  853.     }
  854.  
  855.     player->setNextActionTask(nullptr);
  856.  
  857.     if (item == nullptr) {
  858.         uint8_t fromIndex = 0;
  859.         if (fromPos.x == 0xFFFF) {
  860.             if (fromPos.y & 0x40) {
  861.                 fromIndex = fromPos.z;
  862.             } else {
  863.                 fromIndex = static_cast<uint8_t>(fromPos.y);
  864.             }
  865.         } else {
  866.             fromIndex = fromStackPos;
  867.         }
  868.  
  869.         Thing* thing = internalGetThing(player, fromPos, fromIndex, 0, STACKPOS_MOVE);
  870.         if (!thing || !thing->getItem()) {
  871.             player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  872.             return;
  873.         }
  874.  
  875.         item = thing->getItem();
  876.     }
  877.  
  878.     if (item->getClientID() != spriteId) {
  879.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  880.         return;
  881.     }
  882.  
  883.     Cylinder* fromCylinder = internalGetCylinder(player, fromPos);
  884.     if (fromCylinder == nullptr) {
  885.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  886.         return;
  887.     }
  888.  
  889.     if (toCylinder == nullptr) {
  890.         toCylinder = internalGetCylinder(player, toPos);
  891.         if (toCylinder == nullptr) {
  892.             player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  893.             return;
  894.         }
  895.     }
  896.  
  897.     if (!item->isPushable() || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  898.         player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE);
  899.         return;
  900.     }
  901.  
  902.     const Position& playerPos = player->getPosition();
  903.     const Position& mapFromPos = fromCylinder->getTile()->getPosition();
  904.     if (playerPos.z != mapFromPos.z) {
  905.         player->sendCancelMessage(playerPos.z > mapFromPos.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS);
  906.         return;
  907.     }
  908.  
  909.     if (!Position::areInRange<1, 1>(playerPos, mapFromPos)) {
  910.         //need to walk to the item first before using it
  911.         std::forward_list<Direction> listDir;
  912.         if (player->getPathTo(item->getPosition(), listDir, 0, 1, true, true)) {
  913.             g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  914.                                             this, player->getID(), listDir)));
  915.  
  916.             SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this,
  917.                                   player->getID(), fromPos, spriteId, fromStackPos, toPos, count));
  918.             player->setNextWalkActionTask(task);
  919.         } else {
  920.             player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  921.         }
  922.         return;
  923.     }
  924.  
  925.     const Tile* toCylinderTile = toCylinder->getTile();
  926.     const Position& mapToPos = toCylinderTile->getPosition();
  927.  
  928.     //hangable item specific code
  929.     if (item->isHangable() && toCylinderTile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) {
  930.         //destination supports hangable objects so need to move there first
  931.         bool vertical = toCylinderTile->hasProperty(CONST_PROP_ISVERTICAL);
  932.         if (vertical) {
  933.             if (playerPos.x + 1 == mapToPos.x) {
  934.                 player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  935.                 return;
  936.             }
  937.         } else { // horizontal
  938.             if (playerPos.y + 1 == mapToPos.y) {
  939.                 player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  940.                 return;
  941.             }
  942.         }
  943.  
  944.         if (!Position::areInRange<1, 1, 0>(playerPos, mapToPos)) {
  945.             Position walkPos = mapToPos;
  946.             if (vertical) {
  947.                 walkPos.x++;
  948.             } else {
  949.                 walkPos.y++;
  950.             }
  951.  
  952.             Position itemPos = fromPos;
  953.             uint8_t itemStackPos = fromStackPos;
  954.  
  955.             if (fromPos.x != 0xFFFF && Position::areInRange<1, 1>(mapFromPos, playerPos)
  956.                     && !Position::areInRange<1, 1, 0>(mapFromPos, walkPos)) {
  957.                 //need to pickup the item first
  958.                 Item* moveItem = nullptr;
  959.  
  960.                 ReturnValue ret = internalMoveItem(fromCylinder, player, INDEX_WHEREEVER, item, count, &moveItem);
  961.                 if (ret != RETURNVALUE_NOERROR) {
  962.                     player->sendCancelMessage(ret);
  963.                     return;
  964.                 }
  965.  
  966.                 //changing the position since its now in the inventory of the player
  967.                 internalGetPosition(moveItem, itemPos, itemStackPos);
  968.             }
  969.  
  970.             std::forward_list<Direction> listDir;
  971.             if (player->getPathTo(walkPos, listDir, 0, 0, true, true)) {
  972.                 g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  973.                                                 this, player->getID(), listDir)));
  974.  
  975.                 SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this,
  976.                                       player->getID(), itemPos, spriteId, itemStackPos, toPos, count));
  977.                 player->setNextWalkActionTask(task);
  978.             } else {
  979.                 player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  980.             }
  981.             return;
  982.         }
  983.     }
  984.  
  985.     if ((Position::getDistanceX(playerPos, mapToPos) > item->getThrowRange()) ||
  986.             (Position::getDistanceY(playerPos, mapToPos) > item->getThrowRange()) ||
  987.             (Position::getDistanceZ(mapFromPos, mapToPos) * 4 > item->getThrowRange())) {
  988.         player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
  989.         return;
  990.     }
  991.  
  992.     if (!canThrowObjectTo(mapFromPos, mapToPos, true, MAPPATH_ITEM)) {
  993.         player->sendCancelMessage(RETURNVALUE_CANNOTTHROW);
  994.         return;
  995.     }
  996.  
  997.     if (!g_events->eventPlayerOnMoveItem(player, item, count, fromPos, toPos)) {
  998.         return;
  999.     }
  1000.  
  1001.     uint8_t toIndex = 0;
  1002.     if (toPos.x == 0xFFFF) {
  1003.         if (toPos.y & 0x40) {
  1004.             toIndex = toPos.z;
  1005.         } else {
  1006.             toIndex = static_cast<uint8_t>(toPos.y);
  1007.         }
  1008.     }
  1009.  
  1010.     ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, item->isRune() ? item->getItemCount() : count, nullptr, 0, player);
  1011.     if (ret != RETURNVALUE_NOERROR) {
  1012.         player->sendCancelMessage(ret);
  1013.     }
  1014. }
  1015.  
  1016. ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, int32_t index,
  1017.                                    Item* item, uint32_t count, Item** _moveItem, uint32_t flags /*= 0*/, Creature* actor/* = nullptr*/, Item* tradeItem/* = nullptr*/)
  1018. {
  1019.     Item* toItem = nullptr;
  1020.  
  1021.     Cylinder* subCylinder;
  1022.     int floorN = 0;
  1023.  
  1024.     while ((subCylinder = toCylinder->queryDestination(index, *item, &toItem, flags)) != toCylinder) {
  1025.         toCylinder = subCylinder;
  1026.         flags = 0;
  1027.  
  1028.         //to prevent infinite loop
  1029.         if (++floorN >= MAP_MAX_LAYERS) {
  1030.             break;
  1031.         }
  1032.     }
  1033.  
  1034.     //destination is the same as the source?
  1035.     if (item == toItem) {
  1036.         return RETURNVALUE_NOERROR;    //silently ignore move
  1037.     }
  1038.  
  1039.     //check if we can add this item
  1040.     ReturnValue ret = toCylinder->queryAdd(index, *item, count, flags, actor);
  1041.     if (ret == RETURNVALUE_NEEDEXCHANGE) {
  1042.         //check if we can add it to source cylinder
  1043.         ret = fromCylinder->queryAdd(fromCylinder->getThingIndex(item), *toItem, toItem->getItemCount(), 0);
  1044.         if (ret == RETURNVALUE_NOERROR) {
  1045.             //check how much we can move
  1046.             uint32_t maxExchangeQueryCount = 0;
  1047.             ReturnValue retExchangeMaxCount = fromCylinder->queryMaxCount(INDEX_WHEREEVER, *toItem, toItem->getItemCount(), maxExchangeQueryCount, 0);
  1048.  
  1049.             if (retExchangeMaxCount != RETURNVALUE_NOERROR && maxExchangeQueryCount == 0) {
  1050.                 return retExchangeMaxCount;
  1051.             }
  1052.  
  1053.             if (toCylinder->queryRemove(*toItem, toItem->getItemCount(), flags) == RETURNVALUE_NOERROR) {
  1054.                 int32_t oldToItemIndex = toCylinder->getThingIndex(toItem);
  1055.                 toCylinder->removeThing(toItem, toItem->getItemCount());
  1056.                 fromCylinder->addThing(toItem);
  1057.  
  1058.                 if (oldToItemIndex != -1) {
  1059.                     toCylinder->postRemoveNotification(toItem, fromCylinder, oldToItemIndex);
  1060.                 }
  1061.  
  1062.                 int32_t newToItemIndex = fromCylinder->getThingIndex(toItem);
  1063.                 if (newToItemIndex != -1) {
  1064.                     fromCylinder->postAddNotification(toItem, toCylinder, newToItemIndex);
  1065.                 }
  1066.  
  1067.                 ret = toCylinder->queryAdd(index, *item, count, flags);
  1068.                 toItem = nullptr;
  1069.             }
  1070.         }
  1071.     }
  1072.  
  1073.     if (ret != RETURNVALUE_NOERROR) {
  1074.         return ret;
  1075.     }
  1076.  
  1077.     //check how much we can move
  1078.     uint32_t maxQueryCount = 0;
  1079.     ReturnValue retMaxCount = toCylinder->queryMaxCount(index, *item, count, maxQueryCount, flags);
  1080.     if (retMaxCount != RETURNVALUE_NOERROR && maxQueryCount == 0) {
  1081.         return retMaxCount;
  1082.     }
  1083.  
  1084.     uint32_t m;
  1085.     if (item->isStackable()) {
  1086.         if (item->isRune()) {
  1087.             m = std::min<uint32_t>(item->getItemCount(), maxQueryCount);
  1088.         } else {
  1089.             m = std::min<uint32_t>(count, maxQueryCount);
  1090.         }
  1091.     } else {
  1092.         m = maxQueryCount;
  1093.     }
  1094.  
  1095.     Item* moveItem = item;
  1096.  
  1097.     //check if we can remove this item
  1098.     ret = fromCylinder->queryRemove(*item, m, flags);
  1099.     if (ret != RETURNVALUE_NOERROR) {
  1100.         return ret;
  1101.     }
  1102.  
  1103.     if (tradeItem) {
  1104.         if (toCylinder->getItem() == tradeItem) {
  1105.             return RETURNVALUE_NOTENOUGHROOM;
  1106.         }
  1107.  
  1108.         Cylinder* tmpCylinder = toCylinder->getParent();
  1109.         while (tmpCylinder) {
  1110.             if (tmpCylinder->getItem() == tradeItem) {
  1111.                 return RETURNVALUE_NOTENOUGHROOM;
  1112.             }
  1113.  
  1114.             tmpCylinder = tmpCylinder->getParent();
  1115.         }
  1116.     }
  1117.  
  1118.     //remove the item
  1119.     int32_t itemIndex = fromCylinder->getThingIndex(item);
  1120.     Item* updateItem = nullptr;
  1121.     fromCylinder->removeThing(item, m);
  1122.  
  1123.     //update item(s)
  1124.     if (item->isStackable()) {
  1125.         uint32_t n;
  1126.  
  1127.         if (!item->isRune() && item->equals(toItem)) {
  1128.             n = std::min<uint32_t>(100 - toItem->getItemCount(), m);
  1129.             toCylinder->updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
  1130.             updateItem = toItem;
  1131.         } else {
  1132.             n = 0;
  1133.         }
  1134.  
  1135.         int32_t newCount = m - n;
  1136.         if (newCount > 0) {
  1137.             moveItem = item->clone();
  1138.             moveItem->setItemCount(newCount);
  1139.         } else {
  1140.             moveItem = nullptr;
  1141.         }
  1142.  
  1143.         if (item->isRemoved()) {
  1144.             ReleaseItem(item);
  1145.         }
  1146.     }
  1147.  
  1148.     //add item
  1149.     if (moveItem /*m - n > 0*/) {
  1150.         toCylinder->addThing(index, moveItem);
  1151.     }
  1152.  
  1153.     if (itemIndex != -1) {
  1154.         fromCylinder->postRemoveNotification(item, toCylinder, itemIndex);
  1155.     }
  1156.  
  1157.     if (moveItem) {
  1158.         int32_t moveItemIndex = toCylinder->getThingIndex(moveItem);
  1159.         if (moveItemIndex != -1) {
  1160.             toCylinder->postAddNotification(moveItem, fromCylinder, moveItemIndex);
  1161.         }
  1162.     }
  1163.  
  1164.     if (updateItem) {
  1165.         int32_t updateItemIndex = toCylinder->getThingIndex(updateItem);
  1166.         if (updateItemIndex != -1) {
  1167.             toCylinder->postAddNotification(updateItem, fromCylinder, updateItemIndex);
  1168.         }
  1169.     }
  1170.  
  1171.     if (_moveItem) {
  1172.         if (moveItem) {
  1173.             *_moveItem = moveItem;
  1174.         } else {
  1175.             *_moveItem = item;
  1176.         }
  1177.     }
  1178.  
  1179.     //we could not move all, inform the player
  1180.     if (item->isStackable() && maxQueryCount < count) {
  1181.         return retMaxCount;
  1182.     }
  1183.  
  1184.     return ret;
  1185. }
  1186.  
  1187. ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index /*= INDEX_WHEREEVER*/, uint32_t flags/* = 0*/, bool test/* = false*/)
  1188. {
  1189.     uint32_t remainderCount = 0;
  1190.     return internalAddItem(toCylinder, item, index, flags, test, remainderCount);
  1191. }
  1192.  
  1193. ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index, uint32_t flags, bool test, uint32_t& remainderCount)
  1194. {
  1195.     if (toCylinder == nullptr || item == nullptr) {
  1196.         return RETURNVALUE_NOTPOSSIBLE;
  1197.     }
  1198.  
  1199.     Cylinder* destCylinder = toCylinder;
  1200.     Item* toItem = nullptr;
  1201.     toCylinder = toCylinder->queryDestination(index, *item, &toItem, flags);
  1202.  
  1203.     //check if we can add this item
  1204.     ReturnValue ret = toCylinder->queryAdd(index, *item, item->getItemCount(), flags);
  1205.     if (ret != RETURNVALUE_NOERROR) {
  1206.         return ret;
  1207.     }
  1208.  
  1209.     /*
  1210.     Check if we can move add the whole amount, we do this by checking against the original cylinder,
  1211.     since the queryDestination can return a cylinder that might only hold a part of the full amount.
  1212.     */
  1213.     uint32_t maxQueryCount = 0;
  1214.     ret = destCylinder->queryMaxCount(INDEX_WHEREEVER, *item, item->getItemCount(), maxQueryCount, flags);
  1215.  
  1216.     if (ret != RETURNVALUE_NOERROR) {
  1217.         return ret;
  1218.     }
  1219.  
  1220.     if (test) {
  1221.         return RETURNVALUE_NOERROR;
  1222.     }
  1223.  
  1224.     if (item->isStackable() && !item->isRune() && item->equals(toItem)) {
  1225.         uint32_t m = std::min<uint32_t>(item->getItemCount(), maxQueryCount);
  1226.         uint32_t n = std::min<uint32_t>(100 - toItem->getItemCount(), m);
  1227.  
  1228.         toCylinder->updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
  1229.  
  1230.         int32_t count = m - n;
  1231.         if (count > 0) {
  1232.             if (item->getItemCount() != count) {
  1233.                 Item* remainderItem = item->clone();
  1234.                 remainderItem->setItemCount(count);
  1235.                 if (internalAddItem(destCylinder, remainderItem, INDEX_WHEREEVER, flags, false) != RETURNVALUE_NOERROR) {
  1236.                     ReleaseItem(remainderItem);
  1237.                     remainderCount = count;
  1238.                 }
  1239.             } else {
  1240.                 toCylinder->addThing(index, item);
  1241.  
  1242.                 int32_t itemIndex = toCylinder->getThingIndex(item);
  1243.                 if (itemIndex != -1) {
  1244.                     toCylinder->postAddNotification(item, nullptr, itemIndex);
  1245.                 }
  1246.             }
  1247.         } else {
  1248.             //fully merged with toItem, item will be destroyed
  1249.             item->onRemoved();
  1250.             ReleaseItem(item);
  1251.  
  1252.             int32_t itemIndex = toCylinder->getThingIndex(toItem);
  1253.             if (itemIndex != -1) {
  1254.                 toCylinder->postAddNotification(toItem, nullptr, itemIndex);
  1255.             }
  1256.         }
  1257.     } else {
  1258.         toCylinder->addThing(index, item);
  1259.  
  1260.         int32_t itemIndex = toCylinder->getThingIndex(item);
  1261.         if (itemIndex != -1) {
  1262.             toCylinder->postAddNotification(item, nullptr, itemIndex);
  1263.         }
  1264.     }
  1265.  
  1266.     return RETURNVALUE_NOERROR;
  1267. }
  1268.  
  1269. ReturnValue Game::internalRemoveItem(Item* item, int32_t count /*= -1*/, bool test /*= false*/, uint32_t flags /*= 0*/)
  1270. {
  1271.     Cylinder* cylinder = item->getParent();
  1272.     if (cylinder == nullptr) {
  1273.         return RETURNVALUE_NOTPOSSIBLE;
  1274.     }
  1275.  
  1276.     if (count == -1) {
  1277.         count = item->getItemCount();
  1278.     }
  1279.  
  1280.     //check if we can remove this item
  1281.     ReturnValue ret = cylinder->queryRemove(*item, count, flags | FLAG_IGNORENOTMOVEABLE);
  1282.     if (ret != RETURNVALUE_NOERROR) {
  1283.         return ret;
  1284.     }
  1285.  
  1286.     if (!item->canRemove()) {
  1287.         return RETURNVALUE_NOTPOSSIBLE;
  1288.     }
  1289.  
  1290.     if (!test) {
  1291.         int32_t index = cylinder->getThingIndex(item);
  1292.  
  1293.         //remove the item
  1294.         cylinder->removeThing(item, count);
  1295.  
  1296.         if (item->isRemoved()) {
  1297.             ReleaseItem(item);
  1298.         }
  1299.  
  1300.         cylinder->postRemoveNotification(item, nullptr, index);
  1301.     }
  1302.  
  1303.     item->onRemoved();
  1304.     return RETURNVALUE_NOERROR;
  1305. }
  1306.  
  1307. ReturnValue Game::internalPlayerAddItem(Player* player, Item* item, bool dropOnMap /*= true*/, slots_t slot /*= CONST_SLOT_WHEREEVER*/)
  1308. {
  1309.     uint32_t remainderCount = 0;
  1310.     ReturnValue ret = internalAddItem(player, item, static_cast<int32_t>(slot), 0, false, remainderCount);
  1311.     if (remainderCount != 0) {
  1312.         Item* remainderItem = Item::CreateItem(item->getID(), remainderCount);
  1313.         ReturnValue remaindRet = internalAddItem(player->getTile(), remainderItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1314.         if (remaindRet != RETURNVALUE_NOERROR) {
  1315.             ReleaseItem(remainderItem);
  1316.         }
  1317.     }
  1318.  
  1319.     if (ret != RETURNVALUE_NOERROR && dropOnMap) {
  1320.         ret = internalAddItem(player->getTile(), item, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1321.     }
  1322.  
  1323.     return ret;
  1324. }
  1325.  
  1326. Item* Game::findItemOfType(Cylinder* cylinder, uint16_t itemId,
  1327.                            bool depthSearch /*= true*/, int32_t subType /*= -1*/) const
  1328. {
  1329.     if (cylinder == nullptr) {
  1330.         return nullptr;
  1331.     }
  1332.  
  1333.     std::vector<Container*> containers;
  1334.     for (size_t i = cylinder->getFirstIndex(), j = cylinder->getLastIndex(); i < j; ++i) {
  1335.         Thing* thing = cylinder->getThing(i);
  1336.         if (!thing) {
  1337.             continue;
  1338.         }
  1339.  
  1340.         Item* item = thing->getItem();
  1341.         if (!item) {
  1342.             continue;
  1343.         }
  1344.  
  1345.         if (item->getID() == itemId && (subType == -1 || subType == item->getSubType())) {
  1346.             return item;
  1347.         }
  1348.  
  1349.         if (depthSearch) {
  1350.             Container* container = item->getContainer();
  1351.             if (container) {
  1352.                 containers.push_back(container);
  1353.             }
  1354.         }
  1355.     }
  1356.  
  1357.     size_t i = 0;
  1358.     while (i < containers.size()) {
  1359.         Container* container = containers[i++];
  1360.         for (Item* item : container->getItemList()) {
  1361.             if (item->getID() == itemId && (subType == -1 || subType == item->getSubType())) {
  1362.                 return item;
  1363.             }
  1364.  
  1365.             Container* subContainer = item->getContainer();
  1366.             if (subContainer) {
  1367.                 containers.push_back(subContainer);
  1368.             }
  1369.         }
  1370.     }
  1371.     return nullptr;
  1372. }
  1373.  
  1374. bool Game::removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
  1375. {
  1376.     if (cylinder == nullptr) {
  1377.         return false;
  1378.     }
  1379.  
  1380.     if (money == 0) {
  1381.         return true;
  1382.     }
  1383.  
  1384.     std::vector<Container*> containers;
  1385.  
  1386.     std::multimap<uint64_t, Item*> moneyMap;
  1387.     uint64_t moneyCount = 0;
  1388.  
  1389.     for (size_t i = cylinder->getFirstIndex(), j = cylinder->getLastIndex(); i < j; ++i) {
  1390.         Thing* thing = cylinder->getThing(i);
  1391.         if (!thing) {
  1392.             continue;
  1393.         }
  1394.  
  1395.         Item* item = thing->getItem();
  1396.         if (!item) {
  1397.             continue;
  1398.         }
  1399.  
  1400.         Container* container = item->getContainer();
  1401.         if (container) {
  1402.             containers.push_back(container);
  1403.         } else {
  1404.             const uint32_t worth = item->getWorth();
  1405.             if (worth != 0) {
  1406.                 moneyCount += worth;
  1407.                 moneyMap.emplace(worth, item);
  1408.             }
  1409.         }
  1410.     }
  1411.  
  1412.     size_t i = 0;
  1413.     while (i < containers.size()) {
  1414.         Container* container = containers[i++];
  1415.         for (Item* item : container->getItemList()) {
  1416.             Container* tmpContainer = item->getContainer();
  1417.             if (tmpContainer) {
  1418.                 containers.push_back(tmpContainer);
  1419.             } else {
  1420.                 const uint32_t worth = item->getWorth();
  1421.                 if (worth != 0) {
  1422.                     moneyCount += worth;
  1423.                     moneyMap.emplace(worth, item);
  1424.                 }
  1425.             }
  1426.         }
  1427.     }
  1428.  
  1429.     if (moneyCount < money) {
  1430.         return false;
  1431.     }
  1432.  
  1433.     for (const auto& moneyEntry : moneyMap) {
  1434.         Item* item = moneyEntry.second;
  1435.         if (moneyEntry.first < money) {
  1436.             internalRemoveItem(item);
  1437.             money -= moneyEntry.first;
  1438.         } else if (moneyEntry.first > money) {
  1439.             const uint32_t worth = moneyEntry.first / item->getItemCount();
  1440.             const uint32_t removeCount = (money / worth) + 1;
  1441.  
  1442.             addMoney(cylinder, (worth * removeCount) - money, flags);
  1443.             internalRemoveItem(item, removeCount);
  1444.             break;
  1445.         } else {
  1446.             internalRemoveItem(item);
  1447.             break;
  1448.         }
  1449.     }
  1450.     return true;
  1451. }
  1452.  
  1453. void Game::addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
  1454. {
  1455.     if (money == 0) {
  1456.         return;
  1457.     }
  1458.  
  1459.     uint32_t crystalCoins = money / 10000;
  1460.     money -= crystalCoins * 10000;
  1461.     while (crystalCoins > 0) {
  1462.         const uint16_t count = std::min<uint32_t>(100, crystalCoins);
  1463.  
  1464.         Item* remaindItem = Item::CreateItem(ITEM_CRYSTAL_COIN, count);
  1465.  
  1466.         ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
  1467.         if (ret != RETURNVALUE_NOERROR) {
  1468.             internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1469.         }
  1470.  
  1471.         crystalCoins -= count;
  1472.     }
  1473.  
  1474.     uint16_t platinumCoins = money / 100;
  1475.     if (platinumCoins != 0) {
  1476.         Item* remaindItem = Item::CreateItem(ITEM_PLATINUM_COIN, platinumCoins);
  1477.  
  1478.         ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
  1479.         if (ret != RETURNVALUE_NOERROR) {
  1480.             internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1481.         }
  1482.  
  1483.         money -= platinumCoins * 100;
  1484.     }
  1485.  
  1486.     if (money != 0) {
  1487.         Item* remaindItem = Item::CreateItem(ITEM_GOLD_COIN, money);
  1488.  
  1489.         ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
  1490.         if (ret != RETURNVALUE_NOERROR) {
  1491.             internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1492.         }
  1493.     }
  1494. }
  1495.  
  1496. Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/)
  1497. {
  1498.     if (item->getID() == newId && (newCount == -1 || (newCount == item->getSubType() && newCount != 0))) { //chargeless item placed on map = infinite
  1499.         return item;
  1500.     }
  1501.  
  1502.     Cylinder* cylinder = item->getParent();
  1503.     if (cylinder == nullptr) {
  1504.         return nullptr;
  1505.     }
  1506.  
  1507.     int32_t itemIndex = cylinder->getThingIndex(item);
  1508.     if (itemIndex == -1) {
  1509.         return item;
  1510.     }
  1511.  
  1512.     if (!item->canTransform()) {
  1513.         return item;
  1514.     }
  1515.  
  1516.     const ItemType& newType = Item::items[newId];
  1517.     if (newType.id == 0) {
  1518.         return item;
  1519.     }
  1520.  
  1521.     const ItemType& curType = Item::items[item->getID()];
  1522.     if (curType.alwaysOnTop != newType.alwaysOnTop) {
  1523.         //This only occurs when you transform items on tiles from a downItem to a topItem (or vice versa)
  1524.         //Remove the old, and add the new
  1525.         cylinder->removeThing(item, item->getItemCount());
  1526.         cylinder->postRemoveNotification(item, cylinder, itemIndex);
  1527.  
  1528.         item->setID(newId);
  1529.         if (newCount != -1) {
  1530.             item->setSubType(newCount);
  1531.         }
  1532.         cylinder->addThing(item);
  1533.  
  1534.         Cylinder* newParent = item->getParent();
  1535.         if (newParent == nullptr) {
  1536.             ReleaseItem(item);
  1537.             return nullptr;
  1538.         }
  1539.  
  1540.         newParent->postAddNotification(item, cylinder, newParent->getThingIndex(item));
  1541.         return item;
  1542.     }
  1543.  
  1544.     if (curType.type == newType.type) {
  1545.         //Both items has the same type so we can safely change id/subtype
  1546.         if (newCount == 0 && (item->isStackable() || item->hasAttribute(ITEM_ATTRIBUTE_CHARGES))) {
  1547.             if (item->isStackable()) {
  1548.                 internalRemoveItem(item);
  1549.                 return nullptr;
  1550.             } else {
  1551.                 int32_t newItemId = newId;
  1552.                 if (curType.id == newType.id) {
  1553.                     newItemId = curType.decayTo;
  1554.                 }
  1555.  
  1556.                 if (newItemId < 0) {
  1557.                     internalRemoveItem(item);
  1558.                     return nullptr;
  1559.                 } else if (newItemId != newId) {
  1560.                     //Replacing the the old item with the new while maintaining the old position
  1561.                     Item* newItem = Item::CreateItem(newItemId, 1);
  1562.                     if (newItem == nullptr) {
  1563.                         return nullptr;
  1564.                     }
  1565.  
  1566.                     cylinder->replaceThing(itemIndex, newItem);
  1567.                     cylinder->postAddNotification(newItem, cylinder, itemIndex);
  1568.  
  1569.                     item->setParent(nullptr);
  1570.                     cylinder->postRemoveNotification(item, cylinder, itemIndex);
  1571.                     ReleaseItem(item);
  1572.                     return newItem;
  1573.                 } else {
  1574.                     return transformItem(item, newItemId);
  1575.                 }
  1576.             }
  1577.         } else {
  1578.             cylinder->postRemoveNotification(item, cylinder, itemIndex);
  1579.             uint16_t itemId = item->getID();
  1580.             int32_t count = item->getSubType();
  1581.  
  1582.             if (curType.id != newType.id) {
  1583.                 if (newType.group != curType.group) {
  1584.                     item->setDefaultSubtype();
  1585.                 }
  1586.  
  1587.                 itemId = newId;
  1588.             }
  1589.  
  1590.             if (newCount != -1 && newType.hasSubType()) {
  1591.                 count = newCount;
  1592.             }
  1593.  
  1594.             cylinder->updateThing(item, itemId, count);
  1595.             cylinder->postAddNotification(item, cylinder, itemIndex);
  1596.             return item;
  1597.         }
  1598.     }
  1599.  
  1600.     //Replacing the the old item with the new while maintaining the old position
  1601.     Item* newItem;
  1602.     if (newCount == -1) {
  1603.         newItem = Item::CreateItem(newId);
  1604.     } else {
  1605.         newItem = Item::CreateItem(newId, newCount);
  1606.     }
  1607.  
  1608.     if (newItem == nullptr) {
  1609.         return nullptr;
  1610.     }
  1611.  
  1612.     cylinder->replaceThing(itemIndex, newItem);
  1613.     cylinder->postAddNotification(newItem, cylinder, itemIndex);
  1614.  
  1615.     item->setParent(nullptr);
  1616.     cylinder->postRemoveNotification(item, cylinder, itemIndex);
  1617.     ReleaseItem(item);
  1618.  
  1619.     return newItem;
  1620. }
  1621.  
  1622. ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool pushMove/* = true*/, uint32_t flags /*= 0*/)
  1623. {
  1624.     if (newPos == thing->getPosition()) {
  1625.         return RETURNVALUE_NOERROR;
  1626.     } else if (thing->isRemoved()) {
  1627.         return RETURNVALUE_NOTPOSSIBLE;
  1628.     }
  1629.  
  1630.     Tile* toTile = map.getTile(newPos);
  1631.     if (!toTile) {
  1632.         return RETURNVALUE_NOTPOSSIBLE;
  1633.     }
  1634.  
  1635.     if (Creature* creature = thing->getCreature()) {
  1636.         ReturnValue ret = toTile->queryAdd(0, *creature, 1, FLAG_NOLIMIT);
  1637.         if (ret != RETURNVALUE_NOERROR) {
  1638.             return ret;
  1639.         }
  1640.  
  1641.         map.moveCreature(*creature, *toTile, !pushMove);
  1642.         return RETURNVALUE_NOERROR;
  1643.     } else if (Item* item = thing->getItem()) {
  1644.         return internalMoveItem(item->getParent(), toTile, INDEX_WHEREEVER, item, item->getItemCount(), nullptr, flags);
  1645.     }
  1646.     return RETURNVALUE_NOTPOSSIBLE;
  1647. }
  1648.  
  1649. //Implementation of player invoked events
  1650. void Game::playerMove(uint32_t playerId, Direction direction)
  1651. {
  1652.     Player* player = getPlayerByID(playerId);
  1653.     if (!player) {
  1654.         return;
  1655.     }
  1656.  
  1657.     player->resetIdleTime();
  1658.     player->setNextWalkActionTask(nullptr);
  1659.  
  1660.     player->startAutoWalk(std::forward_list<Direction> { direction });
  1661. }
  1662.  
  1663. bool Game::playerBroadcastMessage(Player* player, const std::string& text) const
  1664. {
  1665.     if (!player->hasFlag(PlayerFlag_CanBroadcast)) {
  1666.         return false;
  1667.     }
  1668.  
  1669.     std::cout << "> " << player->getName() << " broadcasted: \"" << text << "\"." << std::endl;
  1670.  
  1671.     for (const auto& it : players) {
  1672.         it.second->sendPrivateMessage(player, TALKTYPE_BROADCAST, text);
  1673.     }
  1674.  
  1675.     return true;
  1676. }
  1677.  
  1678. void Game::playerCreatePrivateChannel(uint32_t playerId)
  1679. {
  1680.     Player* player = getPlayerByID(playerId);
  1681.     if (!player || !player->isPremium()) {
  1682.         return;
  1683.     }
  1684.  
  1685.     ChatChannel* channel = g_chat->createChannel(*player, CHANNEL_PRIVATE);
  1686.     if (!channel || !channel->addUser(*player)) {
  1687.         return;
  1688.     }
  1689.  
  1690.     player->sendCreatePrivateChannel(channel->getId(), channel->getName());
  1691. }
  1692.  
  1693. void Game::playerChannelInvite(uint32_t playerId, const std::string& name)
  1694. {
  1695.     Player* player = getPlayerByID(playerId);
  1696.     if (!player) {
  1697.         return;
  1698.     }
  1699.  
  1700.     PrivateChatChannel* channel = g_chat->getPrivateChannel(*player);
  1701.     if (!channel) {
  1702.         return;
  1703.     }
  1704.  
  1705.     Player* invitePlayer = getPlayerByName(name);
  1706.     if (!invitePlayer) {
  1707.         return;
  1708.     }
  1709.  
  1710.     if (player == invitePlayer) {
  1711.         return;
  1712.     }
  1713.  
  1714.     channel->invitePlayer(*player, *invitePlayer);
  1715. }
  1716.  
  1717. void Game::playerChannelExclude(uint32_t playerId, const std::string& name)
  1718. {
  1719.     Player* player = getPlayerByID(playerId);
  1720.     if (!player) {
  1721.         return;
  1722.     }
  1723.  
  1724.     PrivateChatChannel* channel = g_chat->getPrivateChannel(*player);
  1725.     if (!channel) {
  1726.         return;
  1727.     }
  1728.  
  1729.     Player* excludePlayer = getPlayerByName(name);
  1730.     if (!excludePlayer) {
  1731.         return;
  1732.     }
  1733.  
  1734.     if (player == excludePlayer) {
  1735.         return;
  1736.     }
  1737.  
  1738.     channel->excludePlayer(*player, *excludePlayer);
  1739. }
  1740.  
  1741. void Game::playerRequestChannels(uint32_t playerId)
  1742. {
  1743.     Player* player = getPlayerByID(playerId);
  1744.     if (!player) {
  1745.         return;
  1746.     }
  1747.  
  1748.     player->sendChannelsDialog();
  1749. }
  1750.  
  1751. void Game::playerOpenChannel(uint32_t playerId, uint16_t channelId)
  1752. {
  1753.     Player* player = getPlayerByID(playerId);
  1754.     if (!player) {
  1755.         return;
  1756.     }
  1757.  
  1758.     ChatChannel* channel = g_chat->addUserToChannel(*player, channelId);
  1759.     if (!channel) {
  1760.         return;
  1761.     }
  1762.  
  1763.     const InvitedMap* invitedUsers = channel->getInvitedUsers();
  1764.     const UsersMap* users;
  1765.     if (!channel->isPublicChannel()) {
  1766.         users = &channel->getUsers();
  1767.     } else {
  1768.         users = nullptr;
  1769.     }
  1770.  
  1771.     player->sendChannel(channel->getId(), channel->getName(), users, invitedUsers);
  1772. }
  1773.  
  1774. void Game::playerCloseChannel(uint32_t playerId, uint16_t channelId)
  1775. {
  1776.     Player* player = getPlayerByID(playerId);
  1777.     if (!player) {
  1778.         return;
  1779.     }
  1780.  
  1781.     g_chat->removeUserFromChannel(*player, channelId);
  1782. }
  1783.  
  1784. void Game::playerOpenPrivateChannel(uint32_t playerId, std::string& receiver)
  1785. {
  1786.     Player* player = getPlayerByID(playerId);
  1787.     if (!player) {
  1788.         return;
  1789.     }
  1790.  
  1791.     if (!IOLoginData::formatPlayerName(receiver)) {
  1792.         player->sendCancelMessage("A player with this name does not exist.");
  1793.         return;
  1794.     }
  1795.  
  1796.     player->sendOpenPrivateChannel(receiver);
  1797. }
  1798.  
  1799. void Game::playerReceivePing(uint32_t playerId)
  1800. {
  1801.     Player* player = getPlayerByID(playerId);
  1802.     if (!player) {
  1803.         return;
  1804.     }
  1805.  
  1806.     player->receivePing();
  1807. }
  1808.  
  1809. void Game::playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir)
  1810. {
  1811.     Player* player = getPlayerByID(playerId);
  1812.     if (!player) {
  1813.         return;
  1814.     }
  1815.  
  1816.     player->resetIdleTime();
  1817.     player->setNextWalkTask(nullptr);
  1818.     player->startAutoWalk(listDir);
  1819. }
  1820.  
  1821. void Game::playerStopAutoWalk(uint32_t playerId)
  1822. {
  1823.     Player* player = getPlayerByID(playerId);
  1824.     if (!player) {
  1825.         return;
  1826.     }
  1827.  
  1828.     player->stopWalk();
  1829. }
  1830.  
  1831. void Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos, uint16_t fromSpriteId,
  1832.                            const Position& toPos, uint8_t toStackPos, uint16_t toSpriteId)
  1833. {
  1834.     Player* player = getPlayerByID(playerId);
  1835.     if (!player) {
  1836.         return;
  1837.     }
  1838.  
  1839.     bool isHotkey = (fromPos.x == 0xFFFF && fromPos.y == 0 && fromPos.z == 0);
  1840.     if (isHotkey && !g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) {
  1841.         return;
  1842.     }
  1843.  
  1844.     Thing* thing = internalGetThing(player, fromPos, fromStackPos, fromSpriteId, STACKPOS_USEITEM);
  1845.     if (!thing) {
  1846.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  1847.         return;
  1848.     }
  1849.  
  1850.     Item* item = thing->getItem();
  1851.     if (!item || !item->isUseable() || item->getClientID() != fromSpriteId) {
  1852.         player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
  1853.         return;
  1854.     }
  1855.  
  1856.     Position walkToPos = fromPos;
  1857.     ReturnValue ret = g_actions->canUse(player, fromPos);
  1858.     if (ret == RETURNVALUE_NOERROR) {
  1859.         ret = g_actions->canUse(player, toPos, item);
  1860.         if (ret == RETURNVALUE_TOOFARAWAY) {
  1861.             walkToPos = toPos;
  1862.         }
  1863.     }
  1864.  
  1865.     if (ret != RETURNVALUE_NOERROR) {
  1866.         if (ret == RETURNVALUE_TOOFARAWAY) {
  1867.             Position itemPos = fromPos;
  1868.             uint8_t itemStackPos = fromStackPos;
  1869.  
  1870.             if (fromPos.x != 0xFFFF && toPos.x != 0xFFFF && Position::areInRange<1, 1, 0>(fromPos, player->getPosition()) &&
  1871.                     !Position::areInRange<1, 1, 0>(fromPos, toPos)) {
  1872.                 Item* moveItem = nullptr;
  1873.  
  1874.                 ret = internalMoveItem(item->getParent(), player, INDEX_WHEREEVER, item, item->getItemCount(), &moveItem);
  1875.                 if (ret != RETURNVALUE_NOERROR) {
  1876.                     player->sendCancelMessage(ret);
  1877.                     return;
  1878.                 }
  1879.  
  1880.                 //changing the position since its now in the inventory of the player
  1881.                 internalGetPosition(moveItem, itemPos, itemStackPos);
  1882.             }
  1883.  
  1884.             std::forward_list<Direction> listDir;
  1885.             if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) {
  1886.                 g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)));
  1887.  
  1888.                 SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseItemEx, this,
  1889.                                       playerId, itemPos, itemStackPos, fromSpriteId, toPos, toStackPos, toSpriteId));
  1890.                 player->setNextWalkActionTask(task);
  1891.             } else {
  1892.                 player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  1893.             }
  1894.             return;
  1895.         }
  1896.  
  1897.         player->sendCancelMessage(ret);
  1898.         return;
  1899.     }
  1900.  
  1901.     if (!player->canDoAction()) {
  1902.         uint32_t delay = player->getNextActionTime();
  1903.         SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseItemEx, this,
  1904.                               playerId, fromPos, fromStackPos, fromSpriteId, toPos, toStackPos, toSpriteId));
  1905.         player->setNextActionTask(task);
  1906.         return;
  1907.     }
  1908.  
  1909.     player->resetIdleTime();
  1910.     player->setNextActionTask(nullptr);
  1911.  
  1912.     g_actions->useItemEx(player, fromPos, toPos, toStackPos, item, isHotkey);
  1913. }
  1914.  
  1915. void Game::playerUseItem(uint32_t playerId, const Position& pos, uint8_t stackPos,
  1916.     uint8_t index, uint16_t spriteId)
  1917. {
  1918.  
  1919.     Player* player = getPlayerByID(playerId);
  1920.     if (!player) {
  1921.         return;
  1922.     }
  1923.  
  1924.     bool isHotkey = (pos.x == 0xFFFF && pos.y == 0 && pos.z == 0);
  1925.     if (isHotkey && !g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) {
  1926.         return;
  1927.     }
  1928.  
  1929.     Thing* thing = internalGetThing(player, pos, stackPos, spriteId, STACKPOS_USEITEM);
  1930.     if (!thing) {
  1931.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  1932.         return;
  1933.     }
  1934.  
  1935.     Item* item = thing->getItem();
  1936.     if (!item || item->isUseable() || item->getClientID() != spriteId) {
  1937.         player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
  1938.         return;
  1939.     }
  1940.  
  1941.     ReturnValue ret = g_actions->canUse(player, pos);
  1942.     if (ret != RETURNVALUE_NOERROR) {
  1943.         if (ret == RETURNVALUE_TOOFARAWAY) {
  1944.             std::forward_list<Direction> listDir;
  1945.             if (player->getPathTo(pos, listDir, 0, 1, true, true)) {
  1946.                 g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)));
  1947.                 SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, spriteId));
  1948.                 player->setNextWalkActionTask(task);
  1949.                 return;
  1950.             }
  1951.  
  1952.             ret = RETURNVALUE_THEREISNOWAY;
  1953.         }
  1954.  
  1955.         player->sendCancelMessage(ret);
  1956.         return;
  1957.     }
  1958.  
  1959.     if (!player->canDoAction()) {
  1960.         uint32_t delay = player->getNextActionTime();
  1961.         SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, spriteId));
  1962.         player->setNextActionTask(task);
  1963.         return;
  1964.     }
  1965.  
  1966.     player->resetIdleTime();
  1967.     player->setNextActionTask(nullptr);
  1968.  
  1969.     g_actions->useItem(player, pos, index, item, isHotkey);
  1970. }
  1971.  
  1972. void Game::playerUseWithCreature(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos, uint32_t creatureId, uint16_t spriteId)
  1973. {
  1974.     Player* player = getPlayerByID(playerId);
  1975.     if (!player) {
  1976.         return;
  1977.     }
  1978.  
  1979.     Creature* creature = getCreatureByID(creatureId);
  1980.     if (!creature) {
  1981.         return;
  1982.     }
  1983.  
  1984.     if (!Position::areInRange<7, 5, 0>(creature->getPosition(), player->getPosition())) {
  1985.         return;
  1986.     }
  1987.  
  1988.     bool isHotkey = (fromPos.x == 0xFFFF && fromPos.y == 0 && fromPos.z == 0);
  1989.     if (!g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) {
  1990.         if (creature->getPlayer() || isHotkey) {
  1991.             player->sendCancelMessage(RETURNVALUE_DIRECTPLAYERSHOOT);
  1992.             return;
  1993.         }
  1994.     }
  1995.  
  1996.     Thing* thing = internalGetThing(player, fromPos, fromStackPos, spriteId, STACKPOS_USEITEM);
  1997.     if (!thing) {
  1998.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  1999.         return;
  2000.     }
  2001.  
  2002.     Item* item = thing->getItem();
  2003.     if (!item || !item->isUseable() || item->getClientID() != spriteId) {
  2004.         player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
  2005.         return;
  2006.     }
  2007.  
  2008.     Position toPos = creature->getPosition();
  2009.     Position walkToPos = fromPos;
  2010.     ReturnValue ret = g_actions->canUse(player, fromPos);
  2011.     if (ret == RETURNVALUE_NOERROR) {
  2012.         ret = g_actions->canUse(player, toPos, item);
  2013.         if (ret == RETURNVALUE_TOOFARAWAY) {
  2014.             walkToPos = toPos;
  2015.         }
  2016.     }
  2017.  
  2018.     if (ret != RETURNVALUE_NOERROR) {
  2019.         if (ret == RETURNVALUE_TOOFARAWAY) {
  2020.             Position itemPos = fromPos;
  2021.             uint8_t itemStackPos = fromStackPos;
  2022.  
  2023.             if (fromPos.x != 0xFFFF && Position::areInRange<1, 1, 0>(fromPos, player->getPosition()) && !Position::areInRange<1, 1, 0>(fromPos, toPos)) {
  2024.                 Item* moveItem = nullptr;
  2025.                 ret = internalMoveItem(item->getParent(), player, INDEX_WHEREEVER, item, item->getItemCount(), &moveItem);
  2026.                 if (ret != RETURNVALUE_NOERROR) {
  2027.                     player->sendCancelMessage(ret);
  2028.                     return;
  2029.                 }
  2030.  
  2031.                 //changing the position since its now in the inventory of the player
  2032.                 internalGetPosition(moveItem, itemPos, itemStackPos);
  2033.             }
  2034.  
  2035.             std::forward_list<Direction> listDir;
  2036.             if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) {
  2037.                 g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  2038.                                                 this, player->getID(), listDir)));
  2039.  
  2040.                 SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseWithCreature, this,
  2041.                                       playerId, itemPos, itemStackPos, creatureId, spriteId));
  2042.                 player->setNextWalkActionTask(task);
  2043.             } else {
  2044.                 player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  2045.             }
  2046.             return;
  2047.         }
  2048.  
  2049.         player->sendCancelMessage(ret);
  2050.         return;
  2051.     }
  2052.  
  2053.     if (!player->canDoAction()) {
  2054.         uint32_t delay = player->getNextActionTime();
  2055.         SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseWithCreature, this,
  2056.                               playerId, fromPos, fromStackPos, creatureId, spriteId));
  2057.         player->setNextActionTask(task);
  2058.         return;
  2059.     }
  2060.  
  2061.     player->resetIdleTime();
  2062.     player->setNextActionTask(nullptr);
  2063.  
  2064.     g_actions->useItemEx(player, fromPos, creature->getPosition(), creature->getParent()->getThingIndex(creature), item, isHotkey, creature);
  2065. }
  2066.  
  2067. void Game::playerCloseContainer(uint32_t playerId, uint8_t cid)
  2068. {
  2069.     Player* player = getPlayerByID(playerId);
  2070.     if (!player) {
  2071.         return;
  2072.     }
  2073.  
  2074.     player->closeContainer(cid);
  2075.     player->sendCloseContainer(cid);
  2076. }
  2077.  
  2078. void Game::playerMoveUpContainer(uint32_t playerId, uint8_t cid)
  2079. {
  2080.     Player* player = getPlayerByID(playerId);
  2081.     if (!player) {
  2082.         return;
  2083.     }
  2084.  
  2085.     Container* container = player->getContainerByID(cid);
  2086.     if (!container) {
  2087.         return;
  2088.     }
  2089.  
  2090.     Container* parentContainer = dynamic_cast<Container*>(container->getRealParent());
  2091.     if (!parentContainer) {
  2092.         return;
  2093.     }
  2094.  
  2095.     player->addContainer(cid, parentContainer);
  2096.     player->sendContainer(cid, parentContainer, parentContainer->hasParent(), player->getContainerIndex(cid));
  2097. }
  2098.  
  2099. void Game::playerUpdateContainer(uint32_t playerId, uint8_t cid)
  2100. {
  2101.     Player* player = getPlayerByID(playerId);
  2102.     if (!player) {
  2103.         return;
  2104.     }
  2105.  
  2106.     Container* container = player->getContainerByID(cid);
  2107.     if (!container) {
  2108.         return;
  2109.     }
  2110.  
  2111.     player->sendContainer(cid, container, container->hasParent(), player->getContainerIndex(cid));
  2112. }
  2113.  
  2114. void Game::playerRotateItem(uint32_t playerId, const Position& pos, uint8_t stackPos, const uint16_t spriteId)
  2115. {
  2116.     Player* player = getPlayerByID(playerId);
  2117.     if (!player) {
  2118.         return;
  2119.     }
  2120.  
  2121.     Thing* thing = internalGetThing(player, pos, stackPos, 0, STACKPOS_TOPDOWN_ITEM);
  2122.     if (!thing) {
  2123.         return;
  2124.     }
  2125.  
  2126.     Item* item = thing->getItem();
  2127.     if (!item || item->getClientID() != spriteId || !item->isRotatable() || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  2128.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2129.         return;
  2130.     }
  2131.  
  2132.     if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) {
  2133.         std::forward_list<Direction> listDir;
  2134.         if (player->getPathTo(pos, listDir, 0, 1, true, true)) {
  2135.             g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  2136.                                             this, player->getID(), listDir)));
  2137.  
  2138.             SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerRotateItem, this,
  2139.                                   playerId, pos, stackPos, spriteId));
  2140.             player->setNextWalkActionTask(task);
  2141.         } else {
  2142.             player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  2143.         }
  2144.         return;
  2145.     }
  2146.  
  2147.     uint16_t newId = Item::items[item->getID()].rotateTo;
  2148.     if (newId != 0) {
  2149.         transformItem(item, newId);
  2150.     }
  2151. }
  2152.  
  2153. void Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string& text)
  2154. {
  2155.     Player* player = getPlayerByID(playerId);
  2156.     if (!player) {
  2157.         return;
  2158.     }
  2159.  
  2160.     uint16_t maxTextLength = 0;
  2161.     uint32_t internalWindowTextId = 0;
  2162.  
  2163.     Item* writeItem = player->getWriteItem(internalWindowTextId, maxTextLength);
  2164.     if (text.length() > maxTextLength || windowTextId != internalWindowTextId) {
  2165.         return;
  2166.     }
  2167.  
  2168.     if (!writeItem || writeItem->isRemoved()) {
  2169.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2170.         return;
  2171.     }
  2172.  
  2173.     Cylinder* topParent = writeItem->getTopParent();
  2174.  
  2175.     Player* owner = dynamic_cast<Player*>(topParent);
  2176.     if (owner && owner != player) {
  2177.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2178.         return;
  2179.     }
  2180.  
  2181.     if (!Position::areInRange<1, 1, 0>(writeItem->getPosition(), player->getPosition())) {
  2182.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2183.         return;
  2184.     }
  2185.  
  2186.     for (auto creatureEvent : player->getCreatureEvents(CREATURE_EVENT_TEXTEDIT)) {
  2187.         if (!creatureEvent->executeTextEdit(player, writeItem, text)) {
  2188.             player->setWriteItem(nullptr);
  2189.             return;
  2190.         }
  2191.     }
  2192.  
  2193.     if (!text.empty()) {
  2194.         if (writeItem->getText() != text) {
  2195.             writeItem->setText(text);
  2196.             writeItem->setWriter(player->getName());
  2197.             writeItem->setDate(time(nullptr));
  2198.         }
  2199.     } else {
  2200.         writeItem->resetText();
  2201.         writeItem->resetWriter();
  2202.         writeItem->resetDate();
  2203.     }
  2204.  
  2205.     uint16_t newId = Item::items[writeItem->getID()].writeOnceItemId;
  2206.     if (newId != 0) {
  2207.         transformItem(writeItem, newId);
  2208.     }
  2209.  
  2210.     player->setWriteItem(nullptr);
  2211. }
  2212.  
  2213. void Game::playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string& text)
  2214. {
  2215.     Player* player = getPlayerByID(playerId);
  2216.     if (!player) {
  2217.         return;
  2218.     }
  2219.  
  2220.     uint32_t internalWindowTextId;
  2221.     uint32_t internalListId;
  2222.  
  2223.     House* house = player->getEditHouse(internalWindowTextId, internalListId);
  2224.     if (house && internalWindowTextId == windowTextId && listId == 0) {
  2225.         house->setAccessList(internalListId, text);
  2226.         player->setEditHouse(nullptr);
  2227.     }
  2228. }
  2229.  
  2230. void Game::playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t stackPos,
  2231.                               uint32_t tradePlayerId, uint16_t spriteId)
  2232. {
  2233.     Player* player = getPlayerByID(playerId);
  2234.     if (!player) {
  2235.         return;
  2236.     }
  2237.  
  2238.     Player* tradePartner = getPlayerByID(tradePlayerId);
  2239.     if (!tradePartner || tradePartner == player) {
  2240.         player->sendTextMessage(MESSAGE_INFO_DESCR, "Sorry, not possible.");
  2241.         return;
  2242.     }
  2243.  
  2244.     if (!Position::areInRange<2, 2, 0>(tradePartner->getPosition(), player->getPosition())) {
  2245.         std::ostringstream ss;
  2246.         ss << tradePartner->getName() << " tells you to move closer.";
  2247.         player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
  2248.         return;
  2249.     }
  2250.  
  2251.     if (!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())) {
  2252.         player->sendCancelMessage(RETURNVALUE_CREATUREISNOTREACHABLE);
  2253.         return;
  2254.     }
  2255.  
  2256.     Thing* tradeThing = internalGetThing(player, pos, stackPos, 0, STACKPOS_TOPDOWN_ITEM);
  2257.     if (!tradeThing) {
  2258.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2259.         return;
  2260.     }
  2261.  
  2262.     Item* tradeItem = tradeThing->getItem();
  2263.     if (tradeItem->getClientID() != spriteId || !tradeItem->isPickupable() || tradeItem->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  2264.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2265.         return;
  2266.     }
  2267.  
  2268.     const Position& playerPosition = player->getPosition();
  2269.     const Position& tradeItemPosition = tradeItem->getPosition();
  2270.     if (playerPosition.z != tradeItemPosition.z) {
  2271.         player->sendCancelMessage(playerPosition.z > tradeItemPosition.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS);
  2272.         return;
  2273.     }
  2274.  
  2275.     if (!Position::areInRange<1, 1>(tradeItemPosition, playerPosition)) {
  2276.         std::forward_list<Direction> listDir;
  2277.         if (player->getPathTo(pos, listDir, 0, 1, true, true)) {
  2278.             g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  2279.                                             this, player->getID(), listDir)));
  2280.  
  2281.             SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerRequestTrade, this,
  2282.                                   playerId, pos, stackPos, tradePlayerId, spriteId));
  2283.             player->setNextWalkActionTask(task);
  2284.         } else {
  2285.             player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  2286.         }
  2287.         return;
  2288.     }
  2289.  
  2290.     Container* tradeItemContainer = tradeItem->getContainer();
  2291.     if (tradeItemContainer) {
  2292.         for (const auto& it : tradeItems) {
  2293.             Item* item = it.first;
  2294.             if (tradeItem == item) {
  2295.                 player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2296.                 return;
  2297.             }
  2298.  
  2299.             if (tradeItemContainer->isHoldingItem(item)) {
  2300.                 player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2301.                 return;
  2302.             }
  2303.  
  2304.             Container* container = item->getContainer();
  2305.             if (container && container->isHoldingItem(tradeItem)) {
  2306.                 player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2307.                 return;
  2308.             }
  2309.         }
  2310.     } else {
  2311.         for (const auto& it : tradeItems) {
  2312.             Item* item = it.first;
  2313.             if (tradeItem == item) {
  2314.                 player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2315.                 return;
  2316.             }
  2317.  
  2318.             Container* container = item->getContainer();
  2319.             if (container && container->isHoldingItem(tradeItem)) {
  2320.                 player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2321.                 return;
  2322.             }
  2323.         }
  2324.     }
  2325.  
  2326.     Container* tradeContainer = tradeItem->getContainer();
  2327.     if (tradeContainer && tradeContainer->getItemHoldingCount() + 1 > 100) {
  2328.         player->sendTextMessage(MESSAGE_INFO_DESCR, "You can not trade more than 100 items.");
  2329.         return;
  2330.     }
  2331.  
  2332.     if (!g_events->eventPlayerOnTradeRequest(player, tradePartner, tradeItem)) {
  2333.         return;
  2334.     }
  2335.  
  2336.     internalStartTrade(player, tradePartner, tradeItem);
  2337. }
  2338.  
  2339. bool Game::internalStartTrade(Player* player, Player* tradePartner, Item* tradeItem)
  2340. {
  2341.     if (player->tradeState != TRADE_NONE && !(player->tradeState == TRADE_ACKNOWLEDGE && player->tradePartner == tradePartner)) {
  2342.         player->sendCancelMessage(RETURNVALUE_YOUAREALREADYTRADING);
  2343.         return false;
  2344.     } else if (tradePartner->tradeState != TRADE_NONE && tradePartner->tradePartner != player) {
  2345.         player->sendCancelMessage(RETURNVALUE_THISPLAYERISALREADYTRADING);
  2346.         return false;
  2347.     }
  2348.  
  2349.     player->tradePartner = tradePartner;
  2350.     player->tradeItem = tradeItem;
  2351.     player->tradeState = TRADE_INITIATED;
  2352.     tradeItem->incrementReferenceCounter();
  2353.     tradeItems[tradeItem] = player->getID();
  2354.  
  2355.     player->sendTradeItemRequest(player->getName(), tradeItem, true);
  2356.  
  2357.     if (tradePartner->tradeState == TRADE_NONE) {
  2358.         std::ostringstream ss;
  2359.         ss << player->getName() << " wants to trade with you.";
  2360.         tradePartner->sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  2361.         tradePartner->tradeState = TRADE_ACKNOWLEDGE;
  2362.         tradePartner->tradePartner = player;
  2363.     } else {
  2364.         Item* counterOfferItem = tradePartner->tradeItem;
  2365.         player->sendTradeItemRequest(tradePartner->getName(), counterOfferItem, false);
  2366.         tradePartner->sendTradeItemRequest(player->getName(), tradeItem, false);
  2367.     }
  2368.  
  2369.     return true;
  2370. }
  2371.  
  2372. void Game::playerAcceptTrade(uint32_t playerId)
  2373. {
  2374.     Player* player = getPlayerByID(playerId);
  2375.     if (!player) {
  2376.         return;
  2377.     }
  2378.  
  2379.     if (!(player->getTradeState() == TRADE_ACKNOWLEDGE || player->getTradeState() == TRADE_INITIATED)) {
  2380.         return;
  2381.     }
  2382.  
  2383.     Player* tradePartner = player->tradePartner;
  2384.     if (!tradePartner) {
  2385.         return;
  2386.     }
  2387.  
  2388.     if (!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())) {
  2389.         player->sendCancelMessage(RETURNVALUE_CREATUREISNOTREACHABLE);
  2390.         return;
  2391.     }
  2392.  
  2393.     player->setTradeState(TRADE_ACCEPT);
  2394.  
  2395.     if (tradePartner->getTradeState() == TRADE_ACCEPT) {
  2396.         Item* tradeItem1 = player->tradeItem;
  2397.         Item* tradeItem2 = tradePartner->tradeItem;
  2398.  
  2399.         if (!g_events->eventPlayerOnTradeAccept(player, tradePartner, tradeItem1, tradeItem2)) {
  2400.             internalCloseTrade(player);
  2401.             return;
  2402.         }
  2403.  
  2404.         player->setTradeState(TRADE_TRANSFER);
  2405.         tradePartner->setTradeState(TRADE_TRANSFER);
  2406.  
  2407.         std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradeItem1);
  2408.         if (it != tradeItems.end()) {
  2409.             ReleaseItem(it->first);
  2410.             tradeItems.erase(it);
  2411.         }
  2412.  
  2413.         it = tradeItems.find(tradeItem2);
  2414.         if (it != tradeItems.end()) {
  2415.             ReleaseItem(it->first);
  2416.             tradeItems.erase(it);
  2417.         }
  2418.  
  2419.         bool isSuccess = false;
  2420.  
  2421.         ReturnValue ret1 = internalAddItem(tradePartner, tradeItem1, INDEX_WHEREEVER, 0, true);
  2422.         ReturnValue ret2 = internalAddItem(player, tradeItem2, INDEX_WHEREEVER, 0, true);
  2423.         if (ret1 == RETURNVALUE_NOERROR && ret2 == RETURNVALUE_NOERROR) {
  2424.             ret1 = internalRemoveItem(tradeItem1, tradeItem1->getItemCount(), true);
  2425.             ret2 = internalRemoveItem(tradeItem2, tradeItem2->getItemCount(), true);
  2426.             if (ret1 == RETURNVALUE_NOERROR && ret2 == RETURNVALUE_NOERROR) {
  2427.                 Cylinder* cylinder1 = tradeItem1->getParent();
  2428.                 Cylinder* cylinder2 = tradeItem2->getParent();
  2429.  
  2430.                 uint32_t count1 = tradeItem1->getItemCount();
  2431.                 uint32_t count2 = tradeItem2->getItemCount();
  2432.  
  2433.                 ret1 = internalMoveItem(cylinder1, tradePartner, INDEX_WHEREEVER, tradeItem1, count1, nullptr, FLAG_IGNOREAUTOSTACK, nullptr, tradeItem2);
  2434.                 if (ret1 == RETURNVALUE_NOERROR) {
  2435.                     internalMoveItem(cylinder2, player, INDEX_WHEREEVER, tradeItem2, count2, nullptr, FLAG_IGNOREAUTOSTACK);
  2436.  
  2437.                     tradeItem1->onTradeEvent(ON_TRADE_TRANSFER, tradePartner);
  2438.                     tradeItem2->onTradeEvent(ON_TRADE_TRANSFER, player);
  2439.  
  2440.                     isSuccess = true;
  2441.                 }
  2442.             }
  2443.         }
  2444.  
  2445.         if (!isSuccess) {
  2446.             std::string errorDescription;
  2447.  
  2448.             if (tradePartner->tradeItem) {
  2449.                 errorDescription = getTradeErrorDescription(ret1, tradeItem1);
  2450.                 tradePartner->sendTextMessage(MESSAGE_EVENT_ADVANCE, errorDescription);
  2451.                 tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner);
  2452.             }
  2453.  
  2454.             if (player->tradeItem) {
  2455.                 errorDescription = getTradeErrorDescription(ret2, tradeItem2);
  2456.                 player->sendTextMessage(MESSAGE_EVENT_ADVANCE, errorDescription);
  2457.                 player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player);
  2458.             }
  2459.         }
  2460.  
  2461.         player->setTradeState(TRADE_NONE);
  2462.         player->tradeItem = nullptr;
  2463.         player->tradePartner = nullptr;
  2464.         player->sendTradeClose();
  2465.  
  2466.         tradePartner->setTradeState(TRADE_NONE);
  2467.         tradePartner->tradeItem = nullptr;
  2468.         tradePartner->tradePartner = nullptr;
  2469.         tradePartner->sendTradeClose();
  2470.     }
  2471. }
  2472.  
  2473. std::string Game::getTradeErrorDescription(ReturnValue ret, Item* item)
  2474. {
  2475.     if (item) {
  2476.         if (ret == RETURNVALUE_NOTENOUGHCAPACITY) {
  2477.             std::ostringstream ss;
  2478.             ss << "You do not have enough capacity to carry";
  2479.  
  2480.             if (item->isStackable() && item->getItemCount() > 1) {
  2481.                 ss << " these objects.";
  2482.             } else {
  2483.                 ss << " this object.";
  2484.             }
  2485.  
  2486.             ss << std::endl << ' ' << item->getWeightDescription();
  2487.             return ss.str();
  2488.         } else if (ret == RETURNVALUE_NOTENOUGHROOM || ret == RETURNVALUE_CONTAINERNOTENOUGHROOM) {
  2489.             std::ostringstream ss;
  2490.             ss << "You do not have enough room to carry";
  2491.  
  2492.             if (item->isStackable() && item->getItemCount() > 1) {
  2493.                 ss << " these objects.";
  2494.             } else {
  2495.                 ss << " this object.";
  2496.             }
  2497.  
  2498.             return ss.str();
  2499.         }
  2500.     }
  2501.     return "Trade could not be completed.";
  2502. }
  2503.  
  2504. void Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t index)
  2505. {
  2506.     Player* player = getPlayerByID(playerId);
  2507.     if (!player) {
  2508.         return;
  2509.     }
  2510.  
  2511.     Player* tradePartner = player->tradePartner;
  2512.     if (!tradePartner) {
  2513.         return;
  2514.     }
  2515.  
  2516.     Item* tradeItem;
  2517.     if (lookAtCounterOffer) {
  2518.         tradeItem = tradePartner->getTradeItem();
  2519.     } else {
  2520.         tradeItem = player->getTradeItem();
  2521.     }
  2522.  
  2523.     if (!tradeItem) {
  2524.         return;
  2525.     }
  2526.  
  2527.     const Position& playerPosition = player->getPosition();
  2528.     const Position& tradeItemPosition = tradeItem->getPosition();
  2529.  
  2530.     int32_t lookDistance = std::max<int32_t>(Position::getDistanceX(playerPosition, tradeItemPosition),
  2531.                                              Position::getDistanceY(playerPosition, tradeItemPosition));
  2532.     if (index == 0) {
  2533.         g_events->eventPlayerOnLookInTrade(player, tradePartner, tradeItem, lookDistance);
  2534.         return;
  2535.     }
  2536.  
  2537.     Container* tradeContainer = tradeItem->getContainer();
  2538.     if (!tradeContainer) {
  2539.         return;
  2540.     }
  2541.  
  2542.     std::vector<const Container*> containers {tradeContainer};
  2543.     size_t i = 0;
  2544.     while (i < containers.size()) {
  2545.         const Container* container = containers[i++];
  2546.         for (Item* item : container->getItemList()) {
  2547.             Container* tmpContainer = item->getContainer();
  2548.             if (tmpContainer) {
  2549.                 containers.push_back(tmpContainer);
  2550.             }
  2551.  
  2552.             if (--index == 0) {
  2553.                 g_events->eventPlayerOnLookInTrade(player, tradePartner, item, lookDistance);
  2554.                 return;
  2555.             }
  2556.         }
  2557.     }
  2558. }
  2559.  
  2560. void Game::playerCloseTrade(uint32_t playerId)
  2561. {
  2562.     Player* player = getPlayerByID(playerId);
  2563.     if (!player) {
  2564.         return;
  2565.     }
  2566.  
  2567.     internalCloseTrade(player);
  2568. }
  2569.  
  2570. void Game::internalCloseTrade(Player* player)
  2571. {
  2572.     Player* tradePartner = player->tradePartner;
  2573.     if ((tradePartner && tradePartner->getTradeState() == TRADE_TRANSFER) || player->getTradeState() == TRADE_TRANSFER) {
  2574.         return;
  2575.     }
  2576.  
  2577.     if (player->getTradeItem()) {
  2578.         std::map<Item*, uint32_t>::iterator it = tradeItems.find(player->getTradeItem());
  2579.         if (it != tradeItems.end()) {
  2580.             ReleaseItem(it->first);
  2581.             tradeItems.erase(it);
  2582.         }
  2583.  
  2584.         player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player);
  2585.         player->tradeItem = nullptr;
  2586.     }
  2587.  
  2588.     player->setTradeState(TRADE_NONE);
  2589.     player->tradePartner = nullptr;
  2590.  
  2591.     player->sendTextMessage(MESSAGE_STATUS_SMALL, "Trade cancelled.");
  2592.     player->sendTradeClose();
  2593.  
  2594.     if (tradePartner) {
  2595.         if (tradePartner->getTradeItem()) {
  2596.             std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradePartner->getTradeItem());
  2597.             if (it != tradeItems.end()) {
  2598.                 ReleaseItem(it->first);
  2599.                 tradeItems.erase(it);
  2600.             }
  2601.  
  2602.             tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner);
  2603.             tradePartner->tradeItem = nullptr;
  2604.         }
  2605.  
  2606.         tradePartner->setTradeState(TRADE_NONE);
  2607.         tradePartner->tradePartner = nullptr;
  2608.  
  2609.         tradePartner->sendTextMessage(MESSAGE_STATUS_SMALL, "Trade cancelled.");
  2610.         tradePartner->sendTradeClose();
  2611.     }
  2612. }
  2613.  
  2614. void Game::playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos)
  2615. {
  2616.     Player* player = getPlayerByID(playerId);
  2617.     if (!player) {
  2618.         return;
  2619.     }
  2620.  
  2621.     Thing* thing = internalGetThing(player, pos, stackPos, 0, STACKPOS_LOOK);
  2622.     if (!thing) {
  2623.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2624.         return;
  2625.     }
  2626.  
  2627.     Position thingPos = thing->getPosition();
  2628.     if (!player->canSee(thingPos)) {
  2629.         player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2630.         return;
  2631.     }
  2632.  
  2633.     Position playerPos = player->getPosition();
  2634.  
  2635.     int32_t lookDistance;
  2636.     if (thing != player) {
  2637.         lookDistance = std::max<int32_t>(Position::getDistanceX(playerPos, thingPos), Position::getDistanceY(playerPos, thingPos));
  2638.         if (playerPos.z != thingPos.z) {
  2639.             lookDistance += 15;
  2640.         }
  2641.     } else {
  2642.         lookDistance = -1;
  2643.     }
  2644.  
  2645.     g_events->eventPlayerOnLook(player, pos, thing, stackPos, lookDistance);
  2646. }
  2647.  
  2648. void Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId)
  2649. {
  2650.     Player* player = getPlayerByID(playerId);
  2651.     if (!player) {
  2652.         return;
  2653.     }
  2654.  
  2655.     Creature* creature = getCreatureByID(creatureId);
  2656.     if (!creature) {
  2657.         return;
  2658.     }
  2659.  
  2660.     if (!player->canSeeCreature(creature)) {
  2661.         return;
  2662.     }
  2663.  
  2664.     const Position& creaturePos = creature->getPosition();
  2665.     if (!player->canSee(creaturePos)) {
  2666.         return;
  2667.     }
  2668.  
  2669.     int32_t lookDistance;
  2670.     if (creature != player) {
  2671.         const Position& playerPos = player->getPosition();
  2672.         lookDistance = std::max<int32_t>(Position::getDistanceX(playerPos, creaturePos), Position::getDistanceY(playerPos, creaturePos));
  2673.         if (playerPos.z != creaturePos.z) {
  2674.             lookDistance += 15;
  2675.         }
  2676.     } else {
  2677.         lookDistance = -1;
  2678.     }
  2679.  
  2680.     g_events->eventPlayerOnLookInBattleList(player, creature, lookDistance);
  2681. }
  2682.  
  2683. void Game::playerCancelAttackAndFollow(uint32_t playerId)
  2684. {
  2685.     Player* player = getPlayerByID(playerId);
  2686.     if (!player) {
  2687.         return;
  2688.     }
  2689.  
  2690.     playerSetAttackedCreature(playerId, 0);
  2691.     playerFollowCreature(playerId, 0);
  2692.     player->stopWalk();
  2693. }
  2694.  
  2695. void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId)
  2696. {
  2697.     Player* player = getPlayerByID(playerId);
  2698.     if (!player) {
  2699.         return;
  2700.     }
  2701.  
  2702.     if (player->getAttackedCreature() && creatureId == 0) {
  2703.         player->setAttackedCreature(nullptr);
  2704.         player->sendCancelTarget();
  2705.         return;
  2706.     }
  2707.  
  2708.     Creature* attackCreature = getCreatureByID(creatureId);
  2709.     if (!attackCreature) {
  2710.         player->setAttackedCreature(nullptr);
  2711.         player->sendCancelTarget();
  2712.         return;
  2713.     }
  2714.  
  2715.     ReturnValue ret = Combat::canTargetCreature(player, attackCreature);
  2716.     if (ret != RETURNVALUE_NOERROR) {
  2717.         player->sendCancelMessage(ret);
  2718.         player->sendCancelTarget();
  2719.         player->setAttackedCreature(nullptr);
  2720.         return;
  2721.     }
  2722.  
  2723.     player->setAttackedCreature(attackCreature);
  2724.     g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, this, player->getID())));
  2725. }
  2726.  
  2727. void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId)
  2728. {
  2729.     Player* player = getPlayerByID(playerId);
  2730.     if (!player) {
  2731.         return;
  2732.     }
  2733.  
  2734.     player->setAttackedCreature(nullptr);
  2735.     g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, this, player->getID())));
  2736.     player->setFollowCreature(getCreatureByID(creatureId));
  2737. }
  2738.  
  2739. void Game::playerSetFightModes(uint32_t playerId, fightMode_t fightMode, chaseMode_t chaseMode, bool secureMode)
  2740. {
  2741.     Player* player = getPlayerByID(playerId);
  2742.     if (!player) {
  2743.         return;
  2744.     }
  2745.  
  2746.     player->setFightMode(fightMode);
  2747.     player->setChaseMode(chaseMode);
  2748.     player->setSecureMode(secureMode);
  2749. }
  2750.  
  2751. void Game::playerRequestAddVip(uint32_t playerId, const std::string& name)
  2752. {
  2753.     if (name.length() > 20) {
  2754.         return;
  2755.     }
  2756.  
  2757.     Player* player = getPlayerByID(playerId);
  2758.     if (!player) {
  2759.         return;
  2760.     }
  2761.  
  2762.     Player* vipPlayer = getPlayerByName(name);
  2763.     if (!vipPlayer) {
  2764.         uint32_t guid;
  2765.         bool specialVip;
  2766.         std::string formattedName = name;
  2767.         if (!IOLoginData::getGuidByNameEx(guid, specialVip, formattedName)) {
  2768.             player->sendTextMessage(MESSAGE_STATUS_SMALL, "A player with this name does not exist.");
  2769.             return;
  2770.         }
  2771.  
  2772.         if (specialVip && !player->hasFlag(PlayerFlag_SpecialVIP)) {
  2773.             player->sendTextMessage(MESSAGE_STATUS_SMALL, "You can not add this player.");
  2774.             return;
  2775.         }
  2776.  
  2777.         player->addVIP(guid, formattedName, VIPSTATUS_OFFLINE);
  2778.     } else {
  2779.         if (vipPlayer->hasFlag(PlayerFlag_SpecialVIP) && !player->hasFlag(PlayerFlag_SpecialVIP)) {
  2780.             player->sendTextMessage(MESSAGE_STATUS_SMALL, "You can not add this player.");
  2781.             return;
  2782.         }
  2783.  
  2784.         if (!vipPlayer->isInGhostMode() || player->isAccessPlayer()) {
  2785.             player->addVIP(vipPlayer->getGUID(), vipPlayer->getName(), VIPSTATUS_ONLINE);
  2786.         } else {
  2787.             player->addVIP(vipPlayer->getGUID(), vipPlayer->getName(), VIPSTATUS_OFFLINE);
  2788.         }
  2789.     }
  2790. }
  2791.  
  2792. void Game::playerRequestRemoveVip(uint32_t playerId, uint32_t guid)
  2793. {
  2794.     Player* player = getPlayerByID(playerId);
  2795.     if (!player) {
  2796.         return;
  2797.     }
  2798.  
  2799.     player->removeVIP(guid);
  2800. }
  2801.  
  2802. void Game::playerTurn(uint32_t playerId, Direction dir)
  2803. {
  2804.     Player* player = getPlayerByID(playerId);
  2805.     if (!player) {
  2806.         return;
  2807.     }
  2808.  
  2809.     if (!g_events->eventPlayerOnTurn(player, dir)) {
  2810.         return;
  2811.     }
  2812.  
  2813.     player->resetIdleTime();
  2814.     internalCreatureTurn(player, dir);
  2815. }
  2816.  
  2817. void Game::playerRequestOutfit(uint32_t playerId)
  2818. {
  2819.     if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) {
  2820.         return;
  2821.     }
  2822.  
  2823.     Player* player = getPlayerByID(playerId);
  2824.     if (!player) {
  2825.         return;
  2826.     }
  2827.  
  2828.     player->sendOutfitWindow();
  2829. }
  2830.  
  2831. void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
  2832. {
  2833.     if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) {
  2834.         return;
  2835.     }
  2836.  
  2837.     Player* player = getPlayerByID(playerId);
  2838.     if (!player) {
  2839.         return;
  2840.     }
  2841.  
  2842.     if (player->canWear(outfit.lookType, outfit.lookAddons)) {
  2843.         player->defaultOutfit = outfit;
  2844.  
  2845.         if (player->hasCondition(CONDITION_OUTFIT)) {
  2846.             return;
  2847.         }
  2848.  
  2849.         internalCreatureChangeOutfit(player, outfit);
  2850.     }
  2851. }
  2852.  
  2853. void Game::playerShowQuestLog(uint32_t playerId)
  2854. {
  2855.     Player* player = getPlayerByID(playerId);
  2856.     if (!player) {
  2857.         return;
  2858.     }
  2859.  
  2860.     player->sendQuestLog();
  2861. }
  2862.  
  2863. void Game::playerShowQuestLine(uint32_t playerId, uint16_t questId)
  2864. {
  2865.     Player* player = getPlayerByID(playerId);
  2866.     if (!player) {
  2867.         return;
  2868.     }
  2869.  
  2870.     Quest* quest = quests.getQuestByID(questId);
  2871.     if (!quest) {
  2872.         return;
  2873.     }
  2874.  
  2875.     player->sendQuestLine(quest);
  2876. }
  2877.  
  2878. void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, const std::string& receiver, const std::string& text)
  2879. {
  2880.     Player* player = getPlayerByID(playerId);
  2881.     if (!player) {
  2882.         return;
  2883.     }
  2884.  
  2885.     player->resetIdleTime();
  2886.    
  2887.     if (playerSaySpell(player, type, text)) {
  2888.         return;
  2889.     }
  2890.  
  2891.     uint32_t muteTime = player->isMuted();
  2892.     if (muteTime > 0) {
  2893.         std::ostringstream ss;
  2894.         ss << "You are still muted for " << muteTime << " seconds.";
  2895.         player->sendTextMessage(MESSAGE_STATUS_SMALL, ss.str());
  2896.         return;
  2897.     }
  2898.  
  2899.     if (playerSayCommand(player, text)) {
  2900.         return;
  2901.     }
  2902.  
  2903.     if (!text.empty() && text.front() == '/' && player->isAccessPlayer()) {
  2904.         return;
  2905.     }
  2906.  
  2907.     if (type != TALKTYPE_PRIVATE) {
  2908.         player->removeMessageBuffer();
  2909.     }
  2910.  
  2911.     switch (type) {
  2912.         case TALKTYPE_SAY:
  2913.             internalCreatureSay(player, TALKTYPE_SAY, text, false);
  2914.             break;
  2915.  
  2916.         case TALKTYPE_WHISPER:
  2917.             playerWhisper(player, text);
  2918.             break;
  2919.  
  2920.         case TALKTYPE_YELL:
  2921.             playerYell(player, text);
  2922.             break;
  2923.  
  2924.         case TALKTYPE_PRIVATE:
  2925.         case TALKTYPE_PRIVATE_RED:
  2926.             playerSpeakTo(player, type, receiver, text);
  2927.             break;
  2928.  
  2929.         case TALKTYPE_CHANNEL_O:
  2930.         case TALKTYPE_CHANNEL_Y:
  2931.         case TALKTYPE_CHANNEL_R1:
  2932.         case TALKTYPE_CHANNEL_R2:
  2933.             g_chat->talkToChannel(*player, type, text, channelId);
  2934.             break;
  2935.  
  2936.         case TALKTYPE_BROADCAST:
  2937.             playerBroadcastMessage(player, text);
  2938.             break;
  2939.  
  2940.         default:
  2941.             break;
  2942.     }
  2943. }
  2944.  
  2945. bool Game::playerSayCommand(Player* player, const std::string& text)
  2946. {
  2947.     if (text.empty()) {
  2948.         return false;
  2949.     }
  2950.  
  2951.     char firstCharacter = text.front();
  2952.     for (char commandTag : commandTags) {
  2953.         if (commandTag == firstCharacter) {
  2954.             if (commands.exeCommand(*player, text)) {
  2955.                 return true;
  2956.             }
  2957.         }
  2958.     }
  2959.     return false;
  2960. }
  2961.  
  2962. bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string& text)
  2963. {
  2964.     std::string words = text;
  2965.  
  2966.     TalkActionResult_t result = g_talkActions->playerSaySpell(player, type, words);
  2967.     if (result == TALKACTION_BREAK) {
  2968.         return true;
  2969.     }
  2970.  
  2971.     result = g_spells->playerSaySpell(player, words);
  2972.     if (result == TALKACTION_BREAK) {
  2973.         if (!g_config.getBoolean(ConfigManager::EMOTE_SPELLS)) {
  2974.             return internalCreatureSay(player, TALKTYPE_SAY, words, false);
  2975.         } else {
  2976.             return internalCreatureSay(player, TALKTYPE_MONSTER_SAY, words, false);
  2977.         }
  2978.  
  2979.     } else if (result == TALKACTION_FAILED) {
  2980.         return true;
  2981.     }
  2982.  
  2983.     return false;
  2984. }
  2985.  
  2986. void Game::playerWhisper(Player* player, const std::string& text)
  2987. {
  2988.     SpectatorVec list;
  2989.     map.getSpectators(list, player->getPosition(), false, false,
  2990.                   Map::maxClientViewportX, Map::maxClientViewportX,
  2991.                   Map::maxClientViewportY, Map::maxClientViewportY);
  2992.  
  2993.     //send to client
  2994.     for (Creature* spectator : list) {
  2995.         if (Player* spectatorPlayer = spectator->getPlayer()) {
  2996.             if (!Position::areInRange<1, 1>(player->getPosition(), spectatorPlayer->getPosition())) {
  2997.                 spectatorPlayer->sendCreatureSay(player, TALKTYPE_WHISPER, "pspsps");
  2998.             } else {
  2999.                 spectatorPlayer->sendCreatureSay(player, TALKTYPE_WHISPER, text);
  3000.             }
  3001.         }
  3002.     }
  3003.  
  3004.     //event method
  3005.     for (Creature* spectator : list) {
  3006.         spectator->onCreatureSay(player, TALKTYPE_WHISPER, text);
  3007.     }
  3008. }
  3009.  
  3010. bool Game::playerYell(Player* player, const std::string& text)
  3011. {
  3012.     if (player->getLevel() < 50) {
  3013.         player->sendTextMessage(MESSAGE_STATUS_SMALL, "You may not yell under 50 level.");
  3014.         return false;
  3015.     }
  3016.  
  3017.     if (player->hasCondition(CONDITION_YELLTICKS)) {
  3018.         player->sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED);
  3019.         return false;
  3020.     }
  3021.  
  3022.     if (player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER) {
  3023.         Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_YELLTICKS, 30000, 0);
  3024.         player->addCondition(condition);
  3025.     }
  3026.  
  3027.     internalCreatureSay(player, TALKTYPE_YELL, asUpperCaseString(text), false);
  3028.     return true;
  3029. }
  3030.  
  3031. bool Game::playerSpeakTo(Player* player, SpeakClasses type, const std::string& receiver, const std::string& text)
  3032. {
  3033.     Player* toPlayer = getPlayerByName(receiver);
  3034.     if (!toPlayer) {
  3035.         player->sendTextMessage(MESSAGE_STATUS_SMALL, "A player with this name is not online.");
  3036.         return false;
  3037.     }
  3038.  
  3039.     if (type == TALKTYPE_PRIVATE_RED && (player->hasFlag(PlayerFlag_CanTalkRedPrivate) || player->getAccountType() >= ACCOUNT_TYPE_GAMEMASTER)) {
  3040.         type = TALKTYPE_PRIVATE_RED;
  3041.     } else {
  3042.         type = TALKTYPE_PRIVATE;
  3043.     }
  3044.  
  3045.     toPlayer->sendPrivateMessage(player, type, text);
  3046.     toPlayer->onCreatureSay(player, type, text);
  3047.  
  3048.     if (toPlayer->isInGhostMode() && !player->isAccessPlayer()) {
  3049.         player->sendTextMessage(MESSAGE_STATUS_SMALL, "A player with this name is not online.");
  3050.     } else {
  3051.         std::ostringstream ss;
  3052.         ss << "Message sent to " << toPlayer->getName() << '.';
  3053.         player->sendTextMessage(MESSAGE_STATUS_SMALL, ss.str());
  3054.     }
  3055.     return true;
  3056. }
  3057.  
  3058. //--
  3059. bool Game::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight /*= true*/, int32_t rangex /*= Map::maxClientViewportX*/, int32_t rangey /*= Map::maxClientViewportY*/, MapPathFinding_t path) const
  3060. {
  3061.     return map.canThrowObjectTo(fromPos, toPos, checkLineOfSight, rangex, rangey, path);
  3062. }
  3063.  
  3064. bool Game::isSightClear(const Position& fromPos, const Position& toPos, bool floorCheck) const
  3065. {
  3066.     return map.isSightClear(fromPos, toPos, floorCheck);
  3067. }
  3068.  
  3069. bool Game::internalCreatureTurn(Creature* creature, Direction dir)
  3070. {
  3071.     if (creature->getDirection() == dir) {
  3072.         return false;
  3073.     }
  3074.  
  3075.     creature->setDirection(dir);
  3076.  
  3077.     //send to client
  3078.     SpectatorVec list;
  3079.     map.getSpectators(list, creature->getPosition(), true, true);
  3080.     for (Creature* spectator : list) {
  3081.         spectator->getPlayer()->sendCreatureTurn(creature);
  3082.     }
  3083.     return true;
  3084. }
  3085.  
  3086. bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text, bool ghostMode, SpectatorVec* listPtr/* = nullptr*/, const Position* pos/* = nullptr*/)
  3087. {
  3088.     if (text.empty()) {
  3089.         return false;
  3090.     }
  3091.  
  3092.     if (!pos) {
  3093.         pos = &creature->getPosition();
  3094.     }
  3095.  
  3096.     SpectatorVec list;
  3097.  
  3098.     if (!listPtr || listPtr->empty()) {
  3099.         // This somewhat complex construct ensures that the cached SpectatorVec
  3100.         // is used if available and if it can be used, else a local vector is
  3101.         // used (hopefully the compiler will optimize away the construction of
  3102.         // the temporary when it's not used).
  3103.         if (type != TALKTYPE_YELL && type != TALKTYPE_MONSTER_YELL) {
  3104.             map.getSpectators(list, *pos, false, false,
  3105.                           Map::maxClientViewportX, Map::maxClientViewportX,
  3106.                           Map::maxClientViewportY, Map::maxClientViewportY);
  3107.         } else {
  3108.             map.getSpectators(list, *pos, true, false, 18, 18, 14, 14);
  3109.         }
  3110.     } else {
  3111.         list = (*listPtr);
  3112.     }
  3113.  
  3114.     //send to client
  3115.     for (Creature* spectator : list) {
  3116.         if (Player* tmpPlayer = spectator->getPlayer()) {
  3117.             if (!ghostMode || tmpPlayer->canSeeCreature(creature)) {
  3118.                 tmpPlayer->sendCreatureSay(creature, type, text, pos);
  3119.             }
  3120.         }
  3121.     }
  3122.  
  3123.     //event method
  3124.     for (Creature* spectator : list) {
  3125.         spectator->onCreatureSay(creature, type, text);
  3126.     }
  3127.     return true;
  3128. }
  3129.  
  3130. void Game::checkCreatureWalk(uint32_t creatureId)
  3131. {
  3132.     Creature* creature = getCreatureByID(creatureId);
  3133.     if (creature && creature->getHealth() > 0) {
  3134.         creature->onWalk();
  3135.         cleanup();
  3136.     }
  3137. }
  3138.  
  3139. void Game::updateCreatureWalk(uint32_t creatureId)
  3140. {
  3141.     Creature* creature = getCreatureByID(creatureId);
  3142.     if (creature && creature->getHealth() > 0) {
  3143.         creature->goToFollowCreature();
  3144.     }
  3145. }
  3146.  
  3147. void Game::checkCreatureAttack(uint32_t creatureId)
  3148. {
  3149.     Creature* creature = getCreatureByID(creatureId);
  3150.     if (creature && creature->getHealth() > 0) {
  3151.         creature->onAttacking(0);
  3152.     }
  3153. }
  3154.  
  3155. void Game::addCreatureCheck(Creature* creature)
  3156. {
  3157.     creature->creatureCheck = true;
  3158.  
  3159.     if (creature->inCheckCreaturesVector) {
  3160.         // already in a vector
  3161.         return;
  3162.     }
  3163.  
  3164.     creature->inCheckCreaturesVector = true;
  3165.     checkCreatureLists[uniform_random(0, EVENT_CREATURECOUNT - 1)].push_back(creature);
  3166.     creature->incrementReferenceCounter();
  3167. }
  3168.  
  3169. void Game::removeCreatureCheck(Creature* creature)
  3170. {
  3171.     if (creature->inCheckCreaturesVector) {
  3172.         creature->creatureCheck = false;
  3173.     }
  3174. }
  3175.  
  3176. void Game::checkCreatures(size_t index)
  3177. {
  3178.     g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this, (index + 1) % EVENT_CREATURECOUNT)));
  3179.  
  3180.     auto& checkCreatureList = checkCreatureLists[index];
  3181.     auto it = checkCreatureList.begin(), end = checkCreatureList.end();
  3182.     while (it != end) {
  3183.         Creature* creature = *it;
  3184.         if (creature->creatureCheck) {
  3185.             if (creature->getHealth() > 0) {
  3186.                 creature->onThink(EVENT_CREATURE_THINK_INTERVAL);
  3187.                 creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL);
  3188.                 creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL);
  3189.             } else {
  3190.                 creature->onDeath();
  3191.             }
  3192.             ++it;
  3193.         } else {
  3194.             creature->inCheckCreaturesVector = false;
  3195.             it = checkCreatureList.erase(it);
  3196.             ReleaseCreature(creature);
  3197.         }
  3198.     }
  3199.  
  3200.     cleanup();
  3201. }
  3202.  
  3203. void Game::changeSpeed(Creature* creature, int32_t varSpeedDelta)
  3204. {
  3205.     int32_t varSpeed = creature->getSpeed() - creature->getBaseSpeed();
  3206.     varSpeed += varSpeedDelta;
  3207.  
  3208.     creature->setSpeed(varSpeed);
  3209.  
  3210.     //send to clients
  3211.     SpectatorVec list;
  3212.     map.getSpectators(list, creature->getPosition(), false, true);
  3213.     for (Creature* spectator : list) {
  3214.         spectator->getPlayer()->sendChangeSpeed(creature, creature->getStepSpeed());
  3215.     }
  3216. }
  3217.  
  3218. void Game::internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit)
  3219. {
  3220.     if (!g_events->eventCreatureOnChangeOutfit(creature, outfit)) {
  3221.         return;
  3222.     }
  3223.  
  3224.     creature->setCurrentOutfit(outfit);
  3225.  
  3226.     if (creature->isInvisible()) {
  3227.         return;
  3228.     }
  3229.  
  3230.     //send to clients
  3231.     SpectatorVec list;
  3232.     map.getSpectators(list, creature->getPosition(), true, true);
  3233.     for (Creature* spectator : list) {
  3234.         spectator->getPlayer()->sendCreatureChangeOutfit(creature, outfit);
  3235.     }
  3236. }
  3237.  
  3238. void Game::internalCreatureChangeVisible(Creature* creature, bool visible)
  3239. {
  3240.     //send to clients
  3241.     SpectatorVec list;
  3242.     map.getSpectators(list, creature->getPosition(), true, true);
  3243.     for (Creature* spectator : list) {
  3244.         spectator->getPlayer()->sendCreatureChangeVisible(creature, visible);
  3245.     }
  3246. }
  3247.  
  3248. void Game::changeLight(const Creature* creature)
  3249. {
  3250.     //send to clients
  3251.     SpectatorVec list;
  3252.     map.getSpectators(list, creature->getPosition(), true, true);
  3253.     for (Creature* spectator : list) {
  3254.         spectator->getPlayer()->sendCreatureLight(creature);
  3255.     }
  3256. }
  3257.  
  3258. bool Game::combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* target, bool checkDefense, bool checkArmor, bool field)
  3259. {
  3260.     if (damage.primary.type == COMBAT_NONE && damage.secondary.type == COMBAT_NONE) {
  3261.         return true;
  3262.     }
  3263.  
  3264.     if (target->getPlayer() && target->isInGhostMode()) {
  3265.         return true;
  3266.     }
  3267.  
  3268.     if (damage.primary.value > 0) {
  3269.         return false;
  3270.     }
  3271.  
  3272.     static const auto sendBlockEffect = [this](BlockType_t blockType, CombatType_t combatType, const Position& targetPos) {
  3273.         if (blockType == BLOCK_DEFENSE) {
  3274.             addMagicEffect(targetPos, CONST_ME_POFF);
  3275.         } else if (blockType == BLOCK_ARMOR) {
  3276.             addMagicEffect(targetPos, CONST_ME_BLOCKHIT);
  3277.         } else if (blockType == BLOCK_IMMUNITY) {
  3278.             uint8_t hitEffect = 0;
  3279.             switch (combatType) {
  3280.                 case COMBAT_UNDEFINEDDAMAGE: {
  3281.                     return;
  3282.                 }
  3283.                 case COMBAT_ENERGYDAMAGE:
  3284.                 case COMBAT_FIREDAMAGE:
  3285.                 case COMBAT_PHYSICALDAMAGE: {
  3286.                     hitEffect = CONST_ME_BLOCKHIT;
  3287.                     break;
  3288.                 }
  3289.                 case COMBAT_EARTHDAMAGE: {
  3290.                     hitEffect = CONST_ME_GREEN_RINGS;
  3291.                     break;
  3292.                 }
  3293.                 default: {
  3294.                     hitEffect = CONST_ME_POFF;
  3295.                     break;
  3296.                 }
  3297.             }
  3298.             addMagicEffect(targetPos, hitEffect);
  3299.         }
  3300.     };
  3301.  
  3302.     BlockType_t primaryBlockType, secondaryBlockType;
  3303.     if (damage.primary.type != COMBAT_NONE) {
  3304.         damage.primary.value = -damage.primary.value;
  3305.         primaryBlockType = target->blockHit(attacker, damage.primary.type, damage.primary.value, checkDefense, checkArmor, field);
  3306.  
  3307.         damage.primary.value = -damage.primary.value;
  3308.         sendBlockEffect(primaryBlockType, damage.primary.type, target->getPosition());
  3309.     } else {
  3310.         primaryBlockType = BLOCK_NONE;
  3311.     }
  3312.  
  3313.     if (damage.secondary.type != COMBAT_NONE) {
  3314.         damage.secondary.value = -damage.secondary.value;
  3315.         secondaryBlockType = target->blockHit(attacker, damage.secondary.type, damage.secondary.value, false, false, field);
  3316.  
  3317.         damage.secondary.value = -damage.secondary.value;
  3318.         sendBlockEffect(secondaryBlockType, damage.secondary.type, target->getPosition());
  3319.     } else {
  3320.         secondaryBlockType = BLOCK_NONE;
  3321.     }
  3322.     return (primaryBlockType != BLOCK_NONE) && (secondaryBlockType != BLOCK_NONE);
  3323. }
  3324.  
  3325. void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColor_t& color, uint8_t& effect)
  3326. {
  3327.     switch (combatType) {
  3328.         case COMBAT_PHYSICALDAMAGE: {
  3329.             Item* splash = nullptr;
  3330.             switch (target->getRace()) {
  3331.                 case RACE_VENOM:
  3332.                     color = TEXTCOLOR_LIGHTGREEN;
  3333.                     effect = CONST_ME_HITBYPOISON;
  3334.                     splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_GREEN);
  3335.                     break;
  3336.                 case RACE_BLOOD:
  3337.                     color = TEXTCOLOR_RED;
  3338.                     effect = CONST_ME_DRAWBLOOD;
  3339.                     splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_BLOOD);
  3340.                     break;
  3341.                 case RACE_UNDEAD:
  3342.                     color = TEXTCOLOR_LIGHTGREY;
  3343.                     effect = CONST_ME_HITAREA;
  3344.                     break;
  3345.                 case RACE_FIRE:
  3346.                     color = TEXTCOLOR_ORANGE;
  3347.                     effect = CONST_ME_DRAWBLOOD;
  3348.                     break;
  3349.                 case RACE_ENERGY:
  3350.                     color = TEXTCOLOR_PURPLE;
  3351.                     effect = CONST_ME_ENERGYHIT;
  3352.                     break;
  3353.                 default:
  3354.                     color = TEXTCOLOR_NONE;
  3355.                     effect = CONST_ME_NONE;
  3356.                     break;
  3357.             }
  3358.  
  3359.             if (splash) {
  3360.                 internalAddItem(target->getTile(), splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
  3361.                 startDecay(splash);
  3362.             }
  3363.  
  3364.             break;
  3365.         }
  3366.  
  3367.         case COMBAT_ENERGYDAMAGE: {
  3368.             color = TEXTCOLOR_PURPLE;
  3369.             effect = CONST_ME_ENERGYHIT;
  3370.             break;
  3371.         }
  3372.  
  3373.         case COMBAT_EARTHDAMAGE: {
  3374.             color = TEXTCOLOR_LIGHTGREEN;
  3375.             effect = CONST_ME_GREEN_RINGS;
  3376.             break;
  3377.         }
  3378.  
  3379.         case COMBAT_FIREDAMAGE: {
  3380.             color = TEXTCOLOR_ORANGE;
  3381.             effect = CONST_ME_HITBYFIRE;
  3382.             break;
  3383.         }
  3384.  
  3385.         case COMBAT_LIFEDRAIN: {
  3386.             color = TEXTCOLOR_RED;
  3387.             effect = CONST_ME_MAGIC_RED;
  3388.             break;
  3389.         }
  3390.  
  3391.         default: {
  3392.             color = TEXTCOLOR_NONE;
  3393.             effect = CONST_ME_NONE;
  3394.             break;
  3395.         }
  3396.     }
  3397. }
  3398.  
  3399. bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage)
  3400. {
  3401.     const Position& targetPos = target->getPosition();
  3402.     if (damage.primary.value > 0) {
  3403.         if (target->getHealth() <= 0) {
  3404.             return false;
  3405.         }
  3406.  
  3407.         Player* attackerPlayer;
  3408.         if (attacker) {
  3409.             attackerPlayer = attacker->getPlayer();
  3410.         } else {
  3411.             attackerPlayer = nullptr;
  3412.         }
  3413.  
  3414.         Player* targetPlayer = target->getPlayer();
  3415.         if (damage.origin != ORIGIN_NONE) {
  3416.             const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
  3417.             if (!events.empty()) {
  3418.                 for (CreatureEvent* creatureEvent : events) {
  3419.                     creatureEvent->executeHealthChange(target, attacker, damage);
  3420.                 }
  3421.                 damage.origin = ORIGIN_NONE;
  3422.                 return combatChangeHealth(attacker, target, damage);
  3423.             }
  3424.         }
  3425.  
  3426.         int32_t realHealthChange = target->getHealth();
  3427.         target->gainHealth(attacker, damage.primary.value);
  3428.         realHealthChange = target->getHealth() - realHealthChange;
  3429.  
  3430.         if (realHealthChange > 0 && !target->isInGhostMode()) {
  3431.             std::string damageString = std::to_string(realHealthChange) + (realHealthChange != 1 ? " hitpoints." : " hitpoint.");
  3432.  
  3433.             std::string spectatorMessage;
  3434.             if (!attacker) {
  3435.                 spectatorMessage += ucfirst(target->getNameDescription());
  3436.                 spectatorMessage += " was healed for " + damageString;
  3437.             } else {
  3438.                 spectatorMessage += ucfirst(attacker->getNameDescription());
  3439.                 spectatorMessage += " healed ";
  3440.                 if (attacker == target) {
  3441.                     spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "herself" : "himself") : "itself");
  3442.                 } else {
  3443.                     spectatorMessage += target->getNameDescription();
  3444.                 }
  3445.                 spectatorMessage += " for " + damageString;
  3446.             }
  3447.  
  3448.             TextMessage message;
  3449.             std::ostringstream strHealthChange;
  3450.             strHealthChange << realHealthChange;
  3451.             addAnimatedText(strHealthChange.str(), targetPos, TEXTCOLOR_MAYABLUE);
  3452.  
  3453.             SpectatorVec list;
  3454.             map.getSpectators(list, targetPos, false, true);
  3455.             for (Creature* spectator : list) {
  3456.                 Player* tmpPlayer = spectator->getPlayer();
  3457.                 if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
  3458.                     message.type = MESSAGE_STATUS_SMALL;
  3459.                     message.text = "You heal " + target->getNameDescription() + " for " + damageString;
  3460.                 } else if (tmpPlayer == targetPlayer) {
  3461.                     message.type = MESSAGE_STATUS_SMALL;
  3462.                     if (!attacker) {
  3463.                         message.text = "You were healed for " + damageString;
  3464.                     } else if (targetPlayer == attackerPlayer) {
  3465.                         message.text = "You heal yourself for " + damageString;
  3466.                     } else {
  3467.                         message.text = "You were healed by " + attacker->getNameDescription() + " for " + damageString;
  3468.                     }
  3469.                 } else {
  3470.                     message.type = MESSAGE_STATUS_SMALL;
  3471.                     message.text = spectatorMessage;
  3472.                 }
  3473.                 tmpPlayer->sendTextMessage(message);
  3474.             }
  3475.         }
  3476.     } else {
  3477.         if (!target->isAttackable()) {
  3478.             if (!target->isInGhostMode()) {
  3479.                 addMagicEffect(targetPos, CONST_ME_POFF);
  3480.             }
  3481.             return true;
  3482.         }
  3483.  
  3484.         Player* attackerPlayer;
  3485.         if (attacker) {
  3486.             attackerPlayer = attacker->getPlayer();
  3487.         } else {
  3488.             attackerPlayer = nullptr;
  3489.         }
  3490.  
  3491.         Player* targetPlayer = target->getPlayer();
  3492.         damage.primary.value = std::abs(damage.primary.value);
  3493.         damage.secondary.value = std::abs(damage.secondary.value);
  3494.  
  3495.         int32_t healthChange = damage.primary.value + damage.secondary.value;
  3496.         if (healthChange == 0) {
  3497.             return true;
  3498.         }
  3499.  
  3500.         TextMessage message;
  3501.         SpectatorVec list;
  3502.         if (target->hasCondition(CONDITION_MANASHIELD) && damage.primary.type != COMBAT_UNDEFINEDDAMAGE) {
  3503.             int32_t manaDamage = std::min<int32_t>(target->getMana(), healthChange);
  3504.             if (manaDamage != 0) {
  3505.                 if (damage.origin != ORIGIN_NONE) {
  3506.                     const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
  3507.                     if (!events.empty()) {
  3508.                         for (CreatureEvent* creatureEvent : events) {
  3509.                             creatureEvent->executeManaChange(target, attacker, healthChange, damage.origin);
  3510.                         }
  3511.                         if (healthChange == 0) {
  3512.                             return true;
  3513.                         }
  3514.                         manaDamage = std::min<int32_t>(target->getMana(), healthChange);
  3515.                     }
  3516.                 }
  3517.  
  3518.                 target->drainMana(attacker, manaDamage);
  3519.                 map.getSpectators(list, targetPos, true, true);
  3520.                 addMagicEffect(list, targetPos, CONST_ME_LOSEENERGY);
  3521.  
  3522.                 std::string damageString = std::to_string(manaDamage);
  3523.                 std::string spectatorMessage = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana";
  3524.                 if (attacker) {
  3525.                     spectatorMessage += " due to ";
  3526.                     if (attacker == target) {
  3527.                         spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack") : "its own attack");
  3528.                     } else {
  3529.                         spectatorMessage += "an attack by " + attacker->getNameDescription();
  3530.                     }
  3531.                 }
  3532.                 spectatorMessage += '.';
  3533.  
  3534.                 std::ostringstream strManaDamage;
  3535.                 strManaDamage << manaDamage;
  3536.                 addAnimatedText(strManaDamage.str(), targetPos, TEXTCOLOR_BLUE);
  3537.  
  3538.                 for (Creature* spectator : list) {
  3539.                     Player* tmpPlayer = spectator->getPlayer();
  3540.                     if (tmpPlayer->getPosition().z != targetPos.z) {
  3541.                         continue;
  3542.                     }
  3543.  
  3544.                     if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
  3545.                         message.type = MESSAGE_STATUS_SMALL;
  3546.                         message.text = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana due to your attack.";
  3547.                     } else if (tmpPlayer == targetPlayer) {
  3548.                         message.type = MESSAGE_STATUS_SMALL;
  3549.                         if (!attacker) {
  3550.                             message.text = "You lose " + damageString + " mana.";
  3551.                         } else if (targetPlayer == attackerPlayer) {
  3552.                             message.text = "You lose " + damageString + " mana due to your own attack.";
  3553.                         } else {
  3554.                             message.text = "You lose " + damageString + " mana due to an attack by " + attacker->getNameDescription() + '.';
  3555.                         }
  3556.                     } else {
  3557.                         message.type = MESSAGE_STATUS_SMALL;
  3558.                         message.text = spectatorMessage;
  3559.                     }
  3560.                     tmpPlayer->sendTextMessage(message);
  3561.                 }
  3562.  
  3563.                 damage.primary.value -= manaDamage;
  3564.                 if (damage.primary.value < 0) {
  3565.                     damage.secondary.value = std::max<int32_t>(0, damage.secondary.value + damage.primary.value);
  3566.                     damage.primary.value = 0;
  3567.                 }
  3568.             }
  3569.         }
  3570.  
  3571.         int32_t realDamage = damage.primary.value + damage.secondary.value;
  3572.         if (realDamage == 0) {
  3573.             return true;
  3574.         }
  3575.  
  3576.         if (damage.origin != ORIGIN_NONE) {
  3577.             const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
  3578.             if (!events.empty()) {
  3579.                 for (CreatureEvent* creatureEvent : events) {
  3580.                     creatureEvent->executeHealthChange(target, attacker, damage);
  3581.                 }
  3582.                 damage.origin = ORIGIN_NONE;
  3583.                 return combatChangeHealth(attacker, target, damage);
  3584.             }
  3585.         }
  3586.  
  3587.         int32_t targetHealth = target->getHealth();
  3588.         if (damage.primary.value >= targetHealth) {
  3589.             damage.primary.value = targetHealth;
  3590.             damage.secondary.value = 0;
  3591.         } else if (damage.secondary.value) {
  3592.             damage.secondary.value = std::min<int32_t>(damage.secondary.value, targetHealth - damage.primary.value);
  3593.         }
  3594.  
  3595.         realDamage = damage.primary.value + damage.secondary.value;
  3596.         if (realDamage == 0) {
  3597.             return true;
  3598.         } else if (realDamage >= targetHealth) {
  3599.             for (CreatureEvent* creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) {
  3600.                 if (!creatureEvent->executeOnPrepareDeath(target, attacker)) {
  3601.                     return false;
  3602.                 }
  3603.             }
  3604.         }
  3605.  
  3606.         target->drainHealth(attacker, realDamage);
  3607.         if (list.empty()) {
  3608.             map.getSpectators(list, targetPos, true, true);
  3609.         }
  3610.         addCreatureHealth(list, target);
  3611.  
  3612.         message.primary.value = damage.primary.value;
  3613.         message.secondary.value = damage.secondary.value;
  3614.  
  3615.         uint8_t hitEffect;
  3616.         if (message.primary.value) {
  3617.             combatGetTypeInfo(damage.primary.type, target, message.primary.color, hitEffect);
  3618.             if (hitEffect != CONST_ME_NONE) {
  3619.                 addMagicEffect(list, targetPos, hitEffect);
  3620.             }
  3621.  
  3622.             if (message.primary.color != TEXTCOLOR_NONE) {
  3623.                 std::ostringstream strPrimaryDamage;
  3624.                 strPrimaryDamage << message.primary.value;
  3625.                 addAnimatedText(strPrimaryDamage.str(), targetPos, message.primary.color);
  3626.             }
  3627.         }
  3628.  
  3629.         if (message.secondary.value) {
  3630.             combatGetTypeInfo(damage.secondary.type, target, message.secondary.color, hitEffect);
  3631.             if (hitEffect != CONST_ME_NONE) {
  3632.                 addMagicEffect(list, targetPos, hitEffect);
  3633.             }
  3634.  
  3635.             if (message.secondary.color != TEXTCOLOR_NONE) {
  3636.                 std::ostringstream strSecondaryDamage;
  3637.                 strSecondaryDamage << message.secondary.value;
  3638.                 addAnimatedText(strSecondaryDamage.str(), targetPos, message.secondary.color);
  3639.             }
  3640.         }
  3641.  
  3642.         if (message.primary.color != TEXTCOLOR_NONE || message.secondary.color != TEXTCOLOR_NONE) {
  3643.             std::string damageString = std::to_string(realDamage) + (realDamage != 1 ? " hitpoints" : " hitpoint");
  3644.             std::string spectatorMessage = ucfirst(target->getNameDescription()) + " loses " + damageString;
  3645.             if (attacker) {
  3646.                 spectatorMessage += " due to ";
  3647.                 if (attacker == target) {
  3648.                     spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack") : "its own attack");
  3649.                 } else {
  3650.                     spectatorMessage += "an attack by " + attacker->getNameDescription();
  3651.                 }
  3652.             }
  3653.             spectatorMessage += '.';
  3654.  
  3655.             for (Creature* spectator : list) {
  3656.                 Player* tmpPlayer = spectator->getPlayer();
  3657.                 if (tmpPlayer->getPosition().z != targetPos.z) {
  3658.                     continue;
  3659.                 }
  3660.  
  3661.                 if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
  3662.                     message.type = MESSAGE_STATUS_SMALL;
  3663.                     message.text = ucfirst(target->getNameDescription()) + " loses " + damageString + " due to your attack.";
  3664.                 } else if (tmpPlayer == targetPlayer) {
  3665.                     message.type = MESSAGE_STATUS_SMALL;
  3666.                     if (!attacker) {
  3667.                         message.text = "You lose " + damageString + '.';
  3668.                     } else if (targetPlayer == attackerPlayer) {
  3669.                         message.text = "You lose " + damageString + " due to your own attack.";
  3670.                     } else {
  3671.                         message.text = "You lose " + damageString + " due to an attack by " + attacker->getNameDescription() + '.';
  3672.                     }
  3673.                 } else {
  3674.                     message.type = MESSAGE_STATUS_SMALL;
  3675.                     // TODO: Avoid copying spectatorMessage everytime we send to a spectator
  3676.                     message.text = spectatorMessage;
  3677.                 }
  3678.                 tmpPlayer->sendTextMessage(message);
  3679.             }
  3680.         }
  3681.     }
  3682.  
  3683.     return true;
  3684. }
  3685.  
  3686. bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange, CombatOrigin origin)
  3687. {
  3688.     if (manaChange > 0) {
  3689.         if (origin != ORIGIN_NONE) {
  3690.             const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
  3691.             if (!events.empty()) {
  3692.                 for (CreatureEvent* creatureEvent : events) {
  3693.                     creatureEvent->executeManaChange(target, attacker, manaChange, origin);
  3694.                 }
  3695.                 return combatChangeMana(attacker, target, manaChange, ORIGIN_NONE);
  3696.             }
  3697.         }
  3698.  
  3699.         target->changeMana(manaChange);
  3700.     } else {
  3701.         const Position& targetPos = target->getPosition();
  3702.         if (!target->isAttackable()) {
  3703.             if (!target->isInGhostMode()) {
  3704.                 addMagicEffect(targetPos, CONST_ME_POFF);
  3705.             }
  3706.             return false;
  3707.         }
  3708.  
  3709.         Player* attackerPlayer;
  3710.         if (attacker) {
  3711.             attackerPlayer = attacker->getPlayer();
  3712.         } else {
  3713.             attackerPlayer = nullptr;
  3714.         }
  3715.  
  3716.         Player* targetPlayer = target->getPlayer();
  3717.         int32_t manaLoss = std::min<int32_t>(target->getMana(), -manaChange);
  3718.         BlockType_t blockType = target->blockHit(attacker, COMBAT_MANADRAIN, manaLoss);
  3719.         if (blockType != BLOCK_NONE) {
  3720.             addMagicEffect(targetPos, CONST_ME_POFF);
  3721.             return false;
  3722.         }
  3723.  
  3724.         if (manaLoss <= 0) {
  3725.             return true;
  3726.         }
  3727.  
  3728.         if (origin != ORIGIN_NONE) {
  3729.             const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
  3730.             if (!events.empty()) {
  3731.                 for (CreatureEvent* creatureEvent : events) {
  3732.                     creatureEvent->executeManaChange(target, attacker, manaChange, origin);
  3733.                 }
  3734.                 return combatChangeMana(attacker, target, manaChange, ORIGIN_NONE);
  3735.             }
  3736.         }
  3737.  
  3738.         target->drainMana(attacker, manaLoss);
  3739.  
  3740.         std::string damageString = std::to_string(manaLoss);
  3741.         std::string spectatorMessage = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana";
  3742.         if (attacker) {
  3743.             spectatorMessage += " due to ";
  3744.             if (attacker == target) {
  3745.                 spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack") : "its own attack");
  3746.             } else {
  3747.                 spectatorMessage += "an attack by " + attacker->getNameDescription();
  3748.             }
  3749.         }
  3750.         spectatorMessage += '.';
  3751.  
  3752.         TextMessage message;
  3753.         std::ostringstream strManaLoss;
  3754.         strManaLoss << manaLoss;
  3755.         addAnimatedText(strManaLoss.str(), targetPos, TEXTCOLOR_BLUE);
  3756.  
  3757.         SpectatorVec list;
  3758.         map.getSpectators(list, targetPos, false, true);
  3759.         for (Creature* spectator : list) {
  3760.             Player* tmpPlayer = spectator->getPlayer();
  3761.             if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
  3762.                 message.type = MESSAGE_STATUS_SMALL;
  3763.                 message.text = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana due to your attack.";
  3764.             } else if (tmpPlayer == targetPlayer) {
  3765.                 message.type = MESSAGE_STATUS_SMALL;
  3766.                 if (!attacker) {
  3767.                     message.text = "You lose " + damageString + " mana.";
  3768.                 } else if (targetPlayer == attackerPlayer) {
  3769.                     message.text = "You lose " + damageString + " mana due to your own attack.";
  3770.                 } else {
  3771.                     message.text = "You lose " + damageString + " mana due to an attack by " + attacker->getNameDescription() + '.';
  3772.                 }
  3773.             } else {
  3774.                 message.type = MESSAGE_STATUS_SMALL;
  3775.                 message.text = spectatorMessage;
  3776.             }
  3777.             tmpPlayer->sendTextMessage(message);
  3778.         }
  3779.     }
  3780.  
  3781.     return true;
  3782. }
  3783.  
  3784. void Game::addCreatureHealth(const Creature* target)
  3785. {
  3786.     SpectatorVec list;
  3787.     map.getSpectators(list, target->getPosition(), true, true);
  3788.     addCreatureHealth(list, target);
  3789. }
  3790.  
  3791. void Game::addCreatureHealth(const SpectatorVec& list, const Creature* target)
  3792. {
  3793.     for (Creature* spectator : list) {
  3794.         if (Player* tmpPlayer = spectator->getPlayer()) {
  3795.             tmpPlayer->sendCreatureHealth(target);
  3796.         }
  3797.     }
  3798. }
  3799.  
  3800. void Game::addAnimatedText(const std::string& message, const Position& pos, TextColor_t color)
  3801. {
  3802.     SpectatorVec list;
  3803.     map.getSpectators(list, pos, true, true);
  3804.     addAnimatedText(list, message, pos, color);
  3805. }
  3806.  
  3807. void Game::addAnimatedText(const SpectatorVec& list, const std::string& message, const Position& pos, TextColor_t color)
  3808. {
  3809.     for (Creature* spectator : list) {
  3810.         if (Player* tmpPlayer = spectator->getPlayer()) {
  3811.             tmpPlayer->sendAnimatedText(message, pos, color);
  3812.         }
  3813.     }
  3814. }
  3815.  
  3816. void Game::addMagicEffect(const Position& pos, uint8_t effect)
  3817. {
  3818.     SpectatorVec list;
  3819.     map.getSpectators(list, pos, true, true);
  3820.     addMagicEffect(list, pos, effect);
  3821. }
  3822.  
  3823. void Game::addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect)
  3824. {
  3825.     for (Creature* spectator : list) {
  3826.         if (Player* tmpPlayer = spectator->getPlayer()) {
  3827.             tmpPlayer->sendMagicEffect(pos, effect);
  3828.         }
  3829.     }
  3830. }
  3831.  
  3832. void Game::addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect)
  3833. {
  3834.     SpectatorVec list;
  3835.     map.getSpectators(list, fromPos, false, true);
  3836.     map.getSpectators(list, toPos, false, true);
  3837.     addDistanceEffect(list, fromPos, toPos, effect);
  3838. }
  3839.  
  3840. void Game::addDistanceEffect(const SpectatorVec& list, const Position& fromPos, const Position& toPos, uint8_t effect)
  3841. {
  3842.     for (Creature* spectator : list) {
  3843.         if (Player* tmpPlayer = spectator->getPlayer()) {
  3844.             tmpPlayer->sendDistanceShoot(fromPos, toPos, effect);
  3845.         }
  3846.     }
  3847. }
  3848.  
  3849. void Game::startDecay(Item* item)
  3850. {
  3851.     if (!item || !item->canDecay()) {
  3852.         return;
  3853.     }
  3854.  
  3855.     ItemDecayState_t decayState = item->getDecaying();
  3856.     if (decayState == DECAYING_TRUE) {
  3857.         return;
  3858.     }
  3859.  
  3860.     if (item->getDuration() > 0) {
  3861.         item->incrementReferenceCounter();
  3862.         item->setDecaying(DECAYING_TRUE);
  3863.         toDecayItems.push_front(item);
  3864.     } else {
  3865.         internalDecayItem(item);
  3866.     }
  3867. }
  3868.  
  3869. void Game::internalDecayItem(Item* item)
  3870. {
  3871.     const ItemType& it = Item::items[item->getID()];
  3872.     if (it.decayTo != 0) {
  3873.         Item* newItem = transformItem(item, it.decayTo);
  3874.         startDecay(newItem);
  3875.     } else {
  3876.         ReturnValue ret = internalRemoveItem(item);
  3877.         if (ret != RETURNVALUE_NOERROR) {
  3878.             std::cout << "[Debug - Game::internalDecayItem] internalDecayItem failed, error code: " << static_cast<uint32_t>(ret) << ", item id: " << item->getID() << std::endl;
  3879.         }
  3880.     }
  3881. }
  3882.  
  3883. void Game::checkDecay()
  3884. {
  3885.     g_scheduler.addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, std::bind(&Game::checkDecay, this)));
  3886.  
  3887.     size_t bucket = (lastBucket + 1) % EVENT_DECAY_BUCKETS;
  3888.  
  3889.     auto it = decayItems[bucket].begin(), end = decayItems[bucket].end();
  3890.     while (it != end) {
  3891.         Item* item = *it;
  3892.         if (!item->canDecay()) {
  3893.             item->setDecaying(DECAYING_FALSE);
  3894.             ReleaseItem(item);
  3895.             it = decayItems[bucket].erase(it);
  3896.             continue;
  3897.         }
  3898.  
  3899.         int32_t duration = item->getDuration();
  3900.         int32_t decreaseTime = std::min<int32_t>(EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS, duration);
  3901.  
  3902.         duration -= decreaseTime;
  3903.         item->decreaseDuration(decreaseTime);
  3904.  
  3905.         if (duration <= 0) {
  3906.             it = decayItems[bucket].erase(it);
  3907.             internalDecayItem(item);
  3908.             ReleaseItem(item);
  3909.         } else if (duration < EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) {
  3910.             it = decayItems[bucket].erase(it);
  3911.             size_t newBucket = (bucket + ((duration + EVENT_DECAYINTERVAL / 2) / 1000)) % EVENT_DECAY_BUCKETS;
  3912.             if (newBucket == bucket) {
  3913.                 internalDecayItem(item);
  3914.                 ReleaseItem(item);
  3915.             } else {
  3916.                 decayItems[newBucket].push_back(item);
  3917.             }
  3918.         } else {
  3919.             ++it;
  3920.         }
  3921.     }
  3922.  
  3923.     lastBucket = bucket;
  3924.     cleanup();
  3925. }
  3926.  
  3927. void Game::checkLight()
  3928. {
  3929.     g_scheduler.addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL, std::bind(&Game::checkLight, this)));
  3930.  
  3931.     lightHour += lightHourDelta;
  3932.  
  3933.     if (lightHour > 1440) {
  3934.         lightHour -= 1440;
  3935.     }
  3936.  
  3937.     if (std::abs(lightHour - SUNRISE) < 2 * lightHourDelta) {
  3938.         lightState = LIGHT_STATE_SUNRISE;
  3939.     } else if (std::abs(lightHour - SUNSET) < 2 * lightHourDelta) {
  3940.         lightState = LIGHT_STATE_SUNSET;
  3941.     }
  3942.  
  3943.     int32_t newLightLevel = lightLevel;
  3944.     bool lightChange = false;
  3945.  
  3946.     switch (lightState) {
  3947.         case LIGHT_STATE_SUNRISE: {
  3948.             newLightLevel += (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
  3949.             lightChange = true;
  3950.             break;
  3951.         }
  3952.         case LIGHT_STATE_SUNSET: {
  3953.             newLightLevel -= (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
  3954.             lightChange = true;
  3955.             break;
  3956.         }
  3957.         default:
  3958.             break;
  3959.     }
  3960.  
  3961.     if (newLightLevel <= LIGHT_LEVEL_NIGHT) {
  3962.         lightLevel = LIGHT_LEVEL_NIGHT;
  3963.         lightState = LIGHT_STATE_NIGHT;
  3964.     } else if (newLightLevel >= LIGHT_LEVEL_DAY) {
  3965.         lightLevel = LIGHT_LEVEL_DAY;
  3966.         lightState = LIGHT_STATE_DAY;
  3967.     } else {
  3968.         lightLevel = newLightLevel;
  3969.     }
  3970.  
  3971.     if (lightChange) {
  3972.         LightInfo lightInfo;
  3973.         getWorldLightInfo(lightInfo);
  3974.  
  3975.         for (const auto& it : players) {
  3976.             it.second->sendWorldLight(lightInfo);
  3977.         }
  3978.     }
  3979. }
  3980.  
  3981. void Game::getWorldLightInfo(LightInfo& lightInfo) const
  3982. {
  3983.     lightInfo.level = lightLevel;
  3984.     lightInfo.color = 0xD7;
  3985. }
  3986.  
  3987. void Game::addCommandTag(char tag)
  3988. {
  3989.     for (char commandTag : commandTags) {
  3990.         if (commandTag == tag) {
  3991.             return;
  3992.         }
  3993.     }
  3994.     commandTags.push_back(tag);
  3995. }
  3996.  
  3997. void Game::resetCommandTag()
  3998. {
  3999.     commandTags.clear();
  4000. }
  4001.  
  4002. void Game::shutdown()
  4003. {
  4004.     std::cout << "Shutting down..." << std::flush;
  4005.  
  4006.     g_scheduler.shutdown();
  4007.     g_databaseTasks.shutdown();
  4008.     g_dispatcher.shutdown();
  4009.     map.spawns.clear();
  4010.     raids.clear();
  4011.  
  4012.     cleanup();
  4013.  
  4014.     if (serviceManager) {
  4015.         serviceManager->stop();
  4016.     }
  4017.  
  4018.     ConnectionManager::getInstance().closeAll();
  4019.  
  4020.     std::cout << " done!" << std::endl;
  4021. }
  4022.  
  4023. void Game::cleanup()
  4024. {
  4025.     //free memory
  4026.     for (auto creature : ToReleaseCreatures) {
  4027.         creature->decrementReferenceCounter();
  4028.     }
  4029.     ToReleaseCreatures.clear();
  4030.  
  4031.     for (auto item : ToReleaseItems) {
  4032.         item->decrementReferenceCounter();
  4033.     }
  4034.     ToReleaseItems.clear();
  4035.  
  4036.     for (Item* item : toDecayItems) {
  4037.         const uint32_t dur = item->getDuration();
  4038.         if (dur >= EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) {
  4039.             decayItems[lastBucket].push_back(item);
  4040.         } else {
  4041.             decayItems[(lastBucket + 1 + dur / 1000) % EVENT_DECAY_BUCKETS].push_back(item);
  4042.         }
  4043.     }
  4044.     toDecayItems.clear();
  4045. }
  4046.  
  4047. void Game::ReleaseCreature(Creature* creature)
  4048. {
  4049.     ToReleaseCreatures.push_back(creature);
  4050. }
  4051.  
  4052. void Game::ReleaseItem(Item* item)
  4053. {
  4054.     ToReleaseItems.push_back(item);
  4055. }
  4056.  
  4057. void Game::broadcastMessage(const std::string& text, MessageClasses type) const
  4058. {
  4059.     std::cout << "> Broadcasted message: \"" << text << "\"." << std::endl;
  4060.     for (const auto& it : players) {
  4061.         it.second->sendTextMessage(type, text);
  4062.     }
  4063. }
  4064.  
  4065. void Game::updateCreatureSkull(const Creature* creature)
  4066. {
  4067.     if (getWorldType() != WORLD_TYPE_PVP) {
  4068.         return;
  4069.     }
  4070.  
  4071.     SpectatorVec list;
  4072.     map.getSpectators(list, creature->getPosition(), true, true);
  4073.     for (Creature* spectator : list) {
  4074.         spectator->getPlayer()->sendCreatureSkull(creature);
  4075.     }
  4076. }
  4077.  
  4078. void Game::updatePlayerShield(Player* player)
  4079. {
  4080.     SpectatorVec list;
  4081.     map.getSpectators(list, player->getPosition(), true, true);
  4082.     for (Creature* spectator : list) {
  4083.         spectator->getPlayer()->sendCreatureShield(player);
  4084.     }
  4085. }
  4086.  
  4087. void Game::updatePremium(Account& account)
  4088. {
  4089.     bool save = false;
  4090.     time_t timeNow = time(nullptr);
  4091.  
  4092.     if (account.premiumDays != 0 && account.premiumDays != std::numeric_limits<uint16_t>::max()) {
  4093.         if (account.lastDay == 0) {
  4094.             account.lastDay = timeNow;
  4095.             save = true;
  4096.         } else {
  4097.             uint32_t days = (timeNow - account.lastDay) / 86400;
  4098.             if (days > 0) {
  4099.                 if (days >= account.premiumDays) {
  4100.                     account.premiumDays = 0;
  4101.                     account.lastDay = 0;
  4102.                 } else {
  4103.                     account.premiumDays -= days;
  4104.                     time_t remainder = (timeNow - account.lastDay) % 86400;
  4105.                     account.lastDay = timeNow - remainder;
  4106.                 }
  4107.  
  4108.                 save = true;
  4109.             }
  4110.         }
  4111.     } else if (account.lastDay != 0) {
  4112.         account.lastDay = 0;
  4113.         save = true;
  4114.     }
  4115.  
  4116.     if (save && !IOLoginData::saveAccount(account)) {
  4117.         std::cout << "> ERROR: Failed to save account: " << account.name << "!" << std::endl;
  4118.     }
  4119. }
  4120.  
  4121. void Game::loadMotdNum()
  4122. {
  4123.     Database& db = Database::getInstance();
  4124.  
  4125.     DBResult_ptr result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_num'");
  4126.     if (result) {
  4127.         motdNum = result->getNumber<uint32_t>("value");
  4128.     } else {
  4129.         db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_num', '0')");
  4130.     }
  4131.  
  4132.     result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_hash'");
  4133.     if (result) {
  4134.         motdHash = result->getString("value");
  4135.         if (motdHash != transformToSHA1(g_config.getString(ConfigManager::MOTD))) {
  4136.             ++motdNum;
  4137.         }
  4138.     } else {
  4139.         db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_hash', '')");
  4140.     }
  4141. }
  4142.  
  4143. void Game::saveMotdNum() const
  4144. {
  4145.     Database& db = Database::getInstance();
  4146.  
  4147.     std::ostringstream query;
  4148.     query << "UPDATE `server_config` SET `value` = '" << motdNum << "' WHERE `config` = 'motd_num'";
  4149.     db.executeQuery(query.str());
  4150.  
  4151.     query.str(std::string());
  4152.     query << "UPDATE `server_config` SET `value` = '" << transformToSHA1(g_config.getString(ConfigManager::MOTD)) << "' WHERE `config` = 'motd_hash'";
  4153.     db.executeQuery(query.str());
  4154. }
  4155.  
  4156. void Game::checkPlayersRecord()
  4157. {
  4158.     const size_t playersOnline = getPlayersOnline();
  4159.     if (playersOnline > playersRecord) {
  4160.         uint32_t previousRecord = playersRecord;
  4161.         playersRecord = playersOnline;
  4162.  
  4163.         for (const auto& it : g_globalEvents->getEventMap(GLOBALEVENT_RECORD)) {
  4164.             it.second->executeRecord(playersRecord, previousRecord);
  4165.         }
  4166.         updatePlayersRecord();
  4167.     }
  4168. }
  4169.  
  4170. void Game::updatePlayersRecord() const
  4171. {
  4172.     Database& db = Database::getInstance();
  4173.  
  4174.     std::ostringstream query;
  4175.     query << "UPDATE `server_config` SET `value` = '" << playersRecord << "' WHERE `config` = 'players_record'";
  4176.     db.executeQuery(query.str());
  4177. }
  4178.  
  4179. void Game::loadPlayersRecord()
  4180. {
  4181.     Database& db = Database::getInstance();
  4182.  
  4183.     DBResult_ptr result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'players_record'");
  4184.     if (result) {
  4185.         playersRecord = result->getNumber<uint32_t>("value");
  4186.     } else {
  4187.         db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('players_record', '0')");
  4188.     }
  4189. }
  4190.  
  4191. uint64_t Game::getExperienceStage(uint32_t level)
  4192. {
  4193.     if (!stagesEnabled) {
  4194.         return g_config.getNumber(ConfigManager::RATE_EXPERIENCE);
  4195.     }
  4196.  
  4197.     if (useLastStageLevel && level >= lastStageLevel) {
  4198.         return stages[lastStageLevel];
  4199.     }
  4200.  
  4201.     return stages[level];
  4202. }
  4203.  
  4204. bool Game::loadExperienceStages()
  4205. {
  4206.     pugi::xml_document doc;
  4207.     pugi::xml_parse_result result = doc.load_file("data/XML/stages.xml");
  4208.     if (!result) {
  4209.         printXMLError("Error - Game::loadExperienceStages", "data/XML/stages.xml", result);
  4210.         return false;
  4211.     }
  4212.  
  4213.     for (auto stageNode : doc.child("stages").children()) {
  4214.         if (strcasecmp(stageNode.name(), "config") == 0) {
  4215.             stagesEnabled = stageNode.attribute("enabled").as_bool();
  4216.         } else {
  4217.             uint32_t minLevel, maxLevel, multiplier;
  4218.  
  4219.             pugi::xml_attribute minLevelAttribute = stageNode.attribute("minlevel");
  4220.             if (minLevelAttribute) {
  4221.                 minLevel = pugi::cast<uint32_t>(minLevelAttribute.value());
  4222.             } else {
  4223.                 minLevel = 1;
  4224.             }
  4225.  
  4226.             pugi::xml_attribute maxLevelAttribute = stageNode.attribute("maxlevel");
  4227.             if (maxLevelAttribute) {
  4228.                 maxLevel = pugi::cast<uint32_t>(maxLevelAttribute.value());
  4229.             } else {
  4230.                 maxLevel = 0;
  4231.                 lastStageLevel = minLevel;
  4232.                 useLastStageLevel = true;
  4233.             }
  4234.  
  4235.             pugi::xml_attribute multiplierAttribute = stageNode.attribute("multiplier");
  4236.             if (multiplierAttribute) {
  4237.                 multiplier = pugi::cast<uint32_t>(multiplierAttribute.value());
  4238.             } else {
  4239.                 multiplier = 1;
  4240.             }
  4241.  
  4242.             if (useLastStageLevel) {
  4243.                 stages[lastStageLevel] = multiplier;
  4244.             } else {
  4245.                 for (uint32_t i = minLevel; i <= maxLevel; ++i) {
  4246.                     stages[i] = multiplier;
  4247.                 }
  4248.             }
  4249.         }
  4250.     }
  4251.     return true;
  4252. }
  4253.  
  4254. void Game::playerInviteToParty(uint32_t playerId, uint32_t invitedId)
  4255. {
  4256.     Player* player = getPlayerByID(playerId);
  4257.     if (!player) {
  4258.         return;
  4259.     }
  4260.  
  4261.     Player* invitedPlayer = getPlayerByID(invitedId);
  4262.     if (!invitedPlayer || invitedPlayer->isInviting(player)) {
  4263.         return;
  4264.     }
  4265.  
  4266.     if (invitedPlayer->getParty()) {
  4267.         std::ostringstream ss;
  4268.         ss << invitedPlayer->getName() << " is already in a party.";
  4269.         player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
  4270.         return;
  4271.     }
  4272.  
  4273.     Party* party = player->getParty();
  4274.     if (!party) {
  4275.         party = new Party(player);
  4276.     } else if (party->getLeader() != player) {
  4277.         return;
  4278.     }
  4279.  
  4280.     party->invitePlayer(*invitedPlayer);
  4281. }
  4282.  
  4283. void Game::playerJoinParty(uint32_t playerId, uint32_t leaderId)
  4284. {
  4285.     Player* player = getPlayerByID(playerId);
  4286.     if (!player) {
  4287.         return;
  4288.     }
  4289.  
  4290.     Player* leader = getPlayerByID(leaderId);
  4291.     if (!leader || !leader->isInviting(player)) {
  4292.         return;
  4293.     }
  4294.  
  4295.     Party* party = leader->getParty();
  4296.     if (!party || party->getLeader() != leader) {
  4297.         return;
  4298.     }
  4299.  
  4300.     if (player->getParty()) {
  4301.         player->sendTextMessage(MESSAGE_INFO_DESCR, "You are already in a party.");
  4302.         return;
  4303.     }
  4304.  
  4305.     party->joinParty(*player);
  4306. }
  4307.  
  4308. void Game::playerRevokePartyInvitation(uint32_t playerId, uint32_t invitedId)
  4309. {
  4310.     Player* player = getPlayerByID(playerId);
  4311.     if (!player) {
  4312.         return;
  4313.     }
  4314.  
  4315.     Party* party = player->getParty();
  4316.     if (!party || party->getLeader() != player) {
  4317.         return;
  4318.     }
  4319.  
  4320.     Player* invitedPlayer = getPlayerByID(invitedId);
  4321.     if (!invitedPlayer || !player->isInviting(invitedPlayer)) {
  4322.         return;
  4323.     }
  4324.  
  4325.     party->revokeInvitation(*invitedPlayer);
  4326. }
  4327.  
  4328. void Game::playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId)
  4329. {
  4330.     Player* player = getPlayerByID(playerId);
  4331.     if (!player) {
  4332.         return;
  4333.     }
  4334.  
  4335.     Party* party = player->getParty();
  4336.     if (!party || party->getLeader() != player) {
  4337.         return;
  4338.     }
  4339.  
  4340.     Player* newLeader = getPlayerByID(newLeaderId);
  4341.     if (!newLeader || !player->isPartner(newLeader)) {
  4342.         return;
  4343.     }
  4344.  
  4345.     party->passPartyLeadership(newLeader);
  4346. }
  4347.  
  4348. void Game::playerLeaveParty(uint32_t playerId)
  4349. {
  4350.     Player* player = getPlayerByID(playerId);
  4351.     if (!player) {
  4352.         return;
  4353.     }
  4354.  
  4355.     Party* party = player->getParty();
  4356.     if (!party || player->hasCondition(CONDITION_INFIGHT)) {
  4357.         return;
  4358.     }
  4359.  
  4360.     party->leaveParty(player);
  4361. }
  4362.  
  4363. void Game::playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive)
  4364. {
  4365.     Player* player = getPlayerByID(playerId);
  4366.     if (!player) {
  4367.         return;
  4368.     }
  4369.  
  4370.     Party* party = player->getParty();
  4371.     if (!party || player->hasCondition(CONDITION_INFIGHT)) {
  4372.         return;
  4373.     }
  4374.  
  4375.     party->setSharedExperience(player, sharedExpActive);
  4376. }
  4377.  
  4378. void Game::sendGuildMotd(uint32_t playerId)
  4379. {
  4380.     Player* player = getPlayerByID(playerId);
  4381.     if (!player) {
  4382.         return;
  4383.     }
  4384.  
  4385.     Guild* guild = player->getGuild();
  4386.     if (guild) {
  4387.         player->sendChannelMessage("Message of the Day", guild->getMotd(), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD);
  4388.     }
  4389. }
  4390.  
  4391. void Game::kickPlayer(uint32_t playerId, bool displayEffect)
  4392. {
  4393.     Player* player = getPlayerByID(playerId);
  4394.     if (!player) {
  4395.         return;
  4396.     }
  4397.  
  4398.     player->kickPlayer(displayEffect);
  4399. }
  4400.  
  4401. void Game::playerReportBug(uint32_t playerId, const std::string& bug)
  4402. {
  4403.     Player* player = getPlayerByID(playerId);
  4404.     if (!player) {
  4405.         return;
  4406.     }
  4407.  
  4408.     if (player->getAccountType() == ACCOUNT_TYPE_NORMAL) {
  4409.         return;
  4410.     }
  4411.  
  4412.     std::string fileName = "data/reports/" + player->getName() + " report.txt";
  4413.     FILE* file = fopen(fileName.c_str(), "a");
  4414.     if (!file) {
  4415.         player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "There was an error when processing your report, please contact a gamemaster.");
  4416.         return;
  4417.     }
  4418.  
  4419.     const Position& position = player->getPosition();
  4420.     fprintf(file, "------------------------------\nName: %s [Position X: %u Y: %u Z: %u]\nBug Report: %s\n", player->getName().c_str(), position.x, position.y, position.z, bug.c_str());
  4421.     fclose(file);
  4422.  
  4423.     player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "Your report has been sent to " + g_config.getString(ConfigManager::SERVER_NAME) + ".");
  4424. }
  4425.  
  4426. void Game::playerDebugAssert(uint32_t playerId, const std::string& assertLine, const std::string& date, const std::string& description, const std::string& comment)
  4427. {
  4428.     Player* player = getPlayerByID(playerId);
  4429.     if (!player) {
  4430.         return;
  4431.     }
  4432.  
  4433.     // TODO: move debug assertions to database
  4434.     FILE* file = fopen("client_assertions.txt", "a");
  4435.     if (file) {
  4436.         fprintf(file, "----- %s - %s (%s) -----\n", formatDate(time(nullptr)).c_str(), player->getName().c_str(), convertIPToString(player->getIP()).c_str());
  4437.         fprintf(file, "%s\n%s\n%s\n%s\n", assertLine.c_str(), date.c_str(), description.c_str(), comment.c_str());
  4438.         fclose(file);
  4439.     }
  4440. }
  4441.  
  4442. void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer)
  4443. {
  4444.     Player* player = getPlayerByID(playerId);
  4445.     if (!player) {
  4446.         return;
  4447.     }
  4448.  
  4449.     for (CreatureEvent* creatureEvent : player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE)) {
  4450.         creatureEvent->executeExtendedOpcode(player, opcode, buffer);
  4451.     }
  4452. }
  4453.  
  4454. void Game::forceAddCondition(uint32_t creatureId, Condition* condition)
  4455. {
  4456.     Creature* creature = getCreatureByID(creatureId);
  4457.     if (!creature) {
  4458.         delete condition;
  4459.         return;
  4460.     }
  4461.  
  4462.     creature->addCondition(condition, true);
  4463. }
  4464.  
  4465. void Game::forceRemoveCondition(uint32_t creatureId, ConditionType_t type)
  4466. {
  4467.     Creature* creature = getCreatureByID(creatureId);
  4468.     if (!creature) {
  4469.         return;
  4470.     }
  4471.  
  4472.     creature->removeCondition(type, true);
  4473. }
  4474.  
  4475. void Game::addPlayer(Player* player)
  4476. {
  4477.     const std::string& lowercase_name = asLowerCaseString(player->getName());
  4478.     mappedPlayerNames[lowercase_name] = player;
  4479.     wildcardTree.insert(lowercase_name);
  4480.     players[player->getID()] = player;
  4481. }
  4482.  
  4483. void Game::removePlayer(Player* player)
  4484. {
  4485.     const std::string& lowercase_name = asLowerCaseString(player->getName());
  4486.     mappedPlayerNames.erase(lowercase_name);
  4487.     wildcardTree.remove(lowercase_name);
  4488.     players.erase(player->getID());
  4489. }
  4490.  
  4491. void Game::addNpc(Npc* npc)
  4492. {
  4493.     npcs[npc->getID()] = npc;
  4494. }
  4495.  
  4496. void Game::removeNpc(Npc* npc)
  4497. {
  4498.     npcs.erase(npc->getID());
  4499. }
  4500.  
  4501. void Game::addMonster(Monster* monster)
  4502. {
  4503.     monsters[monster->getID()] = monster;
  4504. }
  4505.  
  4506. void Game::removeMonster(Monster* monster)
  4507. {
  4508.     monsters.erase(monster->getID());
  4509. }
  4510.  
  4511. Guild* Game::getGuild(uint32_t id) const
  4512. {
  4513.     auto it = guilds.find(id);
  4514.     if (it == guilds.end()) {
  4515.         return nullptr;
  4516.     }
  4517.     return it->second;
  4518. }
  4519.  
  4520. void Game::addGuild(Guild* guild)
  4521. {
  4522.     guilds[guild->getId()] = guild;
  4523. }
  4524.  
  4525. void Game::removeGuild(uint32_t guildId)
  4526. {
  4527.     guilds.erase(guildId);
  4528. }
  4529.  
  4530. void Game::internalRemoveItems(std::vector<Item*> itemList, uint32_t amount, bool stackable)
  4531. {
  4532.     if (stackable) {
  4533.         for (Item* item : itemList) {
  4534.             if (item->getItemCount() > amount) {
  4535.                 internalRemoveItem(item, amount);
  4536.                 break;
  4537.             } else {
  4538.                 amount -= item->getItemCount();
  4539.                 internalRemoveItem(item);
  4540.             }
  4541.         }
  4542.     } else {
  4543.         for (Item* item : itemList) {
  4544.             internalRemoveItem(item);
  4545.         }
  4546.     }
  4547. }
  4548.  
  4549. BedItem* Game::getBedBySleeper(uint32_t guid) const
  4550. {
  4551.     auto it = bedSleepersMap.find(guid);
  4552.     if (it == bedSleepersMap.end()) {
  4553.         return nullptr;
  4554.     }
  4555.     return it->second;
  4556. }
  4557.  
  4558. void Game::setBedSleeper(BedItem* bed, uint32_t guid)
  4559. {
  4560.     bedSleepersMap[guid] = bed;
  4561. }
  4562.  
  4563. void Game::removeBedSleeper(uint32_t guid)
  4564. {
  4565.     auto it = bedSleepersMap.find(guid);
  4566.     if (it != bedSleepersMap.end()) {
  4567.         bedSleepersMap.erase(it);
  4568.     }
  4569. }
  4570.  
  4571. Item* Game::getUniqueItem(uint16_t uniqueId)
  4572. {
  4573.     auto it = uniqueItems.find(uniqueId);
  4574.     if (it == uniqueItems.end()) {
  4575.         return nullptr;
  4576.     }
  4577.     return it->second;
  4578. }
  4579.  
  4580. bool Game::addUniqueItem(uint16_t uniqueId, Item* item)
  4581. {
  4582.     auto result = uniqueItems.emplace(uniqueId, item);
  4583.     if (!result.second) {
  4584.         std::cout << "Duplicate unique id: " << uniqueId << std::endl;
  4585.     }
  4586.     return result.second;
  4587. }
  4588.  
  4589. void Game::removeUniqueItem(uint16_t uniqueId)
  4590. {
  4591.     auto it = uniqueItems.find(uniqueId);
  4592.     if (it != uniqueItems.end()) {
  4593.         uniqueItems.erase(it);
  4594.     }
  4595. }
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