Advertisement
Guest User

Untitled

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