Ir para conteúdo

Hp e mana por porcentagem!


Posts Recomendados

  • Diretor

Fala galera do Xtibia beleza? Alguns meses atrás tinha resolvido um tópico que o membro @julhinhuu tinha pedido Mana e Life por porcentagem para TFS 1.2

Como tem muita gente atrás desse code e não acha em nenhum lugar, resolvi adaptar para TFS 1.x e compartilhar com vocês!

Estarei deixando também para as versões anteriores...

AwkUqYB.png

TFS 1.X

 

Em protocolgame.cpp procure por:

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()));

Troque por isto:

if (player->getPlayerInfo(PLAYERINFO_MAXHEALTH) > 0) {
    msg.add<uint16_t>(player->getHealth() * 100 / player->getPlayerInfo(PLAYERINFO_MAXHEALTH));
    msg.add<uint16_t>(100);
} else {
    msg.add<uint16_t>(0);
    msg.add<uint16_t>(0);
}

Depois procure por:

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()));

Troque por:

if (player->getPlayerInfo(PLAYERINFO_MAXMANA) > 0) {
    msg.add<uint16_t>(player->getMana() * 100 / player->getPlayerInfo(PLAYERINFO_MAXMANA));
    msg.add<uint16_t>(100);
} else {
    msg.add<uint16_t>(0);
    msg.add<uint16_t>(0);
}

Pronto depois é só compilar e ser feliz xD

 

Testado em TFS 1.0/1.2

 

Para as versões anteriores aqui está:

Spoiler

PARA TFS 0.3.7/0.4 (qualquer rev) e para OTX:

Spoiler

Em protocolgame.cpp procure por:



msg->put<uint16_t>(player->getHealth());
msg->put<uint16_t>(player->getPlayerInfo(PLAYERINFO_MAXHEALTH));

Substitui por:



if (player->getPlayerInfo(PLAYERINFO_MAXHEALTH) > 0)
    {
        msg->put<uint16_t>(uint16_t(player->getHealth() * 100 / player->getPlayerInfo(PLAYERINFO_MAXHEALTH)));
        msg->put<uint16_t>(100);
    }
    else
    {
        msg->put<uint16_t>(0);
        msg->put<uint16_t>(0);  
    }

Depois procure por:



msg->put<uint16_t>(player->getPlayerInfo(PLAYERINFO_MANA));
msg->put<uint16_t>(player->getPlayerInfo(PLAYERINFO_MAXMANA));

Substitui por:



if (player->getPlayerInfo(PLAYERINFO_MAXMANA) > 0)
    {
        msg->put<uint16_t>(player->getPlayerInfo(PLAYERINFO_MANA) * 100 / player->getPlayerInfo(PLAYERINFO_MAXMANA));
        msg->put<uint16_t>(100);
    }
    else
    {
        msg->put<uint16_t>(0);
        msg->put<uint16_t>(0);
    }

 

Testado em TFS 0.3.7 e 0.4 creio que funciona em OTX também.

 

PARA TFS 0.3.6/0.3.4 E ANTERIORES:

Spoiler

Em protocolgame.cpp procure por:



msg->AddU16(player->getHealth());
msg->AddU16(player->getPlayerInfo(PLAYERINFO_MAXHEALTH));

Substitui por:



if (player->getPlayerInfo(PLAYERINFO_MAXHEALTH) > 0)
    {
        msg->AddU16(uint16_t(player->getHealth() * 100 / player->getPlayerInfo(PLAYERINFO_MAXHEALTH)));
        msg->AddU16(100);
    }
    else
    {
        msg->AddU16(0);
        msg->AddU16(0);  
    }

Depois procure por:



msg->AddU16(player->getPlayerInfo(PLAYERINFO_MANA));
msg->AddU16(player->getPlayerInfo(PLAYERINFO_MAXMANA));

Substitui por:



if (player->getPlayerInfo(PLAYERINFO_MAXMANA) > 0)
    {
        msg->AddU16(player->getPlayerInfo(PLAYERINFO_MANA) * 100 / player->getPlayerInfo(PLAYERINFO_MAXMANA));
        msg->AddU16(100);
    }
    else
    {
        msg->AddU16(0);
        msg->AddU16(0);
    }

 

Testado em TFS 0.3.6

 

 

Agora inves de ser um número grande --> ssqjn0K.png

  Xn7nChL.png <-- Irá ser um número pequeno

 

Não se preocupe, isso não altera o health nem a mana do jogador, apenas ficara mais fácil de saber se o player irá morrer sim ou não.

Separador.PNG

 

Créditos:

Yan Liima

Link para o comentário
Compartilhar em outros sites

O meu protocolgame.cpp é diferente alguém tem alguma solução??

Spoiler

/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright © 2015  Mark Samman <mark.samman@gmail.com>
*
* 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 "iomarket.h"
#include "waitlist.h"
#include "ban.h"
#include "scheduler.h"
#include "databasetasks.h"

extern Game g_game;
extern ConfigManager g_config;
extern Actions actions;
extern CreatureEvents* g_creatureEvents;
extern Chat* g_chat;

ProtocolGame::LiveCastsMap ProtocolGame::liveCasts;

void ProtocolGame::release()
{
    //dispatcher thread
    stopLiveCast();
    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);
        m_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->clearModalWindows();
    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);
    m_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);
        }
    }

    stopLiveCast();
    disconnect();

    g_game.removeCreature(player);
}

bool ProtocolGame::startLiveCast(const std::string& password /*= ""*/)
{
    auto connection = getConnection();
    if (!g_config.getBoolean(ConfigManager::ENABLE_LIVE_CASTING) || isLiveCaster() || !player || player->isRemoved() || !connection || liveCasts.size() >= getMaxLiveCastCount()) {
        return false;
    }

    {
        std::lock_guard<decltype(liveCastLock)> lock{ liveCastLock };
        //DO NOT do any send operations here
        liveCastName = player->getName();
        liveCastPassword = password;
        isCaster.store(true, std::memory_order_relaxed);
    }

    liveCasts.insert(std::make_pair(player, getThis()));

    registerLiveCast();
    //Send a "dummy" channel
    sendChannel(CHANNEL_CAST, LIVE_CAST_CHAT_NAME, nullptr, nullptr);
    return true;
}

bool ProtocolGame::stopLiveCast()
{
    //dispatcher
    if (!isLiveCaster()) {
        return false;
    }

    CastSpectatorVec spectators;

    {
        std::lock_guard<decltype(liveCastLock)> lock{ liveCastLock };
        //DO NOT do any send operations here
        std::swap(this->spectators, spectators);
        isCaster.store(false, std::memory_order_relaxed);
    }

    liveCasts.erase(player);
    for (auto& spectator : spectators) {
        spectator->onLiveCastStop();
    }
    unregisterLiveCast();

    return true;
}

void ProtocolGame::clearLiveCastInfo()
{
    static std::once_flag flag;
    std::call_once(flag, []() {
        assert(g_game.getGameState() == GAME_STATE_INIT);
        std::ostringstream query;
        query << "TRUNCATE TABLE `live_casts`;";
        g_databaseTasks.addTask(query.str());
    });
}

void ProtocolGame::registerLiveCast()
{
    std::ostringstream query;
    query << "INSERT into `live_casts` (`player_id`, `cast_name`, `password`) VALUES (" << player->getGUID() << ", '"
        << getLiveCastName() << "', " << isPasswordProtected() << ");";
    g_databaseTasks.addTask(query.str());
}

void ProtocolGame::unregisterLiveCast()
{
    std::ostringstream query;
    query << "DELETE FROM `live_casts` WHERE `player_id`=" << player->getGUID() << ";";
    g_databaseTasks.addTask(query.str());
}

void ProtocolGame::updateLiveCastInfo()
{
    std::ostringstream query;
    query << "UPDATE `live_casts` SET `cast_name`='" << getLiveCastName() << "', `password`="
        << isPasswordProtected() << ", `spectators`=" << getSpectatorCount()
        << " WHERE `player_id`=" << player->getGUID() << ";";
    g_databaseTasks.addTask(query.str());
}

void ProtocolGame::addSpectator(ProtocolSpectator_ptr spectatorClient)
{
    std::lock_guard<decltype(liveCastLock)> lock(liveCastLock);
    //DO NOT do any send operations here
    spectators.emplace_back(spectatorClient);
    updateLiveCastInfo();
}

void ProtocolGame::removeSpectator(ProtocolSpectator_ptr spectatorClient)
{
    std::lock_guard<decltype(liveCastLock)> lock(liveCastLock);
    //DO NOT do any send operations here
    auto it = std::find(spectators.begin(), spectators.end(), spectatorClient);
    if (it != spectators.end()) {
        spectators.erase(it);
        updateLiveCastInfo();
    }
}

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>();

    msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision

    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

    std::string sessionKey = msg.getString();
    size_t pos = sessionKey.find('\n');
    if (pos == std::string::npos) {
        disconnectClient("You must enter your account name.");
        return;
    }

    std::string accountName = sessionKey.substr(0, pos);
    if (accountName.empty()) {
        disconnectClient("You must enter your account name.");
        return;
    }

    std::string password = sessionKey.substr(pos + 1);

    std::string characterName = msg.getString();

    uint32_t timeStamp = msg.get<uint32_t>();
    uint8_t randNumber = msg.getByte();
    if (m_challengeTimestamp != timeStamp || m_challengeRandom != randNumber) {
        disconnect();
        return;
    }

    if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) {
        disconnectClient("Only clients with protocol " CLIENT_VERSION_STR " allowed!");
        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::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, bool broadcast /*= true*/)
{
    if (!broadcast && isLiveCaster()) {
        //We're casting and we need to send a packet that's not supposed to be broadcast so we need a new messasge.
        //This shouldn't impact performance by a huge amount as most packets can be broadcast.
        auto out = OutputMessagePool::getOutputMessage();
        out->append(msg);
        send(std::move(out));
    }
    else {
        auto out = getOutputBuffer(msg.getLength());
        if (isLiveCaster()) {
            out->setBroadcastMsg(true);
        }
        out->append(msg);
    }
}

void ProtocolGame::parsePacket(NetworkMessage& msg)
{
    if (!m_acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN || msg.getLength() <= 0) {
        return;
    }

    uint8_t recvbyte = msg.getByte();

    //a dead player can not perform actions
    if (!player || player->isRemoved() || player->getHealth() <= 0) {
        auto this_ptr = getThis();
        g_dispatcher.addTask(createTask([this_ptr]() {
            this_ptr->stopLiveCast();
        }));
        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 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); 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 0x79: parseLookInShop(msg); break;
    case 0x7A: parsePlayerPurchase(msg); break;
    case 0x7B: parsePlayerSale(msg); break;
    case 0x7C: addGameTask(&Game::playerCloseShop, player->getID()); 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 0x9E: addGameTask(&Game::playerCloseNpcChannel, player->getID()); 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 0xA8: parseEnableSharedPartyExperience(msg); 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 0xCB: parseBrowseField(msg); break;
    case 0xCC: parseSeekInContainer(msg); break;
    case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break;
    case 0xD3: parseSetOutfit(msg); break;
    case 0xD4: parseToggleMount(msg); break;
    case 0xDC: parseAddVip(msg); break;
    case 0xDD: parseRemoveVip(msg); break;
    case 0xDE: parseEditVip(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;
    case 0xF4: parseMarketLeave(); break;
    case 0xF5: parseMarketBrowse(msg); break;
    case 0xF6: parseMarketCreateOffer(msg); break;
    case 0xF7: parseMarketCancelOffer(msg); break;
    case 0xF8: parseMarketAcceptOffer(msg); break;
    case 0xF9: parseModalWindowAnswer(msg); 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();
    }
}

// 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() + 8)) {
        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();
    newOutfit.lookMount = msg.get<uint16_t>();
    addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit);
}

void ProtocolGame::parseToggleMount(NetworkMessage& msg)
{
    bool mount = msg.getByte() != 0;
    addGameTask(&Game::playerToggleMount, player->getID(), mount);
}

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_TO:
    case TALKTYPE_PRIVATE_RED_TO:
        receiver = msg.getString();
        channelId = 0;
        break;

    case TALKTYPE_CHANNEL_Y:
    case TALKTYPE_CHANNEL_R1:
        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
                                           // uint8_t rawPvpMode = msg.getByte(); // pvp mode introduced in 10.0

    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::parseLookInShop(NetworkMessage& msg)
{
    uint16_t id = msg.get<uint16_t>();
    uint8_t count = msg.getByte();
    addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInShop, player->getID(), id, count);
}

void ProtocolGame::parsePlayerPurchase(NetworkMessage& msg)
{
    uint16_t id = msg.get<uint16_t>();
    uint8_t count = msg.getByte();
    uint8_t amount = msg.getByte();
    bool ignoreCap = msg.getByte() != 0;
    bool inBackpacks = msg.getByte() != 0;
    addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerPurchaseItem, player->getID(), id, count, amount, ignoreCap, inBackpacks);
}

void ProtocolGame::parsePlayerSale(NetworkMessage& msg)
{
    uint16_t id = msg.get<uint16_t>();
    uint8_t count = msg.getByte();
    uint8_t amount = msg.getByte();
    bool ignoreEquipped = msg.getByte() != 0;
    addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerSellItem, player->getID(), id, count, amount, ignoreEquipped);
}

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::parseEditVip(NetworkMessage& msg)
{
    uint32_t guid = msg.get<uint32_t>();
    const std::string description = msg.getString();
    uint32_t icon = std::min<uint32_t>(10, msg.get<uint32_t>()); // 10 is max icon in 9.63
    bool notify = msg.getByte() != 0;
    addGameTask(&Game::playerRequestEditVip, player->getID(), guid, description, icon, notify);
}

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)
{
    uint8_t category = msg.getByte();
    std::string message = msg.getString();

    Position position;
    if (category == BUG_CATEGORY_MAP) {
        position = msg.getPosition();
    }

    addGameTask(&Game::playerReportBug, player->getID(), message, position, category);
}

void ProtocolGame::parseDebugAssert(NetworkMessage& msg)
{
    if (m_debugAssertSent) {
        return;
    }

    m_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::parseEnableSharedPartyExperience(NetworkMessage& msg)
{
    bool sharedExpActive = msg.getByte() == 1;
    addGameTask(&Game::playerEnableSharedPartyExperience, player->getID(), sharedExpActive);
}

void ProtocolGame::parseQuestLine(NetworkMessage& msg)
{
    uint16_t questId = msg.get<uint16_t>();
    addGameTask(&Game::playerShowQuestLine, player->getID(), questId);
}

void ProtocolGame::parseMarketLeave()
{
    addGameTask(&Game::playerLeaveMarket, player->getID());
}

void ProtocolGame::parseMarketBrowse(NetworkMessage& msg)
{
    uint16_t browseId = msg.get<uint16_t>();

    if (browseId == MARKETREQUEST_OWN_OFFERS) {
        addGameTask(&Game::playerBrowseMarketOwnOffers, player->getID());
    }
    else if (browseId == MARKETREQUEST_OWN_HISTORY) {
        addGameTask(&Game::playerBrowseMarketOwnHistory, player->getID());
    }
    else {
        addGameTask(&Game::playerBrowseMarket, player->getID(), browseId);
    }
}

void ProtocolGame::parseMarketCreateOffer(NetworkMessage& msg)
{
    uint8_t type = msg.getByte();
    uint16_t spriteId = msg.get<uint16_t>();
    uint16_t amount = msg.get<uint16_t>();
    uint32_t price = msg.get<uint32_t>();
    bool anonymous = (msg.getByte() != 0);
    addGameTask(&Game::playerCreateMarketOffer, player->getID(), type, spriteId, amount, price, anonymous);
}

void ProtocolGame::parseMarketCancelOffer(NetworkMessage& msg)
{
    uint32_t timestamp = msg.get<uint32_t>();
    uint16_t counter = msg.get<uint16_t>();
    addGameTask(&Game::playerCancelMarketOffer, player->getID(), timestamp, counter);
}

void ProtocolGame::parseMarketAcceptOffer(NetworkMessage& msg)
{
    uint32_t timestamp = msg.get<uint32_t>();
    uint16_t counter = msg.get<uint16_t>();
    uint16_t amount = msg.get<uint16_t>();
    addGameTask(&Game::playerAcceptMarketOffer, player->getID(), timestamp, counter, amount);
}

void ProtocolGame::parseModalWindowAnswer(NetworkMessage& msg)
{
    uint32_t id = msg.get<uint32_t>();
    uint8_t button = msg.getByte();
    uint8_t choice = msg.getByte();
    addGameTask(&Game::playerAnswerModalWindow, player->getID(), id, button, choice);
}

void ProtocolGame::parseBrowseField(NetworkMessage& msg)
{
    const Position& pos = msg.getPosition();
    addGameTask(&Game::playerBrowseField, player->getID(), pos);
}

void ProtocolGame::parseSeekInContainer(NetworkMessage& msg)
{
    uint8_t containerId = msg.getByte();
    uint16_t index = msg.get<uint16_t>();
    addGameTask(&Game::playerSeekInContainer, player->getID(), containerId, index);
}

// Send methods
void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver)
{
    NetworkMessage msg;
    msg.addByte(0xAD);
    msg.addString(receiver);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent)
{
    NetworkMessage msg;
    msg.addByte(0xF3);
    msg.add<uint16_t>(channelId);
    msg.addString(playerName);
    msg.addByte(channelEvent);
    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::sendCreatureWalkthrough(const Creature* creature, bool walkthrough)
{
    if (!canSee(creature)) {
        return;
    }

    NetworkMessage msg;
    msg.addByte(0x92);
    msg.add<uint32_t>(creature->getID());
    msg.addByte(walkthrough ? 0x00 : 0x01);
    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::sendCreatureType(uint32_t creatureId, uint8_t creatureType)
{
    NetworkMessage msg;
    msg.addByte(0x95);
    msg.add<uint32_t>(creatureId);
    msg.addByte(creatureType);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendCreatureHelpers(uint32_t creatureId, uint16_t helpers)
{
    NetworkMessage msg;
    msg.addByte(0x94);
    msg.add<uint32_t>(creatureId);
    msg.add<uint16_t>(helpers);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t color)
{
    if (!canSee(creature)) {
        return;
    }

    NetworkMessage msg;
    msg.addByte(0x93);
    msg.add<uint32_t>(creature->getID());
    msg.addByte(0x01);
    msg.addByte(color);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendTutorial(uint8_t tutorialId)
{
    NetworkMessage msg;
    msg.addByte(0xDC);
    msg.addByte(tutorialId);
    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(uint8_t unfairFightReduction)
{
    NetworkMessage msg;
    msg.addByte(0x28);
    msg.addByte(0x00);
    msg.addByte(unfairFightReduction);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendTextMessage(const TextMessage& message)
{
    NetworkMessage msg;
    msg.addByte(0xB4);
    msg.addByte(message.type);
    switch (message.type) {
    case MESSAGE_DAMAGE_DEALT:
    case MESSAGE_DAMAGE_RECEIVED:
    case MESSAGE_DAMAGE_OTHERS: {
        msg.addPosition(message.position);
        msg.add<uint32_t>(message.primary.value);
        msg.addByte(message.primary.color);
        msg.add<uint32_t>(message.secondary.value);
        msg.addByte(message.secondary.color);
        break;
    }
    case MESSAGE_HEALED:
    case MESSAGE_HEALED_OTHERS:
    case MESSAGE_EXPERIENCE:
    case MESSAGE_EXPERIENCE_OTHERS: {
        msg.addPosition(message.position);
        msg.add<uint32_t>(message.primary.value);
        msg.addByte(message.primary.color);
        break;
    }
    default: {
        break;
    }
    }
    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);
    msg.add<uint16_t>(0x01);
    msg.addString(player->getName());
    msg.add<uint16_t>(0x00);
    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::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::sendShop(Npc* npc, const ShopInfoList& itemList)
{
    NetworkMessage msg;
    msg.addByte(0x7A);
    msg.addString(npc->getName());

    uint16_t itemsToSend = std::min<size_t>(itemList.size(), std::numeric_limits<uint16_t>::max());
    msg.add<uint16_t>(itemsToSend);

    uint16_t i = 0;
    for (ShopInfoList::const_iterator it = itemList.begin(); i < itemsToSend; ++it, ++i) {
        AddShopItem(msg, *it);
    }

    writeToOutputBuffer(msg);
}

void ProtocolGame::sendCloseShop()
{
    NetworkMessage msg;
    msg.addByte(0x7C);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendSaleItemList(const std::list<ShopInfo>& shop)
{
    NetworkMessage msg;
    msg.addByte(0x7B);
    msg.add<uint64_t>(player->getMoney());

    std::map<uint16_t, uint32_t> saleMap;

    if (shop.size() <= 5) {
        // For very small shops it's not worth it to create the complete map
        for (const ShopInfo& shopInfo : shop) {
            if (shopInfo.sellPrice == 0) {
                continue;
            }

            int8_t subtype = -1;

            const ItemType& itemType = Item::items[shopInfo.itemId];
            if (itemType.hasSubType() && !itemType.stackable) {
                subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
            }

            uint32_t count = player->getItemTypeCount(shopInfo.itemId, subtype);
            if (count > 0) {
                saleMap[shopInfo.itemId] = count;
            }
        }
    }
    else {
        // Large shop, it's better to get a cached map of all item counts and use it
        // We need a temporary map since the finished map should only contain items
        // available in the shop
        std::map<uint32_t, uint32_t> tempSaleMap;
        player->getAllItemTypeCount(tempSaleMap);

        // We must still check manually for the special items that require subtype matches
        // (That is, fluids such as potions etc., actually these items are very few since
        // health potions now use their own ID)
        for (const ShopInfo& shopInfo : shop) {
            if (shopInfo.sellPrice == 0) {
                continue;
            }

            int8_t subtype = -1;

            const ItemType& itemType = Item::items[shopInfo.itemId];
            if (itemType.hasSubType() && !itemType.stackable) {
                subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
            }

            if (subtype != -1) {
                uint32_t count;
                if (!itemType.isFluidContainer() && !itemType.isSplash()) {
                    count = player->getItemTypeCount(shopInfo.itemId, subtype); // This shop item requires extra checks
                }
                else {
                    count = subtype;
                }

                if (count > 0) {
                    saleMap[shopInfo.itemId] = count;
                }
            }
            else {
                std::map<uint32_t, uint32_t>::const_iterator findIt = tempSaleMap.find(shopInfo.itemId);
                if (findIt != tempSaleMap.end() && findIt->second > 0) {
                    saleMap[shopInfo.itemId] = findIt->second;
                }
            }
        }
    }

    uint8_t itemsToSend = std::min<size_t>(saleMap.size(), std::numeric_limits<uint8_t>::max());
    msg.addByte(itemsToSend);

    uint8_t i = 0;
    for (std::map<uint16_t, uint32_t>::const_iterator it = saleMap.begin(); i < itemsToSend; ++it, ++i) {
        msg.addItemId(it->first);
        msg.addByte(std::min<uint32_t>(it->second, std::numeric_limits<uint8_t>::max()));
    }

    writeToOutputBuffer(msg);
}

void ProtocolGame::sendMarketEnter(uint32_t depotId)
{
    NetworkMessage msg;
    msg.addByte(0xF6);

    msg.add<uint64_t>(player->getBankBalance());
    msg.addByte(std::min<uint32_t>(IOMarket::getPlayerOfferCount(player->getGUID()), std::numeric_limits<uint8_t>::max()));

    DepotChest* depotChest = player->getDepotChest(depotId, false);
    if (!depotChest) {
        msg.add<uint16_t>(0x00);
        writeToOutputBuffer(msg);
        return;
    }

    player->setInMarket(true);

    std::map<uint16_t, uint32_t> depotItems;
    std::forward_list<Container*> containerList{ depotChest, player->getInbox() };

    do {
        Container* container = containerList.front();
        containerList.pop_front();

        for (Item* item : container->getItemList()) {
            Container* c = item->getContainer();
            if (c && !c->empty()) {
                containerList.push_front©;
                continue;
            }

            const ItemType& itemType = Item::items[item->getID()];
            if (itemType.wareId == 0) {
                continue;
            }

            if (c && (!itemType.isContainer() || c->capacity() != itemType.maxItems)) {
                continue;
            }

            if (!item->hasMarketAttributes()) {
                continue;
            }

            depotItems[itemType.wareId] += Item::countByType(item, -1);
        }
    } while (!containerList.empty());

    uint16_t itemsToSend = std::min<size_t>(depotItems.size(), std::numeric_limits<uint16_t>::max());
    msg.add<uint16_t>(itemsToSend);

    uint16_t i = 0;
    for (std::map<uint16_t, uint32_t>::const_iterator it = depotItems.begin(); i < itemsToSend; ++it, ++i) {
        msg.add<uint16_t>(it->first);
        msg.add<uint16_t>(std::min<uint32_t>(0xFFFF, it->second));
    }

    writeToOutputBuffer(msg);
}

void ProtocolGame::sendMarketLeave()
{
    NetworkMessage msg;
    msg.addByte(0xF7);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers)
{
    NetworkMessage msg;

    msg.addByte(0xF9);
    msg.addItemId(itemId);

    msg.add<uint32_t>(buyOffers.size());
    for (const MarketOffer& offer : buyOffers) {
        msg.add<uint32_t>(offer.timestamp);
        msg.add<uint16_t>(offer.counter);
        msg.add<uint16_t>(offer.amount);
        msg.add<uint32_t>(offer.price);
        msg.addString(offer.playerName);
    }

    msg.add<uint32_t>(sellOffers.size());
    for (const MarketOffer& offer : sellOffers) {
        msg.add<uint32_t>(offer.timestamp);
        msg.add<uint16_t>(offer.counter);
        msg.add<uint16_t>(offer.amount);
        msg.add<uint32_t>(offer.price);
        msg.addString(offer.playerName);
    }

    writeToOutputBuffer(msg);
}

void ProtocolGame::sendMarketAcceptOffer(const MarketOfferEx& offer)
{
    NetworkMessage msg;
    msg.addByte(0xF9);
    msg.addItemId(offer.itemId);

    if (offer.type == MARKETACTION_BUY) {
        msg.add<uint32_t>(0x01);
        msg.add<uint32_t>(offer.timestamp);
        msg.add<uint16_t>(offer.counter);
        msg.add<uint16_t>(offer.amount);
        msg.add<uint32_t>(offer.price);
        msg.addString(offer.playerName);
        msg.add<uint32_t>(0x00);
    }
    else {
        msg.add<uint32_t>(0x00);
        msg.add<uint32_t>(0x01);
        msg.add<uint32_t>(offer.timestamp);
        msg.add<uint16_t>(offer.counter);
        msg.add<uint16_t>(offer.amount);
        msg.add<uint32_t>(offer.price);
        msg.addString(offer.playerName);
    }

    writeToOutputBuffer(msg);
}

void ProtocolGame::sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers)
{
    NetworkMessage msg;
    msg.addByte(0xF9);
    msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);

    msg.add<uint32_t>(buyOffers.size());
    for (const MarketOffer& offer : buyOffers) {
        msg.add<uint32_t>(offer.timestamp);
        msg.add<uint16_t>(offer.counter);
        msg.addItemId(offer.itemId);
        msg.add<uint16_t>(offer.amount);
        msg.add<uint32_t>(offer.price);
    }

    msg.add<uint32_t>(sellOffers.size());
    for (const MarketOffer& offer : sellOffers) {
        msg.add<uint32_t>(offer.timestamp);
        msg.add<uint16_t>(offer.counter);
        msg.addItemId(offer.itemId);
        msg.add<uint16_t>(offer.amount);
        msg.add<uint32_t>(offer.price);
    }

    writeToOutputBuffer(msg);
}

void ProtocolGame::sendMarketCancelOffer(const MarketOfferEx& offer)
{
    NetworkMessage msg;
    msg.addByte(0xF9);
    msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);

    if (offer.type == MARKETACTION_BUY) {
        msg.add<uint32_t>(0x01);
        msg.add<uint32_t>(offer.timestamp);
        msg.add<uint16_t>(offer.counter);
        msg.addItemId(offer.itemId);
        msg.add<uint16_t>(offer.amount);
        msg.add<uint32_t>(offer.price);
        msg.add<uint32_t>(0x00);
    }
    else {
        msg.add<uint32_t>(0x00);
        msg.add<uint32_t>(0x01);
        msg.add<uint32_t>(offer.timestamp);
        msg.add<uint16_t>(offer.counter);
        msg.addItemId(offer.itemId);
        msg.add<uint16_t>(offer.amount);
        msg.add<uint32_t>(offer.price);
    }

    writeToOutputBuffer(msg);
}

void ProtocolGame::sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers)
{
    uint32_t i = 0;
    std::map<uint32_t, uint16_t> counterMap;
    uint32_t buyOffersToSend = std::min<uint32_t>(buyOffers.size(), 810 + std::max<int32_t>(0, 810 - sellOffers.size()));
    uint32_t sellOffersToSend = std::min<uint32_t>(sellOffers.size(), 810 + std::max<int32_t>(0, 810 - buyOffers.size()));

    NetworkMessage msg;
    msg.addByte(0xF9);
    msg.add<uint16_t>(MARKETREQUEST_OWN_HISTORY);

    msg.add<uint32_t>(buyOffersToSend);
    for (HistoryMarketOfferList::const_iterator it = buyOffers.begin(); i < buyOffersToSend; ++it, ++i) {
        msg.add<uint32_t>(it->timestamp);
        msg.add<uint16_t>(counterMap[it->timestamp]++);
        msg.addItemId(it->itemId);
        msg.add<uint16_t>(it->amount);
        msg.add<uint32_t>(it->price);
        msg.addByte(it->state);
    }

    counterMap.clear();
    i = 0;

    msg.add<uint32_t>(sellOffersToSend);
    for (HistoryMarketOfferList::const_iterator it = sellOffers.begin(); i < sellOffersToSend; ++it, ++i) {
        msg.add<uint32_t>(it->timestamp);
        msg.add<uint16_t>(counterMap[it->timestamp]++);
        msg.addItemId(it->itemId);
        msg.add<uint16_t>(it->amount);
        msg.add<uint32_t>(it->price);
        msg.addByte(it->state);
    }

    writeToOutputBuffer(msg);
}

void ProtocolGame::sendMarketDetail(uint16_t itemId)
{
    NetworkMessage msg;
    msg.addByte(0xF8);
    msg.addItemId(itemId);

    const ItemType& it = Item::items[itemId];
    if (it.armor != 0) {
        msg.addString(std::to_string(it.armor));
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    if (it.attack != 0) {
        // TODO: chance to hit, range
        // example:
        // "attack +x, chance to hit +y%, z fields"
        if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) {
            std::ostringstream ss;
            ss << it.attack << " physical +" << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType);
            msg.addString(ss.str());
        }
        else {
            msg.addString(std::to_string(it.attack));
        }
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    if (it.isContainer()) {
        msg.addString(std::to_string(it.maxItems));
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    if (it.defense != 0) {
        if (it.extraDefense != 0) {
            std::ostringstream ss;
            ss << it.defense << ' ' << std::showpos << it.extraDefense << std::noshowpos;
            msg.addString(ss.str());
        }
        else {
            msg.addString(std::to_string(it.defense));
        }
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    if (!it.description.empty()) {
        const std::string& descr = it.description;
        if (descr.back() == '.') {
            msg.addString(std::string(descr, 0, descr.length() - 1));
        }
        else {
            msg.addString(descr);
        }
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    if (it.decayTime != 0) {
        std::ostringstream ss;
        ss << it.decayTime << " seconds";
        msg.addString(ss.str());
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    if (it.abilities) {
        std::ostringstream ss;
        bool separator = false;

        for (size_t i = 0; i < COMBAT_COUNT; ++i) {
            if (it.abilities->absorbPercent == 0) {
                continue;
            }

            if (separator) {
                ss << ", ";
            }
            else {
                separator = true;
            }

            ss << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent << std::noshowpos << '%';
        }

        msg.addString(ss.str());
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    if (it.minReqLevel != 0) {
        msg.addString(std::to_string(it.minReqLevel));
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    if (it.minReqMagicLevel != 0) {
        msg.addString(std::to_string(it.minReqMagicLevel));
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    msg.addString(it.vocationString);

    msg.addString(it.runeSpellName);

    if (it.abilities) {
        std::ostringstream ss;
        bool separator = false;

        for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) {
            if (!it.abilities->skills) {
                continue;
            }

            if (separator) {
                ss << ", ";
            }
            else {
                separator = true;
            }

            ss << getSkillName(i) << ' ' << std::showpos << it.abilities->skills << std::noshowpos;
        }

        if (it.abilities->stats[sTAT_MAGICPOINTS] != 0) {
            if (separator) {
                ss << ", ";
            }
            else {
                separator = true;
            }

            ss << "magic level " << std::showpos << it.abilities->stats[sTAT_MAGICPOINTS] << std::noshowpos;
        }

        if (it.abilities->speed != 0) {
            if (separator) {
                ss << ", ";
            }

            ss << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos;
        }

        msg.addString(ss.str());
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    if (it.charges != 0) {
        msg.addString(std::to_string(it.charges));
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    std::string weaponName = getWeaponName(it.weaponType);

    if (it.slotPosition & SLOTP_TWO_HAND) {
        if (!weaponName.empty()) {
            weaponName += ", two-handed";
        }
        else {
            weaponName = "two-handed";
        }
    }

    msg.addString(weaponName);

    if (it.weight != 0) {
        std::ostringstream ss;
        if (it.weight < 10) {
            ss << "0.0" << it.weight;
        }
        else if (it.weight < 100) {
            ss << "0." << it.weight;
        }
        else {
            std::string weightString = std::to_string(it.weight);
            weightString.insert(weightString.end() - 2, '.');
            ss << weightString;
        }
        ss << " oz";
        msg.addString(ss.str());
    }
    else {
        msg.add<uint16_t>(0x00);
    }

    MarketStatistics* statistics = IOMarket::getInstance()->getPurchaseStatistics(itemId);
    if (statistics) {
        msg.addByte(0x01);
        msg.add<uint32_t>(statistics->numTransactions);
        msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));
        msg.add<uint32_t>(statistics->highestPrice);
        msg.add<uint32_t>(statistics->lowestPrice);
    }
    else {
        msg.addByte(0x00);
    }

    statistics = IOMarket::getInstance()->getSaleStatistics(itemId);
    if (statistics) {
        msg.addByte(0x01);
        msg.add<uint32_t>(statistics->numTransactions);
        msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));
        msg.add<uint32_t>(statistics->highestPrice);
        msg.add<uint32_t>(statistics->lowestPrice);
    }
    else {
        msg.addByte(0x00);
    }

    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());
    msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01);
    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);
    msg.add<uint32_t>(0x00);
    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>(creature->getBaseSpeed() / 2);
    msg.add<uint16_t>(speed / 2);
    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::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::sendAddTileItem(const Position& pos, uint32_t stackpos, const Item* item)
{
    if (!canSee(pos)) {
        return;
    }

    NetworkMessage msg;
    msg.addByte(0x6A);
    msg.addPosition(pos);
    msg.addByte(stackpos);
    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::sendFightModes()
{
    NetworkMessage msg;
    msg.addByte(0xA7);
    msg.addByte(player->fightMode);
    msg.addByte(player->chaseMode);
    msg.addByte(player->secureMode);
    msg.addByte(PVP_MODE_DOVE);
    writeToOutputBuffer(msg);
    msg.addString("http://static.tibia.com/images/store");
    msg.addByte(0x19);
    msg.addByte(0x00);
}

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::sendAddContainerItem(uint8_t cid, uint16_t slot, const Item* item)
{
    NetworkMessage msg;
    msg.addByte(0x70);
    msg.addByte(cid);
    msg.add<uint16_t>(slot);
    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.add<uint16_t>(slot);
    msg.addItem(item);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot, const Item* lastItem)
{
    NetworkMessage msg;
    msg.addByte(0x72);
    msg.addByte(cid);
    msg.add<uint16_t>(slot);
    if (lastItem) {
        msg.addItem(lastItem);
    }
    else {
        msg.add<uint16_t>(0x00);
    }
    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();
    Mount* currentMount = g_game.mounts.getMountByID(player->getCurrentMount());
    if (currentMount) {
        currentOutfit.lookMount = currentMount->clientId;
    }

    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() == 100) { // 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);
    }

    std::vector<const Mount*> mounts;
    for (const Mount& mount : g_game.mounts.getMounts()) {
        if (player->hasMount(&mount)) {
            mounts.push_back(&mount);
        }
    }

    msg.addByte(mounts.size());
    for (const Mount* mount : mounts) {
        msg.add<uint16_t>(mount->clientId);
        msg.addString(mount->name);
    }

    writeToOutputBuffer(msg);
}

void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus)
{
    NetworkMessage msg;
    msg.addByte(0xD3);
    msg.add<uint32_t>(guid);
    msg.addByte(newStatus);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendSpellCooldown(uint8_t spellId, uint32_t time)
{
    NetworkMessage msg;
    msg.addByte(0xA4);
    msg.addByte(spellId);
    msg.add<uint32_t>(time);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time)
{
    NetworkMessage msg;
    msg.addByte(0xA5);
    msg.addByte(groupId);
    msg.add<uint32_t>(time);
    writeToOutputBuffer(msg);
}

void ProtocolGame::sendModalWindow(const ModalWindow& modalWindow)
{
    NetworkMessage msg;
    msg.addByte(0xFA);

    msg.add<uint32_t>(modalWindow.id);
    msg.addString(modalWindow.title);
    msg.addString(modalWindow.message);

    msg.addByte(modalWindow.buttons.size());
    for (const auto& it : modalWindow.buttons) {
        msg.addString(it.first);
        msg.addByte(it.second);
    }

    msg.addByte(modalWindow.choices.size());
    for (const auto& it : modalWindow.choices) {
        msg.addString(it.first);
        msg.addByte(it.second);
    }

    msg.addByte(modalWindow.defaultEscapeButton);
    msg.addByte(modalWindow.defaultEnterButton);
    msg.addByte(modalWindow.priority ? 0x01 : 0x00);

    writeToOutputBuffer(msg);
}

////////////// Add common messages

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::AddShopItem(NetworkMessage& msg, const ShopInfo& item)
{
    const ItemType& it = Item::items[item.itemId];
    msg.add<uint16_t>(it.clientId);

    if (it.isSplash() || it.isFluidContainer()) {
        msg.addByte(serverFluidToClient(item.subType));
    }
    else {
        msg.addByte(0x00);
    }

    msg.addString(item.realName);
    msg.add<uint32_t>(it.weight);
    msg.add<uint32_t>(item.buyPrice);
    msg.add<uint32_t>(item.sellPrice);
}

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);
}
 

 

Editado por eduardo0001
Link para o comentário
Compartilhar em outros sites

  • Diretor
5 horas atrás, eduardo0001 disse:

O meu protocolgame.cpp é diferente alguém tem alguma solução??

  Mostrar conteúdo oculto

/**

* The Forgotten Server - a free and open-source MMORPG server emulator

* Copyright © 2015  Mark Samman <mark.samman@gmail.com>

*

* 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 "iomarket.h"

#include "waitlist.h"

#include "ban.h"

#include "scheduler.h"

#include "databasetasks.h"

extern Game g_game;

extern ConfigManager g_config;

extern Actions actions;

extern CreatureEvents* g_creatureEvents;

extern Chat* g_chat;

ProtocolGame::LiveCastsMap ProtocolGame::liveCasts;

void ProtocolGame::release()

{

    //dispatcher thread

    stopLiveCast();

    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);

        m_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->clearModalWindows();

    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);

    m_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);

        }

    }

    stopLiveCast();

    disconnect();

    g_game.removeCreature(player);

}

bool ProtocolGame::startLiveCast(const std::string& password /*= ""*/)

{

    auto connection = getConnection();

    if (!g_config.getBoolean(ConfigManager::ENABLE_LIVE_CASTING) || isLiveCaster() || !player || player->isRemoved() || !connection || liveCasts.size() >= getMaxLiveCastCount()) {

        return false;

    }

    {

        std::lock_guard<decltype(liveCastLock)> lock{ liveCastLock };

        //DO NOT do any send operations here

        liveCastName = player->getName();

        liveCastPassword = password;

        isCaster.store(true, std::memory_order_relaxed);

    }

    liveCasts.insert(std::make_pair(player, getThis()));

    registerLiveCast();

    //Send a "dummy" channel

    sendChannel(CHANNEL_CAST, LIVE_CAST_CHAT_NAME, nullptr, nullptr);

    return true;

}

bool ProtocolGame::stopLiveCast()

{

    //dispatcher

    if (!isLiveCaster()) {

        return false;

    }

    CastSpectatorVec spectators;

    {

        std::lock_guard<decltype(liveCastLock)> lock{ liveCastLock };

        //DO NOT do any send operations here

        std::swap(this->spectators, spectators);

        isCaster.store(false, std::memory_order_relaxed);

    }

    liveCasts.erase(player);

    for (auto& spectator : spectators) {

        spectator->onLiveCastStop();

    }

    unregisterLiveCast();

    return true;

}

void ProtocolGame::clearLiveCastInfo()

{

    static std::once_flag flag;

    std::call_once(flag, []() {

        assert(g_game.getGameState() == GAME_STATE_INIT);

        std::ostringstream query;

        query << "TRUNCATE TABLE `live_casts`;";

        g_databaseTasks.addTask(query.str());

    });

}

void ProtocolGame::registerLiveCast()

{

    std::ostringstream query;

    query << "INSERT into `live_casts` (`player_id`, `cast_name`, `password`) VALUES (" << player->getGUID() << ", '"

        << getLiveCastName() << "', " << isPasswordProtected() << ");";

    g_databaseTasks.addTask(query.str());

}

void ProtocolGame::unregisterLiveCast()

{

    std::ostringstream query;

    query << "DELETE FROM `live_casts` WHERE `player_id`=" << player->getGUID() << ";";

    g_databaseTasks.addTask(query.str());

}

void ProtocolGame::updateLiveCastInfo()

{

    std::ostringstream query;

    query << "UPDATE `live_casts` SET `cast_name`='" << getLiveCastName() << "', `password`="

        << isPasswordProtected() << ", `spectators`=" << getSpectatorCount()

        << " WHERE `player_id`=" << player->getGUID() << ";";

    g_databaseTasks.addTask(query.str());

}

void ProtocolGame::addSpectator(ProtocolSpectator_ptr spectatorClient)

{

    std::lock_guard<decltype(liveCastLock)> lock(liveCastLock);

    //DO NOT do any send operations here

    spectators.emplace_back(spectatorClient);

    updateLiveCastInfo();

}

void ProtocolGame::removeSpectator(ProtocolSpectator_ptr spectatorClient)

{

    std::lock_guard<decltype(liveCastLock)> lock(liveCastLock);

    //DO NOT do any send operations here

    auto it = std::find(spectators.begin(), spectators.end(), spectatorClient);

    if (it != spectators.end()) {

        spectators.erase(it);

        updateLiveCastInfo();

    }

}

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>();

    msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision

    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

    std::string sessionKey = msg.getString();

    size_t pos = sessionKey.find('\n');

    if (pos == std::string::npos) {

        disconnectClient("You must enter your account name.");

        return;

    }

    std::string accountName = sessionKey.substr(0, pos);

    if (accountName.empty()) {

        disconnectClient("You must enter your account name.");

        return;

    }

    std::string password = sessionKey.substr(pos + 1);

    std::string characterName = msg.getString();

    uint32_t timeStamp = msg.get<uint32_t>();

    uint8_t randNumber = msg.getByte();

    if (m_challengeTimestamp != timeStamp || m_challengeRandom != randNumber) {

        disconnect();

        return;

    }

    if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) {

        disconnectClient("Only clients with protocol " CLIENT_VERSION_STR " allowed!");

        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::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, bool broadcast /*= true*/)

{

    if (!broadcast && isLiveCaster()) {

        //We're casting and we need to send a packet that's not supposed to be broadcast so we need a new messasge.

        //This shouldn't impact performance by a huge amount as most packets can be broadcast.

        auto out = OutputMessagePool::getOutputMessage();

        out->append(msg);

        send(std::move(out));

    }

    else {

        auto out = getOutputBuffer(msg.getLength());

        if (isLiveCaster()) {

            out->setBroadcastMsg(true);

        }

        out->append(msg);

    }

}

void ProtocolGame::parsePacket(NetworkMessage& msg)

{

    if (!m_acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN || msg.getLength() <= 0) {

        return;

    }

    uint8_t recvbyte = msg.getByte();

    //a dead player can not perform actions

    if (!player || player->isRemoved() || player->getHealth() <= 0) {

        auto this_ptr = getThis();

        g_dispatcher.addTask(createTask([this_ptr]() {

            this_ptr->stopLiveCast();

        }));

        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 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); 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 0x79: parseLookInShop(msg); break;

    case 0x7A: parsePlayerPurchase(msg); break;

    case 0x7B: parsePlayerSale(msg); break;

    case 0x7C: addGameTask(&Game::playerCloseShop, player->getID()); 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 0x9E: addGameTask(&Game::playerCloseNpcChannel, player->getID()); 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 0xA8: parseEnableSharedPartyExperience(msg); 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 0xCB: parseBrowseField(msg); break;

    case 0xCC: parseSeekInContainer(msg); break;

    case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break;

    case 0xD3: parseSetOutfit(msg); break;

    case 0xD4: parseToggleMount(msg); break;

    case 0xDC: parseAddVip(msg); break;

    case 0xDD: parseRemoveVip(msg); break;

    case 0xDE: parseEditVip(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;

    case 0xF4: parseMarketLeave(); break;

    case 0xF5: parseMarketBrowse(msg); break;

    case 0xF6: parseMarketCreateOffer(msg); break;

    case 0xF7: parseMarketCancelOffer(msg); break;

    case 0xF8: parseMarketAcceptOffer(msg); break;

    case 0xF9: parseModalWindowAnswer(msg); 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();

    }

}

// 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() + 8)) {

        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();

    newOutfit.lookMount = msg.get<uint16_t>();

    addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit);

}

void ProtocolGame::parseToggleMount(NetworkMessage& msg)

{

    bool mount = msg.getByte() != 0;

    addGameTask(&Game::playerToggleMount, player->getID(), mount);

}

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_TO:

    case TALKTYPE_PRIVATE_RED_TO:

        receiver = msg.getString();

        channelId = 0;

        break;

    case TALKTYPE_CHANNEL_Y:

    case TALKTYPE_CHANNEL_R1:

        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

                                           // uint8_t rawPvpMode = msg.getByte(); // pvp mode introduced in 10.0

    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::parseLookInShop(NetworkMessage& msg)

{

    uint16_t id = msg.get<uint16_t>();

    uint8_t count = msg.getByte();

    addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInShop, player->getID(), id, count);

}

void ProtocolGame::parsePlayerPurchase(NetworkMessage& msg)

{

    uint16_t id = msg.get<uint16_t>();

    uint8_t count = msg.getByte();

    uint8_t amount = msg.getByte();

    bool ignoreCap = msg.getByte() != 0;

    bool inBackpacks = msg.getByte() != 0;

    addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerPurchaseItem, player->getID(), id, count, amount, ignoreCap, inBackpacks);

}

void ProtocolGame::parsePlayerSale(NetworkMessage& msg)

{

    uint16_t id = msg.get<uint16_t>();

    uint8_t count = msg.getByte();

    uint8_t amount = msg.getByte();

    bool ignoreEquipped = msg.getByte() != 0;

    addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerSellItem, player->getID(), id, count, amount, ignoreEquipped);

}

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::parseEditVip(NetworkMessage& msg)

{

    uint32_t guid = msg.get<uint32_t>();

    const std::string description = msg.getString();

    uint32_t icon = std::min<uint32_t>(10, msg.get<uint32_t>()); // 10 is max icon in 9.63

    bool notify = msg.getByte() != 0;

    addGameTask(&Game::playerRequestEditVip, player->getID(), guid, description, icon, notify);

}

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)

{

    uint8_t category = msg.getByte();

    std::string message = msg.getString();

    Position position;

    if (category == BUG_CATEGORY_MAP) {

        position = msg.getPosition();

    }

    addGameTask(&Game::playerReportBug, player->getID(), message, position, category);

}

void ProtocolGame::parseDebugAssert(NetworkMessage& msg)

{

    if (m_debugAssertSent) {

        return;

    }

    m_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::parseEnableSharedPartyExperience(NetworkMessage& msg)

{

    bool sharedExpActive = msg.getByte() == 1;

    addGameTask(&Game::playerEnableSharedPartyExperience, player->getID(), sharedExpActive);

}

void ProtocolGame::parseQuestLine(NetworkMessage& msg)

{

    uint16_t questId = msg.get<uint16_t>();

    addGameTask(&Game::playerShowQuestLine, player->getID(), questId);

}

void ProtocolGame::parseMarketLeave()

{

    addGameTask(&Game::playerLeaveMarket, player->getID());

}

void ProtocolGame::parseMarketBrowse(NetworkMessage& msg)

{

    uint16_t browseId = msg.get<uint16_t>();

    if (browseId == MARKETREQUEST_OWN_OFFERS) {

        addGameTask(&Game::playerBrowseMarketOwnOffers, player->getID());

    }

    else if (browseId == MARKETREQUEST_OWN_HISTORY) {

        addGameTask(&Game::playerBrowseMarketOwnHistory, player->getID());

    }

    else {

        addGameTask(&Game::playerBrowseMarket, player->getID(), browseId);

    }

}

void ProtocolGame::parseMarketCreateOffer(NetworkMessage& msg)

{

    uint8_t type = msg.getByte();

    uint16_t spriteId = msg.get<uint16_t>();

    uint16_t amount = msg.get<uint16_t>();

    uint32_t price = msg.get<uint32_t>();

    bool anonymous = (msg.getByte() != 0);

    addGameTask(&Game::playerCreateMarketOffer, player->getID(), type, spriteId, amount, price, anonymous);

}

void ProtocolGame::parseMarketCancelOffer(NetworkMessage& msg)

{

    uint32_t timestamp = msg.get<uint32_t>();

    uint16_t counter = msg.get<uint16_t>();

    addGameTask(&Game::playerCancelMarketOffer, player->getID(), timestamp, counter);

}

void ProtocolGame::parseMarketAcceptOffer(NetworkMessage& msg)

{

    uint32_t timestamp = msg.get<uint32_t>();

    uint16_t counter = msg.get<uint16_t>();

    uint16_t amount = msg.get<uint16_t>();

    addGameTask(&Game::playerAcceptMarketOffer, player->getID(), timestamp, counter, amount);

}

void ProtocolGame::parseModalWindowAnswer(NetworkMessage& msg)

{

    uint32_t id = msg.get<uint32_t>();

    uint8_t button = msg.getByte();

    uint8_t choice = msg.getByte();

    addGameTask(&Game::playerAnswerModalWindow, player->getID(), id, button, choice);

}

void ProtocolGame::parseBrowseField(NetworkMessage& msg)

{

    const Position& pos = msg.getPosition();

    addGameTask(&Game::playerBrowseField, player->getID(), pos);

}

void ProtocolGame::parseSeekInContainer(NetworkMessage& msg)

{

    uint8_t containerId = msg.getByte();

    uint16_t index = msg.get<uint16_t>();

    addGameTask(&Game::playerSeekInContainer, player->getID(), containerId, index);

}

// Send methods

void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver)

{

    NetworkMessage msg;

    msg.addByte(0xAD);

    msg.addString(receiver);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent)

{

    NetworkMessage msg;

    msg.addByte(0xF3);

    msg.add<uint16_t>(channelId);

    msg.addString(playerName);

    msg.addByte(channelEvent);

    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::sendCreatureWalkthrough(const Creature* creature, bool walkthrough)

{

    if (!canSee(creature)) {

        return;

    }

    NetworkMessage msg;

    msg.addByte(0x92);

    msg.add<uint32_t>(creature->getID());

    msg.addByte(walkthrough ? 0x00 : 0x01);

    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::sendCreatureType(uint32_t creatureId, uint8_t creatureType)

{

    NetworkMessage msg;

    msg.addByte(0x95);

    msg.add<uint32_t>(creatureId);

    msg.addByte(creatureType);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendCreatureHelpers(uint32_t creatureId, uint16_t helpers)

{

    NetworkMessage msg;

    msg.addByte(0x94);

    msg.add<uint32_t>(creatureId);

    msg.add<uint16_t>(helpers);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t color)

{

    if (!canSee(creature)) {

        return;

    }

    NetworkMessage msg;

    msg.addByte(0x93);

    msg.add<uint32_t>(creature->getID());

    msg.addByte(0x01);

    msg.addByte(color);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendTutorial(uint8_t tutorialId)

{

    NetworkMessage msg;

    msg.addByte(0xDC);

    msg.addByte(tutorialId);

    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(uint8_t unfairFightReduction)

{

    NetworkMessage msg;

    msg.addByte(0x28);

    msg.addByte(0x00);

    msg.addByte(unfairFightReduction);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendTextMessage(const TextMessage& message)

{

    NetworkMessage msg;

    msg.addByte(0xB4);

    msg.addByte(message.type);

    switch (message.type) {

    case MESSAGE_DAMAGE_DEALT:

    case MESSAGE_DAMAGE_RECEIVED:

    case MESSAGE_DAMAGE_OTHERS: {

        msg.addPosition(message.position);

        msg.add<uint32_t>(message.primary.value);

        msg.addByte(message.primary.color);

        msg.add<uint32_t>(message.secondary.value);

        msg.addByte(message.secondary.color);

        break;

    }

    case MESSAGE_HEALED:

    case MESSAGE_HEALED_OTHERS:

    case MESSAGE_EXPERIENCE:

    case MESSAGE_EXPERIENCE_OTHERS: {

        msg.addPosition(message.position);

        msg.add<uint32_t>(message.primary.value);

        msg.addByte(message.primary.color);

        break;

    }

    default: {

        break;

    }

    }

    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);

    msg.add<uint16_t>(0x01);

    msg.addString(player->getName());

    msg.add<uint16_t>(0x00);

    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::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::sendShop(Npc* npc, const ShopInfoList& itemList)

{

    NetworkMessage msg;

    msg.addByte(0x7A);

    msg.addString(npc->getName());

    uint16_t itemsToSend = std::min<size_t>(itemList.size(), std::numeric_limits<uint16_t>::max());

    msg.add<uint16_t>(itemsToSend);

    uint16_t i = 0;

    for (ShopInfoList::const_iterator it = itemList.begin(); i < itemsToSend; ++it, ++i) {

        AddShopItem(msg, *it);

    }

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendCloseShop()

{

    NetworkMessage msg;

    msg.addByte(0x7C);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendSaleItemList(const std::list<ShopInfo>& shop)

{

    NetworkMessage msg;

    msg.addByte(0x7B);

    msg.add<uint64_t>(player->getMoney());

    std::map<uint16_t, uint32_t> saleMap;

    if (shop.size() <= 5) {

        // For very small shops it's not worth it to create the complete map

        for (const ShopInfo& shopInfo : shop) {

            if (shopInfo.sellPrice == 0) {

                continue;

            }

            int8_t subtype = -1;

            const ItemType& itemType = Item::items[shopInfo.itemId];

            if (itemType.hasSubType() && !itemType.stackable) {

                subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);

            }

            uint32_t count = player->getItemTypeCount(shopInfo.itemId, subtype);

            if (count > 0) {

                saleMap[shopInfo.itemId] = count;

            }

        }

    }

    else {

        // Large shop, it's better to get a cached map of all item counts and use it

        // We need a temporary map since the finished map should only contain items

        // available in the shop

        std::map<uint32_t, uint32_t> tempSaleMap;

        player->getAllItemTypeCount(tempSaleMap);

        // We must still check manually for the special items that require subtype matches

        // (That is, fluids such as potions etc., actually these items are very few since

        // health potions now use their own ID)

        for (const ShopInfo& shopInfo : shop) {

            if (shopInfo.sellPrice == 0) {

                continue;

            }

            int8_t subtype = -1;

            const ItemType& itemType = Item::items[shopInfo.itemId];

            if (itemType.hasSubType() && !itemType.stackable) {

                subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);

            }

            if (subtype != -1) {

                uint32_t count;

                if (!itemType.isFluidContainer() && !itemType.isSplash()) {

                    count = player->getItemTypeCount(shopInfo.itemId, subtype); // This shop item requires extra checks

                }

                else {

                    count = subtype;

                }

                if (count > 0) {

                    saleMap[shopInfo.itemId] = count;

                }

            }

            else {

                std::map<uint32_t, uint32_t>::const_iterator findIt = tempSaleMap.find(shopInfo.itemId);

                if (findIt != tempSaleMap.end() && findIt->second > 0) {

                    saleMap[shopInfo.itemId] = findIt->second;

                }

            }

        }

    }

    uint8_t itemsToSend = std::min<size_t>(saleMap.size(), std::numeric_limits<uint8_t>::max());

    msg.addByte(itemsToSend);

    uint8_t i = 0;

    for (std::map<uint16_t, uint32_t>::const_iterator it = saleMap.begin(); i < itemsToSend; ++it, ++i) {

        msg.addItemId(it->first);

        msg.addByte(std::min<uint32_t>(it->second, std::numeric_limits<uint8_t>::max()));

    }

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendMarketEnter(uint32_t depotId)

{

    NetworkMessage msg;

    msg.addByte(0xF6);

    msg.add<uint64_t>(player->getBankBalance());

    msg.addByte(std::min<uint32_t>(IOMarket::getPlayerOfferCount(player->getGUID()), std::numeric_limits<uint8_t>::max()));

    DepotChest* depotChest = player->getDepotChest(depotId, false);

    if (!depotChest) {

        msg.add<uint16_t>(0x00);

        writeToOutputBuffer(msg);

        return;

    }

    player->setInMarket(true);

    std::map<uint16_t, uint32_t> depotItems;

    std::forward_list<Container*> containerList{ depotChest, player->getInbox() };

    do {

        Container* container = containerList.front();

        containerList.pop_front();

        for (Item* item : container->getItemList()) {

            Container* c = item->getContainer();

            if (c && !c->empty()) {

                containerList.push_front©;

                continue;

            }

            const ItemType& itemType = Item::items[item->getID()];

            if (itemType.wareId == 0) {

                continue;

            }

            if (c && (!itemType.isContainer() || c->capacity() != itemType.maxItems)) {

                continue;

            }

            if (!item->hasMarketAttributes()) {

                continue;

            }

            depotItems[itemType.wareId] += Item::countByType(item, -1);

        }

    } while (!containerList.empty());

    uint16_t itemsToSend = std::min<size_t>(depotItems.size(), std::numeric_limits<uint16_t>::max());

    msg.add<uint16_t>(itemsToSend);

    uint16_t i = 0;

    for (std::map<uint16_t, uint32_t>::const_iterator it = depotItems.begin(); i < itemsToSend; ++it, ++i) {

        msg.add<uint16_t>(it->first);

        msg.add<uint16_t>(std::min<uint32_t>(0xFFFF, it->second));

    }

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendMarketLeave()

{

    NetworkMessage msg;

    msg.addByte(0xF7);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers)

{

    NetworkMessage msg;

    msg.addByte(0xF9);

    msg.addItemId(itemId);

    msg.add<uint32_t>(buyOffers.size());

    for (const MarketOffer& offer : buyOffers) {

        msg.add<uint32_t>(offer.timestamp);

        msg.add<uint16_t>(offer.counter);

        msg.add<uint16_t>(offer.amount);

        msg.add<uint32_t>(offer.price);

        msg.addString(offer.playerName);

    }

    msg.add<uint32_t>(sellOffers.size());

    for (const MarketOffer& offer : sellOffers) {

        msg.add<uint32_t>(offer.timestamp);

        msg.add<uint16_t>(offer.counter);

        msg.add<uint16_t>(offer.amount);

        msg.add<uint32_t>(offer.price);

        msg.addString(offer.playerName);

    }

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendMarketAcceptOffer(const MarketOfferEx& offer)

{

    NetworkMessage msg;

    msg.addByte(0xF9);

    msg.addItemId(offer.itemId);

    if (offer.type == MARKETACTION_BUY) {

        msg.add<uint32_t>(0x01);

        msg.add<uint32_t>(offer.timestamp);

        msg.add<uint16_t>(offer.counter);

        msg.add<uint16_t>(offer.amount);

        msg.add<uint32_t>(offer.price);

        msg.addString(offer.playerName);

        msg.add<uint32_t>(0x00);

    }

    else {

        msg.add<uint32_t>(0x00);

        msg.add<uint32_t>(0x01);

        msg.add<uint32_t>(offer.timestamp);

        msg.add<uint16_t>(offer.counter);

        msg.add<uint16_t>(offer.amount);

        msg.add<uint32_t>(offer.price);

        msg.addString(offer.playerName);

    }

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers)

{

    NetworkMessage msg;

    msg.addByte(0xF9);

    msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);

    msg.add<uint32_t>(buyOffers.size());

    for (const MarketOffer& offer : buyOffers) {

        msg.add<uint32_t>(offer.timestamp);

        msg.add<uint16_t>(offer.counter);

        msg.addItemId(offer.itemId);

        msg.add<uint16_t>(offer.amount);

        msg.add<uint32_t>(offer.price);

    }

    msg.add<uint32_t>(sellOffers.size());

    for (const MarketOffer& offer : sellOffers) {

        msg.add<uint32_t>(offer.timestamp);

        msg.add<uint16_t>(offer.counter);

        msg.addItemId(offer.itemId);

        msg.add<uint16_t>(offer.amount);

        msg.add<uint32_t>(offer.price);

    }

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendMarketCancelOffer(const MarketOfferEx& offer)

{

    NetworkMessage msg;

    msg.addByte(0xF9);

    msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);

    if (offer.type == MARKETACTION_BUY) {

        msg.add<uint32_t>(0x01);

        msg.add<uint32_t>(offer.timestamp);

        msg.add<uint16_t>(offer.counter);

        msg.addItemId(offer.itemId);

        msg.add<uint16_t>(offer.amount);

        msg.add<uint32_t>(offer.price);

        msg.add<uint32_t>(0x00);

    }

    else {

        msg.add<uint32_t>(0x00);

        msg.add<uint32_t>(0x01);

        msg.add<uint32_t>(offer.timestamp);

        msg.add<uint16_t>(offer.counter);

        msg.addItemId(offer.itemId);

        msg.add<uint16_t>(offer.amount);

        msg.add<uint32_t>(offer.price);

    }

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers)

{

    uint32_t i = 0;

    std::map<uint32_t, uint16_t> counterMap;

    uint32_t buyOffersToSend = std::min<uint32_t>(buyOffers.size(), 810 + std::max<int32_t>(0, 810 - sellOffers.size()));

    uint32_t sellOffersToSend = std::min<uint32_t>(sellOffers.size(), 810 + std::max<int32_t>(0, 810 - buyOffers.size()));

    NetworkMessage msg;

    msg.addByte(0xF9);

    msg.add<uint16_t>(MARKETREQUEST_OWN_HISTORY);

    msg.add<uint32_t>(buyOffersToSend);

    for (HistoryMarketOfferList::const_iterator it = buyOffers.begin(); i < buyOffersToSend; ++it, ++i) {

        msg.add<uint32_t>(it->timestamp);

        msg.add<uint16_t>(counterMap[it->timestamp]++);

        msg.addItemId(it->itemId);

        msg.add<uint16_t>(it->amount);

        msg.add<uint32_t>(it->price);

        msg.addByte(it->state);

    }

    counterMap.clear();

    i = 0;

    msg.add<uint32_t>(sellOffersToSend);

    for (HistoryMarketOfferList::const_iterator it = sellOffers.begin(); i < sellOffersToSend; ++it, ++i) {

        msg.add<uint32_t>(it->timestamp);

        msg.add<uint16_t>(counterMap[it->timestamp]++);

        msg.addItemId(it->itemId);

        msg.add<uint16_t>(it->amount);

        msg.add<uint32_t>(it->price);

        msg.addByte(it->state);

    }

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendMarketDetail(uint16_t itemId)

{

    NetworkMessage msg;

    msg.addByte(0xF8);

    msg.addItemId(itemId);

    const ItemType& it = Item::items[itemId];

    if (it.armor != 0) {

        msg.addString(std::to_string(it.armor));

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    if (it.attack != 0) {

        // TODO: chance to hit, range

        // example:

        // "attack +x, chance to hit +y%, z fields"

        if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) {

            std::ostringstream ss;

            ss << it.attack << " physical +" << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType);

            msg.addString(ss.str());

        }

        else {

            msg.addString(std::to_string(it.attack));

        }

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    if (it.isContainer()) {

        msg.addString(std::to_string(it.maxItems));

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    if (it.defense != 0) {

        if (it.extraDefense != 0) {

            std::ostringstream ss;

            ss << it.defense << ' ' << std::showpos << it.extraDefense << std::noshowpos;

            msg.addString(ss.str());

        }

        else {

            msg.addString(std::to_string(it.defense));

        }

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    if (!it.description.empty()) {

        const std::string& descr = it.description;

        if (descr.back() == '.') {

            msg.addString(std::string(descr, 0, descr.length() - 1));

        }

        else {

            msg.addString(descr);

        }

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    if (it.decayTime != 0) {

        std::ostringstream ss;

        ss << it.decayTime << " seconds";

        msg.addString(ss.str());

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    if (it.abilities) {

        std::ostringstream ss;

        bool separator = false;

        for (size_t i = 0; i < COMBAT_COUNT; ++i) {

            if (it.abilities->absorbPercent == 0) {

                continue;

            }

            if (separator) {

                ss << ", ";

            }

            else {

                separator = true;

            }

            ss << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent << std::noshowpos << '%';

        }

        msg.addString(ss.str());

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    if (it.minReqLevel != 0) {

        msg.addString(std::to_string(it.minReqLevel));

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    if (it.minReqMagicLevel != 0) {

        msg.addString(std::to_string(it.minReqMagicLevel));

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    msg.addString(it.vocationString);

    msg.addString(it.runeSpellName);

    if (it.abilities) {

        std::ostringstream ss;

        bool separator = false;

        for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) {

            if (!it.abilities->skills) {

                continue;

            }

            if (separator) {

                ss << ", ";

            }

            else {

                separator = true;

            }

            ss << getSkillName(i) << ' ' << std::showpos << it.abilities->skills << std::noshowpos;

        }

        if (it.abilities->stats[sTAT_MAGICPOINTS] != 0) {

            if (separator) {

                ss << ", ";

            }

            else {

                separator = true;

            }

            ss << "magic level " << std::showpos << it.abilities->stats[sTAT_MAGICPOINTS] << std::noshowpos;

        }

        if (it.abilities->speed != 0) {

            if (separator) {

                ss << ", ";

            }

            ss << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos;

        }

        msg.addString(ss.str());

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    if (it.charges != 0) {

        msg.addString(std::to_string(it.charges));

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    std::string weaponName = getWeaponName(it.weaponType);

    if (it.slotPosition & SLOTP_TWO_HAND) {

        if (!weaponName.empty()) {

            weaponName += ", two-handed";

        }

        else {

            weaponName = "two-handed";

        }

    }

    msg.addString(weaponName);

    if (it.weight != 0) {

        std::ostringstream ss;

        if (it.weight < 10) {

            ss << "0.0" << it.weight;

        }

        else if (it.weight < 100) {

            ss << "0." << it.weight;

        }

        else {

            std::string weightString = std::to_string(it.weight);

            weightString.insert(weightString.end() - 2, '.');

            ss << weightString;

        }

        ss << " oz";

        msg.addString(ss.str());

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    MarketStatistics* statistics = IOMarket::getInstance()->getPurchaseStatistics(itemId);

    if (statistics) {

        msg.addByte(0x01);

        msg.add<uint32_t>(statistics->numTransactions);

        msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));

        msg.add<uint32_t>(statistics->highestPrice);

        msg.add<uint32_t>(statistics->lowestPrice);

    }

    else {

        msg.addByte(0x00);

    }

    statistics = IOMarket::getInstance()->getSaleStatistics(itemId);

    if (statistics) {

        msg.addByte(0x01);

        msg.add<uint32_t>(statistics->numTransactions);

        msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));

        msg.add<uint32_t>(statistics->highestPrice);

        msg.add<uint32_t>(statistics->lowestPrice);

    }

    else {

        msg.addByte(0x00);

    }

    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());

    msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01);

    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);

    msg.add<uint32_t>(0x00);

    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>(creature->getBaseSpeed() / 2);

    msg.add<uint16_t>(speed / 2);

    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::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::sendAddTileItem(const Position& pos, uint32_t stackpos, const Item* item)

{

    if (!canSee(pos)) {

        return;

    }

    NetworkMessage msg;

    msg.addByte(0x6A);

    msg.addPosition(pos);

    msg.addByte(stackpos);

    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::sendFightModes()

{

    NetworkMessage msg;

    msg.addByte(0xA7);

    msg.addByte(player->fightMode);

    msg.addByte(player->chaseMode);

    msg.addByte(player->secureMode);

    msg.addByte(PVP_MODE_DOVE);

    writeToOutputBuffer(msg);

    msg.addString("http://static.tibia.com/images/store");

    msg.addByte(0x19);

    msg.addByte(0x00);

}

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::sendAddContainerItem(uint8_t cid, uint16_t slot, const Item* item)

{

    NetworkMessage msg;

    msg.addByte(0x70);

    msg.addByte(cid);

    msg.add<uint16_t>(slot);

    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.add<uint16_t>(slot);

    msg.addItem(item);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot, const Item* lastItem)

{

    NetworkMessage msg;

    msg.addByte(0x72);

    msg.addByte(cid);

    msg.add<uint16_t>(slot);

    if (lastItem) {

        msg.addItem(lastItem);

    }

    else {

        msg.add<uint16_t>(0x00);

    }

    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();

    Mount* currentMount = g_game.mounts.getMountByID(player->getCurrentMount());

    if (currentMount) {

        currentOutfit.lookMount = currentMount->clientId;

    }

    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() == 100) { // 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);

    }

    std::vector<const Mount*> mounts;

    for (const Mount& mount : g_game.mounts.getMounts()) {

        if (player->hasMount(&mount)) {

            mounts.push_back(&mount);

        }

    }

    msg.addByte(mounts.size());

    for (const Mount* mount : mounts) {

        msg.add<uint16_t>(mount->clientId);

        msg.addString(mount->name);

    }

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus)

{

    NetworkMessage msg;

    msg.addByte(0xD3);

    msg.add<uint32_t>(guid);

    msg.addByte(newStatus);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendSpellCooldown(uint8_t spellId, uint32_t time)

{

    NetworkMessage msg;

    msg.addByte(0xA4);

    msg.addByte(spellId);

    msg.add<uint32_t>(time);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time)

{

    NetworkMessage msg;

    msg.addByte(0xA5);

    msg.addByte(groupId);

    msg.add<uint32_t>(time);

    writeToOutputBuffer(msg);

}

void ProtocolGame::sendModalWindow(const ModalWindow& modalWindow)

{

    NetworkMessage msg;

    msg.addByte(0xFA);

    msg.add<uint32_t>(modalWindow.id);

    msg.addString(modalWindow.title);

    msg.addString(modalWindow.message);

    msg.addByte(modalWindow.buttons.size());

    for (const auto& it : modalWindow.buttons) {

        msg.addString(it.first);

        msg.addByte(it.second);

    }

    msg.addByte(modalWindow.choices.size());

    for (const auto& it : modalWindow.choices) {

        msg.addString(it.first);

        msg.addByte(it.second);

    }

    msg.addByte(modalWindow.defaultEscapeButton);

    msg.addByte(modalWindow.defaultEnterButton);

    msg.addByte(modalWindow.priority ? 0x01 : 0x00);

    writeToOutputBuffer(msg);

}

////////////// Add common messages

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::AddShopItem(NetworkMessage& msg, const ShopInfo& item)

{

    const ItemType& it = Item::items[item.itemId];

    msg.add<uint16_t>(it.clientId);

    if (it.isSplash() || it.isFluidContainer()) {

        msg.addByte(serverFluidToClient(item.subType));

    }

    else {

        msg.addByte(0x00);

    }

    msg.addString(item.realName);

    msg.add<uint32_t>(it.weight);

    msg.add<uint32_t>(item.buyPrice);

    msg.add<uint32_t>(item.sellPrice);

}

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);

}

 

 

 

Qual suas sources?

Link para o comentário
Compartilhar em outros sites

  • Diretor
27 minutos atrás, eduardo0001 disse:

É para servidor 10.90 TFS 1.2

 

Que estranho man, o julhinhuu e mais um amigo meu testou em tfs 1.2 e funcionou normal. Creio que pra TFS 10.90 não seja o mesmo procedimento...

Tentei ver aqui e realmente esse protocolgame.cpp (se for o protocolgame) não tem a função AddPlayerStats. Infelizmente não posso te ajudar porque não conheço as novas versões que estão saindo hoje em dia.

Link para o comentário
Compartilhar em outros sites

23 horas atrás, Yan Liima disse:

 

Que estranho man, o julhinhuu e mais um amigo meu testou em tfs 1.2 e funcionou normal. Creio que pra TFS 10.90 não seja o mesmo procedimento...

Tentei ver aqui e realmente esse protocolgame.cpp (se for o protocolgame) não tem a função AddPlayerStats. Infelizmente não posso te ajudar porque não conheço as novas versões que estão saindo hoje em dia.

Consegui , peguei o projeto do vanaheim e nele tinha. Obrigado Yan.

Me diga uma coisa mais se eu quiser colocar 1000 em hp funcionaria se eu trocar aqui? 

msg.add<uint16_t>(100);
msg.add<uint16_t>(player->getHealth() * 100 / player->getPlayerInfo(PLAYERINFO_MAXHEALTH));

mudando pra 1000 no lugar de 100? A fórmula funcionaria?

 

Sem esquecer, REP+

Editado por eduardo0001
Link para o comentário
Compartilhar em outros sites

  • Diretor
4 horas atrás, eduardo0001 disse:

Consegui , peguei o projeto do vanaheim e nele tinha. Obrigado Yan.

Me diga uma coisa mais se eu quiser colocar 1000 em hp funcionaria se eu trocar aqui? 

msg.add<uint16_t>(100);
msg.add<uint16_t>(player->getHealth() * 100 / player->getPlayerInfo(PLAYERINFO_MAXHEALTH));

mudando pra 1000 no lugar de 100? A fórmula funcionaria?

 

Sem esquecer, REP+

 

Sim funciona, só trocar os dois por 1000 xD

Link para o comentário
Compartilhar em outros sites

@Yan Liima Descobri uma coisa, se quiser alterar no seu tópico pra melhorar mais. Enfim, eu não estava achando esse  AddPlayerStats em   protocolgame.cpp  certo?

Mas a source que eu peguei e várias delas que tem pelos fóruns eu encontrei esse AddPlayerStats em outro lugar chamado protocolgamebase.cpp

a linha é 

void ProtocolGameBase::AddPlayerStats(NetworkMessage& msg)

Tem as mesmas características do seu post, mudando apenas o local. Lembrando que existe também o protocolgame.cpp mas é diferente.

Link para o comentário
Compartilhar em outros sites

  • Diretor
1 hora atrás, eduardo0001 disse:

@Yan Liima Descobri uma coisa, se quiser alterar no seu tópico pra melhorar mais. Enfim, eu não estava achando esse  AddPlayerStats em   protocolgame.cpp  certo?

Mas a source que eu peguei e várias delas que tem pelos fóruns eu encontrei esse AddPlayerStats em outro lugar chamado protocolgamebase.cpp

a linha é 


void ProtocolGameBase::AddPlayerStats(NetworkMessage& msg)

Tem as mesmas características do seu post, mudando apenas o local. Lembrando que existe também o protocolgame.cpp mas é diferente.

 

Pô bacana man dessa não sabia, obrigado por avisar estarei editando sim... No caso uma delas seria aquela que você disse que não tinha achado no protocolgame.cpp certo? creio que isso só é a partir da tfs 1.0. Algumas podem ser em protocolgame.cpp e outras podem ser protocolgamebase.cpp como você disse, dependendo da versão é claro

Link para o comentário
Compartilhar em outros sites

15 horas atrás, Yan Liima disse:

 

Pô bacana man dessa não sabia, obrigado por avisar estarei editando sim... No caso uma delas seria aquela que você disse que não tinha achado no protocolgame.cpp certo? creio que isso só é a partir da tfs 1.0. Algumas podem ser em protocolgame.cpp e outras podem ser protocolgamebase.cpp como você disse, dependendo da versão é claro

Isso, tem versão 1.2 que tem o protocolgame com AddPlayerStats , tipo vanaheim, mas tem versões que não tem essa função, sendo encontrada em protocolgamebase.cpp

Link para o comentário
Compartilhar em outros sites

  • 4 months later...
Em 07/06/2016 at 18:36, Yan Liima disse:

 

Sim funciona, só trocar os dois por 1000 xD

o meu ot é reset no lv 717217, ai colocando 100 depois de uns reset o hp fica 65535 como eu aumento a porcentagem, eu coloquei os dois números 1000 mais não adianto.....

Link para o comentário
Compartilhar em outros sites

×
×
  • Criar Novo...