Advertisement
Guest User

Untitled

a guest
May 9th, 2021
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 167.93 KB | None | 0 0
  1. /**
  2. * The Forgotten Server - a free and open-source MMORPG server emulator
  3. * Copyright (C) 2019 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. #include "modules.h"
  39. #include "spells.h"
  40. #include "weapons.h"
  41. #include "imbuements.h"
  42. #include "iobestiary.h"
  43. #include "monsters.h"
  44.  
  45. extern Game g_game;
  46. extern ConfigManager g_config;
  47. extern Actions actions;
  48. extern CreatureEvents *g_creatureEvents;
  49. extern Vocations g_vocations;
  50. extern Chat *g_chat;
  51. extern Modules *g_modules;
  52. extern Spells *g_spells;
  53. extern Imbuements *g_imbuements;
  54. extern Monsters g_monsters;
  55.  
  56. void ProtocolGame::AddItem(NetworkMessage &msg, uint16_t id, uint8_t count)
  57. {
  58. const ItemType &it = Item::items[id];
  59.  
  60. msg.add<uint16_t>(it.clientId);
  61. if (version < 1200) {
  62. msg.addByte(0xFF); // MARK_UNMARKED
  63. }
  64. if (it.stackable)
  65. {
  66. msg.addByte(count);
  67. }
  68. else if (it.isSplash() || it.isFluidContainer())
  69. {
  70. msg.addByte(fluidMap[count & 7]);
  71. }
  72. else if (version >= 1200 && it.isContainer() && player->getOperatingSystem() <= CLIENTOS_NEW_MAC)
  73. {
  74. msg.addByte(0x00);
  75. msg.addByte(0x00);
  76. }
  77. if (version < 1200 && it.isAnimation) {
  78. msg.addByte(0xFE); // random phase (0xFF for async)
  79. }
  80. }
  81.  
  82. void ProtocolGame::AddItem(NetworkMessage &msg, const Item *item)
  83. {
  84. if (!item)
  85. {
  86. return;
  87. }
  88.  
  89. const ItemType &it = Item::items[item->getID()];
  90.  
  91. msg.add<uint16_t>(it.clientId);
  92. if (version < 1200) {
  93. msg.addByte(0xFF); // MARK_UNMARKED
  94. }
  95. if (it.stackable)
  96. {
  97. msg.addByte(std::min<uint16_t>(0xFF, item->getItemCount()));
  98. }
  99. else if (it.isSplash() || it.isFluidContainer())
  100. {
  101. msg.addByte(fluidMap[item->getFluidType() & 7]);
  102. }
  103. else if (version >= 1200 && it.isContainer() && player->getOperatingSystem() <= CLIENTOS_NEW_MAC)
  104. {
  105. const Container *container = item->getContainer();
  106. if (container && container->getHoldingPlayer() == player)
  107. {
  108. uint32_t lootFlags = 0;
  109. for (auto itt : player->quickLootContainers)
  110. {
  111. if (itt.second == container)
  112. {
  113. lootFlags |= 1 << itt.first;
  114. }
  115. }
  116.  
  117. if (lootFlags != 0)
  118. {
  119. msg.addByte(0x01);
  120. msg.add<uint32_t>(lootFlags);
  121. }
  122. else
  123. {
  124. msg.addByte(0x00);
  125. }
  126. }
  127. else
  128. {
  129. msg.addByte(0x00);
  130. }
  131.  
  132. // Quiver ammo count
  133. if (item->getWeaponType() == WEAPON_QUIVER && player->getThing(CONST_SLOT_RIGHT) == item) {
  134. uint16_t ammoTotal = 0;
  135. for (Item* listItem : container->getItemList()) {
  136. ammoTotal += listItem->getItemCount();
  137. }
  138. msg.addByte(0x01);
  139. msg.add<uint32_t>(ammoTotal);
  140. }
  141. else
  142. msg.addByte(0x00);
  143. }
  144. if (version < 1200 && it.isAnimation) {
  145. msg.addByte(0xFE); // random phase (0xFF for async)
  146. }
  147. }
  148.  
  149. void ProtocolGame::release()
  150. {
  151. //dispatcher thread
  152. if (player && player->client == shared_from_this())
  153. {
  154. player->client.reset();
  155. player->decrementReferenceCounter();
  156. player = nullptr;
  157. }
  158.  
  159. OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
  160. Protocol::release();
  161. }
  162.  
  163. void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingSystem_t operatingSystem)
  164. {
  165. //dispatcher thread
  166. Player *foundPlayer = g_game.getPlayerByName(name);
  167. if (!foundPlayer || g_config.getBoolean(ConfigManager::ALLOW_CLONES))
  168. {
  169. player = new Player(getThis());
  170. player->setName(name);
  171.  
  172. player->incrementReferenceCounter();
  173. player->setID();
  174.  
  175. if (!IOLoginData::preloadPlayer(player, name))
  176. {
  177. disconnectClient("Your character could not be loaded.");
  178. return;
  179. }
  180.  
  181. if (IOBan::isPlayerNamelocked(player->getGUID()))
  182. {
  183. disconnectClient("Your character has been namelocked.");
  184. return;
  185. }
  186.  
  187. if (g_game.getGameState() == GAME_STATE_CLOSING && !player->hasFlag(PlayerFlag_CanAlwaysLogin))
  188. {
  189. disconnectClient("The game is just going down.\nPlease try again later.");
  190. return;
  191. }
  192.  
  193. if (g_game.getGameState() == GAME_STATE_CLOSED && !player->hasFlag(PlayerFlag_CanAlwaysLogin))
  194. {
  195. disconnectClient("Server is currently closed.\nPlease try again later.");
  196. return;
  197. }
  198.  
  199. if (g_config.getBoolean(ConfigManager::ONLY_PREMIUM_ACCOUNT) && !player->isPremium() && (player->getGroup()->id < account::GROUP_TYPE_GAMEMASTER || player->getAccountType() < account::ACCOUNT_TYPE_GAMEMASTER))
  200. {
  201. disconnectClient("Your premium time for this account is out.\n\nTo play please buy additional premium time from our website");
  202. return;
  203. }
  204.  
  205. if (g_config.getBoolean(ConfigManager::ONE_PLAYER_ON_ACCOUNT) && player->getAccountType() < account::ACCOUNT_TYPE_GAMEMASTER && g_game.getPlayerByAccount(player->getAccount()))
  206. {
  207. disconnectClient("You may only login with one character\nof your account at the same time.");
  208. return;
  209. }
  210.  
  211. if (!player->hasFlag(PlayerFlag_CannotBeBanned))
  212. {
  213. BanInfo banInfo;
  214. if (IOBan::isAccountBanned(accountId, banInfo))
  215. {
  216. if (banInfo.reason.empty())
  217. {
  218. banInfo.reason = "(none)";
  219. }
  220.  
  221. std::ostringstream ss;
  222. if (banInfo.expiresAt > 0)
  223. {
  224. ss << "Your account has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n"
  225. << banInfo.reason;
  226. }
  227. else
  228. {
  229. ss << "Your account has been permanently banned by " << banInfo.bannedBy << ".\n\nReason specified:\n"
  230. << banInfo.reason;
  231. }
  232. disconnectClient(ss.str());
  233. return;
  234. }
  235. }
  236.  
  237. WaitingList &waitingList = WaitingList::getInstance();
  238. if (!waitingList.clientLogin(player))
  239. {
  240. uint32_t currentSlot = waitingList.getClientSlot(player);
  241. uint32_t retryTime = WaitingList::getTime(currentSlot);
  242. std::ostringstream ss;
  243.  
  244. ss << "Too many players online.\nYou are at place "
  245. << currentSlot << " on the waiting list.";
  246.  
  247. auto output = OutputMessagePool::getOutputMessage();
  248. output->addByte(0x16);
  249. output->addString(ss.str());
  250. output->addByte(retryTime);
  251. send(output);
  252. disconnect();
  253. return;
  254. }
  255.  
  256. if (!IOLoginData::loadPlayerById(player, player->getGUID()))
  257. {
  258. disconnectClient("Your character could not be loaded.");
  259. return;
  260. }
  261.  
  262. // New Prey
  263. if (!IOLoginData::loadPlayerPreyData(player))
  264. {
  265. std::cout << "Prey data could not be loaded" << std::endl;
  266. return;
  267. };
  268.  
  269. player->setOperatingSystem(operatingSystem);
  270.  
  271. if (!g_game.placeCreature(player, player->getLoginPosition()))
  272. {
  273. if (!g_game.placeCreature(player, player->getTemplePosition(), false, true))
  274. {
  275. disconnectClient("Temple position is wrong. Contact the administrator.");
  276. return;
  277. }
  278. }
  279.  
  280. if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX)
  281. {
  282. player->registerCreatureEvent("ExtendedOpcode");
  283. }
  284.  
  285. player->lastIP = player->getIP();
  286. player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
  287. acceptPackets = true;
  288. }
  289. else
  290. {
  291. if (eventConnect != 0 || !g_config.getBoolean(ConfigManager::REPLACE_KICK_ON_LOGIN))
  292. {
  293. //Already trying to connect
  294. disconnectClient("You are already logged in.");
  295. return;
  296. }
  297.  
  298. if (foundPlayer->client)
  299. {
  300. foundPlayer->disconnect();
  301. foundPlayer->isConnecting = true;
  302.  
  303. eventConnect = g_scheduler.addEvent(createSchedulerTask(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getID(), operatingSystem)));
  304. }
  305. else
  306. {
  307. connect(foundPlayer->getID(), operatingSystem);
  308. }
  309. }
  310. OutputMessagePool::getInstance().addProtocolToAutosend(shared_from_this());
  311. }
  312.  
  313. void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem)
  314. {
  315. eventConnect = 0;
  316.  
  317. Player *foundPlayer = g_game.getPlayerByID(playerId);
  318. if (!foundPlayer || foundPlayer->client)
  319. {
  320. disconnectClient("You are already logged in.");
  321. return;
  322. }
  323.  
  324. if (isConnectionExpired())
  325. {
  326. //ProtocolGame::release() has been called at this point and the Connection object
  327. //no longer exists, so we return to prevent leakage of the Player.
  328. return;
  329. }
  330.  
  331. player = foundPlayer;
  332. player->incrementReferenceCounter();
  333.  
  334. g_chat->removeUserFromAllChannels(*player);
  335. player->clearModalWindows();
  336. player->setOperatingSystem(operatingSystem);
  337. player->isConnecting = false;
  338.  
  339. player->client = getThis();
  340. sendAddCreature(player, player->getPosition(), 0, false);
  341. player->lastIP = player->getIP();
  342. player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
  343. acceptPackets = true;
  344. }
  345.  
  346. void ProtocolGame::logout(bool displayEffect, bool forced)
  347. {
  348. //dispatcher thread
  349. if (!player)
  350. {
  351. return;
  352. }
  353.  
  354. if (!player->isRemoved())
  355. {
  356. if (!forced)
  357. {
  358. if (!player->isAccessPlayer())
  359. {
  360. if (player->getTile()->hasFlag(TILESTATE_NOLOGOUT))
  361. {
  362. player->sendCancelMessage(RETURNVALUE_YOUCANNOTLOGOUTHERE);
  363. return;
  364. }
  365.  
  366. if (!player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) && player->hasCondition(CONDITION_INFIGHT))
  367. {
  368. player->sendCancelMessage(RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT);
  369. return;
  370. }
  371. }
  372.  
  373. //scripting event - onLogout
  374. if (!g_creatureEvents->playerLogout(player))
  375. {
  376. //Let the script handle the error message
  377. return;
  378. }
  379. }
  380.  
  381. if (displayEffect && player->getHealth() > 0 && !player->isInGhostMode())
  382. {
  383. g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
  384. }
  385. }
  386.  
  387. disconnect();
  388.  
  389. g_game.removeCreature(player);
  390. }
  391.  
  392. void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg)
  393. {
  394. if (g_game.getGameState() == GAME_STATE_SHUTDOWN)
  395. {
  396. disconnect();
  397. return;
  398. }
  399.  
  400. OperatingSystem_t operatingSystem = static_cast<OperatingSystem_t>(msg.get<uint16_t>());
  401. version = msg.get<uint16_t>();
  402.  
  403. if (version >= 1200 && operatingSystem <= CLIENTOS_NEW_MAC) {
  404. enableCompact();
  405. }
  406.  
  407. clientVersion = static_cast<int32_t>(msg.get<uint32_t>());
  408.  
  409. msg.skipBytes(3); // U16 dat revision, game preview state
  410.  
  411. // In version 12.40.10030 we have 13 extra bytes
  412. if (version >= 1200 && msg.getLength() - msg.getBufferPosition() == 141)
  413. {
  414. msg.skipBytes(13);
  415. }
  416.  
  417. if (!Protocol::RSA_decrypt(msg))
  418. {
  419. std::cout << "[ProtocolGame::onRecvFirstMessage] RSA Decrypt Failed" << std::endl;
  420. disconnect();
  421. return;
  422. }
  423.  
  424. xtea::key key;
  425. key[0] = msg.get<uint32_t>();
  426. key[1] = msg.get<uint32_t>();
  427. key[2] = msg.get<uint32_t>();
  428. key[3] = msg.get<uint32_t>();
  429. enableXTEAEncryption();
  430. setXTEAKey(std::move(key));
  431.  
  432. msg.skipBytes(1); // gamemaster flag
  433.  
  434. std::string sessionKey = msg.getString();
  435. size_t pos = sessionKey.find('\n');
  436. if (pos == std::string::npos)
  437. {
  438. disconnectClient("You must enter your account name.");
  439. return;
  440. }
  441.  
  442. if (operatingSystem == CLIENTOS_NEW_LINUX)
  443. {
  444. // TODO: check what new info for linux is send
  445. msg.getString();
  446. msg.getString();
  447. }
  448.  
  449. std::string accountName = sessionKey.substr(0, pos);
  450. if (accountName.empty())
  451. {
  452. disconnectClient("You must enter your account name.");
  453. return;
  454. }
  455.  
  456. std::string password = sessionKey.substr(pos + 1);
  457. std::string characterName = msg.getString();
  458.  
  459. uint32_t timeStamp = msg.get<uint32_t>();
  460. uint8_t randNumber = msg.getByte();
  461. if (challengeTimestamp != timeStamp || challengeRandom != randNumber)
  462. {
  463. disconnect();
  464. return;
  465. }
  466.  
  467. bool allowClientOld = g_config.getBoolean(ConfigManager::ALLOW_CLIENT_OLD);
  468. if (clientVersion != g_config.getNumber(ConfigManager::CLIENT_VERSION) && (allowClientOld && version != 1100))
  469. {
  470. std::ostringstream ss;
  471. ss << "Only clients with protocol " << g_config.getString(ConfigManager::CLIENT_VERSION_STR);
  472. if (allowClientOld)
  473. ss << " and 10.00";
  474. ss << " allowed!";
  475. disconnectClient(ss.str());
  476. return;
  477. }
  478.  
  479. if (g_game.getGameState() == GAME_STATE_STARTUP)
  480. {
  481. disconnectClient("Gameworld is starting up. Please wait.");
  482. return;
  483. }
  484.  
  485. if (g_game.getGameState() == GAME_STATE_MAINTAIN)
  486. {
  487. disconnectClient("Gameworld is under maintenance. Please re-connect in a while.");
  488. return;
  489. }
  490.  
  491. BanInfo banInfo;
  492. if (IOBan::isIpBanned(getIP(), banInfo))
  493. {
  494. if (banInfo.reason.empty())
  495. {
  496. banInfo.reason = "(none)";
  497. }
  498.  
  499. std::ostringstream ss;
  500. ss << "Your IP has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n"
  501. << banInfo.reason;
  502. disconnectClient(ss.str());
  503. return;
  504. }
  505.  
  506. uint32_t accountId = IOLoginData::gameworldAuthentication(accountName, password, characterName, version < 1200);
  507. if (accountId == 0)
  508. {
  509. disconnectClient("Account name or password is not correct.");
  510. return;
  511. }
  512.  
  513. g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem)));
  514. }
  515.  
  516. void ProtocolGame::onConnect()
  517. {
  518. auto output = OutputMessagePool::getOutputMessage();
  519. static std::random_device rd;
  520. static std::ranlux24 generator(rd());
  521. static std::uniform_int_distribution<uint16_t> randNumber(0x00, 0xFF);
  522.  
  523. // Skip checksum
  524. output->skipBytes(sizeof(uint32_t));
  525.  
  526. // Packet length & type
  527. output->add<uint16_t>(0x0006);
  528. output->addByte(0x1F);
  529.  
  530. // Add timestamp & random number
  531. challengeTimestamp = static_cast<uint32_t>(time(nullptr));
  532. output->add<uint32_t>(challengeTimestamp);
  533.  
  534. challengeRandom = randNumber(generator);
  535. output->addByte(challengeRandom);
  536.  
  537. // Go back and write checksum
  538. output->skipBytes(-12);
  539. // To support 11.10-, not have problems with 11.11+
  540. output->add<uint32_t>(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8));
  541.  
  542. send(std::move(output));
  543. }
  544.  
  545. void ProtocolGame::disconnectClient(const std::string &message) const
  546. {
  547. auto output = OutputMessagePool::getOutputMessage();
  548. output->addByte(0x14);
  549. output->addString(message);
  550. send(output);
  551. disconnect();
  552. }
  553.  
  554. void ProtocolGame::writeToOutputBuffer(const NetworkMessage &msg)
  555. {
  556. auto out = getOutputBuffer(msg.getLength());
  557. out->append(msg);
  558. }
  559.  
  560. void ProtocolGame::parsePacket(NetworkMessage& msg)
  561. {
  562. if (!acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN || msg.getLength() <= 0) {
  563. return;
  564. }
  565.  
  566. uint8_t recvbyte = msg.getByte();
  567.  
  568. //a dead player can not perform actions
  569. if (!player || player->isRemoved() || player->getHealth() <= 0) {
  570. if (recvbyte == 0x0F) {
  571. // we need to make the player pointer != null in this part, game.cpp release is the first step
  572. // login(player->getName(), player->getAccount(), player->operatingSystem);
  573. disconnect();
  574. return;
  575. }
  576.  
  577. if (recvbyte != 0x14) {
  578. return;
  579. }
  580. }
  581.  
  582. //TODO: JLCVP - Refactor this terrible validation
  583. if(recvbyte != 0xD3){
  584. g_dispatcher.addTask(createTask(std::bind(&Modules::executeOnRecvbyte, g_modules, player, msg, recvbyte)));
  585. }
  586.  
  587. switch (recvbyte) {
  588. case 0x14: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); break;
  589. case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); break;
  590. case 0x1E: addGameTask(&Game::playerReceivePing, player->getID()); break;
  591. case 0x2a: addBestiaryTrackerList(msg); break;
  592. case 0x2c: parseLeaderFinderWindow(msg); break;
  593. case 0x2d: parseMemberFinderWindow(msg); break;
  594. case 0x28: parseStashWithdraw(msg); break;
  595. case 0x32: parseExtendedOpcode(msg); break; //otclient extended opcode
  596. case 0x64: parseAutoWalk(msg); break;
  597. case 0x65: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); break;
  598. case 0x66: addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); break;
  599. case 0x67: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTH); break;
  600. case 0x68: addGameTask(&Game::playerMove, player->getID(), DIRECTION_WEST); break;
  601. case 0x69: addGameTask(&Game::playerStopAutoWalk, player->getID()); break;
  602. case 0x6A: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHEAST); break;
  603. case 0x6B: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHEAST); break;
  604. case 0x6C: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHWEST); break;
  605. case 0x6D: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHWEST); break;
  606. case 0x6F: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_NORTH); break;
  607. case 0x70: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_EAST); break;
  608. case 0x71: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_SOUTH); break;
  609. case 0x72: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_WEST); break;
  610. case 0x73: parseTeleport(msg); break;
  611. case 0x77: parseHotkeyEquip(msg); break;
  612. case 0x78: parseThrow(msg); break;
  613. case 0x79: parseLookInShop(msg); break;
  614. case 0x7A: parsePlayerPurchase(msg); break;
  615. case 0x7B: parsePlayerSale(msg); break;
  616. case 0x7C: addGameTask(&Game::playerCloseShop, player->getID()); break;
  617. case 0x7D: parseRequestTrade(msg); break;
  618. case 0x7E: parseLookInTrade(msg); break;
  619. case 0x7F: addGameTask(&Game::playerAcceptTrade, player->getID()); break;
  620. case 0x80: addGameTask(&Game::playerCloseTrade, player->getID()); break;
  621. case 0x82: parseUseItem(msg); break;
  622. case 0x83: parseUseItemEx(msg); break;
  623. case 0x84: parseUseWithCreature(msg); break;
  624. case 0x85: parseRotateItem(msg); break;
  625. case 0x87: parseCloseContainer(msg); break;
  626. case 0x88: parseUpArrowContainer(msg); break;
  627. case 0x89: parseTextWindow(msg); break;
  628. case 0x8A: parseHouseWindow(msg); break;
  629. case 0x8B: parseWrapableItem(msg); break;
  630. case 0x8C: parseLookAt(msg); break;
  631. case 0x8D: parseLookInBattleList(msg); break;
  632. case 0x8E: /* join aggression */ break;
  633. case 0x8F: parseQuickLoot(msg); break;
  634. case 0x90: parseLootContainer(msg); break;
  635. case 0x91: parseQuickLootBlackWhitelist(msg); break;
  636. case 0x92: parseRequestLockItems(); break;
  637. case 0x96: parseSay(msg); break;
  638. case 0x97: addGameTask(&Game::playerRequestChannels, player->getID()); break;
  639. case 0x98: parseOpenChannel(msg); break;
  640. case 0x99: parseCloseChannel(msg); break;
  641. case 0x9A: parseOpenPrivateChannel(msg); break;
  642. case 0x9E: addGameTask(&Game::playerCloseNpcChannel, player->getID()); break;
  643. case 0xA0: parseFightModes(msg); break;
  644. case 0xA1: parseAttack(msg); break;
  645. case 0xA2: parseFollow(msg); break;
  646. case 0xA3: parseInviteToParty(msg); break;
  647. case 0xA4: parseJoinParty(msg); break;
  648. case 0xA5: parseRevokePartyInvite(msg); break;
  649. case 0xA6: parsePassPartyLeadership(msg); break;
  650. case 0xA7: addGameTask(&Game::playerLeaveParty, player->getID()); break;
  651. case 0xA8: parseEnableSharedPartyExperience(msg); break;
  652. case 0xAA: addGameTask(&Game::playerCreatePrivateChannel, player->getID()); break;
  653. case 0xAB: parseChannelInvite(msg); break;
  654. case 0xAC: parseChannelExclude(msg); break;
  655. case 0xB1: parseHighscores(msg); break;
  656. case 0xBE: addGameTask(&Game::playerCancelAttackAndFollow, player->getID()); break;
  657. case 0xC7: parseTournamentLeaderboard(msg); break;
  658. case 0xC9: /* update tile */ break;
  659. case 0xCA: parseUpdateContainer(msg); break;
  660. case 0xCB: parseBrowseField(msg); break;
  661. case 0xCC: parseSeekInContainer(msg); break;
  662. case 0xCD: parseInspectionObject(msg); break;
  663. case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break;
  664. //g_dispatcher.addTask(createTask(std::bind(&Modules::executeOnRecvbyte, g_modules, player, msg, recvbyte)));
  665. case 0xD3: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::parseSetOutfit, this, msg))); break;
  666. case 0xD4: parseToggleMount(msg); break;
  667. case 0xD5: parseApplyImbuement(msg); break;
  668. case 0xD6: parseClearingImbuement(msg); break;
  669. case 0xD7: parseCloseImbuingWindow(msg); break;
  670. case 0xDC: parseAddVip(msg); break;
  671. case 0xDD: parseRemoveVip(msg); break;
  672. case 0xDE: parseEditVip(msg); break;
  673. case 0xE1: parseBestiarysendRaces(); break;
  674. case 0xE2: parseBestiarysendCreatures(msg); break;
  675. case 0xE3: parseBestiarysendMonsterData(msg); break;
  676. case 0xE4: parseSendBuyCharmRune(msg); break;
  677. case 0xE5: parseCyclopediaCharacterInfo(msg); break;
  678. case 0xE6: parseBugReport(msg); break;
  679. case 0xE7: /* thank you */ break;
  680. case 0xE8: parseDebugAssert(msg); break;
  681. case 0xEE: parseGreet(msg); break;
  682. case 0xEF: if (!g_config.getBoolean(ConfigManager::STOREMODULES)) { parseCoinTransfer(msg); } break; /* premium coins transfer */
  683. case 0xF0: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerShowQuestLog, player->getID()); break;
  684. case 0xF1: parseQuestLine(msg); break;
  685. // case 0xF2: parseRuleViolationReport(msg); break;
  686. case 0xF3: /* get object info */ break;
  687. case 0xF4: parseMarketLeave(); break;
  688. case 0xF5: parseMarketBrowse(msg); break;
  689. case 0xF6: parseMarketCreateOffer(msg); break;
  690. case 0xF7: parseMarketCancelOffer(msg); break;
  691. case 0xF8: parseMarketAcceptOffer(msg); break;
  692. case 0xF9: parseModalWindowAnswer(msg); break;
  693. case 0xFA: if (!g_config.getBoolean(ConfigManager::STOREMODULES)) { parseStoreOpen(msg); } break;
  694. case 0xFB: if (!g_config.getBoolean(ConfigManager::STOREMODULES)) { parseStoreRequestOffers(msg); } break;
  695. case 0xFC: if (!g_config.getBoolean(ConfigManager::STOREMODULES)) { parseStoreBuyOffer(msg); } break;
  696. // case 0xFD: parseStoreOpenTransactionHistory(msg); break;
  697. // case 0xFE: parseStoreRequestTransactionHistory(msg); break;
  698.  
  699. //case 0xDF, 0xE0, 0xE1, 0xFB, 0xFC, 0xFD, 0xFE Premium Shop.
  700.  
  701. default:
  702. // std::cout << "Player: " << player->getName() << " sent an unknown packet header: 0x" << std::hex << static_cast<uint16_t>(recvbyte) << std::dec << "!" << std::endl;
  703. break;
  704. }
  705. /* temporary solution to disconnections while opening store
  706. if (msg.isOverrun()) {
  707. disconnect();
  708. }
  709. */
  710. }
  711.  
  712. void ProtocolGame::parseHotkeyEquip(NetworkMessage &msg)
  713. {
  714. if (!player)
  715. {
  716. return;
  717. }
  718. uint16_t spriteid = msg.get<uint16_t>();
  719. addGameTask(&Game::onPressHotkeyEquip, player, spriteid);
  720. return;
  721. }
  722.  
  723. void ProtocolGame::GetTileDescription(const Tile *tile, NetworkMessage &msg)
  724. {
  725. if (version < 1200) {
  726. msg.add<uint16_t>(0x00); //environmental effects
  727. }
  728. int32_t count;
  729. Item *ground = tile->getGround();
  730. if (ground)
  731. {
  732. AddItem(msg, ground);
  733. count = 1;
  734. }
  735. else
  736. {
  737. count = 0;
  738. }
  739.  
  740. const TileItemVector *items = tile->getItemList();
  741. if (items)
  742. {
  743. for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it)
  744. {
  745. AddItem(msg, *it);
  746.  
  747. count++;
  748. if (count == 9 && tile->getPosition() == player->getPosition())
  749. {
  750. break;
  751. }
  752. else if (count == 10)
  753. {
  754. return;
  755. }
  756. }
  757. }
  758.  
  759. const CreatureVector *creatures = tile->getCreatures();
  760. if (creatures)
  761. {
  762. bool playerAdded = false;
  763. for (const Creature *creature : boost::adaptors::reverse(*creatures))
  764. {
  765. if (!player->canSeeCreature(creature))
  766. {
  767. continue;
  768. }
  769.  
  770. if (tile->getPosition() == player->getPosition() && count == 9 && !playerAdded)
  771. {
  772. creature = player;
  773. }
  774.  
  775. if (creature->getID() == player->getID())
  776. {
  777. playerAdded = true;
  778. }
  779.  
  780. bool known;
  781. uint32_t removedKnown;
  782. checkCreatureAsKnown(creature->getID(), known, removedKnown);
  783. AddCreature(msg, creature, known, removedKnown);
  784.  
  785. if (++count == 10)
  786. {
  787. return;
  788. }
  789. }
  790. }
  791.  
  792. if (items)
  793. {
  794. for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it)
  795. {
  796. AddItem(msg, *it);
  797.  
  798. if (++count == 10)
  799. {
  800. return;
  801. }
  802. }
  803. }
  804. }
  805.  
  806. void ProtocolGame::GetMapDescription(int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, NetworkMessage &msg)
  807. {
  808. int32_t skip = -1;
  809. int32_t startz, endz, zstep;
  810.  
  811. if (z > 7)
  812. {
  813. startz = z - 2;
  814. endz = std::min<int32_t>(MAP_MAX_LAYERS - 1, z + 2);
  815. zstep = 1;
  816. }
  817. else
  818. {
  819. startz = 7;
  820. endz = 0;
  821. zstep = -1;
  822. }
  823.  
  824. for (int32_t nz = startz; nz != endz + zstep; nz += zstep)
  825. {
  826. GetFloorDescription(msg, x, y, nz, width, height, z - nz, skip);
  827. }
  828.  
  829. if (skip >= 0)
  830. {
  831. msg.addByte(skip);
  832. msg.addByte(0xFF);
  833. }
  834. }
  835.  
  836. 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)
  837. {
  838. for (int32_t nx = 0; nx < width; nx++)
  839. {
  840. for (int32_t ny = 0; ny < height; ny++)
  841. {
  842. Tile *tile = g_game.map.getTile(x + nx + offset, y + ny + offset, z);
  843. if (tile)
  844. {
  845. if (skip >= 0)
  846. {
  847. msg.addByte(skip);
  848. msg.addByte(0xFF);
  849. }
  850.  
  851. skip = 0;
  852. GetTileDescription(tile, msg);
  853. }
  854. else if (skip == 0xFE)
  855. {
  856. msg.addByte(0xFF);
  857. msg.addByte(0xFF);
  858. skip = -1;
  859. }
  860. else
  861. {
  862. ++skip;
  863. }
  864. }
  865. }
  866. }
  867.  
  868. void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool &known, uint32_t &removedKnown)
  869. {
  870. auto result = knownCreatureSet.insert(id);
  871. if (!result.second)
  872. {
  873. known = true;
  874. return;
  875. }
  876.  
  877. known = false;
  878.  
  879. if (knownCreatureSet.size() > 1300)
  880. {
  881. // Look for a creature to remove
  882. for (auto it = knownCreatureSet.begin(), end = knownCreatureSet.end(); it != end; ++it)
  883. {
  884. Creature *creature = g_game.getCreatureByID(*it);
  885. if (!canSee(creature))
  886. {
  887. removedKnown = *it;
  888. knownCreatureSet.erase(it);
  889. return;
  890. }
  891. }
  892.  
  893. // Bad situation. Let's just remove anyone.
  894. auto it = knownCreatureSet.begin();
  895. if (*it == id)
  896. {
  897. ++it;
  898. }
  899.  
  900. removedKnown = *it;
  901. knownCreatureSet.erase(it);
  902. }
  903. else
  904. {
  905. removedKnown = 0;
  906. }
  907. }
  908.  
  909. bool ProtocolGame::canSee(const Creature *c) const
  910. {
  911. if (!c || !player || c->isRemoved())
  912. {
  913. return false;
  914. }
  915.  
  916. if (!player->canSeeCreature(c))
  917. {
  918. return false;
  919. }
  920.  
  921. return canSee(c->getPosition());
  922. }
  923.  
  924. bool ProtocolGame::canSee(const Position &pos) const
  925. {
  926. return canSee(pos.x, pos.y, pos.z);
  927. }
  928.  
  929. bool ProtocolGame::canSee(int32_t x, int32_t y, int32_t z) const
  930. {
  931. if (!player)
  932. {
  933. return false;
  934. }
  935.  
  936. const Position &myPos = player->getPosition();
  937. if (myPos.z <= 7)
  938. {
  939. //we are on ground level or above (7 -> 0)
  940. //view is from 7 -> 0
  941. if (z > 7)
  942. {
  943. return false;
  944. }
  945. }
  946. else if (myPos.z >= 8)
  947. {
  948. //we are underground (8 -> 15)
  949. //view is +/- 2 from the floor we stand on
  950. if (std::abs(myPos.getZ() - z) > 2)
  951. {
  952. return false;
  953. }
  954. }
  955.  
  956. //negative offset means that the action taken place is on a lower floor than ourself
  957. int32_t offsetz = myPos.getZ() - z;
  958. if ((x >= myPos.getX() - 8 + offsetz) && (x <= myPos.getX() + 9 + offsetz) &&
  959. (y >= myPos.getY() - 6 + offsetz) && (y <= myPos.getY() + 7 + offsetz))
  960. {
  961. return true;
  962. }
  963. return false;
  964. }
  965.  
  966. // Parse methods
  967. void ProtocolGame::parseChannelInvite(NetworkMessage &msg)
  968. {
  969. const std::string name = msg.getString();
  970. addGameTask(&Game::playerChannelInvite, player->getID(), name);
  971. }
  972.  
  973. void ProtocolGame::parseChannelExclude(NetworkMessage &msg)
  974. {
  975. const std::string name = msg.getString();
  976. addGameTask(&Game::playerChannelExclude, player->getID(), name);
  977. }
  978.  
  979. void ProtocolGame::parseOpenChannel(NetworkMessage &msg)
  980. {
  981. uint16_t channelId = msg.get<uint16_t>();
  982. addGameTask(&Game::playerOpenChannel, player->getID(), channelId);
  983. }
  984.  
  985. void ProtocolGame::parseCloseChannel(NetworkMessage &msg)
  986. {
  987. uint16_t channelId = msg.get<uint16_t>();
  988. addGameTask(&Game::playerCloseChannel, player->getID(), channelId);
  989. }
  990.  
  991. void ProtocolGame::parseOpenPrivateChannel(NetworkMessage &msg)
  992. {
  993. const std::string receiver = msg.getString();
  994. addGameTask(&Game::playerOpenPrivateChannel, player->getID(), receiver);
  995. }
  996.  
  997. void ProtocolGame::parseAutoWalk(NetworkMessage &msg)
  998. {
  999. uint8_t numdirs = msg.getByte();
  1000. if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 8))
  1001. {
  1002. return;
  1003. }
  1004.  
  1005. msg.skipBytes(numdirs);
  1006.  
  1007. std::forward_list<Direction> path;
  1008. for (uint8_t i = 0; i < numdirs; ++i)
  1009. {
  1010. uint8_t rawdir = msg.getPreviousByte();
  1011. switch (rawdir)
  1012. {
  1013. case 1:
  1014. path.push_front(DIRECTION_EAST);
  1015. break;
  1016. case 2:
  1017. path.push_front(DIRECTION_NORTHEAST);
  1018. break;
  1019. case 3:
  1020. path.push_front(DIRECTION_NORTH);
  1021. break;
  1022. case 4:
  1023. path.push_front(DIRECTION_NORTHWEST);
  1024. break;
  1025. case 5:
  1026. path.push_front(DIRECTION_WEST);
  1027. break;
  1028. case 6:
  1029. path.push_front(DIRECTION_SOUTHWEST);
  1030. break;
  1031. case 7:
  1032. path.push_front(DIRECTION_SOUTH);
  1033. break;
  1034. case 8:
  1035. path.push_front(DIRECTION_SOUTHEAST);
  1036. break;
  1037. default:
  1038. break;
  1039. }
  1040. }
  1041.  
  1042. if (path.empty())
  1043. {
  1044. return;
  1045. }
  1046.  
  1047. addGameTask(&Game::playerAutoWalk, player->getID(), path);
  1048. }
  1049.  
  1050. void ProtocolGame::parseSetOutfit(NetworkMessage &msg)
  1051. {
  1052. uint16_t startBufferPosition = msg.getBufferPosition();
  1053. if (version >= 1200) {
  1054. Module *outfitModule = g_modules->getEventByRecvbyte(0xD3, false);
  1055. if (outfitModule)
  1056. {
  1057. outfitModule->executeOnRecvbyte(player, msg);
  1058. }
  1059. }
  1060.  
  1061. if (msg.getBufferPosition() == startBufferPosition)
  1062. {
  1063. uint8_t outfitType = 0;
  1064. if (version >= 1200)
  1065. outfitType = msg.getByte();
  1066. Outfit_t newOutfit;
  1067. newOutfit.lookType = msg.get<uint16_t>();
  1068. newOutfit.lookHead = std::min<uint8_t>(132, msg.getByte());
  1069. newOutfit.lookBody = std::min<uint8_t>(132, msg.getByte());
  1070. newOutfit.lookLegs = std::min<uint8_t>(132, msg.getByte());
  1071. newOutfit.lookFeet = std::min<uint8_t>(132, msg.getByte());
  1072. newOutfit.lookAddons = msg.getByte();
  1073. if (outfitType == 0)
  1074. {
  1075. newOutfit.lookMount = msg.get<uint16_t>();
  1076. if (version >= 1200){
  1077. newOutfit.lookMountHead = std::min<uint8_t>(132, msg.getByte());
  1078. newOutfit.lookMountBody = std::min<uint8_t>(132, msg.getByte());
  1079. newOutfit.lookMountLegs = std::min<uint8_t>(132, msg.getByte());
  1080. newOutfit.lookMountFeet = std::min<uint8_t>(132, msg.getByte());
  1081. newOutfit.lookFamiliarsType = msg.get<uint16_t>();
  1082. }
  1083. }
  1084. else if (outfitType == 1)
  1085. {
  1086. //This value probably has something to do with try outfit variable inside outfit window dialog
  1087. //if try outfit is set to 2 it expects uint32_t value after mounted and disable mounts from outfit window dialog
  1088. newOutfit.lookMount = 0;
  1089. msg.get<uint32_t>();
  1090. }
  1091. addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit);
  1092. }
  1093. }
  1094.  
  1095. void ProtocolGame::parseToggleMount(NetworkMessage &msg)
  1096. {
  1097. bool mount = msg.getByte() != 0;
  1098. addGameTask(&Game::playerToggleMount, player->getID(), mount);
  1099. }
  1100.  
  1101. void ProtocolGame::parseApplyImbuement(NetworkMessage &msg)
  1102. {
  1103. uint8_t slot = msg.getByte();
  1104. uint32_t imbuementId = msg.get<uint32_t>();
  1105. bool protectionCharm = msg.getByte() != 0x00;
  1106. addGameTask(&Game::playerApplyImbuement, player->getID(), imbuementId, slot, protectionCharm);
  1107. }
  1108.  
  1109. void ProtocolGame::parseClearingImbuement(NetworkMessage &msg)
  1110. {
  1111. uint8_t slot = msg.getByte();
  1112. addGameTask(&Game::playerClearingImbuement, player->getID(), slot);
  1113. }
  1114.  
  1115. void ProtocolGame::parseCloseImbuingWindow(NetworkMessage &)
  1116. {
  1117. addGameTask(&Game::playerCloseImbuingWindow, player->getID());
  1118. }
  1119.  
  1120. void ProtocolGame::parseUseItem(NetworkMessage &msg)
  1121. {
  1122. Position pos = msg.getPosition();
  1123. uint16_t spriteId = msg.get<uint16_t>();
  1124. uint8_t stackpos = msg.getByte();
  1125. uint8_t index = msg.getByte();
  1126. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItem, player->getID(), pos, stackpos, index, spriteId);
  1127. }
  1128.  
  1129. void ProtocolGame::parseUseItemEx(NetworkMessage &msg)
  1130. {
  1131. Position fromPos = msg.getPosition();
  1132. uint16_t fromSpriteId = msg.get<uint16_t>();
  1133. uint8_t fromStackPos = msg.getByte();
  1134. Position toPos = msg.getPosition();
  1135. uint16_t toSpriteId = msg.get<uint16_t>();
  1136. uint8_t toStackPos = msg.getByte();
  1137. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItemEx, player->getID(), fromPos, fromStackPos, fromSpriteId, toPos, toStackPos, toSpriteId);
  1138. }
  1139.  
  1140. void ProtocolGame::parseUseWithCreature(NetworkMessage &msg)
  1141. {
  1142. Position fromPos = msg.getPosition();
  1143. uint16_t spriteId = msg.get<uint16_t>();
  1144. uint8_t fromStackPos = msg.getByte();
  1145. uint32_t creatureId = msg.get<uint32_t>();
  1146. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseWithCreature, player->getID(), fromPos, fromStackPos, creatureId, spriteId);
  1147. }
  1148.  
  1149. void ProtocolGame::parseCloseContainer(NetworkMessage &msg)
  1150. {
  1151. uint8_t cid = msg.getByte();
  1152. addGameTask(&Game::playerCloseContainer, player->getID(), cid);
  1153. }
  1154.  
  1155. void ProtocolGame::parseUpArrowContainer(NetworkMessage &msg)
  1156. {
  1157. uint8_t cid = msg.getByte();
  1158. addGameTask(&Game::playerMoveUpContainer, player->getID(), cid);
  1159. }
  1160.  
  1161. void ProtocolGame::parseUpdateContainer(NetworkMessage &msg)
  1162. {
  1163. uint8_t cid = msg.getByte();
  1164. addGameTask(&Game::playerUpdateContainer, player->getID(), cid);
  1165. }
  1166.  
  1167. void ProtocolGame::parseTeleport(NetworkMessage &msg)
  1168. {
  1169. Position newPosition = msg.getPosition();
  1170. addGameTask(&Game::playerTeleport, player->getID(), newPosition);
  1171. }
  1172.  
  1173. void ProtocolGame::parseThrow(NetworkMessage &msg)
  1174. {
  1175. Position fromPos = msg.getPosition();
  1176. uint16_t spriteId = msg.get<uint16_t>();
  1177. uint8_t fromStackpos = msg.getByte();
  1178. Position toPos = msg.getPosition();
  1179. uint8_t count = msg.getByte();
  1180.  
  1181. if (toPos != fromPos)
  1182. {
  1183. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerMoveThing, player->getID(), fromPos, spriteId, fromStackpos, toPos, count);
  1184. }
  1185. }
  1186.  
  1187. void ProtocolGame::parseLookAt(NetworkMessage &msg)
  1188. {
  1189. Position pos = msg.getPosition();
  1190. msg.skipBytes(2); // spriteId
  1191. uint8_t stackpos = msg.getByte();
  1192. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookAt, player->getID(), pos, stackpos);
  1193. }
  1194.  
  1195. void ProtocolGame::parseLookInBattleList(NetworkMessage &msg)
  1196. {
  1197. uint32_t creatureId = msg.get<uint32_t>();
  1198. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInBattleList, player->getID(), creatureId);
  1199. }
  1200.  
  1201. void ProtocolGame::parseQuickLoot(NetworkMessage &msg)
  1202. {
  1203. Position pos = msg.getPosition();
  1204. uint16_t spriteId = msg.get<uint16_t>();
  1205. uint8_t stackpos = msg.getByte();
  1206. addGameTask(&Game::playerQuickLoot, player->getID(), pos, spriteId, stackpos, nullptr);
  1207. }
  1208.  
  1209. void ProtocolGame::parseLootContainer(NetworkMessage &msg)
  1210. {
  1211. uint8_t action = msg.getByte();
  1212. if (action == 0)
  1213. {
  1214. ObjectCategory_t category = (ObjectCategory_t)msg.getByte();
  1215. Position pos = msg.getPosition();
  1216. uint16_t spriteId = msg.get<uint16_t>();
  1217. uint8_t stackpos = msg.getByte();
  1218. addGameTask(&Game::playerSetLootContainer, player->getID(), category, pos, spriteId, stackpos);
  1219. }
  1220. else if (action == 1)
  1221. {
  1222. ObjectCategory_t category = (ObjectCategory_t)msg.getByte();
  1223. addGameTask(&Game::playerClearLootContainer, player->getID(), category);
  1224. }
  1225. else if (action == 2)
  1226. {
  1227. ObjectCategory_t category = (ObjectCategory_t)msg.getByte();
  1228. addGameTask(&Game::playerOpenLootContainer, player->getID(), category);
  1229. }
  1230. else if (action == 3)
  1231. {
  1232. bool useMainAsFallback = msg.getByte() == 1;
  1233. addGameTask(&Game::playerSetQuickLootFallback, player->getID(), useMainAsFallback);
  1234. }
  1235. }
  1236.  
  1237. void ProtocolGame::parseQuickLootBlackWhitelist(NetworkMessage &msg)
  1238. {
  1239. QuickLootFilter_t filter = (QuickLootFilter_t)msg.getByte();
  1240. std::vector<uint16_t> listedItems;
  1241.  
  1242. uint16_t size = msg.get<uint16_t>();
  1243. listedItems.reserve(size);
  1244.  
  1245. for (int i = 0; i < size; i++)
  1246. {
  1247. listedItems.push_back(msg.get<uint16_t>());
  1248. }
  1249.  
  1250. addGameTask(&Game::playerQuickLootBlackWhitelist, player->getID(), filter, listedItems);
  1251. }
  1252.  
  1253. void ProtocolGame::parseRequestLockItems()
  1254. {
  1255. addGameTask(&Game::playerRequestLockFind, player->getID());
  1256. }
  1257.  
  1258. void ProtocolGame::parseSay(NetworkMessage &msg)
  1259. {
  1260. std::string receiver;
  1261. uint16_t channelId;
  1262.  
  1263. SpeakClasses type = static_cast<SpeakClasses>(msg.getByte());
  1264. switch (type)
  1265. {
  1266. case TALKTYPE_PRIVATE_TO:
  1267. case TALKTYPE_PRIVATE_RED_TO:
  1268. receiver = msg.getString();
  1269. channelId = 0;
  1270. break;
  1271.  
  1272. case TALKTYPE_CHANNEL_Y:
  1273. case TALKTYPE_CHANNEL_R1:
  1274. channelId = msg.get<uint16_t>();
  1275. break;
  1276.  
  1277. default:
  1278. channelId = 0;
  1279. break;
  1280. }
  1281.  
  1282. const std::string text = msg.getString();
  1283. if (text.length() > 255)
  1284. {
  1285. return;
  1286. }
  1287.  
  1288. addGameTask(&Game::playerSay, player->getID(), channelId, type, receiver, text);
  1289. }
  1290.  
  1291. void ProtocolGame::parseFightModes(NetworkMessage &msg)
  1292. {
  1293. uint8_t rawFightMode = msg.getByte(); // 1 - offensive, 2 - balanced, 3 - defensive
  1294. uint8_t rawChaseMode = msg.getByte(); // 0 - stand while fightning, 1 - chase opponent
  1295. uint8_t rawSecureMode = msg.getByte(); // 0 - can't attack unmarked, 1 - can attack unmarked
  1296. // uint8_t rawPvpMode = msg.getByte(); // pvp mode introduced in 10.0
  1297.  
  1298. fightMode_t fightMode;
  1299. if (rawFightMode == 1)
  1300. {
  1301. fightMode = FIGHTMODE_ATTACK;
  1302. }
  1303. else if (rawFightMode == 2)
  1304. {
  1305. fightMode = FIGHTMODE_BALANCED;
  1306. }
  1307. else
  1308. {
  1309. fightMode = FIGHTMODE_DEFENSE;
  1310. }
  1311.  
  1312. addGameTask(&Game::playerSetFightModes, player->getID(), fightMode, rawChaseMode != 0, rawSecureMode != 0);
  1313. }
  1314.  
  1315. void ProtocolGame::parseAttack(NetworkMessage &msg)
  1316. {
  1317. uint32_t creatureId = msg.get<uint32_t>();
  1318. // msg.get<uint32_t>(); creatureId (same as above)
  1319. addGameTask(&Game::playerSetAttackedCreature, player->getID(), creatureId);
  1320. }
  1321.  
  1322. void ProtocolGame::parseFollow(NetworkMessage &msg)
  1323. {
  1324. uint32_t creatureId = msg.get<uint32_t>();
  1325. // msg.get<uint32_t>(); creatureId (same as above)
  1326. addGameTask(&Game::playerFollowCreature, player->getID(), creatureId);
  1327. }
  1328.  
  1329. void ProtocolGame::parseTextWindow(NetworkMessage &msg)
  1330. {
  1331. uint32_t windowTextId = msg.get<uint32_t>();
  1332. const std::string newText = msg.getString();
  1333. addGameTask(&Game::playerWriteItem, player->getID(), windowTextId, newText);
  1334. }
  1335.  
  1336. void ProtocolGame::parseHouseWindow(NetworkMessage &msg)
  1337. {
  1338. uint8_t doorId = msg.getByte();
  1339. uint32_t id = msg.get<uint32_t>();
  1340. const std::string text = msg.getString();
  1341. addGameTask(&Game::playerUpdateHouseWindow, player->getID(), doorId, id, text);
  1342. }
  1343.  
  1344. void ProtocolGame::parseLookInShop(NetworkMessage &msg)
  1345. {
  1346. uint16_t id = msg.get<uint16_t>();
  1347. uint8_t count = msg.getByte();
  1348. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInShop, player->getID(), id, count);
  1349. }
  1350.  
  1351. void ProtocolGame::parsePlayerPurchase(NetworkMessage &msg)
  1352. {
  1353. uint16_t id = msg.get<uint16_t>();
  1354. uint8_t count = msg.getByte();
  1355. uint8_t amount = msg.getByte();
  1356. bool ignoreCap = msg.getByte() != 0;
  1357. bool inBackpacks = msg.getByte() != 0;
  1358. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerPurchaseItem, player->getID(), id, count, amount, ignoreCap, inBackpacks);
  1359. }
  1360.  
  1361. void ProtocolGame::parsePlayerSale(NetworkMessage &msg)
  1362. {
  1363. uint16_t id = msg.get<uint16_t>();
  1364. uint8_t count = msg.getByte();
  1365. uint8_t amount = msg.getByte();
  1366. bool ignoreEquipped = msg.getByte() != 0;
  1367. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerSellItem, player->getID(), id, count, amount, ignoreEquipped);
  1368. }
  1369.  
  1370. void ProtocolGame::parseRequestTrade(NetworkMessage &msg)
  1371. {
  1372. Position pos = msg.getPosition();
  1373. uint16_t spriteId = msg.get<uint16_t>();
  1374. uint8_t stackpos = msg.getByte();
  1375. uint32_t playerId = msg.get<uint32_t>();
  1376. addGameTask(&Game::playerRequestTrade, player->getID(), pos, stackpos, playerId, spriteId);
  1377. }
  1378.  
  1379. void ProtocolGame::parseLookInTrade(NetworkMessage &msg)
  1380. {
  1381. bool counterOffer = (msg.getByte() == 0x01);
  1382. uint8_t index = msg.getByte();
  1383. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInTrade, player->getID(), counterOffer, index);
  1384. }
  1385.  
  1386. void ProtocolGame::parseAddVip(NetworkMessage &msg)
  1387. {
  1388. const std::string name = msg.getString();
  1389. addGameTask(&Game::playerRequestAddVip, player->getID(), name);
  1390. }
  1391.  
  1392. void ProtocolGame::parseRemoveVip(NetworkMessage &msg)
  1393. {
  1394. uint32_t guid = msg.get<uint32_t>();
  1395. addGameTask(&Game::playerRequestRemoveVip, player->getID(), guid);
  1396. }
  1397.  
  1398. void ProtocolGame::parseEditVip(NetworkMessage &msg)
  1399. {
  1400. uint32_t guid = msg.get<uint32_t>();
  1401. const std::string description = msg.getString();
  1402. uint32_t icon = std::min<uint32_t>(10, msg.get<uint32_t>()); // 10 is max icon in 9.63
  1403. bool notify = msg.getByte() != 0;
  1404. addGameTask(&Game::playerRequestEditVip, player->getID(), guid, description, icon, notify);
  1405. }
  1406.  
  1407. void ProtocolGame::parseRotateItem(NetworkMessage &msg)
  1408. {
  1409. Position pos = msg.getPosition();
  1410. uint16_t spriteId = msg.get<uint16_t>();
  1411. uint8_t stackpos = msg.getByte();
  1412. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerRotateItem, player->getID(), pos, stackpos, spriteId);
  1413. }
  1414.  
  1415. void ProtocolGame::parseWrapableItem(NetworkMessage &msg)
  1416. {
  1417. Position pos = msg.getPosition();
  1418. uint16_t spriteId = msg.get<uint16_t>();
  1419. uint8_t stackpos = msg.getByte();
  1420. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerWrapableItem, player->getID(), pos, stackpos, spriteId);
  1421. }
  1422.  
  1423. void ProtocolGame::parseInspectionObject(NetworkMessage &msg)
  1424. {
  1425. uint8_t inspectionType = msg.getByte();
  1426. if (inspectionType == INSPECT_NORMALOBJECT)
  1427. {
  1428. Position pos = msg.getPosition();
  1429. g_game.playerInspectItem(player, pos);
  1430. }
  1431. else if (inspectionType == INSPECT_NPCTRADE || inspectionType == INSPECT_CYCLOPEDIA)
  1432. {
  1433. uint16_t itemId = msg.get<uint16_t>();
  1434. uint16_t itemCount = msg.getByte();
  1435. g_game.playerInspectItem(player, itemId, itemCount, (inspectionType == INSPECT_CYCLOPEDIA));
  1436. }
  1437. }
  1438.  
  1439. void ProtocolGame::sendItemInspection(uint16_t itemId, uint8_t itemCount, const Item *item, bool cyclopedia)
  1440. {
  1441. NetworkMessage msg;
  1442. msg.addByte(0x76);
  1443. msg.addByte(0x00);
  1444. msg.addByte(cyclopedia ? 0x01 : 0x00);
  1445. msg.addByte(0x01);
  1446.  
  1447. const ItemType &it = Item::items.getItemIdByClientId(itemId);
  1448.  
  1449. if (item)
  1450. {
  1451. msg.addString(item->getName());
  1452. AddItem(msg, item);
  1453. }
  1454. else
  1455. {
  1456. msg.addString(it.name);
  1457. AddItem(msg, it.id, itemCount);
  1458. }
  1459. msg.addByte(0);
  1460.  
  1461. auto descriptions = Item::getDescriptions(it, item);
  1462. msg.addByte(descriptions.size());
  1463. for (const auto &description : descriptions)
  1464. {
  1465. msg.addString(description.first);
  1466. msg.addString(description.second);
  1467. }
  1468. writeToOutputBuffer(msg);
  1469. }
  1470.  
  1471. void ProtocolGame::parseCyclopediaCharacterInfo(NetworkMessage &msg)
  1472. {
  1473. uint32_t characterID;
  1474. CyclopediaCharacterInfoType_t characterInfoType;
  1475. characterID = msg.get<uint32_t>();
  1476. characterInfoType = static_cast<CyclopediaCharacterInfoType_t>(msg.getByte());
  1477. uint16_t entriesPerPage = 0, page = 0;
  1478. if (characterInfoType == CYCLOPEDIA_CHARACTERINFO_RECENTDEATHS || characterInfoType == CYCLOPEDIA_CHARACTERINFO_RECENTPVPKILLS)
  1479. {
  1480. entriesPerPage = std::min<uint16_t>(30, std::max<uint16_t>(5, msg.get<uint16_t>()));
  1481. page = std::max<uint16_t>(1, msg.get<uint16_t>());
  1482. }
  1483. if (characterID == 0)
  1484. {
  1485. characterID = player->getGUID();
  1486. }
  1487. g_game.playerCyclopediaCharacterInfo(player, characterID, characterInfoType, entriesPerPage, page);
  1488. }
  1489.  
  1490. void ProtocolGame::parseHighscores(NetworkMessage &msg)
  1491. {
  1492. HighscoreType_t type = static_cast<HighscoreType_t>(msg.getByte());
  1493. uint8_t category = msg.getByte();
  1494. uint32_t vocation = msg.get<uint32_t>();
  1495. uint16_t page = 1;
  1496. const std::string worldName = msg.getString();
  1497. msg.getByte(); // Game World Category
  1498. msg.getByte(); // BattlEye World Type
  1499. if (type == HIGHSCORE_GETENTRIES)
  1500. {
  1501. page = std::max<uint16_t>(1, msg.get<uint16_t>());
  1502. }
  1503. uint8_t entriesPerPage = std::min<uint8_t>(30, std::max<uint8_t>(5, msg.getByte()));
  1504. g_game.playerHighscores(player, type, category, vocation, worldName, page, entriesPerPage);
  1505. }
  1506.  
  1507. void ProtocolGame::sendHighscoresNoData()
  1508. {
  1509. NetworkMessage msg;
  1510. msg.addByte(0xB1);
  1511. msg.addByte(0x01); // No data available
  1512. writeToOutputBuffer(msg);
  1513. }
  1514.  
  1515. void ProtocolGame::sendHighscores(const std::vector<HighscoreCharacter> &characters, uint8_t categoryId, uint32_t vocationId, uint16_t page, uint16_t pages)
  1516. {
  1517. NetworkMessage msg;
  1518. msg.addByte(0xB1);
  1519. msg.addByte(0x00); // No data available
  1520.  
  1521. msg.addByte(1); // Worlds
  1522. msg.addString(g_config.getString(ConfigManager::SERVER_NAME)); // First World
  1523. msg.addString(g_config.getString(ConfigManager::SERVER_NAME)); // Selected World
  1524.  
  1525. msg.addByte(0); // Game World Category: 0xFF(-1) - Selected World
  1526. msg.addByte(0); // BattlEye World Type
  1527.  
  1528. auto vocationPosition = msg.getBufferPosition();
  1529. uint8_t vocations = 1;
  1530.  
  1531. msg.skipBytes(1); // Vocation Count
  1532. msg.add<uint32_t>(0xFFFFFFFF); // All Vocations - hardcoded
  1533. msg.addString("(all)"); // All Vocations - hardcoded
  1534.  
  1535. uint32_t selectedVocation = 0xFFFFFFFF;
  1536. const auto &vocationsMap = g_vocations.getVocations();
  1537. for (const auto &it : vocationsMap)
  1538. {
  1539. const Vocation &vocation = it.second;
  1540. if (vocation.getFromVocation() == static_cast<uint32_t>(vocation.getId()))
  1541. {
  1542. msg.add<uint32_t>(vocation.getFromVocation()); // Vocation Id
  1543. msg.addString(vocation.getVocName()); // Vocation Name
  1544. ++vocations;
  1545. if (vocation.getFromVocation() == vocationId)
  1546. {
  1547. selectedVocation = vocationId;
  1548. }
  1549. }
  1550. }
  1551. msg.add<uint32_t>(selectedVocation); // Selected Vocation
  1552.  
  1553. HighscoreCategory highscoreCategories[] =
  1554. {
  1555. {"Experience Points", HIGHSCORE_CATEGORY_EXPERIENCE},
  1556. {"Fist Fighting", HIGHSCORE_CATEGORY_FIST_FIGHTING},
  1557. {"Club Fighting", HIGHSCORE_CATEGORY_CLUB_FIGHTING},
  1558. {"Sword Fighting", HIGHSCORE_CATEGORY_SWORD_FIGHTING},
  1559. {"Axe Fighting", HIGHSCORE_CATEGORY_AXE_FIGHTING},
  1560. {"Distance Fighting", HIGHSCORE_CATEGORY_DISTANCE_FIGHTING},
  1561. {"Shielding", HIGHSCORE_CATEGORY_SHIELDING},
  1562. {"Fishing", HIGHSCORE_CATEGORY_FISHING},
  1563. {"Magic Level", HIGHSCORE_CATEGORY_MAGIC_LEVEL}};
  1564.  
  1565. uint8_t selectedCategory = 0;
  1566. msg.addByte(sizeof(highscoreCategories) / sizeof(HighscoreCategory)); // Category Count
  1567. for (HighscoreCategory &category : highscoreCategories)
  1568. {
  1569. msg.addByte(category.id); // Category Id
  1570. msg.addString(category.name); // Category Name
  1571. if (category.id == categoryId)
  1572. {
  1573. selectedCategory = categoryId;
  1574. }
  1575. }
  1576. msg.addByte(selectedCategory); // Selected Category
  1577.  
  1578. msg.add<uint16_t>(page); // Current page
  1579. msg.add<uint16_t>(pages); // Pages
  1580.  
  1581. msg.addByte(characters.size()); // Character Count
  1582. for (const HighscoreCharacter &character : characters)
  1583. {
  1584. msg.add<uint32_t>(character.rank); // Rank
  1585. msg.addString(character.name); // Character Name
  1586. msg.addString(""); // Probably Character Title(not visible in window)
  1587. msg.addByte(character.vocation); // Vocation Id
  1588. msg.addString(g_config.getString(ConfigManager::SERVER_NAME)); // World
  1589. msg.add<uint16_t>(character.level); // Level
  1590. msg.addByte((player->getGUID() == character.id)); // Player Indicator Boolean
  1591. msg.add<uint64_t>(character.points); // Points
  1592. }
  1593.  
  1594. msg.addByte(0xFF); // ??
  1595. msg.addByte(0); // ??
  1596. msg.addByte(1); // ??
  1597. msg.add<uint32_t>(time(nullptr)); // Last Update
  1598.  
  1599. msg.setBufferPosition(vocationPosition);
  1600. msg.addByte(vocations);
  1601. writeToOutputBuffer(msg);
  1602. }
  1603.  
  1604. void ProtocolGame::parseTournamentLeaderboard(NetworkMessage &msg)
  1605. {
  1606. uint8_t ledaerboardType = msg.getByte();
  1607. if (ledaerboardType == 0)
  1608. {
  1609. const std::string worldName = msg.getString();
  1610. uint16_t currentPage = msg.get<uint16_t>();
  1611. (void)worldName;
  1612. (void)currentPage;
  1613. }
  1614. else if (ledaerboardType == 1)
  1615. {
  1616. const std::string worldName = msg.getString();
  1617. const std::string characterName = msg.getString();
  1618. (void)worldName;
  1619. (void)characterName;
  1620. }
  1621. uint8_t elementsPerPage = msg.getByte();
  1622. (void)elementsPerPage;
  1623.  
  1624. addGameTask(&Game::playerTournamentLeaderboard, player->getID(), ledaerboardType);
  1625. }
  1626.  
  1627. void ProtocolGame::parseRuleViolationReport(NetworkMessage &msg)
  1628. {
  1629. uint8_t reportType = msg.getByte();
  1630. uint8_t reportReason = msg.getByte();
  1631. const std::string &targetName = msg.getString();
  1632. const std::string &comment = msg.getString();
  1633. std::string translation;
  1634. if (reportType == REPORT_TYPE_NAME)
  1635. {
  1636. translation = msg.getString();
  1637. }
  1638. else if (reportType == REPORT_TYPE_STATEMENT)
  1639. {
  1640. translation = msg.getString();
  1641. msg.get<uint32_t>(); // statement id, used to get whatever player have said, we don't log that.
  1642. }
  1643.  
  1644. addGameTask(&Game::playerReportRuleViolationReport, player->getID(), targetName, reportType, reportReason, comment, translation);
  1645. }
  1646.  
  1647. void ProtocolGame::parseBestiarysendRaces()
  1648. {
  1649. IOBestiary g_bestiary;
  1650. NetworkMessage msg;
  1651. msg.addByte(0xd5);
  1652. msg.add<uint16_t>(BESTY_RACE_LAST);
  1653. std::map<uint16_t, std::string> mtype_list = g_game.getBestiaryList();
  1654. for (uint8_t i = BESTY_RACE_FIRST; i <= BESTY_RACE_LAST; i++)
  1655. {
  1656. std::string BestClass = "";
  1657. uint16_t count = 0;
  1658. for (auto rit : mtype_list)
  1659. {
  1660. MonsterType *mtype = g_monsters.getMonsterType(rit.second);
  1661. if (!mtype)
  1662. {
  1663. return;
  1664. }
  1665. if (mtype->info.bestiaryRace == static_cast<BestiaryType_t>(i))
  1666. {
  1667. count += 1;
  1668. BestClass = mtype->info.bestiaryClass;
  1669. }
  1670. }
  1671. msg.addString(BestClass);
  1672. msg.add<uint16_t>(count);
  1673. uint16_t unlockedCount = g_bestiary.getBestiaryRaceUnlocked(player, static_cast<BestiaryType_t>(i));
  1674. msg.add<uint16_t>(unlockedCount);
  1675. }
  1676. writeToOutputBuffer(msg);
  1677.  
  1678. player->BestiarysendCharms();
  1679. }
  1680.  
  1681. void ProtocolGame::sendBestiaryEntryChanged(uint16_t raceid)
  1682. {
  1683. if (version < 1200)
  1684. return;
  1685. NetworkMessage msg;
  1686. msg.addByte(0xd9);
  1687. msg.add<uint16_t>(raceid);
  1688. writeToOutputBuffer(msg);
  1689. }
  1690.  
  1691. void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg)
  1692. {
  1693. IOBestiary g_bestiary;
  1694. uint16_t raceId = msg.get<uint16_t>();
  1695. std::string Class = "";
  1696. MonsterType *mtype = nullptr;
  1697. std::map<uint16_t, std::string> mtype_list = g_game.getBestiaryList();
  1698.  
  1699. auto ait = mtype_list.find(raceId);
  1700. if (ait != mtype_list.end())
  1701. {
  1702. MonsterType *mType = g_monsters.getMonsterType(ait->second);
  1703. if (mType)
  1704. {
  1705. Class = mType->info.bestiaryClass;
  1706. mtype = mType;
  1707. }
  1708. }
  1709.  
  1710. if (!mtype)
  1711. {
  1712. std::cout << "> [Bestiary]: monstertype was not found" << std::endl;
  1713. return;
  1714. }
  1715.  
  1716. uint32_t killCounter = player->getBestiaryKillCount(raceId);
  1717. uint8_t currentLevel = g_bestiary.getKillStatus(mtype, killCounter);
  1718.  
  1719. NetworkMessage newmsg;
  1720. newmsg.addByte(0xd7);
  1721. newmsg.add<uint16_t>(raceId);
  1722. newmsg.addString(Class);
  1723.  
  1724. newmsg.addByte(currentLevel);
  1725. newmsg.add<uint32_t>(killCounter);
  1726.  
  1727. newmsg.add<uint16_t>(mtype->info.bestiaryFirstUnlock);
  1728. newmsg.add<uint16_t>(mtype->info.bestiarySecondUnlock);
  1729. newmsg.add<uint16_t>(mtype->info.bestiaryToUnlock);
  1730.  
  1731. newmsg.addByte(mtype->info.bestiaryStars);
  1732. newmsg.addByte(mtype->info.bestiaryOccurrence);
  1733.  
  1734. std::vector<LootBlock> lootList = mtype->info.lootItems;
  1735. newmsg.addByte(lootList.size());
  1736. for (LootBlock loot : lootList)
  1737. {
  1738. newmsg.addItemId(currentLevel > 1 ? loot.id : 0);
  1739. int8_t difficult = g_bestiary.calculateDifficult(loot.chance);
  1740. newmsg.addByte(difficult);
  1741. newmsg.addByte(0); // 1 if special event - 0 if regular loot (?)
  1742. if (currentLevel > 1)
  1743. {
  1744. newmsg.addString(loot.name);
  1745. newmsg.addByte(loot.countmax > 0 ? 0x1 : 0x0);
  1746. }
  1747. }
  1748.  
  1749. if (currentLevel > 1)
  1750. {
  1751. newmsg.add<uint16_t>(mtype->info.bestiaryCharmsPoints);
  1752. int8_t attackmode = 0;
  1753. if (!mtype->info.isHostile)
  1754. {
  1755. attackmode = 2;
  1756. }
  1757. else if (mtype->info.targetDistance)
  1758. {
  1759. attackmode = 1;
  1760. }
  1761.  
  1762. newmsg.addByte(attackmode);
  1763. newmsg.addByte(0x2);
  1764. newmsg.add<uint32_t>(mtype->info.healthMax);
  1765. newmsg.add<uint32_t>(mtype->info.experience);
  1766. newmsg.add<uint16_t>(mtype->info.baseSpeed);
  1767. newmsg.add<uint16_t>(mtype->info.armor);
  1768. }
  1769.  
  1770. if (currentLevel > 2)
  1771. {
  1772. std::map<uint8_t, int16_t> elements = g_bestiary.getMonsterElements(mtype);
  1773.  
  1774. newmsg.addByte(elements.size());
  1775. for (auto it = std::begin(elements), end = std::end(elements); it != end; it++)
  1776. {
  1777. newmsg.addByte(it->first);
  1778. newmsg.add<uint16_t>(it->second);
  1779. }
  1780.  
  1781. newmsg.add<uint16_t>(1);
  1782. newmsg.addString(mtype->info.bestiaryLocations);
  1783. }
  1784.  
  1785. if (currentLevel > 3)
  1786. {
  1787. charmRune_t mType_c = g_bestiary.getCharmFromTarget(player, mtype);
  1788. if (mType_c != CHARM_NONE)
  1789. {
  1790. newmsg.addByte(1);
  1791. newmsg.addByte(mType_c);
  1792. newmsg.add<uint32_t>(player->getLevel() * 100);
  1793. }
  1794. else
  1795. {
  1796. newmsg.addByte(0);
  1797. newmsg.addByte(1);
  1798. }
  1799. }
  1800.  
  1801. writeToOutputBuffer(newmsg);
  1802. }
  1803.  
  1804. void ProtocolGame::addBestiaryTrackerList(NetworkMessage &msg)
  1805. {
  1806. uint16_t thisrace = msg.get<uint16_t>();
  1807. std::map<uint16_t, std::string> mtype_list = g_game.getBestiaryList();
  1808. auto it = mtype_list.find(thisrace);
  1809. if (it != mtype_list.end())
  1810. {
  1811. MonsterType *mtype = g_monsters.getMonsterType(it->second);
  1812. if (mtype)
  1813. {
  1814. player->addBestiaryTrackerList(mtype);
  1815. }
  1816. }
  1817. }
  1818.  
  1819. void ProtocolGame::sendTeamFinderList()
  1820. {
  1821. if (!player)
  1822. return;
  1823.  
  1824. NetworkMessage msg;
  1825. msg.addByte(0x2D);
  1826. msg.addByte(0x00); // Bool value, with 'true' the player exceed packets for second.
  1827. std::map<uint32_t, TeamFinder*> teamFinder = g_game.getTeamFinderList();
  1828. msg.add<uint16_t>(teamFinder.size());
  1829. for (auto it : teamFinder) {
  1830. Player* leader = g_game.getPlayerByGUID(it.first);
  1831. if (!leader)
  1832. return;
  1833.  
  1834. TeamFinder* teamAssemble = it.second;
  1835. if (!teamAssemble)
  1836. return;
  1837.  
  1838. uint8_t status = 0;
  1839. uint16_t membersSize = 0;
  1840. msg.add<uint32_t>(leader->getGUID());
  1841. msg.addString(leader->getName());
  1842. msg.add<uint16_t>(teamAssemble->minLevel);
  1843. msg.add<uint16_t>(teamAssemble->maxLevel);
  1844. msg.addByte(teamAssemble->vocationIDs);
  1845. msg.add<uint16_t>(teamAssemble->teamSlots);
  1846. for (auto itt : teamAssemble->membersMap) {
  1847. Player* member = g_game.getPlayerByGUID(it.first);
  1848. if (member) {
  1849. if (itt.first == player->getGUID())
  1850. status = itt.second;
  1851.  
  1852. if (itt.second == 3)
  1853. membersSize += 1;
  1854. }
  1855. }
  1856. msg.add<uint16_t>(std::max<uint16_t>((teamAssemble->teamSlots - teamAssemble->freeSlots), membersSize));
  1857. // The leader does not count on this math, he is included inside the 'freeSlots'.
  1858. msg.add<uint32_t>(teamAssemble->timestamp);
  1859. msg.addByte(teamAssemble->teamType);
  1860.  
  1861. switch (teamAssemble->teamType) {
  1862. case 1: {
  1863. msg.add<uint16_t>(teamAssemble->bossID);
  1864. break;
  1865. }
  1866. case 2: {
  1867. msg.add<uint16_t>(teamAssemble->hunt_type);
  1868. msg.add<uint16_t>(teamAssemble->hunt_area);
  1869. break;
  1870. }
  1871. case 3: {
  1872. msg.add<uint16_t>(teamAssemble->questID);
  1873. break;
  1874. }
  1875.  
  1876. default:
  1877. break;
  1878. }
  1879.  
  1880. msg.addByte(status);
  1881. }
  1882. writeToOutputBuffer(msg);
  1883. }
  1884.  
  1885. void ProtocolGame::sendLeaderTeamFinder(bool reset)
  1886. {
  1887. if (!player)
  1888. return;
  1889.  
  1890. TeamFinder* teamAssemble = nullptr;
  1891. std::map<uint32_t, TeamFinder*> teamFinder = g_game.getTeamFinderList();
  1892. auto it = teamFinder.find(player->getGUID());
  1893. if (it != teamFinder.end()) {
  1894. teamAssemble = it->second;
  1895. }
  1896.  
  1897. if (!teamAssemble)
  1898. return;
  1899.  
  1900. NetworkMessage msg;
  1901. msg.addByte(0x2C);
  1902. msg.addByte(reset ? 1 : 0);
  1903. if (reset) {
  1904. g_game.removeTeamFinderListed(player->getGUID());
  1905. return;
  1906. }
  1907. msg.add<uint16_t>(teamAssemble->minLevel);
  1908. msg.add<uint16_t>(teamAssemble->maxLevel);
  1909. msg.addByte(teamAssemble->vocationIDs);
  1910. msg.add<uint16_t>(teamAssemble->teamSlots);
  1911. msg.add<uint16_t>(teamAssemble->freeSlots);
  1912. msg.add<uint32_t>(teamAssemble->timestamp);
  1913. msg.addByte(teamAssemble->teamType);
  1914.  
  1915. switch (teamAssemble->teamType) {
  1916. case 1: {
  1917. msg.add<uint16_t>(teamAssemble->bossID);
  1918. break;
  1919. }
  1920. case 2: {
  1921. msg.add<uint16_t>(teamAssemble->hunt_type);
  1922. msg.add<uint16_t>(teamAssemble->hunt_area);
  1923. break;
  1924. }
  1925. case 3: {
  1926. msg.add<uint16_t>(teamAssemble->questID);
  1927. break;
  1928. }
  1929.  
  1930. default:
  1931. break;
  1932. }
  1933.  
  1934. uint16_t membersSize = 1;
  1935. for (auto memberPair : teamAssemble->membersMap) {
  1936. Player* member = g_game.getPlayerByGUID(memberPair.first);
  1937. if (member) {
  1938. membersSize += 1;
  1939. }
  1940. }
  1941.  
  1942. msg.add<uint16_t>(membersSize);
  1943. Player* leader = g_game.getPlayerByGUID(teamAssemble->leaderGuid);
  1944. if (!leader)
  1945. return;
  1946.  
  1947. msg.add<uint32_t>(leader->getGUID());
  1948. msg.addString(leader->getName());
  1949. msg.add<uint16_t>(leader->getLevel());
  1950. msg.addByte(leader->getVocation()->getClientId());
  1951. msg.addByte(3);
  1952.  
  1953. for (auto memberPair : teamAssemble->membersMap) {
  1954. Player* member = g_game.getPlayerByGUID(memberPair.first);
  1955. if (!member) {
  1956. continue;
  1957. }
  1958. msg.add<uint32_t>(member->getGUID());
  1959. msg.addString(member->getName());
  1960. msg.add<uint16_t>(member->getLevel());
  1961. msg.addByte(member->getVocation()->getClientId());
  1962. msg.addByte(memberPair.second);
  1963. }
  1964.  
  1965. writeToOutputBuffer(msg);
  1966. }
  1967.  
  1968. void ProtocolGame::createLeaderTeamFinder(NetworkMessage &msg)
  1969. {
  1970. if (!player)
  1971. return;
  1972.  
  1973. std::map<uint32_t, uint8_t> members;
  1974. std::map<uint32_t, TeamFinder*> teamFinder = g_game.getTeamFinderList();
  1975. TeamFinder* teamAssemble = nullptr;
  1976. auto it = teamFinder.find(player->getGUID());
  1977. if (it != teamFinder.end()) {
  1978. members = it->second->membersMap;
  1979. teamAssemble = it->second;
  1980. }
  1981.  
  1982. if (!teamAssemble)
  1983. teamAssemble = new TeamFinder();
  1984.  
  1985. teamAssemble->minLevel = msg.get<uint16_t>();
  1986. teamAssemble->maxLevel = msg.get<uint16_t>();
  1987. teamAssemble->vocationIDs = msg.getByte();
  1988. teamAssemble->teamSlots = msg.get<uint16_t>();
  1989. teamAssemble->freeSlots = msg.get<uint16_t>();
  1990. teamAssemble->partyBool = (msg.getByte() == 1);
  1991. teamAssemble->timestamp = msg.get<uint32_t>();
  1992. teamAssemble->teamType = msg.getByte();
  1993.  
  1994. uint16_t bossID = 0;
  1995. uint16_t huntType1 = 0;
  1996. uint16_t huntType2 = 0;
  1997. uint16_t questID = 0;
  1998.  
  1999. switch (teamAssemble->teamType) {
  2000. case 1: {
  2001. bossID = msg.get<uint16_t>();
  2002. break;
  2003. }
  2004. case 2: {
  2005. huntType1 = msg.get<uint16_t>();
  2006. huntType2 = msg.get<uint16_t>();
  2007. break;
  2008. }
  2009.  
  2010. case 3: {
  2011. questID = msg.get<uint16_t>();
  2012. break;
  2013. }
  2014.  
  2015. default:
  2016. break;
  2017. }
  2018.  
  2019. teamAssemble->bossID = bossID;
  2020. teamAssemble->hunt_type = huntType1;
  2021. teamAssemble->hunt_area = huntType2;
  2022. teamAssemble->questID = questID;
  2023. teamAssemble->leaderGuid = player->getGUID();
  2024.  
  2025. if (teamAssemble->partyBool && player->getParty()) {
  2026. for (Player* member : player->getParty()->getMembers()) {
  2027. if (member && member->getGUID() != player->getGUID()) {
  2028. members.insert({member->getGUID(), 3});
  2029. }
  2030. }
  2031. if (player->getParty()->getLeader()->getGUID() != player->getGUID()) {
  2032. members.insert({player->getParty()->getLeader()->getGUID(), 3});
  2033. }
  2034. }
  2035. teamAssemble->membersMap = members;
  2036. g_game.registerTeamFinderAssemble(player->getGUID(), teamAssemble);
  2037. }
  2038.  
  2039. void ProtocolGame::parseLeaderFinderWindow(NetworkMessage &msg)
  2040. {
  2041. if (!player)
  2042. return;
  2043.  
  2044. uint8_t action = msg.getByte();
  2045. switch (action) {
  2046. case 0: {
  2047. player->sendLeaderTeamFinder(false);
  2048. break;
  2049. }
  2050. case 1: {
  2051. player->sendLeaderTeamFinder(true);
  2052. break;
  2053. }
  2054. case 2: {
  2055. uint32_t memberID = msg.get<uint32_t>();
  2056. Player* member = g_game.getPlayerByGUID(memberID);
  2057. if (!member)
  2058. return;
  2059.  
  2060. std::map<uint32_t, TeamFinder*> teamFinder = g_game.getTeamFinderList();
  2061. TeamFinder* teamAssemble = nullptr;
  2062. auto it = teamFinder.find(player->getGUID());
  2063. if (it != teamFinder.end()) {
  2064. teamAssemble = it->second;
  2065. }
  2066.  
  2067. if (!teamAssemble)
  2068. return;
  2069.  
  2070. uint8_t memberStatus = msg.getByte();
  2071. for (auto& memberPair : teamAssemble->membersMap) {
  2072. if (memberPair.first == memberID) {
  2073. memberPair.second = memberStatus;
  2074. }
  2075. }
  2076.  
  2077. switch (memberStatus) {
  2078. case 2: {
  2079. member->sendTextMessage(MESSAGE_STATUS, "You are invited to a new team.");
  2080. break;
  2081. }
  2082. case 3: {
  2083. member->sendTextMessage(MESSAGE_STATUS, "Your team finder request was accepted.");
  2084. break;
  2085. }
  2086. case 4: {
  2087. member->sendTextMessage(MESSAGE_STATUS, "Your team finder request was denied.");
  2088. break;
  2089. }
  2090.  
  2091. default:
  2092. break;
  2093. }
  2094. player->sendLeaderTeamFinder(false);
  2095. break;
  2096. }
  2097. case 3: {
  2098. player->createLeaderTeamFinder(msg);
  2099. player->sendLeaderTeamFinder(false);
  2100. break;
  2101. }
  2102.  
  2103. default:
  2104. break;
  2105. }
  2106. }
  2107.  
  2108. void ProtocolGame::parseMemberFinderWindow(NetworkMessage &msg)
  2109. {
  2110. if (!player)
  2111. return;
  2112.  
  2113. uint8_t action = msg.getByte();
  2114. if (action == 0) {
  2115. player->sendTeamFinderList();
  2116. } else {
  2117. uint32_t leaderID = msg.get<uint32_t>();
  2118. Player* leader = g_game.getPlayerByGUID(leaderID);
  2119. if (!leader)
  2120. return;
  2121.  
  2122. std::map<uint32_t, TeamFinder*> teamFinder = g_game.getTeamFinderList();
  2123. TeamFinder* teamAssemble = nullptr;
  2124. auto it = teamFinder.find(leaderID);
  2125. if (it != teamFinder.end()) {
  2126. teamAssemble = it->second;
  2127. }
  2128.  
  2129. if (!teamAssemble)
  2130. return;
  2131.  
  2132. if (action == 1) {
  2133. leader->sendTextMessage(MESSAGE_STATUS, "There is a new request to join your team.");
  2134. teamAssemble->membersMap.insert({player->getGUID(), 1});
  2135. } else {
  2136. for (auto itt = teamAssemble->membersMap.begin(), end = teamAssemble->membersMap.end(); itt != end; ++itt) {
  2137. if (itt->first == player->getGUID()) {
  2138. teamAssemble->membersMap.erase(itt);
  2139. break;
  2140. }
  2141. }
  2142. }
  2143. player->sendTeamFinderList();
  2144. }
  2145. }
  2146.  
  2147. void ProtocolGame::parseSendBuyCharmRune(NetworkMessage &msg)
  2148. {
  2149. IOBestiary g_bestiary;
  2150. charmRune_t runeID = static_cast<charmRune_t>(msg.getByte());
  2151. uint8_t action = msg.getByte();
  2152. uint16_t raceid = msg.get<uint16_t>();
  2153. g_bestiary.sendBuyCharmRune(player, runeID, action, raceid);
  2154. }
  2155.  
  2156. void ProtocolGame::refreshBestiaryTracker(std::list<MonsterType *> trackerList)
  2157. {
  2158. NetworkMessage msg;
  2159. IOBestiary g_bestiary;
  2160. msg.addByte(0xB9);
  2161. msg.addByte(trackerList.size());
  2162. for (MonsterType *mtype : trackerList)
  2163. {
  2164. uint32_t killAmount = player->getBestiaryKillCount(mtype->info.raceid);
  2165. msg.add<uint16_t>(mtype->info.raceid);
  2166. msg.add<uint32_t>(killAmount);
  2167. msg.add<uint16_t>(mtype->info.bestiaryFirstUnlock);
  2168. msg.add<uint16_t>(mtype->info.bestiarySecondUnlock);
  2169. msg.add<uint16_t>(mtype->info.bestiaryToUnlock);
  2170.  
  2171. if (g_bestiary.getKillStatus(mtype, killAmount) == 4)
  2172. {
  2173. msg.addByte(4);
  2174. }
  2175. else
  2176. {
  2177. msg.addByte(0);
  2178. }
  2179. }
  2180. writeToOutputBuffer(msg);
  2181. }
  2182.  
  2183. void ProtocolGame::BestiarysendCharms()
  2184. {
  2185. IOBestiary g_bestiary;
  2186. int32_t removeRuneCost = player->getLevel() * 100;
  2187. if (player->hasCharmExpansion())
  2188. {
  2189. removeRuneCost = (removeRuneCost * 75) / 100;
  2190. }
  2191. NetworkMessage msg;
  2192. msg.addByte(0xd8);
  2193. msg.add<uint32_t>(player->getCharmPoints());
  2194.  
  2195. std::vector<Charm *> charmList = g_game.getCharmList();
  2196. msg.addByte(charmList.size());
  2197. for (Charm *c_type : charmList)
  2198. {
  2199. msg.addByte(c_type->id);
  2200. msg.addString(c_type->name);
  2201. msg.addString(c_type->description);
  2202. msg.addByte(0); // Unknown
  2203. msg.add<uint16_t>(c_type->points);
  2204. if (g_bestiary.hasCharmUnlockedRuneBit(c_type, player->getUnlockedRunesBit()))
  2205. {
  2206. msg.addByte(1);
  2207. uint16_t raceid = player->parseRacebyCharm(c_type->id, false, 0);
  2208. if (raceid > 0)
  2209. {
  2210. msg.addByte(1);
  2211. msg.add<uint16_t>(raceid);
  2212. msg.add<uint32_t>(removeRuneCost);
  2213. }
  2214. else
  2215. {
  2216. msg.addByte(0);
  2217. }
  2218. }
  2219. else
  2220. {
  2221. msg.addByte(0);
  2222. msg.addByte(0);
  2223. }
  2224. }
  2225. msg.addByte(4); // Unknown
  2226.  
  2227. std::list<uint16_t> finishedMonsters = g_bestiary.getBestiaryFinished(player);
  2228. std::list<charmRune_t> usedRunes = g_bestiary.getCharmUsedRuneBitAll(player);
  2229.  
  2230. for (charmRune_t charmRune : usedRunes)
  2231. {
  2232. Charm *tmpCharm = g_bestiary.getBestiaryCharm(charmRune);
  2233. uint16_t tmp_raceid = player->parseRacebyCharm(tmpCharm->id, false, 0);
  2234. finishedMonsters.remove(tmp_raceid);
  2235. }
  2236.  
  2237. msg.add<uint16_t>(finishedMonsters.size());
  2238. for (uint16_t raceid_tmp : finishedMonsters)
  2239. {
  2240. msg.add<uint16_t>(raceid_tmp);
  2241. }
  2242.  
  2243. writeToOutputBuffer(msg);
  2244. }
  2245.  
  2246. void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg)
  2247. {
  2248. IOBestiary g_bestiary;
  2249. std::ostringstream ss;
  2250. std::map<uint16_t, std::string> race = {};
  2251. std::string text = "";
  2252. uint8_t search = msg.getByte();
  2253.  
  2254. if (search == 1) {
  2255. uint16_t monsterAmount = msg.get<uint16_t>();
  2256. std::map<uint16_t, std::string> mtype_list = g_game.getBestiaryList();
  2257. for (uint16_t monsterCount = 1; monsterCount <= monsterAmount; monsterCount++) {
  2258. uint16_t raceid = msg.get<uint16_t>();
  2259. if (player->getBestiaryKillCount(raceid) > 0) {
  2260. auto it = mtype_list.find(raceid);
  2261. if (it != mtype_list.end()) {
  2262. race.insert({raceid, it->second});
  2263. }
  2264. }
  2265. }
  2266. } else {
  2267. std::string raceName = msg.getString();
  2268. race = g_bestiary.findRaceByName(raceName);
  2269.  
  2270. if (race.size() == 0)
  2271. {
  2272. std::cout << "> [Bestiary]: race was not found: " << raceName << " | search " << search << std::endl;
  2273. return;
  2274. }
  2275. text = raceName;
  2276. }
  2277. NetworkMessage newmsg;
  2278. newmsg.addByte(0xd6);
  2279. newmsg.addString(text);
  2280. newmsg.add<uint16_t>(race.size());
  2281. std::map<uint16_t, uint32_t> creaturesKilled = g_bestiary.getBestiaryKillCountByMonsterIDs(player, race);
  2282.  
  2283. for (auto it_ : race)
  2284. {
  2285. uint16_t raceid_ = it_.first;
  2286. newmsg.add<uint16_t>(raceid_);
  2287.  
  2288. uint8_t progress = 0;
  2289. for (const auto &_it : creaturesKilled)
  2290. {
  2291. if (_it.first == raceid_)
  2292. {
  2293. MonsterType *tmpType = g_monsters.getMonsterType(it_.second);
  2294. if (!tmpType)
  2295. {
  2296. return;
  2297. }
  2298. progress = g_bestiary.getKillStatus(tmpType, _it.second);
  2299. }
  2300. }
  2301.  
  2302. if (progress > 0)
  2303. {
  2304. newmsg.add<uint16_t>(static_cast<uint16_t>(progress));
  2305. }
  2306. else
  2307. {
  2308. newmsg.addByte(0);
  2309. }
  2310. }
  2311. writeToOutputBuffer(newmsg);
  2312. }
  2313.  
  2314. void ProtocolGame::parseBugReport(NetworkMessage &msg)
  2315. {
  2316. uint8_t category = msg.getByte();
  2317. std::string message = msg.getString();
  2318.  
  2319. Position position;
  2320. if (category == BUG_CATEGORY_MAP)
  2321. {
  2322. position = msg.getPosition();
  2323. }
  2324.  
  2325. addGameTask(&Game::playerReportBug, player->getID(), message, position, category);
  2326. }
  2327.  
  2328. void ProtocolGame::parseGreet(NetworkMessage &msg)
  2329. {
  2330. uint32_t npcId = msg.get<uint32_t>();
  2331. addGameTask(&Game::playerNpcGreet, player->getID(), npcId);
  2332. }
  2333.  
  2334. void ProtocolGame::parseDebugAssert(NetworkMessage &msg)
  2335. {
  2336. if (debugAssertSent)
  2337. {
  2338. return;
  2339. }
  2340.  
  2341. debugAssertSent = true;
  2342.  
  2343. std::string assertLine = msg.getString();
  2344. std::string date = msg.getString();
  2345. std::string description = msg.getString();
  2346. std::string comment = msg.getString();
  2347. addGameTask(&Game::playerDebugAssert, player->getID(), assertLine, date, description, comment);
  2348. }
  2349.  
  2350. void ProtocolGame::parseInviteToParty(NetworkMessage &msg)
  2351. {
  2352. uint32_t targetId = msg.get<uint32_t>();
  2353. addGameTask(&Game::playerInviteToParty, player->getID(), targetId);
  2354. }
  2355.  
  2356. void ProtocolGame::parseJoinParty(NetworkMessage &msg)
  2357. {
  2358. uint32_t targetId = msg.get<uint32_t>();
  2359. addGameTask(&Game::playerJoinParty, player->getID(), targetId);
  2360. }
  2361.  
  2362. void ProtocolGame::parseRevokePartyInvite(NetworkMessage &msg)
  2363. {
  2364. uint32_t targetId = msg.get<uint32_t>();
  2365. addGameTask(&Game::playerRevokePartyInvitation, player->getID(), targetId);
  2366. }
  2367.  
  2368. void ProtocolGame::parsePassPartyLeadership(NetworkMessage &msg)
  2369. {
  2370. uint32_t targetId = msg.get<uint32_t>();
  2371. addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId);
  2372. }
  2373.  
  2374. void ProtocolGame::parseEnableSharedPartyExperience(NetworkMessage &msg)
  2375. {
  2376. bool sharedExpActive = msg.getByte() == 1;
  2377. addGameTask(&Game::playerEnableSharedPartyExperience, player->getID(), sharedExpActive);
  2378. }
  2379.  
  2380. void ProtocolGame::parseQuestLine(NetworkMessage &msg)
  2381. {
  2382. uint16_t questId = msg.get<uint16_t>();
  2383. addGameTask(&Game::playerShowQuestLine, player->getID(), questId);
  2384. }
  2385.  
  2386. void ProtocolGame::parseMarketLeave()
  2387. {
  2388. addGameTask(&Game::playerLeaveMarket, player->getID());
  2389. }
  2390.  
  2391. void ProtocolGame::parseMarketBrowse(NetworkMessage &msg)
  2392. {
  2393. uint16_t browseId = msg.get<uint16_t>();
  2394.  
  2395. if (browseId == MARKETREQUEST_OWN_OFFERS)
  2396. {
  2397. addGameTask(&Game::playerBrowseMarketOwnOffers, player->getID());
  2398. }
  2399. else if (browseId == MARKETREQUEST_OWN_HISTORY)
  2400. {
  2401. addGameTask(&Game::playerBrowseMarketOwnHistory, player->getID());
  2402. }
  2403. else
  2404. {
  2405. addGameTask(&Game::playerBrowseMarket, player->getID(), browseId);
  2406. }
  2407. }
  2408.  
  2409. void ProtocolGame::parseStoreOpen(NetworkMessage &msg)
  2410. {
  2411. uint8_t serviceType = msg.getByte();
  2412. addGameTaskTimed(600, &Game::playerStoreOpen, player->getID(), serviceType);
  2413. }
  2414.  
  2415. void ProtocolGame::parseStoreRequestOffers(NetworkMessage &message)
  2416. {
  2417. //StoreService_t serviceType = SERVICE_STANDARD;
  2418. message.getByte(); // discard service type byte // version >= 1092
  2419.  
  2420. std::string categoryName = message.getString();
  2421. const int16_t index = g_game.gameStore.getCategoryIndexByName(categoryName);
  2422.  
  2423. if (index >= 0)
  2424. {
  2425. addGameTaskTimed(350, &Game::playerShowStoreCategoryOffers, player->getID(),
  2426. g_game.gameStore.getCategoryOffers().at(index));
  2427. }
  2428. else
  2429. {
  2430. std::cout << "[Warning - ProtocolGame::parseStoreRequestOffers] requested category: \"" << categoryName << "\" doesn't exists" << std::endl;
  2431. }
  2432. }
  2433.  
  2434. void ProtocolGame::parseStoreBuyOffer(NetworkMessage &message)
  2435. {
  2436. uint32_t offerId = message.get<uint32_t>();
  2437. uint8_t productType = message.getByte(); //used only in return of a namechange offer request
  2438. std::string additionalInfo;
  2439. if (productType == ADDITIONALINFO)
  2440. {
  2441. additionalInfo = message.getString();
  2442. }
  2443. addGameTaskTimed(350, &Game::playerBuyStoreOffer, player->getID(), offerId, productType, additionalInfo);
  2444. }
  2445.  
  2446. void ProtocolGame::parseStoreOpenTransactionHistory(NetworkMessage &msg)
  2447. {
  2448. uint8_t entriesPerPage = msg.getByte();
  2449. if (entriesPerPage > 0 && entriesPerPage != GameStore::HISTORY_ENTRIES_PER_PAGE)
  2450. {
  2451. GameStore::HISTORY_ENTRIES_PER_PAGE = entriesPerPage;
  2452. }
  2453.  
  2454. addGameTaskTimed(2000, &Game::playerStoreTransactionHistory, player->getID(), 1);
  2455. }
  2456.  
  2457. void ProtocolGame::parseStoreRequestTransactionHistory(NetworkMessage &msg)
  2458. {
  2459. uint32_t pageNumber = msg.get<uint32_t>();
  2460. addGameTaskTimed(2000, &Game::playerStoreTransactionHistory, player->getID(), pageNumber);
  2461. }
  2462.  
  2463. void ProtocolGame::parseCoinTransfer(NetworkMessage &msg)
  2464. {
  2465. std::string receiverName = msg.getString();
  2466. uint32_t amount = msg.get<uint32_t>();
  2467.  
  2468. if (amount > 0)
  2469. {
  2470. addGameTaskTimed(350, &Game::playerCoinTransfer, player->getID(), receiverName, amount);
  2471. }
  2472.  
  2473. updateCoinBalance();
  2474. }
  2475.  
  2476. void ProtocolGame::parseMarketCreateOffer(NetworkMessage &msg)
  2477. {
  2478. uint8_t type = msg.getByte();
  2479. uint16_t spriteId = msg.get<uint16_t>();
  2480. uint16_t amount = msg.get<uint16_t>();
  2481. uint32_t price = msg.get<uint32_t>();
  2482. bool anonymous = (msg.getByte() != 0);
  2483. if (amount > 0 && price > 0)
  2484. {
  2485. addGameTask(&Game::playerCreateMarketOffer, player->getID(), type, spriteId, amount, price, anonymous);
  2486. }
  2487. }
  2488.  
  2489. void ProtocolGame::parseMarketCancelOffer(NetworkMessage &msg)
  2490. {
  2491. uint32_t timestamp = msg.get<uint32_t>();
  2492. uint16_t counter = msg.get<uint16_t>();
  2493. if (counter > 0)
  2494. {
  2495. addGameTask(&Game::playerCancelMarketOffer, player->getID(), timestamp, counter);
  2496. }
  2497.  
  2498. updateCoinBalance();
  2499. }
  2500.  
  2501. void ProtocolGame::parseMarketAcceptOffer(NetworkMessage &msg)
  2502. {
  2503. uint32_t timestamp = msg.get<uint32_t>();
  2504. uint16_t counter = msg.get<uint16_t>();
  2505. uint16_t amount = msg.get<uint16_t>();
  2506. if (amount > 0 && counter > 0)
  2507. {
  2508. addGameTask(&Game::playerAcceptMarketOffer, player->getID(), timestamp, counter, amount);
  2509. }
  2510.  
  2511. updateCoinBalance();
  2512. }
  2513.  
  2514. void ProtocolGame::parseModalWindowAnswer(NetworkMessage &msg)
  2515. {
  2516. uint32_t id = msg.get<uint32_t>();
  2517. uint8_t button = msg.getByte();
  2518. uint8_t choice = msg.getByte();
  2519. addGameTask(&Game::playerAnswerModalWindow, player->getID(), id, button, choice);
  2520. }
  2521.  
  2522. void ProtocolGame::parseBrowseField(NetworkMessage &msg)
  2523. {
  2524. const Position &pos = msg.getPosition();
  2525. addGameTask(&Game::playerBrowseField, player->getID(), pos);
  2526. }
  2527.  
  2528. void ProtocolGame::parseSeekInContainer(NetworkMessage &msg)
  2529. {
  2530. uint8_t containerId = msg.getByte();
  2531. uint16_t index = msg.get<uint16_t>();
  2532. addGameTask(&Game::playerSeekInContainer, player->getID(), containerId, index);
  2533. }
  2534.  
  2535. // Send methods
  2536. void ProtocolGame::sendOpenPrivateChannel(const std::string &receiver)
  2537. {
  2538. NetworkMessage msg;
  2539. msg.addByte(0xAD);
  2540. msg.addString(receiver);
  2541. writeToOutputBuffer(msg);
  2542. }
  2543.  
  2544. void ProtocolGame::sendChannelEvent(uint16_t channelId, const std::string &playerName, ChannelEvent_t channelEvent)
  2545. {
  2546. NetworkMessage msg;
  2547. msg.addByte(0xF3);
  2548. msg.add<uint16_t>(channelId);
  2549. msg.addString(playerName);
  2550. msg.addByte(channelEvent);
  2551. writeToOutputBuffer(msg);
  2552. }
  2553.  
  2554. void ProtocolGame::sendCreatureOutfit(const Creature *creature, const Outfit_t &outfit)
  2555. {
  2556. if (!canSee(creature))
  2557. {
  2558. return;
  2559. }
  2560.  
  2561. NetworkMessage msg;
  2562. msg.addByte(0x8E);
  2563. msg.add<uint32_t>(creature->getID());
  2564. AddOutfit(msg, outfit);
  2565. if (version >= 1200 && outfit.lookMount != 0)
  2566. {
  2567. msg.addByte(outfit.lookMountHead);
  2568. msg.addByte(outfit.lookMountBody);
  2569. msg.addByte(outfit.lookMountLegs);
  2570. msg.addByte(outfit.lookMountFeet);
  2571. }
  2572. writeToOutputBuffer(msg);
  2573. }
  2574.  
  2575. void ProtocolGame::sendCreatureLight(const Creature *creature)
  2576. {
  2577. if (!canSee(creature))
  2578. {
  2579. return;
  2580. }
  2581.  
  2582. NetworkMessage msg;
  2583. AddCreatureLight(msg, creature);
  2584. writeToOutputBuffer(msg);
  2585. }
  2586.  
  2587. void ProtocolGame::sendCreatureIcon(const Creature* creature)
  2588. {
  2589. if (!creature || version < 1200)
  2590. return;
  2591. CreatureIcon_t icon = creature->getIcon();
  2592. NetworkMessage msg;
  2593. msg.addByte(0x8B);
  2594. msg.add<uint32_t>(creature->getID());
  2595. msg.addByte(14); // type 14 for this
  2596. msg.addByte(icon != CREATUREICON_NONE); // 0 = no icon, 1 = we'll send an icon
  2597. if (icon != CREATUREICON_NONE) {
  2598. msg.addByte(icon);
  2599. msg.addByte(1); // ???
  2600. msg.add<uint16_t>(0); // used for the life in the new quest
  2601. }
  2602. writeToOutputBuffer(msg);
  2603. }
  2604.  
  2605. void ProtocolGame::sendWorldLight(const LightInfo &lightInfo)
  2606. {
  2607. NetworkMessage msg;
  2608. AddWorldLight(msg, lightInfo);
  2609. writeToOutputBuffer(msg);
  2610. }
  2611.  
  2612. void ProtocolGame::sendTibiaTime(int32_t time)
  2613. {
  2614. if (version < 1200)
  2615. return;
  2616. NetworkMessage msg;
  2617. msg.addByte(0xEF);
  2618. msg.addByte(time / 60);
  2619. msg.addByte(time % 60);
  2620. writeToOutputBuffer(msg);
  2621. }
  2622.  
  2623. void ProtocolGame::sendCreatureWalkthrough(const Creature *creature, bool walkthrough)
  2624. {
  2625. if (!canSee(creature))
  2626. {
  2627. return;
  2628. }
  2629.  
  2630. NetworkMessage msg;
  2631. msg.addByte(0x92);
  2632. msg.add<uint32_t>(creature->getID());
  2633. msg.addByte(walkthrough ? 0x00 : 0x01);
  2634. writeToOutputBuffer(msg);
  2635. }
  2636.  
  2637. void ProtocolGame::sendCreatureShield(const Creature *creature)
  2638. {
  2639. if (!canSee(creature))
  2640. {
  2641. return;
  2642. }
  2643.  
  2644. NetworkMessage msg;
  2645. msg.addByte(0x91);
  2646. msg.add<uint32_t>(creature->getID());
  2647. msg.addByte(player->getPartyShield(creature->getPlayer()));
  2648. writeToOutputBuffer(msg);
  2649. }
  2650.  
  2651. void ProtocolGame::sendCreatureSkull(const Creature *creature)
  2652. {
  2653. if (g_game.getWorldType() != WORLD_TYPE_PVP)
  2654. {
  2655. return;
  2656. }
  2657.  
  2658. if (!canSee(creature))
  2659. {
  2660. return;
  2661. }
  2662.  
  2663. NetworkMessage msg;
  2664. msg.addByte(0x90);
  2665. msg.add<uint32_t>(creature->getID());
  2666. msg.addByte(player->getSkullClient(creature));
  2667. writeToOutputBuffer(msg);
  2668. }
  2669.  
  2670. void ProtocolGame::sendCreatureType(const Creature *creature, uint8_t creatureType)
  2671. {
  2672. NetworkMessage msg;
  2673. msg.addByte(0x95);
  2674. msg.add<uint32_t>(creature->getID());
  2675. msg.addByte(creatureType);
  2676.  
  2677. if (player->getOperatingSystem() == CLIENTOS_WINDOWS && version >= 1200)
  2678. {
  2679. msg.addByte(creatureType); // type or any byte idk
  2680. }
  2681.  
  2682. if (creatureType == CREATURETYPE_SUMMONPLAYER && version >= 1200)
  2683. {
  2684. const Creature *master = creature->getMaster();
  2685. if (master)
  2686. {
  2687. msg.add<uint32_t>(master->getID());
  2688. }
  2689. }
  2690.  
  2691. writeToOutputBuffer(msg);
  2692. }
  2693.  
  2694. void ProtocolGame::sendCreatureSquare(const Creature *creature, SquareColor_t color)
  2695. {
  2696. if (!canSee(creature))
  2697. {
  2698. return;
  2699. }
  2700.  
  2701. NetworkMessage msg;
  2702. msg.addByte(0x93);
  2703. msg.add<uint32_t>(creature->getID());
  2704. msg.addByte(0x01);
  2705. msg.addByte(color);
  2706. writeToOutputBuffer(msg);
  2707. }
  2708.  
  2709. void ProtocolGame::sendTutorial(uint8_t tutorialId)
  2710. {
  2711. NetworkMessage msg;
  2712. msg.addByte(0xDC);
  2713. msg.addByte(tutorialId);
  2714. writeToOutputBuffer(msg);
  2715. }
  2716.  
  2717. void ProtocolGame::sendAddMarker(const Position &pos, uint8_t markType, const std::string &desc)
  2718. {
  2719. NetworkMessage msg;
  2720. msg.addByte(0xDD);
  2721. if (version >= 1200)
  2722. msg.addByte(0x00); // unknow
  2723.  
  2724. msg.addPosition(pos);
  2725. msg.addByte(markType);
  2726. msg.addString(desc);
  2727. writeToOutputBuffer(msg);
  2728. }
  2729.  
  2730. void ProtocolGame::sendCyclopediaCharacterNoData(CyclopediaCharacterInfoType_t characterInfoType, uint8_t errorCode)
  2731. {
  2732. NetworkMessage msg;
  2733. msg.addByte(0xDA);
  2734. msg.addByte(static_cast<uint8_t>(characterInfoType));
  2735. msg.addByte(errorCode);
  2736. writeToOutputBuffer(msg);
  2737. }
  2738.  
  2739. void ProtocolGame::sendCyclopediaCharacterBaseInformation()
  2740. {
  2741. NetworkMessage msg;
  2742. msg.addByte(0xDA);
  2743. msg.addByte(CYCLOPEDIA_CHARACTERINFO_BASEINFORMATION);
  2744. msg.addByte(0x00);
  2745. msg.addString(player->getName());
  2746. msg.addString(player->getVocation()->getVocName());
  2747. msg.add<uint16_t>(player->getLevel());
  2748. AddOutfit(msg, player->getDefaultOutfit(), false);
  2749.  
  2750. msg.addByte(0x00); // hide stamina
  2751. msg.addByte(0x00); // enable store summary & character titles
  2752. msg.addString(""); // character title
  2753. writeToOutputBuffer(msg);
  2754. }
  2755.  
  2756. void ProtocolGame::sendCyclopediaCharacterGeneralStats()
  2757. {
  2758. NetworkMessage msg;
  2759. msg.addByte(0xDA);
  2760. msg.addByte(CYCLOPEDIA_CHARACTERINFO_GENERALSTATS);
  2761. msg.addByte(0x00);
  2762. msg.add<uint64_t>(player->getExperience());
  2763. msg.add<uint16_t>(player->getLevel());
  2764. msg.addByte(player->getLevelPercent());
  2765. // BaseXPGainRate
  2766. msg.add<uint16_t>(100);
  2767. // TournamentXPFactor
  2768. msg.add<int32_t>(0);
  2769. // LowLevelBonus
  2770. msg.add<uint16_t>(0);
  2771. // XPBoost
  2772. msg.add<uint16_t>(0);
  2773. // StaminaMultiplier(100=x1.0)
  2774. msg.add<uint16_t>(100);
  2775. // xpBoostRemainingTime
  2776. msg.add<uint16_t>(0);
  2777. // canBuyXpBoost
  2778. msg.addByte(0x00);
  2779. msg.add<uint16_t>(std::min<int32_t>(player->getHealth(), std::numeric_limits<uint16_t>::max()));
  2780. msg.add<uint16_t>(std::min<int32_t>(player->getMaxHealth(), std::numeric_limits<uint16_t>::max()));
  2781. msg.add<uint16_t>(std::min<int32_t>(player->getMana(), std::numeric_limits<uint16_t>::max()));
  2782. msg.add<uint16_t>(std::min<int32_t>(player->getMaxMana(), std::numeric_limits<uint16_t>::max()));
  2783. msg.addByte(player->getSoul());
  2784. msg.add<uint16_t>(player->getStaminaMinutes());
  2785.  
  2786. Condition *condition = player->getCondition(CONDITION_REGENERATION);
  2787. msg.add<uint16_t>(condition ? condition->getTicks() / 1000 : 0x00);
  2788. msg.add<uint16_t>(player->getOfflineTrainingTime() / 60 / 1000);
  2789. msg.add<uint16_t>(player->getSpeed() / 2);
  2790. msg.add<uint16_t>(player->getBaseSpeed() / 2);
  2791. msg.add<uint32_t>(player->getCapacity());
  2792. msg.add<uint32_t>(player->getCapacity());
  2793. msg.add<uint32_t>(player->getFreeCapacity());
  2794. msg.addByte(8);
  2795. msg.addByte(1);
  2796. msg.add<uint16_t>(player->getMagicLevel());
  2797. msg.add<uint16_t>(player->getBaseMagicLevel());
  2798. // loyalty bonus
  2799. msg.add<uint16_t>(player->getBaseMagicLevel());
  2800. msg.add<uint16_t>(player->getMagicLevelPercent() * 100);
  2801. for (uint8_t i = SKILL_FIRST; i < SKILL_CRITICAL_HIT_CHANCE; ++i)
  2802. {
  2803. // check if all clients have the same hardcoded skill ids
  2804. static const uint8_t HardcodedSkillIds[] = {11, 9, 8, 10, 7, 6, 13};
  2805. msg.addByte(HardcodedSkillIds[i]);
  2806. msg.add<uint16_t>(std::min<int32_t>(player->getSkillLevel(i), std::numeric_limits<uint16_t>::max()));
  2807. msg.add<uint16_t>(player->getBaseSkill(i));
  2808. // loyalty bonus
  2809. msg.add<uint16_t>(player->getBaseSkill(i));
  2810. msg.add<uint16_t>(player->getSkillPercent(i) * 100);
  2811. }
  2812. writeToOutputBuffer(msg);
  2813. }
  2814.  
  2815. void ProtocolGame::sendCyclopediaCharacterCombatStats()
  2816. {
  2817. NetworkMessage msg;
  2818. msg.addByte(0xDA);
  2819. msg.addByte(CYCLOPEDIA_CHARACTERINFO_COMBATSTATS);
  2820. msg.addByte(0x00);
  2821. for (uint8_t i = SKILL_CRITICAL_HIT_CHANCE; i <= SKILL_LAST; ++i)
  2822. {
  2823. msg.add<uint16_t>(std::min<int32_t>(player->getSkillLevel(i), std::numeric_limits<uint16_t>::max()));
  2824. msg.add<uint16_t>(0);
  2825. }
  2826. uint8_t haveBlesses = 0;
  2827. uint8_t blessings = 8;
  2828. for (uint8_t i = 1; i < blessings; ++i)
  2829. {
  2830. if (player->hasBlessing(i))
  2831. {
  2832. ++haveBlesses;
  2833. }
  2834. }
  2835. msg.addByte(haveBlesses);
  2836. msg.addByte(blessings);
  2837. const Item *weapon = player->getWeapon();
  2838. if (weapon)
  2839. {
  2840. const ItemType &it = Item::items[weapon->getID()];
  2841. if (it.weaponType == WEAPON_WAND)
  2842. {
  2843. msg.add<uint16_t>(it.maxHitChance);
  2844. msg.addByte(getCipbiaElement(it.combatType));
  2845. msg.addByte(0);
  2846. msg.addByte(CIPBIA_ELEMENTAL_UNDEFINED);
  2847. }
  2848. else if (it.weaponType == WEAPON_DISTANCE || it.weaponType == WEAPON_AMMO)
  2849. {
  2850. int32_t attackValue = weapon->getAttack();
  2851. if (it.weaponType == WEAPON_AMMO)
  2852. {
  2853. const Item *weaponItem = player->getWeapon(true);
  2854. if (weaponItem)
  2855. {
  2856. attackValue += weaponItem->getAttack();
  2857. }
  2858. }
  2859.  
  2860. int32_t attackSkill = player->getSkillLevel(SKILL_DISTANCE);
  2861. float attackFactor = player->getAttackFactor();
  2862. int32_t maxDamage = static_cast<int32_t>(Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor, true) * player->getVocation()->distDamageMultiplier);
  2863. if (it.abilities && it.abilities->elementType != COMBAT_NONE)
  2864. {
  2865. maxDamage += static_cast<int32_t>(Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue - weapon->getAttack() + it.abilities->elementDamage, attackFactor, true) * player->getVocation()->distDamageMultiplier);
  2866. }
  2867. msg.add<uint16_t>(maxDamage >> 1);
  2868. msg.addByte(CIPBIA_ELEMENTAL_PHYSICAL);
  2869. if (it.abilities && it.abilities->elementType != COMBAT_NONE)
  2870. {
  2871. msg.addByte(static_cast<uint32_t>(it.abilities->elementDamage) * 100 / attackValue);
  2872. msg.addByte(getCipbiaElement(it.abilities->elementType));
  2873. }
  2874. else
  2875. {
  2876. msg.addByte(0);
  2877. msg.addByte(CIPBIA_ELEMENTAL_UNDEFINED);
  2878. }
  2879. }
  2880. else
  2881. {
  2882. int32_t attackValue = std::max<int32_t>(0, weapon->getAttack());
  2883. int32_t attackSkill = player->getWeaponSkill(weapon);
  2884. float attackFactor = player->getAttackFactor();
  2885. int32_t maxDamage = static_cast<int32_t>(Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor, true) * player->getVocation()->meleeDamageMultiplier);
  2886. if (it.abilities && it.abilities->elementType != COMBAT_NONE)
  2887. {
  2888. maxDamage += static_cast<int32_t>(Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, it.abilities->elementDamage, attackFactor, true) * player->getVocation()->meleeDamageMultiplier);
  2889. }
  2890. msg.add<uint16_t>(maxDamage >> 1);
  2891. msg.addByte(CIPBIA_ELEMENTAL_PHYSICAL);
  2892. if (it.abilities && it.abilities->elementType != COMBAT_NONE)
  2893. {
  2894. msg.addByte(static_cast<uint32_t>(it.abilities->elementDamage) * 100 / attackValue);
  2895. msg.addByte(getCipbiaElement(it.abilities->elementType));
  2896. }
  2897. else
  2898. {
  2899. msg.addByte(0);
  2900. msg.addByte(CIPBIA_ELEMENTAL_UNDEFINED);
  2901. }
  2902. }
  2903. }
  2904. else
  2905. {
  2906. float attackFactor = player->getAttackFactor();
  2907. int32_t attackSkill = player->getSkillLevel(SKILL_FIST);
  2908. int32_t attackValue = 7;
  2909.  
  2910. int32_t maxDamage = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor, true);
  2911. msg.add<uint16_t>(maxDamage >> 1);
  2912. msg.addByte(CIPBIA_ELEMENTAL_PHYSICAL);
  2913. msg.addByte(0);
  2914. msg.addByte(CIPBIA_ELEMENTAL_UNDEFINED);
  2915. }
  2916. msg.add<uint16_t>(player->getArmor());
  2917. msg.add<uint16_t>(player->getDefense());
  2918.  
  2919. uint8_t combats = 0;
  2920. auto startCombats = msg.getBufferPosition();
  2921. msg.skipBytes(1);
  2922.  
  2923. alignas(16) int16_t absorbs[COMBAT_COUNT] = {};
  2924. for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot)
  2925. {
  2926. if (!player->isItemAbilityEnabled(static_cast<slots_t>(slot)))
  2927. {
  2928. continue;
  2929. }
  2930.  
  2931. Item *item = player->getInventoryItem(static_cast<slots_t>(slot));
  2932. if (!item)
  2933. {
  2934. continue;
  2935. }
  2936.  
  2937. const ItemType &it = Item::items[item->getID()];
  2938. if (!it.abilities)
  2939. {
  2940. continue;
  2941. }
  2942.  
  2943. if (COMBAT_COUNT == 12)
  2944. {
  2945. absorbs[0] += it.abilities->absorbPercent[0];
  2946. absorbs[1] += it.abilities->absorbPercent[1];
  2947. absorbs[2] += it.abilities->absorbPercent[2];
  2948. absorbs[3] += it.abilities->absorbPercent[3];
  2949. absorbs[4] += it.abilities->absorbPercent[4];
  2950. absorbs[5] += it.abilities->absorbPercent[5];
  2951. absorbs[6] += it.abilities->absorbPercent[6];
  2952. absorbs[7] += it.abilities->absorbPercent[7];
  2953. absorbs[8] += it.abilities->absorbPercent[8];
  2954. absorbs[9] += it.abilities->absorbPercent[9];
  2955. absorbs[10] += it.abilities->absorbPercent[10];
  2956. absorbs[11] += it.abilities->absorbPercent[11];
  2957. }
  2958. else
  2959. {
  2960. for (size_t i = 0; i < COMBAT_COUNT; ++i)
  2961. {
  2962. absorbs[i] += it.abilities->absorbPercent[i];
  2963. }
  2964. }
  2965. }
  2966.  
  2967. static const Cipbia_Elementals_t cipbiaCombats[] = {CIPBIA_ELEMENTAL_PHYSICAL, CIPBIA_ELEMENTAL_ENERGY, CIPBIA_ELEMENTAL_EARTH, CIPBIA_ELEMENTAL_FIRE, CIPBIA_ELEMENTAL_UNDEFINED,
  2968. CIPBIA_ELEMENTAL_LIFEDRAIN, CIPBIA_ELEMENTAL_UNDEFINED, CIPBIA_ELEMENTAL_HEALING, CIPBIA_ELEMENTAL_DROWN, CIPBIA_ELEMENTAL_ICE, CIPBIA_ELEMENTAL_HOLY, CIPBIA_ELEMENTAL_DEATH};
  2969. for (size_t i = 0; i < COMBAT_COUNT; ++i)
  2970. {
  2971. if (absorbs[i] != 0)
  2972. {
  2973. msg.addByte(cipbiaCombats[i]);
  2974. msg.addByte(std::max<int16_t>(-100, std::min<int16_t>(100, absorbs[i])));
  2975. ++combats;
  2976. }
  2977. }
  2978.  
  2979. msg.setBufferPosition(startCombats);
  2980. msg.addByte(combats);
  2981.  
  2982. writeToOutputBuffer(msg);
  2983. }
  2984.  
  2985. void ProtocolGame::sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t pages, const std::vector<RecentDeathEntry> &entries)
  2986. {
  2987. NetworkMessage msg;
  2988. msg.addByte(0xDA);
  2989. msg.addByte(CYCLOPEDIA_CHARACTERINFO_RECENTDEATHS);
  2990. msg.addByte(0x00);
  2991. msg.add<uint16_t>(page);
  2992. msg.add<uint16_t>(pages);
  2993. msg.add<uint16_t>(entries.size());
  2994. for (const RecentDeathEntry &entry : entries)
  2995. {
  2996. msg.add<uint32_t>(entry.timestamp);
  2997. msg.addString(entry.cause);
  2998. }
  2999. writeToOutputBuffer(msg);
  3000. }
  3001.  
  3002. void ProtocolGame::sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t pages, const std::vector<RecentPvPKillEntry> &entries)
  3003. {
  3004. NetworkMessage msg;
  3005. msg.addByte(0xDA);
  3006. msg.addByte(CYCLOPEDIA_CHARACTERINFO_RECENTPVPKILLS);
  3007. msg.addByte(0x00);
  3008. msg.add<uint16_t>(page);
  3009. msg.add<uint16_t>(pages);
  3010. msg.add<uint16_t>(entries.size());
  3011. for (const RecentPvPKillEntry &entry : entries)
  3012. {
  3013. msg.add<uint32_t>(entry.timestamp);
  3014. msg.addString(entry.description);
  3015. msg.addByte(entry.status);
  3016. }
  3017. writeToOutputBuffer(msg);
  3018. }
  3019.  
  3020. void ProtocolGame::sendCyclopediaCharacterAchievements()
  3021. {
  3022. NetworkMessage msg;
  3023. msg.addByte(0xDA);
  3024. msg.addByte(CYCLOPEDIA_CHARACTERINFO_ACHIEVEMENTS);
  3025. msg.addByte(0x00);
  3026. msg.add<uint16_t>(0);
  3027. msg.add<uint16_t>(0);
  3028. msg.add<uint16_t>(0);
  3029. writeToOutputBuffer(msg);
  3030. }
  3031.  
  3032. void ProtocolGame::sendCyclopediaCharacterItemSummary()
  3033. {
  3034. NetworkMessage msg;
  3035. msg.addByte(0xDA);
  3036. msg.addByte(CYCLOPEDIA_CHARACTERINFO_ITEMSUMMARY);
  3037. msg.addByte(0x00);
  3038. msg.add<uint16_t>(0);
  3039. msg.add<uint16_t>(0);
  3040. msg.add<uint16_t>(0);
  3041. msg.add<uint16_t>(0);
  3042. msg.add<uint16_t>(0);
  3043. writeToOutputBuffer(msg);
  3044. }
  3045.  
  3046. void ProtocolGame::sendCyclopediaCharacterOutfitsMounts()
  3047. {
  3048. NetworkMessage msg;
  3049. msg.addByte(0xDA);
  3050. msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITSMOUNTS);
  3051. msg.addByte(0x00);
  3052. Outfit_t currentOutfit = player->getDefaultOutfit();
  3053.  
  3054. uint16_t outfitSize = 0;
  3055. auto startOutfits = msg.getBufferPosition();
  3056. msg.skipBytes(2);
  3057.  
  3058. const auto &outfits = Outfits::getInstance().getOutfits(player->getSex());
  3059. for (const Outfit &outfit : outfits)
  3060. {
  3061. uint8_t addons;
  3062. if (!player->getOutfitAddons(outfit, addons))
  3063. {
  3064. continue;
  3065. }
  3066. const std::string from = outfit.from;
  3067. ++outfitSize;
  3068.  
  3069. msg.add<uint16_t>(outfit.lookType);
  3070. msg.addString(outfit.name);
  3071. msg.addByte(addons);
  3072. if (from == "store")
  3073. msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_STORE);
  3074. else if (from == "quest")
  3075. msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_QUEST);
  3076. else
  3077. msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_NONE);
  3078. if (outfit.lookType == currentOutfit.lookType)
  3079. {
  3080. msg.add<uint32_t>(1000);
  3081. }
  3082. else
  3083. {
  3084. msg.add<uint32_t>(0);
  3085. }
  3086. }
  3087. if (outfitSize > 0)
  3088. {
  3089. msg.addByte(currentOutfit.lookHead);
  3090. msg.addByte(currentOutfit.lookBody);
  3091. msg.addByte(currentOutfit.lookLegs);
  3092. msg.addByte(currentOutfit.lookFeet);
  3093. }
  3094.  
  3095. uint16_t mountSize = 0;
  3096. auto startMounts = msg.getBufferPosition();
  3097. msg.skipBytes(2);
  3098. for (const Mount &mount : g_game.mounts.getMounts())
  3099. {
  3100. const std::string type = mount.type;
  3101. if (player->hasMount(&mount))
  3102. {
  3103. ++mountSize;
  3104.  
  3105. msg.add<uint16_t>(mount.clientId);
  3106. msg.addString(mount.name);
  3107. if (type == "store")
  3108. msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_STORE);
  3109. else if (type == "quest")
  3110. msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_QUEST);
  3111. else
  3112. msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_NONE);
  3113. msg.add<uint32_t>(1000);
  3114. }
  3115. }
  3116. if (mountSize > 0)
  3117. {
  3118. msg.addByte(currentOutfit.lookMountHead);
  3119. msg.addByte(currentOutfit.lookMountBody);
  3120. msg.addByte(currentOutfit.lookMountLegs);
  3121. msg.addByte(currentOutfit.lookMountFeet);
  3122. }
  3123.  
  3124. uint16_t familiarsSize = 0;
  3125. auto startFamiliars = msg.getBufferPosition();
  3126. msg.skipBytes(2);
  3127. const auto &familiars = Familiars::getInstance().getFamiliars(player->getVocationId());
  3128. for (const Familiar &familiar : familiars)
  3129. {
  3130. const std::string type = familiar.type;
  3131. if (!player->getFamiliar(familiar))
  3132. {
  3133. continue;
  3134. }
  3135. ++familiarsSize;
  3136. msg.add<uint16_t>(familiar.lookType);
  3137. msg.addString(familiar.name);
  3138. if (type == "quest")
  3139. msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_QUEST);
  3140. else
  3141. msg.addByte(CYCLOPEDIA_CHARACTERINFO_OUTFITTYPE_NONE);
  3142. msg.add<uint32_t>(0);
  3143. }
  3144.  
  3145. msg.setBufferPosition(startOutfits);
  3146. msg.add<uint16_t>(outfitSize);
  3147. msg.setBufferPosition(startMounts);
  3148. msg.add<uint16_t>(mountSize);
  3149. msg.setBufferPosition(startFamiliars);
  3150. msg.add<uint16_t>(familiarsSize);
  3151. writeToOutputBuffer(msg);
  3152. }
  3153.  
  3154. void ProtocolGame::sendCyclopediaCharacterStoreSummary()
  3155. {
  3156. NetworkMessage msg;
  3157. msg.addByte(0xDA);
  3158. msg.addByte(CYCLOPEDIA_CHARACTERINFO_STORESUMMARY);
  3159. msg.addByte(0x00);
  3160. msg.add<uint32_t>(0);
  3161. msg.add<uint32_t>(0);
  3162. msg.addByte(0x00);
  3163. msg.addByte(0x00);
  3164. msg.addByte(0x00);
  3165. msg.addByte(0x00);
  3166. msg.addByte(0x00);
  3167. msg.addByte(0x00);
  3168. msg.addByte(0x00);
  3169. msg.addByte(0x00);
  3170. msg.add<uint16_t>(0);
  3171. writeToOutputBuffer(msg);
  3172. }
  3173.  
  3174. void ProtocolGame::sendCyclopediaCharacterInspection()
  3175. {
  3176. NetworkMessage msg;
  3177. msg.addByte(0xDA);
  3178. msg.addByte(CYCLOPEDIA_CHARACTERINFO_INSPECTION);
  3179. msg.addByte(0x00);
  3180. uint8_t inventoryItems = 0;
  3181. auto startInventory = msg.getBufferPosition();
  3182. msg.skipBytes(1);
  3183. for (std::underlying_type<slots_t>::type slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; slot++)
  3184. {
  3185. Item *inventoryItem = player->getInventoryItem(static_cast<slots_t>(slot));
  3186. if (inventoryItem)
  3187. {
  3188. ++inventoryItems;
  3189.  
  3190. msg.addByte(slot);
  3191. msg.addString(inventoryItem->getName());
  3192. AddItem(msg, inventoryItem);
  3193. msg.addByte(0);
  3194.  
  3195. auto descriptions = Item::getDescriptions(Item::items[inventoryItem->getID()], inventoryItem);
  3196. msg.addByte(descriptions.size());
  3197. for (const auto &description : descriptions)
  3198. {
  3199. msg.addString(description.first);
  3200. msg.addString(description.second);
  3201. }
  3202. }
  3203. }
  3204. msg.addString(player->getName());
  3205. AddOutfit(msg, player->getDefaultOutfit(), false);
  3206.  
  3207. msg.addByte(3);
  3208. msg.addString("Level");
  3209. msg.addString(std::to_string(player->getLevel()));
  3210. msg.addString("Vocation");
  3211. msg.addString(player->getVocation()->getVocName());
  3212. msg.addString("Outfit");
  3213.  
  3214. const Outfit *outfit = Outfits::getInstance().getOutfitByLookType(player->getSex(),
  3215. player->getDefaultOutfit().lookType);
  3216. if (outfit)
  3217. {
  3218. msg.addString(outfit->name);
  3219. }
  3220. else
  3221. {
  3222. msg.addString("unknown");
  3223. }
  3224. msg.setBufferPosition(startInventory);
  3225. msg.addByte(inventoryItems);
  3226. writeToOutputBuffer(msg);
  3227. }
  3228.  
  3229. void ProtocolGame::sendCyclopediaCharacterBadges()
  3230. {
  3231. NetworkMessage msg;
  3232. msg.addByte(0xDA);
  3233. msg.addByte(CYCLOPEDIA_CHARACTERINFO_BADGES);
  3234. msg.addByte(0x00);
  3235. // enable badges
  3236. msg.addByte(0x00);
  3237. writeToOutputBuffer(msg);
  3238. }
  3239.  
  3240. void ProtocolGame::sendCyclopediaCharacterTitles()
  3241. {
  3242. NetworkMessage msg;
  3243. msg.addByte(0xDA);
  3244. msg.addByte(CYCLOPEDIA_CHARACTERINFO_TITLES);
  3245. msg.addByte(0x00);
  3246. msg.addByte(0x00);
  3247. msg.addByte(0x00);
  3248. writeToOutputBuffer(msg);
  3249. }
  3250.  
  3251. void ProtocolGame::sendTournamentLeaderboard()
  3252. {
  3253. NetworkMessage msg;
  3254. msg.addByte(0xC5);
  3255. msg.addByte(0);
  3256. msg.addByte(0x01);
  3257. writeToOutputBuffer(msg);
  3258. }
  3259.  
  3260. void ProtocolGame::sendReLoginWindow(uint8_t unfairFightReduction)
  3261. {
  3262. NetworkMessage msg;
  3263. msg.addByte(0x28);
  3264. msg.addByte(0x00);
  3265. msg.addByte(unfairFightReduction);
  3266. if (version >= 1200)
  3267. msg.addByte(0x00); // use death redemption (boolean)
  3268. writeToOutputBuffer(msg);
  3269. }
  3270.  
  3271. void ProtocolGame::sendStats()
  3272. {
  3273. NetworkMessage msg;
  3274. AddPlayerStats(msg);
  3275. writeToOutputBuffer(msg);
  3276. }
  3277.  
  3278. void ProtocolGame::sendBasicData()
  3279. {
  3280. NetworkMessage msg;
  3281. msg.addByte(0x9F);
  3282. if (player->isPremium())
  3283. {
  3284. msg.addByte(1);
  3285. msg.add<uint32_t>(time(nullptr) + (player->premiumDays * 86400));
  3286. }
  3287. else
  3288. {
  3289. msg.addByte(0);
  3290. msg.add<uint32_t>(0);
  3291. }
  3292. msg.addByte(player->getVocation()->getClientId());
  3293.  
  3294. // Prey window
  3295. if (player->getVocation()->getId() == 0)
  3296. {
  3297. msg.addByte(0);
  3298. }
  3299. else
  3300. {
  3301. msg.addByte(1); // has reached Main (allow player to open Prey window)
  3302. }
  3303.  
  3304. std::list<uint16_t> spellsList = g_spells->getSpellsByVocation(player->getVocationId());
  3305. msg.add<uint16_t>(spellsList.size());
  3306. for (uint16_t sid : spellsList)
  3307. {
  3308. msg.addByte(sid);
  3309. }
  3310. if (version >= 1200)
  3311. msg.addByte(player->getVocation()->getMagicShield()); // bool - determine whether magic shield is active or not
  3312. writeToOutputBuffer(msg);
  3313. }
  3314.  
  3315. void ProtocolGame::sendBlessStatus()
  3316. {
  3317. if (!player) return;
  3318. uint8_t maxClientBlessings = (player->operatingSystem == CLIENTOS_NEW_WINDOWS) ? 8 : 6; // (compartability for the client 10)
  3319. //Ignore ToF (bless 1)
  3320. NetworkMessage msg;
  3321. uint8_t blessCount = 0;
  3322. uint16_t flag = 0;
  3323. uint16_t pow2 = 2;
  3324. for (int i = 1; i <= maxClientBlessings; i++) {
  3325. if (player->hasBlessing(i)) {
  3326. if (i > 1)
  3327. blessCount++;
  3328. flag |= pow2;
  3329. }
  3330. pow2 = pow2 * 2;
  3331. }
  3332.  
  3333. msg.addByte(0x9C);
  3334. if (version >= 1200) {
  3335. if (blessCount >= 5) //Show up the glowing effect in items if have all blesses
  3336. flag |= 1;
  3337.  
  3338. msg.add<uint16_t>(flag);
  3339. msg.addByte((blessCount >= 7) ? 3 : ((blessCount >= 5) ? 2 : 1)); // 1 = Disabled | 2 = normal | 3 = green
  3340. }
  3341. else if (blessCount >= 5) {
  3342. msg.add<uint16_t>(0x01);
  3343. }
  3344. else {
  3345. msg.add<uint16_t>(0x00);
  3346. }
  3347. }
  3348.  
  3349. void ProtocolGame::sendStoreHighlight()
  3350. {
  3351. NetworkMessage msg;
  3352. bool haveSale = g_game.gameStore.haveCategoryByState(StoreState_t::SALE);
  3353. bool haveNewItem = g_game.gameStore.haveCategoryByState(StoreState_t::NEW);
  3354. msg.addByte(0x19);
  3355. msg.addByte((haveSale) ? 1 : 0);
  3356. msg.addByte((haveNewItem) ? 1 : 0);
  3357. writeToOutputBuffer(msg);
  3358. }
  3359.  
  3360. void ProtocolGame::sendPremiumTrigger()
  3361. {
  3362. if (!g_config.getBoolean(ConfigManager::FREE_PREMIUM))
  3363. {
  3364. NetworkMessage msg;
  3365. msg.addByte(0x9E);
  3366. msg.addByte(16);
  3367. for (uint16_t i = 0; i <= 15; i++)
  3368. {
  3369. //PREMIUM_TRIGGER_TRAIN_OFFLINE = false, PREMIUM_TRIGGER_XP_BOOST = false, PREMIUM_TRIGGER_MARKET = false, PREMIUM_TRIGGER_VIP_LIST = false, PREMIUM_TRIGGER_DEPOT_SPACE = false, PREMIUM_TRIGGER_INVITE_PRIVCHAT = false
  3370. msg.addByte(0x01);
  3371. }
  3372. writeToOutputBuffer(msg);
  3373. }
  3374. }
  3375.  
  3376. // Send preyInfo
  3377. void ProtocolGame::closeImbuingWindow()
  3378. {
  3379. NetworkMessage msg;
  3380. msg.addByte(0xEC);
  3381. writeToOutputBuffer(msg);
  3382. }
  3383.  
  3384. void ProtocolGame::initPreyData()
  3385. {
  3386. for (uint8_t i = 0; i <= PREY_SLOTNUM_THIRD; i++)
  3387. {
  3388. sendPreyData(static_cast<PreySlotNum_t>(i), PREY_STATE_LOCKED);
  3389. }
  3390.  
  3391. sendResourcesBalance();
  3392. sendPreyRerollPrice();
  3393. }
  3394.  
  3395. void ProtocolGame::sendPreyRerollPrice(uint32_t price /*= 0*/, uint8_t wildcard /*= 0*/, uint8_t directly /*= 0*/)
  3396. {
  3397. NetworkMessage msg;
  3398. msg.addByte(0xE9); // reroll prices
  3399. msg.add<uint32_t>(price); // price
  3400. if (version >= 1200) {
  3401. msg.addByte(wildcard); // wildcard
  3402. msg.addByte(directly); // selectCreatureDirectly price (5 in tibia)
  3403.  
  3404. // Prey Task
  3405. msg.add<uint32_t>(0);
  3406. msg.add<uint32_t>(0);
  3407. msg.addByte(0);
  3408. msg.addByte(0);
  3409. }
  3410.  
  3411. writeToOutputBuffer(msg);
  3412. }
  3413.  
  3414. void ProtocolGame::sendPreyData(PreySlotNum_t slot, PreyState_t slotState)
  3415. {
  3416. NetworkMessage msg;
  3417. msg.addByte(0xE8);
  3418. msg.addByte(slot);
  3419.  
  3420. if (version >= 1200)
  3421. msg.addByte(slotState);
  3422. else
  3423. msg.addByte(0x00);
  3424. msg.addByte(0x00); // empty byte
  3425. if (version >= 1200)
  3426. msg.add<uint32_t>(0); // next free roll
  3427. else
  3428. msg.add<uint16_t>(0);
  3429.  
  3430. if (version >= 1200) {
  3431. msg.addByte(0x00); // wildCards
  3432. }
  3433.  
  3434. writeToOutputBuffer(msg);
  3435. }
  3436.  
  3437. void ProtocolGame::sendTextMessage(const TextMessage &message)
  3438. {
  3439. NetworkMessage msg;
  3440. msg.addByte(0xB4);
  3441. TextMessage newMsg = message;
  3442. if(version < 1200 && newMsg.type > MESSAGE_MARKET)
  3443. {
  3444. switch(newMsg.type)
  3445. {
  3446. case MESSAGE_BOOSTED_CREATURE:
  3447. newMsg.type = MESSAGE_LOOT;
  3448. break;
  3449. case MESSAGE_OFFLINE_TRAINING:
  3450. case MESSAGE_BEYOND_LAST:
  3451. case MESSAGE_TRANSACTION:
  3452. newMsg.type = MESSAGE_EVENT_ADVANCE;
  3453. break;
  3454. case MESSAGE_ATTENTION:
  3455. case MESSAGE_POTION:
  3456. newMsg.type = MESSAGE_HEALED;
  3457. break;
  3458. default:
  3459. break;
  3460. }
  3461. }
  3462. msg.addByte(newMsg.type);
  3463. switch (newMsg.type)
  3464. {
  3465. case MESSAGE_DAMAGE_DEALT:
  3466. case MESSAGE_DAMAGE_RECEIVED:
  3467. case MESSAGE_DAMAGE_OTHERS:
  3468. {
  3469. msg.addPosition(newMsg.position);
  3470. msg.add<uint32_t>(newMsg.primary.value);
  3471. msg.addByte(newMsg.primary.color);
  3472. msg.add<uint32_t>(newMsg.secondary.value);
  3473. msg.addByte(newMsg.secondary.color);
  3474. break;
  3475. }
  3476. case MESSAGE_HEALED:
  3477. case MESSAGE_HEALED_OTHERS:
  3478. case MESSAGE_EXPERIENCE:
  3479. case MESSAGE_EXPERIENCE_OTHERS:
  3480. {
  3481. msg.addPosition(newMsg.position);
  3482. msg.add<uint32_t>(newMsg.primary.value);
  3483. msg.addByte(newMsg.primary.color);
  3484. break;
  3485. }
  3486. case MESSAGE_GUILD:
  3487. case MESSAGE_PARTY_MANAGEMENT:
  3488. case MESSAGE_PARTY:
  3489. msg.add<uint16_t>(newMsg.channelId);
  3490. break;
  3491. default:
  3492. {
  3493. break;
  3494. }
  3495. }
  3496. msg.addString(newMsg.text);
  3497. writeToOutputBuffer(msg);
  3498. }
  3499.  
  3500. void ProtocolGame::sendClosePrivate(uint16_t channelId)
  3501. {
  3502. NetworkMessage msg;
  3503. msg.addByte(0xB3);
  3504. msg.add<uint16_t>(channelId);
  3505. writeToOutputBuffer(msg);
  3506. }
  3507.  
  3508. void ProtocolGame::sendCreatePrivateChannel(uint16_t channelId, const std::string &channelName)
  3509. {
  3510. NetworkMessage msg;
  3511. msg.addByte(0xB2);
  3512. msg.add<uint16_t>(channelId);
  3513. msg.addString(channelName);
  3514. msg.add<uint16_t>(0x01);
  3515. msg.addString(player->getName());
  3516. msg.add<uint16_t>(0x00);
  3517. writeToOutputBuffer(msg);
  3518. }
  3519.  
  3520. void ProtocolGame::sendChannelsDialog()
  3521. {
  3522. NetworkMessage msg;
  3523. msg.addByte(0xAB);
  3524.  
  3525. const ChannelList &list = g_chat->getChannelList(*player);
  3526. msg.addByte(list.size());
  3527. for (ChatChannel *channel : list)
  3528. {
  3529. msg.add<uint16_t>(channel->getId());
  3530. msg.addString(channel->getName());
  3531. }
  3532.  
  3533. writeToOutputBuffer(msg);
  3534. }
  3535.  
  3536. void ProtocolGame::sendChannel(uint16_t channelId, const std::string &channelName, const UsersMap *channelUsers, const InvitedMap *invitedUsers)
  3537. {
  3538. NetworkMessage msg;
  3539. msg.addByte(0xAC);
  3540.  
  3541. msg.add<uint16_t>(channelId);
  3542. msg.addString(channelName);
  3543.  
  3544. if (channelUsers)
  3545. {
  3546. msg.add<uint16_t>(channelUsers->size());
  3547. for (const auto &it : *channelUsers)
  3548. {
  3549. msg.addString(it.second->getName());
  3550. }
  3551. }
  3552. else
  3553. {
  3554. msg.add<uint16_t>(0x00);
  3555. }
  3556.  
  3557. if (invitedUsers)
  3558. {
  3559. msg.add<uint16_t>(invitedUsers->size());
  3560. for (const auto &it : *invitedUsers)
  3561. {
  3562. msg.addString(it.second->getName());
  3563. }
  3564. }
  3565. else
  3566. {
  3567. msg.add<uint16_t>(0x00);
  3568. }
  3569. writeToOutputBuffer(msg);
  3570. }
  3571.  
  3572. void ProtocolGame::sendChannelMessage(const std::string &author, const std::string &text, SpeakClasses type, uint16_t channel)
  3573. {
  3574. NetworkMessage msg;
  3575. msg.addByte(0xAA);
  3576. msg.add<uint32_t>(0x00);
  3577. msg.addString(author);
  3578. msg.add<uint16_t>(0x00);
  3579. msg.addByte(type);
  3580. msg.add<uint16_t>(channel);
  3581. msg.addString(text);
  3582. writeToOutputBuffer(msg);
  3583. }
  3584.  
  3585. void ProtocolGame::sendIcons(uint32_t icons)
  3586. {
  3587. NetworkMessage msg;
  3588. msg.addByte(0xA2);
  3589. if (version >= 1200) {
  3590. msg.add<uint32_t>(icons);
  3591. }
  3592. else {
  3593. msg.add<uint16_t>(static_cast<uint16_t>(icons));
  3594. }
  3595. writeToOutputBuffer(msg);
  3596. }
  3597.  
  3598. void ProtocolGame::sendUnjustifiedPoints(const uint8_t &dayProgress, const uint8_t &dayLeft, const uint8_t &weekProgress, const uint8_t &weekLeft, const uint8_t &monthProgress, const uint8_t &monthLeft, const uint8_t &skullDuration)
  3599. {
  3600. NetworkMessage msg;
  3601. msg.addByte(0xB7);
  3602. msg.addByte(dayProgress);
  3603. msg.addByte(dayLeft);
  3604. msg.addByte(weekProgress);
  3605. msg.addByte(weekLeft);
  3606. msg.addByte(monthProgress);
  3607. msg.addByte(monthLeft);
  3608. msg.addByte(skullDuration);
  3609. writeToOutputBuffer(msg);
  3610. }
  3611.  
  3612. void ProtocolGame::sendContainer(uint8_t cid, const Container *container, bool hasParent, uint16_t firstIndex)
  3613. {
  3614. NetworkMessage msg;
  3615. msg.addByte(0x6E);
  3616.  
  3617. msg.addByte(cid);
  3618.  
  3619. if (container->getID() == ITEM_BROWSEFIELD)
  3620. {
  3621. AddItem(msg, ITEM_BAG, 1);
  3622. msg.addString("Browse Field");
  3623. }
  3624. else
  3625. {
  3626. AddItem(msg, container);
  3627. msg.addString(container->getName());
  3628. }
  3629.  
  3630. msg.addByte(container->capacity());
  3631.  
  3632. msg.addByte(hasParent ? 0x01 : 0x00);
  3633.  
  3634. if (version >= 1200)
  3635. msg.addByte(0x00); // To-do: Depot Find (boolean)
  3636.  
  3637. msg.addByte(container->isUnlocked() ? 0x01 : 0x00); // Drag and drop
  3638. msg.addByte(container->hasPagination() ? 0x01 : 0x00); // Pagination
  3639.  
  3640. uint32_t containerSize = container->size();
  3641. msg.add<uint16_t>(containerSize);
  3642. msg.add<uint16_t>(firstIndex);
  3643.  
  3644. uint32_t maxItemsToSend;
  3645.  
  3646. if (container->hasPagination() && firstIndex > 0)
  3647. {
  3648. maxItemsToSend = std::min<uint32_t>(container->capacity(), containerSize - firstIndex);
  3649. }
  3650. else
  3651. {
  3652. maxItemsToSend = container->capacity();
  3653. }
  3654.  
  3655. if (firstIndex >= containerSize)
  3656. {
  3657. msg.addByte(0x00);
  3658. }
  3659. else
  3660. {
  3661. msg.addByte(std::min<uint32_t>(maxItemsToSend, containerSize));
  3662.  
  3663. uint32_t i = 0;
  3664. const ItemDeque &itemList = container->getItemList();
  3665. for (ItemDeque::const_iterator it = itemList.begin() + firstIndex, end = itemList.end(); i < maxItemsToSend && it != end; ++it, ++i)
  3666. {
  3667. AddItem(msg, *it);
  3668. }
  3669. }
  3670. writeToOutputBuffer(msg);
  3671. }
  3672.  
  3673. void ProtocolGame::sendLootContainers()
  3674. {
  3675. if (version < 1200)
  3676. return;
  3677. NetworkMessage msg;
  3678. msg.addByte(0xC0);
  3679. msg.addByte(player->quickLootFallbackToMainContainer ? 1 : 0);
  3680. std::map<ObjectCategory_t, Container *> quickLoot;
  3681. for (auto it : player->quickLootContainers)
  3682. {
  3683. if (it.second && !it.second->isRemoved())
  3684. {
  3685. quickLoot[it.first] = it.second;
  3686. }
  3687. }
  3688. msg.addByte(quickLoot.size());
  3689. for (auto it : quickLoot)
  3690. {
  3691. msg.addByte(it.first);
  3692. msg.add<uint16_t>(it.second->getClientID());
  3693. }
  3694.  
  3695. writeToOutputBuffer(msg);
  3696. }
  3697.  
  3698. void ProtocolGame::sendLootStats(Item *item, uint8_t count)
  3699. {
  3700. if (version < 1200)
  3701. return;
  3702. if (!item)
  3703. {
  3704. return;
  3705. }
  3706. Item* lootedItem = nullptr;
  3707. lootedItem = item->clone();
  3708. lootedItem->setItemCount(count);
  3709.  
  3710. NetworkMessage msg;
  3711. msg.addByte(0xCF);
  3712. AddItem(msg, lootedItem);
  3713. msg.addString(lootedItem->getName());
  3714.  
  3715. lootedItem = nullptr;
  3716. writeToOutputBuffer(msg);
  3717. }
  3718.  
  3719. void ProtocolGame::sendShop(Npc *npc, const ShopInfoList &itemList)
  3720. {
  3721. NetworkMessage msg;
  3722. msg.addByte(0x7A);
  3723. msg.addString(npc->getName());
  3724. if (version >= 1200) {
  3725. msg.add<uint16_t>(npc->getCurrencyTrading());
  3726. msg.addString(std::string()); // ??
  3727. }
  3728.  
  3729. uint16_t itemsToSend = std::min<size_t>(itemList.size(), std::numeric_limits<uint16_t>::max());
  3730. msg.add<uint16_t>(itemsToSend);
  3731.  
  3732. uint16_t i = 0;
  3733. for (auto it = itemList.begin(); i < itemsToSend; ++it, ++i)
  3734. {
  3735. AddShopItem(msg, *it);
  3736. }
  3737.  
  3738. writeToOutputBuffer(msg);
  3739. }
  3740.  
  3741. void ProtocolGame::sendCloseShop()
  3742. {
  3743. NetworkMessage msg;
  3744. msg.addByte(0x7C);
  3745. writeToOutputBuffer(msg);
  3746. }
  3747.  
  3748. void ProtocolGame::sendClientCheck()
  3749. {
  3750. NetworkMessage msg;
  3751. msg.addByte(0x63);
  3752. msg.add<uint32_t>(1);
  3753. msg.addByte(1);
  3754. writeToOutputBuffer(msg);
  3755. }
  3756.  
  3757. void ProtocolGame::sendGameNews()
  3758. {
  3759. NetworkMessage msg;
  3760. msg.addByte(0x98);
  3761. msg.add<uint32_t>(1); // unknown
  3762. msg.addByte(1); //(0 = open | 1 = highlight)
  3763. writeToOutputBuffer(msg);
  3764. }
  3765.  
  3766. void ProtocolGame::sendResourcesBalance(uint64_t money /*= 0*/, uint64_t bank /*= 0*/, uint64_t prey /*= 0*/)
  3767. {
  3768. sendResourceBalance(RESOURCE_BANK, bank);
  3769. sendResourceBalance(RESOURCE_INVENTORY, money);
  3770. sendResourceBalance(RESOURCE_PREY, prey);
  3771. }
  3772.  
  3773. void ProtocolGame::sendResourceBalance(Resource_t resourceType, uint64_t value)
  3774. {
  3775. NetworkMessage msg;
  3776. msg.addByte(0xEE);
  3777. msg.addByte(resourceType);
  3778. msg.add<uint64_t>(value);
  3779. writeToOutputBuffer(msg);
  3780. }
  3781.  
  3782. void ProtocolGame::sendSaleItemList(const std::vector<ShopInfo> &shop, const std::map<uint32_t, uint32_t> &inventoryMap)
  3783. {
  3784. //Since we already have full inventory map we shouldn't call getMoney here - it is simply wasting cpu power
  3785. uint64_t playerMoney = 0;
  3786. auto it = inventoryMap.find(ITEM_CRYSTAL_COIN);
  3787. if (it != inventoryMap.end())
  3788. {
  3789. playerMoney += static_cast<uint64_t>(it->second) * 10000;
  3790. }
  3791. it = inventoryMap.find(ITEM_PLATINUM_COIN);
  3792. if (it != inventoryMap.end())
  3793. {
  3794. playerMoney += static_cast<uint64_t>(it->second) * 100;
  3795. }
  3796. it = inventoryMap.find(ITEM_GOLD_COIN);
  3797. if (it != inventoryMap.end())
  3798. {
  3799. playerMoney += static_cast<uint64_t>(it->second);
  3800. }
  3801.  
  3802. NetworkMessage msg;
  3803. msg.addByte(0xEE);
  3804. msg.addByte(0x00);
  3805. msg.add<uint64_t>(player->getBankBalance());
  3806. uint16_t currency = player->getOnlyShopOwner() ? player->getOnlyShopOwner()->getCurrency() : ITEM_GOLD_COIN;
  3807. msg.addByte(0xEE);
  3808. if (currency == ITEM_GOLD_COIN) {
  3809. msg.addByte(0x01);
  3810. msg.add<uint64_t>(playerMoney);
  3811. } else {
  3812. msg.addByte(0x02);
  3813. uint64_t newCurrency = 0;
  3814. auto search = inventoryMap.find(currency);
  3815. if (search != inventoryMap.end()) {
  3816. newCurrency += static_cast<uint64_t>(search->second);
  3817. }
  3818. msg.add<uint64_t>(newCurrency);
  3819. }
  3820. msg.addByte(0x7B);
  3821. msg.add<uint64_t>(version >= 1200 ? playerMoney : (playerMoney + player->getBankBalance()));
  3822.  
  3823. uint8_t itemsToSend = 0;
  3824. auto msgPosition = msg.getBufferPosition();
  3825. msg.skipBytes(1);
  3826.  
  3827. for (const ShopInfo &shopInfo : shop)
  3828. {
  3829. if (shopInfo.sellPrice == 0)
  3830. {
  3831. continue;
  3832. }
  3833.  
  3834. uint32_t index = static_cast<uint32_t>(shopInfo.itemId);
  3835. if (Item::items[shopInfo.itemId].isFluidContainer())
  3836. {
  3837. index |= (static_cast<uint32_t>(shopInfo.subType) << 16);
  3838. }
  3839.  
  3840. it = inventoryMap.find(index);
  3841. if (it != inventoryMap.end())
  3842. {
  3843. msg.addItemId(shopInfo.itemId);
  3844. msg.addByte(std::min<uint32_t>(it->second, std::numeric_limits<uint8_t>::max()));
  3845. if (++itemsToSend >= 0xFF)
  3846. {
  3847. break;
  3848. }
  3849. }
  3850. }
  3851.  
  3852. msg.setBufferPosition(msgPosition);
  3853. msg.addByte(itemsToSend);
  3854. writeToOutputBuffer(msg);
  3855. }
  3856.  
  3857. void ProtocolGame::sendMarketEnter(uint32_t depotId)
  3858. {
  3859. NetworkMessage msg;
  3860. msg.addByte(0xF6);
  3861. if (version < 1200)
  3862. msg.add<uint64_t>(player->getBankBalance());
  3863. msg.addByte(std::min<uint32_t>(IOMarket::getPlayerOfferCount(player->getGUID()), std::numeric_limits<uint8_t>::max()));
  3864.  
  3865. DepotLocker *depotLocker = player->getDepotLocker(depotId);
  3866. if (!depotLocker)
  3867. {
  3868. msg.add<uint16_t>(0x00);
  3869. writeToOutputBuffer(msg);
  3870. return;
  3871. }
  3872.  
  3873. player->setInMarket(true);
  3874.  
  3875. std::map<uint16_t, uint32_t> depotItems;
  3876. std::forward_list<Container *> containerList{depotLocker};
  3877.  
  3878. do
  3879. {
  3880. Container *container = containerList.front();
  3881. containerList.pop_front();
  3882.  
  3883. for (Item *item : container->getItemList())
  3884. {
  3885. Container *c = item->getContainer();
  3886. if (c && !c->empty())
  3887. {
  3888. containerList.push_front(c);
  3889. continue;
  3890. }
  3891.  
  3892. const ItemType &itemType = Item::items[item->getID()];
  3893. if (itemType.wareId == 0)
  3894. {
  3895. continue;
  3896. }
  3897.  
  3898. if (c && (!itemType.isContainer() || c->capacity() != itemType.maxItems))
  3899. {
  3900. continue;
  3901. }
  3902.  
  3903. if (!item->hasMarketAttributes())
  3904. {
  3905. continue;
  3906. }
  3907.  
  3908. depotItems[itemType.wareId] += Item::countByType(item, -1);
  3909. }
  3910. } while (!containerList.empty());
  3911. StashItemList stashToSend = player->getStashItems();
  3912. uint16_t size = 0;
  3913. for (auto item : stashToSend)
  3914. {
  3915. size += ceil(item.second);
  3916. }
  3917.  
  3918. do
  3919. {
  3920. for (auto item : stashToSend)
  3921. {
  3922.  
  3923. const ItemType &itemType = Item::items[Item::items.getItemIdByClientId(item.first).id];
  3924. if (itemType.wareId == 0)
  3925. {
  3926. continue;
  3927. }
  3928.  
  3929. size = size - item.second;
  3930. depotItems[itemType.wareId] += item.second;
  3931. }
  3932. } while (size > 0);
  3933. uint16_t itemsToSend = std::min<size_t>(depotItems.size(), std::numeric_limits<uint16_t>::max());
  3934. msg.add<uint16_t>(itemsToSend);
  3935.  
  3936. uint16_t i = 0;
  3937. for (std::map<uint16_t, uint32_t>::const_iterator it = depotItems.begin(); i < itemsToSend; ++it, ++i)
  3938. {
  3939. msg.add<uint16_t>(it->first);
  3940. msg.add<uint16_t>(std::min<uint32_t>(0xFFFF, it->second));
  3941. }
  3942.  
  3943. writeToOutputBuffer(msg);
  3944.  
  3945. updateCoinBalance();
  3946. sendResourcesBalance(player->getMoney(), player->getBankBalance());
  3947. }
  3948.  
  3949. void ProtocolGame::sendCoinBalance()
  3950. {
  3951. if (!player)
  3952. {
  3953. return;
  3954. }
  3955.  
  3956. // send is updating
  3957. // TODO: export this to it own function
  3958. NetworkMessage msg;
  3959. msg.addByte(0xF2);
  3960. msg.addByte(0x01);
  3961. writeToOutputBuffer(msg);
  3962.  
  3963. msg.reset();
  3964.  
  3965. // send update
  3966. msg.addByte(0xDF);
  3967. msg.addByte(0x01);
  3968.  
  3969. msg.add<uint32_t>(player->coinBalance); // Normal Coins
  3970. msg.add<uint32_t>(player->coinBalance); // Transferable Coins
  3971. if (version >= 1200) {
  3972. msg.add<uint32_t>(player->coinBalance); // Reserved Auction Coin
  3973. msg.add<uint32_t>(0); // Tournament Coins
  3974. }
  3975.  
  3976. writeToOutputBuffer(msg);
  3977. }
  3978.  
  3979. void ProtocolGame::updateCoinBalance()
  3980. {
  3981. g_dispatcher.addTask(
  3982. createTask(std::bind([](ProtocolGame *client) {
  3983. if (client && client->player)
  3984. {
  3985. account::Account account;
  3986. account.LoadAccountDB(client->player->getAccount());
  3987. uint32_t coins;
  3988. account.GetCoins(&coins);
  3989. client->player->coinBalance = coins;
  3990. client->sendCoinBalance();
  3991. }
  3992. },
  3993. this)));
  3994. }
  3995.  
  3996. void ProtocolGame::sendMarketLeave()
  3997. {
  3998. NetworkMessage msg;
  3999. msg.addByte(0xF7);
  4000. writeToOutputBuffer(msg);
  4001. }
  4002.  
  4003. void ProtocolGame::sendMarketBrowseItem(uint16_t itemId, const MarketOfferList &buyOffers, const MarketOfferList &sellOffers)
  4004. {
  4005. NetworkMessage msg;
  4006.  
  4007. msg.addByte(0xF9);
  4008. msg.addItemId(itemId);
  4009.  
  4010. msg.add<uint32_t>(buyOffers.size());
  4011. for (const MarketOffer &offer : buyOffers)
  4012. {
  4013. msg.add<uint32_t>(offer.timestamp);
  4014. msg.add<uint16_t>(offer.counter);
  4015. msg.add<uint16_t>(offer.amount);
  4016. msg.add<uint32_t>(offer.price);
  4017. msg.addString(offer.playerName);
  4018. }
  4019.  
  4020. msg.add<uint32_t>(sellOffers.size());
  4021. for (const MarketOffer &offer : sellOffers)
  4022. {
  4023. msg.add<uint32_t>(offer.timestamp);
  4024. msg.add<uint16_t>(offer.counter);
  4025. msg.add<uint16_t>(offer.amount);
  4026. msg.add<uint32_t>(offer.price);
  4027. msg.addString(offer.playerName);
  4028. }
  4029.  
  4030. updateCoinBalance();
  4031. writeToOutputBuffer(msg);
  4032. }
  4033.  
  4034. void ProtocolGame::sendMarketAcceptOffer(const MarketOfferEx &offer)
  4035. {
  4036. NetworkMessage msg;
  4037. msg.addByte(0xF9);
  4038. msg.addItemId(offer.itemId);
  4039.  
  4040. if (offer.type == MARKETACTION_BUY)
  4041. {
  4042. msg.add<uint32_t>(0x01);
  4043. msg.add<uint32_t>(offer.timestamp);
  4044. msg.add<uint16_t>(offer.counter);
  4045. msg.add<uint16_t>(offer.amount);
  4046. msg.add<uint32_t>(offer.price);
  4047. msg.addString(offer.playerName);
  4048. msg.add<uint32_t>(0x00);
  4049. }
  4050. else
  4051. {
  4052. msg.add<uint32_t>(0x00);
  4053. msg.add<uint32_t>(0x01);
  4054. msg.add<uint32_t>(offer.timestamp);
  4055. msg.add<uint16_t>(offer.counter);
  4056. msg.add<uint16_t>(offer.amount);
  4057. msg.add<uint32_t>(offer.price);
  4058. msg.addString(offer.playerName);
  4059. }
  4060.  
  4061. writeToOutputBuffer(msg);
  4062. }
  4063.  
  4064. void ProtocolGame::sendMarketBrowseOwnOffers(const MarketOfferList &buyOffers, const MarketOfferList &sellOffers)
  4065. {
  4066. NetworkMessage msg;
  4067. msg.addByte(0xF9);
  4068. msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);
  4069.  
  4070. msg.add<uint32_t>(buyOffers.size());
  4071. for (const MarketOffer &offer : buyOffers)
  4072. {
  4073. msg.add<uint32_t>(offer.timestamp);
  4074. msg.add<uint16_t>(offer.counter);
  4075. msg.addItemId(offer.itemId);
  4076. msg.add<uint16_t>(offer.amount);
  4077. msg.add<uint32_t>(offer.price);
  4078. }
  4079.  
  4080. msg.add<uint32_t>(sellOffers.size());
  4081. for (const MarketOffer &offer : sellOffers)
  4082. {
  4083. msg.add<uint32_t>(offer.timestamp);
  4084. msg.add<uint16_t>(offer.counter);
  4085. msg.addItemId(offer.itemId);
  4086. msg.add<uint16_t>(offer.amount);
  4087. msg.add<uint32_t>(offer.price);
  4088. }
  4089.  
  4090. writeToOutputBuffer(msg);
  4091. }
  4092.  
  4093. void ProtocolGame::sendMarketCancelOffer(const MarketOfferEx &offer)
  4094. {
  4095. NetworkMessage msg;
  4096. msg.addByte(0xF9);
  4097. msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);
  4098.  
  4099. if (offer.type == MARKETACTION_BUY)
  4100. {
  4101. msg.add<uint32_t>(0x01);
  4102. msg.add<uint32_t>(offer.timestamp);
  4103. msg.add<uint16_t>(offer.counter);
  4104. msg.addItemId(offer.itemId);
  4105. msg.add<uint16_t>(offer.amount);
  4106. msg.add<uint32_t>(offer.price);
  4107. msg.add<uint32_t>(0x00);
  4108. }
  4109. else
  4110. {
  4111. msg.add<uint32_t>(0x00);
  4112. msg.add<uint32_t>(0x01);
  4113. msg.add<uint32_t>(offer.timestamp);
  4114. msg.add<uint16_t>(offer.counter);
  4115. msg.addItemId(offer.itemId);
  4116. msg.add<uint16_t>(offer.amount);
  4117. msg.add<uint32_t>(offer.price);
  4118. }
  4119.  
  4120. writeToOutputBuffer(msg);
  4121. }
  4122.  
  4123. void ProtocolGame::sendMarketBrowseOwnHistory(const HistoryMarketOfferList &buyOffers, const HistoryMarketOfferList &sellOffers)
  4124. {
  4125. uint32_t i = 0;
  4126. std::map<uint32_t, uint16_t> counterMap;
  4127. uint32_t buyOffersToSend = std::min<uint32_t>(buyOffers.size(), 810 + std::max<int32_t>(0, 810 - sellOffers.size()));
  4128. uint32_t sellOffersToSend = std::min<uint32_t>(sellOffers.size(), 810 + std::max<int32_t>(0, 810 - buyOffers.size()));
  4129.  
  4130. NetworkMessage msg;
  4131. msg.addByte(0xF9);
  4132. msg.add<uint16_t>(MARKETREQUEST_OWN_HISTORY);
  4133.  
  4134. msg.add<uint32_t>(buyOffersToSend);
  4135. for (auto it = buyOffers.begin(); i < buyOffersToSend; ++it, ++i)
  4136. {
  4137. msg.add<uint32_t>(it->timestamp);
  4138. msg.add<uint16_t>(counterMap[it->timestamp]++);
  4139. msg.addItemId(it->itemId);
  4140. msg.add<uint16_t>(it->amount);
  4141. msg.add<uint32_t>(it->price);
  4142. msg.addByte(it->state);
  4143. }
  4144.  
  4145. counterMap.clear();
  4146. i = 0;
  4147.  
  4148. msg.add<uint32_t>(sellOffersToSend);
  4149. for (auto it = sellOffers.begin(); i < sellOffersToSend; ++it, ++i)
  4150. {
  4151. msg.add<uint32_t>(it->timestamp);
  4152. msg.add<uint16_t>(counterMap[it->timestamp]++);
  4153. msg.addItemId(it->itemId);
  4154. msg.add<uint16_t>(it->amount);
  4155. msg.add<uint32_t>(it->price);
  4156. msg.addByte(it->state);
  4157. }
  4158.  
  4159. writeToOutputBuffer(msg);
  4160. }
  4161.  
  4162. void ProtocolGame::sendMarketDetail(uint16_t itemId)
  4163. {
  4164. NetworkMessage msg;
  4165. msg.addByte(0xF8);
  4166. msg.addItemId(itemId);
  4167.  
  4168. const ItemType &it = Item::items[itemId];
  4169. if (it.armor != 0)
  4170. {
  4171. msg.addString(std::to_string(it.armor));
  4172. }
  4173. else
  4174. {
  4175. msg.add<uint16_t>(0x00);
  4176. }
  4177.  
  4178. if (it.attack != 0)
  4179. {
  4180. // TODO: chance to hit, range
  4181. // example:
  4182. // "attack +x, chance to hit +y%, z fields"
  4183. if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0)
  4184. {
  4185. std::ostringstream ss;
  4186. ss << it.attack << " physical +" << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType);
  4187. msg.addString(ss.str());
  4188. }
  4189. else
  4190. {
  4191. msg.addString(std::to_string(it.attack));
  4192. }
  4193. }
  4194. else
  4195. {
  4196. msg.add<uint16_t>(0x00);
  4197. }
  4198.  
  4199. if (it.isContainer())
  4200. {
  4201. msg.addString(std::to_string(it.maxItems));
  4202. }
  4203. else
  4204. {
  4205. msg.add<uint16_t>(0x00);
  4206. }
  4207.  
  4208. if (it.defense != 0)
  4209. {
  4210. if (it.extraDefense != 0)
  4211. {
  4212. std::ostringstream ss;
  4213. ss << it.defense << ' ' << std::showpos << it.extraDefense << std::noshowpos;
  4214. msg.addString(ss.str());
  4215. }
  4216. else
  4217. {
  4218. msg.addString(std::to_string(it.defense));
  4219. }
  4220. }
  4221. else
  4222. {
  4223. msg.add<uint16_t>(0x00);
  4224. }
  4225.  
  4226. if (!it.description.empty())
  4227. {
  4228. const std::string &descr = it.description;
  4229. if (descr.back() == '.')
  4230. {
  4231. msg.addString(std::string(descr, 0, descr.length() - 1));
  4232. }
  4233. else
  4234. {
  4235. msg.addString(descr);
  4236. }
  4237. }
  4238. else
  4239. {
  4240. msg.add<uint16_t>(0x00);
  4241. }
  4242.  
  4243. if (it.decayTime != 0)
  4244. {
  4245. std::ostringstream ss;
  4246. ss << it.decayTime << " seconds";
  4247. msg.addString(ss.str());
  4248. }
  4249. else
  4250. {
  4251. msg.add<uint16_t>(0x00);
  4252. }
  4253.  
  4254. if (it.abilities)
  4255. {
  4256. std::ostringstream ss;
  4257. bool separator = false;
  4258.  
  4259. for (size_t i = 0; i < COMBAT_COUNT; ++i)
  4260. {
  4261. if (it.abilities->absorbPercent[i] == 0)
  4262. {
  4263. continue;
  4264. }
  4265.  
  4266. if (separator)
  4267. {
  4268. ss << ", ";
  4269. }
  4270. else
  4271. {
  4272. separator = true;
  4273. }
  4274.  
  4275. ss << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%';
  4276. }
  4277.  
  4278. msg.addString(ss.str());
  4279. }
  4280. else
  4281. {
  4282. msg.add<uint16_t>(0x00);
  4283. }
  4284.  
  4285. if (it.minReqLevel != 0)
  4286. {
  4287. msg.addString(std::to_string(it.minReqLevel));
  4288. }
  4289. else
  4290. {
  4291. msg.add<uint16_t>(0x00);
  4292. }
  4293.  
  4294. if (it.minReqMagicLevel != 0)
  4295. {
  4296. msg.addString(std::to_string(it.minReqMagicLevel));
  4297. }
  4298. else
  4299. {
  4300. msg.add<uint16_t>(0x00);
  4301. }
  4302.  
  4303. msg.addString(it.vocationString);
  4304.  
  4305. msg.addString(it.runeSpellName);
  4306.  
  4307. if (it.abilities)
  4308. {
  4309. std::ostringstream ss;
  4310. bool separator = false;
  4311.  
  4312. for (uint8_t i = SKILL_FIRST; i <= SKILL_FISHING; i++)
  4313. {
  4314. if (!it.abilities->skills[i])
  4315. {
  4316. continue;
  4317. }
  4318.  
  4319. if (separator)
  4320. {
  4321. ss << ", ";
  4322. }
  4323. else
  4324. {
  4325. separator = true;
  4326. }
  4327.  
  4328. ss << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos;
  4329. }
  4330.  
  4331. for (uint8_t i = SKILL_CRITICAL_HIT_CHANCE; i <= SKILL_LAST; i++)
  4332. {
  4333. if (!it.abilities->skills[i])
  4334. {
  4335. continue;
  4336. }
  4337.  
  4338. if (separator)
  4339. {
  4340. ss << ", ";
  4341. }
  4342. else
  4343. {
  4344. separator = true;
  4345. }
  4346.  
  4347. ss << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos << '%';
  4348. }
  4349.  
  4350. if (it.abilities->stats[STAT_MAGICPOINTS] != 0)
  4351. {
  4352. if (separator)
  4353. {
  4354. ss << ", ";
  4355. }
  4356. else
  4357. {
  4358. separator = true;
  4359. }
  4360.  
  4361. ss << "magic level " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos;
  4362. }
  4363.  
  4364. if (it.abilities->speed != 0)
  4365. {
  4366. if (separator)
  4367. {
  4368. ss << ", ";
  4369. }
  4370.  
  4371. ss << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos;
  4372. }
  4373.  
  4374. msg.addString(ss.str());
  4375. }
  4376. else
  4377. {
  4378. msg.add<uint16_t>(0x00);
  4379. }
  4380.  
  4381. if (it.charges != 0)
  4382. {
  4383. msg.addString(std::to_string(it.charges));
  4384. }
  4385. else
  4386. {
  4387. msg.add<uint16_t>(0x00);
  4388. }
  4389.  
  4390. std::string weaponName = getWeaponName(it.weaponType);
  4391.  
  4392. if (it.slotPosition & SLOTP_TWO_HAND)
  4393. {
  4394. if (!weaponName.empty())
  4395. {
  4396. weaponName += ", two-handed";
  4397. }
  4398. else
  4399. {
  4400. weaponName = "two-handed";
  4401. }
  4402. }
  4403.  
  4404. msg.addString(weaponName);
  4405.  
  4406. if (it.weight != 0)
  4407. {
  4408. std::ostringstream ss;
  4409. if (it.weight < 10)
  4410. {
  4411. ss << "0.0" << it.weight;
  4412. }
  4413. else if (it.weight < 100)
  4414. {
  4415. ss << "0." << it.weight;
  4416. }
  4417. else
  4418. {
  4419. std::string weightString = std::to_string(it.weight);
  4420. weightString.insert(weightString.end() - 2, '.');
  4421. ss << weightString;
  4422. }
  4423. ss << " oz";
  4424. msg.addString(ss.str());
  4425. }
  4426. else
  4427. {
  4428. msg.add<uint16_t>(0x00);
  4429. }
  4430.  
  4431. uint8_t slot = Item::items[itemId].imbuingSlots;
  4432. if (slot > 0)
  4433. {
  4434. msg.addString(std::to_string(slot));
  4435. }
  4436. else
  4437. {
  4438. msg.add<uint16_t>(0x00);
  4439. }
  4440.  
  4441. MarketStatistics *statistics = IOMarket::getInstance().getPurchaseStatistics(itemId);
  4442. if (statistics)
  4443. {
  4444. msg.addByte(0x01);
  4445. msg.add<uint32_t>(statistics->numTransactions);
  4446. msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));
  4447. msg.add<uint32_t>(statistics->highestPrice);
  4448. msg.add<uint32_t>(statistics->lowestPrice);
  4449. }
  4450. else
  4451. {
  4452. msg.addByte(0x00);
  4453. }
  4454.  
  4455. statistics = IOMarket::getInstance().getSaleStatistics(itemId);
  4456. if (statistics)
  4457. {
  4458. msg.addByte(0x01);
  4459. msg.add<uint32_t>(statistics->numTransactions);
  4460. msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));
  4461. msg.add<uint32_t>(statistics->highestPrice);
  4462. msg.add<uint32_t>(statistics->lowestPrice);
  4463. }
  4464. else
  4465. {
  4466. msg.addByte(0x00);
  4467. }
  4468.  
  4469. writeToOutputBuffer(msg);
  4470. }
  4471.  
  4472. void ProtocolGame::sendTradeItemRequest(const std::string &traderName, const Item *item, bool ack)
  4473. {
  4474. NetworkMessage msg;
  4475.  
  4476. if (ack)
  4477. {
  4478. msg.addByte(0x7D);
  4479. }
  4480. else
  4481. {
  4482. msg.addByte(0x7E);
  4483. }
  4484.  
  4485. msg.addString(traderName);
  4486.  
  4487. if (const Container *tradeContainer = item->getContainer())
  4488. {
  4489. std::list<const Container *> listContainer{tradeContainer};
  4490. std::list<const Item *> itemList{tradeContainer};
  4491. while (!listContainer.empty())
  4492. {
  4493. const Container *container = listContainer.front();
  4494. listContainer.pop_front();
  4495.  
  4496. for (Item *containerItem : container->getItemList())
  4497. {
  4498. Container *tmpContainer = containerItem->getContainer();
  4499. if (tmpContainer)
  4500. {
  4501. listContainer.push_back(tmpContainer);
  4502. }
  4503. itemList.push_back(containerItem);
  4504. }
  4505. }
  4506.  
  4507. msg.addByte(itemList.size());
  4508. for (const Item *listItem : itemList)
  4509. {
  4510. AddItem(msg, listItem);
  4511. }
  4512. }
  4513. else
  4514. {
  4515. msg.addByte(0x01);
  4516. AddItem(msg, item);
  4517. }
  4518. writeToOutputBuffer(msg);
  4519. }
  4520.  
  4521. void ProtocolGame::sendCloseTrade()
  4522. {
  4523. NetworkMessage msg;
  4524. msg.addByte(0x7F);
  4525. writeToOutputBuffer(msg);
  4526. }
  4527.  
  4528. void ProtocolGame::sendCloseContainer(uint8_t cid)
  4529. {
  4530. NetworkMessage msg;
  4531. msg.addByte(0x6F);
  4532. msg.addByte(cid);
  4533. writeToOutputBuffer(msg);
  4534. }
  4535.  
  4536. void ProtocolGame::sendCreatureTurn(const Creature *creature, uint32_t stackPos)
  4537. {
  4538. if (!canSee(creature))
  4539. {
  4540. return;
  4541. }
  4542.  
  4543. NetworkMessage msg;
  4544. msg.addByte(0x6B);
  4545. msg.addPosition(creature->getPosition());
  4546. msg.addByte(stackPos);
  4547. msg.add<uint16_t>(0x63);
  4548. msg.add<uint32_t>(creature->getID());
  4549. msg.addByte(creature->getDirection());
  4550. msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01);
  4551. writeToOutputBuffer(msg);
  4552. }
  4553.  
  4554. void ProtocolGame::sendCreatureSay(const Creature *creature, SpeakClasses type, const std::string &text, const Position *pos /* = nullptr*/)
  4555. {
  4556. NetworkMessage msg;
  4557. msg.addByte(0xAA);
  4558.  
  4559. static uint32_t statementId = 0;
  4560. msg.add<uint32_t>(++statementId);
  4561.  
  4562. msg.addString(creature->getName());
  4563. if (version >= 1200)
  4564. msg.addByte(0x00); // Show (Traded)
  4565.  
  4566. //Add level only for players
  4567. if (const Player *speaker = creature->getPlayer())
  4568. {
  4569. msg.add<uint16_t>(speaker->getLevel());
  4570. }
  4571. else
  4572. {
  4573. msg.add<uint16_t>(0x00);
  4574. }
  4575.  
  4576. msg.addByte(type);
  4577. if (pos)
  4578. {
  4579. msg.addPosition(*pos);
  4580. }
  4581. else
  4582. {
  4583. msg.addPosition(creature->getPosition());
  4584. }
  4585.  
  4586. msg.addString(text);
  4587. writeToOutputBuffer(msg);
  4588. }
  4589.  
  4590. void ProtocolGame::sendToChannel(const Creature *creature, SpeakClasses type, const std::string &text, uint16_t channelId)
  4591. {
  4592. NetworkMessage msg;
  4593. msg.addByte(0xAA);
  4594.  
  4595. static uint32_t statementId = 0;
  4596. msg.add<uint32_t>(++statementId);
  4597. if (!creature)
  4598. {
  4599. msg.add<uint32_t>(0x00);
  4600. if (version >= 1200 && statementId != 0)
  4601. {
  4602. msg.addByte(0x00); // Show (Traded)
  4603. }
  4604. }
  4605. else if (type == TALKTYPE_CHANNEL_R2)
  4606. {
  4607. msg.add<uint32_t>(0x00);
  4608. if (version >= 1200 && statementId != 0)
  4609. {
  4610. msg.addByte(0x00); // Show (Traded)
  4611. }
  4612. type = TALKTYPE_CHANNEL_R1;
  4613. }
  4614. else
  4615. {
  4616. msg.addString(creature->getName());
  4617. if (version >= 1200 && statementId != 0)
  4618. {
  4619. msg.addByte(0x00); // Show (Traded)
  4620. }
  4621.  
  4622. //Add level only for players
  4623. if (const Player *speaker = creature->getPlayer())
  4624. {
  4625. msg.add<uint16_t>(speaker->getLevel());
  4626. }
  4627. else
  4628. {
  4629. msg.add<uint16_t>(0x00);
  4630. }
  4631. }
  4632.  
  4633. msg.addByte(type);
  4634. msg.add<uint16_t>(channelId);
  4635. msg.addString(text);
  4636. writeToOutputBuffer(msg);
  4637. }
  4638.  
  4639. void ProtocolGame::sendPrivateMessage(const Player *speaker, SpeakClasses type, const std::string &text)
  4640. {
  4641. NetworkMessage msg;
  4642. msg.addByte(0xAA);
  4643. static uint32_t statementId = 0;
  4644. msg.add<uint32_t>(++statementId);
  4645. if (speaker)
  4646. {
  4647. msg.addString(speaker->getName());
  4648. if (version >= 1200 && statementId != 0)
  4649. {
  4650. msg.addByte(0x00); // Show (Traded)
  4651. }
  4652. msg.add<uint16_t>(speaker->getLevel());
  4653. }
  4654. else
  4655. {
  4656. msg.add<uint32_t>(0x00);
  4657. if (version >= 1200 && statementId != 0)
  4658. {
  4659. msg.addByte(0x00); // Show (Traded)
  4660. }
  4661. }
  4662. msg.addByte(type);
  4663. msg.addString(text);
  4664. writeToOutputBuffer(msg);
  4665. }
  4666.  
  4667. void ProtocolGame::sendCancelTarget()
  4668. {
  4669. NetworkMessage msg;
  4670. msg.addByte(0xA3);
  4671. msg.add<uint32_t>(0x00);
  4672. writeToOutputBuffer(msg);
  4673. }
  4674.  
  4675. void ProtocolGame::sendChangeSpeed(const Creature *creature, uint32_t speed)
  4676. {
  4677. NetworkMessage msg;
  4678. msg.addByte(0x8F);
  4679. msg.add<uint32_t>(creature->getID());
  4680. msg.add<uint16_t>(creature->getBaseSpeed() / 2);
  4681. msg.add<uint16_t>(speed / 2);
  4682. writeToOutputBuffer(msg);
  4683. }
  4684.  
  4685. void ProtocolGame::sendCancelWalk()
  4686. {
  4687. if (player)
  4688. {
  4689. NetworkMessage msg;
  4690. msg.addByte(0xB5);
  4691. msg.addByte(player->getDirection());
  4692. writeToOutputBuffer(msg);
  4693. }
  4694. }
  4695.  
  4696. void ProtocolGame::sendSkills()
  4697. {
  4698. NetworkMessage msg;
  4699. AddPlayerSkills(msg);
  4700. writeToOutputBuffer(msg);
  4701. }
  4702.  
  4703. void ProtocolGame::sendPing()
  4704. {
  4705. if (player)
  4706. {
  4707. NetworkMessage msg;
  4708. msg.addByte(0x1D);
  4709. writeToOutputBuffer(msg);
  4710. }
  4711. }
  4712.  
  4713. void ProtocolGame::sendPingBack()
  4714. {
  4715. NetworkMessage msg;
  4716. msg.addByte(0x1E);
  4717. writeToOutputBuffer(msg);
  4718. }
  4719.  
  4720. void ProtocolGame::sendDistanceShoot(const Position &from, const Position &to, uint8_t type)
  4721. {
  4722. NetworkMessage msg;
  4723. msg.addByte(version >= 1200 ? 0x83 : 0x85);
  4724. msg.addPosition(from);
  4725. if (version >= 1200) {
  4726. msg.addByte(MAGIC_EFFECTS_CREATE_DISTANCEEFFECT);
  4727. msg.addByte(type);
  4728. msg.addByte(static_cast<uint8_t>(static_cast<int8_t>(static_cast<int32_t>(to.x) - static_cast<int32_t>(from.x))));
  4729. msg.addByte(static_cast<uint8_t>(static_cast<int8_t>(static_cast<int32_t>(to.y) - static_cast<int32_t>(from.y))));
  4730. msg.addByte(MAGIC_EFFECTS_END_LOOP);
  4731. }
  4732. else {
  4733. msg.addPosition(to);
  4734. msg.addByte(type);
  4735. }
  4736. writeToOutputBuffer(msg);
  4737. }
  4738.  
  4739. void ProtocolGame::sendRestingStatus(uint8_t protection)
  4740. {
  4741. if (!player || version < 1200)
  4742. {
  4743. return;
  4744. }
  4745.  
  4746. NetworkMessage msg;
  4747. msg.addByte(0xA9);
  4748. msg.addByte(protection); // 1 / 0
  4749. int32_t PlayerdailyStreak = 0;
  4750. player->getStorageValue(STORAGEVALUE_DAILYREWARD, PlayerdailyStreak);
  4751. msg.addByte(PlayerdailyStreak < 2 ? 0 : 1);
  4752. if (PlayerdailyStreak < 2)
  4753. {
  4754. msg.addString("Resting Area (no active bonus)");
  4755. }
  4756. else
  4757. {
  4758. std::ostringstream ss;
  4759. ss << "Active Resting Area Bonuses: ";
  4760. if (PlayerdailyStreak < DAILY_REWARD_DOUBLE_HP_REGENERATION)
  4761. {
  4762. ss << "\nHit Points Regeneration";
  4763. }
  4764. else
  4765. {
  4766. ss << "\nDouble Hit Points Regeneration";
  4767. }
  4768. if (PlayerdailyStreak >= DAILY_REWARD_MP_REGENERATION)
  4769. {
  4770. if (PlayerdailyStreak < DAILY_REWARD_DOUBLE_MP_REGENERATION)
  4771. {
  4772. ss << ",\nMana Points Regeneration";
  4773. }
  4774. else
  4775. {
  4776. ss << ",\nDouble Mana Points Regeneration";
  4777. }
  4778. }
  4779. if (PlayerdailyStreak >= DAILY_REWARD_STAMINA_REGENERATION)
  4780. {
  4781. ss << ",\nStamina Points Regeneration";
  4782. }
  4783. if (PlayerdailyStreak >= DAILY_REWARD_SOUL_REGENERATION)
  4784. {
  4785. ss << ",\nSoul Points Regeneration";
  4786. }
  4787. ss << ".";
  4788. msg.addString(ss.str());
  4789. }
  4790. writeToOutputBuffer(msg);
  4791. }
  4792.  
  4793. void ProtocolGame::sendMagicEffect(const Position &pos, uint8_t type)
  4794. {
  4795. if (!canSee(pos)) {
  4796. return;
  4797. }
  4798.  
  4799. NetworkMessage msg;
  4800. msg.addByte(0x83);
  4801. msg.addPosition(pos);
  4802. if (version >= 1200)
  4803. msg.addByte(MAGIC_EFFECTS_CREATE_EFFECT);
  4804. msg.addByte(type);
  4805. if (version >= 1200)
  4806. msg.addByte(MAGIC_EFFECTS_END_LOOP);
  4807. writeToOutputBuffer(msg);
  4808. }
  4809.  
  4810. void ProtocolGame::sendCreatureHealth(const Creature *creature)
  4811. {
  4812. if (version >= 1200 && creature->isHealthHidden())
  4813. {
  4814. return;
  4815. }
  4816.  
  4817. NetworkMessage msg;
  4818. msg.addByte(0x8C);
  4819. msg.add<uint32_t>(creature->getID());
  4820.  
  4821. if (creature->isHealthHidden())
  4822. {
  4823. msg.addByte(0x00);
  4824. }
  4825. else
  4826. {
  4827. msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100));
  4828. }
  4829. writeToOutputBuffer(msg);
  4830. }
  4831.  
  4832. void ProtocolGame::sendCreatureHelpers(uint32_t creatureId, uint16_t helpers)
  4833. {
  4834. if (version >= 1200) {
  4835. return;
  4836. }
  4837.  
  4838. NetworkMessage msg;
  4839. msg.addByte(0x94);
  4840. msg.add<uint32_t>(creatureId);
  4841. msg.add<uint16_t>(helpers);
  4842. writeToOutputBuffer(msg);
  4843. }
  4844.  
  4845. void ProtocolGame::sendFYIBox(const std::string &message)
  4846. {
  4847. NetworkMessage msg;
  4848. msg.addByte(0x15);
  4849. msg.addString(message);
  4850. writeToOutputBuffer(msg);
  4851. }
  4852.  
  4853. //tile
  4854. void ProtocolGame::sendMapDescription(const Position &pos)
  4855. {
  4856. NetworkMessage msg;
  4857. msg.addByte(0x64);
  4858. msg.addPosition(player->getPosition());
  4859. GetMapDescription(pos.x - 8, pos.y - 6, pos.z, 18, 14, msg);
  4860. writeToOutputBuffer(msg);
  4861. }
  4862.  
  4863. void ProtocolGame::sendAddTileItem(const Position &pos, uint32_t stackpos, const Item *item)
  4864. {
  4865. if (!canSee(pos))
  4866. {
  4867. return;
  4868. }
  4869.  
  4870. NetworkMessage msg;
  4871. msg.addByte(0x6A);
  4872. msg.addPosition(pos);
  4873. msg.addByte(stackpos);
  4874. AddItem(msg, item);
  4875. writeToOutputBuffer(msg);
  4876. }
  4877.  
  4878. void ProtocolGame::sendUpdateTileItem(const Position &pos, uint32_t stackpos, const Item *item)
  4879. {
  4880. if (!canSee(pos))
  4881. {
  4882. return;
  4883. }
  4884.  
  4885. NetworkMessage msg;
  4886. msg.addByte(0x6B);
  4887. msg.addPosition(pos);
  4888. msg.addByte(stackpos);
  4889. AddItem(msg, item);
  4890. writeToOutputBuffer(msg);
  4891. }
  4892.  
  4893. void ProtocolGame::sendRemoveTileThing(const Position &pos, uint32_t stackpos)
  4894. {
  4895. if (!canSee(pos))
  4896. {
  4897. return;
  4898. }
  4899.  
  4900. NetworkMessage msg;
  4901. RemoveTileThing(msg, pos, stackpos);
  4902. writeToOutputBuffer(msg);
  4903. }
  4904.  
  4905. void ProtocolGame::sendUpdateTile(const Tile *tile, const Position &pos)
  4906. {
  4907. if (!canSee(pos))
  4908. {
  4909. return;
  4910. }
  4911.  
  4912. NetworkMessage msg;
  4913. msg.addByte(0x69);
  4914. msg.addPosition(pos);
  4915.  
  4916. if (tile)
  4917. {
  4918. GetTileDescription(tile, msg);
  4919. msg.addByte(0x00);
  4920. msg.addByte(0xFF);
  4921. }
  4922. else
  4923. {
  4924. msg.addByte(0x01);
  4925. msg.addByte(0xFF);
  4926. }
  4927.  
  4928. writeToOutputBuffer(msg);
  4929. }
  4930.  
  4931. void ProtocolGame::sendPendingStateEntered()
  4932. {
  4933. NetworkMessage msg;
  4934. msg.addByte(0x0A);
  4935. writeToOutputBuffer(msg);
  4936. }
  4937.  
  4938. void ProtocolGame::sendEnterWorld()
  4939. {
  4940. NetworkMessage msg;
  4941. msg.addByte(0x0F);
  4942. writeToOutputBuffer(msg);
  4943. }
  4944.  
  4945. void ProtocolGame::sendFightModes()
  4946. {
  4947. NetworkMessage msg;
  4948. msg.addByte(0xA7);
  4949. msg.addByte(player->fightMode);
  4950. msg.addByte(player->chaseMode);
  4951. msg.addByte(player->secureMode);
  4952. msg.addByte(PVP_MODE_DOVE);
  4953. writeToOutputBuffer(msg);
  4954. }
  4955.  
  4956. void ProtocolGame::sendAddCreature(const Creature *creature, const Position &pos, int32_t stackpos, bool isLogin)
  4957. {
  4958. if (!canSee(pos))
  4959. {
  4960. return;
  4961. }
  4962.  
  4963. if (creature != player)
  4964. {
  4965. if (stackpos >= 10)
  4966. {
  4967. return;
  4968. }
  4969.  
  4970. NetworkMessage msg;
  4971. msg.addByte(0x6A);
  4972. msg.addPosition(pos);
  4973. msg.addByte(stackpos);
  4974.  
  4975. bool known;
  4976. uint32_t removedKnown;
  4977. checkCreatureAsKnown(creature->getID(), known, removedKnown);
  4978. AddCreature(msg, creature, known, removedKnown);
  4979. writeToOutputBuffer(msg);
  4980.  
  4981. if (isLogin)
  4982. {
  4983. if (const Player *creaturePlayer = creature->getPlayer())
  4984. {
  4985. if (!creaturePlayer->isAccessPlayer() ||
  4986. creaturePlayer->getAccountType() == account::ACCOUNT_TYPE_NORMAL)
  4987. sendMagicEffect(pos, CONST_ME_TELEPORT);
  4988. }
  4989. else
  4990. {
  4991. sendMagicEffect(pos, CONST_ME_TELEPORT);
  4992. }
  4993. }
  4994.  
  4995. return;
  4996. }
  4997.  
  4998. NetworkMessage msg;
  4999. msg.addByte(0x17);
  5000.  
  5001. msg.add<uint32_t>(player->getID());
  5002. msg.add<uint16_t>(0x32); // beat duration (50)
  5003.  
  5004. msg.addDouble(Creature::speedA, 3);
  5005. msg.addDouble(Creature::speedB, 3);
  5006. msg.addDouble(Creature::speedC, 3);
  5007.  
  5008. // can report bugs?
  5009. if (player->getAccountType() >= account::ACCOUNT_TYPE_NORMAL)
  5010. {
  5011. msg.addByte(0x01);
  5012. }
  5013. else
  5014. {
  5015. msg.addByte(0x00);
  5016. }
  5017.  
  5018. msg.addByte(0x00); // can change pvp framing option
  5019. msg.addByte(0x00); // expert mode button enabled
  5020.  
  5021. msg.addString(g_config.getString(ConfigManager::STORE_IMAGES_URL));
  5022. msg.add<uint16_t>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::STORE_COIN_PACKET)));
  5023.  
  5024. if (version >= 1200) {
  5025. msg.addByte(shouldAddExivaRestrictions ? 0x01 : 0x00); // exiva button enabled
  5026. }
  5027.  
  5028. if (version >= 1200) {
  5029. msg.addByte(0x00); // tournament button enabled
  5030. }
  5031.  
  5032. writeToOutputBuffer(msg);
  5033.  
  5034. if (version >= 1200)
  5035. sendTibiaTime(g_game.getLightHour());
  5036. sendPendingStateEntered();
  5037. sendEnterWorld();
  5038. sendMapDescription(pos);
  5039. loggedIn = true;
  5040.  
  5041. if (isLogin)
  5042. {
  5043. sendMagicEffect(pos, CONST_ME_TELEPORT);
  5044. }
  5045.  
  5046. for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i)
  5047. {
  5048. sendInventoryItem(static_cast<slots_t>(i), player->getInventoryItem(static_cast<slots_t>(i)));
  5049. }
  5050.  
  5051. sendStats();
  5052. sendSkills();
  5053. sendBlessStatus();
  5054.  
  5055. sendPremiumTrigger();
  5056. sendStoreHighlight();
  5057.  
  5058. if (version >= 1200) {
  5059. sendItemsPrice();
  5060. }
  5061.  
  5062. //gameworld light-settings
  5063. sendWorldLight(g_game.getWorldLightInfo());
  5064.  
  5065. //player light level
  5066. sendCreatureLight(creature);
  5067.  
  5068. const std::forward_list<VIPEntry> &vipEntries = IOLoginData::getVIPEntries(player->getAccount());
  5069.  
  5070. if (player->isAccessPlayer())
  5071. {
  5072. for (const VIPEntry &entry : vipEntries)
  5073. {
  5074. VipStatus_t vipStatus;
  5075.  
  5076. Player *vipPlayer = g_game.getPlayerByGUID(entry.guid);
  5077. if (!vipPlayer)
  5078. {
  5079. vipStatus = VIPSTATUS_OFFLINE;
  5080. }
  5081. else
  5082. {
  5083. vipStatus = VIPSTATUS_ONLINE;
  5084. }
  5085.  
  5086. sendVIP(entry.guid, entry.name, entry.description, entry.icon, entry.notify, vipStatus);
  5087. }
  5088. }
  5089. else
  5090. {
  5091. for (const VIPEntry &entry : vipEntries)
  5092. {
  5093. VipStatus_t vipStatus;
  5094.  
  5095. Player *vipPlayer = g_game.getPlayerByGUID(entry.guid);
  5096. if (!vipPlayer || vipPlayer->isInGhostMode())
  5097. {
  5098. vipStatus = VIPSTATUS_OFFLINE;
  5099. }
  5100. else
  5101. {
  5102. vipStatus = VIPSTATUS_ONLINE;
  5103. }
  5104.  
  5105. sendVIP(entry.guid, entry.name, entry.description, entry.icon, entry.notify, vipStatus);
  5106. }
  5107. }
  5108.  
  5109. sendInventoryClientIds();
  5110. Item *slotItem = player->getInventoryItem(CONST_SLOT_BACKPACK);
  5111. if (slotItem)
  5112. {
  5113. Container *mainBackpack = slotItem->getContainer();
  5114. Container *hasQuickLootContainer = player->getLootContainer(OBJECTCATEGORY_DEFAULT);
  5115. if (mainBackpack && !hasQuickLootContainer)
  5116. {
  5117. player->setLootContainer(OBJECTCATEGORY_DEFAULT, mainBackpack);
  5118. sendInventoryItem(CONST_SLOT_BACKPACK, player->getInventoryItem(CONST_SLOT_BACKPACK));
  5119. }
  5120. }
  5121.  
  5122. if (version >= 1200)
  5123. sendLootContainers();
  5124. sendBasicData();
  5125. initPreyData();
  5126.  
  5127. if (version >= 1200) {
  5128. player->sendClientCheck();
  5129. player->sendGameNews();
  5130. }
  5131. player->sendIcons();
  5132. }
  5133.  
  5134. void ProtocolGame::sendMoveCreature(const Creature *creature, const Position &newPos, int32_t newStackPos, const Position &oldPos, int32_t oldStackPos, bool teleport)
  5135. {
  5136. if (creature == player)
  5137. {
  5138. if (oldStackPos >= 10)
  5139. {
  5140. sendMapDescription(newPos);
  5141. }
  5142. else if (teleport)
  5143. {
  5144. NetworkMessage msg;
  5145. RemoveTileThing(msg, oldPos, oldStackPos);
  5146. writeToOutputBuffer(msg);
  5147. sendMapDescription(newPos);
  5148. }
  5149. else
  5150. {
  5151. NetworkMessage msg;
  5152. if (oldPos.z == 7 && newPos.z >= 8)
  5153. {
  5154. RemoveTileThing(msg, oldPos, oldStackPos);
  5155. }
  5156. else
  5157. {
  5158. msg.addByte(0x6D);
  5159. msg.addPosition(oldPos);
  5160. msg.addByte(oldStackPos);
  5161. msg.addPosition(newPos);
  5162. }
  5163.  
  5164. if (newPos.z > oldPos.z)
  5165. {
  5166. MoveDownCreature(msg, creature, newPos, oldPos);
  5167. }
  5168. else if (newPos.z < oldPos.z)
  5169. {
  5170. MoveUpCreature(msg, creature, newPos, oldPos);
  5171. }
  5172.  
  5173. if (oldPos.y > newPos.y)
  5174. { // north, for old x
  5175. msg.addByte(0x65);
  5176. GetMapDescription(oldPos.x - 8, newPos.y - 6, newPos.z, 18, 1, msg);
  5177. }
  5178. else if (oldPos.y < newPos.y)
  5179. { // south, for old x
  5180. msg.addByte(0x67);
  5181. GetMapDescription(oldPos.x - 8, newPos.y + 7, newPos.z, 18, 1, msg);
  5182. }
  5183.  
  5184. if (oldPos.x < newPos.x)
  5185. { // east, [with new y]
  5186. msg.addByte(0x66);
  5187. GetMapDescription(newPos.x + 9, newPos.y - 6, newPos.z, 1, 14, msg);
  5188. }
  5189. else if (oldPos.x > newPos.x)
  5190. { // west, [with new y]
  5191. msg.addByte(0x68);
  5192. GetMapDescription(newPos.x - 8, newPos.y - 6, newPos.z, 1, 14, msg);
  5193. }
  5194. writeToOutputBuffer(msg);
  5195. }
  5196. }
  5197. else if (canSee(oldPos) && canSee(newPos))
  5198. {
  5199. if (teleport || (oldPos.z == 7 && newPos.z >= 8) || oldStackPos >= 10)
  5200. {
  5201. sendRemoveTileThing(oldPos, oldStackPos);
  5202. sendAddCreature(creature, newPos, newStackPos, false);
  5203. }
  5204. else
  5205. {
  5206. NetworkMessage msg;
  5207. msg.addByte(0x6D);
  5208. msg.addPosition(oldPos);
  5209. msg.addByte(oldStackPos);
  5210. msg.addPosition(newPos);
  5211. writeToOutputBuffer(msg);
  5212. }
  5213. }
  5214. else if (canSee(oldPos))
  5215. {
  5216. sendRemoveTileThing(oldPos, oldStackPos);
  5217. }
  5218. else if (canSee(newPos))
  5219. {
  5220. sendAddCreature(creature, newPos, newStackPos, false);
  5221. }
  5222. }
  5223.  
  5224. void ProtocolGame::sendInventoryItem(slots_t slot, const Item *item)
  5225. {
  5226. NetworkMessage msg;
  5227. if (item)
  5228. {
  5229. msg.addByte(0x78);
  5230. msg.addByte(slot);
  5231. AddItem(msg, item);
  5232. }
  5233. else
  5234. {
  5235. msg.addByte(0x79);
  5236. msg.addByte(slot);
  5237. }
  5238. writeToOutputBuffer(msg);
  5239. }
  5240.  
  5241. void ProtocolGame::sendInventoryClientIds()
  5242. {
  5243. std::map<uint16_t, uint16_t> items = player->getInventoryClientIds();
  5244.  
  5245. NetworkMessage msg;
  5246. msg.addByte(0xF5);
  5247. msg.add<uint16_t>(items.size() + 11);
  5248.  
  5249. for (uint16_t i = 1; i <= 11; i++)
  5250. {
  5251. msg.add<uint16_t>(i);
  5252. msg.addByte(0x00);
  5253. msg.add<uint16_t>(0x01);
  5254. }
  5255.  
  5256. for (const auto &it : items)
  5257. {
  5258. msg.add<uint16_t>(it.first);
  5259. msg.addByte(0x00);
  5260. msg.add<uint16_t>(it.second);
  5261. }
  5262. writeToOutputBuffer(msg);
  5263. }
  5264.  
  5265. void ProtocolGame::sendAddContainerItem(uint8_t cid, uint16_t slot, const Item *item)
  5266. {
  5267. NetworkMessage msg;
  5268. msg.addByte(0x70);
  5269. msg.addByte(cid);
  5270. msg.add<uint16_t>(slot);
  5271. AddItem(msg, item);
  5272. writeToOutputBuffer(msg);
  5273. }
  5274.  
  5275. void ProtocolGame::sendUpdateContainerItem(uint8_t cid, uint16_t slot, const Item *item)
  5276. {
  5277. NetworkMessage msg;
  5278. msg.addByte(0x71);
  5279. msg.addByte(cid);
  5280. msg.add<uint16_t>(slot);
  5281. AddItem(msg, item);
  5282. writeToOutputBuffer(msg);
  5283. }
  5284.  
  5285. void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot, const Item *lastItem)
  5286. {
  5287. NetworkMessage msg;
  5288. msg.addByte(0x72);
  5289. msg.addByte(cid);
  5290. msg.add<uint16_t>(slot);
  5291. if (lastItem)
  5292. {
  5293. AddItem(msg, lastItem);
  5294. }
  5295. else
  5296. {
  5297. msg.add<uint16_t>(0x00);
  5298. }
  5299. writeToOutputBuffer(msg);
  5300. }
  5301.  
  5302. void ProtocolGame::sendTextWindow(uint32_t windowTextId, Item *item, uint16_t maxlen, bool canWrite)
  5303. {
  5304. NetworkMessage msg;
  5305. msg.addByte(0x96);
  5306. msg.add<uint32_t>(windowTextId);
  5307. AddItem(msg, item);
  5308.  
  5309. if (canWrite)
  5310. {
  5311. msg.add<uint16_t>(maxlen);
  5312. msg.addString(item->getText());
  5313. }
  5314. else
  5315. {
  5316. const std::string &text = item->getText();
  5317. msg.add<uint16_t>(text.size());
  5318. msg.addString(text);
  5319. }
  5320.  
  5321. const std::string &writer = item->getWriter();
  5322. if (!writer.empty())
  5323. {
  5324. msg.addString(writer);
  5325. }
  5326. else
  5327. {
  5328. msg.add<uint16_t>(0x00);
  5329. }
  5330. if (version >= 1200)
  5331. msg.addByte(0x00); // Show (Traded)
  5332.  
  5333. time_t writtenDate = item->getDate();
  5334. if (writtenDate != 0)
  5335. {
  5336. msg.addString(formatDateShort(writtenDate));
  5337. }
  5338. else
  5339. {
  5340. msg.add<uint16_t>(0x00);
  5341. }
  5342.  
  5343. writeToOutputBuffer(msg);
  5344. }
  5345.  
  5346. void ProtocolGame::sendTextWindow(uint32_t windowTextId, uint32_t itemId, const std::string &text)
  5347. {
  5348. NetworkMessage msg;
  5349. msg.addByte(0x96);
  5350. msg.add<uint32_t>(windowTextId);
  5351. AddItem(msg, itemId, 1);
  5352. msg.add<uint16_t>(text.size());
  5353. msg.addString(text);
  5354. msg.add<uint16_t>(0x00);
  5355. if (version >= 1200)
  5356. msg.addByte(0x00); // Show (Traded)
  5357. msg.add<uint16_t>(0x00);
  5358. writeToOutputBuffer(msg);
  5359. }
  5360.  
  5361. void ProtocolGame::sendHouseWindow(uint32_t windowTextId, const std::string &text)
  5362. {
  5363. NetworkMessage msg;
  5364. msg.addByte(0x97);
  5365. msg.addByte(0x00);
  5366. msg.add<uint32_t>(windowTextId);
  5367. msg.addString(text);
  5368. writeToOutputBuffer(msg);
  5369. }
  5370.  
  5371. void ProtocolGame::sendOutfitWindow()
  5372. {
  5373. NetworkMessage msg;
  5374. msg.addByte(0xC8);
  5375.  
  5376. bool mounted = false;
  5377. Outfit_t currentOutfit = player->getDefaultOutfit();
  5378. Mount* currentMount = g_game.mounts.getMountByID(player->getCurrentMount());
  5379. if (currentMount) {
  5380. mounted = (currentOutfit.lookMount == currentMount->clientId);
  5381. currentOutfit.lookMount = currentMount->clientId;
  5382. }
  5383.  
  5384. AddOutfit(msg, currentOutfit);
  5385.  
  5386. if(version >= 1200){
  5387. msg.addByte(currentOutfit.lookMountHead);
  5388. msg.addByte(currentOutfit.lookMountBody);
  5389. msg.addByte(currentOutfit.lookMountLegs);
  5390. msg.addByte(currentOutfit.lookMountFeet);
  5391. msg.add<uint16_t>(currentOutfit.lookFamiliarsType);
  5392. }
  5393.  
  5394. auto startOutfits = msg.getBufferPosition();
  5395. uint16_t limitOutfits = version >= 1200 ? std::numeric_limits<uint16_t>::max() : 150;
  5396. uint16_t outfitSize = 0;
  5397. msg.skipBytes(version >= 1200 ? 2 : 1);
  5398.  
  5399. if (player->isAccessPlayer()) {
  5400. msg.add<uint16_t>(75);
  5401. msg.addString("Gamemaster");
  5402. msg.addByte(0);
  5403. if (version >= 1200)
  5404. msg.addByte(0x00);
  5405. ++outfitSize;
  5406.  
  5407. msg.add<uint16_t>(266);
  5408. msg.addString("Customer Support");
  5409. msg.addByte(0);
  5410. if (version >= 1200)
  5411. msg.addByte(0x00);
  5412. ++outfitSize;
  5413.  
  5414. msg.add<uint16_t>(302);
  5415. msg.addString("Community Manager");
  5416. msg.addByte(0);
  5417. if (version >= 1200)
  5418. msg.addByte(0x00);
  5419. ++outfitSize;
  5420. }
  5421.  
  5422. const auto& outfits = Outfits::getInstance().getOutfits(player->getSex());
  5423.  
  5424. for (const Outfit& outfit : outfits) {
  5425. uint8_t addons;
  5426. if (!player->getOutfitAddons(outfit, addons)) {
  5427. continue;
  5428. }
  5429.  
  5430. msg.add<uint16_t>(outfit.lookType);
  5431. msg.addString(outfit.name);
  5432. msg.addByte(addons);
  5433. if (version >= 1200)
  5434. msg.addByte(0x00);
  5435. if (++outfitSize == limitOutfits) {
  5436. break;
  5437. }
  5438. }
  5439.  
  5440. auto endOutfits = msg.getBufferPosition();
  5441. msg.setBufferPosition(startOutfits);
  5442. if (version >= 1200)
  5443. msg.add<uint16_t>(outfitSize);
  5444. else
  5445. msg.addByte(static_cast<uint8_t>(outfitSize));
  5446. msg.setBufferPosition(endOutfits);
  5447.  
  5448. auto startMounts = msg.getBufferPosition();
  5449. uint16_t limitMounts = version >= 1200 ? std::numeric_limits<uint16_t>::max() : 150;
  5450. uint16_t mountSize = 0;
  5451. msg.skipBytes(version >= 1200 ? 2 : 1);
  5452.  
  5453. const auto& mounts = g_game.mounts.getMounts();
  5454. for (const Mount& mount : mounts) {
  5455. if (player->hasMount(&mount)) {
  5456. msg.add<uint16_t>(mount.clientId);
  5457. msg.addString(mount.name);
  5458. if (version >= 1200)
  5459. msg.addByte(0x00);
  5460. if (++mountSize == limitMounts) {
  5461. break;
  5462. }
  5463. }
  5464. }
  5465.  
  5466. auto endMounts = msg.getBufferPosition();
  5467. msg.setBufferPosition(startMounts);
  5468. if (version >= 1200)
  5469. msg.add<uint16_t>(mountSize);
  5470. else
  5471. msg.addByte(static_cast<uint8_t>(mountSize));
  5472. msg.setBufferPosition(endMounts);
  5473.  
  5474. if (version >= 1200) {
  5475. auto startFamiliars = msg.getBufferPosition();
  5476. uint16_t limitFamiliars = std::numeric_limits<uint16_t>::max();
  5477. uint16_t familiarSize = 0;
  5478. msg.skipBytes(2);
  5479.  
  5480. const auto& familiars = Familiars::getInstance().getFamiliars(player->getVocationId());
  5481.  
  5482. for (const Familiar& familiar : familiars) {
  5483. if (!player->getFamiliar(familiar)) {
  5484. continue;
  5485. }
  5486.  
  5487. msg.add<uint16_t>(familiar.lookType);
  5488. msg.addString(familiar.name);
  5489. msg.addByte(0x00);
  5490. if (++familiarSize == limitFamiliars) {
  5491. break;
  5492. }
  5493. }
  5494.  
  5495. auto endFamiliars = msg.getBufferPosition();
  5496. msg.setBufferPosition(startFamiliars);
  5497. msg.add<uint16_t>(familiarSize);
  5498. msg.setBufferPosition(endFamiliars);
  5499.  
  5500. msg.addByte(0x00); //Try outfit
  5501. msg.addByte(mounted ? 0x01 : 0x00);
  5502. }
  5503.  
  5504. writeToOutputBuffer(msg);
  5505. }
  5506.  
  5507. void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus)
  5508. {
  5509. NetworkMessage msg;
  5510. msg.addByte(0xD3);
  5511. msg.add<uint32_t>(guid);
  5512. msg.addByte(newStatus);
  5513. writeToOutputBuffer(msg);
  5514. }
  5515.  
  5516. void ProtocolGame::sendVIP(uint32_t guid, const std::string &name, const std::string &description, uint32_t icon, bool notify, VipStatus_t status)
  5517. {
  5518. NetworkMessage msg;
  5519. msg.addByte(0xD2);
  5520. msg.add<uint32_t>(guid);
  5521. msg.addString(name);
  5522. msg.addString(description);
  5523. msg.add<uint32_t>(std::min<uint32_t>(10, icon));
  5524. msg.addByte(notify ? 0x01 : 0x00);
  5525. msg.addByte(status);
  5526. if (version >= 1200)
  5527. msg.addByte(0x00); // vipGroups
  5528. writeToOutputBuffer(msg);
  5529. }
  5530.  
  5531. void ProtocolGame::sendSpellCooldown(uint8_t spellId, uint32_t time)
  5532. {
  5533. NetworkMessage msg;
  5534. msg.addByte(0xA4);
  5535. if (version < 1200 && spellId >= 170) {
  5536. spellId = 150;
  5537. }
  5538. msg.addByte(spellId);
  5539. msg.add<uint32_t>(time);
  5540. writeToOutputBuffer(msg);
  5541. }
  5542.  
  5543. void ProtocolGame::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time)
  5544. {
  5545. if(version < 1200 && groupId > 4)
  5546. return;
  5547. NetworkMessage msg;
  5548. msg.addByte(0xA5);
  5549. msg.addByte(groupId);
  5550. msg.add<uint32_t>(time);
  5551. writeToOutputBuffer(msg);
  5552. }
  5553.  
  5554. void ProtocolGame::sendOpenStore(uint8_t)
  5555. {
  5556. NetworkMessage msg;
  5557.  
  5558. msg.addByte(0xFB); //open store
  5559. msg.addByte(0x00);
  5560.  
  5561. //add categories
  5562. uint16_t categoriesCount = g_game.gameStore.getCategoryOffers().size();
  5563.  
  5564. msg.add<uint16_t>(categoriesCount);
  5565.  
  5566. for (StoreCategory *category : g_game.gameStore.getCategoryOffers())
  5567. {
  5568. msg.addString(category->name);
  5569. msg.addString(category->description);
  5570.  
  5571. uint8_t stateByte;
  5572. switch (category->state)
  5573. {
  5574. case NORMAL:
  5575. stateByte = 0;
  5576. break;
  5577. case NEW:
  5578. stateByte = 1;
  5579. break;
  5580. case SALE:
  5581. stateByte = 2;
  5582. break;
  5583. case LIMITED_TIME:
  5584. stateByte = 3;
  5585. break;
  5586. default:
  5587. stateByte = 0;
  5588. break;
  5589. }
  5590. msg.addByte(stateByte);
  5591.  
  5592. msg.addByte((uint8_t)category->icons.size());
  5593. for (std::string iconStr : category->icons)
  5594. {
  5595. msg.addString(iconStr);
  5596. }
  5597. msg.addString(""); //TODO: parentCategory
  5598. }
  5599.  
  5600. writeToOutputBuffer(msg);
  5601. }
  5602.  
  5603. void ProtocolGame::sendStoreCategoryOffers(StoreCategory *category)
  5604. {
  5605. NetworkMessage msg;
  5606. msg.addByte(0xFC); //StoreOffers
  5607. msg.addString(category->name);
  5608. msg.add<uint16_t>(category->offers.size());
  5609.  
  5610. for (BaseOffer *offer : category->offers)
  5611. {
  5612. msg.add<uint32_t>(offer->id);
  5613. std::stringstream offername;
  5614. if (offer->type == Offer_t::ITEM || offer->type == Offer_t::STACKABLE_ITEM)
  5615. {
  5616. if (((ItemOffer *)offer)->count > 1)
  5617. {
  5618. offername << ((ItemOffer *)offer)->count << "x ";
  5619. }
  5620. }
  5621. offername << offer->name;
  5622.  
  5623. msg.addString(offername.str());
  5624. msg.addString(offer->description);
  5625.  
  5626. msg.add<uint32_t>(offer->price);
  5627. msg.addByte((uint8_t)offer->state);
  5628.  
  5629. //outfits
  5630. uint8_t disabled = 0;
  5631. std::stringstream disabledReason;
  5632.  
  5633. disabledReason << "";
  5634.  
  5635. if (offer->type == OUTFIT || offer->type == OUTFIT_ADDON)
  5636. {
  5637. OutfitOffer *outfitOffer = (OutfitOffer *)offer;
  5638.  
  5639. uint16_t looktype = (player->getSex() == PLAYERSEX_MALE) ? outfitOffer->maleLookType : outfitOffer->femaleLookType;
  5640. uint8_t addons = outfitOffer->addonNumber;
  5641.  
  5642. if (player->canWear(looktype, addons))
  5643. { //player can wear the offer already
  5644. disabled = 1;
  5645. if (addons == 0)
  5646. { //addons == 0 //oufit-only offer and player already has it
  5647. disabledReason << "You already have this outfit.";
  5648. }
  5649. else
  5650. {
  5651. disabledReason << "You already have this outfit/addon.";
  5652. }
  5653. }
  5654. else
  5655. {
  5656. if (outfitOffer->type == OUTFIT_ADDON && !player->canWear(looktype, 0))
  5657. { //addon offer and player doesnt have the base outfit
  5658. disabled = 1;
  5659. disabledReason << "You don't have the outfit, you can't buy the addon.";
  5660. }
  5661. }
  5662. }
  5663. else if (offer->type == MOUNT)
  5664. {
  5665. MountOffer *mountOffer = (MountOffer *)offer;
  5666. Mount *m = g_game.mounts.getMountByID(mountOffer->mountId);
  5667. if (player->hasMount(m))
  5668. {
  5669. disabled = 1;
  5670. disabledReason << "You already have this mount.";
  5671. }
  5672. }
  5673. else if (offer->type == PROMOTION)
  5674. {
  5675. if (player->isPromoted() || !player->isPremium())
  5676. { //TODO: add support to multiple promotion levels
  5677. disabled = 1;
  5678. disabledReason << "You can't get this promotion";
  5679. }
  5680. }
  5681.  
  5682. msg.addByte(disabled);
  5683.  
  5684. if (disabled)
  5685. {
  5686. msg.addString(disabledReason.str());
  5687. }
  5688.  
  5689. //add icons
  5690. msg.addByte((uint8_t)offer->icons.size());
  5691.  
  5692. for (std::string iconName : offer->icons)
  5693. {
  5694. msg.addString(iconName);
  5695. }
  5696.  
  5697. msg.add<uint16_t>(0);
  5698. //TODO: add support to suboffers
  5699. }
  5700.  
  5701. writeToOutputBuffer(msg);
  5702. }
  5703.  
  5704. void ProtocolGame::sendStoreError(GameStoreError_t error, const std::string &message)
  5705. {
  5706. NetworkMessage msg;
  5707.  
  5708. msg.addByte(0xE0); //storeError
  5709. msg.addByte(error);
  5710. msg.addString(message);
  5711.  
  5712. writeToOutputBuffer(msg);
  5713. }
  5714.  
  5715. void ProtocolGame::sendStorePurchaseSuccessful(const std::string &message, const uint32_t coinBalance)
  5716. {
  5717. NetworkMessage msg;
  5718.  
  5719. msg.addByte(0xFE); //CompletePurchase
  5720. msg.addByte(0x00);
  5721.  
  5722. msg.addString(message);
  5723. msg.add<uint32_t>(coinBalance); //dont know why the client needs it duplicated. But ok...
  5724. msg.add<uint32_t>(coinBalance);
  5725.  
  5726. writeToOutputBuffer(msg);
  5727. }
  5728.  
  5729. void ProtocolGame::sendStoreRequestAdditionalInfo(uint32_t offerId, ClientOffer_t clientOfferType)
  5730. {
  5731. NetworkMessage msg;
  5732.  
  5733. msg.addByte(0xE1); //RequestPurchaseData
  5734. msg.add<uint32_t>(offerId);
  5735. msg.addByte(clientOfferType);
  5736.  
  5737. writeToOutputBuffer(msg);
  5738. }
  5739.  
  5740. void ProtocolGame::sendStoreTrasactionHistory(HistoryStoreOfferList &list, uint32_t page, uint8_t entriesPerPage)
  5741. {
  5742. NetworkMessage msg;
  5743. uint32_t isLastPage = (list.size() <= entriesPerPage) ? 0x01 : 0x00;
  5744.  
  5745. //TODO: Support multiple pages
  5746. isLastPage = 0x01; //FIXME
  5747. page = 0x00;
  5748. ////////////////////////
  5749.  
  5750. msg.addByte(0xFD); //BrowseTransactionHistory
  5751. msg.add<uint32_t>(page); //which page
  5752. msg.add<uint32_t>(isLastPage); //is the last page? /
  5753. msg.addByte((uint8_t)list.size()); //how many elements follows
  5754.  
  5755. for (HistoryStoreOffer offer : list)
  5756. {
  5757. msg.add<uint32_t>(offer.time);
  5758. msg.addByte(offer.mode);
  5759. msg.add<uint32_t>(offer.amount); //FIXME: investigate why it doesn't send the price properly
  5760. msg.addByte(0x00); // 0 = transferable tibia coin, 1 = normal tibia coin
  5761. msg.addString(offer.description);
  5762. }
  5763.  
  5764. writeToOutputBuffer(msg);
  5765. }
  5766.  
  5767. void ProtocolGame::sendModalWindow(const ModalWindow &modalWindow)
  5768. {
  5769. NetworkMessage msg;
  5770. msg.addByte(0xFA);
  5771.  
  5772. msg.add<uint32_t>(modalWindow.id);
  5773. msg.addString(modalWindow.title);
  5774. msg.addString(modalWindow.message);
  5775.  
  5776. msg.addByte(modalWindow.buttons.size());
  5777. for (const auto &it : modalWindow.buttons)
  5778. {
  5779. msg.addString(it.first);
  5780. msg.addByte(it.second);
  5781. }
  5782.  
  5783. msg.addByte(modalWindow.choices.size());
  5784. for (const auto &it : modalWindow.choices)
  5785. {
  5786. msg.addString(it.first);
  5787. msg.addByte(it.second);
  5788. }
  5789.  
  5790. msg.addByte(modalWindow.defaultEscapeButton);
  5791. msg.addByte(modalWindow.defaultEnterButton);
  5792. msg.addByte(modalWindow.priority ? 0x01 : 0x00);
  5793.  
  5794. writeToOutputBuffer(msg);
  5795. }
  5796.  
  5797. ////////////// Add common messages
  5798. void ProtocolGame::AddCreature(NetworkMessage &msg, const Creature *creature, bool known, uint32_t remove)
  5799. {
  5800. CreatureType_t creatureType = creature->getType();
  5801. const Player *otherPlayer = creature->getPlayer();
  5802.  
  5803. if (known)
  5804. {
  5805. msg.add<uint16_t>(0x62);
  5806. msg.add<uint32_t>(creature->getID());
  5807. }
  5808. else
  5809. {
  5810. msg.add<uint16_t>(0x61);
  5811. msg.add<uint32_t>(remove);
  5812. msg.add<uint32_t>(creature->getID());
  5813. if (version >= 1200 && creature->isHealthHidden()) {
  5814. msg.addByte(CREATURETYPE_HIDDEN);
  5815. }
  5816. else {
  5817. msg.addByte(creatureType);
  5818. }
  5819.  
  5820. if (version >= 1200 && creatureType == CREATURETYPE_SUMMONPLAYER)
  5821. {
  5822. const Creature *master = creature->getMaster();
  5823. if (master)
  5824. {
  5825. msg.add<uint32_t>(master->getID());
  5826. }
  5827. else
  5828. {
  5829. msg.add<uint32_t>(0x00);
  5830. }
  5831. }
  5832.  
  5833. if (version >= 1200 && creature->isHealthHidden())
  5834. {
  5835. msg.addString("");
  5836. }
  5837. else
  5838. {
  5839. msg.addString(creature->getName());
  5840. }
  5841. }
  5842.  
  5843. if (creature->isHealthHidden())
  5844. {
  5845. msg.addByte(0x00);
  5846. }
  5847. else
  5848. {
  5849. msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100));
  5850. }
  5851.  
  5852. msg.addByte(creature->getDirection());
  5853.  
  5854. if (!creature->isInGhostMode() && !creature->isInvisible())
  5855. {
  5856. const Outfit_t &outfit = creature->getCurrentOutfit();
  5857. AddOutfit(msg, outfit);
  5858. if (version >= 1200 && outfit.lookMount != 0)
  5859. {
  5860. msg.addByte(outfit.lookMountHead);
  5861. msg.addByte(outfit.lookMountBody);
  5862. msg.addByte(outfit.lookMountLegs);
  5863. msg.addByte(outfit.lookMountFeet);
  5864. }
  5865. }
  5866. else
  5867. {
  5868. static Outfit_t outfit;
  5869. AddOutfit(msg, outfit);
  5870. }
  5871.  
  5872. LightInfo lightInfo = creature->getCreatureLight();
  5873. msg.addByte(player->isAccessPlayer() ? 0xFF : lightInfo.level);
  5874. msg.addByte(lightInfo.color);
  5875.  
  5876. msg.add<uint16_t>(creature->getStepSpeed() / 2);
  5877.  
  5878. CreatureIcon_t icon = creature->getIcon();
  5879. if (version >= 1200) {
  5880. msg.addByte(icon != CREATUREICON_NONE); // Icons
  5881. if (icon != CREATUREICON_NONE) {
  5882. msg.addByte(icon);
  5883. msg.addByte(1);
  5884. msg.add<uint16_t>(0);
  5885. }
  5886. }
  5887.  
  5888. msg.addByte(player->getSkullClient(creature));
  5889. msg.addByte(player->getPartyShield(otherPlayer));
  5890.  
  5891. if (!known)
  5892. {
  5893. msg.addByte(player->getGuildEmblem(otherPlayer));
  5894. }
  5895.  
  5896. if (version >= 1200 && creatureType == CREATURETYPE_MONSTER)
  5897. {
  5898. const Creature *master = creature->getMaster();
  5899. if (master)
  5900. {
  5901. const Player *masterPlayer = master->getPlayer();
  5902. if (masterPlayer)
  5903. {
  5904. creatureType = CREATURETYPE_SUMMONPLAYER;
  5905. }
  5906. }
  5907. }
  5908.  
  5909. if (version >= 1200 && creature->isHealthHidden())
  5910. {
  5911. msg.addByte(CREATURETYPE_HIDDEN);
  5912. }
  5913. else
  5914. {
  5915. msg.addByte(creatureType); // Type (for summons)
  5916. }
  5917.  
  5918. if (version >= 1200 && creatureType == CREATURETYPE_SUMMONPLAYER)
  5919. {
  5920. const Creature *master = creature->getMaster();
  5921. if (master)
  5922. {
  5923. msg.add<uint32_t>(master->getID());
  5924. }
  5925. else
  5926. {
  5927. msg.add<uint32_t>(0x00);
  5928. }
  5929. }
  5930.  
  5931. if (version >= 1200 && creatureType == CREATURETYPE_PLAYER)
  5932. {
  5933. const Player *otherCreature = creature->getPlayer();
  5934. if (otherCreature)
  5935. {
  5936. msg.addByte(otherCreature->getVocation()->getClientId());
  5937. }
  5938. else
  5939. {
  5940. msg.addByte(0);
  5941. }
  5942. }
  5943.  
  5944. uint8_t speechbubble = creature->getSpeechBubble();
  5945. if (version < 1200 && speechbubble == 7) {
  5946. speechbubble = 2;
  5947. }
  5948. else if (version < 1200 && speechbubble > 5) {
  5949. speechbubble = 1;
  5950. }
  5951. msg.addByte(speechbubble);
  5952. msg.addByte(0xFF); // MARK_UNMARKED
  5953. if (version >= 1200) {
  5954. msg.addByte(0x00); // inspection type
  5955. }
  5956. if (version < 1200) {
  5957. if (otherPlayer) {
  5958. msg.add<uint16_t>(otherPlayer->getHelpers());
  5959. }
  5960. else {
  5961. msg.add<uint16_t>(0x00);
  5962. }
  5963. }
  5964. msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01);
  5965. }
  5966.  
  5967. void ProtocolGame::AddPlayerStats(NetworkMessage &msg)
  5968. {
  5969. msg.addByte(0xA0);
  5970.  
  5971. msg.add<uint16_t>(std::min<int32_t>(player->getHealth(), std::numeric_limits<uint16_t>::max()));
  5972. msg.add<uint16_t>(std::min<int32_t>(player->getMaxHealth(), std::numeric_limits<uint16_t>::max()));
  5973.  
  5974. msg.add<uint32_t>(player->getFreeCapacity());
  5975. if (version < 1200) {
  5976. msg.add<uint32_t>(player->getCapacity());
  5977. }
  5978.  
  5979. msg.add<uint64_t>(player->getExperience());
  5980.  
  5981. msg.add<uint16_t>(player->getLevel());
  5982. msg.addByte(player->getLevelPercent());
  5983.  
  5984. msg.add<uint16_t>(player->getBaseXpGain()); // base xp gain rate
  5985. if (version < 1200) {
  5986. msg.add<uint16_t>(player->getVoucherXpBoost()); // xp voucher
  5987. }
  5988. msg.add<uint16_t>(player->getGrindingXpBoost()); // low level bonus
  5989. msg.add<uint16_t>(player->getStoreXpBoost()); // xp boost
  5990. msg.add<uint16_t>(player->getStaminaXpBoost()); // stamina multiplier (100 = 1.0x)
  5991.  
  5992. msg.add<uint16_t>(std::min<int32_t>(player->getMana(), std::numeric_limits<uint16_t>::max()));
  5993. msg.add<uint16_t>(std::min<int32_t>(player->getMaxMana(), std::numeric_limits<uint16_t>::max()));
  5994.  
  5995. if (version < 1200) {
  5996. msg.addByte(std::min<uint32_t>(player->getMagicLevel(), std::numeric_limits<uint8_t>::max()));
  5997. msg.addByte(std::min<uint32_t>(player->getBaseMagicLevel(), std::numeric_limits<uint8_t>::max()));
  5998. msg.addByte(player->getMagicLevelPercent());
  5999. }
  6000.  
  6001. msg.addByte(player->getSoul());
  6002.  
  6003. msg.add<uint16_t>(player->getStaminaMinutes());
  6004.  
  6005. msg.add<uint16_t>(player->getBaseSpeed() / 2);
  6006.  
  6007. Condition* condition = player->getCondition(CONDITION_REGENERATION);
  6008. msg.add<uint16_t>(condition ? condition->getTicks() / 1000 : 0x00);
  6009.  
  6010. msg.add<uint16_t>(player->getOfflineTrainingTime() / 60 / 1000);
  6011.  
  6012. msg.add<uint16_t>(player->getExpBoostStamina()); // xp boost time (seconds)
  6013. msg.addByte(1); // enables exp boost in the store
  6014.  
  6015. if (version >= 1200) {
  6016. msg.add<uint16_t>(player->getManaShield()); // remaining mana shield
  6017. msg.add<uint16_t>(player->getMaxManaShield()); // total mana shield
  6018. }
  6019. }
  6020.  
  6021. void ProtocolGame::AddPlayerSkills(NetworkMessage &msg)
  6022. {
  6023. msg.addByte(0xA1);
  6024. if (version >= 1200) {
  6025. msg.add<uint16_t>(player->getMagicLevel());
  6026. if (player->getOperatingSystem() <= CLIENTOS_NEW_MAC) {
  6027. msg.add<uint16_t>(player->getBaseMagicLevel());
  6028. }
  6029. msg.add<uint16_t>(player->getBaseMagicLevel());
  6030. msg.add<uint16_t>(player->getMagicLevelPercent() * 100);
  6031. }
  6032.  
  6033. for (uint8_t i = SKILL_FIRST; i <= SKILL_FISHING; ++i) {
  6034. msg.add<uint16_t>(std::min<int32_t>(player->getSkillLevel(i), std::numeric_limits<uint16_t>::max()));
  6035. msg.add<uint16_t>(player->getBaseSkill(i));
  6036. if (version >= 1200) {
  6037.  
  6038. if (player->getOperatingSystem() <= CLIENTOS_NEW_MAC) {
  6039. msg.add<uint16_t>(player->getBaseSkill(i));
  6040. }
  6041.  
  6042. msg.add<uint16_t>(player->getSkillPercent(i) * 100);
  6043. }
  6044. else {
  6045. msg.addByte(player->getSkillPercent(i));
  6046. }
  6047. }
  6048.  
  6049. for (uint8_t i = SKILL_CRITICAL_HIT_CHANCE; i <= SKILL_LAST; ++i) {
  6050. msg.add<uint16_t>(std::min<int32_t>(player->getSkillLevel(i), std::numeric_limits<uint16_t>::max()));
  6051. msg.add<uint16_t>(player->getBaseSkill(i));
  6052. }
  6053.  
  6054. // used for imbuement (Feather)
  6055. if (version >= 1200) {
  6056. msg.add<uint32_t>(player->getCapacity()); // total capacity
  6057. msg.add<uint32_t>(player->getBaseCapacity()); // base total capacity
  6058. }
  6059. }
  6060.  
  6061. void ProtocolGame::AddOutfit(NetworkMessage &msg, const Outfit_t &outfit, bool addMount /* = true*/)
  6062. {
  6063. msg.add<uint16_t>(outfit.lookType);
  6064. if (outfit.lookType != 0)
  6065. {
  6066. msg.addByte(outfit.lookHead);
  6067. msg.addByte(outfit.lookBody);
  6068. msg.addByte(outfit.lookLegs);
  6069. msg.addByte(outfit.lookFeet);
  6070. msg.addByte(outfit.lookAddons);
  6071. }
  6072. else
  6073. {
  6074. msg.addItemId(outfit.lookTypeEx);
  6075. }
  6076.  
  6077. if (addMount)
  6078. {
  6079. msg.add<uint16_t>(outfit.lookMount);
  6080. }
  6081. }
  6082.  
  6083. void ProtocolGame::addImbuementInfo(NetworkMessage &msg, uint32_t imbuid)
  6084. {
  6085. Imbuement *imbuement = g_imbuements->getImbuement(imbuid);
  6086. BaseImbue *base = g_imbuements->getBaseByID(imbuement->getBaseID());
  6087. Category *category = g_imbuements->getCategoryByID(imbuement->getCategory());
  6088.  
  6089. msg.add<uint32_t>(imbuid);
  6090. msg.addString(base->name + " " + imbuement->getName());
  6091. msg.addString(imbuement->getDescription());
  6092. msg.addString(category->name + imbuement->getSubGroup());
  6093.  
  6094. msg.add<uint16_t>(imbuement->getIconID());
  6095. msg.add<uint32_t>(base->duration);
  6096.  
  6097. msg.addByte(imbuement->isPremium() ? 0x01 : 0x00);
  6098.  
  6099. const auto &items = imbuement->getItems();
  6100. msg.addByte(items.size());
  6101.  
  6102. for (const auto &itm : items)
  6103. {
  6104. const ItemType &it = Item::items[itm.first];
  6105. msg.addItemId(itm.first);
  6106. msg.addString(it.name);
  6107. msg.add<uint16_t>(itm.second);
  6108. }
  6109.  
  6110. msg.add<uint32_t>(base->price);
  6111. msg.addByte(base->percent);
  6112. msg.add<uint32_t>(base->protection);
  6113. }
  6114.  
  6115. void ProtocolGame::sendImbuementWindow(Item *item)
  6116. {
  6117. if (!item || item->isRemoved())
  6118. {
  6119. return;
  6120. }
  6121.  
  6122. const ItemType &it = Item::items[item->getID()];
  6123.  
  6124. bool itemHasImbue = false;
  6125. uint8_t slot = it.imbuingSlots;
  6126. for (uint8_t i = 0; i < slot; i++)
  6127. {
  6128. uint32_t info = item->getImbuement(i);
  6129. if (info >> 8)
  6130. {
  6131. itemHasImbue = true;
  6132. break;
  6133. }
  6134. }
  6135.  
  6136. // Seting imbuing item
  6137. player->inImbuing(item);
  6138.  
  6139. NetworkMessage msg;
  6140. msg.addByte(0xEB);
  6141. msg.addItemId(item->getID());
  6142. msg.addByte(slot);
  6143.  
  6144. for (uint8_t i = 0; i < slot; i++)
  6145. {
  6146. uint32_t info = item->getImbuement(i);
  6147. if (info >> 8)
  6148. {
  6149. msg.addByte(0x01);
  6150.  
  6151. addImbuementInfo(msg, (info & 0xFF));
  6152. msg.add<uint32_t>(info >> 8);
  6153. msg.add<uint32_t>(g_imbuements->getBaseByID(g_imbuements->getImbuement((info & 0xFF))->getBaseID())->removecust);
  6154. }
  6155. else
  6156. {
  6157. msg.addByte(0x00);
  6158. }
  6159. }
  6160.  
  6161. std::vector<Imbuement *> imbuements = g_imbuements->getImbuements(player, item);
  6162. std::unordered_map<uint16_t, uint16_t> needItems;
  6163. msg.add<uint16_t>(imbuements.size());
  6164. for (Imbuement *ib : imbuements)
  6165. {
  6166. addImbuementInfo(msg, ib->getId());
  6167.  
  6168. const auto &items = ib->getItems();
  6169. for (const auto &itm : items)
  6170. {
  6171. if (!needItems.count(itm.first))
  6172. {
  6173. needItems[itm.first] = player->getItemTypeCount(itm.first);
  6174. uint32_t stashCount = player->getStashItemCount(Item::items[itm.first].clientId);
  6175. if (stashCount > 0) {
  6176. needItems[itm.first] += stashCount;
  6177. }
  6178. }
  6179. }
  6180. }
  6181.  
  6182. msg.add<uint32_t>(needItems.size());
  6183. for (const auto &itm : needItems)
  6184. {
  6185. msg.addItemId(itm.first);
  6186. msg.add<uint16_t>(itm.second);
  6187. }
  6188.  
  6189. sendResourcesBalance(player->getMoney(), player->getBankBalance());
  6190.  
  6191. writeToOutputBuffer(msg);
  6192. }
  6193.  
  6194. void ProtocolGame::AddWorldLight(NetworkMessage &msg, LightInfo lightInfo)
  6195. {
  6196. msg.addByte(0x82);
  6197. msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level));
  6198. msg.addByte(lightInfo.color);
  6199. }
  6200.  
  6201. void ProtocolGame::sendSpecialContainersAvailable(bool supplyStashAvailable)
  6202. {
  6203. if (version < 1200)
  6204. return;
  6205. NetworkMessage msg;
  6206. msg.addByte(0x2A);
  6207. msg.addByte(supplyStashAvailable ? 0x01 : 0x00);
  6208. msg.addByte(0x00); // 0x00 if player can use 'show in market' option. TO DO
  6209. writeToOutputBuffer(msg);
  6210. }
  6211.  
  6212. void ProtocolGame::AddCreatureLight(NetworkMessage &msg, const Creature *creature)
  6213. {
  6214. LightInfo lightInfo = creature->getCreatureLight();
  6215.  
  6216. msg.addByte(0x8D);
  6217. msg.add<uint32_t>(creature->getID());
  6218. msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level));
  6219. msg.addByte(lightInfo.color);
  6220. }
  6221.  
  6222. //tile
  6223. void ProtocolGame::RemoveTileThing(NetworkMessage &msg, const Position &pos, uint32_t stackpos)
  6224. {
  6225. if (stackpos >= 10)
  6226. {
  6227. return;
  6228. }
  6229.  
  6230. msg.addByte(0x6C);
  6231. msg.addPosition(pos);
  6232. msg.addByte(stackpos);
  6233. }
  6234.  
  6235. void ProtocolGame::sendKillTrackerUpdate(Container *corpse, const std::string &name, const Outfit_t creatureOutfit)
  6236. {
  6237. if (version < 1200)
  6238. return;
  6239. bool isCorpseEmpty = corpse->empty();
  6240.  
  6241. NetworkMessage msg;
  6242. msg.addByte(0xD1);
  6243. msg.addString(name);
  6244. msg.add<uint16_t>(creatureOutfit.lookType ? creatureOutfit.lookType : 21);
  6245. msg.addByte(creatureOutfit.lookType ? creatureOutfit.lookHead : 0x00);
  6246. msg.addByte(creatureOutfit.lookType ? creatureOutfit.lookBody : 0x00);
  6247. msg.addByte(creatureOutfit.lookType ? creatureOutfit.lookLegs : 0x00);
  6248. msg.addByte(creatureOutfit.lookType ? creatureOutfit.lookFeet : 0x00);
  6249. msg.addByte(creatureOutfit.lookType ? creatureOutfit.lookAddons : 0x00);
  6250. msg.addByte(isCorpseEmpty ? 0 : corpse->size());
  6251.  
  6252. if (!isCorpseEmpty)
  6253. {
  6254. for (const auto &it : corpse->getItemList())
  6255. {
  6256. AddItem(msg, it);
  6257. }
  6258. }
  6259.  
  6260. writeToOutputBuffer(msg);
  6261. }
  6262.  
  6263. void ProtocolGame::sendUpdateSupplyTracker(const Item *item)
  6264. {
  6265. if (!player || !item || version < 1200)
  6266. {
  6267. return;
  6268. }
  6269.  
  6270. NetworkMessage msg;
  6271. msg.addByte(0xCE);
  6272. msg.add<uint16_t>(item->getClientID());
  6273.  
  6274. writeToOutputBuffer(msg);
  6275. }
  6276.  
  6277. void ProtocolGame::sendUpdateImpactTracker(CombatType_t type, int32_t amount)
  6278. {
  6279. if (version < 1200)
  6280. return;
  6281. NetworkMessage msg;
  6282. msg.addByte(0xCC);
  6283. if (type == COMBAT_HEALING)
  6284. {
  6285. msg.addByte(ANALYZER_HEAL);
  6286. msg.add<uint32_t>(amount);
  6287. }
  6288. else
  6289. {
  6290. msg.addByte(ANALYZER_DAMAGE_DEALT);
  6291. msg.add<uint32_t>(amount);
  6292. msg.addByte(getCipbiaElement(type));
  6293. }
  6294. writeToOutputBuffer(msg);
  6295. }
  6296. void ProtocolGame::sendUpdateInputAnalyzer(CombatType_t type, int32_t amount, std::string target)
  6297. {
  6298. if (version < 1200)
  6299. return;
  6300. NetworkMessage msg;
  6301. msg.addByte(0xCC);
  6302. msg.addByte(ANALYZER_DAMAGE_RECEIVED);
  6303. msg.add<uint32_t>(amount);
  6304. msg.addByte(getCipbiaElement(type));
  6305. msg.addString(target);
  6306. writeToOutputBuffer(msg);
  6307. }
  6308.  
  6309. void ProtocolGame::sendUpdateLootTracker(Item *item)
  6310. {
  6311. if (!player || version < 1200)
  6312. {
  6313. return;
  6314. }
  6315.  
  6316. NetworkMessage msg;
  6317. msg.addByte(0xCF);
  6318. AddItem(msg, item);
  6319. msg.addString(item->getName());
  6320. item->setIsLootTrackeable(false);
  6321.  
  6322. writeToOutputBuffer(msg);
  6323. }
  6324.  
  6325. void ProtocolGame::MoveUpCreature(NetworkMessage &msg, const Creature *creature, const Position &newPos, const Position &oldPos)
  6326. {
  6327. if (creature != player)
  6328. {
  6329. return;
  6330. }
  6331.  
  6332. //floor change up
  6333. msg.addByte(0xBE);
  6334.  
  6335. //going to surface
  6336. if (newPos.z == 7)
  6337. {
  6338. int32_t skip = -1;
  6339. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 5, 18, 14, 3, skip); //(floor 7 and 6 already set)
  6340. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 4, 18, 14, 4, skip);
  6341. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 3, 18, 14, 5, skip);
  6342. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 2, 18, 14, 6, skip);
  6343. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 1, 18, 14, 7, skip);
  6344. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 0, 18, 14, 8, skip);
  6345.  
  6346. if (skip >= 0)
  6347. {
  6348. msg.addByte(skip);
  6349. msg.addByte(0xFF);
  6350. }
  6351. }
  6352. //underground, going one floor up (still underground)
  6353. else if (newPos.z > 7)
  6354. {
  6355. int32_t skip = -1;
  6356. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, oldPos.getZ() - 3, 18, 14, 3, skip);
  6357.  
  6358. if (skip >= 0)
  6359. {
  6360. msg.addByte(skip);
  6361. msg.addByte(0xFF);
  6362. }
  6363. }
  6364.  
  6365. //moving up a floor up makes us out of sync
  6366. //west
  6367. msg.addByte(0x68);
  6368. GetMapDescription(oldPos.x - 8, oldPos.y - 5, newPos.z, 1, 14, msg);
  6369.  
  6370. //north
  6371. msg.addByte(0x65);
  6372. GetMapDescription(oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 1, msg);
  6373. }
  6374.  
  6375. void ProtocolGame::MoveDownCreature(NetworkMessage &msg, const Creature *creature, const Position &newPos, const Position &oldPos)
  6376. {
  6377. if (creature != player)
  6378. {
  6379. return;
  6380. }
  6381.  
  6382. //floor change down
  6383. msg.addByte(0xBF);
  6384.  
  6385. //going from surface to underground
  6386. if (newPos.z == 8)
  6387. {
  6388. int32_t skip = -1;
  6389.  
  6390. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 14, -1, skip);
  6391. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 1, 18, 14, -2, skip);
  6392. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip);
  6393.  
  6394. if (skip >= 0)
  6395. {
  6396. msg.addByte(skip);
  6397. msg.addByte(0xFF);
  6398. }
  6399. }
  6400. //going further down
  6401. else if (newPos.z > oldPos.z && newPos.z > 8 && newPos.z < 14)
  6402. {
  6403. int32_t skip = -1;
  6404. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip);
  6405.  
  6406. if (skip >= 0)
  6407. {
  6408. msg.addByte(skip);
  6409. msg.addByte(0xFF);
  6410. }
  6411. }
  6412.  
  6413. //moving down a floor makes us out of sync
  6414. //east
  6415. msg.addByte(0x66);
  6416. GetMapDescription(oldPos.x + 9, oldPos.y - 7, newPos.z, 1, 14, msg);
  6417.  
  6418. //south
  6419. msg.addByte(0x67);
  6420. GetMapDescription(oldPos.x - 8, oldPos.y + 7, newPos.z, 18, 1, msg);
  6421. }
  6422.  
  6423. void ProtocolGame::AddShopItem(NetworkMessage &msg, const ShopInfo &item)
  6424. {
  6425. const ItemType &it = Item::items[item.itemId];
  6426. msg.add<uint16_t>(it.clientId);
  6427.  
  6428. if (it.isSplash() || it.isFluidContainer())
  6429. {
  6430. msg.addByte(serverFluidToClient(item.subType));
  6431. }
  6432. else
  6433. {
  6434. msg.addByte(0x00);
  6435. }
  6436.  
  6437. msg.addString(item.realName);
  6438. msg.add<uint32_t>(it.weight);
  6439. msg.add<uint32_t>(item.buyPrice == 4294967295 ? 0 : item.buyPrice);
  6440. msg.add<uint32_t>(item.sellPrice == 4294967295 ? 0 : item.sellPrice);
  6441. }
  6442.  
  6443. void ProtocolGame::parseExtendedOpcode(NetworkMessage &msg)
  6444. {
  6445. uint8_t opcode = msg.getByte();
  6446. const std::string &buffer = msg.getString();
  6447.  
  6448. // process additional opcodes via lua script event
  6449. addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer);
  6450. }
  6451.  
  6452. void ProtocolGame::sendItemsPrice()
  6453. {
  6454. NetworkMessage msg;
  6455. msg.addByte(0xCD);
  6456.  
  6457. msg.add<uint16_t>(g_game.getItemsPriceCount());
  6458. if (g_game.getItemsPriceCount() > 0)
  6459. {
  6460. std::map<uint16_t, uint32_t> items = g_game.getItemsPrice();
  6461. for (const auto &it : items)
  6462. {
  6463. msg.addItemId(it.first);
  6464. msg.add<uint32_t>(it.second);
  6465. }
  6466. }
  6467.  
  6468. writeToOutputBuffer(msg);
  6469. }
  6470.  
  6471. void ProtocolGame::reloadCreature(const Creature *creature)
  6472. {
  6473. if (!canSee(creature))
  6474. return;
  6475.  
  6476. uint32_t stackpos = creature->getTile()->getClientIndexOfCreature(player, creature);
  6477.  
  6478. if (stackpos >= 10)
  6479. return;
  6480.  
  6481. NetworkMessage msg;
  6482.  
  6483. std::unordered_set<uint32_t>::iterator it = std::find(knownCreatureSet.begin(), knownCreatureSet.end(), creature->getID());
  6484. if (it != knownCreatureSet.end())
  6485. {
  6486. msg.addByte(0x6B);
  6487. msg.addPosition(creature->getPosition());
  6488. msg.addByte(stackpos);
  6489. AddCreature(msg, creature, false, 0);
  6490. }
  6491. else
  6492. {
  6493. sendAddCreature(creature, creature->getPosition(), stackpos, false);
  6494. }
  6495.  
  6496. writeToOutputBuffer(msg);
  6497. }
  6498.  
  6499. void ProtocolGame::sendOpenStash()
  6500. {
  6501. if (version < 1200) {
  6502. player->sendCancelMessage("Stash only works on the client 12.");
  6503. return;
  6504. }
  6505. NetworkMessage msg;
  6506. msg.addByte(0x29);
  6507. AddPlayerStowedItems(msg);
  6508. writeToOutputBuffer(msg);
  6509. }
  6510.  
  6511. void ProtocolGame::AddPlayerStowedItems(NetworkMessage &msg)
  6512. {
  6513. StashItemList list = player->getStashItems();
  6514.  
  6515. msg.add<uint16_t>(list.size());
  6516.  
  6517. for (auto item : list)
  6518. {
  6519. msg.add<uint16_t>(item.first);
  6520. msg.add<uint32_t>(item.second);
  6521. }
  6522. msg.add<uint16_t>(g_config.getNumber(ConfigManager::STASH_ITEMS) - getStashSize(list));
  6523. }
  6524.  
  6525. void ProtocolGame::parseStashWithdraw(NetworkMessage &msg)
  6526. {
  6527. Supply_Stash_Actions_t action = static_cast<Supply_Stash_Actions_t>(msg.getByte());
  6528. switch (action)
  6529. {
  6530. case SUPPLY_STASH_ACTION_STOW_ITEM:
  6531. {
  6532. Position pos = msg.getPosition();
  6533. uint16_t spriteId = msg.get<uint16_t>();
  6534. uint8_t stackpos = msg.getByte();
  6535. uint32_t count = static_cast<uint32_t>(msg.getByte());
  6536. g_game.playerStowItem(player, pos, spriteId, stackpos, count);
  6537. break;
  6538. }
  6539. case SUPPLY_STASH_ACTION_STOW_CONTAINER:
  6540. {
  6541. Position pos = msg.getPosition();
  6542. uint16_t spriteId = msg.get<uint16_t>();
  6543. uint8_t stackpos = msg.getByte();
  6544. g_game.playerStowContainer(player, pos, spriteId, stackpos);
  6545. break;
  6546. }
  6547. case SUPPLY_STASH_ACTION_STOW_STACK:
  6548. {
  6549. Position pos = msg.getPosition();
  6550. uint16_t spriteId = msg.get<uint16_t>();
  6551. uint8_t stackpos = msg.getByte();
  6552. g_game.playerStowAllItems(player, pos, spriteId, stackpos);
  6553. break;
  6554. }
  6555. case SUPPLY_STASH_ACTION_WITHDRAW:
  6556. {
  6557. uint16_t spriteId = msg.get<uint16_t>();
  6558. uint32_t count = msg.get<uint32_t>();
  6559. uint8_t stackpos = msg.getByte();
  6560. g_game.playerStashWithdraw(player, spriteId, count, stackpos);
  6561. sendOpenStash();
  6562. break;
  6563. }
  6564. }
  6565. }
  6566.  
  6567. void ProtocolGame::sendLockerItems(std::map<uint16_t, uint16_t> itemMap, uint16_t count)
  6568. {
  6569. NetworkMessage msg;
  6570. msg.addByte(0x94);
  6571.  
  6572. msg.add<uint16_t>(count);
  6573. for (const auto &it : itemMap)
  6574. {
  6575. msg.addItemId(it.first);
  6576. msg.add<uint16_t>(it.second);
  6577. }
  6578.  
  6579. writeToOutputBuffer(msg);
  6580. }
  6581.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement