Advertisement
Guest User

Untitled

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