Postado Janeiro 29, 2018 7 anos Estou criando um servidor 8.10 usando de base o TFS 1.2 com downgrade para a versão 8.0, Estou tentando adicionar novamente as potions, porém me deparei com um problema bem estranho: As potions funcionam perfeitamente se eu usa-las, porém ao usa-las pelo hotkey, elas simplesmente não funcionam. Não acontece nada, nenhum erro, simplesmente não usa. Meu script das potions.xml em actions (script padrão do tfs 1.2) Spoiler local greatHealthPot = 7591 local greatManaPot = 7590 local strongHealthPot = 7588 local strongManaPot = 7589 local healthPot = 7618 local manaPot = 7620 local greatEmptyPot = 7635 local strongEmptyPot = 7634 local emptyPot = 7636 local antidote = Combat() antidote:setParameter(COMBAT_PARAM_TYPE, COMBAT_HEALING) antidote:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) antidote:setParameter(COMBAT_PARAM_TARGETCASTERORTOPMOST, true) antidote:setParameter(COMBAT_PARAM_AGGRESSIVE, false) antidote:setParameter(COMBAT_PARAM_DISPEL, CONDITION_POISON) local exhaust = Condition(CONDITION_EXHAUST_HEAL) exhaust:setParameter(CONDITION_PARAM_TICKS, (configManager.getNumber(configKeys.EX_ACTIONS_DELAY_INTERVAL) - 100)) -- 1000 - 100 due to exact condition timing. -100 doesn't hurt us, and players don't have reminding ~50ms exhaustion. function onUse(player, item, fromPosition, target, toPosition, isHotkey) if target == nil or not target:isPlayer() then return true end if player:getCondition(CONDITION_EXHAUST_HEAL) then player:sendTextMessage(MESSAGE_STATUS_SMALL, Game.getReturnMessage(RETURNVALUE_YOUAREEXHAUSTED)) return true end local itemId = item:getId() if itemId == antidotePot then if not antidote:execute(target, numberToVariant(target:getId())) then return false end player:addCondition(exhaust) target:say("Aaaah...", TALKTYPE_MONSTER_SAY) item:remove(1) player:addItem(emptyPot, 1) elseif itemId == healthPot then if not doTargetCombatHealth(0, target, COMBAT_HEALING, 125, 175, CONST_ME_MAGIC_BLUE) then return false end player:addCondition(exhaust) target:say("Aaaah...", TALKTYPE_MONSTER_SAY) item:remove(1) player:addItem(emptyPot, 1) elseif itemId == manaPot then if not doTargetCombatMana(0, target, 75, 125, CONST_ME_MAGIC_BLUE) then return false end player:addCondition(exhaust) target:say("Aaaah...", TALKTYPE_MONSTER_SAY) item:remove(1) player:addItem(emptyPot, 1) elseif itemId == strongHealthPot then if (not isInArray({3, 4, 7, 8}, target:getVocation():getId()) or target:getLevel() < 50) and not getPlayerFlagValue(player, PlayerFlag_IgnoreSpellCheck) then player:say("This potion can only be consumed by paladins and knights of level 50 or higher.", TALKTYPE_MONSTER_SAY) return true end if not doTargetCombatHealth(0, target, COMBAT_HEALING, 250, 350, CONST_ME_MAGIC_BLUE) then return false end player:addCondition(exhaust) target:say("Aaaah...", TALKTYPE_MONSTER_SAY) item:remove(1) player:addItem(strongEmptyPot, 1) elseif itemId == strongManaPot then if (not isInArray({1, 2, 3, 5, 6, 7}, target:getVocation():getId()) or target:getLevel() < 50) and not getPlayerFlagValue(player, PlayerFlag_IgnoreSpellCheck) then player:say("This potion can only be consumed by sorcerers, druids and paladins of level 50 or higher.", TALKTYPE_MONSTER_SAY) return true end if not doTargetCombatMana(0, target, 115, 185, CONST_ME_MAGIC_BLUE) then return false end player:addCondition(exhaust) target:say("Aaaah...", TALKTYPE_MONSTER_SAY) item:remove(1) player:addItem(greatEmptyPot, 1) elseif itemId == greatHealthPot then if (not isInArray({4, 8}, target:getVocation():getId()) or target:getLevel() < 80) and not getPlayerFlagValue(player, PlayerFlag_IgnoreSpellCheck) then player:say("This potion can only be consumed by knights of level 80 or higher.", TALKTYPE_MONSTER_SAY) return true end if not doTargetCombatHealth(0, target, COMBAT_HEALING, 425, 575, CONST_ME_MAGIC_BLUE) then return false end player:addCondition(exhaust) target:say("Aaaah...", TALKTYPE_MONSTER_SAY) item:remove(1) player:addItem(greatEmptyPot, 1) elseif itemId == greatManaPot then if (not isInArray({1,2,5,6}, target:getVocation():getId()) or target:getLevel() < 80) and not getPlayerFlagValue(player, PlayerFlag_IgnoreSpellCheck) then player:say("This potion can only be consumed by sorcerers and druids of level 80 or higher.", TALKTYPE_MONSTER_SAY) return true end if not doTargetCombatMana(0, target, 150, 250, CONST_ME_MAGIC_BLUE) then return false end player:addCondition(exhaust) target:say("Aaaah...", TALKTYPE_MONSTER_SAY) item:remove(1) player:addItem(greatEmptyPot, 1) end return true end Meu protocolgame.cpp (acredito que o problema esteja aqui) Spoiler /** * The Forgotten Server - a free and open-source MMORPG server emulator * Copyright (C) 2016 Mark Samman <[email protected]> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "otpch.h" #include <boost/range/adaptor/reversed.hpp> #include "protocolgame.h" #include "outputmessage.h" #include "player.h" #include "configmanager.h" #include "actions.h" #include "game.h" #include "iologindata.h" #include "waitlist.h" #include "ban.h" #include "scheduler.h" extern ConfigManager g_config; extern Actions actions; extern CreatureEvents* g_creatureEvents; extern Chat* g_chat; ProtocolGame::ProtocolGame(Connection_ptr connection) : Protocol(connection), player(nullptr), eventConnect(0), version(CLIENT_VERSION_MIN), debugAssertSent(false), acceptPackets(false) { // } void ProtocolGame::release() { //dispatcher thread if (player && player->client == shared_from_this()) { player->client.reset(); player->decrementReferenceCounter(); player = nullptr; } OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this()); Protocol::release(); } void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem) { //dispatcher thread Player* _player = g_game.getPlayerByName(name); if (!_player || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) { player = new Player(getThis()); player->setName(name); player->incrementReferenceCounter(); player->setID(); if (!IOLoginData::preloadPlayer(player, name)) { disconnectClient("Your character could not be loaded."); return; } if (IOBan::isPlayerNamelocked(player->getGUID())) { disconnectClient("Your character has been namelocked."); return; } if (g_game.getGameState() == GAME_STATE_CLOSING && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) { disconnectClient("The game is just going down.\nPlease try again later."); return; } if (g_game.getGameState() == GAME_STATE_CLOSED && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) { disconnectClient("Server is currently closed.\nPlease try again later."); return; } if (g_config.getBoolean(ConfigManager::ONE_PLAYER_ON_ACCOUNT) && player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && g_game.getPlayerByAccount(player->getAccount())) { disconnectClient("You may only login with one character\nof your account at the same time."); return; } if (!player->hasFlag(PlayerFlag_CannotBeBanned)) { BanInfo banInfo; if (IOBan::isAccountBanned(accountId, banInfo)) { if (banInfo.reason.empty()) { banInfo.reason = "(none)"; } std::ostringstream ss; if (banInfo.expiresAt > 0) { ss << "Your account has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason; } else { ss << "Your account has been permanently banned by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason; } disconnectClient(ss.str()); return; } } if (!WaitingList::getInstance()->clientLogin(player)) { uint32_t currentSlot = WaitingList::getInstance()->getClientSlot(player); uint32_t retryTime = WaitingList::getTime(currentSlot); std::ostringstream ss; ss << "Too many players online.\nYou are at place " << currentSlot << " on the waiting list."; auto output = OutputMessagePool::getOutputMessage(); output->addByte(0x16); output->addString(ss.str()); output->addByte(retryTime); send(output); disconnect(); return; } if (!IOLoginData::loadPlayerByName(player, name)) { disconnectClient("Your character could not be loaded."); return; } player->setOperatingSystem(operatingSystem); if (!g_game.placeCreature(player, player->getLoginPosition())) { if (!g_game.placeCreature(player, player->getTemplePosition(), false, true)) { disconnectClient("Temple position is wrong. Contact the administrator."); return; } } if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) { player->registerCreatureEvent("ExtendedOpcode"); } player->lastIP = player->getIP(); player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1); acceptPackets = true; } else { if (eventConnect != 0 || !g_config.getBoolean(ConfigManager::REPLACE_KICK_ON_LOGIN)) { //Already trying to connect disconnectClient("You are already logged in."); return; } if (_player->client) { _player->disconnect(); _player->isConnecting = true; eventConnect = g_scheduler.addEvent(createSchedulerTask(1000, std::bind(&ProtocolGame::connect, getThis(), _player->getID(), operatingSystem))); } else { connect(_player->getID(), operatingSystem); } } OutputMessagePool::getInstance().addProtocolToAutosend(shared_from_this()); } void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem) { eventConnect = 0; Player* _player = g_game.getPlayerByID(playerId); if (!_player || _player->client) { disconnectClient("You are already logged in."); return; } if (isConnectionExpired()) { //ProtocolGame::release() has been called at this point and the Connection object //no longer exists, so we return to prevent leakage of the Player. return; } player = _player; player->incrementReferenceCounter(); g_chat->removeUserFromAllChannels(*player); player->setOperatingSystem(operatingSystem); player->isConnecting = false; player->client = getThis(); sendAddCreature(player, player->getPosition(), 0, false); player->lastIP = player->getIP(); player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1); acceptPackets = true; } void ProtocolGame::logout(bool displayEffect, bool forced) { //dispatcher thread if (!player) { return; } if (!player->isRemoved()) { if (!forced) { if (!player->isAccessPlayer()) { if (player->getTile()->hasFlag(TILESTATE_NOLOGOUT)) { player->sendCancelMessage(RETURNVALUE_YOUCANNOTLOGOUTHERE); return; } if (!player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) && player->hasCondition(CONDITION_INFIGHT)) { player->sendCancelMessage(RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT); return; } } //scripting event - onLogout if (!g_creatureEvents->playerLogout(player)) { //Let the script handle the error message return; } } if (displayEffect && player->getHealth() > 0) { g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); } } disconnect(); g_game.removeCreature(player); } void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) { if (g_game.getGameState() == GAME_STATE_SHUTDOWN) { disconnect(); return; } OperatingSystem_t operatingSystem = static_cast<OperatingSystem_t>(msg.get<uint16_t>()); version = msg.get<uint16_t>(); if (!Protocol::RSA_decrypt(msg)) { disconnect(); return; } uint32_t key[4]; key[0] = msg.get<uint32_t>(); key[1] = msg.get<uint32_t>(); key[2] = msg.get<uint32_t>(); key[3] = msg.get<uint32_t>(); enableXTEAEncryption(); setXTEAKey(key); if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) { NetworkMessage opcodeMessage; opcodeMessage.addByte(0x32); opcodeMessage.addByte(0x00); opcodeMessage.add<uint16_t>(0x00); writeToOutputBuffer(opcodeMessage); } msg.skipBytes(1); // gamemaster flag uint32_t accountName = msg.get<uint32_t>(); std::string characterName = msg.getString(); std::string password = msg.getString(); if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { disconnectClient("Only clients with protocol " CLIENT_VERSION_STR " allowed!"); return; } if (accountName == 0) { disconnectClient("You must enter your account name."); return; } if (g_game.getGameState() == GAME_STATE_STARTUP) { disconnectClient("Gameworld is starting up. Please wait."); return; } if (g_game.getGameState() == GAME_STATE_MAINTAIN) { disconnectClient("Gameworld is under maintenance. Please re-connect in a while."); return; } BanInfo banInfo; if (IOBan::isIpBanned(getIP(), banInfo)) { if (banInfo.reason.empty()) { banInfo.reason = "(none)"; } std::ostringstream ss; ss << "Your IP has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason; disconnectClient(ss.str()); return; } uint32_t accountId = IOLoginData::gameworldAuthentication(accountName, password, characterName); if (accountId == 0) { disconnectClient("Account name or password is not correct."); return; } g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem))); } void ProtocolGame::onConnect() { // } void ProtocolGame::disconnectClient(const std::string& message) const { auto output = OutputMessagePool::getOutputMessage(); output->addByte(0x14); output->addString(message); send(output); disconnect(); } void ProtocolGame::writeToOutputBuffer(const NetworkMessage& msg) { auto out = getOutputBuffer(msg.getLength()); out->append(msg); } void ProtocolGame::parsePacket(NetworkMessage& msg) { if (!acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN || msg.getLength() <= 0) { return; } uint8_t recvbyte = msg.getByte(); if (!player) { if (recvbyte == 0x0F) { disconnect(); } return; } //a dead player can not performs actions if (player->isRemoved() || player->getHealth() <= 0) { if (recvbyte == 0x0F) { disconnect(); return; } if (recvbyte != 0x14) { return; } } switch (recvbyte) { case 0x14: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); break; case 0x1E: addGameTask(&Game::playerReceivePing, player->getID()); break; case 0x32: parseExtendedOpcode(msg); break; //otclient extended opcode case 0x64: parseAutoWalk(msg); break; case 0x65: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); break; case 0x66: addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); break; case 0x67: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTH); break; case 0x68: addGameTask(&Game::playerMove, player->getID(), DIRECTION_WEST); break; case 0x69: addGameTask(&Game::playerStopAutoWalk, player->getID()); break; case 0x6A: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHEAST); break; case 0x6B: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHEAST); break; case 0x6C: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHWEST); break; case 0x6D: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHWEST); break; case 0x6F: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_NORTH); break; case 0x70: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_EAST); break; case 0x71: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_SOUTH); break; case 0x72: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_WEST); break; case 0x78: parseThrow(msg); break; case 0x7D: parseRequestTrade(msg); break; case 0x7E: parseLookInTrade(msg); break; case 0x7F: addGameTask(&Game::playerAcceptTrade, player->getID()); break; case 0x80: addGameTask(&Game::playerCloseTrade, player->getID()); break; case 0x82: parseUseItem(msg); break; case 0x83: parseUseItemEx(msg); break; case 0x84: parseUseWithCreature(msg); break; case 0x85: parseRotateItem(msg); break; case 0x87: parseCloseContainer(msg); break; case 0x88: parseUpArrowContainer(msg); break; case 0x89: parseTextWindow(msg); break; case 0x8A: parseHouseWindow(msg); break; case 0x8C: parseLookAt(msg); break; case 0x8D: parseLookInBattleList(msg); break; case 0x8E: /* join aggression */ break; case 0x96: parseSay(msg); break; case 0x97: addGameTask(&Game::playerRequestChannels, player->getID()); break; case 0x98: parseOpenChannel(msg); break; case 0x99: parseCloseChannel(msg); break; case 0x9A: parseOpenPrivateChannel(msg); break; case 0xA0: parseFightModes(msg); break; case 0xA1: parseAttack(msg); break; case 0xA2: parseFollow(msg); break; case 0xA3: parseInviteToParty(msg); break; case 0xA4: parseJoinParty(msg); break; case 0xA5: parseRevokePartyInvite(msg); break; case 0xA6: parsePassPartyLeadership(msg); break; case 0xA7: addGameTask(&Game::playerLeaveParty, player->getID()); break; case 0xAA: addGameTask(&Game::playerCreatePrivateChannel, player->getID()); break; case 0xAB: parseChannelInvite(msg); break; case 0xAC: parseChannelExclude(msg); break; case 0xBE: addGameTask(&Game::playerCancelAttackAndFollow, player->getID()); break; case 0xC9: /* update tile */ break; case 0xCA: parseUpdateContainer(msg); break; case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break; case 0xD3: parseSetOutfit(msg); break; case 0xDC: parseAddVip(msg); break; case 0xDD: parseRemoveVip(msg); break; case 0xE6: parseBugReport(msg); break; case 0xE7: /* thank you */ break; case 0xE8: parseDebugAssert(msg); break; case 0xF0: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerShowQuestLog, player->getID()); break; case 0xF1: parseQuestLine(msg); break; case 0xF2: /* rule violation report */ break; case 0xF3: /* get object info */ break; default: // std::cout << "Player: " << player->getName() << " sent an unknown packet header: 0x" << std::hex << static_cast<uint16_t>(recvbyte) << std::dec << "!" << std::endl; break; } if (msg.isOverrun()) { disconnect(); } } void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg) { int32_t count; Item* ground = tile->getGround(); if (ground) { msg.addItem(ground); count = 1; } else { count = 0; } const TileItemVector* items = tile->getItemList(); if (items) { for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) { msg.addItem(*it); if (++count == 10) { return; } } } const CreatureVector* creatures = tile->getCreatures(); if (creatures) { for (const Creature* creature : boost::adaptors::reverse(*creatures)) { if (!player->canSeeCreature(creature)) { continue; } bool known; uint32_t removedKnown; checkCreatureAsKnown(creature->getID(), known, removedKnown); AddCreature(msg, creature, known, removedKnown); if (++count == 10) { return; } } } if (items) { for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) { msg.addItem(*it); if (++count == 10) { return; } } } } void ProtocolGame::GetMapDescription(int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, NetworkMessage& msg) { int32_t skip = -1; int32_t startz, endz, zstep; if (z > 7) { startz = z - 2; endz = std::min<int32_t>(MAP_MAX_LAYERS - 1, z + 2); zstep = 1; } else { startz = 7; endz = 0; zstep = -1; } for (int32_t nz = startz; nz != endz + zstep; nz += zstep) { GetFloorDescription(msg, x, y, nz, width, height, z - nz, skip); } if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } 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) { for (int32_t nx = 0; nx < width; nx++) { for (int32_t ny = 0; ny < height; ny++) { Tile* tile = g_game.map.getTile(x + nx + offset, y + ny + offset, z); if (tile) { if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } skip = 0; GetTileDescription(tile, msg); } else if (skip == 0xFE) { msg.addByte(0xFF); msg.addByte(0xFF); skip = -1; } else { ++skip; } } } } void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& removedKnown) { auto result = knownCreatureSet.insert(id); if (!result.second) { known = true; return; } known = false; if (knownCreatureSet.size() > 150) { // Look for a creature to remove for (std::unordered_set<uint32_t>::iterator it = knownCreatureSet.begin(), end = knownCreatureSet.end(); it != end; ++it) { Creature* creature = g_game.getCreatureByID(*it); if (!canSee(creature)) { removedKnown = *it; knownCreatureSet.erase(it); return; } } // Bad situation. Let's just remove anyone. std::unordered_set<uint32_t>::iterator it = knownCreatureSet.begin(); if (*it == id) { ++it; } removedKnown = *it; knownCreatureSet.erase(it); } else { removedKnown = 0; } } bool ProtocolGame::canSee(const Creature* c) const { if (!c || !player || c->isRemoved()) { return false; } if (!player->canSeeCreature(c)) { return false; } return canSee(c->getPosition()); } bool ProtocolGame::canSee(const Position& pos) const { return canSee(pos.x, pos.y, pos.z); } bool ProtocolGame::canSee(int32_t x, int32_t y, int32_t z) const { if (!player) { return false; } const Position& myPos = player->getPosition(); if (myPos.z <= 7) { //we are on ground level or above (7 -> 0) //view is from 7 -> 0 if (z > 7) { return false; } } else if (myPos.z >= 8) { //we are underground (8 -> 15) //view is +/- 2 from the floor we stand on if (std::abs(myPos.getZ() - z) > 2) { return false; } } //negative offset means that the action taken place is on a lower floor than ourself int32_t offsetz = myPos.getZ() - z; if ((x >= myPos.getX() - 8 + offsetz) && (x <= myPos.getX() + 9 + offsetz) && (y >= myPos.getY() - 6 + offsetz) && (y <= myPos.getY() + 7 + offsetz)) { return true; } return false; } // Parse methods void ProtocolGame::parseChannelInvite(NetworkMessage& msg) { const std::string name = msg.getString(); addGameTask(&Game::playerChannelInvite, player->getID(), name); } void ProtocolGame::parseChannelExclude(NetworkMessage& msg) { const std::string name = msg.getString(); addGameTask(&Game::playerChannelExclude, player->getID(), name); } void ProtocolGame::parseOpenChannel(NetworkMessage& msg) { uint16_t channelId = msg.get<uint16_t>(); addGameTask(&Game::playerOpenChannel, player->getID(), channelId); } void ProtocolGame::parseCloseChannel(NetworkMessage& msg) { uint16_t channelId = msg.get<uint16_t>(); addGameTask(&Game::playerCloseChannel, player->getID(), channelId); } void ProtocolGame::parseOpenPrivateChannel(NetworkMessage& msg) { const std::string receiver = msg.getString(); addGameTask(&Game::playerOpenPrivateChannel, player->getID(), receiver); } void ProtocolGame::parseAutoWalk(NetworkMessage& msg) { uint8_t numdirs = msg.getByte(); if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 4)) { return; } msg.skipBytes(numdirs); std::forward_list<Direction> path; for (uint8_t i = 0; i < numdirs; ++i) { uint8_t rawdir = msg.getPreviousByte(); switch (rawdir) { case 1: path.push_front(DIRECTION_EAST); break; case 2: path.push_front(DIRECTION_NORTHEAST); break; case 3: path.push_front(DIRECTION_NORTH); break; case 4: path.push_front(DIRECTION_NORTHWEST); break; case 5: path.push_front(DIRECTION_WEST); break; case 6: path.push_front(DIRECTION_SOUTHWEST); break; case 7: path.push_front(DIRECTION_SOUTH); break; case 8: path.push_front(DIRECTION_SOUTHEAST); break; default: break; } } if (path.empty()) { return; } addGameTask(&Game::playerAutoWalk, player->getID(), path); } void ProtocolGame::parseSetOutfit(NetworkMessage& msg) { Outfit_t newOutfit; newOutfit.lookType = msg.get<uint16_t>(); newOutfit.lookHead = msg.getByte(); newOutfit.lookBody = msg.getByte(); newOutfit.lookLegs = msg.getByte(); newOutfit.lookFeet = msg.getByte(); newOutfit.lookAddons = msg.getByte(); addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit); } void ProtocolGame::parseUseItem(NetworkMessage& msg) { Position pos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t stackpos = msg.getByte(); uint8_t index = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItem, player->getID(), pos, stackpos, index, spriteId); } void ProtocolGame::parseUseItemEx(NetworkMessage& msg) { Position fromPos = msg.getPosition(); uint16_t fromSpriteId = msg.get<uint16_t>(); uint8_t fromStackPos = msg.getByte(); Position toPos = msg.getPosition(); uint16_t toSpriteId = msg.get<uint16_t>(); uint8_t toStackPos = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItemEx, player->getID(), fromPos, fromStackPos, fromSpriteId, toPos, toStackPos, toSpriteId); } void ProtocolGame::parseUseWithCreature(NetworkMessage& msg) { Position fromPos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t fromStackPos = msg.getByte(); uint32_t creatureId = msg.get<uint32_t>(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseWithCreature, player->getID(), fromPos, fromStackPos, creatureId, spriteId); } void ProtocolGame::parseCloseContainer(NetworkMessage& msg) { uint8_t cid = msg.getByte(); addGameTask(&Game::playerCloseContainer, player->getID(), cid); } void ProtocolGame::parseUpArrowContainer(NetworkMessage& msg) { uint8_t cid = msg.getByte(); addGameTask(&Game::playerMoveUpContainer, player->getID(), cid); } void ProtocolGame::parseUpdateContainer(NetworkMessage& msg) { uint8_t cid = msg.getByte(); addGameTask(&Game::playerUpdateContainer, player->getID(), cid); } void ProtocolGame::parseThrow(NetworkMessage& msg) { Position fromPos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t fromStackpos = msg.getByte(); Position toPos = msg.getPosition(); uint8_t count = msg.getByte(); if (toPos != fromPos) { addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerMoveThing, player->getID(), fromPos, spriteId, fromStackpos, toPos, count); } } void ProtocolGame::parseLookAt(NetworkMessage& msg) { Position pos = msg.getPosition(); msg.skipBytes(2); // spriteId uint8_t stackpos = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookAt, player->getID(), pos, stackpos); } void ProtocolGame::parseLookInBattleList(NetworkMessage& msg) { uint32_t creatureId = msg.get<uint32_t>(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInBattleList, player->getID(), creatureId); } void ProtocolGame::parseSay(NetworkMessage& msg) { std::string receiver; uint16_t channelId; SpeakClasses type = static_cast<SpeakClasses>(msg.getByte()); switch (type) { case TALKTYPE_PRIVATE: case TALKTYPE_PRIVATE_RED: receiver = msg.getString(); channelId = 0; break; case TALKTYPE_CHANNEL_Y: case TALKTYPE_CHANNEL_R1: case TALKTYPE_CHANNEL_R2: channelId = msg.get<uint16_t>(); break; default: channelId = 0; break; } const std::string text = msg.getString(); if (text.length() > 255) { return; } addGameTask(&Game::playerSay, player->getID(), channelId, type, receiver, text); } void ProtocolGame::parseFightModes(NetworkMessage& msg) { uint8_t rawFightMode = msg.getByte(); // 1 - offensive, 2 - balanced, 3 - defensive uint8_t rawChaseMode = msg.getByte(); // 0 - stand while fightning, 1 - chase opponent uint8_t rawSecureMode = msg.getByte(); // 0 - can't attack unmarked, 1 - can attack unmarked chaseMode_t chaseMode; if (rawChaseMode == 1) { chaseMode = CHASEMODE_FOLLOW; } else { chaseMode = CHASEMODE_STANDSTILL; } fightMode_t fightMode; if (rawFightMode == 1) { fightMode = FIGHTMODE_ATTACK; } else if (rawFightMode == 2) { fightMode = FIGHTMODE_BALANCED; } else { fightMode = FIGHTMODE_DEFENSE; } addGameTask(&Game::playerSetFightModes, player->getID(), fightMode, chaseMode, rawSecureMode != 0); } void ProtocolGame::parseAttack(NetworkMessage& msg) { uint32_t creatureId = msg.get<uint32_t>(); // msg.get<uint32_t>(); creatureId (same as above) addGameTask(&Game::playerSetAttackedCreature, player->getID(), creatureId); } void ProtocolGame::parseFollow(NetworkMessage& msg) { uint32_t creatureId = msg.get<uint32_t>(); // msg.get<uint32_t>(); creatureId (same as above) addGameTask(&Game::playerFollowCreature, player->getID(), creatureId); } void ProtocolGame::parseTextWindow(NetworkMessage& msg) { uint32_t windowTextId = msg.get<uint32_t>(); const std::string newText = msg.getString(); addGameTask(&Game::playerWriteItem, player->getID(), windowTextId, newText); } void ProtocolGame::parseHouseWindow(NetworkMessage& msg) { uint8_t doorId = msg.getByte(); uint32_t id = msg.get<uint32_t>(); const std::string text = msg.getString(); addGameTask(&Game::playerUpdateHouseWindow, player->getID(), doorId, id, text); } void ProtocolGame::parseRequestTrade(NetworkMessage& msg) { Position pos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t stackpos = msg.getByte(); uint32_t playerId = msg.get<uint32_t>(); addGameTask(&Game::playerRequestTrade, player->getID(), pos, stackpos, playerId, spriteId); } void ProtocolGame::parseLookInTrade(NetworkMessage& msg) { bool counterOffer = (msg.getByte() == 0x01); uint8_t index = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInTrade, player->getID(), counterOffer, index); } void ProtocolGame::parseAddVip(NetworkMessage& msg) { const std::string name = msg.getString(); addGameTask(&Game::playerRequestAddVip, player->getID(), name); } void ProtocolGame::parseRemoveVip(NetworkMessage& msg) { uint32_t guid = msg.get<uint32_t>(); addGameTask(&Game::playerRequestRemoveVip, player->getID(), guid); } void ProtocolGame::parseRotateItem(NetworkMessage& msg) { Position pos = msg.getPosition(); uint16_t spriteId = msg.get<uint16_t>(); uint8_t stackpos = msg.getByte(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerRotateItem, player->getID(), pos, stackpos, spriteId); } void ProtocolGame::parseBugReport(NetworkMessage& msg) { std::string bug = msg.getString(); addGameTask(&Game::playerReportBug, player->getID(), bug); } void ProtocolGame::parseDebugAssert(NetworkMessage& msg) { if (debugAssertSent) { return; } debugAssertSent = true; std::string assertLine = msg.getString(); std::string date = msg.getString(); std::string description = msg.getString(); std::string comment = msg.getString(); addGameTask(&Game::playerDebugAssert, player->getID(), assertLine, date, description, comment); } void ProtocolGame::parseInviteToParty(NetworkMessage& msg) { uint32_t targetId = msg.get<uint32_t>(); addGameTask(&Game::playerInviteToParty, player->getID(), targetId); } void ProtocolGame::parseJoinParty(NetworkMessage& msg) { uint32_t targetId = msg.get<uint32_t>(); addGameTask(&Game::playerJoinParty, player->getID(), targetId); } void ProtocolGame::parseRevokePartyInvite(NetworkMessage& msg) { uint32_t targetId = msg.get<uint32_t>(); addGameTask(&Game::playerRevokePartyInvitation, player->getID(), targetId); } void ProtocolGame::parsePassPartyLeadership(NetworkMessage& msg) { uint32_t targetId = msg.get<uint32_t>(); addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId); } void ProtocolGame::parseQuestLine(NetworkMessage& msg) { uint16_t questId = msg.get<uint16_t>(); addGameTask(&Game::playerShowQuestLine, player->getID(), questId); } // Send methods void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver) { NetworkMessage msg; msg.addByte(0xAD); msg.addString(receiver); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureOutfit(const Creature* creature, const Outfit_t& outfit) { if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x8E); msg.add<uint32_t>(creature->getID()); AddOutfit(msg, outfit); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureLight(const Creature* creature) { if (!canSee(creature)) { return; } NetworkMessage msg; AddCreatureLight(msg, creature); writeToOutputBuffer(msg); } void ProtocolGame::sendWorldLight(const LightInfo& lightInfo) { NetworkMessage msg; AddWorldLight(msg, lightInfo); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureShield(const Creature* creature) { if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x91); msg.add<uint32_t>(creature->getID()); msg.addByte(player->getPartyShield(creature->getPlayer())); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureSkull(const Creature* creature) { if (g_game.getWorldType() != WORLD_TYPE_PVP) { return; } if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x90); msg.add<uint32_t>(creature->getID()); msg.addByte(player->getSkullClient(creature)); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t color) { if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x86); msg.add<uint32_t>(creature->getID()); msg.addByte(color); writeToOutputBuffer(msg); } void ProtocolGame::sendAddMarker(const Position& pos, uint8_t markType, const std::string& desc) { NetworkMessage msg; msg.addByte(0xDD); msg.addPosition(pos); msg.addByte(markType); msg.addString(desc); writeToOutputBuffer(msg); } void ProtocolGame::sendReLoginWindow() { NetworkMessage msg; msg.addByte(0x28); writeToOutputBuffer(msg); } void ProtocolGame::sendStats() { NetworkMessage msg; AddPlayerStats(msg); writeToOutputBuffer(msg); } void ProtocolGame::sendTextMessage(MessageClasses mclass, const std::string& message) { NetworkMessage msg; msg.addByte(0xB4); msg.addByte(mclass); msg.addString(message); writeToOutputBuffer(msg); } void ProtocolGame::sendTextMessage(const TextMessage& message) { NetworkMessage msg; msg.addByte(0xB4); msg.addByte(message.type); msg.addString(message.text); writeToOutputBuffer(msg); } void ProtocolGame::sendClosePrivate(uint16_t channelId) { NetworkMessage msg; msg.addByte(0xB3); msg.add<uint16_t>(channelId); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatePrivateChannel(uint16_t channelId, const std::string& channelName) { NetworkMessage msg; msg.addByte(0xB2); msg.add<uint16_t>(channelId); msg.addString(channelName); writeToOutputBuffer(msg); } void ProtocolGame::sendChannelsDialog() { NetworkMessage msg; msg.addByte(0xAB); const ChannelList& list = g_chat->getChannelList(*player); msg.addByte(list.size()); for (ChatChannel* channel : list) { msg.add<uint16_t>(channel->getId()); msg.addString(channel->getName()); } writeToOutputBuffer(msg); } void ProtocolGame::sendChannel(uint16_t channelId, const std::string& channelName, const UsersMap*, const InvitedMap*) { NetworkMessage msg; msg.addByte(0xAC); msg.add<uint16_t>(channelId); msg.addString(channelName); writeToOutputBuffer(msg); } void ProtocolGame::sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel) { NetworkMessage msg; msg.addByte(0xAA); msg.add<uint32_t>(0x00); msg.addString(author); msg.add<uint16_t>(0x00); msg.addByte(type); msg.add<uint16_t>(channel); msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendIcons(uint16_t icons) { NetworkMessage msg; msg.addByte(0xA2); msg.add<uint16_t>(icons); writeToOutputBuffer(msg); } void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool hasParent, uint16_t firstIndex) { NetworkMessage msg; msg.addByte(0x6E); msg.addByte(cid); msg.addItem(container); msg.addString(container->getName()); msg.addByte(container->capacity()); msg.addByte(hasParent ? 0x01 : 0x00); msg.addByte(std::min<uint32_t>(0xFF, container->size())); uint32_t i = 0; const ItemDeque& itemList = container->getItemList(); for (ItemDeque::const_iterator it = itemList.begin() + firstIndex, end = itemList.end(); i < 0xFF && it != end; ++it, ++i) { msg.addItem(*it); } writeToOutputBuffer(msg); } void ProtocolGame::sendQuestLog() { NetworkMessage msg; msg.addByte(0xF0); msg.add<uint16_t>(g_game.quests.getQuestsCount(player)); for (const Quest& quest : g_game.quests.getQuests()) { if (quest.isStarted(player)) { msg.add<uint16_t>(quest.getID()); msg.addString(quest.getName()); msg.addByte(quest.isCompleted(player)); } } writeToOutputBuffer(msg); } void ProtocolGame::sendQuestLine(const Quest* quest) { NetworkMessage msg; msg.addByte(0xF1); msg.add<uint16_t>(quest->getID()); msg.addByte(quest->getMissionsCount(player)); for (const Mission& mission : quest->getMissions()) { if (mission.isStarted(player)) { msg.addString(mission.getName(player)); msg.addString(mission.getDescription(player)); } } writeToOutputBuffer(msg); } void ProtocolGame::sendTradeItemRequest(const std::string& traderName, const Item* item, bool ack) { NetworkMessage msg; if (ack) { msg.addByte(0x7D); } else { msg.addByte(0x7E); } msg.addString(traderName); if (const Container* tradeContainer = item->getContainer()) { std::list<const Container*> listContainer {tradeContainer}; std::list<const Item*> itemList {tradeContainer}; while (!listContainer.empty()) { const Container* container = listContainer.front(); listContainer.pop_front(); for (Item* containerItem : container->getItemList()) { Container* tmpContainer = containerItem->getContainer(); if (tmpContainer) { listContainer.push_back(tmpContainer); } itemList.push_back(containerItem); } } msg.addByte(itemList.size()); for (const Item* listItem : itemList) { msg.addItem(listItem); } } else { msg.addByte(0x01); msg.addItem(item); } writeToOutputBuffer(msg); } void ProtocolGame::sendCloseTrade() { NetworkMessage msg; msg.addByte(0x7F); writeToOutputBuffer(msg); } void ProtocolGame::sendCloseContainer(uint8_t cid) { NetworkMessage msg; msg.addByte(0x6F); msg.addByte(cid); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureTurn(const Creature* creature, uint32_t stackPos) { if (!canSee(creature)) { return; } NetworkMessage msg; msg.addByte(0x6B); msg.addPosition(creature->getPosition()); msg.addByte(stackPos); msg.add<uint16_t>(0x63); msg.add<uint32_t>(creature->getID()); msg.addByte(creature->getDirection()); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, const Position* pos/* = nullptr*/) { NetworkMessage msg; msg.addByte(0xAA); static uint32_t statementId = 0; msg.add<uint32_t>(++statementId); msg.addString(creature->getName()); //Add level only for players if (const Player* speaker = creature->getPlayer()) { msg.add<uint16_t>(speaker->getLevel()); } else { msg.add<uint16_t>(0x00); } msg.addByte(type); if (pos) { msg.addPosition(*pos); } else { msg.addPosition(creature->getPosition()); } msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendToChannel(const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId) { NetworkMessage msg; msg.addByte(0xAA); static uint32_t statementId = 0; msg.add<uint32_t>(++statementId); if (!creature) { msg.add<uint32_t>(0x00); } else if (type == TALKTYPE_CHANNEL_R2) { msg.add<uint32_t>(0x00); type = TALKTYPE_CHANNEL_R1; } else { msg.addString(creature->getName()); //Add level only for players if (const Player* speaker = creature->getPlayer()) { msg.add<uint16_t>(speaker->getLevel()); } else { msg.add<uint16_t>(0x00); } } msg.addByte(type); msg.add<uint16_t>(channelId); msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendPrivateMessage(const Player* speaker, SpeakClasses type, const std::string& text) { NetworkMessage msg; msg.addByte(0xAA); static uint32_t statementId = 0; msg.add<uint32_t>(++statementId); if (speaker) { msg.addString(speaker->getName()); msg.add<uint16_t>(speaker->getLevel()); } else { msg.add<uint32_t>(0x00); } msg.addByte(type); msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendCancelTarget() { NetworkMessage msg; msg.addByte(0xA3); writeToOutputBuffer(msg); } void ProtocolGame::sendChangeSpeed(const Creature* creature, uint32_t speed) { NetworkMessage msg; msg.addByte(0x8F); msg.add<uint32_t>(creature->getID()); msg.add<uint16_t>(speed); writeToOutputBuffer(msg); } void ProtocolGame::sendCancelWalk() { NetworkMessage msg; msg.addByte(0xB5); msg.addByte(player->getDirection()); writeToOutputBuffer(msg); } void ProtocolGame::sendSkills() { NetworkMessage msg; AddPlayerSkills(msg); writeToOutputBuffer(msg); } void ProtocolGame::sendPing() { NetworkMessage msg; msg.addByte(0x1E); writeToOutputBuffer(msg); } void ProtocolGame::sendDistanceShoot(const Position& from, const Position& to, uint8_t type) { NetworkMessage msg; msg.addByte(0x85); msg.addPosition(from); msg.addPosition(to); msg.addByte(type); writeToOutputBuffer(msg); } void ProtocolGame::sendMagicEffect(const Position& pos, uint8_t type) { if (!canSee(pos)) { return; } NetworkMessage msg; msg.addByte(0x83); msg.addPosition(pos); msg.addByte(type); writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureHealth(const Creature* creature) { NetworkMessage msg; msg.addByte(0x8C); msg.add<uint32_t>(creature->getID()); if (creature->isHealthHidden()) { msg.addByte(0x00); } else { msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100)); } writeToOutputBuffer(msg); } void ProtocolGame::sendFYIBox(const std::string& message) { NetworkMessage msg; msg.addByte(0x15); msg.addString(message); writeToOutputBuffer(msg); } //tile void ProtocolGame::sendMapDescription(const Position& pos) { NetworkMessage msg; msg.addByte(0x64); msg.addPosition(player->getPosition()); GetMapDescription(pos.x - 8, pos.y - 6, pos.z, 18, 14, msg); writeToOutputBuffer(msg); } void ProtocolGame::sendAddTileItem(const Position& pos, uint32_t, const Item* item) { if (!canSee(pos)) { return; } NetworkMessage msg; msg.addByte(0x6A); msg.addPosition(pos); msg.addItem(item); writeToOutputBuffer(msg); } void ProtocolGame::sendUpdateTileItem(const Position& pos, uint32_t stackpos, const Item* item) { if (!canSee(pos)) { return; } NetworkMessage msg; msg.addByte(0x6B); msg.addPosition(pos); msg.addByte(stackpos); msg.addItem(item); writeToOutputBuffer(msg); } void ProtocolGame::sendRemoveTileThing(const Position& pos, uint32_t stackpos) { if (!canSee(pos)) { return; } NetworkMessage msg; RemoveTileThing(msg, pos, stackpos); writeToOutputBuffer(msg); } void ProtocolGame::sendUpdateTile(const Tile* tile, const Position& pos) { if (!canSee(pos)) { return; } NetworkMessage msg; msg.addByte(0x69); msg.addPosition(pos); if (tile) { GetTileDescription(tile, msg); msg.addByte(0x00); msg.addByte(0xFF); } else { msg.addByte(0x01); msg.addByte(0xFF); } writeToOutputBuffer(msg); } void ProtocolGame::sendFightModes() { NetworkMessage msg; msg.addByte(0xA7); msg.addByte(player->fightMode); msg.addByte(player->chaseMode); msg.addByte(player->secureMode); writeToOutputBuffer(msg); } void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos, int32_t stackpos, bool isLogin) { if (!canSee(pos)) { return; } if (creature != player) { if (stackpos != -1) { NetworkMessage msg; msg.addByte(0x6A); msg.addPosition(pos); bool known; uint32_t removedKnown; checkCreatureAsKnown(creature->getID(), known, removedKnown); AddCreature(msg, creature, known, removedKnown); writeToOutputBuffer(msg); } if (isLogin) { sendMagicEffect(pos, CONST_ME_TELEPORT); } return; } NetworkMessage msg; msg.addByte(0x0A); msg.add<uint32_t>(player->getID()); msg.addByte(0x32); // beat duration (50) msg.addByte(0x00); // can report bugs? if (player->getAccountType() >= ACCOUNT_TYPE_TUTOR) { msg.addByte(0x01); } else { msg.addByte(0x00); } if (player->hasFlag(PlayerFlag_CanAnswerRuleViolations)) { msg.addByte(0x0B); for (uint8_t i = 0; i < 32; i++) { msg.addByte(0xFF); } } writeToOutputBuffer(msg); sendMapDescription(pos); if (isLogin) { sendMagicEffect(pos, CONST_ME_TELEPORT); } sendInventoryItem(CONST_SLOT_HEAD, player->getInventoryItem(CONST_SLOT_HEAD)); sendInventoryItem(CONST_SLOT_NECKLACE, player->getInventoryItem(CONST_SLOT_NECKLACE)); sendInventoryItem(CONST_SLOT_BACKPACK, player->getInventoryItem(CONST_SLOT_BACKPACK)); sendInventoryItem(CONST_SLOT_ARMOR, player->getInventoryItem(CONST_SLOT_ARMOR)); sendInventoryItem(CONST_SLOT_RIGHT, player->getInventoryItem(CONST_SLOT_RIGHT)); sendInventoryItem(CONST_SLOT_LEFT, player->getInventoryItem(CONST_SLOT_LEFT)); sendInventoryItem(CONST_SLOT_LEGS, player->getInventoryItem(CONST_SLOT_LEGS)); sendInventoryItem(CONST_SLOT_FEET, player->getInventoryItem(CONST_SLOT_FEET)); sendInventoryItem(CONST_SLOT_RING, player->getInventoryItem(CONST_SLOT_RING)); sendInventoryItem(CONST_SLOT_AMMO, player->getInventoryItem(CONST_SLOT_AMMO)); sendStats(); sendSkills(); //gameworld light-settings LightInfo lightInfo; g_game.getWorldLightInfo(lightInfo); sendWorldLight(lightInfo); //player light level sendCreatureLight(creature); const std::forward_list<VIPEntry>& vipEntries = IOLoginData::getVIPEntries(player->getAccount()); if (player->isAccessPlayer()) { for (const VIPEntry& entry : vipEntries) { VipStatus_t vipStatus; Player* vipPlayer = g_game.getPlayerByGUID(entry.guid); if (!vipPlayer) { vipStatus = VIPSTATUS_OFFLINE; } else { vipStatus = VIPSTATUS_ONLINE; } sendVIP(entry.guid, entry.name, vipStatus); } } else { for (const VIPEntry& entry : vipEntries) { VipStatus_t vipStatus; Player* vipPlayer = g_game.getPlayerByGUID(entry.guid); if (!vipPlayer || vipPlayer->isInGhostMode()) { vipStatus = VIPSTATUS_OFFLINE; } else { vipStatus = VIPSTATUS_ONLINE; } sendVIP(entry.guid, entry.name, vipStatus); } } player->sendIcons(); } void ProtocolGame::sendMoveCreature(const Creature* creature, const Position& newPos, int32_t newStackPos, const Position& oldPos, int32_t oldStackPos, bool teleport) { if (creature == player) { if (oldStackPos >= 10) { sendMapDescription(newPos); } else if (teleport) { NetworkMessage msg; RemoveTileThing(msg, oldPos, oldStackPos); writeToOutputBuffer(msg); sendMapDescription(newPos); } else { NetworkMessage msg; if (oldPos.z == 7 && newPos.z >= 8) { RemoveTileThing(msg, oldPos, oldStackPos); } else { msg.addByte(0x6D); msg.addPosition(oldPos); msg.addByte(oldStackPos); msg.addPosition(newPos); } if (newPos.z > oldPos.z) { MoveDownCreature(msg, creature, newPos, oldPos); } else if (newPos.z < oldPos.z) { MoveUpCreature(msg, creature, newPos, oldPos); } if (oldPos.y > newPos.y) { // north, for old x msg.addByte(0x65); GetMapDescription(oldPos.x - 8, newPos.y - 6, newPos.z, 18, 1, msg); } else if (oldPos.y < newPos.y) { // south, for old x msg.addByte(0x67); GetMapDescription(oldPos.x - 8, newPos.y + 7, newPos.z, 18, 1, msg); } if (oldPos.x < newPos.x) { // east, [with new y] msg.addByte(0x66); GetMapDescription(newPos.x + 9, newPos.y - 6, newPos.z, 1, 14, msg); } else if (oldPos.x > newPos.x) { // west, [with new y] msg.addByte(0x68); GetMapDescription(newPos.x - 8, newPos.y - 6, newPos.z, 1, 14, msg); } writeToOutputBuffer(msg); } } else if (canSee(oldPos) && canSee(creature->getPosition())) { if (teleport || (oldPos.z == 7 && newPos.z >= 8) || oldStackPos >= 10) { sendRemoveTileThing(oldPos, oldStackPos); sendAddCreature(creature, newPos, newStackPos, false); } else { NetworkMessage msg; msg.addByte(0x6D); msg.addPosition(oldPos); msg.addByte(oldStackPos); msg.addPosition(creature->getPosition()); writeToOutputBuffer(msg); } } else if (canSee(oldPos)) { sendRemoveTileThing(oldPos, oldStackPos); } else if (canSee(creature->getPosition())) { sendAddCreature(creature, newPos, newStackPos, false); } } void ProtocolGame::sendInventoryItem(slots_t slot, const Item* item) { NetworkMessage msg; if (item) { msg.addByte(0x78); msg.addByte(slot); msg.addItem(item); } else { msg.addByte(0x79); msg.addByte(slot); } writeToOutputBuffer(msg); } void ProtocolGame::sendAddContainerItem(uint8_t cid, const Item* item) { NetworkMessage msg; msg.addByte(0x70); msg.addByte(cid); msg.addItem(item); writeToOutputBuffer(msg); } void ProtocolGame::sendUpdateContainerItem(uint8_t cid, uint16_t slot, const Item* item) { NetworkMessage msg; msg.addByte(0x71); msg.addByte(cid); msg.addByte(slot); msg.addItem(item); writeToOutputBuffer(msg); } void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot) { NetworkMessage msg; msg.addByte(0x72); msg.addByte(cid); msg.addByte(slot); writeToOutputBuffer(msg); } void ProtocolGame::sendTextWindow(uint32_t windowTextId, Item* item, uint16_t maxlen, bool canWrite) { NetworkMessage msg; msg.addByte(0x96); msg.add<uint32_t>(windowTextId); msg.addItem(item); if (canWrite) { msg.add<uint16_t>(maxlen); msg.addString(item->getText()); } else { const std::string& text = item->getText(); msg.add<uint16_t>(text.size()); msg.addString(text); } const std::string& writer = item->getWriter(); if (!writer.empty()) { msg.addString(writer); } else { msg.add<uint16_t>(0x00); } time_t writtenDate = item->getDate(); if (writtenDate != 0) { msg.addString(formatDateShort(writtenDate)); } else { msg.add<uint16_t>(0x00); } writeToOutputBuffer(msg); } void ProtocolGame::sendTextWindow(uint32_t windowTextId, uint32_t itemId, const std::string& text) { NetworkMessage msg; msg.addByte(0x96); msg.add<uint32_t>(windowTextId); msg.addItem(itemId, 1); msg.add<uint16_t>(text.size()); msg.addString(text); msg.add<uint16_t>(0x00); msg.add<uint16_t>(0x00); writeToOutputBuffer(msg); } void ProtocolGame::sendHouseWindow(uint32_t windowTextId, const std::string& text) { NetworkMessage msg; msg.addByte(0x97); msg.addByte(0x00); msg.add<uint32_t>(windowTextId); msg.addString(text); writeToOutputBuffer(msg); } void ProtocolGame::sendOutfitWindow() { NetworkMessage msg; msg.addByte(0xC8); Outfit_t currentOutfit = player->getDefaultOutfit(); AddOutfit(msg, currentOutfit); std::vector<ProtocolOutfit> protocolOutfits; if (player->isAccessPlayer()) { static const std::string gamemasterOutfitName = "Gamemaster"; protocolOutfits.emplace_back( &gamemasterOutfitName, 75, 0 ); } const auto& outfits = Outfits::getInstance()->getOutfits(player->getSex()); protocolOutfits.reserve(outfits.size()); for (const Outfit& outfit : outfits) { uint8_t addons; if (!player->getOutfitAddons(outfit, addons)) { continue; } protocolOutfits.emplace_back( &outfit.name, outfit.lookType, addons ); if (protocolOutfits.size() == 50) { // Game client doesn't allow more than 50 outfits break; } } msg.addByte(protocolOutfits.size()); for (const ProtocolOutfit& outfit : protocolOutfits) { msg.add<uint16_t>(outfit.lookType); msg.addString(*outfit.name); msg.addByte(outfit.addons); } writeToOutputBuffer(msg); } void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus) { NetworkMessage msg; msg.addByte(newStatus ? 0xD3 : 0xD4); msg.add<uint32_t>(guid); writeToOutputBuffer(msg); } void ProtocolGame::sendVIP(uint32_t guid, const std::string& name, VipStatus_t status) { NetworkMessage msg; msg.addByte(0xD2); msg.add<uint32_t>(guid); msg.addString(name); msg.addByte(status); writeToOutputBuffer(msg); } void ProtocolGame::sendAnimatedText(const std::string& message, const Position& pos, TextColor_t color) { if (!canSee(pos)) { return; } NetworkMessage msg; msg.addByte(0x84); msg.addPosition(pos); msg.addByte(color); msg.addString(message); writeToOutputBuffer(msg); } ////////////// Add common messages void ProtocolGame::AddCreature(NetworkMessage& msg, const Creature* creature, bool known, uint32_t remove) { const Player* otherPlayer = creature->getPlayer(); if (known) { msg.add<uint16_t>(0x62); msg.add<uint32_t>(creature->getID()); } else { msg.add<uint16_t>(0x61); msg.add<uint32_t>(remove); msg.add<uint32_t>(creature->getID()); msg.addString(creature->getName()); } if (creature->isHealthHidden()) { msg.addByte(0x00); } else { msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100)); } msg.addByte(creature->getDirection()); if (!creature->isInGhostMode() && !creature->isInvisible()) { AddOutfit(msg, creature->getCurrentOutfit()); } else { static Outfit_t outfit; AddOutfit(msg, outfit); } LightInfo lightInfo; creature->getCreatureLight(lightInfo); msg.addByte(player->isAccessPlayer() ? 0xFF : lightInfo.level); msg.addByte(lightInfo.color); msg.add<uint16_t>(creature->getStepSpeed()); msg.addByte(player->getSkullClient(otherPlayer)); msg.addByte(player->getPartyShield(otherPlayer)); } void ProtocolGame::AddPlayerStats(NetworkMessage& msg) { msg.addByte(0xA0); msg.add<uint16_t>(std::min<int32_t>(player->getHealth(), std::numeric_limits<uint16_t>::max())); msg.add<uint16_t>(std::min<int32_t>(player->getPlayerInfo(PLAYERINFO_MAXHEALTH), std::numeric_limits<uint16_t>::max())); msg.add<uint16_t>(player->getFreeCapacity() / 100); msg.add<uint32_t>(std::min<uint32_t>(player->getExperience(), 0x7FFFFFFF)); msg.add<uint16_t>(player->getLevel()); msg.addByte(player->getPlayerInfo(PLAYERINFO_LEVELPERCENT)); msg.add<uint16_t>(std::min<int32_t>(player->getMana(), std::numeric_limits<uint16_t>::max())); msg.add<uint16_t>(std::min<int32_t>(player->getPlayerInfo(PLAYERINFO_MAXMANA), std::numeric_limits<uint16_t>::max())); msg.addByte(std::min<uint32_t>(player->getMagicLevel(), std::numeric_limits<uint8_t>::max())); msg.addByte(player->getPlayerInfo(PLAYERINFO_MAGICLEVELPERCENT)); msg.addByte(player->getSoul()); msg.add<uint16_t>(player->getStaminaMinutes()); } void ProtocolGame::AddPlayerSkills(NetworkMessage& msg) { msg.addByte(0xA1); for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { msg.addByte(std::min<int32_t>(player->getSkillLevel(i), std::numeric_limits<uint16_t>::max())); msg.addByte(player->getSkillPercent(i)); } } void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit) { msg.add<uint16_t>(outfit.lookType); if (outfit.lookType != 0) { msg.addByte(outfit.lookHead); msg.addByte(outfit.lookBody); msg.addByte(outfit.lookLegs); msg.addByte(outfit.lookFeet); msg.addByte(outfit.lookAddons); } else { msg.addItemId(outfit.lookTypeEx); } } void ProtocolGame::AddWorldLight(NetworkMessage& msg, const LightInfo& lightInfo) { msg.addByte(0x82); msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level)); msg.addByte(lightInfo.color); } void ProtocolGame::AddCreatureLight(NetworkMessage& msg, const Creature* creature) { LightInfo lightInfo; creature->getCreatureLight(lightInfo); msg.addByte(0x8D); msg.add<uint32_t>(creature->getID()); msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level)); msg.addByte(lightInfo.color); } //tile void ProtocolGame::RemoveTileThing(NetworkMessage& msg, const Position& pos, uint32_t stackpos) { if (stackpos >= 10) { return; } msg.addByte(0x6C); msg.addPosition(pos); msg.addByte(stackpos); } void ProtocolGame::MoveUpCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos) { if (creature != player) { return; } //floor change up msg.addByte(0xBE); //going to surface if (newPos.z == 7) { int32_t skip = -1; GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 5, 18, 14, 3, skip); //(floor 7 and 6 already set) GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 4, 18, 14, 4, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 3, 18, 14, 5, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 2, 18, 14, 6, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 1, 18, 14, 7, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 0, 18, 14, 8, skip); if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } //underground, going one floor up (still underground) else if (newPos.z > 7) { int32_t skip = -1; GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, oldPos.getZ() - 3, 18, 14, 3, skip); if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } //moving up a floor up makes us out of sync //west msg.addByte(0x68); GetMapDescription(oldPos.x - 8, oldPos.y - 5, newPos.z, 1, 14, msg); //north msg.addByte(0x65); GetMapDescription(oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 1, msg); } void ProtocolGame::MoveDownCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos) { if (creature != player) { return; } //floor change down msg.addByte(0xBF); //going from surface to underground if (newPos.z == 8) { int32_t skip = -1; GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 14, -1, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 1, 18, 14, -2, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip); if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } //going further down else if (newPos.z > oldPos.z && newPos.z > 8 && newPos.z < 14) { int32_t skip = -1; GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip); if (skip >= 0) { msg.addByte(skip); msg.addByte(0xFF); } } //moving down a floor makes us out of sync //east msg.addByte(0x66); GetMapDescription(oldPos.x + 9, oldPos.y - 7, newPos.z, 1, 14, msg); //south msg.addByte(0x67); GetMapDescription(oldPos.x - 8, oldPos.y + 7, newPos.z, 18, 1, msg); } void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg) { uint8_t opcode = msg.getByte(); const std::string& buffer = msg.getString(); // process additional opcodes via lua script event addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer); } Tenho a hipótese que a função que considera as potions como itens usavéis pelo hotkey foram removidas das sources (8.0 não havia potions). Tentei comparar com um tfs 1.2 padrão porém não consegui achar a função que lida com elas, fico muito agradecido se alguem me ajudar!! OBS: Estou usando o client 8.10 com o items.otb devidamente atualizado e configurado acredito eu
Participe da conversa
Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.