Advertisement
Guest User

Untitled

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