Ir para conteúdo
  • Cadastre-se

GaspaR1

Membro
  • Total de itens

    116
  • Registro em

  • Última visita

  • Dias Ganhos

    2

Posts postados por GaspaR1

  1. Em 24/11/2020 em 15:15, najatheus disse:

    Bom vim traze minha antiga base ja que Não tava usando, vim postar ela

    serve-source: http://www.mediafire.com/file/3ev3r37rsnjykno/Poke_Pronto_CERTOV8.rar/file

    client pedir a Source kk
    client : http://www.mediafire.com/file/z3kacangae4p5m0/OTPokemon_.rar/file

     

    scan servidor:https://www.virustotal.com/gui/file/ca0b281ff4cc9f288a934c36a67aa1e60eae6d3c70265da3f388a1a32ad65003/detection

    scan cliente:https://www.virustotal.com/gui/file/d49bc215f790d37bf14ab2aec70cfd90b480248b73656012e4ebb15b14c5dfdf/detection


    Base Editada do Poke dw Editado por min430892.thumb.png.ede67b254d4552077707636c4114be15.png

    Não vou da suporte

                            Não recomendo usa (tfs 0.3.6)

    Huuum, obrigado tem a src, talvez seja bom, você chegou a abrir o game ? Nunca ouvi falar.

  2.                                                                                                  gtatibia.png.027af419c5b8e00a40acf326fd43a8f2.png

     

    Bom primeiramente, quero agradecer a todos que estão lendo essa publicação... e desde já informar que esse é um projeto sério, onde iremos seguir o GTA história por história, em um mundo aberto... para todos os fãs de um Dayz, Samp, então vocês irão gostar do que irei mostrar a vocês.

     

    ? Primeiro de tudo, qual a finalidade do projeto ?

    Fácil, hoje em dia é muito comum ver um Poketibia, um NTO, DBZ E ETC, mas cadê a inovação? pra mim já deu, poketibia existe vários outros grandes, NTO, DBZ, então hoje em dia não compensa mais, a não ser é claro que você faça algo inovador e diferente senão, não irá para frente.

    então eu já vi alguns OTS, de GTA... mas nenhum que se mantém seguindo a risca o próprio jogo, e então vamos mudar isso, transformando o Tibia em um GTA, uma versão leve para quem não tem um PC totalmente bom, porque sim existe pessoas que ainda não tem condição para um PC Gamer extra luxuoso, então a minha primeira meta é deixar o GTA TIBIA jogável para todos.

     

    ? Quais sistemas já temos? ( Alguns irão precisar de modificações claro, para uma melhor jogabilidade )

    --> Sistema de armas.

    --> Sistema de avião.

    --> Sistema de carros, roubos e etc.

    --> Sistema de Jetski.

    --> Sistema de gasolina.

    --> Sistema de compra do seu terreno, onde você mesmo pode construir sua casa in-game.

    --> Não irei citar todos, apenas alguns que acho importante.

     

    ? Faltando fazer?

    --> Estou criando o mapa do zero, irei criar o mapa todo de San Andreas. ( já comecei a criação, prints no final do post)

    --> Criar o sistema quests.

    --> Criar o sistema de estrelas.

    --> Criar o sistema de profissão ( Trabalho ).

    --> e outras coisinhas.

     

    ? !! Lembrando !!

    Já temos launcher, criptografia, layout para o client ( Próprio ), vps linux ( Dedicado ), e site.

     

    ? VAGAS ?

    2 Vaga para Programador.

    2 Vagas para Spriter.

     

    RESSALTANDO NOVAMENTE

    Isso não é para crianças, então se seu intuito não está voltado para essas duas vagas ou ajudar, por favor não venham pedir vaga.

     

    Discord

    AlaOGaspar

    #7077

     

    Não sou muito fã de escrever e ainda mais de organizar kkk, então irei deixar meu discord aqui para caso vocês queiram saber + do servidor.

     

    Citar

     

     

     

     

     

     

     

     

     

  3. 9 minutos atrás, Yan Liima disse:

     

    64bits só com VS.

     

    Possa ser que o diretório das lib do seu dev não estejam corretas. Tenta desinstar todos dev cpp do seu PC e instala esse AQUUUI

    Pra garantir que funcione 100%, instala o openssl também.

    eu instalei o seu, mas vou verificar e com o visual ? vs2019 n funfa

    9 minutos atrás, GaspaR1 disse:

    eu instalei o seu, mas vou verificar e com o visual ? vs2019 n funfa

    e como eu faço pra zerar tudo do dev ?

    2 horas atrás, GaspaR1 disse:

    eu instalei o seu, mas vou verificar e com o visual ? vs2019 n funfa

    e como eu faço pra zerar tudo do dev ?

    EDIT: consegui compilar, mas agora não loga wtf

  4. Em 17/07/2019 em 14:22, Yan Liima disse:

    Salve salve pessoal, no inicio desse ano estava dando uma estudada e mexendo com a TFS 0.4 rev3884, e vi que havia alguns bugs e que não tinha Cast incluso. Com base nisso decidi atualizar e otimizar a source, já que ainda ela é uma das mais utilizadas no mundo de Otserv.  Decidi compartilhar esse meu trabalho com vocês! Acredito que possa ser uma das melhores REV atualmente.

     

    Conto com o seu feedback, caso haja algum bug, algo que tenha que mudar/optimizar, não exite em avisar aqui no tópico. Toda ajuda será bem vinda xD

     

    TheForgottenServer.png.b189141ed902d5b3b381cc2f89f04c20.pngThe Forgotten Server, Tibia Versão: 8.60TheForgottenServer.png.b189141ed902d5b3b381cc2f89f04c20.png

    O que contém nela?

      Mostrar conteúdo oculto
    
    
     War System
     Cast System (Cast do Summ, implementado na Source por mim e corrigido do mesmo. Com uma nova função que adicionei de Kick.)
     Anti-Divulgação (Configuravel no config.lua)
     Bug de Anti-push corrigido!
     Bugs de ElfBot corrigido(De party e alguns outros).
    
     Problema de não aceitar outro items corrigido(skipItemsVersionCheck);
     Opcode incluso;
     Retirado erro de Malformed File;
     creatureevent onMoveItem() & onMoveItem2() incluso;
     getOtsysTime(), getPlayerPing(cid), doPlayerSendPing(cid) incluso;
     função doPlayerOpenChannel incluso;
     função getCreaturePathTo() incluso;
     função doSetCreatureLight() incluso;
    
     Adicionado Max Packet Por Segundo algo que não havia na 0.4 e decidi por. ta 100%)
     Adicionado exhaust ao comprar/vender items(retirei da src do Fir3...)
     Bug de clonar usando o comando !disband corrigido!
     Comandos de house corrigido(como o Aleta Som por exemplo..)
     Ao entrar em PZ remove battle
     Salt removido
     Log do chat, salva tudo que os jogadores falam (Ative no config.lua: "logsPlayers", necessário criar a pasta "players" no logs.)
     O comando /addskill que causava um congelamento foi corrigido!
     Podendo atacar & usar runa ao mesmo tempo.
     É possivel fazer os monstros nascerem mesmo com o jogador perto(no config.lua deixe o allowBlockSpawn como false)
     Com healthHealingColor e manaHealingColor (para alterar as cores do heal, configuravel no config.lua)
     Mailbox Block adicionado (itens de clone mais difíceis com sistema de parcel) + Configurações extras no sistema de mail system.
     timeBetweenCustomActions adicionado (exhausted em talkactions e VIP LIST, para evitar travamentos de elfbot).
     classicEquipmentSlots adicionado (slot correto para cada equipamento, basta por como false no config).
    ••• E muito mais!!! •••

     

     

    Dentro da pasta contém o config.lua com todas as tag já adicionadas.

     

    Não esquecam de executar a Query do Cast na sua DB:

      Mostrar conteúdo oculto
    
    
    ALTER TABLE  `players` ADD  `cast` TINYINT NOT NULL DEFAULT  '0',
    ADD  `castViewers` INT( 11 ) NOT NULL DEFAULT  '0',
    ADD  `castDescription` VARCHAR( 255 ) NOT NULL

     

     

    Downloads uint8(Effects até 255)

    Distro: TheForgottenServer.exe

    Src + datapack: Source & Data

    -------------------------------------------------------

    Downloads uint16(Effects até 65534)

    Distro: TheForgottenServer.exe

    Src + datapack: Source & Data

    É necessário fazer a modificação do Hexadecimal no cliente. Aqui tem um já pronto: Cliente.exe(com mc) ou Cliente.exe(sem mc) (só será necessario se utilizar a src com o uint 16.)

    Lembrado também que precisa utilizar a lib 000-constant.lua deste datapack.

    -------------------------------------------------------

    Scans:

    Distro(uint8) & Distro(uint16)

    Source

    Cliente

     

    Obs: Os virus detectado é um falso positivo, então não se preocupem!

     

    A distro foi testada em Windows e em Linux Ubuntu 14.04, em ambos funcionaram muito bem!

     

    E para quem se interessa em saber onde se localiza os code do Cast, aqui está uma imagem. Você pode achar procurando por "//CAST"

      Mostrar conteúdo oculto

    CAST.thumb.png.9da2070783e4c94217557d5175b32147.png

     

    Façam um ótimo aproveito ?

    mano, estou tento problema na hora de compilar no dev

    da o seguinte erro , tem alguma ideia do que seja ? ou poderia me mandar o seu dev ? tbm tentei compilar com o visual 19, mas tbm da erro falta o otpch.cpp.136346996_Semttulo.thumb.png.885fb06fc76c08391dde624b99703721.png

  5. Salve galera beleza ? antes de tudo não sei se é a área correta, se não estiver na área correta peço que alguém mova porfavor...

    bom recentemente segui um tutorial para aumentar o campo de visão do personagem no meu OT, e funcionou só que fica com umas bordinhas ainda, alguém saberia arrumar ? ou me ajudar...

     

    1946983466_WhatsAppImage2020-10-10at20_33_51.thumb.jpeg.816fa45b55b02917cd7f9965a996b141.jpeg

     

    map.cpp otclient

    /*
     * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    #include "map.h"
    #include "game.h"
    #include "localplayer.h"
    #include "tile.h"
    #include "item.h"
    #include "missile.h"
    #include "statictext.h"
    #include "mapview.h"
    #include "minimap.h"
    
    #include <framework/core/eventdispatcher.h>
    #include <framework/core/application.h>
    
    Map g_map;
    TilePtr Map::m_nulltile;
    
    void Map::init()
    {
        resetAwareRange();
        m_animationFlags |= Animation_Show;
    }
    
    void Map::terminate()
    {
        clean();
    }
    
    void Map::addMapView(const MapViewPtr& mapView)
    {
        m_mapViews.push_back(mapView);
    }
    
    void Map::removeMapView(const MapViewPtr& mapView)
    {
        auto it = std::find(m_mapViews.begin(), m_mapViews.end(), mapView);
        if(it != m_mapViews.end())
            m_mapViews.erase(it);
    }
    
    void Map::notificateTileUpdate(const Position& pos)
    {
        if(!pos.isMapPosition())
            return;
    
        for(const MapViewPtr& mapView : m_mapViews)
            mapView->onTileUpdate(pos);
        g_minimap.updateTile(pos, getTile(pos));
    }
    
    void Map::clean()
    {
        cleanDynamicThings();
    
        for(int i=0;i<=Otc::MAX_Z;++i)
            m_tileBlocks[i].clear();
    
        m_waypoints.clear();
    
        g_towns.clear();
        g_houses.clear();
        g_creatures.clearSpawns();
        m_tilesRect = Rect(65534, 65534, 0, 0);
    }
    
    void Map::cleanDynamicThings()
    {
        for(const auto& pair : m_knownCreatures) {
            const CreaturePtr& creature = pair.second;
            removeThing(creature);
        }
        m_knownCreatures.clear();
    
        for(int i=0;i<=Otc::MAX_Z;++i)
            m_floorMissiles[i].clear();
    
        cleanTexts();
    }
    
    void Map::cleanTexts()
    {
        m_animatedTexts.clear();
        m_staticTexts.clear();
    }
    
    void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
    {
        if(!thing)
            return;
    
        if(thing->isItem() || thing->isCreature() || thing->isEffect()) {
            const TilePtr& tile = getOrCreateTile(pos);
            if(tile)
                tile->addThing(thing, stackPos);
        } else {
            if(thing->isMissile()) {
                m_floorMissiles[pos.z].push_back(thing->static_self_cast<Missile>());
            } else if(thing->isAnimatedText()) {
                // this code will stack animated texts of the same color
                AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>();
                AnimatedTextPtr prevAnimatedText;
                bool merged = false;
                for(auto other : m_animatedTexts) {
                    if(other->getPosition() == pos) {
                        prevAnimatedText = other;
                        if(other->merge(animatedText)) {
                            merged = true;
                            break;
                        }
                    }
                }
                if(!merged) {
                    if(prevAnimatedText) {
                        Point offset = prevAnimatedText->getOffset();
                        float t = prevAnimatedText->getTimer().ticksElapsed();
                        if(t < Otc::ANIMATED_TEXT_DURATION / 4.0) { // didnt move 12 pixels
                            int y = 12 - 48 * t / (float)Otc::ANIMATED_TEXT_DURATION;
                            offset += Point(0, y);
                        }
                        offset.y = std::min<int>(offset.y, 12);
                        animatedText->setOffset(offset);
                    }
                    m_animatedTexts.push_back(animatedText);
                }
            } else if(thing->isStaticText()) {
                StaticTextPtr staticText = thing->static_self_cast<StaticText>();
                bool mustAdd = true;
                for(auto other : m_staticTexts) {
                    // try to combine messages
                    if(other->getPosition() == pos && other->addMessage(staticText->getName(), staticText->getMessageMode(), staticText->getFirstMessage())) {
                        mustAdd = false;
                        break;
                    }
                }
    
                if(mustAdd)
                    m_staticTexts.push_back(staticText);
                else
                    return;
            }
    
            thing->setPosition(pos);
            thing->onAppear();
        }
    
        notificateTileUpdate(pos);
    }
    
    ThingPtr Map::getThing(const Position& pos, int stackPos)
    {
        if(TilePtr tile = getTile(pos))
            return tile->getThing(stackPos);
        return nullptr;
    }
    
    bool Map::removeThing(const ThingPtr& thing)
    {
        if(!thing)
            return false;
    
        bool ret = false;
        if(thing->isMissile()) {
            MissilePtr missile = thing->static_self_cast<Missile>();
            int z = missile->getPosition().z;
            auto it = std::find(m_floorMissiles[z].begin(), m_floorMissiles[z].end(), missile);
            if(it != m_floorMissiles[z].end()) {
                m_floorMissiles[z].erase(it);
                ret = true;
            }
        } else if(thing->isAnimatedText()) {
            AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>();
            auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText);
            if(it != m_animatedTexts.end()) {
                m_animatedTexts.erase(it);
                ret = true;
            }
        } else if(thing->isStaticText()) {
            StaticTextPtr staticText = thing->static_self_cast<StaticText>();
            auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText);
            if(it != m_staticTexts.end()) {
                m_staticTexts.erase(it);
                ret = true;
            }
        } else if(const TilePtr& tile = thing->getTile())
            ret = tile->removeThing(thing);
    
        notificateTileUpdate(thing->getPosition());
        return ret;
    }
    
    bool Map::removeThingByPos(const Position& pos, int stackPos)
    {
        if(TilePtr tile = getTile(pos))
            return removeThing(tile->getThing(stackPos));
        return false;
    }
    
    void Map::colorizeThing(const ThingPtr& thing, const Color& color)
    {
        if(!thing)
            return;
    
        if(thing->isItem())
            thing->static_self_cast<Item>()->setColor(color);
        else if(thing->isCreature()) {
            const TilePtr& tile = thing->getTile();
            assert(tile);
    
            const ThingPtr& topThing = tile->getTopThing();
            assert(topThing);
    
            topThing->static_self_cast<Item>()->setColor(color);
        }
    }
    
    void Map::removeThingColor(const ThingPtr& thing)
    {
        if(!thing)
            return;
    
        if(thing->isItem())
            thing->static_self_cast<Item>()->setColor(Color::alpha);
        else if(thing->isCreature()) {
            const TilePtr& tile = thing->getTile();
            assert(tile);
    
            const ThingPtr& topThing = tile->getTopThing();
            assert(topThing);
    
            topThing->static_self_cast<Item>()->setColor(Color::alpha);
        }
    }
    
    StaticTextPtr Map::getStaticText(const Position& pos)
    {
        for(auto staticText : m_staticTexts) {
            // try to combine messages
            if(staticText->getPosition() == pos)
                return staticText;
        }
        return nullptr;
    }
    
    const TilePtr& Map::createTile(const Position& pos)
    {
        if(!pos.isMapPosition())
            return m_nulltile;
        if(pos.x < m_tilesRect.left())
            m_tilesRect.setLeft(pos.x);
        if(pos.y < m_tilesRect.top())
            m_tilesRect.setTop(pos.y);
        if(pos.x > m_tilesRect.right())
            m_tilesRect.setRight(pos.x);
        if(pos.y > m_tilesRect.bottom())
            m_tilesRect.setBottom(pos.y);
        TileBlock& block = m_tileBlocks[pos.z][getBlockIndex(pos)];
        return block.create(pos);
    }
    
    template <typename... Items>
    const TilePtr& Map::createTileEx(const Position& pos, const Items&... items)
    {
        if(!pos.isValid())
            return m_nulltile;
        const TilePtr& tile = getOrCreateTile(pos);
        auto vec = {items...};
        for(auto it : vec)
            addThing(it, pos);
    
        return tile;
    }
    
    const TilePtr& Map::getOrCreateTile(const Position& pos)
    {
        if(!pos.isMapPosition())
            return m_nulltile;
        if(pos.x < m_tilesRect.left())
            m_tilesRect.setLeft(pos.x);
        if(pos.y < m_tilesRect.top())
            m_tilesRect.setTop(pos.y);
        if(pos.x > m_tilesRect.right())
            m_tilesRect.setRight(pos.x);
        if(pos.y > m_tilesRect.bottom())
            m_tilesRect.setBottom(pos.y);
        TileBlock& block = m_tileBlocks[pos.z][getBlockIndex(pos)];
        return block.getOrCreate(pos);
    }
    
    const TilePtr& Map::getTile(const Position& pos)
    {
        if(!pos.isMapPosition())
            return m_nulltile;
        auto it = m_tileBlocks[pos.z].find(getBlockIndex(pos));
        if(it != m_tileBlocks[pos.z].end())
            return it->second.get(pos);
        return m_nulltile;
    }
    
    const TileList Map::getTiles(int floor/* = -1*/)
    {
        TileList tiles;
        if(floor > Otc::MAX_Z) {
            return tiles;
        }
        else if(floor < 0) {
            // Search all floors
            for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
                for(const auto& pair : m_tileBlocks[z]) {
                    const TileBlock& block = pair.second;
                    for(const TilePtr& tile : block.getTiles()) {
                        if(tile != nullptr)
                            tiles.push_back(tile);
                    }
                }
            }
        }
        else {
            for(const auto& pair : m_tileBlocks[floor]) {
                const TileBlock& block = pair.second;
                for(const TilePtr& tile : block.getTiles()) {
                    if(tile != nullptr)
                        tiles.push_back(tile);
                }
            }
        }
        return tiles;
    }
    
    void Map::cleanTile(const Position& pos)
    {
        if(!pos.isMapPosition())
            return;
        auto it = m_tileBlocks[pos.z].find(getBlockIndex(pos));
        if(it != m_tileBlocks[pos.z].end()) {
            TileBlock& block = it->second;
            if(const TilePtr& tile = block.get(pos)) {
                tile->clean();
                if(tile->canErase())
                    block.remove(pos);
    
                notificateTileUpdate(pos);
            }
        }
        for(auto it = m_staticTexts.begin();it != m_staticTexts.end();) {
            const StaticTextPtr& staticText = *it;
            if(staticText->getPosition() == pos && staticText->getMessageMode() == Otc::MessageNone)
                it = m_staticTexts.erase(it);
            else
                ++it;
        }
    }
    
    void Map::setShowZone(tileflags_t zone, bool show)
    {
        if(show)
            m_zoneFlags |= (uint32)zone;
        else
            m_zoneFlags &= ~(uint32)zone;
    }
    
    void Map::setShowZones(bool show)
    {
        if(!show)
            m_zoneFlags = 0;
        else if(m_zoneFlags == 0)
            m_zoneFlags = TILESTATE_HOUSE | TILESTATE_PROTECTIONZONE;
    }
    
    void Map::setZoneColor(tileflags_t zone, const Color& color)
    {
        if((m_zoneFlags & zone) == zone)
            m_zoneColors[zone] = color;
    }
    
    Color Map::getZoneColor(tileflags_t flag)
    {
        auto it = m_zoneColors.find(flag);
        if(it == m_zoneColors.end())
            return Color::alpha;
        return it->second;
    }
    
    void Map::setForceShowAnimations(bool force)
    {
        if(force) {
            if(!(m_animationFlags & Animation_Force))
                m_animationFlags |= Animation_Force;
        } else
            m_animationFlags &= ~Animation_Force;
    }
    
    bool Map::isForcingAnimations()
    {
        return (m_animationFlags & Animation_Force) == Animation_Force;
    }
    
    bool Map::isShowingAnimations()
    {
        return (m_animationFlags & Animation_Show) == Animation_Show;
    }
    
    void Map::setShowAnimations(bool show)
    {
        if(show) {
            if(!(m_animationFlags & Animation_Show))
                m_animationFlags |= Animation_Show;
        } else
            m_animationFlags &= ~Animation_Show;
    }
    
    void Map::beginGhostMode(float opacity)
    {
        g_painter->setOpacity(opacity);
    }
    
    void Map::endGhostMode()
    {
        g_painter->resetOpacity();
    }
    
    std::map<Position, ItemPtr> Map::findItemsById(uint16 clientId, uint32 max)
    {
        std::map<Position, ItemPtr> ret;
        uint32 count = 0;
        for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
            for(const auto& pair : m_tileBlocks[z]) {
                const TileBlock& block = pair.second;
                for(const TilePtr& tile : block.getTiles()) {
                    if(unlikely(!tile || tile->isEmpty()))
                        continue;
                    for(const ItemPtr& item : tile->getItems()) {
                        if(item->getId() == clientId) {
                            ret.insert(std::make_pair(tile->getPosition(), item));
                            if(++count >= max)
                                break;
                        }
                    }
                }
            }
        }
    
        return ret;
    }
    
    void Map::addCreature(const CreaturePtr& creature)
    {
        m_knownCreatures[creature->getId()] = creature;
    }
    
    CreaturePtr Map::getCreatureById(uint32 id)
    {
        auto it = m_knownCreatures.find(id);
        if(it == m_knownCreatures.end())
            return nullptr;
        return it->second;
    }
    
    void Map::removeCreatureById(uint32 id)
    {
        if(id == 0)
            return;
    
        auto it = m_knownCreatures.find(id);
        if(it != m_knownCreatures.end())
            m_knownCreatures.erase(it);
    }
    
    void Map::removeUnawareThings()
    {
        // remove creatures from tiles that we are not aware of anymore
        for(const auto& pair : m_knownCreatures) {
            const CreaturePtr& creature = pair.second;
            if(!isAwareOfPosition(creature->getPosition()))
                removeThing(creature);
        }
    
        // remove static texts from tiles that we are not aware anymore
        for(auto it = m_staticTexts.begin(); it != m_staticTexts.end();) {
            const StaticTextPtr& staticText = *it;
            if(staticText->getMessageMode() == Otc::MessageNone && !isAwareOfPosition(staticText->getPosition()))
                it = m_staticTexts.erase(it);
            else
                ++it;
        }
    
        if(!g_game.getFeature(Otc::GameKeepUnawareTiles)) {
            // remove tiles that we are not aware anymore
            for(int z = 0; z <= Otc::MAX_Z; ++z) {
                std::unordered_map<uint, TileBlock>& tileBlocks = m_tileBlocks[z];
                for(auto it = tileBlocks.begin(); it != tileBlocks.end();) {
                    TileBlock& block = (*it).second;
                    bool blockEmpty = true;
                    for(const TilePtr& tile : block.getTiles()) {
                        if(!tile)
                            continue;
    
                        const Position& pos = tile->getPosition();
                        if(!isAwareOfPosition(pos))
                            block.remove(pos);
                        else
                            blockEmpty = false;
                    }
    
                    if(blockEmpty)
                        it = tileBlocks.erase(it);
                    else
                        ++it;
                }
            }
        }
    }
    
    void Map::setCentralPosition(const Position& centralPosition)
    {
        if(m_centralPosition == centralPosition)
            return;
    
        m_centralPosition = centralPosition;
    
        removeUnawareThings();
    
        // this fixes local player position when the local player is removed from the map,
        // the local player is removed from the map when there are too many creatures on his tile,
        // so there is no enough stackpos to the server send him
        g_dispatcher.addEvent([this] {
            LocalPlayerPtr localPlayer = g_game.getLocalPlayer();
            if(!localPlayer || localPlayer->getPosition() == m_centralPosition)
                return;
            TilePtr tile = localPlayer->getTile();
            if(tile && tile->hasThing(localPlayer))
                return;
    
            Position oldPos = localPlayer->getPosition();
            Position pos = m_centralPosition;
            if(oldPos != pos) {
                if(!localPlayer->isRemoved())
                    localPlayer->onDisappear();
                localPlayer->setPosition(pos);
                localPlayer->onAppear();
                g_logger.debug("forced player position update");
            }
        });
    
        for(const MapViewPtr& mapView : m_mapViews)
            mapView->onMapCenterChange(centralPosition);
    }
    
    std::vector<CreaturePtr> Map::getSightSpectators(const Position& centerPos, bool multiFloor)
    {
        return getSpectatorsInRangeEx(centerPos, multiFloor, m_awareRange.left - 1, m_awareRange.right - 2, m_awareRange.top - 1, m_awareRange.bottom - 2);
    }
    
    std::vector<CreaturePtr> Map::getSpectators(const Position& centerPos, bool multiFloor)
    {
        return getSpectatorsInRangeEx(centerPos, multiFloor, m_awareRange.left, m_awareRange.right, m_awareRange.top, m_awareRange.bottom);
    }
    
    std::vector<CreaturePtr> Map::getSpectatorsInRange(const Position& centerPos, bool multiFloor, int xRange, int yRange)
    {
        return getSpectatorsInRangeEx(centerPos, multiFloor, xRange, xRange, yRange, yRange);
    }
    
    std::vector<CreaturePtr> Map::getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange)
    {
        int minZRange = 0;
        int maxZRange = 0;
        std::vector<CreaturePtr> creatures;
    
        if(multiFloor) {
            minZRange = 0;
            maxZRange = Otc::MAX_Z;
        }
    
        //TODO: optimize
        //TODO: get creatures from other floors corretly
        //TODO: delivery creatures in distance order
    
        for(int iz=-minZRange; iz<=maxZRange; ++iz) {
            for(int iy=-minYRange; iy<=maxYRange; ++iy) {
                for(int ix=-minXRange; ix<=maxXRange; ++ix) {
                    TilePtr tile = getTile(centerPos.translated(ix,iy,iz));
                    if(!tile)
                        continue;
    
                    auto tileCreatures = tile->getCreatures();
                    creatures.insert(creatures.end(), tileCreatures.rbegin(), tileCreatures.rend());
                }
            }
        }
    
        return creatures;
    }
    
    bool Map::isLookPossible(const Position& pos)
    {
        TilePtr tile = getTile(pos);
        return tile && tile->isLookPossible();
    }
    
    bool Map::isCovered(const Position& pos, int firstFloor)
    {
        // check for tiles on top of the postion
        Position tilePos = pos;
        while(tilePos.coveredUp() && tilePos.z >= firstFloor) {
            TilePtr tile = getTile(tilePos);
            // the below tile is covered when the above tile has a full ground
            if(tile && tile->isFullGround())
                return true;
        }
        return false;
    }
    
    bool Map::isCompletelyCovered(const Position& pos, int firstFloor)
    {
        const TilePtr& checkTile = getTile(pos);
        Position tilePos = pos;
        while(tilePos.coveredUp() && tilePos.z >= firstFloor) {
            bool covered = true;
            bool done = false;
            // check in 2x2 range tiles that has no transparent pixels
            for(int x=0;x<2 && !done;++x) {
                for(int y=0;y<2 && !done;++y) {
                    const TilePtr& tile = getTile(tilePos.translated(-x, -y));
                    if(!tile || !tile->isFullyOpaque()) {
                        covered = false;
                        done = true;
                    } else if(x==0 && y==0 && (!checkTile || checkTile->isSingleDimension())) {
                        done = true;
                    }
                }
            }
            if(covered)
                return true;
        }
        return false;
    }
    
    bool Map::isAwareOfPosition(const Position& pos)
    {
        if(pos.z < getFirstAwareFloor() || pos.z > getLastAwareFloor())
            return false;
    
        Position groundedPos = pos;
        while(groundedPos.z != m_centralPosition.z) {
            if(groundedPos.z > m_centralPosition.z) {
                if(groundedPos.x == 65535 || groundedPos.y == 65535) // When pos == 65535,65535,15 we cant go up to 65536,65536,14
                    break;
                groundedPos.coveredUp();
            }
            else {
                if(groundedPos.x == 0 || groundedPos.y == 0) // When pos == 0,0,0 we cant go down to -1,-1,1
                    break;
                groundedPos.coveredDown();
            }
        }
        return m_centralPosition.isInRange(groundedPos, m_awareRange.left,
                                                        m_awareRange.right,
                                                        m_awareRange.top,
                                                        m_awareRange.bottom);
    }
    
    void Map::setAwareRange(const AwareRange& range)
    {
        m_awareRange = range;
        removeUnawareThings();
    }
    
    void Map::resetAwareRange()
    {
       AwareRange range;
       range.left = 19; //Change this to = maxClientViewportX
       range.top = 19; //Change this to = maxClientViewportY
       range.bottom = range.top+1;
       range.right = range.left+1;
       setAwareRange(range);
    }
    
    int Map::getFirstAwareFloor()
    {
        if(m_centralPosition.z > Otc::SEA_FLOOR)
            return m_centralPosition.z-Otc::AWARE_UNDEGROUND_FLOOR_RANGE;
        else
            return 0;
    }
    
    int Map::getLastAwareFloor()
    {
        if(m_centralPosition.z > Otc::SEA_FLOOR)
            return std::min<int>(m_centralPosition.z+Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z);
        else
            return Otc::SEA_FLOOR;
    }
    
    std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const Position& startPos, const Position& goalPos, int maxComplexity, int flags)
    {
        // pathfinding using A* search algorithm
        // as described in http://en.wikipedia.org/wiki/A*_search_algorithm
    
        struct Node {
            Node(const Position& pos) : cost(0), totalCost(0), pos(pos), prev(nullptr), dir(Otc::InvalidDirection) { }
            float cost;
            float totalCost;
            Position pos;
            Node *prev;
            Otc::Direction dir;
        };
    
        struct LessNode : std::binary_function<std::pair<Node*, float>, std::pair<Node*, float>, bool> {
            bool operator()(std::pair<Node*, float> a, std::pair<Node*, float> b) const {
                return b.second < a.second;
            }
        };
    
        std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> ret;
        std::vector<Otc::Direction>& dirs = std::get<0>(ret);
        Otc::PathFindResult& result = std::get<1>(ret);
    
        result = Otc::PathFindResultNoWay;
    
        if(startPos == goalPos) {
            result = Otc::PathFindResultSamePosition;
            return ret;
        }
    
        if(startPos.z != goalPos.z) {
            result = Otc::PathFindResultImpossible;
            return ret;
        }
    
        // check the goal pos is walkable
        if(g_map.isAwareOfPosition(goalPos)) {
            const TilePtr goalTile = getTile(goalPos);
            if(!goalTile || !goalTile->isWalkable()) {
                return ret;
            }
        }
        else {
            const MinimapTile& goalTile = g_minimap.getTile(goalPos);
            if(goalTile.hasFlag(MinimapTileNotWalkable)) {
                return ret;
            }
        }
    
        std::unordered_map<Position, Node*, PositionHasher> nodes;
        std::priority_queue<std::pair<Node*, float>, std::vector<std::pair<Node*, float>>, LessNode> searchList;
    
        Node *currentNode = new Node(startPos);
        currentNode->pos = startPos;
        nodes[startPos] = currentNode;
        Node *foundNode = nullptr;
        while(currentNode) {
            if((int)nodes.size() > maxComplexity) {
                result = Otc::PathFindResultTooFar;
                break;
            }
    
            // path found
            if(currentNode->pos == goalPos && (!foundNode || currentNode->cost < foundNode->cost))
                foundNode = currentNode;
    
            // cost too high
            if(foundNode && currentNode->totalCost >= foundNode->cost)
                break;
    
            for(int i=-1;i<=1;++i) {
                for(int j=-1;j<=1;++j) {
                    if(i == 0 && j == 0)
                        continue;
    
                    bool wasSeen = false;
                    bool hasCreature = false;
                    bool isNotWalkable = true;
                    bool isNotPathable = true;
                    int speed = 100;
    
                    Position neighborPos = currentNode->pos.translated(i, j);
                    if(g_map.isAwareOfPosition(neighborPos)) {
                        wasSeen = true;
                        if(const TilePtr& tile = getTile(neighborPos)) {
                            hasCreature = tile->hasCreature();
                            isNotWalkable = !tile->isWalkable();
                            isNotPathable = !tile->isPathable();
                            speed = tile->getGroundSpeed();
                        }
                    } else {
                        const MinimapTile& mtile = g_minimap.getTile(neighborPos);
                        wasSeen = mtile.hasFlag(MinimapTileWasSeen);
                        isNotWalkable = mtile.hasFlag(MinimapTileNotWalkable);
                        isNotPathable = mtile.hasFlag(MinimapTileNotPathable);
                        if(isNotWalkable || isNotPathable)
                            wasSeen = true;
                        speed = mtile.getSpeed();
                    }
    
                    float walkFactor = 0;
                    if(neighborPos != goalPos) {
                        if(!(flags & Otc::PathFindAllowNotSeenTiles) && !wasSeen)
                            continue;
                        if(wasSeen) {
                            if(!(flags & Otc::PathFindAllowCreatures) && hasCreature)
                                continue;
                            if(!(flags & Otc::PathFindAllowNonPathable) && isNotPathable)
                                continue;
                            if(!(flags & Otc::PathFindAllowNonWalkable) && isNotWalkable)
                                continue;
                        }
                    } else {
                        if(!(flags & Otc::PathFindAllowNotSeenTiles) && !wasSeen)
                            continue;
                        if(wasSeen) {
                            if(!(flags & Otc::PathFindAllowNonWalkable) && isNotWalkable)
                                continue;
                        }
                    }
    
                    Otc::Direction walkDir = currentNode->pos.getDirectionFromPosition(neighborPos);
                    if(walkDir >= Otc::NorthEast)
                        walkFactor += 3.0f;
                    else
                        walkFactor += 1.0f;
    
                    float cost = currentNode->cost + (speed * walkFactor) / 100.0f;
    
                    Node *neighborNode;
                    if(nodes.find(neighborPos) == nodes.end()) {
                        neighborNode = new Node(neighborPos);
                        nodes[neighborPos] = neighborNode;
                    } else {
                        neighborNode = nodes[neighborPos];
                        if(neighborNode->cost <= cost)
                            continue;
                    }
    
                    neighborNode->prev = currentNode;
                    neighborNode->cost = cost;
                    neighborNode->totalCost = neighborNode->cost + neighborPos.distance(goalPos);
                    neighborNode->dir = walkDir;
                    searchList.push(std::make_pair(neighborNode, neighborNode->totalCost));
                }
            }
    
            if(!searchList.empty()) {
                currentNode = searchList.top().first;
                searchList.pop();
            } else
                currentNode = nullptr;
        }
    
        if(foundNode) {
            currentNode = foundNode;
            while(currentNode) {
                dirs.push_back(currentNode->dir);
                currentNode = currentNode->prev;
            }
            dirs.pop_back();
            std::reverse(dirs.begin(), dirs.end());
            result = Otc::PathFindResultOk;
        }
    
        for(auto it : nodes)
            delete it.second;
    
        return ret;
    }

     

    protocol.cpp

    /**
     * The Forgotten Server - a free and open-source MMORPG server emulator
     * Copyright (C) 2019  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"
    
    extern ConfigManager g_config;
    extern Actions actions;
    extern CreatureEvents* g_creatureEvents;
    extern Chat* g_chat;
    
    void ProtocolGame::release()
    {
    	//dispatcher thread
    	if (player && player->client == shared_from_this()) {
    		player->client.reset();
    		player->decrementReferenceCounter();
    		player = nullptr;
    	}
    
    	OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
    	Protocol::release();
    }
    
    void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem)
    {
    	//dispatcher thread
    	Player* foundPlayer = g_game.getPlayerByName(name);
    	if (!foundPlayer || 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;
    			}
    		}
    
    		WaitingList& waitingList = WaitingList::getInstance();
    		if (!waitingList.clientLogin(player)) {
    			uint32_t currentSlot = waitingList.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::loadPlayerById(player, player->getGUID())) {
    			disconnectClient("Your character could not be loaded.");
    			return;
    		}
    
    		player->setOperatingSystem(operatingSystem);
    
    		if (!g_game.placeCreature(player, player->getLoginPosition())) {
    			if (!g_game.placeCreature(player, player->getTemplePosition(), false, true)) {
    				disconnectClient("Temple position is wrong. Contact the administrator.");
    				return;
    			}
    		}
    
    		if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
    			player->registerCreatureEvent("ExtendedOpcode");
    		}
    
    		player->lastIP = player->getIP();
    		player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
    		acceptPackets = true;
    	} else {
    		if (eventConnect != 0 || !g_config.getBoolean(ConfigManager::REPLACE_KICK_ON_LOGIN)) {
    			//Already trying to connect
    			disconnectClient("You are already logged in.");
    			return;
    		}
    
    		if (foundPlayer->client) {
    			foundPlayer->disconnect();
    			foundPlayer->isConnecting = true;
    
    			eventConnect = g_scheduler.addEvent(createSchedulerTask(1000, std::bind(&ProtocolGame::connect, getThis(), foundPlayer->getID(), operatingSystem)));
    		} else {
    			connect(foundPlayer->getID(), operatingSystem);
    		}
    	}
    	OutputMessagePool::getInstance().addProtocolToAutosend(shared_from_this());
    }
    
    void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem)
    {
    	eventConnect = 0;
    
    	Player* foundPlayer = g_game.getPlayerByID(playerId);
    	if (!foundPlayer || foundPlayer->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 = foundPlayer;
    	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);
    	acceptPackets = true;
    }
    
    void ProtocolGame::logout(bool displayEffect, bool forced)
    {
    	//dispatcher thread
    	if (!player) {
    		return;
    	}
    
    	if (!player->isRemoved()) {
    		if (!forced) {
    			if (!player->isAccessPlayer()) {
    				if (player->getTile()->hasFlag(TILESTATE_NOLOGOUT)) {
    					player->sendCancelMessage(RETURNVALUE_YOUCANNOTLOGOUTHERE);
    					return;
    				}
    
    				if (!player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) && player->hasCondition(CONDITION_INFIGHT)) {
    					player->sendCancelMessage(RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT);
    					return;
    				}
    			}
    
    			//scripting event - onLogout
    			if (!g_creatureEvents->playerLogout(player)) {
    				//Let the script handle the error message
    				return;
    			}
    		}
    
    		if (displayEffect && player->getHealth() > 0) {
    			g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
    		}
    	}
    
    	disconnect();
    
    	g_game.removeCreature(player);
    }
    
    void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
    {
    	if (g_game.getGameState() == GAME_STATE_SHUTDOWN) {
    		disconnect();
    		return;
    	}
    
    	OperatingSystem_t operatingSystem = static_cast<OperatingSystem_t>(msg.get<uint16_t>());
    	version = msg.get<uint16_t>();
    
    	msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision
    
    	if (!Protocol::RSA_decrypt(msg)) {
    		disconnect();
    		return;
    	}
    
    	xtea::key key;
    	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(std::move(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();
    
    	auto sessionArgs = explodeString(sessionKey, "\n", 4);
    	if (sessionArgs.size() != 4) {
    		disconnect();
    		return;
    	}
    
    	std::string& accountName = sessionArgs[0];
    	std::string& password = sessionArgs[1];
    	std::string& token = sessionArgs[2];
    	uint32_t tokenTime = 0;
    	try {
    		tokenTime = std::stoul(sessionArgs[3]);
    	} catch (const std::invalid_argument&) {
    		disconnectClient("Malformed token packet.");
    		return;
    	} catch (const std::out_of_range&) {
    		disconnectClient("Token time is too long.");
    		return;
    	}
    
    	if (accountName.empty()) {
    		disconnectClient("You must enter your account name.");
    		return;
    	}
    
    	std::string characterName = msg.getString();
    
    	uint32_t timeStamp = msg.get<uint32_t>();
    	uint8_t randNumber = msg.getByte();
    	if (challengeTimestamp != timeStamp || challengeRandom != randNumber) {
    		disconnect();
    		return;
    	}
    
    	if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) {
    		std::ostringstream ss;
    		ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!";
    		disconnectClient(ss.str());
    		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, token, tokenTime);
    	if (accountId == 0) {
    		disconnectClient("Account name or password is not correct.");
    		return;
    	}
    
    	g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem)));
    }
    
    void ProtocolGame::onConnect()
    {
    	auto output = OutputMessagePool::getOutputMessage();
    	static std::random_device rd;
    	static std::ranlux24 generator(rd());
    	static std::uniform_int_distribution<uint16_t> randNumber(0x00, 0xFF);
    
    	// Skip checksum
    	output->skipBytes(sizeof(uint32_t));
    
    	// Packet length & type
    	output->add<uint16_t>(0x0006);
    	output->addByte(0x1F);
    
    	// Add timestamp & random number
    	challengeTimestamp = static_cast<uint32_t>(time(nullptr));
    	output->add<uint32_t>(challengeTimestamp);
    
    	challengeRandom = randNumber(generator);
    	output->addByte(challengeRandom);
    
    	// Go back and write checksum
    	output->skipBytes(-12);
    	output->add<uint32_t>(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8));
    
    	send(output);
    }
    
    void ProtocolGame::disconnectClient(const std::string& message) const
    {
    	auto output = OutputMessagePool::getOutputMessage();
    	output->addByte(0x14);
    	output->addString(message);
    	send(output);
    	disconnect();
    }
    
    void ProtocolGame::writeToOutputBuffer(const NetworkMessage& msg)
    {
    	auto out = getOutputBuffer(msg.getLength());
    	out->append(msg);
    }
    
    void ProtocolGame::parsePacket(NetworkMessage& msg)
    {
    	if (!acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN || msg.getLength() <= 0) {
    		return;
    	}
    
    	uint8_t recvbyte = msg.getByte();
    
    	if (!player) {
    		if (recvbyte == 0x0F) {
    			disconnect();
    		}
    
    		return;
    	}
    
    	//a dead player can not performs actions
    	if (player->isRemoved() || player->getHealth() <= 0) {
    		if (recvbyte == 0x0F) {
    			disconnect();
    			return;
    		}
    
    		if (recvbyte != 0x14) {
    			return;
    		}
    	}
    
    	switch (recvbyte) {
    		case 0x14: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); break;
    		case 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 0x77: parseEquipObject(msg); 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: parseRuleViolationReport(msg); 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();
    	}
    }
    
    void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg)
    {
    	msg.add<uint16_t>(0x00); //environmental effects
    
    	int32_t count;
    	Item* ground = tile->getGround();
    	if (ground) {
    		msg.addItem(ground);
    		count = 1;
    	} else {
    		count = 0;
    	}
    
    	const TileItemVector* items = tile->getItemList();
    	if (items) {
    		for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
    			msg.addItem(*it);
    
    			count++;
    			if (count == 9 && tile->getPosition() == player->getPosition()) {
    				break;
    			} else if (count == 10) {
    				return;
    			}
    		}
    	}
    
    	const CreatureVector* creatures = tile->getCreatures();
    	if (creatures) {
    		bool playerAdded = false;
    		for (const Creature* creature : boost::adaptors::reverse(*creatures)) {
    			if (!player->canSeeCreature(creature)) {
    				continue;
    			}
    
    			if (tile->getPosition() == player->getPosition() && count == 9 && !playerAdded) {
    				creature = player;
    			}
    
    			if (creature->getID() == player->getID()) {
    				playerAdded = true;
    			}
    
    			bool known;
    			uint32_t removedKnown;
    			checkCreatureAsKnown(creature->getID(), known, removedKnown);
    			AddCreature(msg, creature, known, removedKnown);
    
    			if (++count == 10) {
    				return;
    			}
    		}
    	}
    
    	if (items) {
    		for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
    			msg.addItem(*it);
    
    			if (++count == 10) {
    				return;
    			}
    		}
    	}
    }
    
    void ProtocolGame::GetMapDescription(int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, NetworkMessage& msg)
    {
    	int32_t skip = -1;
    	int32_t startz, endz, zstep;
    
    	if (z > 7) {
    		startz = z - 2;
    		endz = std::min<int32_t>(MAP_MAX_LAYERS - 1, z + 2);
    		zstep = 1;
    	} else {
    		startz = 7;
    		endz = 0;
    		zstep = -1;
    	}
    
    	for (int32_t nz = startz; nz != endz + zstep; nz += zstep) {
    		GetFloorDescription(msg, x, y, nz, width, height, z - nz, skip);
    	}
    
    	if (skip >= 0) {
    		msg.addByte(skip);
    		msg.addByte(0xFF);
    	}
    }
    
    void ProtocolGame::GetFloorDescription(NetworkMessage& msg, int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, int32_t offset, int32_t& skip)
    {
    	for (int32_t nx = 0; nx < width; nx++) {
    		for (int32_t ny = 0; ny < height; ny++) {
    			Tile* tile = g_game.map.getTile(x + nx + offset, y + ny + offset, z);
    			if (tile) {
    				if (skip >= 0) {
    					msg.addByte(skip);
    					msg.addByte(0xFF);
    				}
    
    				skip = 0;
    				GetTileDescription(tile, msg);
    			} else if (skip == 0xFE) {
    				msg.addByte(0xFF);
    				msg.addByte(0xFF);
    				skip = -1;
    			} else {
    				++skip;
    			}
    		}
    	}
    }
    
    void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& removedKnown)
    {
    	auto result = knownCreatureSet.insert(id);
    	if (!result.second) {
    		known = true;
    		return;
    	}
    
    	known = false;
    
    	if (knownCreatureSet.size() > 1300) {
    		// Look for a creature to remove
    		for (auto it = knownCreatureSet.begin(), end = knownCreatureSet.end(); it != end; ++it) {
    			Creature* creature = g_game.getCreatureByID(*it);
    			if (!canSee(creature)) {
    				removedKnown = *it;
    				knownCreatureSet.erase(it);
    				return;
    			}
    		}
    
    		// Bad situation. Let's just remove anyone.
    		auto it = knownCreatureSet.begin();
    		if (*it == id) {
    			++it;
    		}
    
    		removedKnown = *it;
    		knownCreatureSet.erase(it);
    	} else {
    		removedKnown = 0;
    	}
    }
    
    bool ProtocolGame::canSee(const Creature* c) const
    {
    	if (!c || !player || c->isRemoved()) {
    		return false;
    	}
    
    	if (!player->canSeeCreature(c)) {
    		return false;
    	}
    
    	return canSee(c->getPosition());
    }
    
    bool ProtocolGame::canSee(const Position& pos) const
    {
    	return canSee(pos.x, pos.y, pos.z);
    }
    
    bool ProtocolGame::canSee(int32_t x, int32_t y, int32_t z) const
    {
    	if (!player) {
    		return false;
    	}
    
    	const Position& myPos = player->getPosition();
    	if (myPos.z <= 7) {
    		//we are on ground level or above (7 -> 0)
    		//view is from 7 -> 0
    		if (z > 7) {
    			return false;
    		}
    	} else if (myPos.z >= 8) {
    		//we are underground (8 -> 15)
    		//view is +/- 2 from the floor we stand on
    		if (std::abs(myPos.getZ() - z) > 2) {
    			return false;
    		}
    	}
    
    	//negative offset means that the action taken place is on a lower floor than ourself
    	int32_t offsetz = myPos.getZ() - z;
    	if ((x >= myPos.getX() - Map::maxClientViewportX + offsetz) && (x <= myPos.getX() + (Map::maxClientViewportX+1) + offsetz) &&
         (y >= myPos.getY() - Map::maxClientViewportY + offsetz) && (y <= myPos.getY() + (Map::maxClientViewportY+1) + offsetz)) {
    		return true;
    	}
    	return false;
    }
    
    // Parse methods
    void ProtocolGame::parseChannelInvite(NetworkMessage& msg)
    {
    	const std::string name = msg.getString();
    	addGameTask(&Game::playerChannelInvite, player->getID(), name);
    }
    
    void ProtocolGame::parseChannelExclude(NetworkMessage& msg)
    {
    	const std::string name = msg.getString();
    	addGameTask(&Game::playerChannelExclude, player->getID(), name);
    }
    
    void ProtocolGame::parseOpenChannel(NetworkMessage& msg)
    {
    	uint16_t channelId = msg.get<uint16_t>();
    	addGameTask(&Game::playerOpenChannel, player->getID(), channelId);
    }
    
    void ProtocolGame::parseCloseChannel(NetworkMessage& msg)
    {
    	uint16_t channelId = msg.get<uint16_t>();
    	addGameTask(&Game::playerCloseChannel, player->getID(), channelId);
    }
    
    void ProtocolGame::parseOpenPrivateChannel(NetworkMessage& msg)
    {
    	const std::string receiver = msg.getString();
    	addGameTask(&Game::playerOpenPrivateChannel, player->getID(), receiver);
    }
    
    void ProtocolGame::parseAutoWalk(NetworkMessage& msg)
    {
    	uint8_t numdirs = msg.getByte();
    	if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 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
    
    	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, rawChaseMode != 0, 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::parseEquipObject(NetworkMessage& msg)
    {
    	uint16_t spriteId = msg.get<uint16_t>();
    	// msg.get<uint8_t>();
    
    	addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerEquipItem, player->getID(), spriteId);
    }
    
    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::parseRuleViolationReport(NetworkMessage& msg)
    {
    	uint8_t reportType = msg.getByte();
    	uint8_t reportReason = msg.getByte();
    	const std::string& targetName = msg.getString();
    	const std::string& comment = msg.getString();
    	std::string translation;
    	if (reportType == REPORT_TYPE_NAME) {
    		translation = msg.getString();
    	} else if (reportType == REPORT_TYPE_STATEMENT) {
    		translation = msg.getString();
    		msg.get<uint32_t>(); // statement id, used to get whatever player have said, we don't log that.
    	}
    
    	addGameTask(&Game::playerReportRuleViolation, player->getID(), targetName, reportType, reportReason, comment, translation);
    }
    
    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 (debugAssertSent) {
    		return;
    	}
    
    	debugAssertSent = true;
    
    	std::string assertLine = msg.getString();
    	std::string date = msg.getString();
    	std::string description = msg.getString();
    	std::string comment = msg.getString();
    	addGameTask(&Game::playerDebugAssert, player->getID(), assertLine, date, description, comment);
    }
    
    void ProtocolGame::parseInviteToParty(NetworkMessage& msg)
    {
    	uint32_t targetId = msg.get<uint32_t>();
    	addGameTask(&Game::playerInviteToParty, player->getID(), targetId);
    }
    
    void ProtocolGame::parseJoinParty(NetworkMessage& msg)
    {
    	uint32_t targetId = msg.get<uint32_t>();
    	addGameTask(&Game::playerJoinParty, player->getID(), targetId);
    }
    
    void ProtocolGame::parseRevokePartyInvite(NetworkMessage& msg)
    {
    	uint32_t targetId = msg.get<uint32_t>();
    	addGameTask(&Game::playerRevokePartyInvitation, player->getID(), targetId);
    }
    
    void ProtocolGame::parsePassPartyLeadership(NetworkMessage& msg)
    {
    	uint32_t targetId = msg.get<uint32_t>();
    	addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId);
    }
    
    void ProtocolGame::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::sendCreatureLight(const Creature* creature)
    {
    	if (!canSee(creature)) {
    		return;
    	}
    
    	NetworkMessage msg;
    	AddCreatureLight(msg, creature);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendWorldLight(LightInfo lightInfo)
    {
    	NetworkMessage msg;
    	AddWorldLight(msg, lightInfo);
    	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::sendStats()
    {
    	NetworkMessage msg;
    	AddPlayerStats(msg);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendBasicData()
    {
    	NetworkMessage msg;
    	msg.addByte(0x9F);
    	if (player->isPremium()) {
    		msg.addByte(1);
    		msg.add<uint32_t>(time(nullptr) + (player->premiumDays * 86400));
    	} else {
    		msg.addByte(0);
    		msg.add<uint32_t>(0);
    	}
    	msg.addByte(player->getVocation()->getClientId());
    	msg.add<uint16_t>(0xFF); // number of known spells
    	for (uint8_t spellId = 0x00; spellId < 0xFF; spellId++) {
    		msg.addByte(spellId);
    	}
    	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;
    		}
    		case MESSAGE_GUILD:
    		case MESSAGE_PARTY_MANAGEMENT:
    		case MESSAGE_PARTY:
    			msg.add<uint16_t>(message.channelId);
    			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::sendChannel(uint16_t channelId, const std::string& channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers)
    {
    	NetworkMessage msg;
    	msg.addByte(0xAC);
    
    	msg.add<uint16_t>(channelId);
    	msg.addString(channelName);
    
    	if (channelUsers) {
    		msg.add<uint16_t>(channelUsers->size());
    		for (const auto& it : *channelUsers) {
    			msg.addString(it.second->getName());
    		}
    	} else {
    		msg.add<uint16_t>(0x00);
    	}
    
    	if (invitedUsers) {
    		msg.add<uint16_t>(invitedUsers->size());
    		for (const auto& it : *invitedUsers) {
    			msg.addString(it.second->getName());
    		}
    	} else {
    		msg.add<uint16_t>(0x00);
    	}
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel)
    {
    	NetworkMessage msg;
    	msg.addByte(0xAA);
    	msg.add<uint32_t>(0x00);
    	msg.addString(author);
    	msg.add<uint16_t>(0x00);
    	msg.addByte(type);
    	msg.add<uint16_t>(channel);
    	msg.addString(text);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendIcons(uint16_t icons)
    {
    	NetworkMessage msg;
    	msg.addByte(0xA2);
    	msg.add<uint16_t>(icons);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool hasParent, uint16_t firstIndex)
    {
    	NetworkMessage msg;
    	msg.addByte(0x6E);
    
    	msg.addByte(cid);
    
    	if (container->getID() == ITEM_BROWSEFIELD) {
    		msg.addItem(1987, 1);
    		msg.addString("Browse Field");
    	} else {
    		msg.addItem(container);
    		msg.addString(container->getName());
    	}
    
    	msg.addByte(container->capacity());
    
    	msg.addByte(hasParent ? 0x01 : 0x00);
    
    	msg.addByte(container->isUnlocked() ? 0x01 : 0x00); // Drag and drop
    	msg.addByte(container->hasPagination() ? 0x01 : 0x00); // Pagination
    
    	uint32_t containerSize = container->size();
    	msg.add<uint16_t>(containerSize);
    	msg.add<uint16_t>(firstIndex);
    	if (firstIndex < containerSize) {
    		uint8_t itemsToSend = std::min<uint32_t>(std::min<uint32_t>(container->capacity(), containerSize - firstIndex), std::numeric_limits<uint8_t>::max());
    
    		msg.addByte(itemsToSend);
    		for (auto it = container->getItemList().begin() + firstIndex, end = it + itemsToSend; it != end; ++it) {
    			msg.addItem(*it);
    		}
    	} else {
    		msg.addByte(0x00);
    	}
    	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 (auto 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() + player->getBankBalance());
    
    	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(c);
    				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 (auto 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 (auto 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[i] == 0) {
    				continue;
    			}
    
    			if (separator) {
    				ss << ", ";
    			} else {
    				separator = true;
    			}
    
    			ss << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << 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[i]) {
    				continue;
    			}
    
    			if (separator) {
    				ss << ", ";
    			} else {
    				separator = true;
    			}
    
    			ss << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << 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::sendCancelWalk()
    {
    	NetworkMessage msg;
    	msg.addByte(0xB5);
    	msg.addByte(player->getDirection());
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendSkills()
    {
    	NetworkMessage msg;
    	AddPlayerSkills(msg);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendPing()
    {
    	NetworkMessage msg;
    	msg.addByte(0x1D);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendPingBack()
    {
    	NetworkMessage msg;
    	msg.addByte(0x1E);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendDistanceShoot(const Position& from, const Position& to, uint8_t type)
    {
    	NetworkMessage msg;
    	msg.addByte(0x85);
    	msg.addPosition(from);
    	msg.addPosition(to);
    	msg.addByte(type);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendMagicEffect(const Position& pos, uint8_t type)
    {
    	if (!canSee(pos)) {
    		return;
    	}
    
    	NetworkMessage msg;
    	msg.addByte(0x83);
    	msg.addPosition(pos);
    	msg.addByte(type);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendCreatureHealth(const Creature* creature)
    {
    	NetworkMessage msg;
    	msg.addByte(0x8C);
    	msg.add<uint32_t>(creature->getID());
    
    	if (creature->isHealthHidden()) {
    		msg.addByte(0x00);
    	} else {
    		msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100));
    	}
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendFYIBox(const std::string& message)
    {
    	NetworkMessage msg;
    	msg.addByte(0x15);
    	msg.addString(message);
    	writeToOutputBuffer(msg);
    }
    
    //tile
    void ProtocolGame::sendMapDescription(const Position& pos)
    {
    	NetworkMessage msg;
    	msg.addByte(0x64);
    	msg.addPosition(player->getPosition());
    	GetMapDescription(pos.x - Map::maxClientViewportX, pos.y - Map::maxClientViewportY, pos.z, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, msg);
    	writeToOutputBuffer(msg);
    }
    
    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::sendUpdateTile(const Tile* tile, const Position& pos)
    {
    	if (!canSee(pos)) {
    		return;
    	}
    
    	NetworkMessage msg;
    	msg.addByte(0x69);
    	msg.addPosition(pos);
    
    	if (tile) {
    		GetTileDescription(tile, msg);
    		msg.addByte(0x00);
    		msg.addByte(0xFF);
    	} else {
    		msg.addByte(0x01);
    		msg.addByte(0xFF);
    	}
    
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendPendingStateEntered()
    {
    	NetworkMessage msg;
    	msg.addByte(0x0A);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendEnterWorld()
    {
    	NetworkMessage msg;
    	msg.addByte(0x0F);
    	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);
    }
    
    void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos, int32_t stackpos, bool isLogin)
    {
    	if (!canSee(pos)) {
    		return;
    	}
    
    	if (creature != player) {
    		if (stackpos != -1) {
    			NetworkMessage msg;
    			msg.addByte(0x6A);
    			msg.addPosition(pos);
    			msg.addByte(stackpos);
    
    			bool known;
    			uint32_t removedKnown;
    			checkCreatureAsKnown(creature->getID(), known, removedKnown);
    			AddCreature(msg, creature, known, removedKnown);
    			writeToOutputBuffer(msg);
    		}
    
    		if (isLogin) {
    			sendMagicEffect(pos, CONST_ME_TELEPORT);
    		}
    		return;
    	}
    
    	NetworkMessage msg;
    	msg.addByte(0x17);
    
    	msg.add<uint32_t>(player->getID());
    	msg.add<uint16_t>(0x32); // beat duration (50)
    
    	msg.addDouble(Creature::speedA, 3);
    	msg.addDouble(Creature::speedB, 3);
    	msg.addDouble(Creature::speedC, 3);
    
    	// can report bugs?
    	if (player->getAccountType() >= ACCOUNT_TYPE_TUTOR) {
    		msg.addByte(0x01);
    	} else {
    		msg.addByte(0x00);
    	}
    
    	msg.addByte(0x00); // can change pvp framing option
    	msg.addByte(0x00); // expert mode button enabled
    
    	msg.add<uint16_t>(0x00); // URL (string) to ingame store images
    	msg.add<uint16_t>(25); // premium coin package size
    
    	writeToOutputBuffer(msg);
    
    	sendPendingStateEntered();
    	sendEnterWorld();
    	sendMapDescription(pos);
    
    	if (isLogin) {
    		sendMagicEffect(pos, CONST_ME_TELEPORT);
    	}
    
    	for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
    		sendInventoryItem(static_cast<slots_t>(i), player->getInventoryItem(static_cast<slots_t>(i)));
    	}
    
    	sendStats();
    	sendSkills();
    
    	//gameworld light-settings
    	sendWorldLight(g_game.getWorldLightInfo());
    
    	//player light level
    	sendCreatureLight(creature);
    
    	sendVIPEntries();
    
    	sendBasicData();
    	player->sendIcons();
    }
    
    void ProtocolGame::sendMoveCreature(const Creature* creature, const Position& newPos, int32_t newStackPos, const Position& oldPos, int32_t oldStackPos, bool teleport)
    {
    	if (creature == player) {
    		if (oldStackPos >= 10) {
    			sendMapDescription(newPos);
    		} else if (teleport) {
    			NetworkMessage msg;
    			RemoveTileThing(msg, oldPos, oldStackPos);
    			writeToOutputBuffer(msg);
    			sendMapDescription(newPos);
    		} else {
    			NetworkMessage msg;
    			if (oldPos.z == 7 && newPos.z >= 8) {
    				RemoveTileThing(msg, oldPos, oldStackPos);
    			} else {
    				msg.addByte(0x6D);
    				msg.addPosition(oldPos);
    				msg.addByte(oldStackPos);
    				msg.addPosition(newPos);
    			}
    
    			if (newPos.z > oldPos.z) {
    				MoveDownCreature(msg, creature, newPos, oldPos);
    			} else if (newPos.z < oldPos.z) {
    				MoveUpCreature(msg, creature, newPos, oldPos);
    			}
    
    			if (oldPos.y > newPos.y) { // north, for old x
       msg.addByte(0x65);
       GetMapDescription(oldPos.x - Map::maxClientViewportX, newPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX+1)*2, 1, msg);
    } else if (oldPos.y < newPos.y) { // south, for old x
       msg.addByte(0x67);
       GetMapDescription(oldPos.x - Map::maxClientViewportX, newPos.y + (Map::maxClientViewportY+1), newPos.z, (Map::maxClientViewportX+1)*2, 1, msg);
    }
    if (oldPos.x < newPos.x) { // east, [with new y]
       msg.addByte(0x66);
       GetMapDescription(newPos.x + (Map::maxClientViewportX+1), newPos.y - Map::maxClientViewportY, newPos.z, 1, (Map::maxClientViewportY+1)*2, msg);
    } else if (oldPos.x > newPos.x) { // west, [with new y]
       msg.addByte(0x68);
       GetMapDescription(newPos.x - Map::maxClientViewportX, newPos.y - Map::maxClientViewportY, newPos.z, 1, (Map::maxClientViewportY+1)*2, msg);
    			}
    			writeToOutputBuffer(msg);
    		}
    	} else if (canSee(oldPos) && canSee(creature->getPosition())) {
    		if (teleport || (oldPos.z == 7 && newPos.z >= 8) || oldStackPos >= 10) {
    			sendRemoveTileThing(oldPos, oldStackPos);
    			sendAddCreature(creature, newPos, newStackPos, false);
    		} else {
    			NetworkMessage msg;
    			msg.addByte(0x6D);
    			msg.addPosition(oldPos);
    			msg.addByte(oldStackPos);
    			msg.addPosition(creature->getPosition());
    			writeToOutputBuffer(msg);
    		}
    	} else if (canSee(oldPos)) {
    		sendRemoveTileThing(oldPos, oldStackPos);
    	} else if (canSee(creature->getPosition())) {
    		sendAddCreature(creature, newPos, newStackPos, false);
    	}
    }
    
    void ProtocolGame::sendInventoryItem(slots_t slot, const Item* item)
    {
    	NetworkMessage msg;
    	if (item) {
    		msg.addByte(0x78);
    		msg.addByte(slot);
    		msg.addItem(item);
    	} else {
    		msg.addByte(0x79);
    		msg.addByte(slot);
    	}
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendItems()
    {
    	NetworkMessage msg;
    	msg.addByte(0xF5);
    
    	const std::vector<uint16_t>& inventory = Item::items.getInventory();
    	msg.add<uint16_t>(inventory.size() + 11);
    	for (uint16_t i = 1; i <= 11; i++) {
    		msg.add<uint16_t>(i);
    		msg.addByte(0); //always 0
    		msg.add<uint16_t>(1); // always 1
    	}
    
    	for (auto clientId : inventory) {
    		msg.add<uint16_t>(clientId);
    		msg.addByte(0); //always 0
    		msg.add<uint16_t>(1);
    	}
    
    	writeToOutputBuffer(msg);
    }
    
    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 100 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::sendVIP(uint32_t guid, const std::string& name, const std::string& description, uint32_t icon, bool notify, VipStatus_t status)
    {
    	NetworkMessage msg;
    	msg.addByte(0xD2);
    	msg.add<uint32_t>(guid);
    	msg.addString(name);
    	msg.addString(description);
    	msg.add<uint32_t>(std::min<uint32_t>(10, icon));
    	msg.addByte(notify ? 0x01 : 0x00);
    	msg.addByte(status);
    	writeToOutputBuffer(msg);
    }
    
    void ProtocolGame::sendVIPEntries()
    {
    	const std::forward_list<VIPEntry>& vipEntries = IOLoginData::getVIPEntries(player->getAccount());
    
    	for (const VIPEntry& entry : vipEntries) {
    		VipStatus_t vipStatus = VIPSTATUS_ONLINE;
    
    		Player* vipPlayer = g_game.getPlayerByGUID(entry.guid);
    
    		if (!vipPlayer || vipPlayer->isInGhostMode() || player->isAccessPlayer()) {
    			vipStatus = VIPSTATUS_OFFLINE;
    		}
    
    		sendVIP(entry.guid, entry.name, entry.description, entry.icon, entry.notify, vipStatus);
    	}
    }
    
    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::AddCreature(NetworkMessage& msg, const Creature* creature, bool known, uint32_t remove)
    {
    	CreatureType_t creatureType = creature->getType();
    
    	const Player* otherPlayer = creature->getPlayer();
    
    	if (known) {
    		msg.add<uint16_t>(0x62);
    		msg.add<uint32_t>(creature->getID());
    	} else {
    		msg.add<uint16_t>(0x61);
    		msg.add<uint32_t>(remove);
    		msg.add<uint32_t>(creature->getID());
    		msg.addByte(creatureType);
    		msg.addString(creature->getName());
    	}
    
    	if (creature->isHealthHidden()) {
    		msg.addByte(0x00);
    	} else {
    		msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100));
    	}
    
    	msg.addByte(creature->getDirection());
    
    	if (!creature->isInGhostMode() && !creature->isInvisible()) {
    		AddOutfit(msg, creature->getCurrentOutfit());
    	} else {
    		static Outfit_t outfit;
    		AddOutfit(msg, outfit);
    	}
    
    	LightInfo lightInfo = creature->getCreatureLight();
    	msg.addByte(player->isAccessPlayer() ? 0xFF : lightInfo.level);
    	msg.addByte(lightInfo.color);
    
    	msg.add<uint16_t>(creature->getStepSpeed() / 2);
    
    	msg.addByte(player->getSkullClient(creature));
    	msg.addByte(player->getPartyShield(otherPlayer));
    
    	if (!known) {
    		msg.addByte(player->getGuildEmblem(otherPlayer));
    	}
    
    	if (creatureType == CREATURETYPE_MONSTER) {
    		const Creature* master = creature->getMaster();
    		if (master) {
    			const Player* masterPlayer = master->getPlayer();
    			if (masterPlayer) {
    				if (masterPlayer == player) {
    					creatureType = CREATURETYPE_SUMMON_OWN;
    				} else {
    					creatureType = CREATURETYPE_SUMMON_OTHERS;
    				}
    			}
    		}
    	}
    
    	msg.addByte(creatureType); // Type (for summons)
    	msg.addByte(creature->getSpeechBubble());
    	msg.addByte(0xFF); // MARK_UNMARKED
    
    	if (otherPlayer) {
    		msg.add<uint16_t>(otherPlayer->getHelpers());
    	} else {
    		msg.add<uint16_t>(0x00);
    	}
    
    	msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01);
    }
    
    void ProtocolGame::AddPlayerStats(NetworkMessage& msg)
    {
    	msg.addByte(0xA0);
    
    	msg.add<uint16_t>(std::min<int32_t>(player->getHealth(), std::numeric_limits<uint16_t>::max()));
    	msg.add<uint16_t>(std::min<int32_t>(player->getMaxHealth(), std::numeric_limits<uint16_t>::max()));
    
    	msg.add<uint32_t>(player->getFreeCapacity());
    	msg.add<uint32_t>(player->getCapacity());
    
    	msg.add<uint64_t>(player->getExperience());
    
    	msg.add<uint16_t>(player->getLevel());
    	msg.addByte(player->getLevelPercent());
    
    	msg.add<uint16_t>(100); // base xp gain rate
    	msg.add<uint16_t>(0); // xp voucher
    	msg.add<uint16_t>(0); // low level bonus
    	msg.add<uint16_t>(0); // xp boost
    	msg.add<uint16_t>(100); // stamina multiplier (100 = x1.0)
    
    	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->getMaxMana(), std::numeric_limits<uint16_t>::max()));
    
    	msg.addByte(std::min<uint32_t>(player->getMagicLevel(), std::numeric_limits<uint8_t>::max()));
    	msg.addByte(std::min<uint32_t>(player->getBaseMagicLevel(), std::numeric_limits<uint8_t>::max()));
    	msg.addByte(player->getMagicLevelPercent());
    
    	msg.addByte(player->getSoul());
    
    	msg.add<uint16_t>(player->getStaminaMinutes());
    
    	msg.add<uint16_t>(player->getBaseSpeed() / 2);
    
    	Condition* condition = player->getCondition(CONDITION_REGENERATION);
    	msg.add<uint16_t>(condition ? condition->getTicks() / 1000 : 0x00);
    
    	msg.add<uint16_t>(player->getOfflineTrainingTime() / 60 / 1000);
    
    	msg.add<uint16_t>(0); // xp boost time (seconds)
    	msg.addByte(0); // enables exp boost in the store
    }
    
    void ProtocolGame::AddPlayerSkills(NetworkMessage& msg)
    {
    	msg.addByte(0xA1);
    
    	for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) {
    		msg.add<uint16_t>(std::min<int32_t>(player->getSkillLevel(i), std::numeric_limits<uint16_t>::max()));
    		msg.add<uint16_t>(player->getBaseSkill(i));
    		msg.addByte(player->getSkillPercent(i));
    	}
    
    	for (uint8_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) {
    		msg.add<uint16_t>(std::min<int32_t>(100, player->varSpecialSkills[i]));
    		msg.add<uint16_t>(0);
    	}
    }
    
    void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit)
    {
    	msg.add<uint16_t>(outfit.lookType);
    
    	if (outfit.lookType != 0) {
    		msg.addByte(outfit.lookHead);
    		msg.addByte(outfit.lookBody);
    		msg.addByte(outfit.lookLegs);
    		msg.addByte(outfit.lookFeet);
    		msg.addByte(outfit.lookAddons);
    	} else {
    		msg.addItemId(outfit.lookTypeEx);
    	}
    
    	msg.add<uint16_t>(outfit.lookMount);
    }
    
    void ProtocolGame::AddWorldLight(NetworkMessage& msg, LightInfo lightInfo)
    {
    	msg.addByte(0x82);
    	msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level));
    	msg.addByte(lightInfo.color);
    }
    
    void ProtocolGame::AddCreatureLight(NetworkMessage& msg, const Creature* creature)
    {
    	LightInfo lightInfo = creature->getCreatureLight();
    
    	msg.addByte(0x8D);
    	msg.add<uint32_t>(creature->getID());
    	msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level));
    	msg.addByte(lightInfo.color);
    }
    
    //tile
    void ProtocolGame::RemoveTileThing(NetworkMessage& msg, const Position& pos, uint32_t stackpos)
    {
    	if (stackpos >= 10) {
    		return;
    	}
    
    	msg.addByte(0x6C);
    	msg.addPosition(pos);
    	msg.addByte(stackpos);
    }
    
    void ProtocolGame::MoveUpCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos)
    {
    	if (creature != player) {
    		return;
    	}
    
    	//floor change up
    	msg.addByte(0xBE);
    
    	//going to surface
    	if (newPos.z == 7) {
       int32_t skip = -1;
       GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 5, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 3, skip); //(floor 7 and 6 already set)
       GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 4, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 4, skip);
       GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 3, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 5, skip);
       GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 2, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 6, skip);
       GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 1, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 7, skip);
       GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 0, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 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 - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, oldPos.getZ() - 3, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 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 - Map::maxClientViewportX, oldPos.y - (Map::maxClientViewportY-1), newPos.z, 1, (Map::maxClientViewportY+1)*2, msg);
    //north
    msg.addByte(0x65);
    GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX+1)*2, 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 - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -1, skip);
       GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 1, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -2, skip);
       GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 2, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -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 - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 2, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -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 + Map::maxClientViewportX+1, oldPos.y - (Map::maxClientViewportY+1), newPos.z, 1, ((Map::maxClientViewportY+1)*2), msg);
    //south
    msg.addByte(0x67);
    GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y + (Map::maxClientViewportY+1), newPos.z, ((Map::maxClientViewportX+1)*2), 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);
    }

     

  6. Galera estou enfrentando um problema no meu site na hora de criação de conta

    Já procurei em tudo que é lugar, acontece qnd eu desativo os inicias do site com os iniciais ativos a conta cria 100%, mas qnd eu desativo da isso ai + a conta cria..

    quem poder me ajudar urgente eu pago

     

    wpp : 85991761786 

    Discord AlaOGaspar

    #7077

     

    Error: Incorrect integer value: '' for column 'key' at row 1

     

     

     

     

  7. Em 23/04/2020 em 21:57, Everaldo Woopz disse:

    Nao achei o arquivo, é normal? 

    
    PermitRootLogin no

    image.thumb.png.7688f3f4cc4ea3aea693594fc19d29ab.png

    eu to com esse mesmo problema :x mas, nao consigo conectar no bit, eu consigo colocar o user e a senha

    Enter new UNIX password:

    Retype new UNIX password:

    passwd: password updated successfully

     

    mas não da em nada no bit, ele pede minha senha eu coloco e fala pra colocar denovo e fica nessa

  8. 1 minuto atrás, Carlos Dukka disse:

    tem 3 mil arquivos dps vo passa o de 7 agora o movimento e so preocurar que axa estavtudo cortado e separado tem q ir juntando

     

    não teria só a spr e dat? mais pratico.

  9. Fala ae galera beleza?! Bom antes de tudo eu quero me apresentar para vocês, eu me chamo Mikael ( conhecido como alaogaspar no youtube ), tenho 22 anos e sou do ceará, jogo poketibia desde 2013, 1014, por aí nem lembro mais, sou mapper a um bom tempo, e também busco sempre aprender sobre mais coisas, trabalho, e recentemente estou a iniciar uma loja virtual, mas meu sonho sempre foi ter um projeto de poketibia, onde não tenha isso de P2W o famoso pay to win, pois todo servidor hoje em dia é assim... e eu tenho um projeto a um bom tempo, criei o mapa do zero, e ainda estou a editar esse mapa... e estou contratando algumas pessoas que querem ser da staff e me ajudar a abrir esse servidor o mais rápido possível, pois tenho uma vps dedicada e um domínio já e quero muito abrir esse servidor, então do que eu preciso ? 

    imageproxy.png.1d2ed83a6fd96b0a6886dfa397733d76.png

     

    Antes de tudo fiquem com algumas imagens do servidor, lembrando que ainda estou editando algumas partes.

    Spoiler

    3.thumb.png.57ff4d5e31279781f5c3befd6dde71c5.png2.thumb.png.08041ed3690a22e86fcdfc4ab038358c.png1.thumb.png.11a5d3aa324c37aeba4a662cb34d914b.png10.thumb.png.a8f2fa5a0d870b7b4ceebaf9756d67f0.png9.thumb.png.d3e04fc777bbdff84a66cd95ff2e9543.png8.thumb.png.57dadf80f28dd4d254513430abc785e7.png7.thumb.png.4d4ff7a5e221768deb6a388d2c39efd3.png6.thumb.png.24d433efae5f223078016650ebc58717.png5.thumb.png.59fcf8fef14cfd1ea8513655764425f7.png4.thumb.png.c614eaf933c63fdb967112edb053300f.png

     

    Necessito de: 

    Programador Lua/C++, 

    Web Designer

    OTC Maker

     

    Requisitos:

    Ser maior de 17 anos, ter no minimo 2 horas disponível por dia, ter uma boa comunicação, educação e ser de confiança.

     

    Benefícios:

    A mais eu vou trabalhar de graça ? vou ganhar o que ?

    Bom, além de ter um canto na minha staff, você será recompensado por trabalhos, claro que não muito... mas o bastante para conseguimos manter o servidor.

     

    está interessado? mande-me mensagem no wpp ( 85994142350) 

    não está conseguindo?  preencha isso aqui.
     

    • Nome Completo:
    • Idade:
    • Escolaridade:
    • Whatsapp para Contato:
    • Experiencia:
    • Conhecimentos no Otserv:

     

    Aguardando by : AlaOGaspar

  10. Galera, alguém poderia me ajudar ? recentemente adicionei um sistema de surf no meu servidor, agora ele tá funfando de boa, mas algumas vezes que entro na água ele dá esse erro "[Error - MoveEvent::executeStep] Call stack overflow. "  aí o char trava e desloga, rep+ pra quem me ajudar, meus sistemas.

    movements.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <movements>
    
    
    
    
    <movevent type="StepIn" actionid="54545" event="script" value="portacp.lua"/>
    <movevent type="StepIn" actionid="54333" event="script" value="tpfishing.lua"/>
    <movevent type="StepOut" actionid="54545" event="script" value="portacp.lua"/>
    <movevent type="StepIn" actionid="54555" event="script" value="portapredioverde.lua"/>
    <movevent type="StepOut" actionid="54555" event="script" value="portapredioverde.lua"/>
    <movevent type="StepIn" actionid="54546" event="script" value="portacp2.lua"/>
    <movevent type="StepOut" actionid="54546" event="script" value="portacp2.lua"/>
    <movevent type="StepIn" actionid="54547" event="script" value="portashop.lua"/>
    <movevent type="StepOut" actionid="54547" event="script" value="portashop.lua"/>
    <movevent type="StepIn" actionid="54548" event="script" value="portatrade.lua"/>
    <movevent type="StepOut" actionid="54548" event="script" value="portatrade.lua"/>
    <movevent type="StepIn" actionid="54549" event="script" value="portalavender.lua"/>
    <movevent type="StepOut" actionid="54549" event="script" value="portalavender.lua"/>
    <movevent type="StepIn" actionid="54550" event="script" value="portashoplavender.lua"/>
    <movevent type="StepOut" actionid="54550" event="script" value="portashoplavender.lua"/>
    <movevent type="StepIn" actionid="54551" event="script" value="portatradelavender.lua"/>
    <movevent type="StepOut" actionid="54551" event="script" value="portatradelavender.lua"/>
    <movevent type="StepIn" actionid="54552" event="script" value="portatradesaffron1.lua"/>
    <movevent type="StepOut" actionid="54552" event="script" value="portatradesaffron1.lua"/>
    <movevent type="StepIn" actionid="54553" event="script" value="portatradesaffron2.lua"/>
    <movevent type="StepOut" actionid="54553" event="script" value="portatradesaffron2.lua"/>
    
    
    
    -- Icone System
     	<movevent type="Equip" itemid="11826-11837;11737-11748;12826-12831;10975-10977;12621-12623;12861-13781;13797-13823;13836-13861;13902-13904;13919-13930;13933-13935;13937-13939;14020-14022;14015-14017;14033-14035;14038-14040;14043-14045;14141-14143;14157-14159;14163-14165;14173-14175;14081-14083;14190-14192;14178-14180;14193-14195;14199-14201;14202-14204;14205-14207;14208-14210;14211-14213;14216-14218;14219-14221;14222-14224;14227-14229;14232-14234;14237-14239;14242-14244;14247-14249;14254-14256;14258-14260;14261-14263;14264-14266;14267-14269;14270-14272;14273-14275;14276-14278;14280-14282;14285-14287;14290-14292;14294-14296;14300-14302;14305-14307;14310-14312;14315-14317;14320-14322;14325-14327;14330-14332;14335-14337;14340-14342;14345-14347;14355-14357;14360-14362;14365-14367;14370-14372;14375-14377;14381-14383;14386-14388;14391-14393;14396-14398;14086-14088;14091-14093;14106-14108;14111-14113;14121-14123;14126-14128;14101-14103;14401-14403;14406-14408;14411-14413;14416-14418;14421-14423;14426-14428;14431-14433;14436-14438;14442-14444;14447-14449;14451-14453;14455-14457;20561-20563;20564-20566;20567-20569;20570-20572;20573-20575;20576-20578;20580-20582;20584-20586;20589-20591;20595-20597;20600-20601;20605-20607;20610-20612;20680-20682;20685-20687;20695-20697;20700-20702;20705-20707;20710-20712;20714-20716;20725-20727;20730-20732;20740-20742;20751-20753;20754-20756;20758-20760;20763-20765;20766-20768;20770-20772;20773-20775;20777-20779;20782-20784;20787-20789;20791-20793;" slot="feet" event="script" value="portrait.lua"/>
     	<movevent type="DeEquip" itemid="11826-11837;11737-11748;12826-12831;10975-10977;12621-12623;12861-13781;13797-13823;13836-13861;13902-13904;13919-13930;13933-13935;13937-13939;14020-14022;14015-14017;14141-14143;14157-14159;14163-14165;14173-14175;14081-14083;14190-14192;14178-14180;14193-14195;14199-14201;14202-14204;14205-14207;14208-14210;14211-14213;14216-14218;14219-14221;14222-14224;14227-14229;14232-14234;14237-14239;14242-14244;14247-14249;14254-14256;14258-14260;14261-14263;14264-14266;14267-14269;14270-14272;14273-14275;14276-14278;14280-14282;14285-14287;14290-14292;14294-14296;14300-14302;14305-14307;14310-14312;14315-14317;14320-14322;14325-14327;14330-14332;14335-14337;14340-14342;14345-14347;14355-14357;14360-14362;14365-14367;14370-14372;14375-14377;14381-14383;14386-14388;14391-14393;14396-14398;14086-14088;14091-14093;14106-14108;14111-14113;14121-14123;14126-14128;14101-14103;14401-14403;14406-14408;14411-14413;14416-14418;14421-14423;14426-14428;14431-14433;14436-14438;14442-14444;14447-14449;14451-14453;14455-14457;20561-20563;20564-20566;20567-20569;20570-20572;20573-20575;20576-20578;20580-20582;20584-20586;20589-20591;20595-20597;20600-20601;20605-20607;20610-20612;20680-20682;20685-20687;20695-20697;20700-20702;20705-20707;20710-20712;20714-20716;20725-20727;20730-20732;20740-20742;20795-20797;" slot="feet" event="script" value="portrait.lua"/>
    
    -- Outland
    	<movevent type="StepIn" actionid="25007" event="script" value="outlandtile.lua"/>
    	<movevent type="StepIn" actionid="25000" event="script" value="outlandtile1.lua"/>
    
    	<movevent type="StepIn" actionid="23021" event="script" value="tiletpback.lua"/>
    	<movevent type="StepIn" actionid="23020" event="script" value="tiletp.lua"/>
    	<movevent type="StepIn" actionid="23022" event="script" value="tiletpelec.lua"/>
    	<movevent type="StepIn" actionid="23037" event="script" value="tiletpelecback.lua"/>
    	<movevent type="StepIn" actionid="23023" event="script" value="tiletpgengar.lua"/>
    	<movevent type="StepIn" actionid="23024" event="script" value="tiletpgengarback.lua"/>
    	<movevent type="StepIn" actionid="23025" event="script" value="tiletpprimeape.lua"/>
    	<movevent type="StepIn" actionid="23026" event="script" value="tiletpprimeapeback.lua"/>
    	<movevent type="StepIn" actionid="23027" event="script" value="tiletpgya.lua"/>
    	<movevent type="StepIn" actionid="23028" event="script" value="tiletpgyaback.lua"/>
    	<movevent type="StepIn" actionid="23029" event="script" value="tiletpvenusaur.lua"/>
    	<movevent type="StepIn" actionid="23030" event="script" value="tiletpvenusaurback.lua"/>
    	<movevent type="StepIn" actionid="23031" event="script" value="tiletppassaro.lua"/>
    	<movevent type="StepIn" actionid="23032" event="script" value="tiletppassaroback.lua"/>
    	<movevent type="StepIn" actionid="23033" event="script" value="tiletpsteelix.lua"/>
    	<movevent type="StepIn" actionid="23034" event="script" value="tiletpsteelixback.lua"/>
    	<movevent type="StepIn" actionid="23035" event="script" value="tiletphypno.lua"/>
    	<movevent type="StepIn" actionid="23036" event="script" value="tiletphypnoback.lua"/>
    	
    	
    	
    	<movevent type="StepIn" actionid="33691;33692;33693;33694;33695;33696;33697;3368;33699;33711;33712;33713;33714;33715;33716;33717;33718;33719;33720;33721;33722;33723;33724;33725" event="script" value="PVP/Trade_Go.lua"/>
    	<movevent type="StepIn" actionid="33799;33800-33808" event="script" value="PVP/Trade_Back.lua"/>
    	<movevent type="StepIn" actionid="12572-12575" event="script" value="PVP/PVP_tile.lua"/>	
    		<movevent type="StepIn" actionid="54322" event="script" value="tppvp.lua"/>	
    		<movevent type="StepIn" actionid="54323" event="script" value="tppvpback.lua"/>	
            <movevent type="StepIn" actionid="7001" event="script" value="tprandom.lua" />
            <movevent type="StepIn" actionid="7002-7004" event="script" value="tprandom1.lua" />
            <movevent type="StepIn" actionid="7005-7007" event="script" value="tprandom2.lua" />
    	<movevent type="StepIn" actionid="8192" event="script" value="evento tile.lua"/>
    	<movevent type="StepIn" actionid="59668" event="script" value="msg inicial.lua"/>
    	<movevent type="StepIn" actionid="59669" event="script" value="cordenadas iniciais.lua"/>
            <movevent type="StepIn" actionid="19456" event="script" value="premiumtile.lua" />
            <movevent type="StepIn" actionid="59997" event="script" value="stafftile.lua" />
    		<movevent type="stepIn" actionid="6859" event="script" value="hunt.lua" />
    
    
    	<movevent type="StepIn" itemid="11777-11785;1533-1542;11809-11811;5303-5304;1111-1119;873-878;2703;2767;1036-1048;6436-6439" event="script" value="blockwall.lua"/>
    
    
    	<!-- Decaying tiles -->
    	<movevent type="StepIn" itemid="293" event="script" value="decay.lua"/>
    	<movevent type="StepIn" itemid="461" event="script" value="decay.lua"/>
    
    	<movevent type="StepIn" actionid="7300" script="StorageTile.lua"/>
    	<movevent type="StepIn" actionid="7301" script="TileQuestInicial/TileHouseDelia.lua"/>
    	<movevent type="StepIn" actionid="7302" script="TileQuestInicial/TileHouseClickFood.lua"/>
            <movevent type="StepIn" actionid="7303" script="ReturnPOS.lua"/>
    	<movevent type="StepIn" actionid="7310" script="TileQuestInicial/Prof.RobertTILE.lua"/>
    
    	<!-- Underwater drown -->
    	<movevent type="StepIn" fromid="5405" toid="5410" event="script" value="drown.lua"/>
    	<movevent type="StepOut" fromid="5405" toid="5410" event="script" value="drown.lua"/>
    
    	<!-- (Depot & Level) tiles -->
    	<movevent type="StepIn" itemid="416" event="script" value="tiles.lua"/>
    	<movevent type="StepOut" itemid="417" event="script" value="tiles.lua"/>
    	<movevent type="StepIn" itemid="426" event="script" value="tiles.lua"/>
    	<movevent type="StepOut" itemid="425" event="script" value="tiles.lua"/>
    	<movevent type="StepIn" itemid="446" event="script" value="tiles.lua"/>
    	<movevent type="StepOut" itemid="447" event="script" value="tiles.lua"/>
    	<movevent type="StepIn" itemid="3216" event="script" value="tiles.lua"/>
    	<movevent type="StepOut" itemid="3217" event="script" value="tiles.lua"/>
    	<movevent type="StepIn" itemid="3202" event="script" value="tiles.lua"/>
    	<movevent type="StepOut" itemid="3215" event="script" value="tiles.lua"/>
    	<movevent type="StepIn" itemid="11059" event="script" value="tiles.lua"/>
    	<movevent type="StepOut" itemid="11060" event="script" value="tiles.lua"/>
    	<!-- <movevent type="StepIn" itemid="8714" event="script" value="tiles.lua"/> -->
    
    	<!-- Traps -->
    	<movevent type="StepIn" itemid="1510" event="script" value="trap.lua"/>
    	<movevent type="StepOut" itemid="1511" event="script" value="trap.lua"/>
    	<movevent type="StepIn" itemid="1512" event="script" value="trap.lua"/>
    	<movevent type="StepOut" itemid="1513" event="script" value="trap.lua"/>
    	<movevent type="StepIn" itemid="2579" event="script" value="trap.lua"/>
    	<movevent type="RemoveItem" itemid="2579" event="script" value="trap.lua"/>
    
    	<!-- Walkback when walking on quest chest -->
    	<!--<movevent type="StepIn" itemid="1738" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1740" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" fromid="1746" toid="1749" event="script" value="walkback.lua"/>-->
    
    	<!-- (Level & quest) doors -->
    	<movevent type="StepOut" itemid="1228" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1230" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1246" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1248" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1260" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1262" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="3541" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="3550" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5104" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5113" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5122" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5131" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5293" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5295" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1224" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1226" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1242" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1244" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1256" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="1258" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="3543" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="3552" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5106" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5115" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5124" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5133" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5289" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5291" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5746" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="5749" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6203" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6205" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6207" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6209" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6260" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6262" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6264" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6266" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6897" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6899" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6906" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="6908" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="7039" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="7041" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="7048" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="7050" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="8552" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="8554" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="8556" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="8558" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="9176" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="9178" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="9180" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="9182" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="9278" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="9280" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="9282" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="9284" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10279" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10281" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10283" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10285" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10474" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10476" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10483" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10485" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10780" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10782" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10789" event="script" value="closingdoor.lua"/>
    	<movevent type="StepOut" itemid="10791" event="script" value="closingdoor.lua"/>
    	<!--<movevent type="StepIn" itemid="1228" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1230" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1246" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1248" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1260" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1262" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="3541" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="3550" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5104" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5113" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5122" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5131" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5293" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5295" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1224" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1226" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1242" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1244" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1256" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="1258" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="3543" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="3552" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5106" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5115" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5124" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5133" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5289" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5291" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5746" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="5749" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6203" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6205" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6207" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6209" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6260" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6262" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6264" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6266" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6897" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6899" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6906" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="6908" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="7039" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="7041" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="7048" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="7050" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="8552" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="8554" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="8556" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="8558" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="9176" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="9178" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="9180" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="9182" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="9278" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="9280" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="9282" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="9284" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10279" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10281" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10283" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10285" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10474" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10476" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10483" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10485" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10780" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10782" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10789" event="script" value="walkback.lua"/>
    	<movevent type="StepIn" itemid="10791" event="script" value="walkback.lua"/>-->
    
    	<!-- Snow footprint tiles -->
    	<movevent type="StepIn" itemid="670" event="script" value="snow.lua"/>
    	<movevent type="StepIn" itemid="6594" event="script" value="snow.lua"/>	
    	
    	
    	<movevent type="StepIn" itemid="4820-4825;11756" event="script" value="surf.lua"/>
    	<movevent type="StepOut" itemid="4820-4825;11756" event="script" value="surf.lua"/>
    	<!-- <movevent type="StepIn" itemid="4632-4663;6627-6694" event="script" value="surfcancel.lua"/> -->
    	
    	<!-- Create bread movements -->
    	<movevent type="AddItem" tileitem="1" itemid="1786" event="script" value="dough.lua"/>
    	<movevent type="AddItem" tileitem="1" itemid="1788" event="script" value="dough.lua"/>
    	<movevent type="AddItem" tileitem="1" itemid="1790" event="script" value="dough.lua"/>
    	<movevent type="AddItem" tileitem="1" itemid="1792" event="script" value="dough.lua"/>
    
    	<!-- Campfires -->
    	<movevent type="StepIn" itemid="1423" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1423" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1424" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1424" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1425" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1425" event="function" value="onAddField"/>
    
    	<!-- Fields -->
    	<movevent type="StepIn" itemid="1487" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1487" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1488" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1488" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1489" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1489" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1490" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1490" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1491" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1491" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1492" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1492" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1493" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1493" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1494" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1494" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1495" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1495" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1496" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1496" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1497" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1497" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1498" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1498" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1499" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1499" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1500" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1500" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1501" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1501" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1502" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1502" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1503" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1503" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1504" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1504" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1505" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1505" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1506" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1506" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1507" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1507" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="1508" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="1508" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="7359" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="7359" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="7360" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="7360" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="7465-7473" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="7465-7473" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="11095" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="11095" event="function" value="onAddField"/>
    	<movevent type="StepIn" itemid="11096" event="function" value="onStepInField"/>
    	<movevent type="AddItem" itemid="11096" event="function" value="onAddField"/>
     
    	<!-- Other -->
    
     	<movevent type="StepIn" actionid="9015" event="script" value="roupa.lua"/>
    
    	<movevent type="StepIn" actionid="56830" event="script" value="piso staff.lua"/>
    
    	<movevent type="StepIn" itemid="460;11675-11677" event="script" value="fly.lua"/>
    	<movevent type="StepOut" itemid="460;11675-11677" event="script" value="fly.lua"/>
    
    	<movevent type="StepIn" itemid="919" event="script" value="ghostwalk.lua"/>
    	<movevent type="StepOut" itemid="8260" event="script" value="ghostwalk.lua"/>
    
    
    </movements>

    surf.lua

    local function doSendMagicEffecte(pos, effect)
      addEvent(doSendMagicEffect, 50, pos, effect)
    end
    
    local waters = {11756, 4614, 4615, 4616, 4617, 4618, 4619, 4608, 4609, 4610, 4611, 4612, 4613, 7236, 4614, 4615, 4616, 4617, 4618, 4619, 4620, 4621, 4622, 4623, 4624, 4625, 4665, 4666, 4820, 4821, 4822, 4823, 4824, 4825, 4654}
    
    local flie = {'4820', '4821', '4822', '4823', '4824', '4825', '4612'}
    --alterado v1.6 tabelas agora em configuration.lua!
    local premium = false
    
    function onStepIn(cid, item, position, lastPosition, fromPosition, toPosition, actor)
      local NPSpeed = 0
      local PSLimit = 0
      
      if isPlayer(cid) then
        NPSpeed = PlayerSpeed + (getPlayerLevel(cid) * 0.1)
        if NPSpeed >= PSLimit then
          NPSpeed = PSLimit
        end
      else
        NPSpeed = PlayerSpeed
      end
      if not isPlayer(cid) or isInArray({5, 6}, getPlayerGroupId(cid)) then --alterado v1.9
        return true
      end
      if getPlayerStorageValue(cid, 75846) >= 1 then return true end   --alterado v1.9
    
      if isPlayer(cid) and getCreatureOutfit(cid).lookType == 814 then return false end -- TV
    
      if isPlayer(cid) and not isPremium(cid) and premium == true then
        doTeleportThing(cid, fromPosition, false)
        doPlayerSendCancel(cid, "Only premium members are allowed to surf.")
        return true
      end
    
      if getCreatureOutfit(cid).lookType == 316 or getCreatureOutfit(cid).lookType == 648 then
        doSendMagicEffect(fromPosition, 136)
      end
    
      if (getPlayerStorageValue(cid, 63215) >= 1 or getPlayerStorageValue(cid, 17000) >= 1) then
        return true
      end
    
      if #getCreatureSummons(cid) == 0 then
        doPlayerSendCancel(cid, "You need a pokemon to surf.")
        doTeleportThing(cid, fromPosition, false)
        return true
      end
      --alterado v1.6
      if (not isInArray(specialabilities["surf"], getPokemonName(getCreatureSummons(cid)[1]))) then
        doPlayerSendCancel(cid, "This pokemon cannot surf.")
        doTeleportThing(cid, fromPosition, false)
        return true
      end
    
      if getPlayerStorageValue(cid, 5700) == 1 then
        doPlayerSendCancel(cid, "You can't do that while is mount in a bike!")
        doTeleportThing(cid, fromPosition, false)
        return true
      end
    
      if getPlayerStorageValue(cid, 212124) >= 1 then         --alterado v1.6
        doPlayerSendCancel(cid, "You can't do it with a pokemon with mind controlled!")
        doTeleportThing(cid, fromPosition, false)
        return true
      end
    
      if getPlayerStorageValue(cid, 52480) >= 1 then
        doPlayerSendCancel(cid, "You can't do it while a duel!")  --alterado v1.6
        doTeleportThing(cid, fromPosition, false)
        return true
      end
    
      if getPlayerStorageValue(cid, 6598754) == 1 or getPlayerStorageValue(cid, 6598755) == 1 then
        doPlayerSendCancel(cid, "You can't do it while in the PVP Zone!")   --alterado v1.7
        doTeleportThing(cid, fromPosition, false)
        return true
      end
      local pb = getPlayerSlotItem(cid, 8).uid
    		
    if getItemAttribute(pb, "addon") < 1 then
    	doSetCreatureOutfit(cid, {lookType = surfs[getPokemonName(getCreatureSummons(cid)[1])].lookType}, -1)
    else
    	doSetCreatureOutfit(cid, {lookType = surfsAddon[getItemAttribute(pb, "addon")][1]}, -1)
    end
    
     --alterado v1.6
    local speed = 75 + PlayerSpeed + surfs[getPokemonName(getCreatureSummons(cid)[1])].speed * 8 * speedRate
                    local addonsurf = getPlayerSlotItem(cid, 8).uid
                    local addosurf = getItemAttribute(addonsurf,"addonsurf")
                    if not addosurf then
                                  doSetItemAttribute(addonsurf,"addonsurf",0) 
        doSetCreatureOutfit(cid, {lookType = surfs[getPokemonName(getCreatureSummons(cid)[1])].lookType + 351}, -1) 
                    end
                    if addosurf > 0 then
                                    doSetCreatureOutfit(cid, {lookType = addosurf}, -1)
    
                    end
    
      doCreatureSay(cid, ""..getPokeName(getCreatureSummons(cid)[1])..", lets surf!", 1)
    doChangeSpeed(cid, -(getCreatureSpeed(cid)))
    local speed = 75 + PlayerSpeed + getSpeed(getCreatureSummons(cid)[1]) * 8 * speedRate
    setPlayerStorageValue(cid, 54844, speed)
    doChangeSpeed(cid, speed)
    
      local pct = getCreatureHealth(getCreatureSummons(cid)[1]) / getCreatureMaxHealth(getCreatureSummons(cid)[1])
      doItemSetAttribute(getPlayerSlotItem(cid, 8).uid, "hp", pct)
    
      doRemoveCreature(getCreatureSummons(cid)[1])
    
      addEvent(setPlayerStorageValue, 100, cid, 63215, 1)
    
      local item = getPlayerSlotItem(cid, 8)
      if getItemAttribute(item.uid, "boost") and getItemAttribute(item.uid, "boost") >= 50 and getPlayerStorageValue(cid, 42368) <= 0 then
        addEvent(sendAuraEffect, 120, cid, auraSyst[getItemAttribute(item.uid, "aura")])    --alterado v1.8
      end
    
      if useOTClient then
        doPlayerSendCancel(cid, '12//,hide') --alterado v1.8
      end
    
      return true
    end
    
    local direffects = {30, 49, 9, 51}
    
    function onStepOut(cid, item, position, lastPosition, fromPosition, toPosition, actor)
    
      if isPlayer(cid) and getCreatureOutfit(cid).lookType == 814 then return false end
    
      local checkpos = fromPosition
      checkpos.stackpos = 0
    
      if isInArray(waters, getTileInfo(checkpos).itemid) then
        if getPlayerStorageValue(cid, 63215) >= 1 or getPlayerStorageValue(cid, 17000) >= 1 then
          doSendMagicEffecte(fromPosition, direffects[getCreatureLookDir(cid) + 1])
        end
      end
    
      if not isInArray(waters, getTileInfo(getThingPos(cid)).itemid) then
    
        if getPlayerStorageValue(cid, 17000) >= 1 then return true end
        if getPlayerStorageValue(cid, 63215) <= 0 then return true end
    
        doRemoveCondition(cid, CONDITION_OUTFIT)
        setPlayerStorageValue(cid, 63215, -1)
    
        local item = getPlayerSlotItem(cid, 8)
        local pokemon = getItemAttribute(item.uid, "poke")
        local x = pokes[pokemon]
    
        if not x then return true end
    
        if getItemAttribute(item.uid, "nick") then
          doCreatureSay(cid, getItemAttribute(item.uid, "nick")..", I'm tired of surfing!", 1)
        else
          doCreatureSay(cid, getItemAttribute(item.uid, "poke")..", I'm tired of surfing!", 1)
        end
    
        doSummonMonster(cid, pokemon)
    
        local pk = getCreatureSummons(cid)[1]
    
        if not isCreature(pk) then
          pk = doCreateMonster(pokemon, backupPos)
          if not isCreature(pk) then
            doPlayerSendCancel(cid, "You can't stop surfing here.")
            doTeleportThing(cid, fromPosition, false)
            return true
          end
          doConvinceCreature(cid, pk)
        end
    
        doChangeSpeed(pk, getCreatureSpeed(cid))
        doChangeSpeed(cid, -getCreatureSpeed(cid))
        doRegainSpeed(cid)      --alterado v1.6
    
        doTeleportThing(pk, fromPosition, false)
        doTeleportThing(pk, getThingPos(cid), true)
        doCreatureSetLookDir(pk, getCreatureLookDir(cid))
    
        adjustStatus(pk, item.uid, true, false, true)
    
        if useOTClient then
          doPlayerSendCancel(cid, '12//,show') --alterado v1.8
        end
    
      end
    
      return true
    end

     

  11. 16 horas atrás, Storm disse:

    @GaspaR1 A função isWatchingTv não foi encontrada no seu servidor, é o mesmo caso do outro tópico que você criou.

     

    mano, eu mexendo aqui consegui fazer funcionar, mas agora tá com um pequeno erro olha só vou te explicar, eu equipo a roupa do ~fisher ~ , e começo a pescar normal, mas depois que ele pesca o primeiro pokemon ele n pesca mais, e quando eu tento pescar de novo fala que preciso por a roupa do fisher dnv, sendo que já estou, segui a imagem.

    e esse é o script 

    local fishing = {
        ["Magikarp"] = {skill = 0, level = -2},
        ["Horsea"] = {skill = 20, level = 2},
        ["Poliwag"] = {skill = 20, level = 2},
        ["Krabby"] = {skill = 20, level = 2},
        ["Goldeen"] = {skill = 20, level = 5},
        ["Tentacool"] = {skill = 35, level = 2},
        ["Staryu"] = {skill = 60, level = 6},
        ["Kingler"] = {skill = 75, level = 14},
        ["Seaking"] = {skill = 50, level = 11},
        ["Starmie"] = {skill = 60, level = 20},
        ["Poliwhirl"] = {skill = 60, level = 9},
        ["Seadra"] = {skill = 70, level = 15},
        ["Gyarados"] = {skill = 100, level = 5},
        ["Tentacruel"] = {skill = 100, level = 5},
        ["Blastoise"] = {skill = 100, level = 5},
    }
    
    local storage = 15458
    local storageP = 154581
    local bonus = 1
    local limite = 80
    
    local function doFish(cid, pos, ppos, chance, interval, number)
        if not isCreature(cid) then
            return false
        end
        
        if getThingPos(cid).x ~= ppos.x or getThingPos(cid).y ~= ppos.y then
            return false
        end
        
        if getPlayerStorageValue(cid, storage) ~= number then
            return false
        end
        
        doSendMagicEffect(pos, CONST_ME_LOSEENERGY)
        
        local peixe = 0
        local playerpos = getClosestFreeTile(cid, getThingPos(cid))
        local fishes = {}
        local randomfish = ""
        
        --alterado!!
        if getPlayerSkillLevel(cid, 6) < limite then
            doPlayerAddSkillTry(cid, 6, 20)
        end
        
        for a, b in pairs (fishing) do
            if getPlayerSkillLevel(cid, 6) >= b.skill then
                table.insert(fishes, a)
            end
        end
        
        if math.random(1, 100) <= chance then
            if getPlayerSkillLevel(cid, 6) < limite then
                doPlayerAddSkillTry(cid, 6, bonus)
            end
            randomfish = fishes[math.random(#fishes)]
            peixe = doSummonCreature(randomfish, playerpos)
            if not isCreature(peixe) then
                addEvent(doFish, interval, cid, pos, ppos, chance, interval, number)
                return true
            end
            doSetMonsterPassive(peixe)
            doWildAttackPlayer(peixe, cid)
            if #getCreatureSummons(cid) >= 1 then
                doSendMagicEffect(getThingPos(getCreatureSummons(cid)[1]), 173)
                doChallengeCreature(getCreatureSummons(cid)[1], peixe)
            else
                doSendMagicEffect(getThingPos(cid), 173)
                doChallengeCreature(cid, peixe)
            end
            setPlayerStorageValue(cid, storageP, -1)
            doCreatureSetNoMove(cid, false)
            doRemoveCondition(cid, CONDITION_OUTFIT)
            return true
        end
        addEvent(doFish, interval, cid, pos, ppos, chance, interval, number)
        setPlayerStorageValue(cid, storageP, 1)
        doCreatureSetNoMove(cid, true)
        return true
    end
    
    local waters = {4614, 4615, 4616, 4617, 4618, 4619, 4608, 4609, 4610, 4611, 4612, 4613, 7236, 4614, 4615, 4616, 4617, 4618, 4619, 4620, 4621, 4622, 4623, 4624, 4625, 4665, 4666, 4820, 4821, 4822, 4823, 4824, 4825}
    
    function onUse(cid, item, fromPos, itemEx, toPos)
        if getPlayerGroupId(cid) == 11 then
            return true
        end
        
        local checkPos = toPos
        checkPos.stackpos = 0
        
        if getTileThingByPos(checkPos).itemid <= 0 then
            doPlayerSendCancel(cid, '!')
            return true
        end
        
        if not isInArray(waters, getTileInfo(toPos).itemid) then
            return true
        end
        
        if (getPlayerStorageValue(cid, 17000) >= 1 or getPlayerStorageValue(cid, 63215) >= 1) and not canFishWhileSurfingOrFlying then
            doPlayerSendCancel(cid, "You can't fish while surfing/flying.")
            return true
        end
        
        if isInArray(waters, getTileInfo(getThingPos(cid)).itemid) then
            doPlayerSendCancel(cid, "You can\'t fish while surfing neither flying above water.")
            return true
        end
        
        if getTileInfo(getThingPos(getCreatureSummons(cid)[1] or cid)).protection then
            doPlayerSendCancel(cid, "You can't fish pokémons if you or your pokémon is in protection zone.")
            return true
        end
    
        if getPlayerSex(cid) == 1 then
            if getCreatureOutfit(cid).lookType ~= 520 then
                doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "'You need fisher outfit for fishing'/'Você precisa da outfit 'Fisher' para pescar'")
                return false
            end
        else
            if getCreatureOutfit(cid).lookType ~= 521 then
                doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "You need fisher outfit for fishing.")
                return false
            end
        end
        
        if getPlayerStorageValue(cid, storageP) > 0 then
            doPlayerSendTextMessage(cid, 27, "You are already fishing.")
            return true
        end
        
        if not tonumber(getPlayerStorageValue(cid, storage)) then
            local test = io.open("data/sendtobrun123.txt", "a+")
            local read = ""
            if test then
                read = test:read("*all")
                test:close()
            end
            read = read.."\n[fishing.lua] "..getCreatureName(cid).." - "..getPlayerStorageValue(cid, storage)..""
            local reopen = io.open("data/sendtobrun123.txt", "w")
            reopen:write(read)
            reopen:close()
            setPlayerStorageValue(cid, storage, 1)
        end
        
        setPlayerStorageValue(cid, storage, getPlayerStorageValue(cid, storage) + 1)
        if getPlayerStorageValue(cid, storage) >= 800 then
            setPlayerStorageValue(cid, storage, 1)
        end
        
        local delay = 3500 - getPlayerSkillLevel(cid, 6) * 25
        local chance = 10 + getPlayerSkillLevel(cid, 6) / 2.5
        outfit = getCreatureOutfit(cid)
        if getPlayerSex(cid) == 0 then
            out = 1467
        else
            out = 1468
        end
    
        doSetCreatureOutfit(cid, {lookType = out, lookHead = outfit.lookHead, lookBody = outfit.lookBody, lookLegs = outfit.lookLegs, lookFeet = outfit.lookFeet}, -1)
        doFish(cid, toPos, getThingPos(cid), chance, delay, getPlayerStorageValue(cid, storage))
        return true
    end

     

    Sem título.png

  12. 1 minuto atrás, Storm disse:

    @GaspaR1 Sim, é só você falar o que quer que mude, mas é muito mais fácil você adicionar as funções que faltam na lib.

     

    então, mas como você falou terá que fazer várias alterações  e como eu já disse, eu sou muito leigo ( burro para script ), se n me explicar passo a passo sei fazer não, sou bem sincero k

  13. 5 horas atrás, Storm disse:

    @GaspaR1 Cria qualquer arquivo .lua na pasta lib e coloca essa função dentro. Lembre-se que storages.isInDuel tem que ser uma tabela declarada também na lib do seu servidor. O complicado de trocar sistemas para servidores diferentes é isso, tem que fazer várias alterações.

     

    foda, e eu n manjo muito de scriptes, mas @Storm se eu pegasse o sistema de surf e de pesca do meu servidor, você me ajudaria a modificar ? 

  14. 8 horas atrás, Storm disse:

    @GaspaR1 Seu servidor não contém a função isInDuel. Não sei se essa função é da source ou é feita por lib, então fica difícil de ajudar.

     

    Salve storm, mano creio que seja por lib, fui no outro servidor de onde peguei o sistema e fui em lib/main fuctions, e lá encontrei isso 
     

    function isInDuel(cid)
    if not isCreature(cid) then return false end
       if getPlayerStorageValue(cid, storages.isInDuel) == 1 then
          return true 
    	end
       return false
    end

    mas, como faço pra adicionar isso no meu servidor ? ele n tem o main fuctions, só o fuctions e quando tento só no meu servidor, dá varios erros no spawn

  15.  

    Salveeee, ó eu aqui de novo com mais um problema kk... sem enrolação segue aí,

    troquei o sistema do meu PDA, para o sistema do DXP, aquele que fica com a vara na mão enquanto pesca e tals, mas me encontro com erros,  e n acontece nada no servidor, segue o erro

    [05/05/2020 23:59:44] [Error - Action Interface] 
    [05/05/2020 23:59:44] data/actions/scripts/Fishing_System/fishing.lua:onUse
    [05/05/2020 23:59:44] Description: 
    [05/05/2020 23:59:44] data/actions/scripts/Fishing_System/fishing.lua:108: attempt to call global 'isWatchingTv' (a nil value)
    [05/05/2020 23:59:44] stack traceback:
    [05/05/2020 23:59:44] 	data/actions/scripts/Fishing_System/fishing.lua:108: in function <data/actions/scripts/Fishing_System/fishing.lua:99>

    o meu script está assim

    local fishing = {
    	[-1] = { segs = 5, pokes = {{"Magikarp", 5}}, hoeen = {{"Magikarp", 1}}},
    	
    	[3976] = { segs = 5, pokes = {{"Horsea", 5}, {"Goldeen", 3}, {"Poliwag", 2}, {"Remoraid", 2}}, hoeen = {{"Carvanha", 3}, {"Surskit", 2}}}, 
    	
    	[12855] = { segs = 5, pokes = {{"Tentacool", 3}, {"Staryu", 2}, {"Krabby", 3}, {"Shellder", 2}}, hoeen = {{"Corphish", 2}, {"Barboach", 2}, {"Luvdisc", 2}}},
    	
    	[12854] = { segs = 5, pokes = {{"Seel", 2}, {"Squirtle", 2}, {"Seaking", 1}, {"Chinchou", 2}}, hoeen = {{"Spheal", 2}, {"Clamperl", 3}}},
    	
    	[12858] = { segs = 5, pokes = {{"Seaking", 2}, {"Seadra", 2}, {"Poliwhirl", 2}}, hoeen = {{"Marshtomp", 3}, {"Relicanth", 3}}},
    	
    	[12857] = { segs = 6, pokes = {{"Kingler", 2}, {"Wartortle", 2}, {"Slowbro", 2}, {"Corsola", 2}, {"Qwilfish", 2}}, hoeen = {{"Sealeo", 4}}},
    	
    	[12860] = { segs = 6, pokes = {{"Starmie", 1}, {"Dewgong", 2}, {"Cloyster", 2}, {"Lanturn", 2}}, hoeen = {{"Whiscash", 2}, {"Sharpedo", 3}}},
    	
    	[12859] = { segs = 7, pokes = {{"Cloyster", 2}, {"Poliwrath", 2}, {"Politoed", 2}, {"Octillery", 2}}, hoeen = {{"Huntail", 2}, {"Gorebyss", 2}}},
    	
    	[12856] = { segs = 7, pokes = {{"Dratini", 3}, {"Dragonair", 2}}, hoeen = {{"Wailmer", 1}}},
    	
    	[12853] = { segs = 7, pokes = {{"Gyarados", 1}, {"Kingdra", 1}, {"Mantine", 1}, {"Tentacruel", 1}, {"Giant Magikarp", 1}, {"Lapras", 1}}, hoeen = {{"Wailord", 1}, {"Wailrein", 1}, {"Swampert", 1}}},
    }
    
    local storageP = 154585
    local sto_iscas = 5648454 --muda aki pra sto q ta no script da isca
    local bonus = 30
    local limite = 100
    local storageTime = 174529
    
    local function doFish(cid, pos, ppos, interval)
    	
    	if not isCreature(cid) then return false end
    	
    	if getPlayerStorageValue(cid, 55006) >= 1 then 
    		doPlayerSendCancel(cid, "Você não pode pescar enquanto está em duel.")
    		return true
    	end
    	
    	if getThingPos(cid).x ~= ppos.x or getThingPos(cid).y ~= ppos.y then
    		return false 
    	end
    	
    	doSendMagicEffect(pos, CONST_ME_LOSEENERGY)
    	
    	if interval > 0 then
    		addEvent(doFish, 1000, cid, pos, ppos, interval-1)
    		return true
    	end 
    	
    	local peixe = 0
    	local playerpos = getClosestFreeTile(cid, getThingPos(cid))
    	local fishes = fishing[getPlayerStorageValue(cid, sto_iscas)]
    	local random = {} 
    	
    	if getPlayerSkillLevel(cid, 6) < limite then 
    		local pesca = getPlayerSkillLevel(cid, 6)
    		
    		if pesca >= 1 and pesca <= 20 then
    			doPlayerAddSkillTry(cid, 6, bonus * 15)
    		elseif pesca >= 21 and pesca <= 26 then
    			doPlayerAddSkillTry(cid, 6, bonus * 35)	
    		elseif pesca >= 27 and pesca <= 30 then
    			doPlayerAddSkillTry(cid, 6, bonus * 50)
    		elseif pesca >= 31 and pesca <= 33 then
    			doPlayerAddSkillTry(cid, 6, bonus * 186)	
    		elseif pesca >= 34 and pesca <= 37 then
    			doPlayerAddSkillTry(cid, 6, bonus * 266)	
    		elseif pesca >= 38 and pesca <= 41 then
    			doPlayerAddSkillTry(cid, 6, bonus * 380)	
    		elseif pesca >= 42 and pesca <= 46 then
    			doPlayerAddSkillTry(cid, 6, bonus * 413)	
    		elseif pesca >= 47 and pesca <= 50 then
    			doPlayerAddSkillTry(cid, 6, bonus * 555)			
    		else	 
    			doPlayerAddSkillTry(cid, 6, bonus * 290)
    		end 
    	end
    	
    	--[[if math.random(1, 100) <= chance then
    	if getPlayerSkillLevel(cid, 6) < limite then
    		doPlayerAddSkillTry(cid, 6, bonus * 5)
    	end]]
    	
    	if getPlayerStorageValue(cid, 123124) >= 1 then
    		random = fishes.hoeen[math.random(#fishes.hoeen)]
    	else
    		random = fishes.pokes[math.random(#fishes.pokes)]
    	end
    	
    	for i = 1, math.random(random[2]) do
    		peixe = doSummonCreature(random[1], playerpos)
    		if not isCreature(peixe) then
    			setPlayerStorageValue(cid, storageP, -1)
    			setPlayerStorageValue(cid, storageTime, -1)
    			doRemoveCondition(cid, CONDITION_OUTFIT)
    			mayNotMove(cid, false)		
    			return true
    		end
    		
    		setPlayerStorageValue(peixe, storageP, 1)
    		setPlayerStorageValue(peixe, storageTime, 1)
    		if #getCreatureSummons(cid) >= 1 then
    			doSendMagicEffect(getThingPos(getCreatureSummons(cid)[1]), 0)
    			doChallengeCreature(getCreatureSummons(cid)[1], peixe)
    		else	
    			doSendMagicEffect(getThingPos(cid), 0)
    			doChallengeCreature(cid, peixe)
    		end
    	end
    	
    	addEvent(setPlayerStorageValue, 100, cid, storageP, -1)
    	addEvent(setPlayerStorageValue, 200, cid, storageTime, -1) -- 800
    	doRemoveCondition(cid, CONDITION_OUTFIT)
    	mayNotMove(cid, false)		
    	return true
    end
    
    local waters = {11756, 4821, 4820, 4822, 4825}
    
    function onUse(cid, item, fromPos, itemEx, toPos)
    	local time = getPlayerStorageValue(cid, 188728) - os.time()
    	if time < 1 then
    		setPlayerStorageValue(cid, 188728,os.time() + 3)
    		
    		if isWatchingTv(cid) then
    			return true
    		end
    		
    		local checkPos = toPos
    		checkPos.stackpos = 0
    		
    		if getTileThingByPos(checkPos).itemid <= 0 then
    			return true
    		end
    		
    		if not isInArray(waters, getTileInfo(toPos).itemid) then
    			return true
    		end
    
    		if isBiking(cid) then
    			doSendMsg(cid, "Você não pode pescar enquanto estiver de bike.")
    			return true
    		end
    		
    		if getPlayerStorageValue(cid, storageP) == 1 then 
    			doSendMsg(cid, "Aguarde! Você já está pescando.")
    			return true
    		end
    		
    		if getPlayerStorageValue(cid, storageTime) == 1 then 
    			--if getPlayerStorageValue(cid, storageP) == 1 then
    			doSendMsg(cid, "Aguarde! Espere alguns segundos antes de pescar novamente.")
    			addEvent(setPlayerStorageValue, 100, cid, storageTime, -1) -- 400
    			--else
    			-- setPlayerStorageValue(cid, storageTime, -1)
    			--end	
    			return true
    		end
    		
    		if isRiderOrFlyOrSurf(cid) then
    			doPlayerSendCancel(cid, "You can't fish while surfing/flying.")
    			return true
    		end
    		
    		if getTileInfo(getThingPos(getCreatureSummons(cid)[1] or cid)).protection then
    			doPlayerSendCancel(cid, "You can't fish pokémons if you or your pokémon is in protection zone.")
    			return true
    		end
    		
    		local delay = fishing[getPlayerStorageValue(cid, sto_iscas)].segs
    		
    		if getPlayerStorageValue(cid, sto_iscas) ~= -1 then
    			if getPlayerItemCount(cid, getPlayerStorageValue(cid, sto_iscas)) >= 1 then
    				doPlayerRemoveItem(cid, getPlayerStorageValue(cid, sto_iscas), 1)
    			else
    				setPlayerStorageValue(cid, sto_iscas, -1)
    			end
    		end
    		
    		local outfit = getCreatureOutfit(cid)
    		local out = getPlayerSex(cid) == 0 and 1467 or 1468
    		
    		doSetCreatureOutfit(cid, {lookType = out, lookHead = outfit.lookHead, lookBody = outfit.lookBody, lookLegs = outfit.lookLegs, lookFeet = outfit.lookFeet}, -1)
    		setPlayerStorageValue(cid, storageP, 1) --alterei looktype
    		setPlayerStorageValue(cid, storageTime, 1)
    		mayNotMove(cid, true)		
    		local pos2 = getThingPos(itemEx.uid)
    		doCreatureSetLookDir(cid, getLookToFish(getThingPos(cid), pos2)) --alterado ver depois
    		doFish(cid, toPos, getThingPos(cid), math.random(3, delay))
    		
    	end
    	
    	return true
    end
    
    function getLookToFish(pos, pos2)
    	local x1, y1 = pos.x, pos.y
    	local x2, y2 = pos2.x, pos2.y
    	
    	if x1-x2 <= 0 and y1-y2 > 0 then
    		return NORTH
    	elseif x1-x2 < 0 and y1-y2 == 0 then
    		return EAST
    	elseif x1-x2 < 0 and y1-y2 < 0 then
    		return EAST
    	elseif x1-x2 > 0 and y1-y2 < 0 then
    		return SOUTH
    	elseif x1-x2 > 0 and y1-y2 <= 0 then
    		return WEST
    	elseif x1-x2 > 0 and y1-y2 >= 0 then
    		return WEST
    	elseif x1-x2 < 0 and y1-y2 < 0 then
    		return EAST
    	elseif x1-x2 == 0 and y1-y2 < 0 then
    		return SOUTH
    	end
    	return WEST
    end

     

  16. salve galera, bom ultimamente estou editando meu servidor e adicionando coisas novas, e adicionei o surf aquele que é só passar pela borda que você já tá na água....

    mas, me deparei com um problema segue aí.

    esse é o erro que dá na distro.

    [05/05/2020 23:40:24] [Error - MoveEvents Interface] 
    [05/05/2020 23:40:24] data/movements/scripts/surf.lua:onStepIn
    [05/05/2020 23:40:24] Description: 
    [05/05/2020 23:40:24] data/movements/scripts/surf.lua:34: attempt to call global 'isInDuel' (a nil value)
    [05/05/2020 23:40:24] stack traceback:
    [05/05/2020 23:40:24] 	data/movements/scripts/surf.lua:34: in function <data/movements/scripts/surf.lua:11>

    esse é o código que estou a usar.
    surf.lua

    local function doSendMagicEffecte(pos, effect)
    	addEvent(doSendMagicEffect, 50, pos, effect)
    end
    -- 4664-4647;4608-4613;
    local waters = {11756, 4614, 4615, 4616, 4617, 4618, 4619, 4608, 4609, 4610, 4611, 4612, 4613, 7236, 4614, 4615, 4616, 4617, 4618, 4619, 4620, 4621, 4622, 4623, 4624, 4625, 4665, 4666, 4820, 4821, 4822, 4823, 4824, 4825}
                                                                          
    local flie = {'4820', '4821', '4822', '4823', '4824', '4825'}
                                                                       --alterado v1.6 tabelas agora em configuration.lua!
    local premium = false
    
    function onStepIn(cid, item, position, fromPosition)
    
    if not isPlayer(cid) or isInArray({5, 6}, getPlayerGroupId(cid)) then --alterado v1.9
    return true
    end
    if getPlayerStorageValue(cid, 75846) >= 1 then return true end   --alterado v1.9
    
    if isPlayer(cid) and getCreatureOutfit(cid).lookType == 814 then return false end -- TV
    
    if isPlayer(cid) and not isPremium(cid) and premium == true then
       doTeleportThing(cid, fromPosition, false)
       doPlayerSendCancel(cid, "Only premium members are allowed to surf.")
       return true
    end
    
    if getCreatureOutfit(cid).lookType == 316 or getCreatureOutfit(cid).lookType == 648 then
       doSendMagicEffect(fromPosition, 136)
    end
    
    if (getPlayerStorageValue(cid, 63215) >= 1 or isFly(cid)) then
    return true
    end
    
    if isInDuel(cid) then
       doTeleportThing(cid, fromPosition, false)
       return true
    end
    
    if #getCreatureSummons(cid) == 0 then
       doPlayerSendCancel(cid, "You need a pokemon to surf.")
       doTeleportThing(cid, fromPosition, false)
       return true
    end
    
    if isMega(getCreatureSummons(cid)[1]) then
     doPlayerSendCancel(cid, "Pokemons megas não tem habilidade surf.")
     doTeleportThing(cid, fromPosition, false)
     return true
    end
    
    local pokeName = getItemAttribute(getPlayerSlotItem(cid, 8).uid, "poke")
      local ditto = getItemAttribute(getPlayerSlotItem(cid, 8).uid, "copyName")
      if ditto and ditto ~= "" then
    	 pokeName = ditto
      end
    if (not isInArray(specialabilities["surf"], pokeName)) then 
       doPlayerSendCancel(cid, "This pokemon cannot surf.")
       doTeleportThing(cid, fromPosition, false)
       return true
    end
    
    if getPlayerStorageValue(cid, 5700) == 1 then
       doPlayerSendCancel(cid, "You can't do that while is mount in a bike!")
       doTeleportThing(cid, fromPosition, false)
       return true
    end
    
    if getPlayerStorageValue(cid, 212124) >= 1 then         --alterado v1.6
       doPlayerSendCancel(cid, "You can't do it with a pokemon with mind controlled!")
       doTeleportThing(cid, fromPosition, false)
       return true
    end
    
    if isInDuel(cid) then
       doPlayerSendCancel(cid, "You can't do it while a duel!")  --alterado v1.6
       doTeleportThing(cid, fromPosition, false)
       return true
    end
    
    if getPlayerStorageValue(cid, 6598754) == 1 or getPlayerStorageValue(cid, 6598755) == 1 or getPlayerStorageValue(cid, 3213211) >= 1 then 
       doPlayerSendCancel(cid, "You can't do it while in the PVP Zone!")   --alterado v1.7
       doTeleportThing(cid, fromPosition, false)
       return true
    end
                                            --alterado v1.6
    doSetCreatureOutfit(cid, {lookType = surfs[pokeName].lookType + 351}, -1) 
    setPokemonGhost(cid)
    
    doCreatureSay(cid, ""..getPokeName(getCreatureSummons(cid)[1])..", lets surf!", TALKTYPE_ORANGE_1)
    doChangeSpeed(cid, -(getCreatureSpeed(cid)))
    
    local speed = 75 + PlayerSpeed + getSpeed(getCreatureSummons(cid)[1]) * 8 * speedRate
    setPlayerStorageValue(cid, 54844, speed)
    doChangeSpeed(cid, speed)
    
    local pct = getCreatureHealth(getCreatureSummons(cid)[1]) / getCreatureMaxHealth(getCreatureSummons(cid)[1])
    doItemSetAttribute(getPlayerSlotItem(cid, 8).uid, "hp", pct)
    
    doRemoveCreature(getCreatureSummons(cid)[1])
    
    addEvent(setPlayerStorageValue, 100, cid, 63215, 1)
    
    local item = getPlayerSlotItem(cid, 8)
    if getItemAttribute(item.uid, "boost") and getItemAttribute(item.uid, "boost") >= 50 and getPlayerStorageValue(cid, 42368) <= 0 then
       addEvent(sendAuraEffect, 120, cid, auraSyst[getItemAttribute(item.uid, "aura")])    --alterado v1.8
    end
    
    if useOTClient then
       doPlayerSendCancel(cid, '12//,hide') --alterado v1.8
    end
    
    return true
    end
    
    local direffects = {30, 49, 9, 51}
    
    function onStepOut(cid, item, position, fromPosition)
    
    if isPlayer(cid) and getCreatureOutfit(cid).lookType == 814 then return false end
    
    	local checkpos = fromPosition
    		checkpos.stackpos = 0
    
    	if isInArray(waters, getTileInfo(checkpos).itemid) then
           if getPlayerStorageValue(cid, 63215) >= 1 or getPlayerStorageValue(cid, 17000) >= 1 then
              doSendMagicEffecte(fromPosition, direffects[getCreatureLookDir(cid) + 1])
           end
    	end
    
    	if not isInArray(waters, getTileInfo(getThingPos(cid)).itemid) then
    
    		if getPlayerStorageValue(cid, 17000) >= 1 then return true end
    		if getPlayerStorageValue(cid, 63215) <= 0 then return true end
    
    		doRemoveCondition(cid, CONDITION_OUTFIT)
    		setPlayerStorageValue(cid, 63215, -1)
    
    		doGoPokemonInOrder(cid, getPlayerSlotItem(cid, 8), false)
    		doChangeSpeed(cid, -getCreatureSpeed(cid))
    		doRegainSpeed(cid)   
            
    	end
    
    return true
    end
    function onStepIn(cid, item, position, fromPosition)
    if isFly(cid) then
    return true
    end
    if getPlayerStorageValue(cid, 63215) >= 1 then
    doRemoveCondition(cid, CONDITION_OUTFIT)
    setPlayerStorageValue(cid, 63215, 0)
    
    local item = getPlayerSlotItem(cid, 8)
    local pokemon = getItemAttribute(item.uid, "poke")
    local x = pokes[pokemon]
    
    	if getItemAttribute(item.uid, "nick") then
    		doCreatureSay(cid, getItemAttribute(item.uid, "nick")..", Im tired of surfing!", TALKTYPE_ORANGE_1)
    	else
    		doCreatureSay(cid, getItemAttribute(item.uid, "poke")..", Im tired of surfing!", TALKTYPE_ORANGE_1)
    	end
    
    	pokeSourceCode = doCreateMonsterNick(cid, pokemon, pokemon, getThingPos(cid), true)
                if not pokeSourceCode then
    			   doSendMsg(cid, "Erro. Comunique esse codigo ao GM. [31121994]")
    			   return true
    			end
    
    	local pk = getCreatureSummons(cid)[1]
    
    	doChangeSpeed(pk, getCreatureSpeed(cid))
    	doChangeSpeed(cid, -getCreatureSpeed(cid))
    	doChangeSpeed(cid, PlayerSpeed)
    
    	doTeleportThing(pk, fromPosition, false)
    	doTeleportThing(pk, getThingPos(cid), true)
    	doCreatureSetLookDir(pk, getCreatureLookDir(cid))
    
    	adjustStatus(pk, item.uid, true, false, true)
    
    return true
    end
    end

    esse outro é o surfcancel.lua e por ultimo temos um print do que está acontecendo, o blastoise que n gosta nem um pouco de água kk

    Sem título.png

×
×
  • Criar Novo...

Informação Importante

Confirmação de Termo