Guest User

protocolgame.cpp

a guest
Sep 29th, 2017
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 83.35 KB | None | 0 0
  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 <boost/range/adaptor/reversed.hpp>
  23.  
  24. #include "protocolgame.h"
  25.  
  26. #include "outputmessage.h"
  27.  
  28. #include "player.h"
  29.  
  30. #include "configmanager.h"
  31. #include "actions.h"
  32. #include "game.h"
  33. #include "iologindata.h"
  34. #include "iomarket.h"
  35. #include "waitlist.h"
  36. #include "ban.h"
  37. #include "scheduler.h"
  38.  
  39. extern ConfigManager g_config;
  40. extern Actions actions;
  41. extern CreatureEvents* g_creatureEvents;
  42. extern Chat* g_chat;
  43.  
  44. void ProtocolGame::release()
  45. {
  46.     //dispatcher thread
  47.     if (player && player->client == shared_from_this()) {
  48.         player->client.reset();
  49.         player->decrementReferenceCounter();
  50.         player = nullptr;
  51.     }
  52.  
  53.     OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
  54.     Protocol::release();
  55. }
  56.  
  57. void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem)
  58. {
  59.     //dispatcher thread
  60.     Player* foundPlayer = g_game.getPlayerByName(name);
  61.     if (!foundPlayer || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) {
  62.         player = new Player(getThis());
  63.         player->setName(name);
  64.  
  65.         player->incrementReferenceCounter();
  66.         player->setID();
  67.  
  68.         if (!IOLoginData::preloadPlayer(player, name)) {
  69.             disconnectClient("Your character could not be loaded.");
  70.             return;
  71.         }
  72.  
  73.         if (IOBan::isPlayerNamelocked(player->getGUID())) {
  74.             disconnectClient("Your character has been namelocked.");
  75.             return;
  76.         }
  77.  
  78.         if (g_game.getGameState() == GAME_STATE_CLOSING && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) {
  79.             disconnectClient("The game is just going down.\nPlease try again later.");
  80.             return;
  81.         }
  82.  
  83.         if (g_game.getGameState() == GAME_STATE_CLOSED && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) {
  84.             disconnectClient("Server is currently closed.\nPlease try again later.");
  85.             return;
  86.         }
  87.  
  88.         if (g_config.getBoolean(ConfigManager::ONE_PLAYER_ON_ACCOUNT) && player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && g_game.getPlayerByAccount(player->getAccount())) {
  89.             disconnectClient("You may only login with one character\nof your account at the same time.");
  90.             return;
  91.         }
  92.  
  93.         if (!player->hasFlag(PlayerFlag_CannotBeBanned)) {
  94.             BanInfo banInfo;
  95.             if (IOBan::isAccountBanned(accountId, banInfo)) {
  96.                 if (banInfo.reason.empty()) {
  97.                     banInfo.reason = "(none)";
  98.                 }
  99.  
  100.                 std::ostringstream ss;
  101.                 if (banInfo.expiresAt > 0) {
  102.                     ss << "Your account has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason;
  103.                 } else {
  104.                     ss << "Your account has been permanently banned by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason;
  105.                 }
  106.                 disconnectClient(ss.str());
  107.                 return;
  108.             }
  109.         }
  110.  
  111.         if (!WaitingList::getInstance()->clientLogin(player)) {
  112.             uint32_t currentSlot = WaitingList::getInstance()->getClientSlot(player);
  113.             uint32_t retryTime = WaitingList::getTime(currentSlot);
  114.             std::ostringstream ss;
  115.  
  116.             ss << "Too many players online.\nYou are at place "
  117.                << currentSlot << " on the waiting list.";
  118.  
  119.             auto output = OutputMessagePool::getOutputMessage();
  120.             output->addByte(0x16);
  121.             output->addString(ss.str());
  122.             output->addByte(retryTime);
  123.             send(output);
  124.             disconnect();
  125.             return;
  126.         }
  127.  
  128.         if (!IOLoginData::loadPlayerByName(player, name)) {
  129.             disconnectClient("Your character could not be loaded.");
  130.             return;
  131.         }
  132.  
  133.         player->setOperatingSystem(operatingSystem);
  134.  
  135.         if (!g_game.placeCreature(player, player->getLoginPosition())) {
  136.             if (!g_game.placeCreature(player, player->getTemplePosition(), false, true)) {
  137.                 disconnectClient("Temple position is wrong. Contact the administrator.");
  138.                 return;
  139.             }
  140.         }
  141.  
  142.         if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
  143.             player->registerCreatureEvent("ExtendedOpcode");
  144.         }
  145.  
  146.         player->lastIP = player->getIP();
  147.         player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
  148.         acceptPackets = true;
  149.     } else {
  150.         if (eventConnect != 0 || !g_config.getBoolean(ConfigManager::REPLACE_KICK_ON_LOGIN)) {
  151.             //Already trying to connect
  152.             disconnectClient("You are already logged in.");
  153.             return;
  154.         }
  155.  
  156.         if (foundPlayer->client) {
  157.             foundPlayer->disconnect();
  158.             foundPlayer->isConnecting = true;
  159.  
  160.             eventConnect = g_scheduler.addEvent(createSchedulerTask(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getID(), operatingSystem)));
  161.         } else {
  162.             connect(foundPlayer->getID(), operatingSystem);
  163.         }
  164.     }
  165.     OutputMessagePool::getInstance().addProtocolToAutosend(shared_from_this());
  166. }
  167.  
  168. void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem)
  169. {
  170.     eventConnect = 0;
  171.  
  172.     Player* foundPlayer = g_game.getPlayerByID(playerId);
  173.     if (!foundPlayer || foundPlayer->client) {
  174.         disconnectClient("You are already logged in.");
  175.         return;
  176.     }
  177.  
  178.     if (isConnectionExpired()) {
  179.         //ProtocolGame::release() has been called at this point and the Connection object
  180.         //no longer exists, so we return to prevent leakage of the Player.
  181.         return;
  182.     }
  183.  
  184.     player = foundPlayer;
  185.     player->incrementReferenceCounter();
  186.  
  187.     g_chat->removeUserFromAllChannels(*player);
  188.     player->clearModalWindows();
  189.     player->setOperatingSystem(operatingSystem);
  190.     player->isConnecting = false;
  191.  
  192.     player->client = getThis();
  193.     sendAddCreature(player, player->getPosition(), 0, false);
  194.     player->lastIP = player->getIP();
  195.     player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
  196.     acceptPackets = true;
  197. }
  198.  
  199. void ProtocolGame::logout(bool displayEffect, bool forced)
  200. {
  201.     //dispatcher thread
  202.     if (!player) {
  203.         return;
  204.     }
  205.  
  206.     if (!player->isRemoved()) {
  207.         if (!forced) {
  208.             if (!player->isAccessPlayer()) {
  209.                 if (player->getTile()->hasFlag(TILESTATE_NOLOGOUT)) {
  210.                     player->sendCancelMessage(RETURNVALUE_YOUCANNOTLOGOUTHERE);
  211.                     return;
  212.                 }
  213.  
  214.                 if (!player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) && player->hasCondition(CONDITION_INFIGHT)) {
  215.                     player->sendCancelMessage(RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT);
  216.                     return;
  217.                 }
  218.             }
  219.  
  220.             //scripting event - onLogout
  221.             if (!g_creatureEvents->playerLogout(player)) {
  222.                 //Let the script handle the error message
  223.                 return;
  224.             }
  225.         }
  226.  
  227.         if (displayEffect && player->getHealth() > 0) {
  228.             g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
  229.         }
  230.     }
  231.  
  232.     disconnect();
  233.  
  234.     g_game.removeCreature(player);
  235. }
  236.  
  237. void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
  238. {
  239.     if (g_game.getGameState() == GAME_STATE_SHUTDOWN) {
  240.         disconnect();
  241.         return;
  242.     }
  243.  
  244.     OperatingSystem_t operatingSystem = static_cast<OperatingSystem_t>(msg.get<uint16_t>());
  245.     version = msg.get<uint16_t>();
  246.  
  247.     msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision
  248.  
  249.     if (!Protocol::RSA_decrypt(msg)) {
  250.         disconnect();
  251.         return;
  252.     }
  253.  
  254.     uint32_t key[4];
  255.     key[0] = msg.get<uint32_t>();
  256.     key[1] = msg.get<uint32_t>();
  257.     key[2] = msg.get<uint32_t>();
  258.     key[3] = msg.get<uint32_t>();
  259.     enableXTEAEncryption();
  260.     setXTEAKey(key);
  261.  
  262.     if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
  263.         NetworkMessage opcodeMessage;
  264.         opcodeMessage.addByte(0x32);
  265.         opcodeMessage.addByte(0x00);
  266.         opcodeMessage.add<uint16_t>(0x00);
  267.         writeToOutputBuffer(opcodeMessage);
  268.     }
  269.  
  270.     msg.skipBytes(1); // gamemaster flag
  271.  
  272.     std::string sessionKey = msg.getString();
  273.     size_t pos = sessionKey.find('\n');
  274.     if (pos == std::string::npos) {
  275.         disconnectClient("You must enter your account name.");
  276.         return;
  277.     }
  278.  
  279.     std::string accountName = sessionKey.substr(0, pos);
  280.     if (accountName.empty()) {
  281.         disconnectClient("You must enter your account name.");
  282.         return;
  283.     }
  284.  
  285.     std::string password = sessionKey.substr(pos + 1);
  286.  
  287.     std::string characterName = msg.getString();
  288.  
  289.     uint32_t timeStamp = msg.get<uint32_t>();
  290.     uint8_t randNumber = msg.getByte();
  291.     if (challengeTimestamp != timeStamp || challengeRandom != randNumber) {
  292.         disconnect();
  293.         return;
  294.     }
  295.  
  296.     if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) {
  297.         std::ostringstream ss;
  298.         ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!";
  299.         disconnectClient(ss.str());
  300.         return;
  301.     }
  302.  
  303.     if (g_game.getGameState() == GAME_STATE_STARTUP) {
  304.         disconnectClient("Gameworld is starting up. Please wait.");
  305.         return;
  306.     }
  307.  
  308.     if (g_game.getGameState() == GAME_STATE_MAINTAIN) {
  309.         disconnectClient("Gameworld is under maintenance. Please re-connect in a while.");
  310.         return;
  311.     }
  312.  
  313.     BanInfo banInfo;
  314.     if (IOBan::isIpBanned(getIP(), banInfo)) {
  315.         if (banInfo.reason.empty()) {
  316.             banInfo.reason = "(none)";
  317.         }
  318.  
  319.         std::ostringstream ss;
  320.         ss << "Your IP has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason;
  321.         disconnectClient(ss.str());
  322.         return;
  323.     }
  324.  
  325.     uint32_t accountId = IOLoginData::gameworldAuthentication(accountName, password, characterName);
  326.     if (accountId == 0) {
  327.         disconnectClient("Account name or password is not correct.");
  328.         return;
  329.     }
  330.  
  331.     g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem)));
  332. }
  333.  
  334. void ProtocolGame::onConnect()
  335. {
  336.     auto output = OutputMessagePool::getOutputMessage();
  337.     static std::random_device rd;
  338.     static std::ranlux24 generator(rd());
  339.     static std::uniform_int_distribution<uint16_t> randNumber(0x00, 0xFF);
  340.  
  341.     // Skip checksum
  342.     output->skipBytes(sizeof(uint32_t));
  343.  
  344.     // Packet length & type
  345.     output->add<uint16_t>(0x0006);
  346.     output->addByte(0x1F);
  347.  
  348.     // Add timestamp & random number
  349.     challengeTimestamp = static_cast<uint32_t>(time(nullptr));
  350.     output->add<uint32_t>(challengeTimestamp);
  351.  
  352.     challengeRandom = randNumber(generator);
  353.     output->addByte(challengeRandom);
  354.  
  355.     // Go back and write checksum
  356.     output->skipBytes(-12);
  357.     output->add<uint32_t>(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8));
  358.  
  359.     send(output);
  360. }
  361.  
  362. void ProtocolGame::disconnectClient(const std::string& message) const
  363. {
  364.     auto output = OutputMessagePool::getOutputMessage();
  365.     output->addByte(0x14);
  366.     output->addString(message);
  367.     send(output);
  368.     disconnect();
  369. }
  370.  
  371. void ProtocolGame::writeToOutputBuffer(const NetworkMessage& msg)
  372. {
  373.     auto out = getOutputBuffer(msg.getLength());
  374.     out->append(msg);
  375. }
  376.  
  377. void ProtocolGame::parsePacket(NetworkMessage& msg)
  378. {
  379.     if (!acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN || msg.getLength() <= 0) {
  380.         return;
  381.     }
  382.  
  383.     uint8_t recvbyte = msg.getByte();
  384.  
  385.     if (!player) {
  386.         if (recvbyte == 0x0F) {
  387.             disconnect();
  388.         }
  389.  
  390.         return;
  391.     }
  392.  
  393.     //a dead player can not performs actions
  394.     if (player->isRemoved() || player->getHealth() <= 0) {
  395.         if (recvbyte == 0x0F) {
  396.             disconnect();
  397.             return;
  398.         }
  399.  
  400.         if (recvbyte != 0x14) {
  401.             return;
  402.         }
  403.     }
  404.  
  405.     switch (recvbyte) {
  406.         case 0x14: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); break;
  407.         case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); break;
  408.         case 0x1E: addGameTask(&Game::playerReceivePing, player->getID()); break;
  409.         case 0x32: parseExtendedOpcode(msg); break; //otclient extended opcode
  410.         case 0x64: parseAutoWalk(msg); break;
  411.         case 0x65: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); break;
  412.         case 0x66: addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); break;
  413.         case 0x67: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTH); break;
  414.         case 0x68: addGameTask(&Game::playerMove, player->getID(), DIRECTION_WEST); break;
  415.         case 0x69: addGameTask(&Game::playerStopAutoWalk, player->getID()); break;
  416.         case 0x6A: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHEAST); break;
  417.         case 0x6B: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHEAST); break;
  418.         case 0x6C: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHWEST); break;
  419.         case 0x6D: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHWEST); break;
  420.         case 0x6F: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_NORTH); break;
  421.         case 0x70: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_EAST); break;
  422.         case 0x71: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_SOUTH); break;
  423.         case 0x72: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_WEST); break;
  424.         case 0x78: parseThrow(msg); break;
  425.         case 0x79: parseLookInShop(msg); break;
  426.         case 0x7A: parsePlayerPurchase(msg); break;
  427.         case 0x7B: parsePlayerSale(msg); break;
  428.         case 0x7C: addGameTask(&Game::playerCloseShop, player->getID()); break;
  429.         case 0x7D: parseRequestTrade(msg); break;
  430.         case 0x7E: parseLookInTrade(msg); break;
  431.         case 0x7F: addGameTask(&Game::playerAcceptTrade, player->getID()); break;
  432.         case 0x80: addGameTask(&Game::playerCloseTrade, player->getID()); break;
  433.         case 0x82: parseUseItem(msg); break;
  434.         case 0x83: parseUseItemEx(msg); break;
  435.         case 0x84: parseUseWithCreature(msg); break;
  436.         case 0x85: parseRotateItem(msg); break;
  437.         case 0x87: parseCloseContainer(msg); break;
  438.         case 0x88: parseUpArrowContainer(msg); break;
  439.         case 0x89: parseTextWindow(msg); break;
  440.         case 0x8A: parseHouseWindow(msg); break;
  441.         case 0x8C: parseLookAt(msg); break;
  442.         case 0x8D: parseLookInBattleList(msg); break;
  443.         case 0x8E: /* join aggression */ break;
  444.         case 0x96: parseSay(msg); break;
  445.         case 0x97: addGameTask(&Game::playerRequestChannels, player->getID()); break;
  446.         case 0x98: parseOpenChannel(msg); break;
  447.         case 0x99: parseCloseChannel(msg); break;
  448.         case 0x9A: parseOpenPrivateChannel(msg); break;
  449.         case 0x9E: addGameTask(&Game::playerCloseNpcChannel, player->getID()); break;
  450.         case 0xA0: parseFightModes(msg); break;
  451.         case 0xA1: parseAttack(msg); break;
  452.         case 0xA2: parseFollow(msg); break;
  453.         case 0xA3: parseInviteToParty(msg); break;
  454.         case 0xA4: parseJoinParty(msg); break;
  455.         case 0xA5: parseRevokePartyInvite(msg); break;
  456.         case 0xA6: parsePassPartyLeadership(msg); break;
  457.         case 0xA7: addGameTask(&Game::playerLeaveParty, player->getID()); break;
  458.         case 0xA8: parseEnableSharedPartyExperience(msg); break;
  459.         case 0xAA: addGameTask(&Game::playerCreatePrivateChannel, player->getID()); break;
  460.         case 0xAB: parseChannelInvite(msg); break;
  461.         case 0xAC: parseChannelExclude(msg); break;
  462.         case 0xBE: addGameTask(&Game::playerCancelAttackAndFollow, player->getID()); break;
  463.         case 0xC9: /* update tile */ break;
  464.         case 0xCA: parseUpdateContainer(msg); break;
  465.         case 0xCB: parseBrowseField(msg); break;
  466.         case 0xCC: parseSeekInContainer(msg); break;
  467.         case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break;
  468.         case 0xD3: parseSetOutfit(msg); break;
  469.         case 0xD4: parseToggleMount(msg); break;
  470.         case 0xDC: parseAddVip(msg); break;
  471.         case 0xDD: parseRemoveVip(msg); break;
  472.         case 0xDE: parseEditVip(msg); break;
  473.         case 0xE6: parseBugReport(msg); break;
  474.         case 0xE7: /* thank you */ break;
  475.         case 0xE8: parseDebugAssert(msg); break;
  476.         case 0xF0: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerShowQuestLog, player->getID()); break;
  477.         case 0xF1: parseQuestLine(msg); break;
  478.         case 0xF2: /* rule violation report */ break;
  479.         case 0xF3: /* get object info */ break;
  480.         case 0xF4: parseMarketLeave(); break;
  481.         case 0xF5: parseMarketBrowse(msg); break;
  482.         case 0xF6: parseMarketCreateOffer(msg); break;
  483.         case 0xF7: parseMarketCancelOffer(msg); break;
  484.         case 0xF8: parseMarketAcceptOffer(msg); break;
  485.         case 0xF9: parseModalWindowAnswer(msg); break;
  486.  
  487.         default:
  488.             // std::cout << "Player: " << player->getName() << " sent an unknown packet header: 0x" << std::hex << static_cast<uint16_t>(recvbyte) << std::dec << "!" << std::endl;
  489.             break;
  490.     }
  491.  
  492.     if (msg.isOverrun()) {
  493.         disconnect();
  494.     }
  495. }
  496.  
  497. void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg)
  498. {
  499.     msg.add<uint16_t>(0x00); //environmental effects
  500.  
  501.     int32_t count;
  502.     Item* ground = tile->getGround();
  503.     if (ground) {
  504.         msg.addItem(ground);
  505.         count = 1;
  506.     } else {
  507.         count = 0;
  508.     }
  509.  
  510.     const TileItemVector* items = tile->getItemList();
  511.     if (items) {
  512.         for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
  513.             msg.addItem(*it);
  514.  
  515.             if (++count == 10) {
  516.                 return;
  517.             }
  518.         }
  519.     }
  520.  
  521.     const CreatureVector* creatures = tile->getCreatures();
  522.     if (creatures) {
  523.         for (const Creature* creature : boost::adaptors::reverse(*creatures)) {
  524.             if (!player->canSeeCreature(creature)) {
  525.                 continue;
  526.             }
  527.  
  528.             bool known;
  529.             uint32_t removedKnown;
  530.             checkCreatureAsKnown(creature->getID(), known, removedKnown);
  531.             AddCreature(msg, creature, known, removedKnown);
  532.  
  533.             if (++count == 10) {
  534.                 return;
  535.             }
  536.         }
  537.     }
  538.  
  539.     if (items) {
  540.         for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
  541.             msg.addItem(*it);
  542.  
  543.             if (++count == 10) {
  544.                 return;
  545.             }
  546.         }
  547.     }
  548. }
  549.  
  550. void ProtocolGame::GetMapDescription(int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, NetworkMessage& msg)
  551. {
  552.     int32_t skip = -1;
  553.     int32_t startz, endz, zstep;
  554.  
  555.     if (z > 7) {
  556.         startz = z - 2;
  557.         endz = std::min<int32_t>(MAP_MAX_LAYERS - 1, z + 2);
  558.         zstep = 1;
  559.     } else {
  560.         startz = 7;
  561.         endz = 0;
  562.         zstep = -1;
  563.     }
  564.  
  565.     for (int32_t nz = startz; nz != endz + zstep; nz += zstep) {
  566.         GetFloorDescription(msg, x, y, nz, width, height, z - nz, skip);
  567.     }
  568.  
  569.     if (skip >= 0) {
  570.         msg.addByte(skip);
  571.         msg.addByte(0xFF);
  572.     }
  573. }
  574.  
  575. void ProtocolGame::GetFloorDescription(NetworkMessage& msg, int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, int32_t offset, int32_t& skip)
  576. {
  577.     for (int32_t nx = 0; nx < width; nx++) {
  578.         for (int32_t ny = 0; ny < height; ny++) {
  579.             Tile* tile = g_game.map.getTile(x + nx + offset, y + ny + offset, z);
  580.             if (tile) {
  581.                 if (skip >= 0) {
  582.                     msg.addByte(skip);
  583.                     msg.addByte(0xFF);
  584.                 }
  585.  
  586.                 skip = 0;
  587.                 GetTileDescription(tile, msg);
  588.             } else if (skip == 0xFE) {
  589.                 msg.addByte(0xFF);
  590.                 msg.addByte(0xFF);
  591.                 skip = -1;
  592.             } else {
  593.                 ++skip;
  594.             }
  595.         }
  596.     }
  597. }
  598.  
  599. void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& removedKnown)
  600. {
  601.     auto result = knownCreatureSet.insert(id);
  602.     if (!result.second) {
  603.         known = true;
  604.         return;
  605.     }
  606.  
  607.     known = false;
  608.  
  609.     if (knownCreatureSet.size() > 1300) {
  610.         // Look for a creature to remove
  611.         for (auto it = knownCreatureSet.begin(), end = knownCreatureSet.end(); it != end; ++it) {
  612.             Creature* creature = g_game.getCreatureByID(*it);
  613.             if (!canSee(creature)) {
  614.                 removedKnown = *it;
  615.                 knownCreatureSet.erase(it);
  616.                 return;
  617.             }
  618.         }
  619.  
  620.         // Bad situation. Let's just remove anyone.
  621.         auto it = knownCreatureSet.begin();
  622.         if (*it == id) {
  623.             ++it;
  624.         }
  625.  
  626.         removedKnown = *it;
  627.         knownCreatureSet.erase(it);
  628.     } else {
  629.         removedKnown = 0;
  630.     }
  631. }
  632.  
  633. bool ProtocolGame::canSee(const Creature* c) const
  634. {
  635.     if (!c || !player || c->isRemoved()) {
  636.         return false;
  637.     }
  638.  
  639.     if (!player->canSeeCreature(c)) {
  640.         return false;
  641.     }
  642.  
  643.     return canSee(c->getPosition());
  644. }
  645.  
  646. bool ProtocolGame::canSee(const Position& pos) const
  647. {
  648.     return canSee(pos.x, pos.y, pos.z);
  649. }
  650.  
  651. bool ProtocolGame::canSee(int32_t x, int32_t y, int32_t z) const
  652. {
  653.     if (!player) {
  654.         return false;
  655.     }
  656.  
  657.     const Position& myPos = player->getPosition();
  658.     if (myPos.z <= 7) {
  659.         //we are on ground level or above (7 -> 0)
  660.         //view is from 7 -> 0
  661.         if (z > 7) {
  662.             return false;
  663.         }
  664.     } else if (myPos.z >= 8) {
  665.         //we are underground (8 -> 15)
  666.         //view is +/- 2 from the floor we stand on
  667.         if (std::abs(myPos.getZ() - z) > 2) {
  668.             return false;
  669.         }
  670.     }
  671.  
  672.     //negative offset means that the action taken place is on a lower floor than ourself
  673.     int32_t offsetz = myPos.getZ() - z;
  674.     if ((x >= myPos.getX() - 8 + offsetz) && (x <= myPos.getX() + 9 + offsetz) &&
  675.             (y >= myPos.getY() - 6 + offsetz) && (y <= myPos.getY() + 7 + offsetz)) {
  676.         return true;
  677.     }
  678.     return false;
  679. }
  680.  
  681. // Parse methods
  682. void ProtocolGame::parseChannelInvite(NetworkMessage& msg)
  683. {
  684.     const std::string name = msg.getString();
  685.     addGameTask(&Game::playerChannelInvite, player->getID(), name);
  686. }
  687.  
  688. void ProtocolGame::parseChannelExclude(NetworkMessage& msg)
  689. {
  690.     const std::string name = msg.getString();
  691.     addGameTask(&Game::playerChannelExclude, player->getID(), name);
  692. }
  693.  
  694. void ProtocolGame::parseOpenChannel(NetworkMessage& msg)
  695. {
  696.     uint16_t channelId = msg.get<uint16_t>();
  697.     addGameTask(&Game::playerOpenChannel, player->getID(), channelId);
  698. }
  699.  
  700. void ProtocolGame::parseCloseChannel(NetworkMessage& msg)
  701. {
  702.     uint16_t channelId = msg.get<uint16_t>();
  703.     addGameTask(&Game::playerCloseChannel, player->getID(), channelId);
  704. }
  705.  
  706. void ProtocolGame::parseOpenPrivateChannel(NetworkMessage& msg)
  707. {
  708.     const std::string receiver = msg.getString();
  709.     addGameTask(&Game::playerOpenPrivateChannel, player->getID(), receiver);
  710. }
  711.  
  712. void ProtocolGame::parseAutoWalk(NetworkMessage& msg)
  713. {
  714.     uint8_t numdirs = msg.getByte();
  715.     if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 8)) {
  716.         return;
  717.     }
  718.  
  719.     msg.skipBytes(numdirs);
  720.  
  721.     std::forward_list<Direction> path;
  722.     for (uint8_t i = 0; i < numdirs; ++i) {
  723.         uint8_t rawdir = msg.getPreviousByte();
  724.         switch (rawdir) {
  725.             case 1: path.push_front(DIRECTION_EAST); break;
  726.             case 2: path.push_front(DIRECTION_NORTHEAST); break;
  727.             case 3: path.push_front(DIRECTION_NORTH); break;
  728.             case 4: path.push_front(DIRECTION_NORTHWEST); break;
  729.             case 5: path.push_front(DIRECTION_WEST); break;
  730.             case 6: path.push_front(DIRECTION_SOUTHWEST); break;
  731.             case 7: path.push_front(DIRECTION_SOUTH); break;
  732.             case 8: path.push_front(DIRECTION_SOUTHEAST); break;
  733.             default: break;
  734.         }
  735.     }
  736.  
  737.     if (path.empty()) {
  738.         return;
  739.     }
  740.  
  741.     addGameTask(&Game::playerAutoWalk, player->getID(), path);
  742. }
  743.  
  744. void ProtocolGame::parseSetOutfit(NetworkMessage& msg)
  745. {
  746.     Outfit_t newOutfit;
  747.     newOutfit.lookType = msg.get<uint16_t>();
  748.     newOutfit.lookHead = msg.getByte();
  749.     newOutfit.lookBody = msg.getByte();
  750.     newOutfit.lookLegs = msg.getByte();
  751.     newOutfit.lookFeet = msg.getByte();
  752.     newOutfit.lookAddons = msg.getByte();
  753.     newOutfit.lookMount = msg.get<uint16_t>();
  754.     addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit);
  755. }
  756.  
  757. void ProtocolGame::parseToggleMount(NetworkMessage& msg)
  758. {
  759.     bool mount = msg.getByte() != 0;
  760.     addGameTask(&Game::playerToggleMount, player->getID(), mount);
  761. }
  762.  
  763. void ProtocolGame::parseUseItem(NetworkMessage& msg)
  764. {
  765.     Position pos = msg.getPosition();
  766.     uint16_t spriteId = msg.get<uint16_t>();
  767.     uint8_t stackpos = msg.getByte();
  768.     uint8_t index = msg.getByte();
  769.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItem, player->getID(), pos, stackpos, index, spriteId);
  770. }
  771.  
  772. void ProtocolGame::parseUseItemEx(NetworkMessage& msg)
  773. {
  774.     Position fromPos = msg.getPosition();
  775.     uint16_t fromSpriteId = msg.get<uint16_t>();
  776.     uint8_t fromStackPos = msg.getByte();
  777.     Position toPos = msg.getPosition();
  778.     uint16_t toSpriteId = msg.get<uint16_t>();
  779.     uint8_t toStackPos = msg.getByte();
  780.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItemEx, player->getID(), fromPos, fromStackPos, fromSpriteId, toPos, toStackPos, toSpriteId);
  781. }
  782.  
  783. void ProtocolGame::parseUseWithCreature(NetworkMessage& msg)
  784. {
  785.     Position fromPos = msg.getPosition();
  786.     uint16_t spriteId = msg.get<uint16_t>();
  787.     uint8_t fromStackPos = msg.getByte();
  788.     uint32_t creatureId = msg.get<uint32_t>();
  789.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseWithCreature, player->getID(), fromPos, fromStackPos, creatureId, spriteId);
  790. }
  791.  
  792. void ProtocolGame::parseCloseContainer(NetworkMessage& msg)
  793. {
  794.     uint8_t cid = msg.getByte();
  795.     addGameTask(&Game::playerCloseContainer, player->getID(), cid);
  796. }
  797.  
  798. void ProtocolGame::parseUpArrowContainer(NetworkMessage& msg)
  799. {
  800.     uint8_t cid = msg.getByte();
  801.     addGameTask(&Game::playerMoveUpContainer, player->getID(), cid);
  802. }
  803.  
  804. void ProtocolGame::parseUpdateContainer(NetworkMessage& msg)
  805. {
  806.     uint8_t cid = msg.getByte();
  807.     addGameTask(&Game::playerUpdateContainer, player->getID(), cid);
  808. }
  809.  
  810. void ProtocolGame::parseThrow(NetworkMessage& msg)
  811. {
  812.     Position fromPos = msg.getPosition();
  813.     uint16_t spriteId = msg.get<uint16_t>();
  814.     uint8_t fromStackpos = msg.getByte();
  815.     Position toPos = msg.getPosition();
  816.     uint8_t count = msg.getByte();
  817.  
  818.     if (toPos != fromPos) {
  819.         addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerMoveThing, player->getID(), fromPos, spriteId, fromStackpos, toPos, count);
  820.     }
  821. }
  822.  
  823. void ProtocolGame::parseLookAt(NetworkMessage& msg)
  824. {
  825.     Position pos = msg.getPosition();
  826.     msg.skipBytes(2); // spriteId
  827.     uint8_t stackpos = msg.getByte();
  828.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookAt, player->getID(), pos, stackpos);
  829. }
  830.  
  831. void ProtocolGame::parseLookInBattleList(NetworkMessage& msg)
  832. {
  833.     uint32_t creatureId = msg.get<uint32_t>();
  834.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInBattleList, player->getID(), creatureId);
  835. }
  836.  
  837. void ProtocolGame::parseSay(NetworkMessage& msg)
  838. {
  839.     std::string receiver;
  840.     uint16_t channelId;
  841.  
  842.     SpeakClasses type = static_cast<SpeakClasses>(msg.getByte());
  843.     switch (type) {
  844.         case TALKTYPE_PRIVATE_TO:
  845.         case TALKTYPE_PRIVATE_RED_TO:
  846.             receiver = msg.getString();
  847.             channelId = 0;
  848.             break;
  849.  
  850.         case TALKTYPE_CHANNEL_Y:
  851.         case TALKTYPE_CHANNEL_R1:
  852.             channelId = msg.get<uint16_t>();
  853.             break;
  854.  
  855.         default:
  856.             channelId = 0;
  857.             break;
  858.     }
  859.  
  860.     const std::string text = msg.getString();
  861.     if (text.length() > 255) {
  862.         return;
  863.     }
  864.  
  865.     addGameTask(&Game::playerSay, player->getID(), channelId, type, receiver, text);
  866. }
  867.  
  868. void ProtocolGame::parseFightModes(NetworkMessage& msg)
  869. {
  870.     uint8_t rawFightMode = msg.getByte(); // 1 - offensive, 2 - balanced, 3 - defensive
  871.     uint8_t rawChaseMode = msg.getByte(); // 0 - stand while fightning, 1 - chase opponent
  872.     uint8_t rawSecureMode = msg.getByte(); // 0 - can't attack unmarked, 1 - can attack unmarked
  873.     // uint8_t rawPvpMode = msg.getByte(); // pvp mode introduced in 10.0
  874.  
  875.     chaseMode_t chaseMode;
  876.     if (rawChaseMode == 1) {
  877.         chaseMode = CHASEMODE_FOLLOW;
  878.     } else {
  879.         chaseMode = CHASEMODE_STANDSTILL;
  880.     }
  881.  
  882.     fightMode_t fightMode;
  883.     if (rawFightMode == 1) {
  884.         fightMode = FIGHTMODE_ATTACK;
  885.     } else if (rawFightMode == 2) {
  886.         fightMode = FIGHTMODE_BALANCED;
  887.     } else {
  888.         fightMode = FIGHTMODE_DEFENSE;
  889.     }
  890.  
  891.     addGameTask(&Game::playerSetFightModes, player->getID(), fightMode, chaseMode, rawSecureMode != 0);
  892. }
  893.  
  894. void ProtocolGame::parseAttack(NetworkMessage& msg)
  895. {
  896.     uint32_t creatureId = msg.get<uint32_t>();
  897.     // msg.get<uint32_t>(); creatureId (same as above)
  898.     addGameTask(&Game::playerSetAttackedCreature, player->getID(), creatureId);
  899. }
  900.  
  901. void ProtocolGame::parseFollow(NetworkMessage& msg)
  902. {
  903.     uint32_t creatureId = msg.get<uint32_t>();
  904.     // msg.get<uint32_t>(); creatureId (same as above)
  905.     addGameTask(&Game::playerFollowCreature, player->getID(), creatureId);
  906. }
  907.  
  908. void ProtocolGame::parseTextWindow(NetworkMessage& msg)
  909. {
  910.     uint32_t windowTextId = msg.get<uint32_t>();
  911.     const std::string newText = msg.getString();
  912.     addGameTask(&Game::playerWriteItem, player->getID(), windowTextId, newText);
  913. }
  914.  
  915. void ProtocolGame::parseHouseWindow(NetworkMessage& msg)
  916. {
  917.     uint8_t doorId = msg.getByte();
  918.     uint32_t id = msg.get<uint32_t>();
  919.     const std::string text = msg.getString();
  920.     addGameTask(&Game::playerUpdateHouseWindow, player->getID(), doorId, id, text);
  921. }
  922.  
  923. void ProtocolGame::parseLookInShop(NetworkMessage& msg)
  924. {
  925.     uint16_t id = msg.get<uint16_t>();
  926.     uint8_t count = msg.getByte();
  927.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInShop, player->getID(), id, count);
  928. }
  929.  
  930. void ProtocolGame::parsePlayerPurchase(NetworkMessage& msg)
  931. {
  932.     uint16_t id = msg.get<uint16_t>();
  933.     uint8_t count = msg.getByte();
  934.     uint8_t amount = msg.getByte();
  935.     bool ignoreCap = msg.getByte() != 0;
  936.     bool inBackpacks = msg.getByte() != 0;
  937.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerPurchaseItem, player->getID(), id, count, amount, ignoreCap, inBackpacks);
  938. }
  939.  
  940. void ProtocolGame::parsePlayerSale(NetworkMessage& msg)
  941. {
  942.     uint16_t id = msg.get<uint16_t>();
  943.     uint8_t count = msg.getByte();
  944.     uint8_t amount = msg.getByte();
  945.     bool ignoreEquipped = msg.getByte() != 0;
  946.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerSellItem, player->getID(), id, count, amount, ignoreEquipped);
  947. }
  948.  
  949. void ProtocolGame::parseRequestTrade(NetworkMessage& msg)
  950. {
  951.     Position pos = msg.getPosition();
  952.     uint16_t spriteId = msg.get<uint16_t>();
  953.     uint8_t stackpos = msg.getByte();
  954.     uint32_t playerId = msg.get<uint32_t>();
  955.     addGameTask(&Game::playerRequestTrade, player->getID(), pos, stackpos, playerId, spriteId);
  956. }
  957.  
  958. void ProtocolGame::parseLookInTrade(NetworkMessage& msg)
  959. {
  960.     bool counterOffer = (msg.getByte() == 0x01);
  961.     uint8_t index = msg.getByte();
  962.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInTrade, player->getID(), counterOffer, index);
  963. }
  964.  
  965. void ProtocolGame::parseAddVip(NetworkMessage& msg)
  966. {
  967.     const std::string name = msg.getString();
  968.     addGameTask(&Game::playerRequestAddVip, player->getID(), name);
  969. }
  970.  
  971. void ProtocolGame::parseRemoveVip(NetworkMessage& msg)
  972. {
  973.     uint32_t guid = msg.get<uint32_t>();
  974.     addGameTask(&Game::playerRequestRemoveVip, player->getID(), guid);
  975. }
  976.  
  977. void ProtocolGame::parseEditVip(NetworkMessage& msg)
  978. {
  979.     uint32_t guid = msg.get<uint32_t>();
  980.     const std::string description = msg.getString();
  981.     uint32_t icon = std::min<uint32_t>(10, msg.get<uint32_t>()); // 10 is max icon in 9.63
  982.     bool notify = msg.getByte() != 0;
  983.     addGameTask(&Game::playerRequestEditVip, player->getID(), guid, description, icon, notify);
  984. }
  985.  
  986. void ProtocolGame::parseRotateItem(NetworkMessage& msg)
  987. {
  988.     Position pos = msg.getPosition();
  989.     uint16_t spriteId = msg.get<uint16_t>();
  990.     uint8_t stackpos = msg.getByte();
  991.     addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerRotateItem, player->getID(), pos, stackpos, spriteId);
  992. }
  993.  
  994. void ProtocolGame::parseBugReport(NetworkMessage& msg)
  995. {
  996.     uint8_t category = msg.getByte();
  997.     std::string message = msg.getString();
  998.  
  999.     Position position;
  1000.     if (category == BUG_CATEGORY_MAP) {
  1001.         position = msg.getPosition();
  1002.     }
  1003.  
  1004.     addGameTask(&Game::playerReportBug, player->getID(), message, position, category);
  1005. }
  1006.  
  1007. void ProtocolGame::parseDebugAssert(NetworkMessage& msg)
  1008. {
  1009.     if (debugAssertSent) {
  1010.         return;
  1011.     }
  1012.  
  1013.     debugAssertSent = true;
  1014.  
  1015.     std::string assertLine = msg.getString();
  1016.     std::string date = msg.getString();
  1017.     std::string description = msg.getString();
  1018.     std::string comment = msg.getString();
  1019.     addGameTask(&Game::playerDebugAssert, player->getID(), assertLine, date, description, comment);
  1020. }
  1021.  
  1022. void ProtocolGame::parseInviteToParty(NetworkMessage& msg)
  1023. {
  1024.     uint32_t targetId = msg.get<uint32_t>();
  1025.     addGameTask(&Game::playerInviteToParty, player->getID(), targetId);
  1026. }
  1027.  
  1028. void ProtocolGame::parseJoinParty(NetworkMessage& msg)
  1029. {
  1030.     uint32_t targetId = msg.get<uint32_t>();
  1031.     addGameTask(&Game::playerJoinParty, player->getID(), targetId);
  1032. }
  1033.  
  1034. void ProtocolGame::parseRevokePartyInvite(NetworkMessage& msg)
  1035. {
  1036.     uint32_t targetId = msg.get<uint32_t>();
  1037.     addGameTask(&Game::playerRevokePartyInvitation, player->getID(), targetId);
  1038. }
  1039.  
  1040. void ProtocolGame::parsePassPartyLeadership(NetworkMessage& msg)
  1041. {
  1042.     uint32_t targetId = msg.get<uint32_t>();
  1043.     addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId);
  1044. }
  1045.  
  1046. void ProtocolGame::parseEnableSharedPartyExperience(NetworkMessage& msg)
  1047. {
  1048.     bool sharedExpActive = msg.getByte() == 1;
  1049.     addGameTask(&Game::playerEnableSharedPartyExperience, player->getID(), sharedExpActive);
  1050. }
  1051.  
  1052. void ProtocolGame::parseQuestLine(NetworkMessage& msg)
  1053. {
  1054.     uint16_t questId = msg.get<uint16_t>();
  1055.     addGameTask(&Game::playerShowQuestLine, player->getID(), questId);
  1056. }
  1057.  
  1058. void ProtocolGame::parseMarketLeave()
  1059. {
  1060.     addGameTask(&Game::playerLeaveMarket, player->getID());
  1061. }
  1062.  
  1063. void ProtocolGame::parseMarketBrowse(NetworkMessage& msg)
  1064. {
  1065.     uint16_t browseId = msg.get<uint16_t>();
  1066.  
  1067.     if (browseId == MARKETREQUEST_OWN_OFFERS) {
  1068.         addGameTask(&Game::playerBrowseMarketOwnOffers, player->getID());
  1069.     } else if (browseId == MARKETREQUEST_OWN_HISTORY) {
  1070.         addGameTask(&Game::playerBrowseMarketOwnHistory, player->getID());
  1071.     } else {
  1072.         addGameTask(&Game::playerBrowseMarket, player->getID(), browseId);
  1073.     }
  1074. }
  1075.  
  1076. void ProtocolGame::parseMarketCreateOffer(NetworkMessage& msg)
  1077. {
  1078.     uint8_t type = msg.getByte();
  1079.     uint16_t spriteId = msg.get<uint16_t>();
  1080.     uint16_t amount = msg.get<uint16_t>();
  1081.     uint32_t price = msg.get<uint32_t>();
  1082.     bool anonymous = (msg.getByte() != 0);
  1083.     addGameTask(&Game::playerCreateMarketOffer, player->getID(), type, spriteId, amount, price, anonymous);
  1084. }
  1085.  
  1086. void ProtocolGame::parseMarketCancelOffer(NetworkMessage& msg)
  1087. {
  1088.     uint32_t timestamp = msg.get<uint32_t>();
  1089.     uint16_t counter = msg.get<uint16_t>();
  1090.     addGameTask(&Game::playerCancelMarketOffer, player->getID(), timestamp, counter);
  1091. }
  1092.  
  1093. void ProtocolGame::parseMarketAcceptOffer(NetworkMessage& msg)
  1094. {
  1095.     uint32_t timestamp = msg.get<uint32_t>();
  1096.     uint16_t counter = msg.get<uint16_t>();
  1097.     uint16_t amount = msg.get<uint16_t>();
  1098.     addGameTask(&Game::playerAcceptMarketOffer, player->getID(), timestamp, counter, amount);
  1099. }
  1100.  
  1101. void ProtocolGame::parseModalWindowAnswer(NetworkMessage& msg)
  1102. {
  1103.     uint32_t id = msg.get<uint32_t>();
  1104.     uint8_t button = msg.getByte();
  1105.     uint8_t choice = msg.getByte();
  1106.     addGameTask(&Game::playerAnswerModalWindow, player->getID(), id, button, choice);
  1107. }
  1108.  
  1109. void ProtocolGame::parseBrowseField(NetworkMessage& msg)
  1110. {
  1111.     const Position& pos = msg.getPosition();
  1112.     addGameTask(&Game::playerBrowseField, player->getID(), pos);
  1113. }
  1114.  
  1115. void ProtocolGame::parseSeekInContainer(NetworkMessage& msg)
  1116. {
  1117.     uint8_t containerId = msg.getByte();
  1118.     uint16_t index = msg.get<uint16_t>();
  1119.     addGameTask(&Game::playerSeekInContainer, player->getID(), containerId, index);
  1120. }
  1121.  
  1122. // Send methods
  1123. void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver)
  1124. {
  1125.     NetworkMessage msg;
  1126.     msg.addByte(0xAD);
  1127.     msg.addString(receiver);
  1128.     writeToOutputBuffer(msg);
  1129. }
  1130.  
  1131. void ProtocolGame::sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent)
  1132. {
  1133.     NetworkMessage msg;
  1134.     msg.addByte(0xF3);
  1135.     msg.add<uint16_t>(channelId);
  1136.     msg.addString(playerName);
  1137.     msg.addByte(channelEvent);
  1138.     writeToOutputBuffer(msg);
  1139. }
  1140.  
  1141. void ProtocolGame::sendCreatureOutfit(const Creature* creature, const Outfit_t& outfit)
  1142. {
  1143.     if (!canSee(creature)) {
  1144.         return;
  1145.     }
  1146.  
  1147.     NetworkMessage msg;
  1148.     msg.addByte(0x8E);
  1149.     msg.add<uint32_t>(creature->getID());
  1150.     AddOutfit(msg, outfit);
  1151.     writeToOutputBuffer(msg);
  1152. }
  1153.  
  1154. void ProtocolGame::sendCreatureLight(const Creature* creature)
  1155. {
  1156.     if (!canSee(creature)) {
  1157.         return;
  1158.     }
  1159.  
  1160.     NetworkMessage msg;
  1161.     AddCreatureLight(msg, creature);
  1162.     writeToOutputBuffer(msg);
  1163. }
  1164.  
  1165. void ProtocolGame::sendWorldLight(const LightInfo& lightInfo)
  1166. {
  1167.     NetworkMessage msg;
  1168.     AddWorldLight(msg, lightInfo);
  1169.     writeToOutputBuffer(msg);
  1170. }
  1171.  
  1172. void ProtocolGame::sendCreatureWalkthrough(const Creature* creature, bool walkthrough)
  1173. {
  1174.     if (!canSee(creature)) {
  1175.         return;
  1176.     }
  1177.  
  1178.     NetworkMessage msg;
  1179.     msg.addByte(0x92);
  1180.     msg.add<uint32_t>(creature->getID());
  1181.     msg.addByte(walkthrough ? 0x00 : 0x01);
  1182.     writeToOutputBuffer(msg);
  1183. }
  1184.  
  1185. void ProtocolGame::sendCreatureShield(const Creature* creature)
  1186. {
  1187.     if (!canSee(creature)) {
  1188.         return;
  1189.     }
  1190.  
  1191.     NetworkMessage msg;
  1192.     msg.addByte(0x91);
  1193.     msg.add<uint32_t>(creature->getID());
  1194.     msg.addByte(player->getPartyShield(creature->getPlayer()));
  1195.     writeToOutputBuffer(msg);
  1196. }
  1197.  
  1198. void ProtocolGame::sendCreatureSkull(const Creature* creature)
  1199. {
  1200.     if (g_game.getWorldType() != WORLD_TYPE_PVP) {
  1201.         return;
  1202.     }
  1203.  
  1204.     if (!canSee(creature)) {
  1205.         return;
  1206.     }
  1207.  
  1208.     NetworkMessage msg;
  1209.     msg.addByte(0x90);
  1210.     msg.add<uint32_t>(creature->getID());
  1211.     msg.addByte(player->getSkullClient(creature));
  1212.     writeToOutputBuffer(msg);
  1213. }
  1214.  
  1215. void ProtocolGame::sendCreatureType(uint32_t creatureId, uint8_t creatureType)
  1216. {
  1217.     NetworkMessage msg;
  1218.     msg.addByte(0x95);
  1219.     msg.add<uint32_t>(creatureId);
  1220.     msg.addByte(creatureType);
  1221.     writeToOutputBuffer(msg);
  1222. }
  1223.  
  1224. void ProtocolGame::sendCreatureHelpers(uint32_t creatureId, uint16_t helpers)
  1225. {
  1226.     NetworkMessage msg;
  1227.     msg.addByte(0x94);
  1228.     msg.add<uint32_t>(creatureId);
  1229.     msg.add<uint16_t>(helpers);
  1230.     writeToOutputBuffer(msg);
  1231. }
  1232.  
  1233. void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t color)
  1234. {
  1235.     if (!canSee(creature)) {
  1236.         return;
  1237.     }
  1238.  
  1239.     NetworkMessage msg;
  1240.     msg.addByte(0x93);
  1241.     msg.add<uint32_t>(creature->getID());
  1242.     msg.addByte(0x01);
  1243.     msg.addByte(color);
  1244.     writeToOutputBuffer(msg);
  1245. }
  1246.  
  1247. void ProtocolGame::sendTutorial(uint8_t tutorialId)
  1248. {
  1249.     NetworkMessage msg;
  1250.     msg.addByte(0xDC);
  1251.     msg.addByte(tutorialId);
  1252.     writeToOutputBuffer(msg);
  1253. }
  1254.  
  1255. void ProtocolGame::sendAddMarker(const Position& pos, uint8_t markType, const std::string& desc)
  1256. {
  1257.     NetworkMessage msg;
  1258.     msg.addByte(0xDD);
  1259.     msg.addPosition(pos);
  1260.     msg.addByte(markType);
  1261.     msg.addString(desc);
  1262.     writeToOutputBuffer(msg);
  1263. }
  1264.  
  1265. void ProtocolGame::sendReLoginWindow(uint8_t unfairFightReduction)
  1266. {
  1267.     NetworkMessage msg;
  1268.     msg.addByte(0x28);
  1269.     msg.addByte(0x00);
  1270.     msg.addByte(unfairFightReduction);
  1271.     writeToOutputBuffer(msg);
  1272. }
  1273.  
  1274. void ProtocolGame::sendStats()
  1275. {
  1276.     NetworkMessage msg;
  1277.     AddPlayerStats(msg);
  1278.     writeToOutputBuffer(msg);
  1279. }
  1280.  
  1281. void ProtocolGame::sendBasicData()
  1282. {
  1283.     NetworkMessage msg;
  1284.     msg.addByte(0x9F);
  1285.     if (player->isPremium()) {
  1286.         msg.addByte(1);
  1287.         msg.add<uint32_t>(time(nullptr) + (player->premiumDays * 86400));
  1288.     } else {
  1289.         msg.addByte(0);
  1290.         msg.add<uint32_t>(0);
  1291.     }
  1292.     msg.addByte(player->getVocation()->getClientId());
  1293.     msg.add<uint16_t>(0x00);
  1294.     writeToOutputBuffer(msg);
  1295. }
  1296.  
  1297. void ProtocolGame::sendTextMessage(const TextMessage& message)
  1298. {
  1299.     NetworkMessage msg;
  1300.     msg.addByte(0xB4);
  1301.     msg.addByte(message.type);
  1302.     switch (message.type) {
  1303.         case MESSAGE_DAMAGE_DEALT:
  1304.         case MESSAGE_DAMAGE_RECEIVED:
  1305.         case MESSAGE_DAMAGE_OTHERS: {
  1306.             msg.addPosition(message.position);
  1307.             msg.add<uint32_t>(message.primary.value);
  1308.             msg.addByte(message.primary.color);
  1309.             msg.add<uint32_t>(message.secondary.value);
  1310.             msg.addByte(message.secondary.color);
  1311.             break;
  1312.         }
  1313.         case MESSAGE_HEALED:
  1314.         case MESSAGE_HEALED_OTHERS:
  1315.         case MESSAGE_EXPERIENCE:
  1316.         case MESSAGE_EXPERIENCE_OTHERS: {
  1317.             msg.addPosition(message.position);
  1318.             msg.add<uint32_t>(message.primary.value);
  1319.             msg.addByte(message.primary.color);
  1320.             break;
  1321.         }
  1322.         default: {
  1323.             break;
  1324.         }
  1325.     }
  1326.     msg.addString(message.text);
  1327.     writeToOutputBuffer(msg);
  1328. }
  1329.  
  1330. void ProtocolGame::sendClosePrivate(uint16_t channelId)
  1331. {
  1332.     NetworkMessage msg;
  1333.     msg.addByte(0xB3);
  1334.     msg.add<uint16_t>(channelId);
  1335.     writeToOutputBuffer(msg);
  1336. }
  1337.  
  1338. void ProtocolGame::sendCreatePrivateChannel(uint16_t channelId, const std::string& channelName)
  1339. {
  1340.     NetworkMessage msg;
  1341.     msg.addByte(0xB2);
  1342.     msg.add<uint16_t>(channelId);
  1343.     msg.addString(channelName);
  1344.     msg.add<uint16_t>(0x01);
  1345.     msg.addString(player->getName());
  1346.     msg.add<uint16_t>(0x00);
  1347.     writeToOutputBuffer(msg);
  1348. }
  1349.  
  1350. void ProtocolGame::sendChannelsDialog()
  1351. {
  1352.     NetworkMessage msg;
  1353.     msg.addByte(0xAB);
  1354.  
  1355.     const ChannelList& list = g_chat->getChannelList(*player);
  1356.     msg.addByte(list.size());
  1357.     for (ChatChannel* channel : list) {
  1358.         msg.add<uint16_t>(channel->getId());
  1359.         msg.addString(channel->getName());
  1360.     }
  1361.  
  1362.     writeToOutputBuffer(msg);
  1363. }
  1364.  
  1365. void ProtocolGame::sendChannel(uint16_t channelId, const std::string& channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers)
  1366. {
  1367.     NetworkMessage msg;
  1368.     msg.addByte(0xAC);
  1369.  
  1370.     msg.add<uint16_t>(channelId);
  1371.     msg.addString(channelName);
  1372.  
  1373.     if (channelUsers) {
  1374.         msg.add<uint16_t>(channelUsers->size());
  1375.         for (const auto& it : *channelUsers) {
  1376.             msg.addString(it.second->getName());
  1377.         }
  1378.     } else {
  1379.         msg.add<uint16_t>(0x00);
  1380.     }
  1381.  
  1382.     if (invitedUsers) {
  1383.         msg.add<uint16_t>(invitedUsers->size());
  1384.         for (const auto& it : *invitedUsers) {
  1385.             msg.addString(it.second->getName());
  1386.         }
  1387.     } else {
  1388.         msg.add<uint16_t>(0x00);
  1389.     }
  1390.     writeToOutputBuffer(msg);
  1391. }
  1392.  
  1393. void ProtocolGame::sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel)
  1394. {
  1395.     NetworkMessage msg;
  1396.     msg.addByte(0xAA);
  1397.     msg.add<uint32_t>(0x00);
  1398.     msg.addString(author);
  1399.     msg.add<uint16_t>(0x00);
  1400.     msg.addByte(type);
  1401.     msg.add<uint16_t>(channel);
  1402.     msg.addString(text);
  1403.     writeToOutputBuffer(msg);
  1404. }
  1405.  
  1406. void ProtocolGame::sendIcons(uint16_t icons)
  1407. {
  1408.     NetworkMessage msg;
  1409.     msg.addByte(0xA2);
  1410.     msg.add<uint16_t>(icons);
  1411.     writeToOutputBuffer(msg);
  1412. }
  1413.  
  1414. void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool hasParent, uint16_t firstIndex)
  1415. {
  1416.     NetworkMessage msg;
  1417.     msg.addByte(0x6E);
  1418.  
  1419.     msg.addByte(cid);
  1420.  
  1421.     if (container->getID() == ITEM_BROWSEFIELD) {
  1422.         msg.addItem(1987, 1);
  1423.         msg.addString("Browse Field");
  1424.     } else {
  1425.         msg.addItem(container);
  1426.         msg.addString(container->getName());
  1427.     }
  1428.  
  1429.     msg.addByte(container->capacity());
  1430.  
  1431.     msg.addByte(hasParent ? 0x01 : 0x00);
  1432.  
  1433.     msg.addByte(container->isUnlocked() ? 0x01 : 0x00); // Drag and drop
  1434.     msg.addByte(container->hasPagination() ? 0x01 : 0x00); // Pagination
  1435.  
  1436.     uint32_t containerSize = container->size();
  1437.     msg.add<uint16_t>(containerSize);
  1438.     msg.add<uint16_t>(firstIndex);
  1439.     if (firstIndex < containerSize) {
  1440.         uint8_t itemsToSend = std::min<uint32_t>(std::min<uint32_t>(container->capacity(), containerSize - firstIndex), std::numeric_limits<uint8_t>::max());
  1441.  
  1442.         msg.addByte(itemsToSend);
  1443.         for (auto it = container->getItemList().begin() + firstIndex, end = it + itemsToSend; it != end; ++it) {
  1444.             msg.addItem(*it);
  1445.         }
  1446.     } else {
  1447.         msg.addByte(0x00);
  1448.     }
  1449.     writeToOutputBuffer(msg);
  1450. }
  1451.  
  1452. void ProtocolGame::sendShop(Npc* npc, const ShopInfoList& itemList)
  1453. {
  1454.     NetworkMessage msg;
  1455.     msg.addByte(0x7A);
  1456.     msg.addString(npc->getName());
  1457.  
  1458.     uint16_t itemsToSend = std::min<size_t>(itemList.size(), std::numeric_limits<uint16_t>::max());
  1459.     msg.add<uint16_t>(itemsToSend);
  1460.  
  1461.     uint16_t i = 0;
  1462.     for (auto it = itemList.begin(); i < itemsToSend; ++it, ++i) {
  1463.         AddShopItem(msg, *it);
  1464.     }
  1465.  
  1466.     writeToOutputBuffer(msg);
  1467. }
  1468.  
  1469. void ProtocolGame::sendCloseShop()
  1470. {
  1471.     NetworkMessage msg;
  1472.     msg.addByte(0x7C);
  1473.     writeToOutputBuffer(msg);
  1474. }
  1475.  
  1476. void ProtocolGame::sendSaleItemList(const std::list<ShopInfo>& shop)
  1477. {
  1478.     NetworkMessage msg;
  1479.     msg.addByte(0x7B);
  1480.     msg.add<uint64_t>(player->getMoney());
  1481.  
  1482.     std::map<uint16_t, uint32_t> saleMap;
  1483.  
  1484.     if (shop.size() <= 5) {
  1485.         // For very small shops it's not worth it to create the complete map
  1486.         for (const ShopInfo& shopInfo : shop) {
  1487.             if (shopInfo.sellPrice == 0) {
  1488.                 continue;
  1489.             }
  1490.  
  1491.             int8_t subtype = -1;
  1492.  
  1493.             const ItemType& itemType = Item::items[shopInfo.itemId];
  1494.             if (itemType.hasSubType() && !itemType.stackable) {
  1495.                 subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
  1496.             }
  1497.  
  1498.             uint32_t count = player->getItemTypeCount(shopInfo.itemId, subtype);
  1499.             if (count > 0) {
  1500.                 saleMap[shopInfo.itemId] = count;
  1501.             }
  1502.         }
  1503.     } else {
  1504.         // Large shop, it's better to get a cached map of all item counts and use it
  1505.         // We need a temporary map since the finished map should only contain items
  1506.         // available in the shop
  1507.         std::map<uint32_t, uint32_t> tempSaleMap;
  1508.         player->getAllItemTypeCount(tempSaleMap);
  1509.  
  1510.         // We must still check manually for the special items that require subtype matches
  1511.         // (That is, fluids such as potions etc., actually these items are very few since
  1512.         // health potions now use their own ID)
  1513.         for (const ShopInfo& shopInfo : shop) {
  1514.             if (shopInfo.sellPrice == 0) {
  1515.                 continue;
  1516.             }
  1517.  
  1518.             int8_t subtype = -1;
  1519.  
  1520.             const ItemType& itemType = Item::items[shopInfo.itemId];
  1521.             if (itemType.hasSubType() && !itemType.stackable) {
  1522.                 subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
  1523.             }
  1524.  
  1525.             if (subtype != -1) {
  1526.                 uint32_t count;
  1527.                 if (!itemType.isFluidContainer() && !itemType.isSplash()) {
  1528.                     count = player->getItemTypeCount(shopInfo.itemId, subtype); // This shop item requires extra checks
  1529.                 } else {
  1530.                     count = subtype;
  1531.                 }
  1532.  
  1533.                 if (count > 0) {
  1534.                     saleMap[shopInfo.itemId] = count;
  1535.                 }
  1536.             } else {
  1537.                 std::map<uint32_t, uint32_t>::const_iterator findIt = tempSaleMap.find(shopInfo.itemId);
  1538.                 if (findIt != tempSaleMap.end() && findIt->second > 0) {
  1539.                     saleMap[shopInfo.itemId] = findIt->second;
  1540.                 }
  1541.             }
  1542.         }
  1543.     }
  1544.  
  1545.     uint8_t itemsToSend = std::min<size_t>(saleMap.size(), std::numeric_limits<uint8_t>::max());
  1546.     msg.addByte(itemsToSend);
  1547.  
  1548.     uint8_t i = 0;
  1549.     for (std::map<uint16_t, uint32_t>::const_iterator it = saleMap.begin(); i < itemsToSend; ++it, ++i) {
  1550.         msg.addItemId(it->first);
  1551.         msg.addByte(std::min<uint32_t>(it->second, std::numeric_limits<uint8_t>::max()));
  1552.     }
  1553.  
  1554.     writeToOutputBuffer(msg);
  1555. }
  1556.  
  1557. void ProtocolGame::sendMarketEnter(uint32_t depotId)
  1558. {
  1559.     NetworkMessage msg;
  1560.     msg.addByte(0xF6);
  1561.  
  1562.     msg.add<uint64_t>(player->getBankBalance());
  1563.     msg.addByte(std::min<uint32_t>(IOMarket::getPlayerOfferCount(player->getGUID()), std::numeric_limits<uint8_t>::max()));
  1564.  
  1565.     DepotChest* depotChest = player->getDepotChest(depotId, false);
  1566.     if (!depotChest) {
  1567.         msg.add<uint16_t>(0x00);
  1568.         writeToOutputBuffer(msg);
  1569.         return;
  1570.     }
  1571.  
  1572.     player->setInMarket(true);
  1573.  
  1574.     std::map<uint16_t, uint32_t> depotItems;
  1575.     std::forward_list<Container*> containerList { depotChest, player->getInbox() };
  1576.  
  1577.     do {
  1578.         Container* container = containerList.front();
  1579.         containerList.pop_front();
  1580.  
  1581.         for (Item* item : container->getItemList()) {
  1582.             Container* c = item->getContainer();
  1583.             if (c && !c->empty()) {
  1584.                 containerList.push_front(c);
  1585.                 continue;
  1586.             }
  1587.  
  1588.             const ItemType& itemType = Item::items[item->getID()];
  1589.             if (itemType.wareId == 0) {
  1590.                 continue;
  1591.             }
  1592.  
  1593.             if (c && (!itemType.isContainer() || c->capacity() != itemType.maxItems)) {
  1594.                 continue;
  1595.             }
  1596.  
  1597.             if (!item->hasMarketAttributes()) {
  1598.                 continue;
  1599.             }
  1600.  
  1601.             depotItems[itemType.wareId] += Item::countByType(item, -1);
  1602.         }
  1603.     } while (!containerList.empty());
  1604.  
  1605.     uint16_t itemsToSend = std::min<size_t>(depotItems.size(), std::numeric_limits<uint16_t>::max());
  1606.     msg.add<uint16_t>(itemsToSend);
  1607.  
  1608.     uint16_t i = 0;
  1609.     for (std::map<uint16_t, uint32_t>::const_iterator it = depotItems.begin(); i < itemsToSend; ++it, ++i) {
  1610.         msg.add<uint16_t>(it->first);
  1611.         msg.add<uint16_t>(std::min<uint32_t>(0xFFFF, it->second));
  1612.     }
  1613.  
  1614.     writeToOutputBuffer(msg);
  1615. }
  1616.  
  1617. void ProtocolGame::sendMarketLeave()
  1618. {
  1619.     NetworkMessage msg;
  1620.     msg.addByte(0xF7);
  1621.     writeToOutputBuffer(msg);
  1622. }
  1623.  
  1624. void ProtocolGame::sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers)
  1625. {
  1626.     NetworkMessage msg;
  1627.  
  1628.     msg.addByte(0xF9);
  1629.     msg.addItemId(itemId);
  1630.  
  1631.     msg.add<uint32_t>(buyOffers.size());
  1632.     for (const MarketOffer& offer : buyOffers) {
  1633.         msg.add<uint32_t>(offer.timestamp);
  1634.         msg.add<uint16_t>(offer.counter);
  1635.         msg.add<uint16_t>(offer.amount);
  1636.         msg.add<uint32_t>(offer.price);
  1637.         msg.addString(offer.playerName);
  1638.     }
  1639.  
  1640.     msg.add<uint32_t>(sellOffers.size());
  1641.     for (const MarketOffer& offer : sellOffers) {
  1642.         msg.add<uint32_t>(offer.timestamp);
  1643.         msg.add<uint16_t>(offer.counter);
  1644.         msg.add<uint16_t>(offer.amount);
  1645.         msg.add<uint32_t>(offer.price);
  1646.         msg.addString(offer.playerName);
  1647.     }
  1648.  
  1649.     writeToOutputBuffer(msg);
  1650. }
  1651.  
  1652. void ProtocolGame::sendMarketAcceptOffer(const MarketOfferEx& offer)
  1653. {
  1654.     NetworkMessage msg;
  1655.     msg.addByte(0xF9);
  1656.     msg.addItemId(offer.itemId);
  1657.  
  1658.     if (offer.type == MARKETACTION_BUY) {
  1659.         msg.add<uint32_t>(0x01);
  1660.         msg.add<uint32_t>(offer.timestamp);
  1661.         msg.add<uint16_t>(offer.counter);
  1662.         msg.add<uint16_t>(offer.amount);
  1663.         msg.add<uint32_t>(offer.price);
  1664.         msg.addString(offer.playerName);
  1665.         msg.add<uint32_t>(0x00);
  1666.     } else {
  1667.         msg.add<uint32_t>(0x00);
  1668.         msg.add<uint32_t>(0x01);
  1669.         msg.add<uint32_t>(offer.timestamp);
  1670.         msg.add<uint16_t>(offer.counter);
  1671.         msg.add<uint16_t>(offer.amount);
  1672.         msg.add<uint32_t>(offer.price);
  1673.         msg.addString(offer.playerName);
  1674.     }
  1675.  
  1676.     writeToOutputBuffer(msg);
  1677. }
  1678.  
  1679. void ProtocolGame::sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers)
  1680. {
  1681.     NetworkMessage msg;
  1682.     msg.addByte(0xF9);
  1683.     msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);
  1684.  
  1685.     msg.add<uint32_t>(buyOffers.size());
  1686.     for (const MarketOffer& offer : buyOffers) {
  1687.         msg.add<uint32_t>(offer.timestamp);
  1688.         msg.add<uint16_t>(offer.counter);
  1689.         msg.addItemId(offer.itemId);
  1690.         msg.add<uint16_t>(offer.amount);
  1691.         msg.add<uint32_t>(offer.price);
  1692.     }
  1693.  
  1694.     msg.add<uint32_t>(sellOffers.size());
  1695.     for (const MarketOffer& offer : sellOffers) {
  1696.         msg.add<uint32_t>(offer.timestamp);
  1697.         msg.add<uint16_t>(offer.counter);
  1698.         msg.addItemId(offer.itemId);
  1699.         msg.add<uint16_t>(offer.amount);
  1700.         msg.add<uint32_t>(offer.price);
  1701.     }
  1702.  
  1703.     writeToOutputBuffer(msg);
  1704. }
  1705.  
  1706. void ProtocolGame::sendMarketCancelOffer(const MarketOfferEx& offer)
  1707. {
  1708.     NetworkMessage msg;
  1709.     msg.addByte(0xF9);
  1710.     msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);
  1711.  
  1712.     if (offer.type == MARKETACTION_BUY) {
  1713.         msg.add<uint32_t>(0x01);
  1714.         msg.add<uint32_t>(offer.timestamp);
  1715.         msg.add<uint16_t>(offer.counter);
  1716.         msg.addItemId(offer.itemId);
  1717.         msg.add<uint16_t>(offer.amount);
  1718.         msg.add<uint32_t>(offer.price);
  1719.         msg.add<uint32_t>(0x00);
  1720.     } else {
  1721.         msg.add<uint32_t>(0x00);
  1722.         msg.add<uint32_t>(0x01);
  1723.         msg.add<uint32_t>(offer.timestamp);
  1724.         msg.add<uint16_t>(offer.counter);
  1725.         msg.addItemId(offer.itemId);
  1726.         msg.add<uint16_t>(offer.amount);
  1727.         msg.add<uint32_t>(offer.price);
  1728.     }
  1729.  
  1730.     writeToOutputBuffer(msg);
  1731. }
  1732.  
  1733. void ProtocolGame::sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers)
  1734. {
  1735.     uint32_t i = 0;
  1736.     std::map<uint32_t, uint16_t> counterMap;
  1737.     uint32_t buyOffersToSend = std::min<uint32_t>(buyOffers.size(), 810 + std::max<int32_t>(0, 810 - sellOffers.size()));
  1738.     uint32_t sellOffersToSend = std::min<uint32_t>(sellOffers.size(), 810 + std::max<int32_t>(0, 810 - buyOffers.size()));
  1739.  
  1740.     NetworkMessage msg;
  1741.     msg.addByte(0xF9);
  1742.     msg.add<uint16_t>(MARKETREQUEST_OWN_HISTORY);
  1743.  
  1744.     msg.add<uint32_t>(buyOffersToSend);
  1745.     for (auto it = buyOffers.begin(); i < buyOffersToSend; ++it, ++i) {
  1746.         msg.add<uint32_t>(it->timestamp);
  1747.         msg.add<uint16_t>(counterMap[it->timestamp]++);
  1748.         msg.addItemId(it->itemId);
  1749.         msg.add<uint16_t>(it->amount);
  1750.         msg.add<uint32_t>(it->price);
  1751.         msg.addByte(it->state);
  1752.     }
  1753.  
  1754.     counterMap.clear();
  1755.     i = 0;
  1756.  
  1757.     msg.add<uint32_t>(sellOffersToSend);
  1758.     for (auto it = sellOffers.begin(); i < sellOffersToSend; ++it, ++i) {
  1759.         msg.add<uint32_t>(it->timestamp);
  1760.         msg.add<uint16_t>(counterMap[it->timestamp]++);
  1761.         msg.addItemId(it->itemId);
  1762.         msg.add<uint16_t>(it->amount);
  1763.         msg.add<uint32_t>(it->price);
  1764.         msg.addByte(it->state);
  1765.     }
  1766.  
  1767.     writeToOutputBuffer(msg);
  1768. }
  1769.  
  1770. void ProtocolGame::sendMarketDetail(uint16_t itemId)
  1771. {
  1772.     NetworkMessage msg;
  1773.     msg.addByte(0xF8);
  1774.     msg.addItemId(itemId);
  1775.  
  1776.     const ItemType& it = Item::items[itemId];
  1777.     if (it.armor != 0) {
  1778.         msg.addString(std::to_string(it.armor));
  1779.     } else {
  1780.         msg.add<uint16_t>(0x00);
  1781.     }
  1782.  
  1783.     if (it.attack != 0) {
  1784.         // TODO: chance to hit, range
  1785.         // example:
  1786.         // "attack +x, chance to hit +y%, z fields"
  1787.         if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) {
  1788.             std::ostringstream ss;
  1789.             ss << it.attack << " physical +" << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType);
  1790.             msg.addString(ss.str());
  1791.         } else {
  1792.             msg.addString(std::to_string(it.attack));
  1793.         }
  1794.     } else {
  1795.         msg.add<uint16_t>(0x00);
  1796.     }
  1797.  
  1798.     if (it.isContainer()) {
  1799.         msg.addString(std::to_string(it.maxItems));
  1800.     } else {
  1801.         msg.add<uint16_t>(0x00);
  1802.     }
  1803.  
  1804.     if (it.defense != 0) {
  1805.         if (it.extraDefense != 0) {
  1806.             std::ostringstream ss;
  1807.             ss << it.defense << ' ' << std::showpos << it.extraDefense << std::noshowpos;
  1808.             msg.addString(ss.str());
  1809.         } else {
  1810.             msg.addString(std::to_string(it.defense));
  1811.         }
  1812.     } else {
  1813.         msg.add<uint16_t>(0x00);
  1814.     }
  1815.  
  1816.     if (!it.description.empty()) {
  1817.         const std::string& descr = it.description;
  1818.         if (descr.back() == '.') {
  1819.             msg.addString(std::string(descr, 0, descr.length() - 1));
  1820.         } else {
  1821.             msg.addString(descr);
  1822.         }
  1823.     } else {
  1824.         msg.add<uint16_t>(0x00);
  1825.     }
  1826.  
  1827.     if (it.decayTime != 0) {
  1828.         std::ostringstream ss;
  1829.         ss << it.decayTime << " seconds";
  1830.         msg.addString(ss.str());
  1831.     } else {
  1832.         msg.add<uint16_t>(0x00);
  1833.     }
  1834.  
  1835.     if (it.abilities) {
  1836.         std::ostringstream ss;
  1837.         bool separator = false;
  1838.  
  1839.         for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  1840.             if (it.abilities->absorbPercent[i] == 0) {
  1841.                 continue;
  1842.             }
  1843.  
  1844.             if (separator) {
  1845.                 ss << ", ";
  1846.             } else {
  1847.                 separator = true;
  1848.             }
  1849.  
  1850.             ss << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%';
  1851.         }
  1852.  
  1853.         msg.addString(ss.str());
  1854.     } else {
  1855.         msg.add<uint16_t>(0x00);
  1856.     }
  1857.  
  1858.     if (it.minReqLevel != 0) {
  1859.         msg.addString(std::to_string(it.minReqLevel));
  1860.     } else {
  1861.         msg.add<uint16_t>(0x00);
  1862.     }
  1863.  
  1864.     if (it.minReqMagicLevel != 0) {
  1865.         msg.addString(std::to_string(it.minReqMagicLevel));
  1866.     } else {
  1867.         msg.add<uint16_t>(0x00);
  1868.     }
  1869.  
  1870.     msg.addString(it.vocationString);
  1871.  
  1872.     msg.addString(it.runeSpellName);
  1873.  
  1874.     if (it.abilities) {
  1875.         std::ostringstream ss;
  1876.         bool separator = false;
  1877.  
  1878.         for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) {
  1879.             if (!it.abilities->skills[i]) {
  1880.                 continue;
  1881.             }
  1882.  
  1883.             if (separator) {
  1884.                 ss << ", ";
  1885.             } else {
  1886.                 separator = true;
  1887.             }
  1888.  
  1889.             ss << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos;
  1890.         }
  1891.  
  1892.         if (it.abilities->stats[STAT_MAGICPOINTS] != 0) {
  1893.             if (separator) {
  1894.                 ss << ", ";
  1895.             } else {
  1896.                 separator = true;
  1897.             }
  1898.  
  1899.             ss << "magic level " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos;
  1900.         }
  1901.  
  1902.         if (it.abilities->speed != 0) {
  1903.             if (separator) {
  1904.                 ss << ", ";
  1905.             }
  1906.  
  1907.             ss << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos;
  1908.         }
  1909.  
  1910.         msg.addString(ss.str());
  1911.     } else {
  1912.         msg.add<uint16_t>(0x00);
  1913.     }
  1914.  
  1915.     if (it.charges != 0) {
  1916.         msg.addString(std::to_string(it.charges));
  1917.     } else {
  1918.         msg.add<uint16_t>(0x00);
  1919.     }
  1920.  
  1921.     std::string weaponName = getWeaponName(it.weaponType);
  1922.  
  1923.     if (it.slotPosition & SLOTP_TWO_HAND) {
  1924.         if (!weaponName.empty()) {
  1925.             weaponName += ", two-handed";
  1926.         } else {
  1927.             weaponName = "two-handed";
  1928.         }
  1929.     }
  1930.  
  1931.     msg.addString(weaponName);
  1932.  
  1933.     if (it.weight != 0) {
  1934.         std::ostringstream ss;
  1935.         if (it.weight < 10) {
  1936.             ss << "0.0" << it.weight;
  1937.         } else if (it.weight < 100) {
  1938.             ss << "0." << it.weight;
  1939.         } else {
  1940.             std::string weightString = std::to_string(it.weight);
  1941.             weightString.insert(weightString.end() - 2, '.');
  1942.             ss << weightString;
  1943.         }
  1944.         ss << " oz";
  1945.         msg.addString(ss.str());
  1946.     } else {
  1947.         msg.add<uint16_t>(0x00);
  1948.     }
  1949.  
  1950.     MarketStatistics* statistics = IOMarket::getInstance()->getPurchaseStatistics(itemId);
  1951.     if (statistics) {
  1952.         msg.addByte(0x01);
  1953.         msg.add<uint32_t>(statistics->numTransactions);
  1954.         msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));
  1955.         msg.add<uint32_t>(statistics->highestPrice);
  1956.         msg.add<uint32_t>(statistics->lowestPrice);
  1957.     } else {
  1958.         msg.addByte(0x00);
  1959.     }
  1960.  
  1961.     statistics = IOMarket::getInstance()->getSaleStatistics(itemId);
  1962.     if (statistics) {
  1963.         msg.addByte(0x01);
  1964.         msg.add<uint32_t>(statistics->numTransactions);
  1965.         msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));
  1966.         msg.add<uint32_t>(statistics->highestPrice);
  1967.         msg.add<uint32_t>(statistics->lowestPrice);
  1968.     } else {
  1969.         msg.addByte(0x00);
  1970.     }
  1971.  
  1972.     writeToOutputBuffer(msg);
  1973. }
  1974.  
  1975. void ProtocolGame::sendQuestLog()
  1976. {
  1977.     NetworkMessage msg;
  1978.     msg.addByte(0xF0);
  1979.     msg.add<uint16_t>(g_game.quests.getQuestsCount(player));
  1980.  
  1981.     for (const Quest& quest : g_game.quests.getQuests()) {
  1982.         if (quest.isStarted(player)) {
  1983.             msg.add<uint16_t>(quest.getID());
  1984.             msg.addString(quest.getName());
  1985.             msg.addByte(quest.isCompleted(player));
  1986.         }
  1987.     }
  1988.  
  1989.     writeToOutputBuffer(msg);
  1990. }
  1991.  
  1992. void ProtocolGame::sendQuestLine(const Quest* quest)
  1993. {
  1994.     NetworkMessage msg;
  1995.     msg.addByte(0xF1);
  1996.     msg.add<uint16_t>(quest->getID());
  1997.     msg.addByte(quest->getMissionsCount(player));
  1998.  
  1999.     for (const Mission& mission : quest->getMissions()) {
  2000.         if (mission.isStarted(player)) {
  2001.             msg.addString(mission.getName(player));
  2002.             msg.addString(mission.getDescription(player));
  2003.         }
  2004.     }
  2005.  
  2006.     writeToOutputBuffer(msg);
  2007. }
  2008.  
  2009. void ProtocolGame::sendTradeItemRequest(const std::string& traderName, const Item* item, bool ack)
  2010. {
  2011.     NetworkMessage msg;
  2012.  
  2013.     if (ack) {
  2014.         msg.addByte(0x7D);
  2015.     } else {
  2016.         msg.addByte(0x7E);
  2017.     }
  2018.  
  2019.     msg.addString(traderName);
  2020.  
  2021.     if (const Container* tradeContainer = item->getContainer()) {
  2022.         std::list<const Container*> listContainer {tradeContainer};
  2023.         std::list<const Item*> itemList {tradeContainer};
  2024.         while (!listContainer.empty()) {
  2025.             const Container* container = listContainer.front();
  2026.             listContainer.pop_front();
  2027.  
  2028.             for (Item* containerItem : container->getItemList()) {
  2029.                 Container* tmpContainer = containerItem->getContainer();
  2030.                 if (tmpContainer) {
  2031.                     listContainer.push_back(tmpContainer);
  2032.                 }
  2033.                 itemList.push_back(containerItem);
  2034.             }
  2035.         }
  2036.  
  2037.         msg.addByte(itemList.size());
  2038.         for (const Item* listItem : itemList) {
  2039.             msg.addItem(listItem);
  2040.         }
  2041.     } else {
  2042.         msg.addByte(0x01);
  2043.         msg.addItem(item);
  2044.     }
  2045.     writeToOutputBuffer(msg);
  2046. }
  2047.  
  2048. void ProtocolGame::sendCloseTrade()
  2049. {
  2050.     NetworkMessage msg;
  2051.     msg.addByte(0x7F);
  2052.     writeToOutputBuffer(msg);
  2053. }
  2054.  
  2055. void ProtocolGame::sendCloseContainer(uint8_t cid)
  2056. {
  2057.     NetworkMessage msg;
  2058.     msg.addByte(0x6F);
  2059.     msg.addByte(cid);
  2060.     writeToOutputBuffer(msg);
  2061. }
  2062.  
  2063. void ProtocolGame::sendCreatureTurn(const Creature* creature, uint32_t stackPos)
  2064. {
  2065.     if (!canSee(creature)) {
  2066.         return;
  2067.     }
  2068.  
  2069.     NetworkMessage msg;
  2070.     msg.addByte(0x6B);
  2071.     msg.addPosition(creature->getPosition());
  2072.     msg.addByte(stackPos);
  2073.     msg.add<uint16_t>(0x63);
  2074.     msg.add<uint32_t>(creature->getID());
  2075.     msg.addByte(creature->getDirection());
  2076.     msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01);
  2077.     writeToOutputBuffer(msg);
  2078. }
  2079.  
  2080. void ProtocolGame::sendCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, const Position* pos/* = nullptr*/)
  2081. {
  2082.     NetworkMessage msg;
  2083.     msg.addByte(0xAA);
  2084.  
  2085.     static uint32_t statementId = 0;
  2086.     msg.add<uint32_t>(++statementId);
  2087.  
  2088.     msg.addString(creature->getName());
  2089.  
  2090.     //Add level only for players
  2091.     if (const Player* speaker = creature->getPlayer()) {
  2092.         msg.add<uint16_t>(speaker->getLevel());
  2093.     } else {
  2094.         msg.add<uint16_t>(0x00);
  2095.     }
  2096.  
  2097.     msg.addByte(type);
  2098.     if (pos) {
  2099.         msg.addPosition(*pos);
  2100.     } else {
  2101.         msg.addPosition(creature->getPosition());
  2102.     }
  2103.  
  2104.     msg.addString(text);
  2105.     writeToOutputBuffer(msg);
  2106. }
  2107.  
  2108. void ProtocolGame::sendToChannel(const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId)
  2109. {
  2110.     NetworkMessage msg;
  2111.     msg.addByte(0xAA);
  2112.  
  2113.     static uint32_t statementId = 0;
  2114.     msg.add<uint32_t>(++statementId);
  2115.     if (!creature) {
  2116.         msg.add<uint32_t>(0x00);
  2117.     } else if (type == TALKTYPE_CHANNEL_R2) {
  2118.         msg.add<uint32_t>(0x00);
  2119.         type = TALKTYPE_CHANNEL_R1;
  2120.     } else {
  2121.         msg.addString(creature->getName());
  2122.         //Add level only for players
  2123.         if (const Player* speaker = creature->getPlayer()) {
  2124.             msg.add<uint16_t>(speaker->getLevel());
  2125.         } else {
  2126.             msg.add<uint16_t>(0x00);
  2127.         }
  2128.     }
  2129.  
  2130.     msg.addByte(type);
  2131.     msg.add<uint16_t>(channelId);
  2132.     msg.addString(text);
  2133.     writeToOutputBuffer(msg);
  2134. }
  2135.  
  2136. void ProtocolGame::sendPrivateMessage(const Player* speaker, SpeakClasses type, const std::string& text)
  2137. {
  2138.     NetworkMessage msg;
  2139.     msg.addByte(0xAA);
  2140.     static uint32_t statementId = 0;
  2141.     msg.add<uint32_t>(++statementId);
  2142.     if (speaker) {
  2143.         msg.addString(speaker->getName());
  2144.         msg.add<uint16_t>(speaker->getLevel());
  2145.     } else {
  2146.         msg.add<uint32_t>(0x00);
  2147.     }
  2148.     msg.addByte(type);
  2149.     msg.addString(text);
  2150.     writeToOutputBuffer(msg);
  2151. }
  2152.  
  2153. void ProtocolGame::sendCancelTarget()
  2154. {
  2155.     NetworkMessage msg;
  2156.     msg.addByte(0xA3);
  2157.     msg.add<uint32_t>(0x00);
  2158.     writeToOutputBuffer(msg);
  2159. }
  2160.  
  2161. void ProtocolGame::sendChangeSpeed(const Creature* creature, uint32_t speed)
  2162. {
  2163.     NetworkMessage msg;
  2164.     msg.addByte(0x8F);
  2165.     msg.add<uint32_t>(creature->getID());
  2166.     msg.add<uint16_t>(creature->getBaseSpeed() / 2);
  2167.     msg.add<uint16_t>(speed / 2);
  2168.     writeToOutputBuffer(msg);
  2169. }
  2170.  
  2171. void ProtocolGame::sendCancelWalk()
  2172. {
  2173.     NetworkMessage msg;
  2174.     msg.addByte(0xB5);
  2175.     msg.addByte(player->getDirection());
  2176.     writeToOutputBuffer(msg);
  2177. }
  2178.  
  2179. void ProtocolGame::sendSkills()
  2180. {
  2181.     NetworkMessage msg;
  2182.     AddPlayerSkills(msg);
  2183.     writeToOutputBuffer(msg);
  2184. }
  2185.  
  2186. void ProtocolGame::sendPing()
  2187. {
  2188.     NetworkMessage msg;
  2189.     msg.addByte(0x1D);
  2190.     writeToOutputBuffer(msg);
  2191. }
  2192.  
  2193. void ProtocolGame::sendPingBack()
  2194. {
  2195.     NetworkMessage msg;
  2196.     msg.addByte(0x1E);
  2197.     writeToOutputBuffer(msg);
  2198. }
  2199.  
  2200. void ProtocolGame::sendDistanceShoot(const Position& from, const Position& to, uint8_t type)
  2201. {
  2202.     NetworkMessage msg;
  2203.     msg.addByte(0x85);
  2204.     msg.addPosition(from);
  2205.     msg.addPosition(to);
  2206.     msg.addByte(type);
  2207.     writeToOutputBuffer(msg);
  2208. }
  2209.  
  2210. void ProtocolGame::sendMagicEffect(const Position& pos, uint8_t type)
  2211. {
  2212.     if (!canSee(pos)) {
  2213.         return;
  2214.     }
  2215.  
  2216.     NetworkMessage msg;
  2217.     msg.addByte(0x83);
  2218.     msg.addPosition(pos);
  2219.     msg.addByte(type);
  2220.     writeToOutputBuffer(msg);
  2221. }
  2222.  
  2223. void ProtocolGame::sendCreatureHealth(const Creature* creature)
  2224. {
  2225.     NetworkMessage msg;
  2226.     msg.addByte(0x8C);
  2227.     msg.add<uint32_t>(creature->getID());
  2228.  
  2229.     if (creature->isHealthHidden()) {
  2230.         msg.addByte(0x00);
  2231.     } else {
  2232.         msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100));
  2233.     }
  2234.     writeToOutputBuffer(msg);
  2235. }
  2236.  
  2237. void ProtocolGame::sendFYIBox(const std::string& message)
  2238. {
  2239.     NetworkMessage msg;
  2240.     msg.addByte(0x15);
  2241.     msg.addString(message);
  2242.     writeToOutputBuffer(msg);
  2243. }
  2244.  
  2245. //tile
  2246. void ProtocolGame::sendMapDescription(const Position& pos)
  2247. {
  2248.     NetworkMessage msg;
  2249.     msg.addByte(0x64);
  2250.     msg.addPosition(player->getPosition());
  2251.     GetMapDescription(pos.x - 8, pos.y - 6, pos.z, 18, 14, msg);
  2252.     writeToOutputBuffer(msg);
  2253. }
  2254.  
  2255. void ProtocolGame::sendAddTileItem(const Position& pos, uint32_t stackpos, const Item* item)
  2256. {
  2257.     if (!canSee(pos)) {
  2258.         return;
  2259.     }
  2260.  
  2261.     NetworkMessage msg;
  2262.     msg.addByte(0x6A);
  2263.     msg.addPosition(pos);
  2264.     msg.addByte(stackpos);
  2265.     msg.addItem(item);
  2266.     writeToOutputBuffer(msg);
  2267. }
  2268.  
  2269. void ProtocolGame::sendUpdateTileItem(const Position& pos, uint32_t stackpos, const Item* item)
  2270. {
  2271.     if (!canSee(pos)) {
  2272.         return;
  2273.     }
  2274.  
  2275.     NetworkMessage msg;
  2276.     msg.addByte(0x6B);
  2277.     msg.addPosition(pos);
  2278.     msg.addByte(stackpos);
  2279.     msg.addItem(item);
  2280.     writeToOutputBuffer(msg);
  2281. }
  2282.  
  2283. void ProtocolGame::sendRemoveTileThing(const Position& pos, uint32_t stackpos)
  2284. {
  2285.     if (!canSee(pos)) {
  2286.         return;
  2287.     }
  2288.  
  2289.     NetworkMessage msg;
  2290.     RemoveTileThing(msg, pos, stackpos);
  2291.     writeToOutputBuffer(msg);
  2292. }
  2293.  
  2294. void ProtocolGame::sendUpdateTile(const Tile* tile, const Position& pos)
  2295. {
  2296.     if (!canSee(pos)) {
  2297.         return;
  2298.     }
  2299.  
  2300.     NetworkMessage msg;
  2301.     msg.addByte(0x69);
  2302.     msg.addPosition(pos);
  2303.  
  2304.     if (tile) {
  2305.         GetTileDescription(tile, msg);
  2306.         msg.addByte(0x00);
  2307.         msg.addByte(0xFF);
  2308.     } else {
  2309.         msg.addByte(0x01);
  2310.         msg.addByte(0xFF);
  2311.     }
  2312.  
  2313.     writeToOutputBuffer(msg);
  2314. }
  2315.  
  2316. void ProtocolGame::sendPendingStateEntered()
  2317. {
  2318.     NetworkMessage msg;
  2319.     msg.addByte(0x0A);
  2320.     writeToOutputBuffer(msg);
  2321. }
  2322.  
  2323. void ProtocolGame::sendEnterWorld()
  2324. {
  2325.     NetworkMessage msg;
  2326.     msg.addByte(0x0F);
  2327.     writeToOutputBuffer(msg);
  2328. }
  2329.  
  2330. void ProtocolGame::sendFightModes()
  2331. {
  2332.     NetworkMessage msg;
  2333.     msg.addByte(0xA7);
  2334.     msg.addByte(player->fightMode);
  2335.     msg.addByte(player->chaseMode);
  2336.     msg.addByte(player->secureMode);
  2337.     msg.addByte(PVP_MODE_DOVE);
  2338.     writeToOutputBuffer(msg);
  2339. }
  2340.  
  2341. void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos, int32_t stackpos, bool isLogin)
  2342. {
  2343.     if (!canSee(pos)) {
  2344.         return;
  2345.     }
  2346.  
  2347.     if (creature != player) {
  2348.         if (stackpos != -1) {
  2349.             NetworkMessage msg;
  2350.             msg.addByte(0x6A);
  2351.             msg.addPosition(pos);
  2352.             msg.addByte(stackpos);
  2353.  
  2354.             bool known;
  2355.             uint32_t removedKnown;
  2356.             checkCreatureAsKnown(creature->getID(), known, removedKnown);
  2357.             AddCreature(msg, creature, known, removedKnown);
  2358.             writeToOutputBuffer(msg);
  2359.         }
  2360.  
  2361.         if (isLogin) {
  2362.             sendMagicEffect(pos, CONST_ME_TELEPORT);
  2363.         }
  2364.         return;
  2365.     }
  2366.  
  2367.     NetworkMessage msg;
  2368.     msg.addByte(0x17);
  2369.  
  2370.     msg.add<uint32_t>(player->getID());
  2371.     msg.add<uint16_t>(0x32); // beat duration (50)
  2372.  
  2373.     msg.addDouble(Creature::speedA, 3);
  2374.     msg.addDouble(Creature::speedB, 3);
  2375.     msg.addDouble(Creature::speedC, 3);
  2376.  
  2377.     // can report bugs?
  2378.     if (player->getAccountType() >= ACCOUNT_TYPE_TUTOR) {
  2379.         msg.addByte(0x01);
  2380.     } else {
  2381.         msg.addByte(0x00);
  2382.     }
  2383.  
  2384.     msg.addByte(0x00); // can change pvp framing option
  2385.     msg.addByte(0x00); // expert mode button enabled
  2386.  
  2387.     msg.add<uint16_t>(0x00); // URL (string) to ingame store images
  2388.     msg.add<uint16_t>(25); // premium coin package size
  2389.  
  2390.     writeToOutputBuffer(msg);
  2391.  
  2392.     sendPendingStateEntered();
  2393.     sendEnterWorld();
  2394.     sendMapDescription(pos);
  2395.  
  2396.     if (isLogin) {
  2397.         sendMagicEffect(pos, CONST_ME_TELEPORT);
  2398.     }
  2399.  
  2400.     sendInventoryItem(CONST_SLOT_HEAD, player->getInventoryItem(CONST_SLOT_HEAD));
  2401.     sendInventoryItem(CONST_SLOT_NECKLACE, player->getInventoryItem(CONST_SLOT_NECKLACE));
  2402.     sendInventoryItem(CONST_SLOT_BACKPACK, player->getInventoryItem(CONST_SLOT_BACKPACK));
  2403.     sendInventoryItem(CONST_SLOT_ARMOR, player->getInventoryItem(CONST_SLOT_ARMOR));
  2404.     sendInventoryItem(CONST_SLOT_RIGHT, player->getInventoryItem(CONST_SLOT_RIGHT));
  2405.     sendInventoryItem(CONST_SLOT_LEFT, player->getInventoryItem(CONST_SLOT_LEFT));
  2406.     sendInventoryItem(CONST_SLOT_LEGS, player->getInventoryItem(CONST_SLOT_LEGS));
  2407.     sendInventoryItem(CONST_SLOT_FEET, player->getInventoryItem(CONST_SLOT_FEET));
  2408.     sendInventoryItem(CONST_SLOT_RING, player->getInventoryItem(CONST_SLOT_RING));
  2409.     sendInventoryItem(CONST_SLOT_AMMO, player->getInventoryItem(CONST_SLOT_AMMO));
  2410.  
  2411.     sendStats();
  2412.     sendSkills();
  2413.  
  2414.     //gameworld light-settings
  2415.     LightInfo lightInfo;
  2416.     g_game.getWorldLightInfo(lightInfo);
  2417.     sendWorldLight(lightInfo);
  2418.  
  2419.     //player light level
  2420.     sendCreatureLight(creature);
  2421.  
  2422.     const std::forward_list<VIPEntry>& vipEntries = IOLoginData::getVIPEntries(player->getAccount());
  2423.  
  2424.     if (player->isAccessPlayer()) {
  2425.         for (const VIPEntry& entry : vipEntries) {
  2426.             VipStatus_t vipStatus;
  2427.  
  2428.             Player* vipPlayer = g_game.getPlayerByGUID(entry.guid);
  2429.             if (!vipPlayer) {
  2430.                 vipStatus = VIPSTATUS_OFFLINE;
  2431.             } else {
  2432.                 vipStatus = VIPSTATUS_ONLINE;
  2433.             }
  2434.  
  2435.             sendVIP(entry.guid, entry.name, entry.description, entry.icon, entry.notify, vipStatus);
  2436.         }
  2437.     } else {
  2438.         for (const VIPEntry& entry : vipEntries) {
  2439.             VipStatus_t vipStatus;
  2440.  
  2441.             Player* vipPlayer = g_game.getPlayerByGUID(entry.guid);
  2442.             if (!vipPlayer || vipPlayer->isInGhostMode()) {
  2443.                 vipStatus = VIPSTATUS_OFFLINE;
  2444.             } else {
  2445.                 vipStatus = VIPSTATUS_ONLINE;
  2446.             }
  2447.  
  2448.             sendVIP(entry.guid, entry.name, entry.description, entry.icon, entry.notify, vipStatus);
  2449.         }
  2450.     }
  2451.  
  2452.     sendBasicData();
  2453.     player->sendIcons();
  2454. }
  2455.  
  2456. void ProtocolGame::sendMoveCreature(const Creature* creature, const Position& newPos, int32_t newStackPos, const Position& oldPos, int32_t oldStackPos, bool teleport)
  2457. {
  2458.     if (creature == player) {
  2459.         if (oldStackPos >= 10) {
  2460.             sendMapDescription(newPos);
  2461.         } else if (teleport) {
  2462.             NetworkMessage msg;
  2463.             RemoveTileThing(msg, oldPos, oldStackPos);
  2464.             writeToOutputBuffer(msg);
  2465.             sendMapDescription(newPos);
  2466.         } else {
  2467.             NetworkMessage msg;
  2468.             if (oldPos.z == 7 && newPos.z >= 8) {
  2469.                 RemoveTileThing(msg, oldPos, oldStackPos);
  2470.             } else {
  2471.                 msg.addByte(0x6D);
  2472.                 msg.addPosition(oldPos);
  2473.                 msg.addByte(oldStackPos);
  2474.                 msg.addPosition(newPos);
  2475.             }
  2476.  
  2477.             if (newPos.z > oldPos.z) {
  2478.                 MoveDownCreature(msg, creature, newPos, oldPos);
  2479.             } else if (newPos.z < oldPos.z) {
  2480.                 MoveUpCreature(msg, creature, newPos, oldPos);
  2481.             }
  2482.  
  2483.             if (oldPos.y > newPos.y) { // north, for old x
  2484.                 msg.addByte(0x65);
  2485.                 GetMapDescription(oldPos.x - 8, newPos.y - 6, newPos.z, 18, 1, msg);
  2486.             } else if (oldPos.y < newPos.y) { // south, for old x
  2487.                 msg.addByte(0x67);
  2488.                 GetMapDescription(oldPos.x - 8, newPos.y + 7, newPos.z, 18, 1, msg);
  2489.             }
  2490.  
  2491.             if (oldPos.x < newPos.x) { // east, [with new y]
  2492.                 msg.addByte(0x66);
  2493.                 GetMapDescription(newPos.x + 9, newPos.y - 6, newPos.z, 1, 14, msg);
  2494.             } else if (oldPos.x > newPos.x) { // west, [with new y]
  2495.                 msg.addByte(0x68);
  2496.                 GetMapDescription(newPos.x - 8, newPos.y - 6, newPos.z, 1, 14, msg);
  2497.             }
  2498.             writeToOutputBuffer(msg);
  2499.         }
  2500.     } else if (canSee(oldPos) && canSee(creature->getPosition())) {
  2501.         if (teleport || (oldPos.z == 7 && newPos.z >= 8) || oldStackPos >= 10) {
  2502.             sendRemoveTileThing(oldPos, oldStackPos);
  2503.             sendAddCreature(creature, newPos, newStackPos, false);
  2504.         } else {
  2505.             NetworkMessage msg;
  2506.             msg.addByte(0x6D);
  2507.             msg.addPosition(oldPos);
  2508.             msg.addByte(oldStackPos);
  2509.             msg.addPosition(creature->getPosition());
  2510.             writeToOutputBuffer(msg);
  2511.         }
  2512.     } else if (canSee(oldPos)) {
  2513.         sendRemoveTileThing(oldPos, oldStackPos);
  2514.     } else if (canSee(creature->getPosition())) {
  2515.         sendAddCreature(creature, newPos, newStackPos, false);
  2516.     }
  2517. }
  2518.  
  2519. void ProtocolGame::sendInventoryItem(slots_t slot, const Item* item)
  2520. {
  2521.     NetworkMessage msg;
  2522.     if (item) {
  2523.         msg.addByte(0x78);
  2524.         msg.addByte(slot);
  2525.         msg.addItem(item);
  2526.     } else {
  2527.         msg.addByte(0x79);
  2528.         msg.addByte(slot);
  2529.     }
  2530.     writeToOutputBuffer(msg);
  2531. }
  2532.  
  2533. void ProtocolGame::sendAddContainerItem(uint8_t cid, uint16_t slot, const Item* item)
  2534. {
  2535.     NetworkMessage msg;
  2536.     msg.addByte(0x70);
  2537.     msg.addByte(cid);
  2538.     msg.add<uint16_t>(slot);
  2539.     msg.addItem(item);
  2540.     writeToOutputBuffer(msg);
  2541. }
  2542.  
  2543. void ProtocolGame::sendUpdateContainerItem(uint8_t cid, uint16_t slot, const Item* item)
  2544. {
  2545.     NetworkMessage msg;
  2546.     msg.addByte(0x71);
  2547.     msg.addByte(cid);
  2548.     msg.add<uint16_t>(slot);
  2549.     msg.addItem(item);
  2550.     writeToOutputBuffer(msg);
  2551. }
  2552.  
  2553. void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot, const Item* lastItem)
  2554. {
  2555.     NetworkMessage msg;
  2556.     msg.addByte(0x72);
  2557.     msg.addByte(cid);
  2558.     msg.add<uint16_t>(slot);
  2559.     if (lastItem) {
  2560.         msg.addItem(lastItem);
  2561.     } else {
  2562.         msg.add<uint16_t>(0x00);
  2563.     }
  2564.     writeToOutputBuffer(msg);
  2565. }
  2566.  
  2567. void ProtocolGame::sendTextWindow(uint32_t windowTextId, Item* item, uint16_t maxlen, bool canWrite)
  2568. {
  2569.     NetworkMessage msg;
  2570.     msg.addByte(0x96);
  2571.     msg.add<uint32_t>(windowTextId);
  2572.     msg.addItem(item);
  2573.  
  2574.     if (canWrite) {
  2575.         msg.add<uint16_t>(maxlen);
  2576.         msg.addString(item->getText());
  2577.     } else {
  2578.         const std::string& text = item->getText();
  2579.         msg.add<uint16_t>(text.size());
  2580.         msg.addString(text);
  2581.     }
  2582.  
  2583.     const std::string& writer = item->getWriter();
  2584.     if (!writer.empty()) {
  2585.         msg.addString(writer);
  2586.     } else {
  2587.         msg.add<uint16_t>(0x00);
  2588.     }
  2589.  
  2590.     time_t writtenDate = item->getDate();
  2591.     if (writtenDate != 0) {
  2592.         msg.addString(formatDateShort(writtenDate));
  2593.     } else {
  2594.         msg.add<uint16_t>(0x00);
  2595.     }
  2596.  
  2597.     writeToOutputBuffer(msg);
  2598. }
  2599.  
  2600. void ProtocolGame::sendTextWindow(uint32_t windowTextId, uint32_t itemId, const std::string& text)
  2601. {
  2602.     NetworkMessage msg;
  2603.     msg.addByte(0x96);
  2604.     msg.add<uint32_t>(windowTextId);
  2605.     msg.addItem(itemId, 1);
  2606.     msg.add<uint16_t>(text.size());
  2607.     msg.addString(text);
  2608.     msg.add<uint16_t>(0x00);
  2609.     msg.add<uint16_t>(0x00);
  2610.     writeToOutputBuffer(msg);
  2611. }
  2612.  
  2613. void ProtocolGame::sendHouseWindow(uint32_t windowTextId, const std::string& text)
  2614. {
  2615.     NetworkMessage msg;
  2616.     msg.addByte(0x97);
  2617.     msg.addByte(0x00);
  2618.     msg.add<uint32_t>(windowTextId);
  2619.     msg.addString(text);
  2620.     writeToOutputBuffer(msg);
  2621. }
  2622.  
  2623. void ProtocolGame::sendOutfitWindow()
  2624. {
  2625.     NetworkMessage msg;
  2626.     msg.addByte(0xC8);
  2627.  
  2628.     Outfit_t currentOutfit = player->getDefaultOutfit();
  2629.     Mount* currentMount = g_game.mounts.getMountByID(player->getCurrentMount());
  2630.     if (currentMount) {
  2631.         currentOutfit.lookMount = currentMount->clientId;
  2632.     }
  2633.  
  2634.     AddOutfit(msg, currentOutfit);
  2635.  
  2636.     std::vector<ProtocolOutfit> protocolOutfits;
  2637.     if (player->isAccessPlayer()) {
  2638.         static const std::string gamemasterOutfitName = "Gamemaster";
  2639.         protocolOutfits.emplace_back(gamemasterOutfitName, 75, 0);
  2640.     }
  2641.  
  2642.     const auto& outfits = Outfits::getInstance()->getOutfits(player->getSex());
  2643.     protocolOutfits.reserve(outfits.size());
  2644.     for (const Outfit& outfit : outfits) {
  2645.         uint8_t addons;
  2646.         if (!player->getOutfitAddons(outfit, addons)) {
  2647.             continue;
  2648.         }
  2649.  
  2650.         protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons);
  2651.         if (protocolOutfits.size() == 100) { // Game client doesn't allow more than 100 outfits
  2652.             break;
  2653.         }
  2654.     }
  2655.  
  2656.     msg.addByte(protocolOutfits.size());
  2657.     for (const ProtocolOutfit& outfit : protocolOutfits) {
  2658.         msg.add<uint16_t>(outfit.lookType);
  2659.         msg.addString(outfit.name);
  2660.         msg.addByte(outfit.addons);
  2661.     }
  2662.  
  2663.     std::vector<const Mount*> mounts;
  2664.     for (const Mount& mount : g_game.mounts.getMounts()) {
  2665.         if (player->hasMount(&mount)) {
  2666.             mounts.push_back(&mount);
  2667.         }
  2668.     }
  2669.  
  2670.     msg.addByte(mounts.size());
  2671.     for (const Mount* mount : mounts) {
  2672.         msg.add<uint16_t>(mount->clientId);
  2673.         msg.addString(mount->name);
  2674.     }
  2675.  
  2676.     writeToOutputBuffer(msg);
  2677. }
  2678.  
  2679. void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus)
  2680. {
  2681.     NetworkMessage msg;
  2682.     msg.addByte(0xD3);
  2683.     msg.add<uint32_t>(guid);
  2684.     msg.addByte(newStatus);
  2685.     writeToOutputBuffer(msg);
  2686. }
  2687.  
  2688. void ProtocolGame::sendVIP(uint32_t guid, const std::string& name, const std::string& description, uint32_t icon, bool notify, VipStatus_t status)
  2689. {
  2690.     NetworkMessage msg;
  2691.     msg.addByte(0xD2);
  2692.     msg.add<uint32_t>(guid);
  2693.     msg.addString(name);
  2694.     msg.addString(description);
  2695.     msg.add<uint32_t>(std::min<uint32_t>(10, icon));
  2696.     msg.addByte(notify ? 0x01 : 0x00);
  2697.     msg.addByte(status);
  2698.     writeToOutputBuffer(msg);
  2699. }
  2700.  
  2701. void ProtocolGame::sendSpellCooldown(uint8_t spellId, uint32_t time)
  2702. {
  2703.     NetworkMessage msg;
  2704.     msg.addByte(0xA4);
  2705.     msg.addByte(spellId);
  2706.     msg.add<uint32_t>(time);
  2707.     writeToOutputBuffer(msg);
  2708. }
  2709.  
  2710. void ProtocolGame::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time)
  2711. {
  2712.     NetworkMessage msg;
  2713.     msg.addByte(0xA5);
  2714.     msg.addByte(groupId);
  2715.     msg.add<uint32_t>(time);
  2716.     writeToOutputBuffer(msg);
  2717. }
  2718.  
  2719. void ProtocolGame::sendModalWindow(const ModalWindow& modalWindow)
  2720. {
  2721.     NetworkMessage msg;
  2722.     msg.addByte(0xFA);
  2723.  
  2724.     msg.add<uint32_t>(modalWindow.id);
  2725.     msg.addString(modalWindow.title);
  2726.     msg.addString(modalWindow.message);
  2727.  
  2728.     msg.addByte(modalWindow.buttons.size());
  2729.     for (const auto& it : modalWindow.buttons) {
  2730.         msg.addString(it.first);
  2731.         msg.addByte(it.second);
  2732.     }
  2733.  
  2734.     msg.addByte(modalWindow.choices.size());
  2735.     for (const auto& it : modalWindow.choices) {
  2736.         msg.addString(it.first);
  2737.         msg.addByte(it.second);
  2738.     }
  2739.  
  2740.     msg.addByte(modalWindow.defaultEscapeButton);
  2741.     msg.addByte(modalWindow.defaultEnterButton);
  2742.     msg.addByte(modalWindow.priority ? 0x01 : 0x00);
  2743.  
  2744.     writeToOutputBuffer(msg);
  2745. }
  2746.  
  2747. ////////////// Add common messages
  2748. void ProtocolGame::AddCreature(NetworkMessage& msg, const Creature* creature, bool known, uint32_t remove)
  2749. {
  2750.     CreatureType_t creatureType = creature->getType();
  2751.  
  2752.     const Player* otherPlayer = creature->getPlayer();
  2753.  
  2754.     if (known) {
  2755.         msg.add<uint16_t>(0x62);
  2756.         msg.add<uint32_t>(creature->getID());
  2757.     } else {
  2758.         msg.add<uint16_t>(0x61);
  2759.         msg.add<uint32_t>(remove);
  2760.         msg.add<uint32_t>(creature->getID());
  2761.         msg.addByte(creatureType);
  2762.         msg.addString(creature->getName());
  2763.     }
  2764.  
  2765.     if (creature->isHealthHidden()) {
  2766.         msg.addByte(0x00);
  2767.     } else {
  2768.         msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100));
  2769.     }
  2770.  
  2771.     msg.addByte(creature->getDirection());
  2772.  
  2773.     if (!creature->isInGhostMode() && !creature->isInvisible()) {
  2774.         AddOutfit(msg, creature->getCurrentOutfit());
  2775.     } else {
  2776.         static Outfit_t outfit;
  2777.         AddOutfit(msg, outfit);
  2778.     }
  2779.  
  2780.     LightInfo lightInfo;
  2781.     creature->getCreatureLight(lightInfo);
  2782.     msg.addByte(player->isAccessPlayer() ? 0xFF : lightInfo.level);
  2783.     msg.addByte(lightInfo.color);
  2784.  
  2785.     msg.add<uint16_t>(creature->getStepSpeed() / 2);
  2786.  
  2787.     msg.addByte(player->getSkullClient(creature));
  2788.     msg.addByte(player->getPartyShield(otherPlayer));
  2789.  
  2790.     if (!known) {
  2791.         msg.addByte(player->getGuildEmblem(otherPlayer));
  2792.     }
  2793.  
  2794.     if (creatureType == CREATURETYPE_MONSTER) {
  2795.         const Creature* master = creature->getMaster();
  2796.         if (master) {
  2797.             const Player* masterPlayer = master->getPlayer();
  2798.             if (masterPlayer) {
  2799.                 if (masterPlayer == player) {
  2800.                     creatureType = CREATURETYPE_SUMMON_OWN;
  2801.                 } else {
  2802.                     creatureType = CREATURETYPE_SUMMON_OTHERS;
  2803.                 }
  2804.             }
  2805.         }
  2806.     }
  2807.  
  2808.     msg.addByte(creatureType); // Type (for summons)
  2809.     msg.addByte(creature->getSpeechBubble());
  2810.     msg.addByte(0xFF); // MARK_UNMARKED
  2811.  
  2812.     if (otherPlayer) {
  2813.         msg.add<uint16_t>(otherPlayer->getHelpers());
  2814.     } else {
  2815.         msg.add<uint16_t>(0x00);
  2816.     }
  2817.  
  2818.     msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01);
  2819. }
  2820.  
  2821. void ProtocolGame::AddPlayerStats(NetworkMessage& msg)
  2822. {
  2823.     msg.addByte(0xA0);
  2824.  
  2825.     msg.add<uint16_t>(std::min<int32_t>(player->getHealth(), std::numeric_limits<uint16_t>::max()));
  2826.     msg.add<uint16_t>(std::min<int32_t>(player->getPlayerInfo(PLAYERINFO_MAXHEALTH), std::numeric_limits<uint16_t>::max()));
  2827.  
  2828.     msg.add<uint32_t>(player->getFreeCapacity());
  2829.     msg.add<uint32_t>(player->getCapacity());
  2830.  
  2831.     msg.add<uint64_t>(player->getExperience());
  2832.  
  2833.     msg.add<uint16_t>(player->getLevel());
  2834.     msg.addByte(player->getPlayerInfo(PLAYERINFO_LEVELPERCENT));
  2835.  
  2836.     msg.add<uint16_t>(100); // base xp gain rate
  2837.     msg.add<uint16_t>(0); // xp voucher
  2838.     msg.add<uint16_t>(0); // low level bonus
  2839.     msg.add<uint16_t>(0); // xp boost
  2840.     msg.add<uint16_t>(100); // stamina multiplier (100 = x1.0)
  2841.  
  2842.     msg.add<uint16_t>(std::min<int32_t>(player->getMana(), std::numeric_limits<uint16_t>::max()));
  2843.     msg.add<uint16_t>(std::min<int32_t>(player->getPlayerInfo(PLAYERINFO_MAXMANA), std::numeric_limits<uint16_t>::max()));
  2844.  
  2845.     msg.addByte(std::min<uint32_t>(player->getMagicLevel(), std::numeric_limits<uint8_t>::max()));
  2846.     msg.addByte(std::min<uint32_t>(player->getBaseMagicLevel(), std::numeric_limits<uint8_t>::max()));
  2847.     msg.addByte(player->getPlayerInfo(PLAYERINFO_MAGICLEVELPERCENT));
  2848.  
  2849.     msg.addByte(player->getSoul());
  2850.  
  2851.     msg.add<uint16_t>(player->getStaminaMinutes());
  2852.  
  2853.     msg.add<uint16_t>(player->getBaseSpeed() / 2);
  2854.  
  2855.     Condition* condition = player->getCondition(CONDITION_REGENERATION);
  2856.     msg.add<uint16_t>(condition ? condition->getTicks() / 1000 : 0x00);
  2857.  
  2858.     msg.add<uint16_t>(player->getOfflineTrainingTime() / 60 / 1000);
  2859.  
  2860.     msg.add<uint16_t>(0); // xp boost time (seconds)
  2861.     msg.addByte(0); // ?
  2862. }
  2863.  
  2864. void ProtocolGame::AddPlayerSkills(NetworkMessage& msg)
  2865. {
  2866.     msg.addByte(0xA1);
  2867.  
  2868.     for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) {
  2869.         msg.add<uint16_t>(std::min<int32_t>(player->getSkillLevel(i), std::numeric_limits<uint16_t>::max()));
  2870.         msg.add<uint16_t>(player->getBaseSkill(i));
  2871.         msg.addByte(player->getSkillPercent(i));
  2872.     }
  2873.  
  2874.     // critical chance
  2875.     msg.add<uint16_t>(0);
  2876.     msg.add<uint16_t>(0);
  2877.  
  2878.     // critical damage
  2879.     msg.add<uint16_t>(0);
  2880.     msg.add<uint16_t>(0);
  2881.  
  2882.     // life leech chance
  2883.     msg.add<uint16_t>(0);
  2884.     msg.add<uint16_t>(0);
  2885.  
  2886.     // life leech
  2887.     msg.add<uint16_t>(0);
  2888.     msg.add<uint16_t>(0);
  2889.  
  2890.     // mana leech chance
  2891.     msg.add<uint16_t>(0);
  2892.     msg.add<uint16_t>(0);
  2893.  
  2894.     // mana leech
  2895.     msg.add<uint16_t>(0);
  2896.     msg.add<uint16_t>(0);
  2897. }
  2898.  
  2899. void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit)
  2900. {
  2901.     msg.add<uint16_t>(outfit.lookType);
  2902.  
  2903.     if (outfit.lookType != 0) {
  2904.         msg.addByte(outfit.lookHead);
  2905.         msg.addByte(outfit.lookBody);
  2906.         msg.addByte(outfit.lookLegs);
  2907.         msg.addByte(outfit.lookFeet);
  2908.         msg.addByte(outfit.lookAddons);
  2909.     } else {
  2910.         msg.addItemId(outfit.lookTypeEx);
  2911.     }
  2912.  
  2913.     msg.add<uint16_t>(outfit.lookMount);
  2914. }
  2915.  
  2916. void ProtocolGame::AddWorldLight(NetworkMessage& msg, const LightInfo& lightInfo)
  2917. {
  2918.     msg.addByte(0x82);
  2919.     msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level));
  2920.     msg.addByte(lightInfo.color);
  2921. }
  2922.  
  2923. void ProtocolGame::AddCreatureLight(NetworkMessage& msg, const Creature* creature)
  2924. {
  2925.     LightInfo lightInfo;
  2926.     creature->getCreatureLight(lightInfo);
  2927.  
  2928.     msg.addByte(0x8D);
  2929.     msg.add<uint32_t>(creature->getID());
  2930.     msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level));
  2931.     msg.addByte(lightInfo.color);
  2932. }
  2933.  
  2934. //tile
  2935. void ProtocolGame::RemoveTileThing(NetworkMessage& msg, const Position& pos, uint32_t stackpos)
  2936. {
  2937.     if (stackpos >= 10) {
  2938.         return;
  2939.     }
  2940.  
  2941.     msg.addByte(0x6C);
  2942.     msg.addPosition(pos);
  2943.     msg.addByte(stackpos);
  2944. }
  2945.  
  2946. void ProtocolGame::MoveUpCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos)
  2947. {
  2948.     if (creature != player) {
  2949.         return;
  2950.     }
  2951.  
  2952.     //floor change up
  2953.     msg.addByte(0xBE);
  2954.  
  2955.     //going to surface
  2956.     if (newPos.z == 7) {
  2957.         int32_t skip = -1;
  2958.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 5, 18, 14, 3, skip); //(floor 7 and 6 already set)
  2959.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 4, 18, 14, 4, skip);
  2960.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 3, 18, 14, 5, skip);
  2961.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 2, 18, 14, 6, skip);
  2962.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 1, 18, 14, 7, skip);
  2963.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 0, 18, 14, 8, skip);
  2964.  
  2965.         if (skip >= 0) {
  2966.             msg.addByte(skip);
  2967.             msg.addByte(0xFF);
  2968.         }
  2969.     }
  2970.     //underground, going one floor up (still underground)
  2971.     else if (newPos.z > 7) {
  2972.         int32_t skip = -1;
  2973.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, oldPos.getZ() - 3, 18, 14, 3, skip);
  2974.  
  2975.         if (skip >= 0) {
  2976.             msg.addByte(skip);
  2977.             msg.addByte(0xFF);
  2978.         }
  2979.     }
  2980.  
  2981.     //moving up a floor up makes us out of sync
  2982.     //west
  2983.     msg.addByte(0x68);
  2984.     GetMapDescription(oldPos.x - 8, oldPos.y - 5, newPos.z, 1, 14, msg);
  2985.  
  2986.     //north
  2987.     msg.addByte(0x65);
  2988.     GetMapDescription(oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 1, msg);
  2989. }
  2990.  
  2991. void ProtocolGame::MoveDownCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos)
  2992. {
  2993.     if (creature != player) {
  2994.         return;
  2995.     }
  2996.  
  2997.     //floor change down
  2998.     msg.addByte(0xBF);
  2999.  
  3000.     //going from surface to underground
  3001.     if (newPos.z == 8) {
  3002.         int32_t skip = -1;
  3003.  
  3004.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 14, -1, skip);
  3005.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 1, 18, 14, -2, skip);
  3006.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip);
  3007.  
  3008.         if (skip >= 0) {
  3009.             msg.addByte(skip);
  3010.             msg.addByte(0xFF);
  3011.         }
  3012.     }
  3013.     //going further down
  3014.     else if (newPos.z > oldPos.z && newPos.z > 8 && newPos.z < 14) {
  3015.         int32_t skip = -1;
  3016.         GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip);
  3017.  
  3018.         if (skip >= 0) {
  3019.             msg.addByte(skip);
  3020.             msg.addByte(0xFF);
  3021.         }
  3022.     }
  3023.  
  3024.     //moving down a floor makes us out of sync
  3025.     //east
  3026.     msg.addByte(0x66);
  3027.     GetMapDescription(oldPos.x + 9, oldPos.y - 7, newPos.z, 1, 14, msg);
  3028.  
  3029.     //south
  3030.     msg.addByte(0x67);
  3031.     GetMapDescription(oldPos.x - 8, oldPos.y + 7, newPos.z, 18, 1, msg);
  3032. }
  3033.  
  3034. void ProtocolGame::AddShopItem(NetworkMessage& msg, const ShopInfo& item)
  3035. {
  3036.     const ItemType& it = Item::items[item.itemId];
  3037.     msg.add<uint16_t>(it.clientId);
  3038.  
  3039.     if (it.isSplash() || it.isFluidContainer()) {
  3040.         msg.addByte(serverFluidToClient(item.subType));
  3041.     } else {
  3042.         msg.addByte(0x00);
  3043.     }
  3044.  
  3045.     msg.addString(item.realName);
  3046.     msg.add<uint32_t>(it.weight);
  3047.     msg.add<uint32_t>(item.buyPrice);
  3048.     msg.add<uint32_t>(item.sellPrice);
  3049. }
  3050.  
  3051. void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg)
  3052. {
  3053.     uint8_t opcode = msg.getByte();
  3054.     const std::string& buffer = msg.getString();
  3055.  
  3056.     // process additional opcodes via lua script event
  3057.     addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer);
  3058. }
Add Comment
Please, Sign In to add comment