Ir para conteúdo
  • Cadastre-se

Posts Recomendados

Base: 

OTX Server by Fir3z

 

Qual erro está surgindo/O que você procura?

Queria saber como diminuir exhaust da SD , sem ser pelo spells.xml por que não esta funcionando já abaixei mas no game não muda nada, gostaria que a SD saísse mais rápida !

Link para o post
Compartilhar em outros sites

Cara, a solução que postei no outro tópico (já que vocês fizeram 2 tópicos com o mesmo assunto), funcionou perfeitamente aqui.

 

 

BySw_aPvTIS0dgS9ZqWAjQ.png

Config.lua

timeBetweenExActions = 0

Spells.xml

<rune name="Sudden Death" id="2268" allowfaruse="1" charges="3" lvl="45" maglv="15" exhaustion="0" needtarget="1" blocktype="solid" event="script" value="attack/sudden death.lua"/>

 

Editado por Way20 (veja o histórico de edições)

 

 

 

Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito.

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites
2 minutos atrás, Toulouse disse:

@Way20 Eu fiz isso, não adiantou

 

sd.gif.be3cd638d3ceb1ed14885754ff447de5.gif

 

Muito estranho, se você possui as sources, posta actions.cpp aqui.

 

 

 

Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito.

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

@Way20 

 

Spoiler

////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// 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 3 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, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"

#include "const.h"

#include "actions.h"
#include "tools.h"

#include "player.h"
#include "monster.h"
#include "npc.h"

#include "item.h"
#include "container.h"

#include "game.h"
#include "configmanager.h"

#include "combat.h"
#include "spells.h"

#include "house.h"
#include "beds.h"

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

extern Game g_game;
extern Spells* g_spells;
extern Actions* g_actions;
extern ConfigManager g_config;

Actions::Actions():
m_interface("Action Interface")
{
    m_interface.initState();
    defaultAction = NULL;
}

Actions::~Actions()
{
    clear();
}

inline void Actions::clearMap(ActionUseMap& map)
{
    for(ActionUseMap::iterator it = map.begin(); it != map.end(); ++it)
        delete it->second;

    map.clear();
}

void Actions::clear()
{
    clearMap(useItemMap);
    clearMap(uniqueItemMap);
    clearMap(actionItemMap);

    m_interface.reInitState();
    delete defaultAction;
    defaultAction = NULL;
}

Event* Actions::getEvent(const std::string& nodeName)
{
    if(asLowerCaseString(nodeName) == "action")
        return new Action(&m_interface);

    return NULL;
}

bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override)
{
    Action* action = dynamic_cast<Action*>(event);
    if(!action)
        return false;

    std::string strValue;
    if(readXMLString(p, "default", strValue) && booleanString(strValue))
    {
        if(!defaultAction)
            defaultAction = action;
        else if(override)
        {
            delete defaultAction;
            defaultAction = action;
        }
        else
            std::clog << "[Warning - Actions::registerEvent] You cannot define more than one default action, if you want to do so "
                << "Please define \"override\"." << std::endl;

        return true;
    }

    bool success = true;
    std::string endValue;
    if(readXMLString(p, "itemid", strValue))
    {
        IntegerVec intVector;
        if(!parseIntegerVec(strValue, intVector))
        {
            std::clog << "[Warning - Actions::registerEvent] Invalid itemid - '" << strValue << "'" << std::endl;
            return false;
        }

        if(useItemMap.find(intVector[0]) != useItemMap.end())
        {
            if(!override)
            {
                std::clog << "[Warning - Actions::registerEvent] Duplicate registered item id: " << intVector[0] << std::endl;
                success = false;
            }
            else
                delete useItemMap[intVector[0]];
        }

        if(success)
            useItemMap[intVector[0]] = action;

        for(size_t i = 1, size = intVector.size(); i < size; ++i)
        {
            if(useItemMap.find(intVector) != useItemMap.end())
            {
                if(!override)
                {
                    std::clog << "[Warning - Actions::registerEvent] Duplicate registered item id: " << intVector << std::endl;
                    continue;
                }
                else
                    delete useItemMap[intVector];
            }

            useItemMap[intVector] = new Action(action);
        }
    }
    else if(readXMLString(p, "fromid", strValue) && readXMLString(p, "toid", endValue))
    {
        IntegerVec intVector = vectorAtoi(explodeString(strValue, ";")), endVector = vectorAtoi(explodeString(endValue, ";"));
        if(intVector[0] && endVector[0] && intVector.size() == endVector.size())
        {
            int32_t tmp = 0;
            for(size_t i = 0, size = intVector.size(); i < size; ++i)
            {
                tmp = intVector;
                while(intVector <= endVector)
                {
                    if(useItemMap.find(intVector) != useItemMap.end())
                    {
                        if(!override)
                        {
                            std::clog << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << intVector <<
                                ", in fromid: " << tmp << " and toid: " << endVector << std::endl;
                            intVector++;
                            continue;
                        }
                        else
                            delete useItemMap[intVector];
                    }

                    useItemMap[intVector++] = new Action(action);
                }
            }
        }
        else
            std::clog << "[Warning - Actions::registerEvent] Malformed entry (from item: \"" << strValue <<
                "\", to item: \"" << endValue << "\")" << std::endl;
    }

    if(readXMLString(p, "uniqueid", strValue))
    {
        IntegerVec intVector;
        if(!parseIntegerVec(strValue, intVector))
        {
            std::clog << "[Warning - Actions::registerEvent] Invalid uniqueid - '" << strValue << "'" << std::endl;
            return false;
        }

        if(uniqueItemMap.find(intVector[0]) != uniqueItemMap.end())
        {
            if(!override)
            {
                std::clog << "[Warning - Actions::registerEvent] Duplicate registered item uid: " << intVector[0] << std::endl;
                success = false;
            }
            else
                delete uniqueItemMap[intVector[0]];
        }

        if(success)
            uniqueItemMap[intVector[0]] = action;

        for(size_t i = 1, size = intVector.size(); i < size; ++i)
        {
            if(uniqueItemMap.find(intVector) != uniqueItemMap.end())
            {
                if(!override)
                {
                    std::clog << "[Warning - Actions::registerEvent] Duplicate registered item uid: " << intVector << std::endl;
                    continue;
                }
                else
                    delete uniqueItemMap[intVector];
            }

            uniqueItemMap[intVector] = new Action(action);
        }
    }
    else if(readXMLString(p, "fromuid", strValue) && readXMLString(p, "touid", endValue))
    {
        IntegerVec intVector = vectorAtoi(explodeString(strValue, ";")), endVector = vectorAtoi(explodeString(endValue, ";"));
        if(intVector[0] && endVector[0] && intVector.size() == endVector.size())
        {
            int32_t tmp = 0;
            for(size_t i = 0, size = intVector.size(); i < size; ++i)
            {
                tmp = intVector;
                while(intVector <= endVector)
                {
                    if(uniqueItemMap.find(intVector) != uniqueItemMap.end())
                    {
                        if(!override)
                        {
                            std::clog << "[Warning - Actions::registerEvent] Duplicate registered item with uid: " << intVector <<
                                ", in fromuid: " << tmp << " and touid: " << endVector << std::endl;
                            intVector++;
                            continue;
                        }
                        else
                            delete uniqueItemMap[intVector];
                    }

                    uniqueItemMap[intVector++] = new Action(action);
                }
            }
        }
        else
            std::clog << "[Warning - Actions::registerEvent] Malformed entry (from unique: \"" << strValue <<
                "\", to unique: \"" << endValue << "\")" << std::endl;
    }

    if(readXMLString(p, "actionid", strValue) || readXMLString(p, "aid", strValue))
    {
        IntegerVec intVector;
        if(!parseIntegerVec(strValue, intVector))
        {
            std::clog << "[Warning - Actions::registerEvent] Invalid actionid - '" << strValue << "'" << std::endl;
            return false;
        }

        if(actionItemMap.find(intVector[0]) != actionItemMap.end())
        {
            if(!override)
            {
                std::clog << "[Warning - Actions::registerEvent] Duplicate registered item aid: " << intVector[0] << std::endl;
                success = false;
            }
            else
                delete actionItemMap[intVector[0]];
        }

        if(success)
            actionItemMap[intVector[0]] = action;

        for(size_t i = 1, size = intVector.size(); i < size; ++i)
        {
            if(actionItemMap.find(intVector) != actionItemMap.end())
            {
                if(!override)
                {
                    std::clog << "[Warning - Actions::registerEvent] Duplicate registered item aid: " << intVector << std::endl;
                    continue;
                }
                else
                    delete actionItemMap[intVector];
            }

            actionItemMap[intVector] = new Action(action);
        }
    }
    else if(readXMLString(p, "fromaid", strValue) && readXMLString(p, "toaid", endValue))
    {
        IntegerVec intVector = vectorAtoi(explodeString(strValue, ";")), endVector = vectorAtoi(explodeString(endValue, ";"));
        if(intVector[0] && endVector[0] && intVector.size() == endVector.size())
        {
            int32_t tmp = 0;
            for(size_t i = 0, size = intVector.size(); i < size; ++i)
            {
                tmp = intVector;
                while(intVector <= endVector)
                {
                    if(actionItemMap.find(intVector) != actionItemMap.end())
                    {
                        if(!override)
                        {
                            std::clog << "[Warning - Actions::registerEvent] Duplicate registered item with aid: " << intVector <<
                                ", in fromaid: " << tmp << " and toaid: " << endVector << std::endl;
                            intVector++;
                            continue;
                        }
                        else
                            delete actionItemMap[intVector];
                    }

                    actionItemMap[intVector++] = new Action(action);
                }
            }
        }
        else
            std::clog << "[Warning - Actions::registerEvent] Malformed entry (from action: \"" << strValue <<
                "\", to action: \"" << endValue << "\")" << std::endl;
    }

    return success;
}

ReturnValue Actions::canUse(const Player* player, const Position& pos)
{
    const Position& playerPos = player->getPosition();
    if(pos.x == 0xFFFF)
        return RET_NOERROR;

    if(playerPos.z > pos.z)
        return RET_FIRSTGOUPSTAIRS;

    if(playerPos.z < pos.z)
        return RET_FIRSTGODOWNSTAIRS;

    if(!Position::areInRange<1,1,0>(playerPos, pos))
        return RET_TOOFARAWAY;

    Tile* tile = g_game.getTile(pos);
    if(tile)
    {
        HouseTile* houseTile = tile->getHouseTile();
        if(houseTile && houseTile->getHouse() && !houseTile->getHouse()->isInvited(player))
            return RET_PLAYERISNOTINVITED;
    }
    return RET_NOERROR;
}

ReturnValue Actions::canUseEx(const Player* player, const Position& pos, const Item* item)
{
    Action* action = NULL;
    if((action = getAction(item, ACTION_UNIQUEID)))
        return action->canExecuteAction(player, pos);

    if((action = getAction(item, ACTION_ACTIONID)))
        return action->canExecuteAction(player, pos);

    if((action = getAction(item, ACTION_ITEMID)))
        return action->canExecuteAction(player, pos);

    if((action = getAction(item, ACTION_RUNEID)))
        return action->canExecuteAction(player, pos);

    if(defaultAction)
        return defaultAction->canExecuteAction(player, pos);

    return RET_NOERROR;
}

ReturnValue Actions::canUseFar(const Creature* creature, const Position& toPos, bool checkLineOfSight)
{
    if(toPos.x == 0xFFFF)
        return RET_NOERROR;

    const Position& creaturePos = creature->getPosition();
    if(creaturePos.z > toPos.z)
        return RET_FIRSTGOUPSTAIRS;

    if(creaturePos.z < toPos.z)
        return RET_FIRSTGODOWNSTAIRS;

    if(!Position::areInRange<7,5,0>(toPos, creaturePos))
        return RET_TOOFARAWAY;

    if(checkLineOfSight && !g_game.canThrowObjectTo(creaturePos, toPos))
        return RET_CANNOTTHROW;

    return RET_NOERROR;
}

Action* Actions::getAction(const Item* item, ActionType_t type) const
{
    if(item->getUniqueId() && (type == ACTION_ANY || type == ACTION_UNIQUEID))
    {
        ActionUseMap::const_iterator it = uniqueItemMap.find(item->getUniqueId());
        if(it != uniqueItemMap.end())
            return it->second;
    }

    if(item->getActionId() && (type == ACTION_ANY || type == ACTION_ACTIONID))
    {
        ActionUseMap::const_iterator it = actionItemMap.find(item->getActionId());
        if(it != actionItemMap.end())
            return it->second;
    }

    if(type == ACTION_ANY || type == ACTION_ITEMID)
    {
        ActionUseMap::const_iterator it = useItemMap.find(item->getID());
        if(it != useItemMap.end())
            return it->second;
    }

    if(type == ACTION_ANY || type == ACTION_RUNEID)
    {
        if(Action* runeSpell = g_spells->getRuneSpell(item->getID()))
            return runeSpell;
    }

    return NULL;
}

bool Actions::executeUse(Action* action, Player* player, Item* item,
    const PositionEx& posEx, uint32_t creatureId)
{
    return action->executeUse(player, item, posEx, posEx, false, creatureId);
}

ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item, uint32_t creatureId)
{
    if(Door* door = item->getDoor())
    {
        if(!door->canUse(player))
            return RET_CANNOTUSETHISOBJECT;
    }

    int32_t tmp = 0;
    if(item->getParent())
        tmp = item->getParent()->__getIndexOfThing(item);

    PositionEx posEx(pos, tmp);
    Action* action = NULL;
    if((action = getAction(item, ACTION_UNIQUEID)))
    {
        if(executeUse(action, player, item, posEx, creatureId))
            return RET_NOERROR;
    }

    if((action = getAction(item, ACTION_ACTIONID)))
    {
        if(executeUse(action, player, item, posEx, creatureId))
            return RET_NOERROR;
    }

    if((action = getAction(item, ACTION_ITEMID)))
    {
        if(executeUse(action, player, item, posEx, creatureId))
            return RET_NOERROR;
    }

    if((action = getAction(item, ACTION_RUNEID)))
    {
        if(executeUse(action, player, item, posEx, creatureId))
            return RET_NOERROR;
    }

    if(defaultAction)
    {
        if(executeUse(defaultAction, player, item, posEx, creatureId))
            return RET_NOERROR;
    }

    if(BedItem* bed = item->getBed())
    {
        if(!bed->canUse(player))
            return RET_CANNOTUSETHISOBJECT;

        bed->sleep(player);
        return RET_NOERROR;
    }

    if(Container* container = item->getContainer())
    {
        if(container->getCorpseOwner() && !player->canOpenCorpse(container->getCorpseOwner())
            && g_config.getBool(ConfigManager::CHECK_CORPSE_OWNER))
            return RET_YOUARENOTTHEOWNER;

        Container* tmpContainer = NULL;
        if(DepotLocker* depot = container->getDepotLocker())
        {
            if(player->hasFlag(PlayerFlag_CannotPickupItem))
                return RET_CANNOTUSETHISOBJECT;

            DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId());
            myDepotLocker->setParent(depot->getParent());
            tmpContainer = myDepotLocker;
            player->setDepotChange(true);
            player->setLastDepotId(depot->getDepotId());
        }

        if(!tmpContainer)
            tmpContainer = container;

        int32_t oldId = player->getContainerID(tmpContainer);
        if(oldId != -1)
        {
            player->onCloseContainer(tmpContainer);
            player->closeContainer(oldId);
        }
        else
        {
            player->addContainer(index, tmpContainer);
            player->onSendContainer(tmpContainer);
        }

        return RET_NOERROR;
    }

    if(item->isReadable())
    {
        if(item->canWriteText())
        {
            player->setWriteItem(item, item->getMaxWriteLength());
            player->sendTextWindow(item, item->getMaxWriteLength(), true);
        }
        else
        {
            player->setWriteItem(NULL);
            player->sendTextWindow(item, 0, false);
        }

        return RET_NOERROR;
    }

    const ItemType& it = Item::items[item->getID()];
    if(it.transformUseTo)
    {
        g_game.transformItem(item, it.transformUseTo);
        g_game.startDecay(item);
        return RET_NOERROR;
    }

    return RET_CANNOTUSETHISOBJECT;
}

bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* item)
{
    if(!player->canDoAction())
        return false;

    player->setNextActionTask(NULL);
    player->stopWalk();
    player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL) - 10);

    ReturnValue ret = internalUseItem(player, pos, index, item, 0);
    if(ret == RET_NOERROR)
        return true;

    player->sendCancelMessage(ret);
    return false;
}

bool Actions::executeUseEx(Action* action, Player* player, Item* item, const PositionEx& fromPosEx,
    const PositionEx& toPosEx, bool isHotkey, uint32_t creatureId)
{
    return (action->executeUse(player, item, fromPosEx, toPosEx, isHotkey,
        creatureId) || action->hasOwnErrorHandler());
}

ReturnValue Actions::internalUseItemEx(Player* player, const PositionEx& fromPosEx, const PositionEx& toPosEx,
    Item* item, bool isHotkey, uint32_t creatureId)
{
    Action* action = NULL;
    if((action = getAction(item, ACTION_UNIQUEID)))
    {
        ReturnValue ret = action->canExecuteAction(player, toPosEx);
        if(ret != RET_NOERROR)
            return ret;

        //only continue with next action in the list if the previous returns false
        if(executeUseEx(action, player, item, fromPosEx, toPosEx, isHotkey, creatureId))
            return RET_NOERROR;
    }

    if((action = getAction(item, ACTION_ACTIONID)))
    {
        ReturnValue ret = action->canExecuteAction(player, toPosEx);
        if(ret != RET_NOERROR)
            return ret;

        //only continue with next action in the list if the previous returns false
        if(executeUseEx(action, player, item, fromPosEx, toPosEx, isHotkey, creatureId))
            return RET_NOERROR;

    }

    if((action = getAction(item, ACTION_ITEMID)))
    {
        ReturnValue ret = action->canExecuteAction(player, toPosEx);
        if(ret != RET_NOERROR)
            return ret;

        //only continue with next action in the list if the previous returns false
        if(executeUseEx(action, player, item, fromPosEx, toPosEx, isHotkey, creatureId))
            return RET_NOERROR;
    }

    if((action = getAction(item, ACTION_RUNEID)))
    {
        ReturnValue ret = action->canExecuteAction(player, toPosEx);
        if(ret != RET_NOERROR)
            return ret;

        //only continue with next action in the list if the previous returns false
        if(executeUseEx(action, player, item, fromPosEx, toPosEx, isHotkey, creatureId))
            return RET_NOERROR;
    }

    if(defaultAction)
    {
        ReturnValue ret = defaultAction->canExecuteAction(player, toPosEx);
        if(ret != RET_NOERROR)
            return ret;

        //only continue with next action in the list if the previous returns false
        if(executeUseEx(defaultAction, player, item, fromPosEx, toPosEx, isHotkey, creatureId))
            return RET_NOERROR;
    }

    return RET_CANNOTUSETHISOBJECT;
}

bool Actions::useItemEx(Player* player, const Position& fromPos, const Position& toPos,
    uint8_t toStackPos, Item* item, bool isHotkey, uint32_t creatureId/* = 0*/)
{
    if(!player->canDoAction())
        return false;

    player->setNextActionTask(NULL);
    player->stopWalk();
    player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL) - 10);

    int32_t fromStackPos = 0;
    if(item->getParent())
        fromStackPos = item->getParent()->__getIndexOfThing(item);

    PositionEx fromPosEx(fromPos, fromStackPos);
    PositionEx toPosEx(toPos, toStackPos);

    ReturnValue ret = internalUseItemEx(player, fromPosEx, toPosEx, item, isHotkey, creatureId);
    if(ret == RET_NOERROR)
        return true;

    player->sendCancelMessage(ret);
    return false;
}

Action::Action(LuaInterface* _interface):
Event(_interface)
{
    allowFarUse = false;
    checkLineOfSight = true;
}

Action::Action(const Action* copy):
Event(copy)
{
    allowFarUse = copy->allowFarUse;
    checkLineOfSight = copy->checkLineOfSight;
}

bool Action::configureEvent(xmlNodePtr p)
{
    std::string strValue;
    if(readXMLString(p, "allowfaruse", strValue) || readXMLString(p, "allowFarUse", strValue))
        setAllowFarUse(booleanString(strValue));

    if(readXMLString(p, "blockwalls", strValue) || readXMLString(p, "blockWalls", strValue))
        setCheckLineOfSight(booleanString(strValue));

    return true;
}

ReturnValue Action::canExecuteAction(const Player* player, const Position& pos)
{
    if(player->hasCustomFlag(PlayerCustomFlag_CanUseFar))
        return RET_NOERROR;

    if(!getAllowFarUse())
        return g_actions->canUse(player, pos);

    return g_actions->canUseFar(player, pos, getCheckLineOfSight());
}

bool Action::executeUse(Player* player, Item* item, const PositionEx& fromPos, const PositionEx& toPos, bool extendedUse, uint32_t)
{
    //onUse(cid, item, fromPosition, itemEx, toPosition)
    if(m_interface->reserveEnv())
    {
        ScriptEnviroment* env = m_interface->getEnv();
        if(m_scripted == EVENT_SCRIPT_BUFFER)
        {
            env->setRealPos(player->getPosition());
            std::stringstream scriptstream;

            scriptstream << "local cid = " << env->addThing(player) << std::endl;
            env->streamThing(scriptstream, "item", item, env->addThing(item));
            env->streamPosition(scriptstream, "fromPosition", fromPos, fromPos.stackpos);

            Thing* thing = g_game.internalGetThing(player, toPos, toPos.stackpos);
            if(thing && (thing != item || !extendedUse))
            {
                env->streamThing(scriptstream, "itemEx", thing, env->addThing(thing));
                env->streamPosition(scriptstream, "toPosition", toPos, toPos.stackpos);
            }
            else
            {
                env->streamThing(scriptstream, "itemEx", NULL, 0);
                env->streamPosition(scriptstream, "toPosition", PositionEx());
            }

            if(m_scriptData)
                scriptstream << *m_scriptData;

            bool result = true;
            if(m_interface->loadBuffer(scriptstream.str()))
            {
                lua_State* L = m_interface->getState();
                result = m_interface->getGlobalBool(L, "_result", true);
            }

            m_interface->releaseEnv();
            return result;
        }
        else
        {
            #ifdef __DEBUG_LUASCRIPTS__
            std::stringstream desc;
            desc << player->getName() << " - " << item->getID() << " " << fromPos << "|" << toPos;
            env->setEvent(desc.str());
            #endif

            env->setScriptId(m_scriptId, m_interface);
            env->setRealPos(player->getPosition());

            lua_State* L = m_interface->getState();
            m_interface->pushFunction(m_scriptId);

            lua_pushnumber(L, env->addThing(player));
            LuaInterface::pushThing(L, item, env->addThing(item));
            LuaInterface::pushPosition(L, fromPos, fromPos.stackpos);

            Thing* thing = g_game.internalGetThing(player, toPos, toPos.stackpos);
            if(thing && (thing != item || !extendedUse))
            {
                LuaInterface::pushThing(L, thing, env->addThing(thing));
                LuaInterface::pushPosition(L, toPos, toPos.stackpos);
            }
            else
            {
                LuaInterface::pushThing(L, NULL, 0);
                LuaInterface::pushPosition(L, PositionEx());
            }

            bool result = m_interface->callFunction(5);
            m_interface->releaseEnv();
            return result;
        }
    }
    else
    {
        std::clog << "[Error - Action::executeUse]: Call stack overflow." << std::endl;
        return false;
    }
}
 

 

Editado por Toulouse (veja o histórico de edições)
Link para o post
Compartilhar em outros sites

@Toulouse, aparentemente está tudo normal. Posta o script da SD.

 

 

 

Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito.

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

@Way20 No ADM o exaust fica igual da sua foto, mas no player fica aquela lerdeza mesmo:

 

Spoiler

local combat = createCombatObject()
setCombatParam(combat, COMBAT_PARAM_TARGETCASTERORTOPMOST, true)
setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE)
setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MORTAREA)
setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_SUDDENDEATH)
setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -60, -1, -60, 5, 5, 4, 7)

function onCastSpell(cid, var)
    return doCombat(cid, combat, var)
end

 

Só as runas ficam lentas, agora outras action mega rapidas, como potion usa sem parar.

Editado por Toulouse (veja o histórico de edições)
Link para o post
Compartilhar em outros sites
3 minutos atrás, Toulouse disse:

@Way20 No ADM o exaust fica igual da sua foto, mas no player fica aquela lerdeza mesmo:

 

  Ocultar conteúdo

local combat = createCombatObject()
setCombatParam(combat, COMBAT_PARAM_TARGETCASTERORTOPMOST, true)
setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE)
setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MORTAREA)
setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_SUDDENDEATH)
setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -60, -1, -60, 5, 5, 4, 7)

function onCastSpell(cid, var)
    return doCombat(cid, combat, var)
end

 

Só as runas ficam lentas, agora outras action mega rapidas, como potion usa sem parar.

 

 

Verifica se ele diz se o player está com exhausted ou não, se simplesmente vai lerdo. E qual distro você está usando?

 

 

 

Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito.

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites
1 minuto atrás, Toulouse disse:

@Way20 Sim, diz que está com exaust, a distro é OTX Server Version: (2.52 - 1557)

 

Impossível isso, posta seu spells.xml.

 

 

 

Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito.

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

@Way20 

Spoiler

<?xml version="1.0" encoding="UTF-8"?>
<spells>
    <!-- MONSTER SPELLS -->
    <instant name="tower" words="tower" lvl="0" mana="0" prem="0" aggressive="1" exhaustion="1500" needlearn="1" event="script" value="monsters/tower.lua">
    </instant> 
    
    <!-- Attack Runes -->
    <rune name="Poison Field" id="2285" allowfaruse="1" charges="3" lvl="14" maglv="0" exhaustion="175" blocktype="solid" event="script" value="attack/poison field.lua"/>
    <rune name="Poison Bomb" id="2286" allowfaruse="1" charges="2" lvl="25" maglv="4" exhaustion="175" blocktype="solid" event="script" value="attack/poison bomb.lua"/>
    <rune name="Poison Wall" id="2289" allowfaruse="1" charges="4" lvl="29" maglv="5" exhaustion="175" blocktype="solid" event="script" value="attack/poison wall.lua"/>
    <rune name="Fire Field" id="2301" allowfaruse="1" charges="3" lvl="15" maglv="1" exhaustion="175" blocktype="solid" event="script" value="attack/fire field.lua"/>
    <rune name="Firebomb" id="2305" allowfaruse="1" charges="2" lvl="27" maglv="5" exhaustion="175" blocktype="solid" event="script" value="attack/fire bomb.lua"/>
    <rune name="Fire Wall" id="2303" allowfaruse="1" charges="4" lvl="33" maglv="6" exhaustion="175" blocktype="solid" event="script" value="attack/fire wall.lua"/>
    <rune name="Soulfire" id="2308" allowfaruse="1" charges="3" lvl="27" maglv="7" exhaustion="175" needtarget="1" blocktype="solid" event="script" value="attack/soul fire.lua"/>
    <rune name="Fireball" id="2302" allowfaruse="1" charges="5" lvl="27" maglv="4" exhaustion="175" needtarget="1" blocktype="solid" event="script" value="attack/fireball.lua"/>
    <rune name="Great Fireball" id="2304" allowfaruse="1" charges="4" lvl="30" maglv="4" exhaustion="175" blocktype="solid" event="script" value="attack/great fireball.lua"/>
    <rune name="Energy Field" id="2277" allowfaruse="1" charges="3" lvl="18" maglv="3" exhaustion="175" blocktype="solid" event="script" value="attack/energy field.lua"/>
    <rune name="Energybomb" id="2262" allowfaruse="1" charges="2" lvl="37" maglv="10" exhaustion="175" blocktype="solid" event="script" value="attack/energy bomb.lua"/>
    <rune name="Energy Wall" id="2279" allowfaruse="1" charges="4" lvl="41" maglv="9" exhaustion="175" blocktype="solid" event="script" value="attack/energy wall.lua"/>
    <rune name="Light Magic Missile" id="2287" allowfaruse="1" charges="10" lvl="15" exhaustion="175" maglv="0" needtarget="1" blocktype="solid" event="script" value="attack/light magic missile.lua"/>
    <rune name="Heavy Magic Missile" id="2311" allowfaruse="1" charges="10" lvl="25" exhaustion="175" maglv="3" needtarget="1" blocktype="solid" event="script" value="attack/heavy magic missile.lua"/>
    <rune name="Explosion" id="2313" allowfaruse="1" charges="6" lvl="31" maglv="6" exhaustion="175" blocktype="solid" event="script" value="attack/explosion.lua"/>
    <rune name="Sudden Death" id="2268" allowfaruse="1" charges="3" lvl="45" maglv="15" exhaustion="0" needtarget="1" blocktype="solid" event="script" value="attack/sudden death.lua"/>
    <rune name="Icicle" id="2271" allowfaruse="1" charges="5" lvl="28" maglv="4" exhaustion="175" needtarget="1" event="script" value="attack/icicle.lua"/>
    <rune name="Avalanche" id="2274" allowfaruse="1" charges="4" lvl="30" maglv="4" exhaustion="175" event="script" value="attack/avalanche.lua"/>
    <rune name="Stone Shower" id="2288" allowfaruse="1" charges="4" lvl="28" maglv="4" exhaustion="175" event="script" value="attack/stone shower.lua"/>
    <rune name="Thunderstorm" id="2315" allowfaruse="1" charges="4" lvl="28" maglv="4" exhaustion="175" event="script" value="attack/thunderstorm.lua"/>
    <rune name="Stalagmite" id="2292" allowfaruse="1" charges="10" lvl="24" maglv="3" exhaustion="175" needtarget="1" event="script" value="attack/stalagmite.lua"/>
    <rune name="Holy Missile" id="2295" allowfaruse="1" charges="5" lvl="27" maglv="4" exhaustion="175" needtarget="1" blocktype="solid" event="script" value="attack/holy missile.lua">
        <vocation id="3"/>
        <vocation id="7" showInDescription="0"/>
    </rune>

    <!-- Summon Runes -->
    <rune name="Convince Creature" id="2290" allowfaruse="1" charges="1" lvl="16" maglv="5" exhaustion="175" needtarget="1" blocktype="solid" event="function" value="convince"/>
    <rune name="Animate Dead" id="2316" allowfaruse="1" charges="1" lvl="27" maglv="4" exhaustion="175" blocktype="solid" event="script" value="summon/animate dead rune.lua"/>

    <!-- Support Runes -->
    <rune name="Intense Healing Rune" id="2265" allowfaruse="1" charges="1" lvl="15" maglv="1" exhaustion="175" aggressive="0" needtarget="1" blocktype="solid" event="script" value="healing/intense healing.lua"/>
    <rune name="Ultimate Healing Rune" id="2273" allowfaruse="1" charges="1" lvl="24" maglv="4" exhaustion="175" aggressive="0" needtarget="1" blocktype="solid" event="script" value="healing/ultimate healing rune.lua"/>
    <rune name="Desintegrate" id="2310" allowfaruse="0" charges="3" lvl="21" maglv="4" exhaustion="175" range="1" event="script" value="support/desintegrate rune.lua"/>
    <rune name="Chameleon" id="2291" allowfaruse="1" charges="1" lvl="27" maglv="4" exhaustion="175" aggressive="0" selftarget="1" blocktype="solid" event="function" value="chameleon"/>
    <rune name="Magic Wall" id="2293" allowfaruse="1" charges="3" lvl="32" maglv="9" exhaustion="575" blocktype="all" event="script" value="support/magic wall rune.lua"/>
    <rune name="Super Magic Wall" id="2296" allowfaruse="1" charges="3" lvl="32" maglv="9" exhaustion="575" blocktype="all" event="script" value="support/super mw.lua"/>
    <rune name="Wild Growth" id="2269" allowfaruse="1" charges="2" lvl="27" maglv="8" exhaustion="575" blocktype="all" event="script" value="support/wild growth rune.lua">
        <vocation id="2"/>
        <vocation id="6" showInDescription="0"/>
    </rune>
    <rune name="Paralyze" id="2278" allowfaruse="1" charges="1" lvl="54" maglv="18" exhaustion="575" mana="1400" needtarget="1" blocktype="solid" event="script" value="support/paralyze rune.lua">
        <vocation id="2"/>
        <vocation id="6" showInDescription="0"/>
    </rune>


    <!-- All -->
    <instant name="Under Hur" words="Under Hur" lvl="100" mana="20" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="attack/1underworld/under hur.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Death Strike" words="exori mort" lvl="16" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/death strike.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Flame Strike" words="exori flam" lvl="12" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/flame strike.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Energy Strike" words="exori vis" lvl="12" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/energy strike.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Whirlwind Throw" words="exori hur" lvl="15" mana="40" prem="0" range="5" needtarget="1" blockwalls="1" needweapon="1" exhaustion="1075" needlearn="0" event="script" value="attack/whirlwind throw.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Fire Wave" words="exevo flam hur" lvl="18" mana="25" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/fire wave.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Ethereal Spear" words="exori con" lvl="23" mana="25" prem="0" range="5" needtarget="1" exhaustion="1075" blockwalls="1" needlearn="0" event="script" value="attack/ethereal spear.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Energy Beam" words="exevo vis lux" lvl="23" mana="40" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/energy beam.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Great Energy Beam" words="exevo gran vis lux" lvl="29" mana="110" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/great energy beam.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Groundshaker" words="exori mas" lvl="33" mana="160" prem="0" needweapon="1" exhaustion="1075" needlearn="0" event="script" value="attack/groundshaker.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Berserk" words="exori" lvl="35" mana="115" prem="0" needweapon="1" exhaustion="1075" needlearn="0" event="script" value="attack/berserk.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Energy Wave" words="exevo vis hur" lvl="38" mana="170" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/energy wave.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Rage of the Skies" words="exevo gran mas vis" lvl="55" mana="650" selftarget="1" prem="0" exhaustion="1075" needlearn="0" event="script" value="attack/rage of the skies.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Fierce Berserk" words="exori gran" lvl="70" mana="340" prem="0" needweapon="1" exhaustion="1075" needlearn="0" event="script" value="attack/fierce berserk.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
        <instant name="Hells Core" words="exevo gran mas flam" lvl="60" mana="1200" prem="0" exhaustion="1075" selftarget="1" needlearn="0" event="script" value="attack/hells core.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Divine Missile" words="exori san" lvl="40" mana="20" prem="0" range="4" casterTargetOrDirection="1" needlearn="0" blockwalls="1" exhaustion="1075" event="script" value="attack/divine missile.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Divine Caldera" words="exevo mas san" lvl="50" mana="160" prem="0" selftarget="1" exhaustion="1075" needlearn="0" event="script" value="attack/divine caldera.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Eternal Winter" words="exevo gran mas frigo" lvl="60" mana="1200" prem="0" selftarget="1" exhaustion="1075" needlearn="0" event="script" value="attack/eternal winter.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Ice Strike" words="exori frigo" lvl="15" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/ice strike.lua">
        <vocation id="1"/>
        <vocation id="5"/>
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Ice Wave" words="exevo frigo hur" lvl="18" mana="25" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/ice wave.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Terra Strike" words="exori tera" lvl="13" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/terra strike.lua">
        <vocation id="1"/>
        <vocation id="5"/>
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Terra Wave" words="exevo tera hur" lvl="38" mana="210" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/terra wave.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Wrath of Nature" words="exevo gran mas tera" lvl="55" mana="770" prem="0" selftarget="1" exhaustion="1075" needlearn="0" event="script" value="attack/wrath of nature.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>

    <!-- Healing Spells -->
    <instant name="Light Healing" words="exura" lvl="9" mana="20" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/light healing.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Antidote" words="exana pox" lvl="10" mana="30" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/antidote.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="AntidoteFire" words="exana flam" lvl="10" mana="30" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/antidotefire.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="AntidoteVis" words="exana vis" lvl="10" mana="30" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/antidotevis.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Intense Healing" words="exura gran" lvl="11" mana="70" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/intense healing.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </instant>
    <instant name="Heal Friend" words="exura sio" lvl="18" mana="140" prem="0" aggressive="0" needtarget="1" params="1" exhaustion="575" needlearn="0" event="script" value="healing/heal friend.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Ultimate Healing" words="exura vita" lvl="20" mana="160" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/ultimate healing.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Mass Healing" words="exura gran mas res" lvl="36" mana="150" prem="0" aggressive="0" exhaustion="575" needlearn="0" event="script" value="healing/mass healing.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Divine Healing" words="exura san" lvl="35" mana="210" prem="0" selftarget="1" aggressive="0" exhaustion="575" needlearn="0" event="script" value="healing/divine healing.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Wound Cleansing" words="exana mort" lvl="30" mana="65" prem="0" selftarget="1" aggressive="0" exhaustion="575" needlearn="0" event="script" value="healing/wound cleasing.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>

    <!-- Support Spells -->
    <instant name="Light" words="utevo lux" lvl="8" mana="20" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/light.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Find Person" words="exiva" lvl="8" mana="20" aggressive="0" params="1" exhaustion="575" needlearn="0" event="function" value="searchPlayer">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Magic Rope" words="exani tera" lvl="9" mana="20" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/magic rope.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Levitate" words="exani hur" lvl="12" mana="50" prem="0" aggressive="0" exhaustion="575" params="1" needlearn="0" event="function" value="Levitate">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Great Light" words="utevo gran lux" lvl="13" mana="60" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/great light.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Magic Shield" words="utamo vita" lvl="14" mana="50" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/magic shield.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </instant>
    <instant name="Haste" words="utani hur" lvl="14" mana="60" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/haste.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Charge" words="utani tempo hur" lvl="25" mana="100" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/charge.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Swift Foot" words="utamo tempo san" lvl="55" mana="400" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/swift foot.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Challenge" words="exeta res" lvl="20" mana="30" prem="0" aggressive="0" exhaustion="575" needlearn="0" event="script" value="support/challenge.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Strong Haste" words="utani gran hur" lvl="20" mana="100" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/strong haste.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Creature Illusion" words="utevo res ina" lvl="23" mana="100" aggressive="0" params="1" exhaustion="575" needlearn="0" event="function" value="Illusion">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Ultimate Light" words="utevo vis lux" lvl="26" mana="140" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/ultimate light.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Cancel Invisibility" words="exana ina" lvl="26" mana="200" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/cancel invisibility.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Invisibility" words="utana vid" lvl="35" mana="440" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/invisible.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </instant>
    <instant name="Sharpshooter" words="utito tempo san" lvl="60" mana="450" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/sharpshooter.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Protector" words="utamo tempo" lvl="55" mana="200" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/protector.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Blood Rage" words="utito tempo" lvl="60" mana="290" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/blood rage.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>

    <!-- Party Spells -->
    <instant name="Train Party" words="utito mas sio" lvl="32" mana="60" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="party/train.lua">
        <vocation id="8"/>
    </instant>
    <instant name="Protect Party" words="utamo mas sio" lvl="32" mana="90" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="party/protect.lua">
        <vocation id="7"/>
    </instant>
    <instant name="Heal Party" words="utura mas sio" lvl="32" mana="120" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="party/heal.lua">
        <vocation id="6"/>
    </instant>
    <instant name="Enchant Party" words="utori mas sio" lvl="32" mana="120" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="party/enchant.lua">
        <vocation id="5"/>
    </instant>

    <!-- Summon Spells -->
    <instant name="Summon Creature" words="utevo res" lvl="25" params="1" exhaustion="1075" needlearn="0" event="function" value="summonMonster">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Undead Legion" words="exana mas mort" lvl="30" mana="500" prem="0" exhaustion="1075" needlearn="0" event="script" value="summon/undead legion.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>

    <!-- Conjure Spells -->
    <conjure name="Conjure Arrow" words="exevo con" lvl="13" mana="100" soul="1" conjureId="2544" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <instant name="Food" words="exevo pan" lvl="14" mana="120" soul="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/conjure food.lua">
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </instant>
    <conjure name="Poisoned Arrow" words="exevo con pox" lvl="16" mana="130" soul="2" conjureId="2545" conjureCount="7" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Adori Blank" words="adori blank" lvl="10" mana="10" conjureId="2260" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </conjure>
    <conjure name="Conjure Bolt" words="exevo con mort" lvl="17" mana="140" soul="2" prem="0" conjureId="2543" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Conjure Sniper Arrow" words="exevo con hur" lvl="24" mana="160" soul="3" prem="0" conjureId="7364" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Explosive Arrow" words="exevo con flam" lvl="25" mana="290" soul="3" conjureId="2546" conjureCount="8" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Conjure Piercing Bolt" words="exevo con grav" lvl="33" mana="180" soul="3" prem="0" conjureId="7363" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Enchant Staff" words="exeta vis" lvl="41" mana="80" prem="0" conjureId="2433" reagentId="2401" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="5"/>
    </conjure>
    <conjure name="Enchant Spear" words="exeta con" lvl="45" mana="350" soul="3" prem="0" conjureId="7367" reagentId="2389" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Power Bolt" words="exevo con vis" lvl="59" mana="800" soul="4" prem="0" conjureId="2547" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="7"/>
    </conjure>
    <conjure name="Poison Field" words="adevo grav pox" lvl="14" mana="200" soul="1" reagentId="2260" conjureId="2285" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Light Magic Missile" words="adori min vis" lvl="15" mana="120" soul="1" reagentId="2260" conjureId="2287" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Fire Field" words="adevo grav flam" lvl="15" mana="240" soul="1" reagentId="2260" conjureId="2301" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Fireball" words="adori flam" lvl="27" mana="460" soul="3" prem="0" reagentId="2260" conjureId="2302" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Energy Field" words="adevo grav vis" lvl="18" mana="320" soul="2" reagentId="2260" conjureId="2277" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Stalagmite" words="adori tera" lvl="24" mana="400" soul="2" prem="2" reagentId="2260" conjureId="2292" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Great Fireball" words="adori mas flam" lvl="30" mana="530" soul="3" reagentId="2260" conjureId="2304" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Heavy Magic Missile" words="adori vis" lvl="25" mana="350" soul="2" reagentId="2260" conjureId="2311" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Poison Bomb" words="adevo mas pox" lvl="25" mana="520" soul="2" prem="0" reagentId="2260" conjureId="2286" conjureCount="2" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Firebomb" words="adevo mas flam" lvl="27" mana="600" soul="4" reagentId="2260" conjureId="2305" conjureCount="2" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Soulfire" words="adevo res flam" lvl="27" mana="600" soul="3" prem="0" reagentId="2260" conjureId="2308" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Poison Wall" words="adevo mas grav pox" lvl="29" mana="640" soul="3" reagentId="2260" conjureId="2289" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Explosion" words="adevo mas hur" lvl="31" mana="570" soul="4" reagentId="2260" conjureId="2313" conjureCount="6" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Fire Wall" words="adevo mas grav flam" lvl="33" mana="780" soul="4" reagentId="2260" conjureId="2303" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Energybomb" words="adevo mas vis" lvl="37" mana="880" soul="5" prem="0" reagentId="2260" conjureId="2262" conjureCount="2" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Energy Wall" words="adevo mas grav vis" lvl="41" mana="1000" soul="5" reagentId="2260" conjureId="2279" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Sudden Death" words="adori gran mort" lvl="45" mana="985" soul="5" reagentId="2260" conjureId="2268" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Antidote Rune" words="adana pox" lvl="15" mana="200" soul="1" reagentId="2260" conjureId="2266" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Intense Healing Rune" words="adura gran" lvl="15" mana="240" soul="2" reagentId="2260" conjureId="2265" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Ultimate Healing Rune" words="adura vita" lvl="24" mana="400" soul="3" reagentId="2260" conjureId="2273" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Convince Creature" words="adeta sio" lvl="16" mana="200" soul="3" reagentId="2260" conjureId="2290" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Animate Dead" words="adana mort" lvl="27" mana="600" soul="5" prem="0" reagentId="2260" conjureId="2316" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Chameleon" words="adevo ina" lvl="27" mana="600" soul="2" reagentId="2260" conjureId="2291" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Destroy Field" words="adito grav" lvl="17" mana="120" soul="2" reagentId="2260" conjureId="2261" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Desintegrate" words="adito tera" lvl="21" mana="200" soul="3" prem="0" reagentId="2260" conjureId="2310" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Magic Wall" words="adevo grav tera" lvl="32" mana="750" soul="5" prem="0" reagentId="2260" conjureId="2293" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Wild Growth" words="adevo grav vita" lvl="27" mana="600" soul="5" prem="0" reagentId="2260" conjureId="2269" conjureCount="2" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Paralyze" words="adana ani" lvl="54" mana="1400" soul="3" prem="0" reagentId="2260" conjureId="2278" conjureCount="2" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Icicle" words="adori frigo" lvl="28" mana="460" soul="3" prem="0" reagentId="2260" conjureId="2271" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Avalanche" words="adori mas frigo" lvl="30" mana="530" soul="3" reagentId="2260" conjureId="2274" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Stone Shower" words="adori mas tera" lvl="28" mana="430" soul="3" prem="0" reagentId="2260" conjureId="2288" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Thunderstorm" words="adori mas vis" lvl="28" mana="430" soul="3" prem="0" reagentId="2260" conjureId="2315" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Holy Missile" words="adori san" lvl="27" mana="350" soul="3" prem="0" reagentId="2260" conjureId="2295" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
</spells>
 

 

Link para o post
Compartilhar em outros sites
11 minutos atrás, Toulouse disse:

@Way20 

  Mostrar conteúdo oculto

<?xml version="1.0" encoding="UTF-8"?>
<spells>
    <!-- MONSTER SPELLS -->
    <instant name="tower" words="tower" lvl="0" mana="0" prem="0" aggressive="1" exhaustion="1500" needlearn="1" event="script" value="monsters/tower.lua">
    </instant> 
    
    <!-- Attack Runes -->
    <rune name="Poison Field" id="2285" allowfaruse="1" charges="3" lvl="14" maglv="0" exhaustion="175" blocktype="solid" event="script" value="attack/poison field.lua"/>
    <rune name="Poison Bomb" id="2286" allowfaruse="1" charges="2" lvl="25" maglv="4" exhaustion="175" blocktype="solid" event="script" value="attack/poison bomb.lua"/>
    <rune name="Poison Wall" id="2289" allowfaruse="1" charges="4" lvl="29" maglv="5" exhaustion="175" blocktype="solid" event="script" value="attack/poison wall.lua"/>
    <rune name="Fire Field" id="2301" allowfaruse="1" charges="3" lvl="15" maglv="1" exhaustion="175" blocktype="solid" event="script" value="attack/fire field.lua"/>
    <rune name="Firebomb" id="2305" allowfaruse="1" charges="2" lvl="27" maglv="5" exhaustion="175" blocktype="solid" event="script" value="attack/fire bomb.lua"/>
    <rune name="Fire Wall" id="2303" allowfaruse="1" charges="4" lvl="33" maglv="6" exhaustion="175" blocktype="solid" event="script" value="attack/fire wall.lua"/>
    <rune name="Soulfire" id="2308" allowfaruse="1" charges="3" lvl="27" maglv="7" exhaustion="175" needtarget="1" blocktype="solid" event="script" value="attack/soul fire.lua"/>
    <rune name="Fireball" id="2302" allowfaruse="1" charges="5" lvl="27" maglv="4" exhaustion="175" needtarget="1" blocktype="solid" event="script" value="attack/fireball.lua"/>
    <rune name="Great Fireball" id="2304" allowfaruse="1" charges="4" lvl="30" maglv="4" exhaustion="175" blocktype="solid" event="script" value="attack/great fireball.lua"/>
    <rune name="Energy Field" id="2277" allowfaruse="1" charges="3" lvl="18" maglv="3" exhaustion="175" blocktype="solid" event="script" value="attack/energy field.lua"/>
    <rune name="Energybomb" id="2262" allowfaruse="1" charges="2" lvl="37" maglv="10" exhaustion="175" blocktype="solid" event="script" value="attack/energy bomb.lua"/>
    <rune name="Energy Wall" id="2279" allowfaruse="1" charges="4" lvl="41" maglv="9" exhaustion="175" blocktype="solid" event="script" value="attack/energy wall.lua"/>
    <rune name="Light Magic Missile" id="2287" allowfaruse="1" charges="10" lvl="15" exhaustion="175" maglv="0" needtarget="1" blocktype="solid" event="script" value="attack/light magic missile.lua"/>
    <rune name="Heavy Magic Missile" id="2311" allowfaruse="1" charges="10" lvl="25" exhaustion="175" maglv="3" needtarget="1" blocktype="solid" event="script" value="attack/heavy magic missile.lua"/>
    <rune name="Explosion" id="2313" allowfaruse="1" charges="6" lvl="31" maglv="6" exhaustion="175" blocktype="solid" event="script" value="attack/explosion.lua"/>
    <rune name="Sudden Death" id="2268" allowfaruse="1" charges="3" lvl="45" maglv="15" exhaustion="0" needtarget="1" blocktype="solid" event="script" value="attack/sudden death.lua"/>
    <rune name="Icicle" id="2271" allowfaruse="1" charges="5" lvl="28" maglv="4" exhaustion="175" needtarget="1" event="script" value="attack/icicle.lua"/>
    <rune name="Avalanche" id="2274" allowfaruse="1" charges="4" lvl="30" maglv="4" exhaustion="175" event="script" value="attack/avalanche.lua"/>
    <rune name="Stone Shower" id="2288" allowfaruse="1" charges="4" lvl="28" maglv="4" exhaustion="175" event="script" value="attack/stone shower.lua"/>
    <rune name="Thunderstorm" id="2315" allowfaruse="1" charges="4" lvl="28" maglv="4" exhaustion="175" event="script" value="attack/thunderstorm.lua"/>
    <rune name="Stalagmite" id="2292" allowfaruse="1" charges="10" lvl="24" maglv="3" exhaustion="175" needtarget="1" event="script" value="attack/stalagmite.lua"/>
    <rune name="Holy Missile" id="2295" allowfaruse="1" charges="5" lvl="27" maglv="4" exhaustion="175" needtarget="1" blocktype="solid" event="script" value="attack/holy missile.lua">
        <vocation id="3"/>
        <vocation id="7" showInDescription="0"/>
    </rune>

    <!-- Summon Runes -->
    <rune name="Convince Creature" id="2290" allowfaruse="1" charges="1" lvl="16" maglv="5" exhaustion="175" needtarget="1" blocktype="solid" event="function" value="convince"/>
    <rune name="Animate Dead" id="2316" allowfaruse="1" charges="1" lvl="27" maglv="4" exhaustion="175" blocktype="solid" event="script" value="summon/animate dead rune.lua"/>

    <!-- Support Runes -->
    <rune name="Intense Healing Rune" id="2265" allowfaruse="1" charges="1" lvl="15" maglv="1" exhaustion="175" aggressive="0" needtarget="1" blocktype="solid" event="script" value="healing/intense healing.lua"/>
    <rune name="Ultimate Healing Rune" id="2273" allowfaruse="1" charges="1" lvl="24" maglv="4" exhaustion="175" aggressive="0" needtarget="1" blocktype="solid" event="script" value="healing/ultimate healing rune.lua"/>
    <rune name="Desintegrate" id="2310" allowfaruse="0" charges="3" lvl="21" maglv="4" exhaustion="175" range="1" event="script" value="support/desintegrate rune.lua"/>
    <rune name="Chameleon" id="2291" allowfaruse="1" charges="1" lvl="27" maglv="4" exhaustion="175" aggressive="0" selftarget="1" blocktype="solid" event="function" value="chameleon"/>
    <rune name="Magic Wall" id="2293" allowfaruse="1" charges="3" lvl="32" maglv="9" exhaustion="575" blocktype="all" event="script" value="support/magic wall rune.lua"/>
    <rune name="Super Magic Wall" id="2296" allowfaruse="1" charges="3" lvl="32" maglv="9" exhaustion="575" blocktype="all" event="script" value="support/super mw.lua"/>
    <rune name="Wild Growth" id="2269" allowfaruse="1" charges="2" lvl="27" maglv="8" exhaustion="575" blocktype="all" event="script" value="support/wild growth rune.lua">
        <vocation id="2"/>
        <vocation id="6" showInDescription="0"/>
    </rune>
    <rune name="Paralyze" id="2278" allowfaruse="1" charges="1" lvl="54" maglv="18" exhaustion="575" mana="1400" needtarget="1" blocktype="solid" event="script" value="support/paralyze rune.lua">
        <vocation id="2"/>
        <vocation id="6" showInDescription="0"/>
    </rune>


    <!-- All -->
    <instant name="Under Hur" words="Under Hur" lvl="100" mana="20" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="attack/1underworld/under hur.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Death Strike" words="exori mort" lvl="16" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/death strike.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Flame Strike" words="exori flam" lvl="12" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/flame strike.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Energy Strike" words="exori vis" lvl="12" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/energy strike.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Whirlwind Throw" words="exori hur" lvl="15" mana="40" prem="0" range="5" needtarget="1" blockwalls="1" needweapon="1" exhaustion="1075" needlearn="0" event="script" value="attack/whirlwind throw.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Fire Wave" words="exevo flam hur" lvl="18" mana="25" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/fire wave.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Ethereal Spear" words="exori con" lvl="23" mana="25" prem="0" range="5" needtarget="1" exhaustion="1075" blockwalls="1" needlearn="0" event="script" value="attack/ethereal spear.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Energy Beam" words="exevo vis lux" lvl="23" mana="40" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/energy beam.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Great Energy Beam" words="exevo gran vis lux" lvl="29" mana="110" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/great energy beam.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Groundshaker" words="exori mas" lvl="33" mana="160" prem="0" needweapon="1" exhaustion="1075" needlearn="0" event="script" value="attack/groundshaker.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Berserk" words="exori" lvl="35" mana="115" prem="0" needweapon="1" exhaustion="1075" needlearn="0" event="script" value="attack/berserk.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Energy Wave" words="exevo vis hur" lvl="38" mana="170" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/energy wave.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Rage of the Skies" words="exevo gran mas vis" lvl="55" mana="650" selftarget="1" prem="0" exhaustion="1075" needlearn="0" event="script" value="attack/rage of the skies.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Fierce Berserk" words="exori gran" lvl="70" mana="340" prem="0" needweapon="1" exhaustion="1075" needlearn="0" event="script" value="attack/fierce berserk.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
        <instant name="Hells Core" words="exevo gran mas flam" lvl="60" mana="1200" prem="0" exhaustion="1075" selftarget="1" needlearn="0" event="script" value="attack/hells core.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Divine Missile" words="exori san" lvl="40" mana="20" prem="0" range="4" casterTargetOrDirection="1" needlearn="0" blockwalls="1" exhaustion="1075" event="script" value="attack/divine missile.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Divine Caldera" words="exevo mas san" lvl="50" mana="160" prem="0" selftarget="1" exhaustion="1075" needlearn="0" event="script" value="attack/divine caldera.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Eternal Winter" words="exevo gran mas frigo" lvl="60" mana="1200" prem="0" selftarget="1" exhaustion="1075" needlearn="0" event="script" value="attack/eternal winter.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Ice Strike" words="exori frigo" lvl="15" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/ice strike.lua">
        <vocation id="1"/>
        <vocation id="5"/>
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Ice Wave" words="exevo frigo hur" lvl="18" mana="25" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/ice wave.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Terra Strike" words="exori tera" lvl="13" mana="20" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="1075" needlearn="0" event="script" value="attack/terra strike.lua">
        <vocation id="1"/>
        <vocation id="5"/>
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Terra Wave" words="exevo tera hur" lvl="38" mana="210" direction="1" exhaustion="1075" needlearn="0" event="script" value="attack/terra wave.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Wrath of Nature" words="exevo gran mas tera" lvl="55" mana="770" prem="0" selftarget="1" exhaustion="1075" needlearn="0" event="script" value="attack/wrath of nature.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>

    <!-- Healing Spells -->
    <instant name="Light Healing" words="exura" lvl="9" mana="20" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/light healing.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Antidote" words="exana pox" lvl="10" mana="30" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/antidote.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="AntidoteFire" words="exana flam" lvl="10" mana="30" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/antidotefire.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="AntidoteVis" words="exana vis" lvl="10" mana="30" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/antidotevis.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Intense Healing" words="exura gran" lvl="11" mana="70" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/intense healing.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </instant>
    <instant name="Heal Friend" words="exura sio" lvl="18" mana="140" prem="0" aggressive="0" needtarget="1" params="1" exhaustion="575" needlearn="0" event="script" value="healing/heal friend.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Ultimate Healing" words="exura vita" lvl="20" mana="160" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="healing/ultimate healing.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Mass Healing" words="exura gran mas res" lvl="36" mana="150" prem="0" aggressive="0" exhaustion="575" needlearn="0" event="script" value="healing/mass healing.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>
    <instant name="Divine Healing" words="exura san" lvl="35" mana="210" prem="0" selftarget="1" aggressive="0" exhaustion="575" needlearn="0" event="script" value="healing/divine healing.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Wound Cleansing" words="exana mort" lvl="30" mana="65" prem="0" selftarget="1" aggressive="0" exhaustion="575" needlearn="0" event="script" value="healing/wound cleasing.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>

    <!-- Support Spells -->
    <instant name="Light" words="utevo lux" lvl="8" mana="20" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/light.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Find Person" words="exiva" lvl="8" mana="20" aggressive="0" params="1" exhaustion="575" needlearn="0" event="function" value="searchPlayer">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Magic Rope" words="exani tera" lvl="9" mana="20" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/magic rope.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Levitate" words="exani hur" lvl="12" mana="50" prem="0" aggressive="0" exhaustion="575" params="1" needlearn="0" event="function" value="Levitate">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Great Light" words="utevo gran lux" lvl="13" mana="60" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/great light.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Magic Shield" words="utamo vita" lvl="14" mana="50" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/magic shield.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </instant>
    <instant name="Haste" words="utani hur" lvl="14" mana="60" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/haste.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </instant>
    <instant name="Charge" words="utani tempo hur" lvl="25" mana="100" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/charge.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Swift Foot" words="utamo tempo san" lvl="55" mana="400" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/swift foot.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Challenge" words="exeta res" lvl="20" mana="30" prem="0" aggressive="0" exhaustion="575" needlearn="0" event="script" value="support/challenge.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Strong Haste" words="utani gran hur" lvl="20" mana="100" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/strong haste.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Creature Illusion" words="utevo res ina" lvl="23" mana="100" aggressive="0" params="1" exhaustion="575" needlearn="0" event="function" value="Illusion">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Ultimate Light" words="utevo vis lux" lvl="26" mana="140" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/ultimate light.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Cancel Invisibility" words="exana ina" lvl="26" mana="200" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/cancel invisibility.lua">
        <vocation id="1"/>
        <vocation id="5"/>
    </instant>
    <instant name="Invisibility" words="utana vid" lvl="35" mana="440" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/invisible.lua">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </instant>
    <instant name="Sharpshooter" words="utito tempo san" lvl="60" mana="450" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/sharpshooter.lua">
        <vocation id="3"/>
        <vocation id="7"/>
    </instant>
    <instant name="Protector" words="utamo tempo" lvl="55" mana="200" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/protector.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>
    <instant name="Blood Rage" words="utito tempo" lvl="60" mana="290" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/blood rage.lua">
        <vocation id="4"/>
        <vocation id="8"/>
    </instant>

    <!-- Party Spells -->
    <instant name="Train Party" words="utito mas sio" lvl="32" mana="60" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="party/train.lua">
        <vocation id="8"/>
    </instant>
    <instant name="Protect Party" words="utamo mas sio" lvl="32" mana="90" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="party/protect.lua">
        <vocation id="7"/>
    </instant>
    <instant name="Heal Party" words="utura mas sio" lvl="32" mana="120" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="party/heal.lua">
        <vocation id="6"/>
    </instant>
    <instant name="Enchant Party" words="utori mas sio" lvl="32" mana="120" prem="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="party/enchant.lua">
        <vocation id="5"/>
    </instant>

    <!-- Summon Spells -->
    <instant name="Summon Creature" words="utevo res" lvl="25" params="1" exhaustion="1075" needlearn="0" event="function" value="summonMonster">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </instant>
    <instant name="Undead Legion" words="exana mas mort" lvl="30" mana="500" prem="0" exhaustion="1075" needlearn="0" event="script" value="summon/undead legion.lua">
        <vocation id="2"/>
        <vocation id="6"/>
    </instant>

    <!-- Conjure Spells -->
    <conjure name="Conjure Arrow" words="exevo con" lvl="13" mana="100" soul="1" conjureId="2544" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <instant name="Food" words="exevo pan" lvl="14" mana="120" soul="0" aggressive="0" selftarget="1" exhaustion="575" needlearn="0" event="script" value="support/conjure food.lua">
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </instant>
    <conjure name="Poisoned Arrow" words="exevo con pox" lvl="16" mana="130" soul="2" conjureId="2545" conjureCount="7" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Adori Blank" words="adori blank" lvl="10" mana="10" conjureId="2260" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="4"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
        <vocation id="8"/>
    </conjure>
    <conjure name="Conjure Bolt" words="exevo con mort" lvl="17" mana="140" soul="2" prem="0" conjureId="2543" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Conjure Sniper Arrow" words="exevo con hur" lvl="24" mana="160" soul="3" prem="0" conjureId="7364" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Explosive Arrow" words="exevo con flam" lvl="25" mana="290" soul="3" conjureId="2546" conjureCount="8" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Conjure Piercing Bolt" words="exevo con grav" lvl="33" mana="180" soul="3" prem="0" conjureId="7363" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Enchant Staff" words="exeta vis" lvl="41" mana="80" prem="0" conjureId="2433" reagentId="2401" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="5"/>
    </conjure>
    <conjure name="Enchant Spear" words="exeta con" lvl="45" mana="350" soul="3" prem="0" conjureId="7367" reagentId="2389" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Power Bolt" words="exevo con vis" lvl="59" mana="800" soul="4" prem="0" conjureId="2547" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureItem">
        <vocation id="7"/>
    </conjure>
    <conjure name="Poison Field" words="adevo grav pox" lvl="14" mana="200" soul="1" reagentId="2260" conjureId="2285" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Light Magic Missile" words="adori min vis" lvl="15" mana="120" soul="1" reagentId="2260" conjureId="2287" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Fire Field" words="adevo grav flam" lvl="15" mana="240" soul="1" reagentId="2260" conjureId="2301" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Fireball" words="adori flam" lvl="27" mana="460" soul="3" prem="0" reagentId="2260" conjureId="2302" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Energy Field" words="adevo grav vis" lvl="18" mana="320" soul="2" reagentId="2260" conjureId="2277" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Stalagmite" words="adori tera" lvl="24" mana="400" soul="2" prem="2" reagentId="2260" conjureId="2292" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Great Fireball" words="adori mas flam" lvl="30" mana="530" soul="3" reagentId="2260" conjureId="2304" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Heavy Magic Missile" words="adori vis" lvl="25" mana="350" soul="2" reagentId="2260" conjureId="2311" conjureCount="10" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Poison Bomb" words="adevo mas pox" lvl="25" mana="520" soul="2" prem="0" reagentId="2260" conjureId="2286" conjureCount="2" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Firebomb" words="adevo mas flam" lvl="27" mana="600" soul="4" reagentId="2260" conjureId="2305" conjureCount="2" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Soulfire" words="adevo res flam" lvl="27" mana="600" soul="3" prem="0" reagentId="2260" conjureId="2308" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Poison Wall" words="adevo mas grav pox" lvl="29" mana="640" soul="3" reagentId="2260" conjureId="2289" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Explosion" words="adevo mas hur" lvl="31" mana="570" soul="4" reagentId="2260" conjureId="2313" conjureCount="6" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Fire Wall" words="adevo mas grav flam" lvl="33" mana="780" soul="4" reagentId="2260" conjureId="2303" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Energybomb" words="adevo mas vis" lvl="37" mana="880" soul="5" prem="0" reagentId="2260" conjureId="2262" conjureCount="2" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Energy Wall" words="adevo mas grav vis" lvl="41" mana="1000" soul="5" reagentId="2260" conjureId="2279" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Sudden Death" words="adori gran mort" lvl="45" mana="985" soul="5" reagentId="2260" conjureId="2268" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Antidote Rune" words="adana pox" lvl="15" mana="200" soul="1" reagentId="2260" conjureId="2266" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Intense Healing Rune" words="adura gran" lvl="15" mana="240" soul="2" reagentId="2260" conjureId="2265" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Ultimate Healing Rune" words="adura vita" lvl="24" mana="400" soul="3" reagentId="2260" conjureId="2273" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Convince Creature" words="adeta sio" lvl="16" mana="200" soul="3" reagentId="2260" conjureId="2290" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Animate Dead" words="adana mort" lvl="27" mana="600" soul="5" prem="0" reagentId="2260" conjureId="2316" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="5"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Chameleon" words="adevo ina" lvl="27" mana="600" soul="2" reagentId="2260" conjureId="2291" conjureCount="1" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Destroy Field" words="adito grav" lvl="17" mana="120" soul="2" reagentId="2260" conjureId="2261" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Desintegrate" words="adito tera" lvl="21" mana="200" soul="3" prem="0" reagentId="2260" conjureId="2310" conjureCount="3" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="2"/>
        <vocation id="3"/>
        <vocation id="5"/>
        <vocation id="6"/>
        <vocation id="7"/>
    </conjure>
    <conjure name="Magic Wall" words="adevo grav tera" lvl="32" mana="750" soul="5" prem="0" reagentId="2260" conjureId="2293" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Wild Growth" words="adevo grav vita" lvl="27" mana="600" soul="5" prem="0" reagentId="2260" conjureId="2269" conjureCount="2" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Paralyze" words="adana ani" lvl="54" mana="1400" soul="3" prem="0" reagentId="2260" conjureId="2278" conjureCount="2" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Icicle" words="adori frigo" lvl="28" mana="460" soul="3" prem="0" reagentId="2260" conjureId="2271" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Avalanche" words="adori mas frigo" lvl="30" mana="530" soul="3" reagentId="2260" conjureId="2274" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Stone Shower" words="adori mas tera" lvl="28" mana="430" soul="3" prem="0" reagentId="2260" conjureId="2288" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="2"/>
        <vocation id="6"/>
    </conjure>
    <conjure name="Thunderstorm" words="adori mas vis" lvl="28" mana="430" soul="3" prem="0" reagentId="2260" conjureId="2315" conjureCount="4" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="1"/>
        <vocation id="5"/>
    </conjure>
    <conjure name="Holy Missile" words="adori san" lvl="27" mana="350" soul="3" prem="0" reagentId="2260" conjureId="2295" conjureCount="5" exhaustion="575" needlearn="0" event="function" value="conjureRune">
        <vocation id="3"/>
        <vocation id="7"/>
    </conjure>
</spells>
 

 

 

Não tenho ideia do que possar ser então, não há verificação de nenhuma flag referente a exhausted em actions.cpp.

 

 

 

Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito.

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites
15 minutos atrás, Toulouse disse:

@Way20 Apesar de em adm ser tudo sempre mais rapido, nele aquele fast na sd aplica, mas nos players não, só acontece  com runa, não sei mais o que fazer tbm

 

A questão é que como eu falei, pelo menos em actions.cpp não está sendo feito verificação em nenhuma flag de exhausted, então não tem porque funcionar no god e no player não.

 

 

 

Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito.

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

@Way20 Não pode ser em outra parte da source? 

 

Em creatures.h tem isso:

Spoiler

#ifndef __GROUPED_ATTACKS__
#define EVENT_CREATURE_THINK_INTERVAL 1000
#else
#define EVENT_CREATURE_THINK_INTERVAL 500
#endif

 

Não seria uma alguma definição de delay?

Link para o post
Compartilhar em outros sites
5 horas atrás, Toulouse disse:

@Way20 Não pode ser em outra parte da source? 

 

Em creatures.h tem isso:

  Ocultar conteúdo

#ifndef __GROUPED_ATTACKS__
#define EVENT_CREATURE_THINK_INTERVAL 1000
#else
#define EVENT_CREATURE_THINK_INTERVAL 500
#endif

 

Não seria uma alguma definição de delay?

 

Pode ser em outra parte sim, mas não é creature.h, essa parada que tu mandou não tem nada a ver. Me envia seu game.cpp.

 

 

 

Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito.

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

@Way20 

 

Spoiler

////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// 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 3 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, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include "game.h"

#include "configmanager.h"
#ifdef __LOGIN_SERVER__
#include "gameservers.h"
#endif
#include "server.h"
#include "chat.h"

#include "luascript.h"
#include "creature.h"
#include "combat.h"
#include "tile.h"

#include "database.h"
#include "iologindata.h"
#include "ioban.h"
#include "ioguild.h"

#include "items.h"
#include "trashholder.h"
#include "container.h"
#include "monsters.h"

#include "house.h"
#include "quests.h"

#include "actions.h"
#include "globalevent.h"
#include "movement.h"
#include "raids.h"
#include "scriptmanager.h"
#include "spells.h"
#include "talkaction.h"
#include "weapons.h"

#include "textlogger.h"
#include "vocation.h"
#include "group.h"

#ifdef __EXCEPTION_TRACER__
#include "exception.h"
#endif

extern ConfigManager g_config;
extern Actions* g_actions;
extern Monsters g_monsters;
extern Npcs g_npcs;
extern Chat g_chat;
extern TalkActions* g_talkActions;
extern Spells* g_spells;
extern MoveEvents* g_moveEvents;
extern Weapons* g_weapons;
extern CreatureEvents* g_creatureEvents;
extern GlobalEvents* g_globalEvents;

Game::Game()
{
    gameState = GAMESTATE_NORMAL;
    worldType = WORLDTYPE_OPEN;
    map = NULL;
    playersRecord = lastStageLevel = 0;

    //(1440 minutes/day) * 10 seconds event interval / (3600 seconds/day)
    lightHourDelta = 1440 * 10 / 3600;
    lightHour = SUNRISE + (SUNSET - SUNRISE) / 2;
    lightLevel = LIGHT_LEVEL_DAY;
    lightState = LIGHT_STATE_DAY;

    lastBucket = checkCreatureLastIndex = checkLightEvent = checkCreatureEvent = checkDecayEvent = saveEvent = 0;
    checkWarsEvent = 0;
    checkEndingWars = false;
}

Game::~Game()
{
    delete map;
}

void Game::start(ServiceManager* servicer)
{
    checkDecayEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_DECAYINTERVAL,
        boost::bind(&Game::checkDecay, this)));
    checkCreatureEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_CREATURE_THINK_INTERVAL,
        boost::bind(&Game::checkCreatures, this)));
    checkLightEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL,
        boost::bind(&Game::checkLight, this)));
    checkWarsEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_WARSINTERVAL,
        boost::bind(&Game::checkWars, this)));

    services = servicer;
    if(!g_config.getBool(ConfigManager::GLOBALSAVE_ENABLED))
        return;

    int32_t prepareHour = g_config.getNumber(ConfigManager::GLOBALSAVE_H),
        prepareMinute = g_config.getNumber(ConfigManager::GLOBALSAVE_M);

    if(prepareHour < 0 || prepareHour > 24)
    {
        std::clog << "> WARNING: No valid hour (" << prepareHour << ") for a global save, should be between 0-23. Global save disabled." << std::endl;
        return;
    }

    if(prepareMinute < 0 || prepareMinute > 59)
    {
        std::clog << "> WARNING: No valid minute (" << prepareMinute << ") for a global save, should be between 0-59. Global save disabled." << std::endl;
        return;
    }

    time_t timeNow = time(NULL);
    const tm* theTime = localtime(&timeNow);

    int32_t hour = theTime->tm_hour, minute = theTime->tm_min, second = theTime->tm_sec,
        hoursLeft = 0, minutesLeft = 0, broadcast = 5;

    if(!prepareHour)
        prepareHour = 24;

    if(hour != prepareHour)
    {
        if(prepareMinute >= 5)
            prepareMinute -= 5;
        else
        {
            prepareHour--;
            prepareMinute = 55 + prepareMinute;
        }

        if(hour > prepareHour)
            hoursLeft = 24 - (hour - prepareHour);
        else
            hoursLeft = prepareHour - hour;

        if(minute > prepareMinute)
        {
            minutesLeft = 60 - (minute - prepareMinute);
            hoursLeft--;
        }
        else if(minute != prepareMinute)
            minutesLeft = prepareMinute - minute;
    }
    else
    {
        if(minute > prepareMinute)
        {
            minutesLeft = 55 - (minute - prepareMinute);
            hoursLeft = 23;
        }
        else
        {
            minutesLeft = prepareMinute - minute;
            if(minutesLeft >= 5)
                minutesLeft = minutesLeft - 5;
            else if(minutesLeft == 3 || minutesLeft == 1)
            {
                prepareGlobalSave(minutesLeft);
                return;
            }
            else if(minutesLeft > 0)
            {
                broadcast = (minutesLeft == 2 ? 1 : 3);
                minutesLeft = 1;
            }
        }
    }

    uint32_t timeLeft = (hoursLeft * 60 * 60 * 1000) + (minutesLeft * 60 * 1000);
    if(timeLeft > 0)
    {
        timeLeft -= second * 1000;
        saveEvent = Scheduler::getInstance().addEvent(createSchedulerTask(timeLeft,
            boost::bind(&Game::prepareGlobalSave, this, broadcast)));
    }
}

void Game::loadGameState()
{
    ScriptEnviroment::loadGameState();
    loadPlayersRecord();
    loadMotd();
}

void Game::setGameState(GameState_t newState)
{
    if(gameState == GAMESTATE_SHUTDOWN)
        return; //this cannot be stopped

    if(gameState != newState)
    {
        gameState = newState;
        switch(newState)
        {
            case GAMESTATE_INIT:
            {
                Spawns::getInstance()->startup();
                Raids::getInstance()->startup();

                loadGameState();

                g_globalEvents->startup();
                if(g_config.getBool(ConfigManager::INIT_PREMIUM_UPDATE))
                    IOLoginData::getInstance()->updatePremiumDays();

                IOGuild::getInstance()->checkWars();
                IOGuild::getInstance()->checkEndingWars();
                break;
            }

            case GAMESTATE_SHUTDOWN:
            {
                g_globalEvents->execute(GLOBALEVENT_SHUTDOWN);
                AutoList<Player>::iterator it = Player::autoList.begin();
                while(it != Player::autoList.end()) //kick all players that are still online
                {
                    it->second->kick(true, true);
                    it = Player::autoList.begin();
                }

                Houses::getInstance()->check();
                saveGameState((uint8_t)SAVE_PLAYERS | (uint8_t)SAVE_MAP | (uint8_t)SAVE_STATE);

                if(g_config.getBool(ConfigManager::CLOSE_INSTANCE_ON_SHUTDOWN))
                    Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::shutdown, this)));

                Scheduler::getInstance().stop();
                Dispatcher::getInstance().stop();
                break;
            }

            case GAMESTATE_CLOSED:
            {
                AutoList<Player>::iterator it = Player::autoList.begin();
                while(it != Player::autoList.end()) //kick all players who not allowed to stay
                {
                    if(!it->second->hasFlag(PlayerFlag_CanAlwaysLogin))
                    {
                        it->second->kick(true, true);
                        it = Player::autoList.begin();
                    }
                    else
                        ++it;
                }

                map->updateAuctions();
                saveGameState((uint8_t)SAVE_PLAYERS | (uint8_t)SAVE_MAP | (uint8_t)SAVE_STATE);
                break;
            }

            case GAMESTATE_NORMAL:
            case GAMESTATE_MAINTAIN:
            case GAMESTATE_STARTUP:
            case GAMESTATE_CLOSING:
            default:
                break;
        }
    }
}

void Game::saveGameState(uint8_t flags)
{
    std::clog << "> Saving server..." << std::endl;
    uint64_t start = OTSYS_TIME();
    if(gameState == GAMESTATE_NORMAL)
        setGameState(GAMESTATE_MAINTAIN);

    if(hasBitSet(SAVE_PLAYERS, flags))
    {
        IOLoginData* io = IOLoginData::getInstance();
        for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
        {
            it->second->loginPosition = it->second->getPosition();
            io->savePlayer(it->second, false, hasBitSet(SAVE_PLAYERS_SHALLOW, flags));
        }
    }

    if(hasBitSet(SAVE_MAP, flags))
        map->saveMap();

    if(hasBitSet(SAVE_STATE, flags))
        ScriptEnviroment::saveGameState();

    if(gameState == GAMESTATE_MAINTAIN)
        setGameState(GAMESTATE_NORMAL);

    std::clog << "> SAVE: Complete in " << (OTSYS_TIME() - start) / (1000.) << " seconds using "
        << asLowerCaseString(g_config.getString(ConfigManager::HOUSE_STORAGE))
        << " house storage." << std::endl;
}

int32_t Game::loadMap(std::string filename)
{
    if(!map)
        map = new Map;

    std::string file = getFilePath(FILE_TYPE_CONFIG, "world/" + filename);
    if(!fileExists(file.c_str()))
        file = getFilePath(FILE_TYPE_OTHER, "world/" + filename);

    return map->loadMap(file);
}

void Game::cleanMapEx(uint32_t& count)
{
    uint64_t start = OTSYS_TIME();
    uint32_t tiles = 0; count = 0;

    int32_t marked = -1;
    if(gameState == GAMESTATE_NORMAL)
        setGameState(GAMESTATE_MAINTAIN);

    Tile* tile = NULL;
    ItemVector::iterator tit;
    if(g_config.getBool(ConfigManager::STORE_TRASH))
    {
        marked = trash.size();
        Trash::iterator it = trash.begin();
        if(g_config.getBool(ConfigManager::CLEAN_PROTECTED_ZONES))
        {
            for(; it != trash.end(); ++it)
            {
                if(!(tile = getTile(*it)))
                    continue;

                tile->resetFlag(TILESTATE_TRASHED);
                if(tile->hasFlag(TILESTATE_HOUSE) || !tile->getItemList())
                    continue;

                ++tiles;
                tit = tile->getItemList()->begin();
                while(tile->getItemList() && tit != tile->getItemList()->end())
                {
                    if((*tit)->isMovable() && !(*tit)->isLoadedFromMap()
                        && !(*tit)->isScriptProtected())
                    {
                        internalRemoveItem(NULL, *tit);
                        if(tile->getItemList())
                            tit = tile->getItemList()->begin();

                        ++count;
                    }
                    else
                        ++tit;
                }
            }
        }
        else
        {
            for(; it != trash.end(); ++it)
            {
                if(!(tile = getTile(*it)))
                    continue;

                tile->resetFlag(TILESTATE_TRASHED);
                if(tile->hasFlag(TILESTATE_PROTECTIONZONE) || !tile->getItemList())
                    continue;

                ++tiles;
                tit = tile->getItemList()->begin();
                while(tile->getItemList() && tit != tile->getItemList()->end())
                {
                    if((*tit)->isMovable() && !(*tit)->isLoadedFromMap()
                        && !(*tit)->isScriptProtected())
                    {
                        internalRemoveItem(NULL, *tit);
                        if(tile->getItemList())
                            tit = tile->getItemList()->begin();

                        ++count;
                    }
                    else
                        ++tit;
                }
            }
        }

        trash.clear();
    }
    else if(g_config.getBool(ConfigManager::CLEAN_PROTECTED_ZONES))
    {
        for(uint16_t z = 0; z < (uint16_t)MAP_MAX_LAYERS; z++)
        {
            for(uint16_t y = 1; y <= map->mapHeight; y++)
            {
                for(uint16_t x = 1; x <= map->mapWidth; x++)
                {
                    if(!(tile = getTile(x, y, z)) || tile->hasFlag(TILESTATE_HOUSE) || !tile->getItemList())
                        continue;

                    ++tiles;
                    tit = tile->getItemList()->begin();
                    while(tile->getItemList() && tit != tile->getItemList()->end())
                    {
                        if((*tit)->isMovable() && !(*tit)->isLoadedFromMap()
                            && !(*tit)->isScriptProtected())
                        {
                            internalRemoveItem(NULL, *tit);
                            if(tile->getItemList())
                                tit = tile->getItemList()->begin();

                            ++count;
                        }
                        else
                            ++tit;
                    }
                }
            }
        }
    }
    else
    {
        for(uint16_t z = 0; z < (uint16_t)MAP_MAX_LAYERS; z++)
        {
            for(uint16_t y = 1; y <= map->mapHeight; y++)
            {
                for(uint16_t x = 1; x <= map->mapWidth; x++)
                {
                    if(!(tile = getTile(x, y, z)) || tile->hasFlag(TILESTATE_PROTECTIONZONE) || !tile->getItemList())
                        continue;

                    ++tiles;
                    tit = tile->getItemList()->begin();
                    while(tile->getItemList() && tit != tile->getItemList()->end())
                    {
                        if((*tit)->isMovable() && !(*tit)->isLoadedFromMap()
                            && !(*tit)->isScriptProtected())
                        {
                            internalRemoveItem(NULL, *tit);
                            if(tile->getItemList())
                                tit = tile->getItemList()->begin();

                            ++count;
                        }
                        else
                            ++tit;
                    }
                }
            }
        }
    }

    if(gameState == GAMESTATE_MAINTAIN)
        setGameState(GAMESTATE_NORMAL);

    std::clog << "> CLEAN: Removed " << count << " item" << (count != 1 ? "s" : "")
        << " from " << tiles << " tile" << (tiles != 1 ? "s" : "");
    if(marked >= 0)
        std::clog << " (" << marked << " were marked)";

    std::clog << " in " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl;
}

void Game::cleanMap()
{
    uint32_t dummy;
    cleanMapEx(dummy);
}

void Game::proceduralRefresh(RefreshTiles::iterator* it/* = NULL*/)
{
    if(!it)
        it = new RefreshTiles::iterator(refreshTiles.begin());

    // Refresh 250 tiles each cycle
    refreshMap(it, 250);
    if((*it) != refreshTiles.end())
    {
        delete it;
        return;
    }

    // Refresh some items every 100 ms until all tiles has been checked
    // For 100k tiles, this would take 100000/2500 = 40s = half a minute
    Scheduler::getInstance().addEvent(createSchedulerTask(SCHEDULER_MINTICKS,
        boost::bind(&Game::proceduralRefresh, this, it)));
}

void Game::refreshMap(RefreshTiles::iterator* it/* = NULL*/, uint32_t limit/* = 0*/)
{
    RefreshTiles::iterator end = refreshTiles.end();
    if(!it)
    {
        RefreshTiles::iterator begin = refreshTiles.begin();
        it = &begin;
    }

    Tile* tile = NULL;
    TileItemVector* items = NULL;

    Item* item = NULL;
    for(uint32_t cleaned = 0, downItemsSize = 0; (*it) != end &&
        (limit ? (cleaned < limit) : true); ++(*it), ++cleaned)
    {
        if(!(tile = (*it)->first))
            continue;

        if((items = tile->getItemList()))
        {
            downItemsSize = tile->getDownItemCount();
            for(uint32_t i = downItemsSize - 1; i; --i)
            {
                if((item = items->at(i)))
                {
                    #ifndef __DEBUG__
                    internalRemoveItem(NULL, item);
                    #else
                    if(internalRemoveItem(NULL, item) != RET_NOERROR)
                    {
                        std::clog << "> WARNING: Could not refresh item: " << item->getID();
                        std::clog << " at position: " << tile->getPosition() << std::endl;
                    }
                    #endif
                }
            }
        }

        cleanup();
        TileItemVector list = (*it)->second.list;
        for(ItemVector::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
        {
            Item* item = (*it)->clone();
            if(internalAddItem(NULL, tile, item, INDEX_WHEREEVER, FLAG_NOLIMIT) == RET_NOERROR)
            {
                if(item->getUniqueId())
                    ScriptEnviroment::addUniqueThing(item);

                startDecay(item);
            }
            else
            {
                std::clog << "> WARNING: Could not refresh item: " << item->getID()
                    << " at position: " << tile->getPosition() << std::endl;
                delete item;
            }
        }
    }
}

bool Game::isSwimmingPool(Item* item, const Tile* tile, bool checkProtection) const
{
    if(!tile)
        return false;

    TrashHolder* trashHolder = NULL;
    if(!item)
        trashHolder = tile->getTrashHolder();
    else
        trashHolder = item->getTrashHolder();

    return trashHolder && trashHolder->getEffect() == MAGIC_EFFECT_LOSE_ENERGY && (!checkProtection
        || tile->getZone() == ZONE_PROTECTION || tile->getZone() == ZONE_OPTIONAL);
}

Cylinder* Game::internalGetCylinder(Player* player, const Position& pos)
{
    if(pos.x != 0xFFFF)
        return getTile(pos);

    //container
    if(pos.y & 0x40)
        return player->getContainer((uint8_t)(pos.y & 0x0F));

    return player;
}

Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index,
    uint32_t spriteId/* = 0*/, stackposType_t type/* = STACKPOS_NORMAL*/)
{
    if(pos.x != 0xFFFF)
    {
        Tile* tile = getTile(pos);
        if(!tile)
            return NULL;

        if(type == STACKPOS_LOOK)
            return tile->getTopVisibleThing(player);

        Thing* thing = NULL;
        switch(type)
        {
            case STACKPOS_MOVE:
            {
                Item* item = tile->getTopDownItem();
                if(item && item->isMovable())
                    thing = item;
                else
                    thing = tile->getTopVisibleCreature(player);

                break;
            }

            case STACKPOS_USE:
            {
                thing = tile->getTopDownItem();
                break;
            }

            case STACKPOS_USEITEM:
            {
                thing = tile->getTopDownItem();
                Item* item = tile->getItemByTopOrder(2);
                if(item && g_actions->hasAction(item))
                {
                    const ItemType& it = Item::items[item->getID()];
                    if(!thing || (!it.hasHeight && !it.allowPickupable))
                        thing = item;
                }

                if(!thing)
                    thing = tile->getTopTopItem();

                if(!thing)
                    thing = tile->ground;

                break;
            }

            default:
                thing = tile->__getThing(index);
                break;
        }

        if(player && thing && thing->getItem())
        {
            if(tile->hasProperty(ISVERTICAL))
            {
                if(player->getPosition().x + 1 == tile->getPosition().x)
                    thing = NULL;
            }
            else if(tile->hasProperty(ISHORIZONTAL) && player->getPosition().y + 1 == tile->getPosition().y)
                thing = NULL;
        }

        return thing;
    }
    else if(pos.y & 0x40)
    {
        uint8_t fromCid = pos.y & 0x0F, slot = pos.z;
        if(Container* parentcontainer = player->getContainer(fromCid))
            return parentcontainer->getItem(slot);
    }
    else if(!pos.y && !pos.z)
    {
        const ItemType& it = Item::items.getItemIdByClientId(spriteId);
        if(!it.id)
            return NULL;

        int32_t subType = -1;
        if(it.isFluidContainer() && index < int32_t(sizeof(reverseFluidMap) / sizeof(int8_t)))
            subType = reverseFluidMap[index];

        return findItemOfType(player, it.id, true, subType);
    }

    return player->getInventoryItem((slots_t)static_cast<uint8_t>(pos.y));
}

void Game::internalGetPosition(Item* item, Position& pos, int16_t& stackpos)
{
    if(!item)
        return;

    pos.x = pos.y = pos.z = stackpos = 0;
    if(Cylinder* topParent = item->getTopParent())
    {
        if(Player* player = dynamic_cast<Player*>(topParent))
        {
            pos.x = 0xFFFF;

            Container* container = dynamic_cast<Container*>(item->getParent());
            if(container)
            {
                pos.y = ((uint16_t) ((uint16_t)0x40) | ((uint16_t)player->getContainerID(container)) );
                pos.z = container->__getIndexOfThing(item);
                stackpos = pos.z;
            }
            else
            {
                pos.y = player->__getIndexOfThing(item);
                stackpos = pos.y;
            }
        }
        else if(Tile* tile = topParent->getTile())
        {
            pos = tile->getPosition();
            stackpos = tile->__getIndexOfThing(item);
        }
    }
}

Creature* Game::getCreatureByID(uint32_t id)
{
    if(!id)
        return NULL;

    AutoList<Creature>::iterator it = autoList.find(id);
    if(it != autoList.end() && !it->second->isRemoved())
        return it->second;

    return NULL; //just in case the player doesnt exist
}

Player* Game::getPlayerByID(uint32_t id)
{
    if(!id)
        return NULL;

    AutoList<Player>::iterator it = Player::autoList.find(id);
    if(it != Player::autoList.end() && !it->second->isRemoved())
        return it->second;

    return NULL; //just in case the player doesnt exist
}

Player* Game::getPlayerByGUID(const uint32_t& guid)
{
    if(!guid)
        return NULL;

    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        Player* player = (*it).second;
        if(!player->isRemoved())
        {
            if(guid == player->getGUID())
                return player;
        }
    }
    return NULL;
}

Creature* Game::getCreatureByName(std::string s)
{
    if(s.empty())
        return NULL;

    toLowerCaseString(s);
    for(AutoList<Creature>::iterator it = autoList.begin(); it != autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && asLowerCaseString(it->second->getName()) == s)
            return it->second;
    }

    return NULL; //just in case the creature doesnt exist
}

Player* Game::getPlayerByName(std::string s)
{
    if(s.empty())
        return NULL;

    toLowerCaseString(s);
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && asLowerCaseString(it->second->getName()) == s)
            return it->second;
    }

    return NULL;
}

Player* Game::getPlayerByNameEx(const std::string& s)
{
    Player* player = getPlayerByName(s);
    if(player)
        return player;

    std::string name = s;
    if(!IOLoginData::getInstance()->playerExists(name))
        return NULL;

    player = new Player(name, NULL);
    if(IOLoginData::getInstance()->loadPlayer(player, name))
        return player;

#ifdef __DEBUG__
    std::clog << "[Failure - Game::getPlayerByNameEx] Cannot load player: " << name << std::endl;
#endif
    delete player;
    return NULL;
}

Player* Game::getPlayerByGuid(uint32_t guid)
{
    if(!guid)
        return NULL;

    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && it->second->getGUID() == guid)
            return it->second;
    }

    return NULL;
}

Player* Game::getPlayerByGuidEx(uint32_t guid)
{
    Player* player = getPlayerByGuid(guid);
    if(player)
        return player;

    std::string name;
    if(!IOLoginData::getInstance()->getNameByGuid(guid, name))
        return NULL;

    player = new Player(name, NULL);
    if(IOLoginData::getInstance()->loadPlayer(player, name))
        return player;

#ifdef __DEBUG__
    std::clog << "[Failure - Game::getPlayerByGuidEx] Cannot load player: " << name << std::endl;
#endif
    delete player;
    return NULL;
}

ReturnValue Game::getPlayerByNameWildcard(std::string s, Player*& player)
{
    player = NULL;
    if(s.empty())
        return RET_PLAYERWITHTHISNAMEISNOTONLINE;

    char tmp = *s.rbegin();
    if(tmp != '~' && tmp != '*')
    {
        player = getPlayerByName(s);
        if(!player)
            return RET_PLAYERWITHTHISNAMEISNOTONLINE;

        return RET_NOERROR;
    }

    Player* last = NULL;
    s = s.substr(0, s.length() - 1);

    toLowerCaseString(s);
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(it->second->isRemoved())
            continue;

        std::string name = asLowerCaseString(it->second->getName());
        if(name.substr(0, s.length()) != s)
            continue;

        if(last)
            return RET_NAMEISTOOAMBIGUOUS;

        last = it->second;
    }

    if(!last)
        return RET_PLAYERWITHTHISNAMEISNOTONLINE;

    player = last;
    return RET_NOERROR;
}

Player* Game::getPlayerByAccount(uint32_t acc)
{
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && it->second->getAccount() == acc)
            return it->second;
    }

    return NULL;
}

PlayerVector Game::getPlayersByName(std::string s)
{
    toLowerCaseString(s);
    PlayerVector players;
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && asLowerCaseString(it->second->getName()) == s)
            players.push_back(it->second);
    }

    return players;
}

PlayerVector Game::getPlayersByAccount(uint32_t acc)
{
    PlayerVector players;
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && it->second->getAccount() == acc)
            players.push_back(it->second);
    }

    return players;
}

PlayerVector Game::getPlayersByIP(uint32_t ip, uint32_t mask)
{
    PlayerVector players;
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && (it->second->getIP() & mask) == (ip & mask))
            players.push_back(it->second);
    }

    return players;
}

bool Game::internalPlaceCreature(Creature* creature, const Position& pos, bool extendedPos /*= false*/, bool forced /*= false*/)
{
    if(creature->getParent())
        return false;

    if(!map->placeCreature(pos, creature, extendedPos, forced))
        return false;

    creature->addRef();
    creature->setID();

    autoList[creature->getID()] = creature;
    creature->addList();
    return true;
}

bool Game::placeCreature(Creature* creature, const Position& pos, bool extendedPos /*= false*/, bool forced /*= false*/)
{
    if(!internalPlaceCreature(creature, pos, extendedPos, forced))
        return false;

    Player* tmpPlayer = NULL;
    if((tmpPlayer = creature->getPlayer()) && !tmpPlayer->storedConditionList.empty())
    {
        for(ConditionList::iterator it = tmpPlayer->storedConditionList.begin(); it != tmpPlayer->storedConditionList.end(); ++it)
        {
            if((*it)->getType() == CONDITION_MUTED && (*it)->getTicks() != -1 &&
                (*it)->getTicks() - ((time(NULL) - tmpPlayer->getLastLogout()) * 1000) <= 0)
                continue;

            tmpPlayer->addCondition(*it);
        }

        tmpPlayer->storedConditionList.clear();
    }

    SpectatorVec::iterator it;
    SpectatorVec list;

    getSpectators(list, creature->getPosition(), false, true);
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureAppear(creature);
    }

    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureAppear(creature);

    creature->setLastPosition(pos);
    creature->getParent()->postAddNotification(NULL, creature, NULL, creature->getParent()->__getIndexOfThing(creature));

    addCreatureCheck(creature);
    creature->onPlacedCreature();
    return true;
}

ReturnValue Game::placeSummon(Creature* creature, const std::string& name)
{
    Monster* monster = Monster::createMonster(name);
    if(!monster)
        return RET_NOTPOSSIBLE;

    // Place the monster
    creature->addSummon(monster);
    if(placeCreature(monster, creature->getPosition(), true))
    {
        addMagicEffect(monster->getPosition(), MAGIC_EFFECT_TELEPORT);
        return RET_NOERROR;
    }

    creature->removeSummon(monster);
    return RET_NOTENOUGHROOM;
}

bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/)
{
    if(creature->isRemoved())
        return false;

    Tile* tile = creature->getTile();
    SpectatorVec list;
    getSpectators(list, tile->getPosition(), false, true);

    SpectatorVec::iterator it;
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureDisappear(creature, isLogout);

    if(tile != creature->getTile())
    {
        list.clear();
        tile = creature->getTile();
        getSpectators(list, tile->getPosition(), false, true);
    }

    Player* player = NULL;
    std::vector<uint32_t> oldStackPosVector;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()) && player->canSeeCreature(creature))
            oldStackPosVector.push_back(tile->getClientIndexOfThing(player, creature));
    }

    int32_t oldIndex = tile->__getIndexOfThing(creature);
    if(!map->removeCreature(creature))
        return false;

    //send to client
    uint32_t i = 0;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if(creature != (*it))
            (*it)->updateTileCache(tile);

        if(!(player = (*it)->getPlayer()) || !player->canSeeCreature(creature))
            continue;

        player->setWalkthrough(creature, false);
        player->sendCreatureDisappear(creature, oldStackPosVector);
        ++i;
    }

    creature->getParent()->postRemoveNotification(NULL, creature, NULL, oldIndex, true);
    creature->onRemovedCreature();

    autoList.erase(creature->getID());
    freeThing(creature);

    removeCreatureCheck(creature);
    for(std::list<Creature*>::iterator it = creature->summons.begin(); it != creature->summons.end(); ++it)
        removeCreature(*it);

    return true;
}

bool Game::playerMoveThing(uint32_t playerId, const Position& fromPos,
    uint16_t spriteId, int16_t fromStackpos, const Position& toPos, uint8_t count)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->getNoMove())
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    uint8_t fromIndex = 0;
    if(fromPos.x == 0xFFFF)
    {
        if(fromPos.y & 0x40)
            fromIndex = static_cast<uint8_t>(fromPos.z);
        else
            fromIndex = static_cast<uint8_t>(fromPos.y);
    }
    else
        fromIndex = fromStackpos;

    Thing* thing = internalGetThing(player, fromPos, fromIndex, spriteId, STACKPOS_MOVE);
    Cylinder* toCylinder = internalGetCylinder(player, toPos);
    if(!thing || !toCylinder)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(Creature* movingCreature = thing->getCreature())
        playerMoveCreature(playerId, movingCreature->getID(), movingCreature->getPosition(), toCylinder->getPosition(), true);
    else if(thing->getItem())
        playerMoveItem(playerId, fromPos, spriteId, fromStackpos, toPos, count);

    return true;
}

bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId,
    const Position& movingCreaturePos, const Position& toPos, bool delay)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || player->hasFlag(PlayerFlag_CannotMoveCreatures))
        return false;

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerMoveCreature, this, playerId, movingCreatureId, movingCreaturePos, toPos, true));
        player->setNextActionTask(task);
        return false;
    }

    Creature* movingCreature = getCreatureByID(movingCreatureId);
    if(!movingCreature || movingCreature->isRemoved() || !player->canSeeCreature(movingCreature))
        return false;

    player->setNextActionTask(NULL);
    if(!Position::areInRange<1,1,0>(movingCreaturePos, player->getPosition()) && !player->hasCustomFlag(PlayerCustomFlag_CanMoveFromFar))
    {
        //need to walk to the creature first before moving it
        std::list<Direction> listDir;
        if(getPathToEx(player, movingCreaturePos, listDir, 0, 1, true, true))
        {
            Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                this, player->getID(), listDir)));
            SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                boost::bind(&Game::playerMoveCreature, this, playerId, movingCreatureId, movingCreaturePos, toPos, true));

            player->setNextWalkActionTask(task);
            return true;
        }

        player->sendCancelMessage(RET_THEREISNOWAY);
        return false;
    }
    else if(delay)
    {
        uint32_t delayTime = g_config.getNumber(ConfigManager::PUSH_CREATURE_DELAY);
        if(delayTime > 0)
        {
            SchedulerTask* task = createSchedulerTask(delayTime,
                boost::bind(&Game::playerMoveCreature, this, playerId, movingCreatureId, movingCreaturePos, toPos, false));
            player->setNextActionTask(task);
            return true;
        }
    }

    Tile* toTile = map->getTile(toPos);
    if(!toTile)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(!player->hasFlag(PlayerFlag_CanPushAllCreatures))
    {
        if(!movingCreature->isPushable())
        {
            player->sendCancelMessage(RET_NOTMOVABLE);
            return false;
        }

        if(movingCreature->getNoMove())
        {
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        if(toTile->hasProperty(BLOCKPATH))
        {
            player->sendCancelMessage(RET_NOTENOUGHROOM);
            return false;
        }
    }

    //check throw distance
    const Position& pos = movingCreature->getPosition();
    if(!player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere) && ((std::abs(pos.x - toPos.x) > movingCreature->getThrowRange()) ||
        (std::abs(pos.y - toPos.y) > movingCreature->getThrowRange()) || (std::abs(pos.z - toPos.z) * 4 > movingCreature->getThrowRange())))
    {
        player->sendCancelMessage(RET_DESTINATIONOUTOFREACH);
        return false;
    }

    if(player != movingCreature)
    {
        if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone) && (movingCreature->getZone() == ZONE_PROTECTION
            || movingCreature->getZone() == ZONE_OPTIONAL) && !toTile->hasFlag(TILESTATE_OPTIONALZONE)
            && !toTile->hasFlag(TILESTATE_PROTECTIONZONE))
        {
            player->sendCancelMessage(RET_ACTIONNOTPERMITTEDINANOPVPZONE);
            return false;
        }

        if(!player->hasFlag(PlayerFlag_CanPushAllCreatures))
        {
            if(toTile->getCreatureCount() && !Item::items[
                movingCreature->getTile()->ground->getID()].walkStack)
            {
                player->sendCancelMessage(RET_NOTENOUGHROOM);
                return false;
            }

            if(MagicField* field = toTile->getFieldItem())
            {
                if(field->isUnstepable() || field->isBlocking(movingCreature)
                    || !movingCreature->isImmune(field->getCombatType()))
                {
                    player->sendCancelMessage(RET_NOTPOSSIBLE);
                    return false;
                }
            }

            if(player->isProtected())
            {
                Player* movingPlayer = movingCreature->getPlayer();
                if(movingPlayer && !movingPlayer->isProtected())
                {
                    player->sendCancelMessage(RET_NOTMOVABLE);
                    return false;
                }
            }
        }
    }

    bool deny = false;
    CreatureEventList pushEvents = player->getCreatureEvents(CREATURE_EVENT_PUSH);
    for(CreatureEventList::iterator it = pushEvents.begin(); it != pushEvents.end(); ++it)
    {
        if(!(*it)->executePush(player, movingCreature, toTile) && !deny)
            deny = true;
    }

    if(deny)
        return false;

    ReturnValue ret = internalMoveCreature(player, movingCreature, movingCreature->getTile(), toTile);
    if(ret != RET_NOERROR)
    {
        if(!player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere))
        {
            player->sendCancelMessage(ret);
            return false;
        }

        if(!toTile->ground)
        {
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        internalTeleport(movingCreature, toTile->getPosition(), false);
    }
    else if(Player* movingPlayer = movingCreature->getPlayer())
    {
        uint64_t delay = OTSYS_TIME() + movingPlayer->getStepDuration();
        if(delay > movingPlayer->getNextActionTime(false))
            movingPlayer->setNextAction(delay);
    }

    return true;
}

ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, uint32_t flags/* = 0*/)
{
    const Position& currentPos = creature->getPosition();
    Cylinder* fromTile = creature->getTile();
    Cylinder* toTile = NULL;

    Position destPos = getNextPosition(direction, currentPos);
    if(direction < SOUTHWEST && creature->getPlayer())
    {
        Tile* tmpTile = NULL;
        if(currentPos.z != 8 && creature->getTile()->hasHeight(3)) //try go up
        {
            if((!(tmpTile = map->getTile(Position(currentPos.x, currentPos.y, currentPos.z - 1)))
                || (!tmpTile->ground && !tmpTile->hasProperty(BLOCKSOLID))) &&
                (tmpTile = map->getTile(Position(destPos.x, destPos.y, destPos.z - 1)))
                && tmpTile->ground && !tmpTile->hasProperty(BLOCKSOLID) && !tmpTile->hasProperty(FLOORCHANGEDOWN))
            {
                flags = flags | FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
                destPos.z--;
            }
        }
        else if(currentPos.z != 7 && (!(tmpTile = map->getTile(destPos)) || (!tmpTile->ground &&
            !tmpTile->hasProperty(BLOCKSOLID))) && (tmpTile = map->getTile(Position(
            destPos.x, destPos.y, destPos.z + 1))) && tmpTile->hasHeight(3)) //try go down
        {
            flags = flags | FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
            destPos.z++;
        }
    }

    ReturnValue ret = RET_NOTPOSSIBLE;
    if((toTile = map->getTile(destPos)))
        ret = internalMoveCreature(NULL, creature, fromTile, toTile, flags);

    if(ret == RET_NOERROR)
        return RET_NOERROR;

    Player* player = creature->getPlayer();
    if(!player)
        return ret;

    player->sendCancelMessage(ret);
    player->sendCancelWalk();
    return ret;
}

ReturnValue Game::internalMoveCreature(Creature* actor, Creature* creature, Cylinder* fromCylinder,
    Cylinder* toCylinder, uint32_t flags/* = 0*/, bool forceTeleport/* = false*/)
{
    //check if we can move the creature to the destination
    ReturnValue ret = toCylinder->__queryAdd(0, creature, 1, flags, actor);
    if(ret != RET_NOERROR)
        return ret;

    fromCylinder->getTile()->moveCreature(actor, creature, toCylinder, forceTeleport);
    if(creature->getParent() != toCylinder)
        return RET_NOERROR;

    Item* toItem = NULL;
    Cylinder* subCylinder = NULL;

    int32_t n = 0, tmp = 0;
    while((subCylinder = toCylinder->__queryDestination(tmp, creature, &toItem, flags)) != toCylinder)
    {
        internalCreatureTurn(creature, getDirectionTo(toCylinder->getTile()->getPosition(), subCylinder->getTile()->getPosition(), false));
        toCylinder->getTile()->moveCreature(actor, creature, subCylinder);
        if(creature->getParent() != subCylinder) //could happen if a script move the creature
            break;

        toCylinder = subCylinder;
        flags = 0;
        if(++n >= MAP_MAX_LAYERS) //to prevent infinite loop
            break;
    }

    return RET_NOERROR;
}

bool Game::playerMoveItem(uint32_t playerId, const Position& fromPos,
    uint16_t spriteId, int16_t fromStackpos, const Position& toPos, uint8_t count)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || player->hasFlag(PlayerFlag_CannotMoveItems))
        return false;

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerMoveItem, this, playerId, fromPos, spriteId, fromStackpos, toPos, count));
        player->setNextActionTask(task);
        return false;
    }

    player->setNextActionTask(NULL);
    Cylinder* fromCylinder = internalGetCylinder(player, fromPos);

    uint8_t fromIndex = 0;
    if(fromPos.x == 0xFFFF)
    {
        if(fromPos.y & 0x40)
            fromIndex = static_cast<uint8_t>(fromPos.z);
        else
            fromIndex = static_cast<uint8_t>(fromPos.y);
    }
    else
        fromIndex = fromStackpos;

    Thing* thing = internalGetThing(player, fromPos, fromIndex, spriteId, STACKPOS_MOVE);
    if(!thing || !thing->getItem())
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Item* item = thing->getItem();
    Cylinder* toCylinder = internalGetCylinder(player, toPos);

    uint8_t toIndex = 0;
    if(toPos.x == 0xFFFF)
    {
        if(toPos.y & 0x40)
            toIndex = static_cast<uint8_t>(toPos.z);
        else
            toIndex = static_cast<uint8_t>(toPos.y);
    }

    if(!fromCylinder || !toCylinder || !item || item->getClientID() != spriteId)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(!player->hasCustomFlag(PlayerCustomFlag_CanPushAllItems) && (!item->isPushable() || (item->isLoadedFromMap() &&
        (item->getUniqueId() || (item->getActionId() && item->getContainer())))))
    {
        player->sendCancelMessage(RET_NOTMOVABLE);
        return false;
    }

    const Position &mapToPos = toCylinder->getTile()->getPosition(), &playerPos = player->getPosition(),
        &mapFromPos = fromCylinder->getTile()->getPosition();
    if(playerPos.z > mapFromPos.z && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_FIRSTGOUPSTAIRS);
        return false;
    }

    if(playerPos.z < mapFromPos.z && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_FIRSTGODOWNSTAIRS);
        return false;
    }

    if(!Position::areInRange<1,1,0>(playerPos, mapFromPos) && !player->hasCustomFlag(PlayerCustomFlag_CanMoveFromFar))
    {
        //need to walk to the item first before using it
        std::list<Direction> listDir;
        if(getPathToEx(player, item->getPosition(), listDir, 0, 1, true, true))
        {
            Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                this, player->getID(), listDir)));
            SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                boost::bind(&Game::playerMoveItem, this, playerId, fromPos, spriteId, fromStackpos, toPos, count));

            player->setNextWalkActionTask(task);
            return true;
        }

        player->sendCancelMessage(RET_THEREISNOWAY);
        return false;
    }

    //hangable item specific code
    if(item->isHangable() && toCylinder->getTile()->hasProperty(SUPPORTHANGABLE))
    {
        //destination supports hangable objects so need to move there first
        if(toCylinder->getTile()->hasProperty(ISVERTICAL))
        {
            if(player->getPosition().x + 1 == mapToPos.x)
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }
        }
        else if(toCylinder->getTile()->hasProperty(ISHORIZONTAL))
        {
            if(player->getPosition().y + 1 == mapToPos.y)
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }
        }

        if(!Position::areInRange<1,1,0>(playerPos, mapToPos) && !player->hasCustomFlag(PlayerCustomFlag_CanMoveFromFar))
        {
            Position walkPos = mapToPos;
            if(toCylinder->getTile()->hasProperty(ISVERTICAL))
                walkPos.x -= -1;

            if(toCylinder->getTile()->hasProperty(ISHORIZONTAL))
                walkPos.y -= -1;

            Position itemPos = fromPos;
            int16_t itemStackpos = fromStackpos;
            if(fromPos.x != 0xFFFF && Position::areInRange<1,1,0>(mapFromPos, player->getPosition())
                && !Position::areInRange<1,1,0>(mapFromPos, walkPos))
            {
                //need to pickup the item first
                Item* moveItem = NULL;
                ReturnValue ret = internalMoveItem(player, fromCylinder, player, INDEX_WHEREEVER, item, count, &moveItem);
                if(ret != RET_NOERROR)
                {
                    player->sendCancelMessage(ret);
                    return false;
                }

                //changing the position since its now in the inventory of the player
                internalGetPosition(moveItem, itemPos, itemStackpos);
            }

            std::list<Direction> listDir;
            if(map->getPathTo(player, walkPos, listDir))
            {
                Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                    this, player->getID(), listDir)));
                SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                    boost::bind(&Game::playerMoveItem, this, playerId, itemPos, spriteId, itemStackpos, toPos, count));

                player->setNextWalkActionTask(task);
                return true;
            }

            player->sendCancelMessage(RET_THEREISNOWAY);
            return false;
        }
    }

    if(!player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        if((std::abs(playerPos.x - mapToPos.x) > item->getThrowRange()) ||
            (std::abs(playerPos.y - mapToPos.y) > item->getThrowRange()) ||
            (std::abs(mapFromPos.z - mapToPos.z) * 4 > item->getThrowRange()))
        {
            player->sendCancelMessage(RET_DESTINATIONOUTOFREACH);
            return false;
        }
    }

    if(!canThrowObjectTo(mapFromPos, mapToPos) && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_CANNOTTHROW);
        return false;
    }

    bool deny = false;
    CreatureEventList throwEvents = player->getCreatureEvents(CREATURE_EVENT_THROW);
    for(CreatureEventList::iterator it = throwEvents.begin(); it != throwEvents.end(); ++it)
    {
        if(!(*it)->executeThrow(player, item, fromPos, toPos) && !deny)
            deny = true;
    }

    if(deny)
        return false;

    ReturnValue ret = internalMoveItem(player, fromCylinder, toCylinder, toIndex, item, count, NULL);
    if(ret != RET_NOERROR)
    {
        player->sendCancelMessage(ret);
        return false;
    }

    player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL) - 10);
    return true;
}

ReturnValue Game::internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cylinder* toCylinder,
    int32_t index, Item* item, uint32_t count, Item** _moveItem, uint32_t flags /*= 0*/)
{
    if(!toCylinder)
        return RET_NOTPOSSIBLE;

    if(!item->getParent())
    {
        assert(fromCylinder == item->getParent());
        return internalAddItem(actor, toCylinder, item, INDEX_WHEREEVER, FLAG_NOLIMIT);
    }

    Item* toItem = NULL;
    Cylinder* subCylinder = NULL;

    int32_t floor = 0;
    while((subCylinder = toCylinder->__queryDestination(index, item, &toItem, flags)) != toCylinder)
    {
        flags = 0;
        toCylinder = subCylinder;
        //to prevent infinite loop
        if(++floor >= MAP_MAX_LAYERS)
            break;
    }

    //destination is the same as the source?
    if(item == toItem)
        return RET_NOERROR; //silently ignore move

    //check if we can add this item
    ReturnValue ret = toCylinder->__queryAdd(index, item, count, flags, actor);
    if(ret == RET_NEEDEXCHANGE)
    {
        //check if we can add it to source cylinder
        int32_t fromIndex = fromCylinder->__getIndexOfThing(item);
        if((ret = fromCylinder->__queryAdd(fromIndex, toItem, toItem->getItemCount(), 0, actor)) == RET_NOERROR)
        {
            //check how much we can move
            uint32_t maxExchangeQueryCount = 0;
            ReturnValue retExchangeMaxCount = fromCylinder->__queryMaxCount(INDEX_WHEREEVER, toItem, toItem->getItemCount(), maxExchangeQueryCount, 0);
            if(retExchangeMaxCount != RET_NOERROR && !maxExchangeQueryCount)
                return retExchangeMaxCount;

            if((toCylinder->__queryRemove(toItem, toItem->getItemCount(), flags, actor) == RET_NOERROR) && ret == RET_NOERROR)
            {
                int32_t oldToItemIndex = toCylinder->__getIndexOfThing(toItem);
                toCylinder->__removeThing(toItem, toItem->getItemCount());

                fromCylinder->__addThing(actor, toItem);
                if(oldToItemIndex != -1)
                    toCylinder->postRemoveNotification(actor, toItem, fromCylinder, oldToItemIndex, true);

                int32_t newToItemIndex = fromCylinder->__getIndexOfThing(toItem);
                if(newToItemIndex != -1)
                    fromCylinder->postAddNotification(actor, toItem, toCylinder, newToItemIndex);

                ret = toCylinder->__queryAdd(index, item, count, flags, actor);
                toItem = NULL;
            }
        }
    }

    if(ret != RET_NOERROR)
        return ret;

    //check how much we can move
    uint32_t maxQueryCount = 0;
    ReturnValue retMaxCount = toCylinder->__queryMaxCount(index, item, count, maxQueryCount, flags);
    if(retMaxCount != RET_NOERROR && !maxQueryCount)
        return ret;

    uint32_t m = maxQueryCount;
    if(item->isStackable())
        m = std::min((uint32_t)count, m);

    Item* moveItem = item;
    //check if we can remove this item
    if((ret = fromCylinder->__queryRemove(item, m, flags, actor)) != RET_NOERROR)
        return ret;

    //remove the item
    int32_t itemIndex = fromCylinder->__getIndexOfThing(item);
    fromCylinder->__removeThing(item, m);
    bool isCompleteRemoval = item->isRemoved();

    Item* updateItem = NULL;
    if(item->isStackable())
    {
        uint8_t n = 0;
        if(toItem && toItem->getID() == item->getID())
        {
            n = std::min((uint32_t)100 - toItem->getItemCount(), m);
            toCylinder->__updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
            updateItem = toItem;
        }

        if(m - n > 0)
            moveItem = Item::CreateItem(item->getID(), m - n);
        else
            moveItem = NULL;

        if(item->isRemoved())
            freeThing(item);
    }

    if(moveItem)
        toCylinder->__addThing(actor, index, moveItem);

    if(itemIndex != -1)
        fromCylinder->postRemoveNotification(actor, item, toCylinder, itemIndex, isCompleteRemoval);

    if(moveItem)
    {
        int32_t moveItemIndex = toCylinder->__getIndexOfThing(moveItem);
        if(moveItemIndex != -1)
            toCylinder->postAddNotification(actor, moveItem, fromCylinder, moveItemIndex);
    }

    if(updateItem)
    {
        int32_t updateItemIndex = toCylinder->__getIndexOfThing(updateItem);
        if(updateItemIndex != -1)
            toCylinder->postAddNotification(actor, updateItem, fromCylinder, updateItemIndex);
    }

    if(_moveItem)
    {
        if(moveItem)
            *_moveItem = moveItem;
        else
            *_moveItem = item;
    }

    //we could not move all, inform the player
    if(item->isStackable() && maxQueryCount < count)
        return retMaxCount;

    return ret;
}

ReturnValue Game::internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index /*= INDEX_WHEREEVER*/,
    uint32_t flags/* = 0*/, bool test/* = false*/)
{
    uint32_t remainderCount = 0;
    return internalAddItem(actor, toCylinder, item, index, flags, test, remainderCount);
}

ReturnValue Game::internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index,
    uint32_t flags, bool test, uint32_t& remainderCount)
{
    Item* stackItem = NULL;
    return internalAddItem(actor, toCylinder, item, index, flags, test, remainderCount, &stackItem);
}

ReturnValue Game::internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index,
    uint32_t flags, bool test, uint32_t& remainderCount, Item** stackItem)
{
    *stackItem = NULL;
    remainderCount = 0;
    if(!toCylinder || !item)
        return RET_NOTPOSSIBLE;

    Cylinder* destCylinder = toCylinder;
    toCylinder = toCylinder->__queryDestination(index, item, stackItem, flags);

    //check if we can add this item
    ReturnValue ret = toCylinder->__queryAdd(index, item, item->getItemCount(), flags, actor);
    if(ret != RET_NOERROR)
        return ret;

    uint32_t maxQueryCount = 0;
    ret = destCylinder->__queryMaxCount(INDEX_WHEREEVER, item, item->getItemCount(), maxQueryCount, flags);
    if(ret != RET_NOERROR)
        return ret;

    if(test)
        return RET_NOERROR;

    Item* toItem = *stackItem;
    if(item->isStackable() && toItem)
    {
        uint32_t m = std::min((uint32_t)item->getItemCount(), maxQueryCount), n = 0;
        if(toItem->getID() == item->getID())
        {
            n = std::min((uint32_t)100 - toItem->getItemCount(), m);
            toCylinder->__updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
        }

        uint32_t count = m - n;
        if(count > 0)
        {
            if(item->getItemCount() != count)
            {
                Item* remainderItem = Item::CreateItem(item->getID(), count);
                if((ret = internalAddItem(NULL, toCylinder, remainderItem, INDEX_WHEREEVER, flags, false)) == RET_NOERROR)
                {
                    if(item->getParent() != VirtualCylinder::virtualCylinder)
                    {
                        item->onRemoved();
                        freeThing(item);
                    }

                    return RET_NOERROR;
                }

                delete remainderItem;
                remainderCount = count;
                return ret;
            }
        }
        else
        {
            if(item->getParent() != VirtualCylinder::virtualCylinder)
            {
                item->onRemoved();
                freeThing(item);
            }

            return RET_NOERROR;
        }
    }

    toCylinder->__addThing(NULL, index, item);
    int32_t itemIndex = toCylinder->__getIndexOfThing(item);
    if(itemIndex != -1)
        toCylinder->postAddNotification(actor, item, NULL, itemIndex);

    return RET_NOERROR;
}

ReturnValue Game::internalRemoveItem(Creature* actor, Item* item, int32_t count/* = -1*/, bool test/* = false*/, uint32_t flags/* = 0*/)
{
    Cylinder* cylinder = item->getParent();
    if(!cylinder)
        return RET_NOTPOSSIBLE;

    if(count == -1)
        count = item->getItemCount();

    //check if we can remove this item
    ReturnValue ret = cylinder->__queryRemove(item, count, flags | FLAG_IGNORENOTMOVABLE, actor);
    if(ret != RET_NOERROR)
        return ret;

    if(!item->canRemove())
        return RET_NOTPOSSIBLE;

    if(!test)
    {
        //remove the item
        int32_t index = cylinder->__getIndexOfThing(item);
        cylinder->__removeThing(item, count);

        cylinder->postRemoveNotification(actor, item, NULL, index, item->isRemoved());
        if(item->isRemoved())
            freeThing(item);
    }

    item->onRemoved();
    return RET_NOERROR;
}

ReturnValue Game::internalPlayerAddItem(Creature* actor, Player* player, Item* item,
    bool dropOnMap/* = true*/, slots_t slot/* = SLOT_WHEREEVER*/)
{
    Item* toItem = NULL;
    return internalPlayerAddItem(actor, player, item, dropOnMap, slot, &toItem);
}

ReturnValue Game::internalPlayerAddItem(Creature* actor, Player* player, Item* item,
    bool dropOnMap, slots_t slot, Item** toItem)
{
    uint32_t remainderCount = 0, count = item->getItemCount();
    ReturnValue ret = internalAddItem(actor, player, item, (int32_t)slot, 0, false, remainderCount, toItem);
    if(ret == RET_NOERROR)
        return RET_NOERROR;

    if(dropOnMap)
    {
        if(!remainderCount)
            return internalAddItem(actor, player->getTile(), item, (int32_t)slot, FLAG_NOLIMIT);

        Item* remainderItem = Item::CreateItem(item->getID(), remainderCount);
        if(internalAddItem(actor, player->getTile(), remainderItem, INDEX_WHEREEVER, FLAG_NOLIMIT) == RET_NOERROR)
            return RET_NOERROR;

        delete remainderItem;
    }

    if(remainderCount && toItem)
        transformItem(*toItem, (*toItem)->getID(), ((*toItem)->getItemCount() - (count - remainderCount)));

    return ret;
}

Item* Game::findItemOfType(Cylinder* cylinder, uint16_t itemId,
    bool depthSearch /*= true*/, int32_t subType /*= -1*/)
{
    if(!cylinder)
        return NULL;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;
    Item* item = NULL;

    Thing* thing = NULL;
    for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex();)
    {
        if((thing = cylinder->__getThing(i)) && (item = thing->getItem()))
        {
            if(item->getID() == itemId && (subType == -1 || subType == item->getSubType()))
                return item;

            ++i;
            if(depthSearch && (tmpContainer = item->getContainer()))
                listContainer.push_back(tmpContainer);
        }
        else
            ++i;
    }

    Container* container = NULL;
    while(listContainer.size() > 0)
    {
        container = listContainer.front();
        listContainer.pop_front();
        for(int32_t i = 0; i < (int32_t)container->size(); )
        {
            if((item = container->getItem(i)))
            {
                if(item->getID() == itemId && (subType == -1 || subType == item->getSubType()))
                    return item;

                ++i;
                if((tmpContainer = item->getContainer()))
                    listContainer.push_back(tmpContainer);
            }
            else
                ++i;
        }
    }

    return NULL;
}

bool Game::removeItemOfType(Cylinder* cylinder, uint16_t itemId, int32_t count, int32_t subType/* = -1*/, bool onlyContainers/* = false*/)
{
    if(!cylinder || ((int32_t)cylinder->__getItemTypeCount(itemId, subType) < count))
        return false;

    if(count <= 0)
        return true;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;
    Item* item = NULL;

    Thing* thing = NULL;
    for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex() && count > 0; )
    {
        if((thing = cylinder->__getThing(i)) && (item = thing->getItem()))
        {
            if(!onlyContainers && item->getID() == itemId)
            {
                if(item->isStackable())
                {
                    if(item->getItemCount() > count)
                    {
                        internalRemoveItem(NULL, item, count);
                        count = 0;
                    }
                    else
                    {
                        count -= item->getItemCount();
                        internalRemoveItem(NULL, item);
                    }
                }
                else if(subType == -1 || subType == item->getSubType())
                {
                    --count;
                    internalRemoveItem(NULL, item);
                }
                else
                    ++i;
            }
            else
            {
                ++i;
                if((tmpContainer = item->getContainer()))
                    listContainer.push_back(tmpContainer);
            }
        }
        else
            ++i;
    }

    Container* container = NULL;
    while(listContainer.size() > 0 && count > 0)
    {
        container = listContainer.front();
        listContainer.pop_front();
        for(int32_t i = 0; i < (int32_t)container->size() && count > 0; )
        {
            if((item = container->getItem(i)))
            {
                if(item->getID() == itemId)
                {
                    if(item->isStackable())
                    {
                        if(item->getItemCount() > count)
                        {
                            internalRemoveItem(NULL, item, count);
                            count = 0;
                        }
                        else
                        {
                            count-= item->getItemCount();
                            internalRemoveItem(NULL, item);
                        }
                    }
                    else if(subType == -1 || subType == item->getSubType())
                    {
                        --count;
                        internalRemoveItem(NULL, item);
                    }
                    else
                        ++i;
                }
                else
                {
                    ++i;
                    if((tmpContainer = item->getContainer()))
                        listContainer.push_back(tmpContainer);
                }
            }
            else
                ++i;
        }
    }

    return !count;
}

uint64_t Game::getMoney(const Cylinder* cylinder)
{
    if(!cylinder)
        return 0;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;

    Thing* thing = NULL;
    Item* item = NULL;

    uint64_t moneyCount = 0;
    for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex(); ++i)
    {
        if(!(thing = cylinder->__getThing(i)) || !(item = thing->getItem()))
            continue;

        if((tmpContainer = item->getContainer()))
            listContainer.push_back(tmpContainer);
        else if(item->getWorth())
            moneyCount += item->getWorth();
    }

    Container* container = NULL;
    while(listContainer.size() > 0)
    {
        container = listContainer.front();
        listContainer.pop_front();
        for(ItemList::const_iterator it = container->getItems(); it != container->getEnd(); ++it)
        {
            item = *it;
            if((tmpContainer = item->getContainer()))
                listContainer.push_back(tmpContainer);
            else if(item->getWorth())
                moneyCount += item->getWorth();
        }
    }

    return moneyCount;
}

bool Game::removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    if(!cylinder)
        return false;

    if(money <= 0)
        return true;

    typedef std::multimap<int32_t, Item*, std::less<int32_t> > MoneyMultiMap;
    MoneyMultiMap moneyMap;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;

    Thing* thing = NULL;
    Item* item = NULL;

    int64_t moneyCount = 0;
    for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex(); ++i)
    {
        if(!(thing = cylinder->__getThing(i)) || !(item = thing->getItem()))
            continue;

        if((tmpContainer = item->getContainer()))
            listContainer.push_back(tmpContainer);
        else if(item->getWorth())
        {
            moneyCount += item->getWorth();
            moneyMap.insert(std::make_pair(item->getWorth(), item));
        }
    }

    while(listContainer.size() > 0)
    {
        Container* container = listContainer.front();
        listContainer.pop_front();
        for(int32_t i = 0; i < (int32_t)container->size(); ++i)
        {
            Item* item = container->getItem(i);
            if((tmpContainer = item->getContainer()))
                listContainer.push_back(tmpContainer);
            else if(item->getWorth())
            {
                moneyCount += item->getWorth();
                moneyMap.insert(std::make_pair(item->getWorth(), item));
            }
        }
    }

    // Not enough money
    if(moneyCount < money)
        return false;

    for(MoneyMultiMap::iterator mit = moneyMap.begin(); mit != moneyMap.end() && money > 0; ++mit)
    {
        if(!(item = mit->second))
            continue;

        internalRemoveItem(NULL, item);
        if(mit->first > money)
        {
            // Remove a monetary value from an item
            addMoney(cylinder, (int64_t)(item->getWorth() - money), flags);
            money = 0;
        }
        else
            money -= mit->first;

        mit->second = NULL;
    }

    moneyMap.clear();
    return !money;
}

void Game::addMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    IntegerMap moneyMap = Item::items.getMoneyMap();
    for(IntegerMap::reverse_iterator it = moneyMap.rbegin(); it != moneyMap.rend(); ++it)
    {
        int64_t tmp = money / it->first;
        money -= tmp * it->first;
        if(!tmp)
            continue;

        do
        {
            uint32_t remainderCount = 0;
            Item* item = Item::CreateItem(it->second, std::min<uint16_t>(100, tmp));
            if(internalAddItem(NULL, cylinder, item, INDEX_WHEREEVER, flags, false, remainderCount) != RET_NOERROR)
            {
                if(remainderCount)
                {
                    delete item;
                    item = Item::CreateItem(it->second, remainderCount);
                }

                if(internalAddItem(NULL, cylinder->getTile(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR)
                    delete item;
            }

            tmp -= std::min((int64_t)100, tmp);
        }
        while(tmp > 0);
    }
}

Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount/* = -1*/)
{
    if(item->getID() == newId && (newCount == -1 || (newCount == item->getSubType() && newCount)))
        return item;

    Cylinder* cylinder = item->getParent();
    if(!cylinder)
        return NULL;

    int32_t itemIndex = cylinder->__getIndexOfThing(item);
    if(itemIndex == -1)
    {
#ifdef __DEBUG__
        std::clog << "Error: transformItem, itemIndex == -1" << std::endl;
#endif
        return item;
    }

    if(!item->canTransform())
        return item;

    const ItemType &curType = Item::items[item->getID()], &newType = Item::items[newId];
    if(curType.alwaysOnTop != newType.alwaysOnTop)
    {
        // This only occurs when you transform items on tiles from a downItem to a topItem (or vice versa)
        // Remove the old, and add the new
        ReturnValue ret = internalRemoveItem(NULL, item);
        if(ret != RET_NOERROR)
            return item;

        Item* newItem = NULL;
        if(newCount == -1)
            newItem = Item::CreateItem(newId);
        else
            newItem = Item::CreateItem(newId, newCount);

        if(!newItem)
            return NULL;

        newItem->copyAttributes(item);
        if(internalAddItem(NULL, cylinder, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR)
        {
            delete newItem;
            return NULL;
        }

        newItem->makeUnique(item);
        return newItem;
    }

    if(curType.type == newType.type)
    {
        //Both items has the same type so we can safely change id/subtype
        if(!newCount && (item->isStackable() || item->hasCharges()))
        {
            if(!item->isStackable() && (!item->getDefaultDuration() || item->getDuration() <= 0))
            {
                int16_t tmp = newId;
                if(curType.id == newId)
                    tmp = curType.decayTo;

                if(tmp != -1)
                    return transformItem(item, tmp);
            }

            internalRemoveItem(NULL, item);
            return NULL;
        }

        cylinder->postRemoveNotification(NULL, item, cylinder, itemIndex, false);
        uint16_t tmp = item->getID();
        if(curType.id != newId)
        {
            tmp = newId;
            if(newType.group != curType.group)
                item->setDefaultSubtype();

            if(curType.hasSubType() && !newType.hasSubType())
            {
                item->resetFluidType();
                item->resetCharges();
            }
        }

        int32_t count = item->getSubType();
        if(newCount != -1 && newType.hasSubType())
            count = newCount;

        cylinder->__updateThing(item, tmp, count);
        cylinder->postAddNotification(NULL, item, cylinder, itemIndex);
        return item;
    }

    //Replacing the the old item with the new while maintaining the old position
    Item* newItem = NULL;
    if(newCount == -1)
        newItem = Item::CreateItem(newId);
    else
        newItem = Item::CreateItem(newId, newCount);

    if(!newItem)
        return NULL;

    newItem->copyAttributes(item);
    newItem->makeUnique(item);
    cylinder->__replaceThing(itemIndex, newItem);

    cylinder->postAddNotification(NULL, newItem, cylinder, itemIndex);
    item->setParent(NULL);
    cylinder->postRemoveNotification(NULL, item, cylinder, itemIndex, true);

    freeThing(item);
    return newItem;
}

ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool forceTeleport, uint32_t flags/* = 0*/, bool fullTeleport/* = true*/)
{
    if(newPos == thing->getPosition())
        return RET_NOERROR;

    if(thing->isRemoved())
        return RET_NOTPOSSIBLE;

    if(Tile* toTile = map->getTile(newPos))
    {
        if(Creature* creature = thing->getCreature())
        {
            if(fullTeleport)
                return internalMoveCreature(NULL, creature, creature->getParent(), toTile, flags, forceTeleport);

            creature->getTile()->moveCreature(NULL, creature, toTile, forceTeleport);
            return RET_NOERROR;
        }

        if(Item* item = thing->getItem())
            return internalMoveItem(NULL, item->getParent(), toTile, INDEX_WHEREEVER, item, item->getItemCount(), NULL, flags);
    }

    return RET_NOTPOSSIBLE;
}

bool Game::playerMove(uint32_t playerId, Direction dir)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->setIdleTime(0);
    if(player->getNoMove())
    {
        player->sendCancelWalk();
        return false;
    }

    std::list<Direction> dirs;
    dirs.push_back(dir);

    player->setNextWalkActionTask(NULL);
    return player->startAutoWalk(dirs);
}

bool Game::playerBroadcastMessage(Player* player, MessageClasses type, const std::string& text, uint32_t statementId)
{
    if(!player->hasFlag(PlayerFlag_CanBroadcast) || !((type >= MSG_SPEAK_FIRST && type <= MSG_SPEAK_LAST) ||
            (type >= MSG_SPEAK_MONSTER_FIRST && type <= MSG_SPEAK_MONSTER_LAST)))
        return false;

    Logger::getInstance()->eFile("talkactions/" + player->getName() + ".log", "#b " + text, true);
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
        it->second->sendCreatureSay(player, type, text, NULL, statementId);

    std::clog << "> " << player->getName() << " broadcasted: \"" << text << "\"." << std::endl;
    return true;
}

bool Game::playerCreatePrivateChannel(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->isPremium())
        return false;

    ChatChannel* channel = g_chat.createChannel(player, 0xFFFF);
    if(!channel || !channel->addUser(player))
        return false;

    player->sendCreatePrivateChannel(channel->getId(), channel->getName());
    return true;
}

bool Game::playerChannelInvite(uint32_t playerId, const std::string& name)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
    if(!channel)
        return false;

    Player* invitePlayer = getPlayerByName(name);
    if(!invitePlayer)
        return false;

    channel->invitePlayer(player, invitePlayer);
    return true;
}

bool Game::playerChannelExclude(uint32_t playerId, const std::string& name)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
    if(!channel)
        return false;

    Player* excludePlayer = getPlayerByName(name);
    if(!excludePlayer)
        return false;

    channel->excludePlayer(player, excludePlayer);
    return true;
}

bool Game::playerRequestChannels(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->sendChannelsDialog(g_chat.getChannelList(player));
    player->setSentChat(true);
    return true;
}

bool Game::playerOpenChannel(uint32_t playerId, uint16_t channelId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    bool deny = false;
    CreatureEventList openEvents = player->getCreatureEvents(CREATURE_EVENT_CHANNEL_REQUEST);
    for(CreatureEventList::iterator it = openEvents.begin(); it != openEvents.end(); ++it)
    {
        if(!(*it)->executeChannelRequest(player, asString(channelId), false, !player->hasSentChat()) && !deny)
            deny = true;
    }

    player->setSentChat(false);
    if(deny)
        return false;

    ChatChannel* channel = g_chat.addUserToChannel(player, channelId);
    if(!channel)
    {
        #ifdef __DEBUG_CHAT__
        std::clog << "Game::playerOpenChannel - failed adding user to channel." << std::endl;
        #endif
        return false;
    }

    player->sendChannel(channel->getId(), channel->getName());
    return true;
}

bool Game::playerCloseChannel(uint32_t playerId, uint16_t channelId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    g_chat.removeUserFromChannel(player, channelId);
    return true;
}

bool Game::playerOpenPrivateChannel(uint32_t playerId, std::string& receiver)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    bool deny = false;
    CreatureEventList openEvents = player->getCreatureEvents(CREATURE_EVENT_CHANNEL_REQUEST);
    for(CreatureEventList::iterator it = openEvents.begin(); it != openEvents.end(); ++it)
    {
        if(!(*it)->executeChannelRequest(player, receiver, true, !player->hasSentChat()) && !deny)
            deny = true;
    }

    player->setSentChat(false);
    if(deny)
        return false;

    if(IOLoginData::getInstance()->playerExists(receiver))
        player->sendOpenPrivateChannel(receiver);
    else
        player->sendCancel("A player with this name does not exist.");

    return true;
}

bool Game::playerCloseNpcChannel(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    SpectatorVec list;
    SpectatorVec::iterator it;

    getSpectators(list, player->getPosition());
    Npc* npc = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((npc = (*it)->getNpc()))
            npc->onPlayerCloseChannel(player);
    }

    return true;
}

bool Game::playerReceivePing(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->receivePing();
    return true;
}

bool Game::playerAutoWalk(uint32_t playerId, std::list<Direction>& listDir)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->setIdleTime(0);
    if(player->getNoMove())
    {
        player->sendCancelWalk();
        return false;
    }

    if(player->hasCondition(CONDITION_GAMEMASTER, GAMEMASTER_TELEPORT))
    {
        Position pos = player->getPosition();
        for(std::list<Direction>::iterator it = listDir.begin(); it != listDir.end(); ++it)
            pos = getNextPosition((*it), pos);

        pos = getClosestFreeTile(player, pos, true, false);
        if(!pos.x || !pos.y)
        {
            player->sendCancelWalk();
            return false;
        }

        internalCreatureTurn(player, getDirectionTo(player->getPosition(), pos, false));
        internalTeleport(player, pos, true);
        return true;
    }

    player->setNextWalkTask(NULL);
    return player->startAutoWalk(listDir);
}

bool Game::playerStopAutoWalk(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->stopWalk();
    return true;
}

bool Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, int16_t fromStackpos, uint16_t fromSpriteId,
    const Position& toPos, int16_t toStackpos, uint16_t toSpriteId, bool isHotkey)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(isHotkey && !g_config.getBool(ConfigManager::AIMBOT_HOTKEY_ENABLED))
        return false;

    Thing* thing = internalGetThing(player, fromPos, fromStackpos, fromSpriteId, STACKPOS_USEITEM);
    if(!thing)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Item* item = thing->getItem();
    if(!item || !item->isUsable())
    {
        player->sendCancelMessage(RET_CANNOTUSETHISOBJECT);
        return false;
    }

    Position walkToPos = fromPos;
    ReturnValue ret = g_actions->canUse(player, fromPos);
    if(ret == RET_NOERROR)
    {
        ret = g_actions->canUseEx(player, toPos, item);
        if(ret == RET_TOOFARAWAY)
        {
            if(!player->hasCustomFlag(PlayerCustomFlag_CanUseFar))
                walkToPos = toPos;
            else
                ret = RET_NOERROR;
        }
    }

    if(ret != RET_NOERROR)
    {
        if(ret == RET_TOOFARAWAY)
        {
            Position itemPos = fromPos;
            if(fromPos.x != 0xFFFF && toPos.x != 0xFFFF && Position::areInRange<1,1,0>(fromPos,
                player->getPosition()) && !Position::areInRange<1,1,0>(fromPos, toPos))
            {
                Item* moveItem = NULL;
                ReturnValue retTmp = internalMoveItem(player, item->getParent(), player,
                    INDEX_WHEREEVER, item, item->getItemCount(), &moveItem);
                if(retTmp != RET_NOERROR)
                {
                    player->sendCancelMessage(retTmp);
                    return false;
                }

                //changing the position since its now in the inventory of the player
                internalGetPosition(moveItem, itemPos, fromStackpos);
            }

            if(player->getNoMove())
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }

            std::list<Direction> listDir;
            if(getPathToEx(player, walkToPos, listDir, 0, 1, true, true, 10))
            {
                Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                    this, player->getID(), listDir)));

                SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                    boost::bind(&Game::playerUseItemEx, this, playerId, itemPos, fromStackpos, fromSpriteId, toPos, toStackpos, toSpriteId, isHotkey));

                player->setNextWalkActionTask(task);
                return true;
            }

            ret = RET_THEREISNOWAY;
        }

        player->sendCancelMessage(ret);
        return false;
    }

    if(isHotkey)
        showHotkeyUseMessage(player, item);

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerUseItemEx, this, playerId, fromPos, fromStackpos, fromSpriteId, toPos, toStackpos, toSpriteId, isHotkey));
        player->setNextActionTask(task);
        return false;
    }

    player->setIdleTime(0);
    player->setNextActionTask(NULL);
    return g_actions->useItemEx(player, fromPos, toPos, toStackpos, item, isHotkey);
}

bool Game::playerUseItem(uint32_t playerId, const Position& pos, int16_t stackpos,
    uint8_t index, uint16_t spriteId, bool isHotkey)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(isHotkey && !g_config.getBool(ConfigManager::AIMBOT_HOTKEY_ENABLED))
        return false;

    Thing* thing = internalGetThing(player, pos, stackpos, spriteId, STACKPOS_USEITEM);
    if(!thing)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Item* item = thing->getItem();
    if(!item || item->isUsable())
    {
        player->sendCancelMessage(RET_CANNOTUSETHISOBJECT);
        return false;
    }

    ReturnValue ret = g_actions->canUse(player, pos);
    if(ret == RET_TOOFARAWAY && player->hasCustomFlag(PlayerCustomFlag_CanUseFar))
        ret = RET_NOERROR;

    if(ret != RET_NOERROR)
    {
        if(ret == RET_TOOFARAWAY)
        {
            if(player->getNoMove())
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }

            std::list<Direction> listDir;
            if(getPathToEx(player, pos, listDir, 0, 1, true, true))
            {
                Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                    this, player->getID(), listDir)));

                SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                    boost::bind(&Game::playerUseItem, this, playerId, pos, stackpos, index, spriteId, isHotkey));

                player->setNextWalkActionTask(task);
                return true;
            }

            ret = RET_THEREISNOWAY;
        }

        player->sendCancelMessage(ret);
        return false;
    }

    if(isHotkey)
        showHotkeyUseMessage(player, item);

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerUseItem, this, playerId, pos, stackpos, index, spriteId, isHotkey));
        player->setNextActionTask(task);
        return false;
    }

    player->setIdleTime(0);
    player->setNextActionTask(NULL);
    return g_actions->useItem(player, pos, index, item);
}

bool Game::playerUseBattleWindow(uint32_t playerId, const Position& pos, int16_t stackpos,
    uint32_t creatureId, uint16_t spriteId, bool isHotkey)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Creature* creature = getCreatureByID(creatureId);
    if(!creature)
        return false;

    if(!Position::areInRange<7,5,0>(creature->getPosition(), player->getPosition()))
        return false;

    if(!g_config.getBool(ConfigManager::AIMBOT_HOTKEY_ENABLED) && (creature->getPlayer() || isHotkey))
    {
        player->sendCancelMessage(RET_DIRECTPLAYERSHOOT);
        return false;
    }

    Thing* thing = internalGetThing(player, pos, stackpos, spriteId, STACKPOS_USE);
    if(!thing)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Item* item = thing->getItem();
    if(!item || item->getClientID() != spriteId)
    {
        player->sendCancelMessage(RET_CANNOTUSETHISOBJECT);
        return false;
    }

    ReturnValue ret = g_actions->canUse(player, pos);
    if(ret != RET_NOERROR)
    {
        if(ret == RET_TOOFARAWAY)
        {
            if(player->getNoMove())
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }

            std::list<Direction> listDir;
            if(getPathToEx(player, item->getPosition(), listDir, 0, 1, true, true))
            {
                Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                    this, player->getID(), listDir)));

                SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                    boost::bind(&Game::playerUseBattleWindow, this, playerId, pos, stackpos, creatureId, spriteId, isHotkey));

                player->setNextWalkActionTask(task);
                return true;
            }

            ret = RET_THEREISNOWAY;
        }

        player->sendCancelMessage(ret);
        return false;
    }

    if(isHotkey)
        showHotkeyUseMessage(player, item);

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerUseBattleWindow, this, playerId, pos, stackpos, creatureId, spriteId, isHotkey));
        player->setNextActionTask(task);
        return false;
    }

    player->setIdleTime(0);
    player->setNextActionTask(NULL);
    return g_actions->useItemEx(player, pos, creature->getPosition(),
        creature->getParent()->__getIndexOfThing(creature), item, isHotkey, creatureId);
}

bool Game::playerCloseContainer(uint32_t playerId, uint8_t cid)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->closeContainer(cid);
    player->sendCloseContainer(cid);
    return true;
}

bool Game::playerMoveUpContainer(uint32_t playerId, uint8_t cid)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Container* container = player->getContainer(cid);
    if(!container)
        return false;

    Container* parentContainer = dynamic_cast<Container*>(container->getParent());
    if(!parentContainer)
        return false;

    player->addContainer(cid, parentContainer);
    player->sendContainer(cid, parentContainer, (dynamic_cast<const Container*>(parentContainer->getParent()) != NULL));
    return true;
}

bool Game::playerUpdateTile(uint32_t playerId, const Position& pos)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(!player->canSee(pos))
        return false;

    if(Tile* tile = getTile(pos))
        player->sendUpdateTile(tile, pos);

    return true;
}

bool Game::playerUpdateContainer(uint32_t playerId, uint8_t cid)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Container* container = player->getContainer(cid);
    if(!container)
        return false;

    player->sendContainer(cid, container, (dynamic_cast<const Container*>(container->getParent()) != NULL));
    return true;
}

bool Game::playerRotateItem(uint32_t playerId, const Position& pos, int16_t stackpos, const uint16_t spriteId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Thing* thing = internalGetThing(player, pos, stackpos);
    if(!thing)
        return false;

    Item* item = thing->getItem();
    if(!item || item->getClientID() != spriteId || !item->isRoteable() || (item->isLoadedFromMap() &&
        (item->getUniqueId() || (item->getActionId() && item->getContainer()))))
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(pos.x != 0xFFFF && !Position::areInRange<1,1,0>(pos, player->getPosition()))
    {
        if(player->getNoMove())
        {
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        std::list<Direction> listDir;
        if(getPathToEx(player, pos, listDir, 0, 1, true, true))
        {
            Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                this, player->getID(), listDir)));

            SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                boost::bind(&Game::playerRotateItem, this, playerId, pos, stackpos, spriteId));

            player->setNextWalkActionTask(task);
            return true;
        }

        player->sendCancelMessage(RET_THEREISNOWAY);
        return false;
    }

    uint16_t newId = Item::items[item->getID()].rotateTo;
    if(newId)
        transformItem(item, newId);

    return true;
}

bool Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string& text)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    uint16_t maxTextLength = 0;
    uint32_t internalWindowTextId = 0;

    Item* writeItem = player->getWriteItem(internalWindowTextId, maxTextLength);
    if(text.length() > maxTextLength || windowTextId != internalWindowTextId)
        return false;

    if(!writeItem || writeItem->isRemoved())
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Cylinder* topParent = writeItem->getTopParent();
    Player* owner = dynamic_cast<Player*>(topParent);
    if(owner && owner != player)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(!Position::areInRange<1,1,0>(writeItem->getPosition(), player->getPosition()))
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    bool deny = false;
    CreatureEventList textEditEvents = player->getCreatureEvents(CREATURE_EVENT_TEXTEDIT);
    for(CreatureEventList::iterator it = textEditEvents.begin(); it != textEditEvents.end(); ++it)
    {
        if(!(*it)->executeTextEdit(player, writeItem, text))
            deny = true;
    }

    player->setWriteItem(NULL);
    if((Container*)writeItem->getParent() == &player->transferContainer)
    {
        player->transferContainer.setParent(NULL);
        player->transferContainer.__removeThing(writeItem, writeItem->getItemCount());

        freeThing(writeItem);
        return true;
    }

    if(deny)
        return false;

    if(!text.empty())
    {
        if(writeItem->getText() != text)
        {
            writeItem->setText(text);
            writeItem->setWriter(player->getName());
            writeItem->setDate(std::time(NULL));
        }
    }
    else
    {
        writeItem->resetText();
        writeItem->resetWriter();
        writeItem->resetDate();
    }

    uint16_t newId = Item::items[writeItem->getID()].writeOnceItemId;
    if(newId)
        transformItem(writeItem, newId);

    return true;
}

bool Game::playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string& text)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    uint32_t internalWindowTextId = 0, internalListId = 0;
    House* house = player->getEditHouse(internalWindowTextId, internalListId);
    if(!house || internalWindowTextId != windowTextId || listId)
        return true;

    bool deny = false;
    CreatureEventList houseEditEvents = player->getCreatureEvents(CREATURE_EVENT_HOUSEEDIT);
    for(CreatureEventList::iterator it = houseEditEvents.begin(); it != houseEditEvents.end(); ++it)
    {
        if(!(*it)->executeHouseEdit(player, house->getId(), internalListId, text))
            deny = true;
    }

    player->setEditHouse(NULL);
    if(deny)
        return false;

    house->setAccessList(internalListId, text);
    return true;
}

bool Game::playerRequestTrade(uint32_t playerId, const Position& pos, int16_t stackpos,
    uint32_t tradePlayerId, uint16_t spriteId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 8))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditiontrade = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 8))
        player->addCondition(conditiontrade);

    Player* tradePartner = getPlayerByID(tradePlayerId);
    if(!tradePartner || tradePartner == player)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(!Position::areInRange<2,2,0>(tradePartner->getPosition(), player->getPosition()))
    {
        player->sendCancelMessage(RET_DESTINATIONOUTOFREACH);
        return false;
    }

    if(!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())
        && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_CANNOTTHROW);
        return false;
    }

    Item* tradeItem = dynamic_cast<Item*>(internalGetThing(player, pos, stackpos, spriteId, STACKPOS_USE));
    if(!tradeItem || tradeItem->getClientID() != spriteId || !tradeItem->isPickupable() || (tradeItem->isLoadedFromMap()
        && (tradeItem->getUniqueId() || (tradeItem->getActionId() && tradeItem->getContainer()))))
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(player->getPosition().z > tradeItem->getPosition().z)
    {
        player->sendCancelMessage(RET_FIRSTGOUPSTAIRS);
        return false;
    }

    if(player->getPosition().z < tradeItem->getPosition().z)
    {
        player->sendCancelMessage(RET_FIRSTGODOWNSTAIRS);
        return false;
    }

    HouseTile* houseTile = dynamic_cast<HouseTile*>(tradeItem->getParent());
    if(houseTile && houseTile->getHouse() && !houseTile->getHouse()->isInvited(player))
    {
        player->sendCancelMessage(RET_PLAYERISNOTINVITED);
        return false;
    }

    if(!Position::areInRange<1,1,0>(tradeItem->getPosition(), player->getPosition()))
    {
        if(player->getNoMove())
        {
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        std::list<Direction> listDir;
        if(getPathToEx(player, pos, listDir, 0, 1, true, true))
        {
            Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                this, player->getID(), listDir)));

            SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                boost::bind(&Game::playerRequestTrade, this, playerId, pos, stackpos, tradePlayerId, spriteId));

            player->setNextWalkActionTask(task);
            return true;
        }

        player->sendCancelMessage(RET_THEREISNOWAY);
        return false;
    }

    const Container* container = NULL;
    for(std::map<Item*, uint32_t>::const_iterator it = tradeItems.begin(); it != tradeItems.end(); ++it)
    {
        if(tradeItem == it->first ||
            ((container = dynamic_cast<const Container*>(tradeItem)) && container->isHoldingItem(it->first)) ||
            ((container = dynamic_cast<const Container*>(it->first)) && container->isHoldingItem(tradeItem)))
        {
            player->sendCancelMessage(RET_YOUAREALREADYTRADING);
            return false;
        }
    }

    Container* tradeContainer = tradeItem->getContainer();
    if(tradeContainer && tradeContainer->getItemHoldingCount() + 1 > (uint32_t)g_config.getNumber(ConfigManager::TRADE_LIMIT))
    {
        player->sendCancelMessage(RET_YOUCANONLYTRADEUPTOX);
        return false;
    }

    bool deny = false;
    CreatureEventList tradeEvents = player->getCreatureEvents(CREATURE_EVENT_TRADE_REQUEST);
    for(CreatureEventList::iterator it = tradeEvents.begin(); it != tradeEvents.end(); ++it)
    {
        if(!(*it)->executeTradeRequest(player, tradePartner, tradeItem))
            deny = true;
    }

    if(deny)
        return false;

    return internalStartTrade(player, tradePartner, tradeItem);
}

bool Game::internalStartTrade(Player* player, Player* tradePartner, Item* tradeItem)
{
    if(player->tradeState != TRADE_NONE && !(player->tradeState == TRADE_ACKNOWLEDGE && player->tradePartner == tradePartner))
    {
        player->sendCancelMessage(RET_YOUAREALREADYTRADING);
        return false;
    }
    else if(tradePartner->tradeState != TRADE_NONE && tradePartner->tradePartner != player)
    {
        player->sendCancelMessage(RET_THISPLAYERISALREADYTRADING);
        return false;
    }

    player->tradePartner = tradePartner;
    player->tradeItem = tradeItem;
    player->tradeState = TRADE_INITIATED;

    tradeItem->addRef();
    tradeItems[tradeItem] = player->getID();

    player->sendTradeItemRequest(player, tradeItem, true);
    if(tradePartner->tradeState == TRADE_NONE)
    {
        char buffer[100];
        sprintf(buffer, "%s wants to trade with you", player->getName().c_str());
        tradePartner->sendTextMessage(MSG_EVENT_ADVANCE, buffer);

        tradePartner->tradeState = TRADE_ACKNOWLEDGE;
        tradePartner->tradePartner = player;
    }
    else
    {
        Item* counterItem = tradePartner->tradeItem;
        player->sendTradeItemRequest(tradePartner, counterItem, false);
        tradePartner->sendTradeItemRequest(player, tradeItem, false);
    }

    return true;
}

bool Game::playerAcceptTrade(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || (player->getTradeState() != TRADE_ACKNOWLEDGE
        && player->getTradeState() != TRADE_INITIATED))
        return false;

    Player* tradePartner = player->tradePartner;
    if(!tradePartner)
        return false;

    if(!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())
        && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_CREATUREISNOTREACHABLE);
        return false;
    }

    player->setTradeState(TRADE_ACCEPT);
    if(tradePartner->getTradeState() != TRADE_ACCEPT)
        return false;

    Item* tradeItem1 = player->tradeItem;
    Item* tradeItem2 = tradePartner->tradeItem;

    bool deny = false;
    CreatureEventList tradeEvents = player->getCreatureEvents(CREATURE_EVENT_TRADE_ACCEPT);
    for(CreatureEventList::iterator it = tradeEvents.begin(); it != tradeEvents.end(); ++it)
    {
        if(!(*it)->executeTradeAccept(player, tradePartner, tradeItem1, tradeItem2))
            deny = true;
    }

    if(deny)
        return false;

    player->setTradeState(TRADE_TRANSFER);
    tradePartner->setTradeState(TRADE_TRANSFER);

    std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradeItem1);
    if(it != tradeItems.end())
    {
        freeThing(it->first);
        tradeItems.erase(it);
    }

    it = tradeItems.find(tradeItem2);
    if(it != tradeItems.end())
    {
        freeThing(it->first);
        tradeItems.erase(it);
    }

    ReturnValue ret1 = internalAddItem(player, tradePartner, tradeItem1, INDEX_WHEREEVER, FLAG_IGNOREAUTOSTACK, true);
    ReturnValue ret2 = internalAddItem(tradePartner, player, tradeItem2, INDEX_WHEREEVER, FLAG_IGNOREAUTOSTACK, true);

    bool success = false;
    if(ret1 == RET_NOERROR && ret2 == RET_NOERROR)
    {
        ret1 = internalRemoveItem(tradePartner, tradeItem1, tradeItem1->getItemCount(), true);
        ret2 = internalRemoveItem(player, tradeItem2, tradeItem2->getItemCount(), true);
        if(ret1 == RET_NOERROR && ret2 == RET_NOERROR)
        {
            internalMoveItem(player, tradeItem1->getParent(), tradePartner, INDEX_WHEREEVER,
                tradeItem1, tradeItem1->getItemCount(), NULL, FLAG_IGNOREAUTOSTACK);
            internalMoveItem(tradePartner, tradeItem2->getParent(), player, INDEX_WHEREEVER,
                tradeItem2, tradeItem2->getItemCount(), NULL, FLAG_IGNOREAUTOSTACK);

            tradeItem1->onTradeEvent(ON_TRADE_TRANSFER, tradePartner, player);
            tradeItem2->onTradeEvent(ON_TRADE_TRANSFER, player, tradePartner);
            success = true;
        }
    }

    if(!success)
    {
        std::string error;
        if(tradeItem2)
        {
            error = getTradeErrorDescription(ret1, tradeItem1);
            tradePartner->sendTextMessage(MSG_EVENT_ADVANCE, error);
            tradeItem2->onTradeEvent(ON_TRADE_CANCEL, tradePartner, player);
        }

        if(tradeItem1)
        {
            error = getTradeErrorDescription(ret2, tradeItem2);
            player->sendTextMessage(MSG_EVENT_ADVANCE, error);
            tradeItem1->onTradeEvent(ON_TRADE_CANCEL, player, tradePartner);
        }
    }

    player->setTradeState(TRADE_NONE);
    player->tradeItem = NULL;
    player->tradePartner = NULL;

    tradePartner->setTradeState(TRADE_NONE);
    tradePartner->tradeItem = NULL;
    tradePartner->tradePartner = NULL;

    player->sendTradeClose();
    tradePartner->sendTradeClose();
    return success;
}

std::string Game::getTradeErrorDescription(ReturnValue ret, Item* item)
{
    if(!item)
        return std::string();

    std::stringstream ss;
    if(ret == RET_NOTENOUGHCAPACITY)
    {
        ss << "You do not have enough capacity to carry";
        if(item->isStackable() && item->getItemCount() > 1)
            ss << " these objects.";
        else
            ss << " this object." ;

        ss << std::endl << " " << item->getWeightDescription();
    }
    else if(ret == RET_NOTENOUGHROOM || ret == RET_CONTAINERNOTENOUGHROOM)
    {
        ss << "You do not have enough room to carry";
        if(item->isStackable() && item->getItemCount() > 1)
            ss << " these objects.";
        else
            ss << " this object.";
    }
    else
        ss << "Trade could not be completed.";

    return ss.str().c_str();
}

bool Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, int32_t index)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Player* tradePartner = player->tradePartner;
    if(!tradePartner)
        return false;

    Item* tradeItem = NULL;
    if(lookAtCounterOffer)
        tradeItem = tradePartner->getTradeItem();
    else
        tradeItem = player->getTradeItem();

    if(!tradeItem)
        return false;

    std::stringstream ss;
    ss << "You see ";

    int32_t lookDistance = std::max(std::abs(player->getPosition().x - tradeItem->getPosition().x),
        std::abs(player->getPosition().y - tradeItem->getPosition().y));
    if(!index)
    {
        ss << tradeItem->getDescription(lookDistance);
        if(player->hasCustomFlag(PlayerCustomFlag_CanSeeItemDetails))
        {
            ss << std::endl << "ItemID: [" << tradeItem->getID() << "]";
            if(tradeItem->getActionId() > 0)
                ss << ", ActionID: [" << tradeItem->getActionId() << "]";

            if(tradeItem->getUniqueId() > 0)
                ss << ", UniqueID: [" << tradeItem->getUniqueId() << "]";

            ss << ".";
            const ItemType& it = Item::items[tradeItem->getID()];
            if(it.transformEquipTo)
                ss << std::endl << "TransformTo (onEquip): [" << it.transformEquipTo << "]";
            else if(it.transformDeEquipTo)
                ss << std::endl << "TransformTo (onDeEquip): [" << it.transformDeEquipTo << "]";
            else if(it.decayTo != -1)
                ss << std::endl << "DecayTo: [" << it.decayTo << "]";
        }

        player->sendTextMessage(MSG_INFO_DESCR, ss.str());
        return false;
    }

    Container* tradeContainer = tradeItem->getContainer();
    if(!tradeContainer || index > (int32_t)tradeContainer->getItemHoldingCount())
        return false;

    std::list<const Container*> listContainer;
    listContainer.push_back(tradeContainer);
    ItemList::const_iterator it;

    Container* tmpContainer = NULL;
    while(listContainer.size() > 0)
    {
        const Container* container = listContainer.front();
        listContainer.pop_front();
        for(it = container->getItems(); it != container->getEnd(); ++it)
        {
            if((tmpContainer = (*it)->getContainer()))
                listContainer.push_back(tmpContainer);

            --index;
            if(index)
                continue;

            ss << (*it)->getDescription(lookDistance);
            if(player->hasCustomFlag(PlayerCustomFlag_CanSeeItemDetails))
            {
                ss << std::endl << "ItemID: [" << (*it)->getID() << "]";
                if((*it)->getActionId() > 0)
                    ss << ", ActionID: [" << (*it)->getActionId() << "]";

                if((*it)->getUniqueId() > 0)
                    ss << ", UniqueID: [" << (*it)->getUniqueId() << "]";

                ss << ".";
                const ItemType& iit = Item::items[(*it)->getID()];
                if(iit.transformEquipTo)
                    ss << std::endl << "TransformTo: [" << iit.transformEquipTo << "] (onEquip).";
                else if(iit.transformDeEquipTo)
                    ss << std::endl << "TransformTo: [" << iit.transformDeEquipTo << "] (onDeEquip).";
                else if(iit.decayTo != -1)
                    ss << std::endl << "DecayTo: [" << iit.decayTo << "].";
            }

            player->sendTextMessage(MSG_INFO_DESCR, ss.str());
            return true;
        }
    }

    return false;
}

bool Game::playerCloseTrade(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    return internalCloseTrade(player);
}

bool Game::internalCloseTrade(Player* player)
{
    Player* tradePartner = player->tradePartner;
    if((tradePartner && tradePartner->getTradeState() == TRADE_TRANSFER) || player->getTradeState() == TRADE_TRANSFER)
    {
        std::clog << "[Warning - Game::internalCloseTrade] TradeState == TRADE_TRANSFER, " <<
            player->getName() << " " << player->getTradeState() << ", " <<
            tradePartner->getName() << " " << tradePartner->getTradeState() << std::endl;
        return true;
    }

    std::vector<Item*>::iterator it;
    if(player->getTradeItem())
    {
        std::map<Item*, uint32_t>::iterator it = tradeItems.find(player->getTradeItem());
        if(it != tradeItems.end())
        {
            freeThing(it->first);
            tradeItems.erase(it);
        }

        player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player, tradePartner);
        player->tradeItem = NULL;
    }

    player->setTradeState(TRADE_NONE);
    player->tradePartner = NULL;

    player->sendTextMessage(MSG_STATUS_SMALL, "Trade cancelled.");
    player->sendTradeClose();
    if(tradePartner)
    {
        if(tradePartner->getTradeItem())
        {
            std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradePartner->getTradeItem());
            if(it != tradeItems.end())
            {
                freeThing(it->first);
                tradeItems.erase(it);
            }

            tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner, player);
            tradePartner->tradeItem = NULL;
        }

        tradePartner->setTradeState(TRADE_NONE);
        tradePartner->tradePartner = NULL;

        tradePartner->sendTextMessage(MSG_STATUS_SMALL, "Trade cancelled.");
        tradePartner->sendTradeClose();
    }

    return true;
}

bool Game::playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount,
    bool ignoreCap/* = false*/, bool inBackpacks/* = false*/)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    int32_t onBuy, onSell;
    Npc* merchant = player->getShopOwner(onBuy, onSell);
    if(!merchant)
        return false;

    const ItemType& it = Item::items.getItemIdByClientId(spriteId);
    if(!it.id)
        return false;

    uint8_t subType = count;
    if(it.isFluidContainer() && count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t)))
        subType = reverseFluidMap[count];
    else if(!it.hasSubType())
        subType = 0;

    if(!player->canShopItem(it.id, subType, SHOPEVENT_BUY))
        return false;

    merchant->onPlayerTrade(player, SHOPEVENT_BUY, onBuy, it.id, subType, amount, ignoreCap, inBackpacks);
    return true;
}

bool Game::playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, bool ignoreEquipped)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    int32_t onBuy, onSell;
    Npc* merchant = player->getShopOwner(onBuy, onSell);
    if(!merchant)
        return false;

    const ItemType& it = Item::items.getItemIdByClientId(spriteId);
    if(!it.id)
        return false;

    uint8_t subType = count;
    if(it.isFluidContainer() && count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t)))
        subType = reverseFluidMap[count];
    else if(!it.hasSubType())
        subType = 0;

    if(!player->canShopItem(it.id, subType, SHOPEVENT_SELL))
        return false;

    merchant->onPlayerTrade(player, SHOPEVENT_SELL, onSell, it.id, subType, amount, ignoreEquipped);
    return true;
}

bool Game::playerCloseShop(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(player == NULL || player->isRemoved())
        return false;

    player->closeShopWindow();
    return true;
}

bool Game::playerLookInShop(uint32_t playerId, uint16_t spriteId, uint8_t count)
{
    Player* player = getPlayerByID(playerId);
    if(player == NULL || player->isRemoved())
        return false;

    const ItemType& it = Item::items.getItemIdByClientId(spriteId);
    if(!it.id)
        return false;

    int32_t subType = count;
    if(it.isSplash() || it.isFluidContainer())
    {
        if(subType == 3)
            subType = 11;
        else if(count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t)))
            subType = reverseFluidMap[count];
    }

    std::stringstream ss;
    ss << "You see " << Item::getDescription(it, 1, NULL, subType);
    if(player->hasCustomFlag(PlayerCustomFlag_CanSeeItemDetails))
        ss << std::endl << "ItemID: [" << it.id << "].";

    player->sendTextMessage(MSG_INFO_DESCR, ss.str());
    return true;
}

bool Game::playerLookAt(uint32_t playerId, const Position& pos, uint16_t spriteId, int16_t stackpos)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 7))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionlook = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 7))
        player->addCondition(conditionlook);

    Thing* thing = internalGetThing(player, pos, stackpos, spriteId, STACKPOS_LOOK);
    if(!thing)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Position thingPos = pos;
    if(pos.x == 0xFFFF)
        thingPos = thing->getPosition();

    if(!player->canSee(thingPos))
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Position playerPos = player->getPosition();
    int32_t lookDistance = -1;
    if(thing != player)
    {
        lookDistance = std::max(std::abs(playerPos.x - thingPos.x), std::abs(playerPos.y - thingPos.y));
        if(playerPos.z != thingPos.z)
            lookDistance += 15;
    }

    bool deny = false;
    CreatureEventList lookEvents = player->getCreatureEvents(CREATURE_EVENT_LOOK);
    for(CreatureEventList::iterator it = lookEvents.begin(); it != lookEvents.end(); ++it)
    {
        if(!(*it)->executeLook(player, thing, thingPos, stackpos, lookDistance))
            deny = true;
    }

    if(deny)
        return false;

    std::stringstream ss;
    ss << "You see " << thing->getDescription(lookDistance);
    if(player->hasCustomFlag(PlayerCustomFlag_CanSeeItemDetails))
    {
        if(Item* item = thing->getItem())
        {
            ss << std::endl << "ItemID: [" << item->getID() << "]";
            if(item->getActionId() > 0)
                ss << ", ActionID: [" << item->getActionId() << "]";

            if(item->getUniqueId() > 0)
                ss << ", UniqueID: [" << item->getUniqueId() << "]";

            ss << ".";
            const ItemType& it = Item::items[item->getID()];
            if(it.transformEquipTo)
                ss << std::endl << "TransformTo: [" << it.transformEquipTo << "] (onEquip).";
            else if(it.transformDeEquipTo)
                ss << std::endl << "TransformTo: [" << it.transformDeEquipTo << "] (onDeEquip).";

            if(it.transformUseTo)
                ss << std::endl << "TransformTo: [" << it.transformUseTo << "] (onUse).";

            if(it.decayTo != -1)
                ss << std::endl << "DecayTo: [" << it.decayTo << "].";
        }
    }

    if(player->hasCustomFlag(PlayerCustomFlag_CanSeeCreatureDetails))
    {
        if(const Creature* creature = thing->getCreature())
        {
            if(!player->hasFlag(PlayerFlag_HideHealth))
            {
                ss << std::endl << "Health: [" << creature->getHealth() << " / " << creature->getMaxHealth() << "]";
                if(creature->getMaxMana() > 0)
                    ss << ", Mana: [" << creature->getMana() << " / " << creature->getMaxMana() << "]";

                ss << ".";
            }

            if(const Player* target = creature->getPlayer())
            {
                ss << std::endl << "IP: " << convertIPAddress(target->getIP());
#if CLIENT_VERSION_MIN != CLIENT_VERSION_MAX
                ss << ", Client: " << target->getClientVersion();
#endif
                ss << ".";
            }

            if(creature->isGhost())
                ss << std::endl << "* Ghost mode *";
        }
    }

    if(player->hasCustomFlag(PlayerCustomFlag_CanSeePosition))
    {
        ss << std::endl << "Position: [X: " << thingPos.x << "] [Y: " << thingPos.y << "] [Z: " << thingPos.z << "]";
        if(Tile* tile = getTile(thingPos))
        {
            if(House* house = tile->getHouse())
                ss << " [House: " << house->getId() << "]";
        }

        ss << ".";
    }

    player->sendTextMessage(MSG_INFO_DESCR, ss.str());
    return true;
}

bool Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Creature* creature = getCreatureByID(creatureId);
    if(!creature || creature->isRemoved())
        return false;

    if(!player->canSeeCreature(creature))
        return false;

    const Position& creaturePos = creature->getPosition();
    if(!player->canSee(creaturePos))
        return false;

    int32_t lookDistance;
    if(creature != player)
    {
        const Position& playerPos = player->getPosition();
        lookDistance = std::max(std::abs(playerPos.x - creaturePos.x), std::abs(playerPos.y - creaturePos.y));
        if(playerPos.z != creaturePos.z)
            lookDistance += 15;
    }
    else
        lookDistance = -1;

    std::ostringstream ss;
    ss << "You see " << creature->getDescription(lookDistance);
    if(player->hasCustomFlag(PlayerCustomFlag_CanSeeCreatureDetails))
    {
        if(!player->hasFlag(PlayerFlag_HideHealth))
        {
            ss << std::endl << "Health: [" << creature->getHealth() << " / " << creature->getMaxHealth() << "]";
            if(creature->getMaxMana() > 0)
                ss << ", Mana: [" << creature->getMana() << " / " << creature->getMaxMana() << "]";

            ss << ".";
        }

        if(const Player* target = creature->getPlayer())
        {
            ss << std::endl << "IP: " << convertIPAddress(target->getIP());
#if CLIENT_VERSION_MIN != CLIENT_VERSION_MAX
            ss << ", Client: " << target->getClientVersion();
#endif
            ss << ".";
        }

        if(creature->isGhost())
            ss << std::endl << "* Ghost mode *";
    }

    if(player->hasCustomFlag(PlayerCustomFlag_CanSeePosition))
    {
        ss << std::endl << "Position: [X: " << creaturePos.x << "] [Y: " << creaturePos.y << "] [Z: " << creaturePos.z << "]";
        ss << ".";
    }

    player->sendTextMessage(MSG_INFO_DESCR, ss.str());
    return true;
}

bool Game::playerQuests(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->sendQuests();
    return true;
}

bool Game::playerQuestInfo(uint32_t playerId, uint16_t questId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Quest* quest = Quests::getInstance()->getQuestById(questId);
    if(!quest)
        return false;

    player->sendQuestInfo(quest);
    return true;
}

bool Game::playerCancelAttackAndFollow(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    playerSetAttackedCreature(playerId, 0);
    playerFollowCreature(playerId, 0);

    player->stopWalk();
    return true;
}

bool Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->getAttackedCreature() && !creatureId)
    {
        player->setAttackedCreature(NULL);
        player->sendCancelTarget();
        return true;
    }

    Creature* attackCreature = getCreatureByID(creatureId);
    if(!attackCreature)
    {
        player->setAttackedCreature(NULL);
        player->sendCancelTarget();
        return false;
    }

    ReturnValue ret = Combat::canTargetCreature(player, attackCreature);
    if(ret != RET_NOERROR)
    {
        if(ret != RET_NEEDEXCHANGE)
            player->sendCancelMessage(ret);

        player->sendCancelTarget();
        player->setAttackedCreature(NULL);
        return false;
    }

    player->setAttackedCreature(attackCreature);
    Dispatcher::getInstance().addTask(createTask(boost::bind(
        &Game::updateCreatureWalk, this, player->getID())));
    return true;
}

bool Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Creature* followCreature = NULL;
    if(creatureId)
    {
        if(player->getNoMove())
        {
            playerCancelAttackAndFollow(playerId);
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        followCreature = getCreatureByID(creatureId);
    }

    player->setAttackedCreature(NULL);
    Dispatcher::getInstance().addTask(createTask(boost::bind(
        &Game::updateCreatureWalk, this, player->getID())));
    return player->setFollowCreature(followCreature);
}

bool Game::playerSetFightModes(uint32_t playerId, fightMode_t fightMode, chaseMode_t chaseMode, secureMode_t secureMode)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->setFightMode(fightMode);
    player->setChaseMode(chaseMode);
    player->setSecureMode(secureMode);

    player->setLastAttack(OTSYS_TIME());
    return true;
}

bool Game::playerRequestAddVip(uint32_t playerId, const std::string& vipName)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->canDoExAction())
        return false;

    uint32_t guid;
    bool specialVip;
    std::string name = vipName;

    player->setNextExAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::CUSTOM_ACTIONS_DELAY_INTERVAL) - 10);
    if(!IOLoginData::getInstance()->getGuidByNameEx(guid, specialVip, name))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "A player with that name does not exist.");
        return false;
    }

    if(specialVip && !player->hasFlag(PlayerFlag_SpecialVIP))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You cannot add this player.");
        return false;
    }

    if(player->hasCondition(CONDITION_EXHAUST, 1))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "Please wait few seconds before adding new player to your vip list.");
        return false;
    }

    bool online = false;
    if(Player* target = getPlayerByName(name))
        online = player->canSeeCreature(target);

    if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 3000, 0, false, 1))
        player->addCondition(condition);

    return player->addVIP(guid, name, online);
}

bool Game::playerRequestRemoveVip(uint32_t playerId, uint32_t guid)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 1))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "Please wait few seconds before deleting next player from your vip list.");
        return false;
    }

    if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 3000, 0, false, 1))
        player->addCondition(condition);

    player->removeVIP(guid);
    return true;
}

bool Game::playerTurn(uint32_t playerId, Direction dir)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(internalCreatureTurn(player, dir))
    {
        player->setIdleTime(0);
        return true;
    }

    if(player->getDirection() != dir || !player->hasCustomFlag(PlayerCustomFlag_CanTurnhop))
        return false;

    Position pos = getNextPosition(dir, player->getPosition());
    Tile* tile = map->getTile(pos);
    if(!tile || !tile->ground)
        return false;

    player->setIdleTime(0);
    ReturnValue ret = tile->__queryAdd(0, player, 1, FLAG_IGNOREBLOCKITEM);
    if(ret != RET_NOTENOUGHROOM && (ret != RET_NOTPOSSIBLE || player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere)))
        return (internalTeleport(player, pos, false, FLAG_NOLIMIT, false) != RET_NOERROR);

    player->sendCancelMessage(ret);
    return false;
}

bool Game::playerRequestOutfit(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 4))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionoutfit = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 4))
        player->addCondition(conditionoutfit);

    player->sendOutfitWindow();
    return true;
}

bool Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 4))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionoutfit = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 1000, 0, false, 4))
        player->addCondition(conditionoutfit);

    if(!player->changeOutfit(outfit, true))
        return false;

    player->setIdleTime(0);
    if(!player->hasCondition(CONDITION_OUTFIT, -1))
        internalCreatureChangeOutfit(player, outfit);

    return true;
}

bool Game::playerSay(uint32_t playerId, uint16_t channelId, MessageClasses type, const std::string& receiver, const std::string& text)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->setIdleTime(0);

    int32_t muted = 0;
    bool mute = player->isMuted(channelId, type, muted);
    if(muted && mute)
    {
        if(muted > 0)
        {
            char buffer[75];
            sprintf(buffer, "You are still muted for %d seconds.", muted);
            player->sendTextMessage(MSG_STATUS_SMALL, buffer);
        }
        else
            player->sendTextMessage(MSG_STATUS_SMALL, "You are muted permanently.");

        return false;
    }

    if(player->isAccountManager())
    {
        if(mute)
            player->removeMessageBuffer();

        return internalCreatureSay(player, MSG_SPEAK_SAY, text, false);
    }

    if(g_talkActions->onPlayerSay(player, type == MSG_SPEAK_SAY ? (unsigned)CHANNEL_DEFAULT : channelId, text, false))
        return true;

    ReturnValue ret = RET_NOERROR;
    if(!muted)
    {
        ret = g_spells->onPlayerSay(player, text);
        if(ret == RET_NOERROR || (ret == RET_NEEDEXCHANGE &&
            !g_config.getBool(ConfigManager::BUFFER_SPELL_FAILURE)))
            return true;
    }

    if(mute && type != MSG_NPC_TO)
        player->removeMessageBuffer();

    if(ret == RET_NEEDEXCHANGE)
        return true;

    uint32_t statementId = 0;
    if(g_config.getBool(ConfigManager::SAVE_STATEMENT))
        IOLoginData::getInstance()->playerStatement(player, channelId, text, statementId);

    switch(type)
    {
        case MSG_SPEAK_SAY:
            return internalCreatureSay(player, MSG_SPEAK_SAY, text, false, NULL, NULL, statementId);
        case MSG_SPEAK_WHISPER:
            return playerWhisper(player, text, statementId);
        case MSG_SPEAK_YELL:
            return playerYell(player, text, statementId);
        case MSG_PRIVATE:
        case MSG_GAMEMASTER_PRIVATE:
        case MSG_RVR_ANSWER:
            return playerSpeakTo(player, type, receiver, text, statementId);
        case MSG_CHANNEL:
        case MSG_CHANNEL_HIGHLIGHT:
        case MSG_GAMEMASTER_CHANNEL:
        case MSG_GAMEMASTER_ANONYMOUS:
        {
            if(playerSpeakToChannel(player, type, text, channelId, statementId))
                return true;

            return internalCreatureSay(player, MSG_SPEAK_SAY, text, false, NULL, NULL, statementId);
        }
        case MSG_NPC_TO:
            return playerSpeakToNpc(player, text);
        case MSG_GAMEMASTER_BROADCAST:
            return playerBroadcastMessage(player, MSG_GAMEMASTER_BROADCAST, text, statementId);

        default:
            break;
    }

    return false;
}

bool Game::playerWhisper(Player* player, const std::string& text, uint32_t statementId)
{
    SpectatorVec list;
    getSpectators(list, player->getPosition(), false, false, 1, 1);
    internalCreatureSay(player, MSG_SPEAK_WHISPER, text, false, &list, NULL, statementId);
    return true;
}

bool Game::playerYell(Player* player, const std::string& text, uint32_t statementId)
{
    if(player->getLevel() <= 1 && !player->hasFlag(PlayerFlag_CannotBeMuted))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You may not yell as long as you are on level 1.");
        return true;
    }

    if(player->hasCondition(CONDITION_MUTED, 1))
    {
        player->sendCancelMessage(RET_YOUAREEXHAUSTED);
        return true;
    }

    if(!player->hasFlag(PlayerFlag_CannotBeMuted))
    {
        if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, 30000, 0, false, 1))
            player->addCondition(condition);
    }

    internalCreatureSay(player, MSG_SPEAK_YELL, asUpperCaseString(text), false, NULL, NULL, statementId);
    return true;
}

bool Game::playerSpeakTo(Player* player, MessageClasses type, const std::string& receiver,
    const std::string& text, uint32_t statementId)
{
    Player* toPlayer = getPlayerByName(receiver);
    if(!toPlayer || toPlayer->isRemoved())
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "A player with this name is not online.");
        return false;
    }

    bool canSee = player->canSeeCreature(toPlayer);
    if(toPlayer->hasCondition(CONDITION_GAMEMASTER, GAMEMASTER_IGNORE)
        && !player->hasFlag(PlayerFlag_CannotBeMuted))
    {
        char buffer[70];
        if(!canSee)
            sprintf(buffer, "A player with this name is not online.");
        else
            sprintf(buffer, "Sorry, %s is currently ignoring private messages.", toPlayer->getName().c_str());

        player->sendTextMessage(MSG_STATUS_SMALL, buffer);
        return false;
    }

    if(type == MSG_GAMEMASTER_PRIVATE && !player->hasFlag(PlayerFlag_CanTalkRedPrivate))
        type = MSG_PRIVATE;

    toPlayer->sendCreatureSay(player, type, text, NULL, statementId);
    toPlayer->onCreatureSay(player, type, text);
    if(!canSee)
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "A player with this name is not online.");
        return false;
    }

    char buffer[80];
    sprintf(buffer, "Message sent to %s.", toPlayer->getName().c_str());
    player->sendTextMessage(MSG_STATUS_SMALL, buffer);
    return true;
}

bool Game::playerSpeakToChannel(Player* player, MessageClasses type, const std::string& text, uint16_t channelId, uint32_t statementId)
{
    if(player->hasCondition(CONDITION_EXHAUST, 6))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionchannel = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 6))
        player->addCondition(conditionchannel);

    switch(type)
    {
        case MSG_CHANNEL:
        {
            if(channelId == CHANNEL_HELP && player->hasFlag(PlayerFlag_TalkOrangeHelpChannel))
                type = MSG_CHANNEL_HIGHLIGHT;

            break;
        }

        case MSG_GAMEMASTER_CHANNEL:
        {
            if(!player->hasFlag(PlayerFlag_CanTalkRedChannel))
                type = MSG_CHANNEL;
            break;
        }

        case MSG_GAMEMASTER_ANONYMOUS:
        {
            if(!player->hasFlag(PlayerFlag_CanTalkRedChannelAnonymous))
                type = MSG_CHANNEL;
            break;
        }

        case MSG_GAMEMASTER_BROADCAST:
        {
            if(!player->hasFlag(PlayerFlag_CanBroadcast))
                type = MSG_CHANNEL;
            break;
        }

        default:
            break;
    }

    if(text.length() < 251)
        return g_chat.talk(player, type, text, channelId, statementId, false);

    player->sendCancelMessage(RET_NOTPOSSIBLE);
    return false;
}

bool Game::playerSpeakToNpc(Player* player, const std::string& text)
{
    SpectatorVec list;
    SpectatorVec::iterator it;
    getSpectators(list, player->getPosition());

    if(player->hasCondition(CONDITION_EXHAUST, 2))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionnpc = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 2))
        player->addCondition(conditionnpc);

    //send to npcs only
    Npc* tmpNpc;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpNpc = (*it)->getNpc()))
            (*it)->onCreatureSay(player, MSG_NPC_TO, text);
    }
    return true;
}

bool Game::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight /*= true*/,
    int32_t rangex/* = Map::maxClientViewportX*/, int32_t rangey/* = Map::maxClientViewportY*/)
{
    return map->canThrowObjectTo(fromPos, toPos, checkLineOfSight, rangex, rangey);
}

bool Game::isSightClear(const Position& fromPos, const Position& toPos, bool floorCheck)
{
    return map->isSightClear(fromPos, toPos, floorCheck);
}

bool Game::internalCreatureTurn(Creature* creature, Direction dir)
{
    bool deny = false;
    CreatureEventList directionEvents = creature->getCreatureEvents(CREATURE_EVENT_DIRECTION);
    for(CreatureEventList::iterator it = directionEvents.begin(); it != directionEvents.end(); ++it)
    {
        if(!(*it)->executeDirection(creature, creature->getDirection(), dir) && !deny)
            deny = true;
    }

    if(deny || creature->getDirection() == dir)
        return false;

    creature->setDirection(dir);
    const SpectatorVec& list = getSpectators(creature->getPosition());
    SpectatorVec::const_iterator it;

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureTurn(creature);
    }

    //event method
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureTurn(creature);

    return true;
}

bool Game::internalCreatureSay(Creature* creature, MessageClasses type, const std::string& text,
    bool ghostMode, SpectatorVec* spectators/* = NULL*/, Position* pos/* = NULL*/, uint32_t statementId/* = 0*/)
{
    Player* player = creature->getPlayer();
    if(player && player->isAccountManager() && !ghostMode)
    {
        player->manageAccount(text);
        return true;
    }

    Position destPos = creature->getPosition();
    if(pos)
        destPos = (*pos);

    SpectatorVec list;
    SpectatorVec::const_iterator it;
    if(!spectators || !spectators->size())
    {
        // This somewhat complex construct ensures that the cached SpectatorVec
        // is used if available and if it can be used, else a local vector is
        // used (hopefully the compiler will optimize away the construction of
        // the temporary when it's not used).
        if(type != MSG_SPEAK_YELL && type != MSG_SPEAK_MONSTER_YELL)
            getSpectators(list, destPos, false, false,
                Map::maxClientViewportX, Map::maxClientViewportX,
                Map::maxClientViewportY, Map::maxClientViewportY);
        else
            getSpectators(list, destPos, false, true, 18, 18, 14, 14);
    }
    else
        list = (*spectators);

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if(!(tmpPlayer = (*it)->getPlayer()))
            continue;

        if(!ghostMode || tmpPlayer->canSeeCreature(creature))
            tmpPlayer->sendCreatureSay(creature, type, text, &destPos, statementId);
    }

    //event method
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureSay(creature, type, text, &destPos);

    return true;
}

bool Game::getPathTo(const Creature* creature, const Position& destPos,
    std::list<Direction>& listDir, int32_t maxSearchDist /*= -1*/)
{
    return map->getPathTo(creature, destPos, listDir, maxSearchDist);
}

bool Game::getPathToEx(const Creature* creature, const Position& targetPos,
    std::list<Direction>& dirList, const FindPathParams& fpp)
{
    return map->getPathMatching(creature, dirList, FrozenPathingConditionCall(targetPos), fpp);
}

bool Game::getPathToEx(const Creature* creature, const Position& targetPos, std::list<Direction>& dirList,
    uint32_t minTargetDist, uint32_t maxTargetDist, bool fullPathSearch/* = true*/,
    bool clearSight/* = true*/, int32_t maxSearchDist/* = -1*/)
{
    FindPathParams fpp;
    fpp.fullPathSearch = fullPathSearch;
    fpp.maxSearchDist = maxSearchDist;
    fpp.clearSight = clearSight;
    fpp.minTargetDist = minTargetDist;
    fpp.maxTargetDist = maxTargetDist;
    return getPathToEx(creature, targetPos, dirList, fpp);
}

bool Game::steerCreature(Creature* creature, const Position& position, uint16_t maxNodes/* = 100*/)
{
    FindPathParams fpp;
    fpp.maxClosedNodes = maxNodes;

    fpp.fullPathSearch = true;
    fpp.maxSearchDist = -1;
    fpp.minTargetDist = 0;
    fpp.maxTargetDist = 1;

    std::list<Direction> dirList;
    if(!getPathToEx(creature, position, dirList, fpp))
        return false;

    if(Player* player = creature->getPlayer())
        player->setNextWalkTask(NULL);

    creature->startAutoWalk(dirList);
    return true;
}

void Game::checkCreatureWalk(uint32_t creatureId)
{
    Creature* creature = getCreatureByID(creatureId);
    if(!creature || creature->getHealth() < 1)
        return;

    creature->onWalk();
    cleanup();
}

void Game::updateCreatureWalk(uint32_t creatureId)
{
    Creature* creature = getCreatureByID(creatureId);
    if(creature && creature->getHealth() > 0)
        creature->goToFollowCreature();
}

void Game::checkCreatureAttack(uint32_t creatureId)
{
    Creature* creature = getCreatureByID(creatureId);
    if(creature && creature->getHealth() > 0)
        creature->onAttacking(0);
}

void Game::addCreatureCheck(Creature* creature)
{
    if(creature->isRemoved())
        return;

    creature->checked = true;
    if(creature->checkVector >= 0) //already in a vector, or about to be added
        return;

    toAddCheckCreatureVector.push_back(creature);
    creature->checkVector = random_range(0, EVENT_CREATURECOUNT - 1);
    creature->addRef();
}

void Game::removeCreatureCheck(Creature* creature)
{
    if(creature->checkVector == -1) //not in any vector
        return;

    creature->checked = false;
}

void Game::checkCreatures()
{
    checkCreatureEvent = Scheduler::getInstance().addEvent(createSchedulerTask(
        EVENT_CHECK_CREATURE_INTERVAL, boost::bind(&Game::checkCreatures, this)));
    if(++checkCreatureLastIndex == EVENT_CREATURECOUNT)
        checkCreatureLastIndex = 0;

    std::vector<Creature*>::iterator it;
#ifndef __GROUPED_ATTACKS__
    std::vector<Creature*> creatureVector;
    for(uint16_t i = 0; i < EVENT_CREATURECOUNT; ++i)
    {
        if(i == checkCreatureLastIndex)
            continue;

        creatureVector = checkCreatureVectors;
        for(it = creatureVector.begin(); it != creatureVector.end(); ++it)
        {
            if((*it)->checked && (*it)->getHealth() > 0)
                (*it)->onAttacking(EVENT_CHECK_CREATURE_INTERVAL);
        }
    }
#endif

    for(it = toAddCheckCreatureVector.begin(); it != toAddCheckCreatureVector.end(); ++it)
        checkCreatureVectors[(*it)->checkVector].push_back(*it);

    toAddCheckCreatureVector.clear();
    std::vector<Creature*>& checkCreatureVector = checkCreatureVectors[checkCreatureLastIndex];
    for(it = checkCreatureVector.begin(); it != checkCreatureVector.end(); )
    {
        if((*it)->checked)
        {
            if((*it)->getHealth() > 0 || !(*it)->onDeath())
                (*it)->onThink(EVENT_CREATURE_THINK_INTERVAL);

            ++it;
        }
        else
        {
            (*it)->checkVector = -1;
            freeThing(*it);
            it = checkCreatureVector.erase(it);
        }
    }

    cleanup();
}

void Game::changeSpeed(Creature* creature, int32_t varSpeed)
{
    if(!creature)
        return;

    creature->setSpeed(creature->getSpeed() - creature->getBaseSpeed() + varSpeed);
    const SpectatorVec& list = getSpectators(creature->getPosition());
    SpectatorVec::const_iterator it;

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((*it) && (tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendChangeSpeed(creature, creature->getStepSpeed());
    }
}

void Game::internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit, bool forced/* = false*/)
{
    if(!forced)
    {
        bool deny = false;
        CreatureEventList outfitEvents = creature->getCreatureEvents(CREATURE_EVENT_OUTFIT);
        for(CreatureEventList::iterator it = outfitEvents.begin(); it != outfitEvents.end(); ++it)
        {
            if(!(*it)->executeOutfit(creature, creature->getCurrentOutfit(), outfit) && !deny)
                deny = true;
        }

        if(deny || creature->getCurrentOutfit() == outfit)
            return;
    }

    creature->setCurrentOutfit(outfit);
    const SpectatorVec& list = getSpectators(creature->getPosition());
    SpectatorVec::const_iterator it;

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureChangeOutfit(creature, outfit);
    }

    //event method
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureChangeOutfit(creature, outfit);
}

void Game::internalCreatureChangeVisible(Creature* creature, Visible_t visible)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());
    SpectatorVec::const_iterator it;

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureChangeVisible(creature, visible);
    }

    //event method
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureChangeVisible(creature, visible);
}


void Game::changeLight(const Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()) && !tmpPlayer->hasCustomFlag(PlayerCustomFlag_HasFullLight))
            tmpPlayer->sendCreatureLight(creature);
    }
}

bool Game::combatBlockHit(CombatType_t combatType, Creature* attacker, Creature* target,
    int32_t& healthChange, bool checkDefense, bool checkArmor, bool field/* = false*/, bool element/* = false*/)
{
    if(healthChange > 0)
        return false;

    const Position& targetPos = target->getPosition();
    const SpectatorVec& list = getSpectators(targetPos);
    if(Combat::canDoCombat(attacker, target, true) != RET_NOERROR)
    {
        if(!element)
            addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF, target->isGhost());

        return true;
    }

    int32_t damage = -healthChange;
    BlockType_t blockType = target->blockHit(attacker, combatType,
        damage, checkDefense, checkArmor, !field, field, element);

    healthChange = -damage;
    if(blockType == BLOCK_DEFENSE)
    {
        if(!element)
            addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF);

        return true;
    }

    if(blockType == BLOCK_ARMOR)
    {
        if(!element)
            addMagicEffect(list, targetPos, MAGIC_EFFECT_BLOCKHIT);

        return true;
    }

    if(blockType != BLOCK_IMMUNITY)
        return false;

    if(element)
        return true;

    MagicEffect_t effect = MAGIC_EFFECT_NONE;
    switch(combatType)
    {
        case COMBAT_UNDEFINEDDAMAGE:
            break;

        case COMBAT_ENERGYDAMAGE:
        case COMBAT_FIREDAMAGE:
        case COMBAT_PHYSICALDAMAGE:
        case COMBAT_ICEDAMAGE:
        case COMBAT_DEATHDAMAGE:
        case COMBAT_EARTHDAMAGE:
        case COMBAT_HOLYDAMAGE:
        {
            effect = MAGIC_EFFECT_BLOCKHIT;
            break;
        }

        default:
        {
            effect = MAGIC_EFFECT_POFF;
            break;
        }
    }

    addMagicEffect(list, targetPos, effect);
    return true;
}

bool Game::combatChangeHealth(CombatType_t combatType, Creature* attacker, Creature* target, int32_t healthChange,
    MagicEffect_t hitEffect/* = MAGIC_EFFECT_UNKNOWN*/, Color_t hitColor/* = COLOR_UNKNOWN*/, bool force/* = false*/)
{
    CombatParams params;
    params.effects.hit =  hitEffect;
    params.effects.color = hitColor;

    params.combatType = combatType;
    return combatChangeHealth(params, attacker, target, healthChange, force);
}

bool Game::combatChangeHealth(const CombatParams& params, Creature* attacker, Creature* target, int32_t healthChange, bool force)
{
    const Position& targetPos = target->getPosition();
    if(healthChange > 0)
    {
        if(!force && target->getHealth() <= 0)
            return false;

        bool deny = false;
        CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE);
        for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it)
        {
            if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_HEALTHGAIN, params.combatType, healthChange))
                deny = true;
        }

        if(deny)
            return false;

        int32_t oldHealth = target->getHealth();
        target->gainHealth(attacker, healthChange);
        if(oldHealth != target->getHealth() && g_config.getBool(ConfigManager::SHOW_HEALTH_CHANGE) && !target->isGhost() &&
            (g_config.getBool(ConfigManager::SHOW_HEALTH_CHANGE_MONSTER) || !target->getMonster()))
        {
            const SpectatorVec& list = getSpectators(targetPos);
            if(params.combatType != COMBAT_HEALING)
                addMagicEffect(list, targetPos, MAGIC_EFFECT_WRAPS_BLUE);

            SpectatorVec textList;
            for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
            {
                if(!(*it)->getPlayer())
                    continue;

                if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z)
                    textList.push_back(*it);
            }

            healthChange = (target->getHealth() - oldHealth);
            std::string plural = (healthChange != 1 ? "s." : ".");

            std::stringstream ss;
            char buffer[20];
            sprintf(buffer, "+%d", healthChange);
            addAnimatedText(list, targetPos, COLOR_MAYABLUE, buffer);
            if(!textList.empty())
            {
                if(!attacker)
                    ss << ucfirst(target->getNameDescription()) << " is healed for " << healthChange << " hitpoint" << plural;
                else if(attacker != target)
                    ss << ucfirst(attacker->getNameDescription()) << " heals " << target->getNameDescription() << " for " << healthChange << " hitpoint" << plural;
                else
                {
                    ss << ucfirst(attacker->getNameDescription()) << " heals ";
                    if(Player* attackerPlayer = attacker->getPlayer())
                        ss << (attackerPlayer->getSex(false) == PLAYERSEX_FEMALE ? "herself" : "himself") << " for " << healthChange << " hitpoint" << plural;
                    else
                        ss << "itself for " << healthChange << " hitpoint" << plural;
                }

                addStatsMessage(textList, MSG_HEALED_OTHERS, ss.str(), targetPos);
                ss.str("");
            }

            Player* player = NULL;
            if(attacker && (player = attacker->getPlayer()))
            {
                if(attacker != target)
                    ss << "You healed " << target->getNameDescription() << " for " << healthChange << " hitpoint" << plural;
                else
                    ss << "You healed yourself for " << healthChange << " hitpoint" << plural;

                player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos);
                ss.str("");
            }

            if((player = target->getPlayer()) && attacker != target)
            {
                if(attacker)
                    ss << ucfirst(attacker->getNameDescription()) << " heals you for " << healthChange << " hitpoint" << plural;
                else
                    ss << "You are healed for " << healthChange << " hitpoint" << plural;

                player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos);
            }
        }
    }
    else
    {
        const SpectatorVec& list = getSpectators(targetPos);
        if(target->getHealth() < 1 || Combat::canDoCombat(attacker, target, true) != RET_NOERROR)
        {
            addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF);
            return true;
        }

        int32_t elementDamage = 0;
        if(params.element.damage && params.element.type != COMBAT_NONE)
            elementDamage = -params.element.damage;

        int32_t damage = -healthChange;
        if(damage > 0)
        {
            if(target->hasCondition(CONDITION_MANASHIELD) && params.combatType != COMBAT_UNDEFINEDDAMAGE)
            {
                int32_t manaDamage = std::min(target->getMana(), damage + elementDamage);
                damage = std::max((int32_t)0, damage + elementDamage - manaDamage);

                elementDamage = 0; // TODO: I don't know how it works ;(
                if(manaDamage && combatChangeMana(attacker, target, -manaDamage, params.combatType, true))
                    addMagicEffect(list, targetPos, MAGIC_EFFECT_LOSE_ENERGY);
            }

            damage = std::min(target->getHealth(), damage);
            if(damage > 0)
            {
                bool deny = false;
                CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE);
                for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it)
                {
                    if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_HEALTHLOSS, params.combatType, damage))
                        deny = true;
                }

                if(deny)
                    return false;

                target->drainHealth(attacker, params.combatType, damage);
                if(elementDamage)
                    target->drainHealth(attacker, params.element.type, elementDamage);

                Color_t textColor = COLOR_NONE;
                MagicEffect_t magicEffect = MAGIC_EFFECT_NONE;

                addCreatureHealth(list, target);
                if(params.combatType == COMBAT_PHYSICALDAMAGE)
                {
                    Item* splash = NULL;
                    switch(target->getRace())
                    {
                        case RACE_VENOM:
                            textColor = COLOR_LIGHTGREEN;
                            magicEffect = MAGIC_EFFECT_POISON;
                            splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_GREEN);
                            break;

                        case RACE_BLOOD:
                            textColor = COLOR_RED;
                            magicEffect = MAGIC_EFFECT_DRAW_BLOOD;
                            splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_BLOOD);
                            break;

                        case RACE_UNDEAD:
                            textColor = COLOR_GREY;
                            magicEffect = MAGIC_EFFECT_HIT_AREA;
                            break;

                        case RACE_FIRE:
                            textColor = COLOR_ORANGE;
                            magicEffect = MAGIC_EFFECT_DRAW_BLOOD;
                            break;

                        case RACE_ENERGY:
                            textColor = COLOR_PURPLE;
                            magicEffect = MAGIC_EFFECT_PURPLEENERGY;
                            break;

                        default:
                            break;
                    }

                    if(splash)
                    {
                        internalAddItem(NULL, target->getTile(), splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
                        startDecay(splash);
                    }
                }
                else
                    getCombatDetails(params.combatType, magicEffect, textColor);

                if(params.effects.hit != MAGIC_EFFECT_UNKNOWN)
                    magicEffect = params.effects.hit;

                if(params.effects.color != COLOR_UNKNOWN)
                    textColor = params.effects.color;

                if(textColor < COLOR_NONE && magicEffect < MAGIC_EFFECT_NONE)
                {
                    addMagicEffect(list, targetPos, magicEffect);
                    SpectatorVec textList;
                    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
                    {
                        if(!(*it)->getPlayer())
                            continue;

                        if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z)
                            textList.push_back(*it);
                    }

                    MessageDetails* details = new MessageDetails(damage, textColor);
                    if(elementDamage)
                    {
                        getCombatDetails(params.element.type, magicEffect, textColor);
                        details->sub = new MessageDetails(elementDamage, textColor);
                        addMagicEffect(list, targetPos, magicEffect);
                    }

                    std::stringstream ss;
                    int32_t totalDamage = damage + elementDamage;

                    std::string plural = (totalDamage != 1 ? "s" : "");
                    if(!textList.empty())
                    {
                        if(!attacker)
                            ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << ".";
                        else if(attacker != target)
                            ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << " due to an attack by " << attacker->getNameDescription() << ".";
                        else
                            ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << " due to a self attack.";

                        addStatsMessage(textList, MSG_DAMAGE_OTHERS, ss.str(), targetPos, details);
                        ss.str("");
                    }

                    Player* player = NULL;
                    if(attacker && (player = attacker->getPlayer()))
                    {
                        if(attacker != target)
                            ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << " due to your attack.";
                        else
                            ss << "You lose " << totalDamage << " hitpoint" << plural << " due to your attack.";

                        player->sendStatsMessage(MSG_DAMAGE_DEALT, ss.str(), targetPos, details);
                        ss.str("");
                    }

                    if((player = target->getPlayer()) && attacker != target)
                    {
                        if(attacker)
                            ss << "You lose " << totalDamage << " hitpoint" << plural << " due to an attack by " << attacker->getNameDescription() << ".";
                        else
                            ss << "You lose " << totalDamage << " hitpoint" << plural << ".";

                        player->sendStatsMessage(MSG_DAMAGE_RECEIVED, ss.str(), targetPos, details);
                    }

                    if(details->sub)
                        delete details->sub;

                    delete details;
                }
            }
        }
    }

    return true;
}

bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange,
    CombatType_t combatType/* = COMBAT_MANADRAIN*/, bool inherited/* = false*/)
{
    const Position& targetPos = target->getPosition();
    if(manaChange > 0)
    {
        bool deny = false;
        CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE);
        for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it)
        {
            if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_MANAGAIN, COMBAT_HEALING, manaChange))
                deny = true;
        }

        if(deny)
            return false;

        int32_t oldMana = target->getMana();
        target->changeMana(manaChange);
        if(oldMana != target->getMana() && g_config.getBool(ConfigManager::SHOW_MANA_CHANGE) && !target->isGhost() &&
            (g_config.getBool(ConfigManager::SHOW_MANA_CHANGE_MONSTER) || !target->getMonster()))
        {
            const SpectatorVec& list = getSpectators(targetPos);

            SpectatorVec textList;
            for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
            {
                if(!(*it)->getPlayer())
                    continue;

                if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z)
                    textList.push_back(*it);
            }

            manaChange = (target->getMana() - oldMana);
            std::string plural = (manaChange != 1 ? "s." : ".");

            std::stringstream ss;
            char buffer[20];
            sprintf(buffer, "+%d", manaChange);
            addAnimatedText(list, targetPos, COLOR_DARKPURPLE, buffer);
            if(!textList.empty())
            {
                if(!attacker)
                    ss << ucfirst(target->getNameDescription()) << " is regenerated with " << manaChange << " mana" << plural;
                else if(attacker != target)
                    ss << ucfirst(attacker->getNameDescription()) << " regenerates " << target->getNameDescription() << " with " << manaChange << " mana" << plural;
                else
                {
                    ss << ucfirst(attacker->getNameDescription()) << " regenerates ";
                    if(Player* attackerPlayer = attacker->getPlayer())
                        ss << (attackerPlayer->getSex(false) == PLAYERSEX_FEMALE ? "herself" : "himself") << " with " << manaChange << " mana" << plural;
                    else
                        ss << "itself with " << manaChange << " mana" << plural;
                }

                addStatsMessage(textList, MSG_HEALED_OTHERS, ss.str(), targetPos);
                ss.str("");
            }

            Player* player = NULL;
            if(attacker && (player = attacker->getPlayer()))
            {
                if(attacker != target)
                    ss << "You regenerate " << target->getNameDescription() << " with " << manaChange << " mana" << plural;
                else
                    ss << "You regenerate yourself with " << manaChange << " mana" << plural;

                player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos);
                ss.str("");
            }

            if((player = target->getPlayer()) && attacker != target)
            {
                if(attacker)
                    ss << ucfirst(attacker->getNameDescription()) << " regenerates you with " << manaChange << " mana" << plural;
                else
                    ss << "You are regenerated with " << manaChange << " mana" << plural;

                player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos);
            }
        }
    }
    else if(!inherited && Combat::canDoCombat(attacker, target, true) != RET_NOERROR)
    {
        const SpectatorVec& list = getSpectators(targetPos);
        addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF);
        return false;
    }
    else
    {
        int32_t manaLoss = std::min(target->getMana(), -manaChange);
        if(manaLoss > 0)
        {
            bool deny = false;
            CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE);
            for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it)
            {
                if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_MANALOSS, combatType, manaLoss))
                    deny = true;
            }

            if(deny)
                return false;

            target->drainMana(attacker, combatType, manaLoss);
            const SpectatorVec& list = getSpectators(targetPos);

            SpectatorVec textList;
            for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
            {
                if(!(*it)->getPlayer())
                    continue;

                if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z)
                    textList.push_back(*it);
            }

            std::stringstream ss;
            MessageDetails* details = new MessageDetails(manaLoss, COLOR_BLUE);
            if(!textList.empty())
            {
                if(!attacker)
                    ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana.";
                else if(attacker != target)
                    ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana due to an attack by " << attacker->getNameDescription();
                else
                    ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana due to a self attack.";

                addStatsMessage(textList, MSG_DAMAGE_OTHERS, ss.str(), targetPos, details);
                ss.str("");
            }

            Player* player;
            if(attacker && (player = attacker->getPlayer()))
            {
                if(attacker != target)
                    ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana due to your attack.";
                else
                    ss << "You lose " << manaLoss << " mana due to your attack.";

                player->sendStatsMessage(MSG_DAMAGE_DEALT, ss.str(), targetPos, details);
                ss.str("");
            }

            if((player = target->getPlayer()) && attacker != target)
            {
                if(attacker)
                    ss << "You lose " << manaLoss << " mana due to an attack by " << attacker->getNameDescription();
                else
                    ss << "You lose " << manaLoss << " mana.";

                player->sendStatsMessage(MSG_DAMAGE_RECEIVED, ss.str(), targetPos, details);
            }

            delete details;
        }
    }

    return true;
}

void Game::addCreatureHealth(const Creature* target)
{
    const SpectatorVec& list = getSpectators(target->getPosition());
    addCreatureHealth(list, target);
}

void Game::addCreatureHealth(const SpectatorVec& list, const Creature* target)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendCreatureHealth(target);
    }
}

void Game::addCreatureSquare(const Creature* target, uint8_t squareColor)
{
    const SpectatorVec& list = getSpectators(target->getPosition());
    addCreatureSquare(list, target, squareColor);
}

void Game::addCreatureSquare(const SpectatorVec& list, const Creature* target, uint8_t squareColor)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendCreatureSquare(target, squareColor);
    }
}

void Game::addAnimatedText(const Position& pos, uint8_t textColor, const std::string& text)
{
    const SpectatorVec& list = getSpectators(pos);
    addAnimatedText(list, pos, textColor, text);
}

void Game::addAnimatedText(const SpectatorVec& list, const Position& pos, uint8_t textColor,
    const std::string& text)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendAnimatedText(pos, textColor, text);
    }
}

void Game::addMagicEffect(const Position& pos, uint8_t effect, bool ghostMode/* = false*/)
{
    if(ghostMode)
        return;

    const SpectatorVec& list = getSpectators(pos);
    addMagicEffect(list, pos, effect);
}

void Game::addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect,
    bool ghostMode/* = false*/)
{
    if(ghostMode)
        return;

    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendMagicEffect(pos, effect);
    }
}

void Game::addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect)
{
    SpectatorVec list;
    getSpectators(list, fromPos, false);
    getSpectators(list, toPos, true);
    addDistanceEffect(list, fromPos, toPos, effect);
}

void Game::addDistanceEffect(const SpectatorVec& list, const Position& fromPos,
    const Position& toPos, uint8_t effect)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendDistanceShoot(fromPos, toPos, effect);
    }
}

void Game::addStatsMessage(const SpectatorVec& list, MessageClasses mClass, const std::string& message,
    const Position& pos, MessageDetails* details/* = NULL*/)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendStatsMessage(mClass, message, pos, details);
    }
}

void Game::startDecay(Item* item)
{
    if(!item || !item->canDecay() || item->getDecaying() == DECAYING_TRUE)
        return;

    if(item->getDuration() > 0)
    {
        item->addRef();
        item->setDecaying(DECAYING_TRUE);
        toDecayItems.push_back(item);
    }
    else
        internalDecayItem(item);
}

void Game::internalDecayItem(Item* item)
{
    const ItemType& it = Item::items.getItemType(item->getID());
    if(it.decayTo)
    {
        Item* newItem = transformItem(item, it.decayTo);
        startDecay(newItem);
    }
    else
    {
        ReturnValue ret = internalRemoveItem(NULL, item);
        if(ret != RET_NOERROR)
            std::clog << "> DEBUG: internalDecayItem failed, error code: " << (int32_t)ret << ", item id: " << item->getID() << std::endl;
    }
}

void Game::checkDecay()
{
    checkDecayEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_DECAYINTERVAL,
        boost::bind(&Game::checkDecay, this)));

    size_t bucket = (lastBucket + 1) % EVENT_DECAYBUCKETS;
    for(DecayList::iterator it = decayItems[bucket].begin(); it != decayItems[bucket].end();)
    {
        Item* item = *it;
        if(!item->canDecay())
        {
            item->setDecaying(DECAYING_FALSE);
            freeThing(item);
            it = decayItems[bucket].erase(it);
            continue;
        }

        int32_t decreaseTime = EVENT_DECAYINTERVAL * EVENT_DECAYBUCKETS;
        if((int32_t)item->getDuration() - decreaseTime < 0)
            decreaseTime = item->getDuration();

        item->decreaseDuration(decreaseTime);

        int32_t dur = item->getDuration();
        if(dur <= 0)
        {
            it = decayItems[bucket].erase(it);
            internalDecayItem(item);
            freeThing(item);
        }
        else if(dur < EVENT_DECAYINTERVAL * EVENT_DECAYBUCKETS)
        {
            it = decayItems[bucket].erase(it);
            size_t newBucket = (bucket + ((dur + EVENT_DECAYINTERVAL / 2) / 1000)) % EVENT_DECAYBUCKETS;
            if(newBucket == bucket)
            {
                internalDecayItem(item);
                freeThing(item);
            }
            else
                decayItems[newBucket].push_back(item);
        }
        else
            ++it;
    }

    lastBucket = bucket;
    cleanup();
}

void Game::checkLight()
{
    checkLightEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL,
        boost::bind(&Game::checkLight, this)));

    lightHour = lightHour + lightHourDelta;
    if(lightHour > 1440)
        lightHour -= 1440;

    if(std::abs(lightHour - SUNRISE) < 2 * lightHourDelta)
        lightState = LIGHT_STATE_SUNRISE;
    else if(std::abs(lightHour - SUNSET) < 2 * lightHourDelta)
        lightState = LIGHT_STATE_SUNSET;

    int32_t newLightLevel = lightLevel;
    bool lightChange = false;
    switch(lightState)
    {
        case LIGHT_STATE_SUNRISE:
        {
            newLightLevel += (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
            lightChange = true;
            break;
        }
        case LIGHT_STATE_SUNSET:
        {
            newLightLevel -= (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
            lightChange = true;
            break;
        }
        default:
            break;
    }

    if(newLightLevel <= LIGHT_LEVEL_NIGHT)
    {
        lightLevel = LIGHT_LEVEL_NIGHT;
        lightState = LIGHT_STATE_NIGHT;
    }
    else if(newLightLevel >= LIGHT_LEVEL_DAY)
    {
        lightLevel = LIGHT_LEVEL_DAY;
        lightState = LIGHT_STATE_DAY;
    }
    else
        lightLevel = newLightLevel;

    if(lightChange)
    {
        LightInfo lightInfo;
        getWorldLightInfo(lightInfo);
        for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
        {
            if(!it->second->hasCustomFlag(PlayerCustomFlag_HasFullLight))
                it->second->sendWorldLight(lightInfo);
        }
    }
}

void Game::checkWars()
{
    //executes every EVENT_WARSINTERVAL
    IOGuild::getInstance()->checkWars();
    if(checkEndingWars)
    {
        //executes every EVENT_WARSINTERVAL*2
        checkEndingWars = false;
        IOGuild::getInstance()->checkEndingWars();
    }
    else
        checkEndingWars = true;

    checkWarsEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_WARSINTERVAL,
        boost::bind(&Game::checkWars, this)));
}

void Game::getWorldLightInfo(LightInfo& lightInfo)
{
    lightInfo.level = lightLevel;
    lightInfo.color = 0xD7;
}

void Game::updateCreatureSkull(Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
         if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureSkull(creature);
    }
}

void Game::updateCreatureShield(Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureShield(creature);
    }
}

void Game::updateCreatureEmblem(Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureEmblem(creature);
    }
}

void Game::updateCreatureWalkthrough(Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureWalkthrough(creature, (*it)->canWalkthrough(creature));
    }
}

bool Game::playerInviteToParty(uint32_t playerId, uint32_t invitedId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 5))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionparty = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 1000, 0, false, 5))
        player->addCondition(conditionparty);

    Player* invitedPlayer = getPlayerByID(invitedId);
    if(!invitedPlayer || invitedPlayer->isRemoved() || invitedPlayer->isInviting(player))
        return false;

    if(invitedPlayer->getParty())
    {
        char buffer[90];
        sprintf(buffer, "%s is already in a party.", invitedPlayer->getName().c_str());
        player->sendTextMessage(MSG_PARTY, buffer);
        return false;
    }

    Party* party = player->getParty();
    if(!party)
        party = new Party(player);

    return party->getLeader() == player && party->invitePlayer(invitedPlayer);
}

bool Game::playerJoinParty(uint32_t playerId, uint32_t leaderId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 5))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionparty = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 1000, 0, false, 5))
        player->addCondition(conditionparty);

    Player* leader = getPlayerByID(leaderId);
    if(!leader || leader->isRemoved() || !leader->isInviting(player))
        return false;

    if(!player->getParty())
        return leader->getParty()->join(player);

    player->sendTextMessage(MSG_PARTY, "You are already in a party.");
    return false;
}

bool Game::playerRevokePartyInvitation(uint32_t playerId, uint32_t invitedId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->getParty() || player->getParty()->getLeader() != player)
        return false;

    Player* invitedPlayer = getPlayerByID(invitedId);
    if(!invitedPlayer || invitedPlayer->isRemoved() || !player->isInviting(invitedPlayer))
        return false;

    player->getParty()->revokeInvitation(invitedPlayer);
    return true;
}

bool Game::playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->getParty() || player->getParty()->getLeader() != player)
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 5))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionparty = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 1000, 0, false, 5))
        player->addCondition(conditionparty);

    Player* newLeader = getPlayerByID(newLeaderId);
    if(!newLeader || newLeader->isRemoved() || !newLeader->getParty() || newLeader->getParty() != player->getParty())
        return false;

    return player->getParty()->passLeadership(newLeader);
}

bool Game::playerLeaveParty(uint32_t playerId, bool forced/* = false*/)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->getParty() || (player->hasCondition(CONDITION_INFIGHT) && !forced))
        return false;

    return player->getParty()->leave(player);
}

bool Game::playerSharePartyExperience(uint32_t playerId, bool activate)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(!player->getParty() || (!player->hasFlag(PlayerFlag_NotGainInFight)
        && player->hasCondition(CONDITION_INFIGHT)))
        return false;

    return player->getParty()->setSharedExperience(player, activate);
}

bool Game::playerReportBug(uint32_t playerId, std::string comment)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(!player->hasFlag(PlayerFlag_CanReportBugs))
        return false;

    CreatureEventList reportBugEvents = player->getCreatureEvents(CREATURE_EVENT_REPORTBUG);
    for(CreatureEventList::iterator it = reportBugEvents.begin(); it != reportBugEvents.end(); ++it)
        (*it)->executeReportBug(player, comment);

    return true;
}

bool Game::playerReportViolation(uint32_t playerId, ReportType_t type, uint8_t reason, const std::string& name,
    const std::string& comment, const std::string& translation, uint32_t statementId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    CreatureEventList reportViolationEvents = player->getCreatureEvents(CREATURE_EVENT_REPORTVIOLATION);
    for(CreatureEventList::iterator it = reportViolationEvents.begin(); it != reportViolationEvents.end(); ++it)
        (*it)->executeReportViolation(player, type, reason, name, comment, translation, statementId);

    return true;
}

void Game::kickPlayer(uint32_t playerId, bool displayEffect)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return;

    player->kick(displayEffect, true);
}

bool Game::broadcastMessage(const std::string& text, MessageClasses type)
{
    std::clog << "> Broadcasted message: \"" << text << "\"." << std::endl;
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
        it->second->sendTextMessage(type, text);

    return true;
}

Position Game::getClosestFreeTile(Creature* creature, Position pos, bool extended/* = false*/, bool ignoreHouse/* = true*/)
{
    PairVector relList;
    relList.push_back(PositionPair(0, 0));
    relList.push_back(PositionPair(-1, -1));
    relList.push_back(PositionPair(-1, 0));
    relList.push_back(PositionPair(-1, 1));
    relList.push_back(PositionPair(0, -1));
    relList.push_back(PositionPair(0, 1));
    relList.push_back(PositionPair(1, -1));
    relList.push_back(PositionPair(1, 0));
    relList.push_back(PositionPair(1, 1));

    if(extended)
    {
        relList.push_back(PositionPair(-2, 0));
        relList.push_back(PositionPair(0, -2));
        relList.push_back(PositionPair(0, 2));
        relList.push_back(PositionPair(2, 0));
    }

    std::random_shuffle(relList.begin() + 1, relList.end());
    if(Player* player = creature->getPlayer())
    {
        for(PairVector::iterator it = relList.begin(); it != relList.end(); ++it)
        {
            Tile* tile = map->getTile(Position((pos.x + it->first), (pos.y + it->second), pos.z));
            if(!tile || !tile->ground)
                continue;

            ReturnValue ret = tile->__queryAdd(0, player, 1, FLAG_IGNOREBLOCKITEM);
            if(ret == RET_NOTENOUGHROOM || (ret == RET_NOTPOSSIBLE && !player->hasCustomFlag(
                PlayerCustomFlag_CanMoveAnywhere)) || (ret == RET_PLAYERISNOTINVITED && !ignoreHouse))
                continue;

            return tile->getPosition();
        }
    }
    else
    {
        for(PairVector::iterator it = relList.begin(); it != relList.end(); ++it)
        {
            Tile* tile = NULL;
            if((tile = map->getTile(Position((pos.x + it->first), (pos.y + it->second), pos.z)))
                && tile->__queryAdd(0, creature, 1, FLAG_IGNOREBLOCKITEM) == RET_NOERROR)
                return tile->getPosition();
        }
    }

    return Position(0, 0, 0);
}

std::string Game::getSearchString(const Position& fromPos, const Position& toPos, bool fromIsCreature/* = false*/, bool toIsCreature/* = false*/)
{
    /*
     * When the position is on same level and 0 to 4 squares away, they are "[toIsCreature: standing] next to you"
     * When the position is on same level and 5 to 100 squares away they are "to the north/west/south/east."
     * When the position is on any level and 101 to 274 squares away they are "far to the north/west/south/east."
     * When the position is on any level and 275+ squares away they are "very far to the north/west/south/east."
     * When the position is not directly north/west/south/east of you they are "((very) far) to the north-west/south-west/south-east/north-east."
     * When the position is on a lower or higher level and 5 to 100 squares away they are "on a lower (or) higher level to the north/west/south/east."
     * When the position is on a lower or higher level and 0 to 4 squares away they are "below (or) above you."
     */

    enum direction_t
    {
        DIR_N, DIR_S, DIR_E, DIR_W,
        DIR_NE, DIR_NW, DIR_SE, DIR_SW
    };

    enum distance_t
    {
        DISTANCE_BESIDE,
        DISTANCE_CLOSE,
        DISTANCE_FAR,
        DISTANCE_VERYFAR
    };

    enum level_t
    {
        LEVEL_HIGHER,
        LEVEL_LOWER,
        LEVEL_SAME
    };

    direction_t direction;
    distance_t distance;
    level_t level;

    int32_t dx = fromPos.x - toPos.x, dy = fromPos.y - toPos.y, dz = fromPos.z - toPos.z;
    if(dz > 0)
        level = LEVEL_HIGHER;
    else if(dz < 0)
        level = LEVEL_LOWER;
    else
        level = LEVEL_SAME;

    if(std::abs(dx) < 5 && std::abs(dy) < 5)
        distance = DISTANCE_BESIDE;
    else
    {
        int32_t tmp = dx * dx + dy * dy;
        if(tmp < 10000)
            distance = DISTANCE_CLOSE;
        else if(tmp < 75625)
            distance = DISTANCE_FAR;
        else
            distance = DISTANCE_VERYFAR;
    }

    float tan;
    if(dx)
        tan = (float)dy / (float)dx;
    else
        tan = 10.;

    if(std::abs(tan) < 0.4142)
    {
        if(dx > 0)
            direction = DIR_W;
        else
            direction = DIR_E;
    }
    else if(std::abs(tan) < 2.4142)
    {
        if(tan > 0)
        {
            if(dy > 0)
                direction = DIR_NW;
            else
                direction = DIR_SE;
        }
        else
        {
            if(dx > 0)
                direction = DIR_SW;
            else
                direction = DIR_NE;
        }
    }
    else
    {
        if(dy > 0)
            direction = DIR_N;
        else
            direction = DIR_S;
    }

    std::stringstream ss;
    switch(distance)
    {
        case DISTANCE_BESIDE:
        {
            switch(level)
            {
                case LEVEL_SAME:
                {
                    ss << "is ";
                    if(toIsCreature)
                        ss << "standing ";

                    ss << "next to you";
                    break;
                }

                case LEVEL_HIGHER:
                {
                    ss << "is above ";
                    if(fromIsCreature)
                        ss << "you";

                    break;
                }

                case LEVEL_LOWER:
                {
                    ss << "is below ";
                    if(fromIsCreature)
                        ss << "you";

                    break;
                }

                default:
                    break;
            }

            break;
        }

        case DISTANCE_CLOSE:
        {
            switch(level)
            {
                case LEVEL_SAME:
                    ss << "is to the";
                    break;
                case LEVEL_HIGHER:
                    ss << "is on a higher level to the";
                    break;
                case LEVEL_LOWER:
                    ss << "is on a lower level to the";
                    break;
                default:
                    break;
            }

            break;
        }

        case DISTANCE_FAR:
            ss << "is far to the";
            break;

        case DISTANCE_VERYFAR:
            ss << "is very far to the";
            break;

        default:
            break;
    }

    if(distance != DISTANCE_BESIDE)
    {
        ss << " ";
        switch(direction)
        {
            case DIR_N:
                ss << "north";
                break;

            case DIR_S:
                ss << "south";
                break;

            case DIR_E:
                ss << "east";
                break;

            case DIR_W:
                ss << "west";
                break;

            case DIR_NE:
                ss << "north-east";
                break;

            case DIR_NW:
                ss << "north-west";
                break;

            case DIR_SE:
                ss << "south-east";
                break;

            case DIR_SW:
                ss << "south-west";
                break;

            default:
                break;
        }
    }

    return ss.str();
}

double Game::getExperienceStage(uint32_t level, double divider/* = 1.*/)
{
    if(!g_config.getBool(ConfigManager::EXPERIENCE_STAGES))
        return g_config.getDouble(ConfigManager::RATE_EXPERIENCE) * divider;

    if(lastStageLevel && level >= lastStageLevel)
        return stages[lastStageLevel] * divider;

    return stages[level] * divider;
}

bool Game::loadExperienceStages()
{
    if(!g_config.getBool(ConfigManager::EXPERIENCE_STAGES))
    {
        if(!stages.empty())
            stages.clear();

        return true;
    }

    xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "stages.xml").c_str());
    if(!doc)
    {
        std::clog << "[Warning - Game::loadExperienceStages] Cannot load stages file."
            << std::endl << getLastXMLError() << std::endl;
        return false;
    }

    xmlNodePtr q, p, root = xmlDocGetRootElement(doc);
    if(xmlStrcmp(root->name, (const xmlChar*)"stages"))
    {
        std::clog << "[Error - Game::loadExperienceStages] Malformed stages file" << std::endl;
        xmlFreeDoc(doc);
        return false;
    }

    int32_t intValue, low = 0, high = 0;
    float floatValue, mul = 1.0f, defStageMultiplier;
    std::string strValue;

    lastStageLevel = 0;
    stages.clear();

    q = root->children;
    while(q)
    {
        if(!xmlStrcmp(q->name, (const xmlChar*)"world"))
        {
            if(readXMLString(q, "id", strValue))
            {
                IntegerVec intVector;
                if(!parseIntegerVec(strValue, intVector) || std::find(intVector.begin(),
                    intVector.end(), g_config.getNumber(ConfigManager::WORLD_ID)) == intVector.end())
                {
                    q = q->next;
                    continue;
                }
            }

            defStageMultiplier = 1.0f;
            if(readXMLFloat(q, "multiplier", floatValue))
                defStageMultiplier = floatValue;

            p = q->children;
            while(p)
            {
                if(!xmlStrcmp(p->name, (const xmlChar*)"stage"))
                {
                    low = 1;
                    if(readXMLInteger(p, "minlevel", intValue) || readXMLInteger(p, "minLevel", intValue))
                        low = intValue;

                    high = 0;
                    if(readXMLInteger(p, "maxlevel", intValue) || readXMLInteger(p, "maxLevel", intValue))
                        high = intValue;
                    else
                        lastStageLevel = low;

                    mul = 1.0f;
                    if(readXMLFloat(p, "multiplier", floatValue))
                        mul = floatValue;

                    mul *= defStageMultiplier;
                    if(lastStageLevel && lastStageLevel == (uint32_t)low)
                        stages[lastStageLevel] = mul;
                    else
                    {
                        for(int32_t i = low; i <= high; ++i)
                            stages = mul;
                    }
                }

                p = p->next;
            }
        }

        if(!xmlStrcmp(q->name, (const xmlChar*)"stage"))
        {
            low = 1;
            if(readXMLInteger(q, "minlevel", intValue))
                low = intValue;
            else

            high = 0;
            if(readXMLInteger(q, "maxlevel", intValue))
                high = intValue;
            else
                lastStageLevel = low;

            mul = 1.0f;
            if(readXMLFloat(q, "multiplier", floatValue))
                mul = floatValue;

            if(lastStageLevel && lastStageLevel == (uint32_t)low)
                stages[lastStageLevel] = mul;
            else
            {
                for(int32_t i = low; i <= high; ++i)
                    stages = mul;
            }
        }

        q = q->next;
    }

    xmlFreeDoc(doc);
    return true;
}

int32_t Game::getMotdId()
{
    if(lastMotd.length() == g_config.getString(ConfigManager::MOTD).length())
    {
        if(lastMotd == g_config.getString(ConfigManager::MOTD))
            return lastMotdId;
    }

    lastMotd = g_config.getString(ConfigManager::MOTD);
    Database* db = Database::getInstance();

    DBQuery query;
    query << "INSERT INTO `server_motd` (`id`, `world_id`, `text`) VALUES (" << lastMotdId + 1 << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << db->escapeString(lastMotd) << ")";
    if(db->query(query.str()))
        ++lastMotdId;

    return lastMotdId;
}

void Game::loadMotd()
{
    Database* db = Database::getInstance();
    DBQuery query;
    query << "SELECT `id`, `text` FROM `server_motd` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " ORDER BY `id` DESC LIMIT 1";

    DBResult* result;
    if(!(result = db->storeQuery(query.str())))
    {
        std::clog << "> ERROR: Failed to load motd!" << std::endl;
        lastMotdId = random_range(5, 500);
        return;
    }

    lastMotdId = result->getDataInt("id");
    lastMotd = result->getDataString("text");
    result->free();
}

void Game::checkPlayersRecord(Player* player)
{
    uint32_t count = getPlayersOnline();
    if(count <= playersRecord)
        return;

    GlobalEventMap recordEvents = g_globalEvents->getEventMap(GLOBALEVENT_RECORD);
    for(GlobalEventMap::iterator it = recordEvents.begin(); it != recordEvents.end(); ++it)
        it->second->executeRecord(count, playersRecord, player);

    playersRecord = count;
}

void Game::loadPlayersRecord()
{
    Database* db = Database::getInstance();
    DBQuery query;
    query << "SELECT `record` FROM `server_record` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " ORDER BY `timestamp` DESC LIMIT 1";

    DBResult* result;
    if(!(result = db->storeQuery(query.str())))
    {
        std::clog << "> ERROR: Failed to load players record!" << std::endl;
        return;
    }

    playersRecord = result->getDataInt("record");
    result->free();
}

bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/, bool completeReload/* = false*/)
{
    bool done = false;
    switch(reload)
    {
        case RELOAD_ACTIONS:
        {
            if(g_actions->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload actions." << std::endl;

            break;
        }

        case RELOAD_CHAT:
        {
            if(g_chat.reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload chat." << std::endl;

            break;
        }

        case RELOAD_CONFIG:
        {
            if(g_config.reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload config." << std::endl;

            break;
        }

        case RELOAD_CREATUREEVENTS:
        {
            if(g_creatureEvents->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload creature events." << std::endl;

            break;
        }

        case RELOAD_GAMESERVERS:
        {
            #ifdef __LOGIN_SERVER__
            if(GameServers::getInstance()->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload game servers." << std::endl;

            #endif
            break;
        }

        case RELOAD_GLOBALEVENTS:
        {
            if(g_globalEvents->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload global events." << std::endl;

            break;
        }

        case RELOAD_GROUPS:
        {
            break;
        }

        case RELOAD_ITEMS:
        {
            std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl;
            done = true;
            break;
        }

        case RELOAD_MODS:
        {
            if(ScriptManager::getInstance()->reloadMods())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload mods." << std::endl;

            break;
        }

        case RELOAD_MONSTERS:
        {
            if(g_monsters.reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload monsters." << std::endl;

            break;
        }

        case RELOAD_MOVEEVENTS:
        {
            if(g_moveEvents->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload move events." << std::endl;

            break;
        }

        case RELOAD_NPCS:
        {
            g_npcs.reload();
            done = true;
            break;
        }

        case RELOAD_OUTFITS:
        {
            std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl;
            done = true;
            break;
        }

        case RELOAD_QUESTS:
        {
            if(Quests::getInstance()->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload quests." << std::endl;

            break;
        }

        case RELOAD_RAIDS:
        {
            if(!Raids::getInstance()->reload())
                std::clog << "[Error - Game::reloadInfo] Failed to reload raids." << std::endl;
            else if(!Raids::getInstance()->startup())
                std::clog << "[Error - Game::reloadInfo] Failed to startup raids when reloading." << std::endl;
            else
                done = true;

            break;
        }

        case RELOAD_SPELLS:
        {
            if(!g_spells->reload())
                std::clog << "[Error - Game::reloadInfo] Failed to reload spells." << std::endl;
            else if(!g_monsters.reload())
                std::clog << "[Error - Game::reloadInfo] Failed to reload monsters when reloading spells." << std::endl;
            else
                done = true;

            break;
        }

        case RELOAD_STAGES:
        {
            if(loadExperienceStages())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload stages." << std::endl;

            break;
        }

        case RELOAD_TALKACTIONS:
        {
            if(g_talkActions->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload talk actions." << std::endl;

            break;
        }

        case RELOAD_VOCATIONS:
        {
            break;
        }

        case RELOAD_WEAPONS:
        {
            std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl;
            done = true;
            break;
        }

        case RELOAD_ALL:
        {
            done = true;
            for(int32_t i = RELOAD_FIRST; i <= RELOAD_LAST; ++i)
            {
                if(!reloadInfo((ReloadInfo_t)i, 0, true) && done)
                    done = false;
            }

            if(!ScriptManager::getInstance()->reloadMods() && done)
                done = false;

            break;
        }

        default:
        {
            std::clog << "[Warning - Game::reloadInfo] Reload type not found." << std::endl;
            break;
        }
    }

    if(reload != RELOAD_CONFIG && reload != RELOAD_MODS && !completeReload && !ScriptManager::getInstance()->reloadMods())
        std::clog << "[Error - Game::reloadInfo] Failed to reload mods." << std::endl;

    if(!playerId)
        return done;

    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return done;

    if(done)
    {
        player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Reloaded successfully.");
        return true;
    }

    if(reload == RELOAD_ALL)
        player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Failed to reload some parts.");
    else
        player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Failed to reload.");

    return false;
}

void Game::prepareGlobalSave(uint8_t minutes)
{
    std::clog << "Game::prepareGlobalSave in " << (uint32_t)minutes << " minutes" << std::endl;
    switch(minutes)
    {
        case 5:
            setGameState(GAMESTATE_CLOSING);
            broadcastMessage("Server is going down for a global save within 5 minutes. Please logout.", MSG_STATUS_WARNING);
            Scheduler::getInstance().addEvent(createSchedulerTask(2 * 60000, boost::bind(&Game::prepareGlobalSave, this, 3)));
            break;

        case 3:
            broadcastMessage("Server is going down for a global save within 3 minutes. Please logout.", MSG_STATUS_WARNING);
            Scheduler::getInstance().addEvent(createSchedulerTask(2 * 60000, boost::bind(&Game::prepareGlobalSave, this, 1)));
            break;

        case 1:
            broadcastMessage("Server is going down for a global save in one minute, please logout!", MSG_STATUS_WARNING);
            Scheduler::getInstance().addEvent(createSchedulerTask(60000, boost::bind(&Game::prepareGlobalSave, this, 0)));
            break;

        case 0:
            globalSave();
            break;

        default:
            if(minutes > 5)
                Scheduler::getInstance().addEvent(createSchedulerTask((minutes - 5) * 1000, boost::bind(&Game::prepareGlobalSave, this, 5)));
            break;
    }
}

void Game::globalSave()
{
    bool close = g_config.getBool(ConfigManager::SHUTDOWN_AT_GLOBALSAVE);
    if(!close) // check are we're going to close the server
        Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAMESTATE_CLOSED)));

    // call the global event
    g_globalEvents->execute(GLOBALEVENT_GLOBALSAVE);
    if(close)
    {
        //shutdown server
        Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAMESTATE_SHUTDOWN)));
        return;
    }

    //pay houses
    Houses::getInstance()->check();
    //clean map if configured to
    if(g_config.getBool(ConfigManager::CLEAN_MAP_AT_GLOBALSAVE))
        cleanMap();

    //remove premium days globally if configured to
    if(g_config.getBool(ConfigManager::INIT_PREMIUM_UPDATE))
        IOLoginData::getInstance()->updatePremiumDays();

    //reload everything
    reloadInfo(RELOAD_ALL);
    //prepare for next global save after 24 hours
    Scheduler::getInstance().addEvent(createSchedulerTask(((24 * 60 * 60) - (5 * 60)) * 1000, boost::bind(&Game::prepareGlobalSave, this, 5)));
    //open server
    Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAMESTATE_NORMAL)));
}

void Game::shutdown()
{
    std::clog << "Preparing";
    Scheduler::getInstance().shutdown();
    std::clog << " to";
    Dispatcher::getInstance().shutdown();
    std::clog << " shutdown";
    Spawns::getInstance()->clear();
    std::clog << " the";
    Raids::getInstance()->clear();
    std::clog << " server... ";
    cleanup();
    std::clog << "(done)." << std::endl;
    if(services)
        services->stop();

#if defined(WINDOWS) && !defined(_CONSOLE)
    exit(1);
#endif
}

void Game::cleanup()
{
    //free memory
    for(std::vector<Thing*>::iterator it = releaseThings.begin(); it != releaseThings.end(); ++it)
        (*it)->unRef();

    releaseThings.clear();
    for(DecayList::iterator it = toDecayItems.begin(); it != toDecayItems.end(); ++it)
    {
        int32_t dur = (*it)->getDuration();
        if(dur >= EVENT_DECAYINTERVAL * EVENT_DECAYBUCKETS)
            decayItems[lastBucket].push_back(*it);
        else
            decayItems[(lastBucket + 1 + (*it)->getDuration() / 1000) % EVENT_DECAYBUCKETS].push_back(*it);
    }

    toDecayItems.clear();
}

void Game::freeThing(Thing* thing)
{
    releaseThings.push_back(thing);
}

void Game::showHotkeyUseMessage(Player* player, Item* item)
{
    const ItemType& it = Item::items[item->getID()];
    uint32_t count = player->__getItemTypeCount(item->getID(), item->isFluidContainer() ? item->getFluidType() : -1);

    std::stringstream stream;
    if(!it.showCount)
        stream << "Using one of " << it.name << "...";
    else if(count == 1)
        stream << "Using the last " << it.name.c_str() << "...";
    else
        stream << "Using one of " << count << " " << it.pluralName.c_str() << "...";

    player->sendTextMessage(MSG_HOTKEY_USE, stream.str().c_str());
}

void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return;

    CreatureEventList extendedOpcodeEvents = player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE);
    for(CreatureEventList::iterator it = extendedOpcodeEvents.begin(); it != extendedOpcodeEvents.end(); ++it)
        (*it)->executeExtendedOpcode(player, opcode, buffer);
}
 

 

Link para o post
Compartilhar em outros sites
46 minutos atrás, Toulouse disse:

@Way20 

 

  Mostrar conteúdo oculto

////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// 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 3 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, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include "game.h"

#include "configmanager.h"
#ifdef __LOGIN_SERVER__
#include "gameservers.h"
#endif
#include "server.h"
#include "chat.h"

#include "luascript.h"
#include "creature.h"
#include "combat.h"
#include "tile.h"

#include "database.h"
#include "iologindata.h"
#include "ioban.h"
#include "ioguild.h"

#include "items.h"
#include "trashholder.h"
#include "container.h"
#include "monsters.h"

#include "house.h"
#include "quests.h"

#include "actions.h"
#include "globalevent.h"
#include "movement.h"
#include "raids.h"
#include "scriptmanager.h"
#include "spells.h"
#include "talkaction.h"
#include "weapons.h"

#include "textlogger.h"
#include "vocation.h"
#include "group.h"

#ifdef __EXCEPTION_TRACER__
#include "exception.h"
#endif

extern ConfigManager g_config;
extern Actions* g_actions;
extern Monsters g_monsters;
extern Npcs g_npcs;
extern Chat g_chat;
extern TalkActions* g_talkActions;
extern Spells* g_spells;
extern MoveEvents* g_moveEvents;
extern Weapons* g_weapons;
extern CreatureEvents* g_creatureEvents;
extern GlobalEvents* g_globalEvents;

Game::Game()
{
    gameState = GAMESTATE_NORMAL;
    worldType = WORLDTYPE_OPEN;
    map = NULL;
    playersRecord = lastStageLevel = 0;

    //(1440 minutes/day) * 10 seconds event interval / (3600 seconds/day)
    lightHourDelta = 1440 * 10 / 3600;
    lightHour = SUNRISE + (SUNSET - SUNRISE) / 2;
    lightLevel = LIGHT_LEVEL_DAY;
    lightState = LIGHT_STATE_DAY;

    lastBucket = checkCreatureLastIndex = checkLightEvent = checkCreatureEvent = checkDecayEvent = saveEvent = 0;
    checkWarsEvent = 0;
    checkEndingWars = false;
}

Game::~Game()
{
    delete map;
}

void Game::start(ServiceManager* servicer)
{
    checkDecayEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_DECAYINTERVAL,
        boost::bind(&Game::checkDecay, this)));
    checkCreatureEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_CREATURE_THINK_INTERVAL,
        boost::bind(&Game::checkCreatures, this)));
    checkLightEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL,
        boost::bind(&Game::checkLight, this)));
    checkWarsEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_WARSINTERVAL,
        boost::bind(&Game::checkWars, this)));

    services = servicer;
    if(!g_config.getBool(ConfigManager::GLOBALSAVE_ENABLED))
        return;

    int32_t prepareHour = g_config.getNumber(ConfigManager::GLOBALSAVE_H),
        prepareMinute = g_config.getNumber(ConfigManager::GLOBALSAVE_M);

    if(prepareHour < 0 || prepareHour > 24)
    {
        std::clog << "> WARNING: No valid hour (" << prepareHour << ") for a global save, should be between 0-23. Global save disabled." << std::endl;
        return;
    }

    if(prepareMinute < 0 || prepareMinute > 59)
    {
        std::clog << "> WARNING: No valid minute (" << prepareMinute << ") for a global save, should be between 0-59. Global save disabled." << std::endl;
        return;
    }

    time_t timeNow = time(NULL);
    const tm* theTime = localtime(&timeNow);

    int32_t hour = theTime->tm_hour, minute = theTime->tm_min, second = theTime->tm_sec,
        hoursLeft = 0, minutesLeft = 0, broadcast = 5;

    if(!prepareHour)
        prepareHour = 24;

    if(hour != prepareHour)
    {
        if(prepareMinute >= 5)
            prepareMinute -= 5;
        else
        {
            prepareHour--;
            prepareMinute = 55 + prepareMinute;
        }

        if(hour > prepareHour)
            hoursLeft = 24 - (hour - prepareHour);
        else
            hoursLeft = prepareHour - hour;

        if(minute > prepareMinute)
        {
            minutesLeft = 60 - (minute - prepareMinute);
            hoursLeft--;
        }
        else if(minute != prepareMinute)
            minutesLeft = prepareMinute - minute;
    }
    else
    {
        if(minute > prepareMinute)
        {
            minutesLeft = 55 - (minute - prepareMinute);
            hoursLeft = 23;
        }
        else
        {
            minutesLeft = prepareMinute - minute;
            if(minutesLeft >= 5)
                minutesLeft = minutesLeft - 5;
            else if(minutesLeft == 3 || minutesLeft == 1)
            {
                prepareGlobalSave(minutesLeft);
                return;
            }
            else if(minutesLeft > 0)
            {
                broadcast = (minutesLeft == 2 ? 1 : 3);
                minutesLeft = 1;
            }
        }
    }

    uint32_t timeLeft = (hoursLeft * 60 * 60 * 1000) + (minutesLeft * 60 * 1000);
    if(timeLeft > 0)
    {
        timeLeft -= second * 1000;
        saveEvent = Scheduler::getInstance().addEvent(createSchedulerTask(timeLeft,
            boost::bind(&Game::prepareGlobalSave, this, broadcast)));
    }
}

void Game::loadGameState()
{
    ScriptEnviroment::loadGameState();
    loadPlayersRecord();
    loadMotd();
}

void Game::setGameState(GameState_t newState)
{
    if(gameState == GAMESTATE_SHUTDOWN)
        return; //this cannot be stopped

    if(gameState != newState)
    {
        gameState = newState;
        switch(newState)
        {
            case GAMESTATE_INIT:
            {
                Spawns::getInstance()->startup();
                Raids::getInstance()->startup();

                loadGameState();

                g_globalEvents->startup();
                if(g_config.getBool(ConfigManager::INIT_PREMIUM_UPDATE))
                    IOLoginData::getInstance()->updatePremiumDays();

                IOGuild::getInstance()->checkWars();
                IOGuild::getInstance()->checkEndingWars();
                break;
            }

            case GAMESTATE_SHUTDOWN:
            {
                g_globalEvents->execute(GLOBALEVENT_SHUTDOWN);
                AutoList<Player>::iterator it = Player::autoList.begin();
                while(it != Player::autoList.end()) //kick all players that are still online
                {
                    it->second->kick(true, true);
                    it = Player::autoList.begin();
                }

                Houses::getInstance()->check();
                saveGameState((uint8_t)SAVE_PLAYERS | (uint8_t)SAVE_MAP | (uint8_t)SAVE_STATE);

                if(g_config.getBool(ConfigManager::CLOSE_INSTANCE_ON_SHUTDOWN))
                    Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::shutdown, this)));

                Scheduler::getInstance().stop();
                Dispatcher::getInstance().stop();
                break;
            }

            case GAMESTATE_CLOSED:
            {
                AutoList<Player>::iterator it = Player::autoList.begin();
                while(it != Player::autoList.end()) //kick all players who not allowed to stay
                {
                    if(!it->second->hasFlag(PlayerFlag_CanAlwaysLogin))
                    {
                        it->second->kick(true, true);
                        it = Player::autoList.begin();
                    }
                    else
                        ++it;
                }

                map->updateAuctions();
                saveGameState((uint8_t)SAVE_PLAYERS | (uint8_t)SAVE_MAP | (uint8_t)SAVE_STATE);
                break;
            }

            case GAMESTATE_NORMAL:
            case GAMESTATE_MAINTAIN:
            case GAMESTATE_STARTUP:
            case GAMESTATE_CLOSING:
            default:
                break;
        }
    }
}

void Game::saveGameState(uint8_t flags)
{
    std::clog << "> Saving server..." << std::endl;
    uint64_t start = OTSYS_TIME();
    if(gameState == GAMESTATE_NORMAL)
        setGameState(GAMESTATE_MAINTAIN);

    if(hasBitSet(SAVE_PLAYERS, flags))
    {
        IOLoginData* io = IOLoginData::getInstance();
        for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
        {
            it->second->loginPosition = it->second->getPosition();
            io->savePlayer(it->second, false, hasBitSet(SAVE_PLAYERS_SHALLOW, flags));
        }
    }

    if(hasBitSet(SAVE_MAP, flags))
        map->saveMap();

    if(hasBitSet(SAVE_STATE, flags))
        ScriptEnviroment::saveGameState();

    if(gameState == GAMESTATE_MAINTAIN)
        setGameState(GAMESTATE_NORMAL);

    std::clog << "> SAVE: Complete in " << (OTSYS_TIME() - start) / (1000.) << " seconds using "
        << asLowerCaseString(g_config.getString(ConfigManager::HOUSE_STORAGE))
        << " house storage." << std::endl;
}

int32_t Game::loadMap(std::string filename)
{
    if(!map)
        map = new Map;

    std::string file = getFilePath(FILE_TYPE_CONFIG, "world/" + filename);
    if(!fileExists(file.c_str()))
        file = getFilePath(FILE_TYPE_OTHER, "world/" + filename);

    return map->loadMap(file);
}

void Game::cleanMapEx(uint32_t& count)
{
    uint64_t start = OTSYS_TIME();
    uint32_t tiles = 0; count = 0;

    int32_t marked = -1;
    if(gameState == GAMESTATE_NORMAL)
        setGameState(GAMESTATE_MAINTAIN);

    Tile* tile = NULL;
    ItemVector::iterator tit;
    if(g_config.getBool(ConfigManager::STORE_TRASH))
    {
        marked = trash.size();
        Trash::iterator it = trash.begin();
        if(g_config.getBool(ConfigManager::CLEAN_PROTECTED_ZONES))
        {
            for(; it != trash.end(); ++it)
            {
                if(!(tile = getTile(*it)))
                    continue;

                tile->resetFlag(TILESTATE_TRASHED);
                if(tile->hasFlag(TILESTATE_HOUSE) || !tile->getItemList())
                    continue;

                ++tiles;
                tit = tile->getItemList()->begin();
                while(tile->getItemList() && tit != tile->getItemList()->end())
                {
                    if((*tit)->isMovable() && !(*tit)->isLoadedFromMap()
                        && !(*tit)->isScriptProtected())
                    {
                        internalRemoveItem(NULL, *tit);
                        if(tile->getItemList())
                            tit = tile->getItemList()->begin();

                        ++count;
                    }
                    else
                        ++tit;
                }
            }
        }
        else
        {
            for(; it != trash.end(); ++it)
            {
                if(!(tile = getTile(*it)))
                    continue;

                tile->resetFlag(TILESTATE_TRASHED);
                if(tile->hasFlag(TILESTATE_PROTECTIONZONE) || !tile->getItemList())
                    continue;

                ++tiles;
                tit = tile->getItemList()->begin();
                while(tile->getItemList() && tit != tile->getItemList()->end())
                {
                    if((*tit)->isMovable() && !(*tit)->isLoadedFromMap()
                        && !(*tit)->isScriptProtected())
                    {
                        internalRemoveItem(NULL, *tit);
                        if(tile->getItemList())
                            tit = tile->getItemList()->begin();

                        ++count;
                    }
                    else
                        ++tit;
                }
            }
        }

        trash.clear();
    }
    else if(g_config.getBool(ConfigManager::CLEAN_PROTECTED_ZONES))
    {
        for(uint16_t z = 0; z < (uint16_t)MAP_MAX_LAYERS; z++)
        {
            for(uint16_t y = 1; y <= map->mapHeight; y++)
            {
                for(uint16_t x = 1; x <= map->mapWidth; x++)
                {
                    if(!(tile = getTile(x, y, z)) || tile->hasFlag(TILESTATE_HOUSE) || !tile->getItemList())
                        continue;

                    ++tiles;
                    tit = tile->getItemList()->begin();
                    while(tile->getItemList() && tit != tile->getItemList()->end())
                    {
                        if((*tit)->isMovable() && !(*tit)->isLoadedFromMap()
                            && !(*tit)->isScriptProtected())
                        {
                            internalRemoveItem(NULL, *tit);
                            if(tile->getItemList())
                                tit = tile->getItemList()->begin();

                            ++count;
                        }
                        else
                            ++tit;
                    }
                }
            }
        }
    }
    else
    {
        for(uint16_t z = 0; z < (uint16_t)MAP_MAX_LAYERS; z++)
        {
            for(uint16_t y = 1; y <= map->mapHeight; y++)
            {
                for(uint16_t x = 1; x <= map->mapWidth; x++)
                {
                    if(!(tile = getTile(x, y, z)) || tile->hasFlag(TILESTATE_PROTECTIONZONE) || !tile->getItemList())
                        continue;

                    ++tiles;
                    tit = tile->getItemList()->begin();
                    while(tile->getItemList() && tit != tile->getItemList()->end())
                    {
                        if((*tit)->isMovable() && !(*tit)->isLoadedFromMap()
                            && !(*tit)->isScriptProtected())
                        {
                            internalRemoveItem(NULL, *tit);
                            if(tile->getItemList())
                                tit = tile->getItemList()->begin();

                            ++count;
                        }
                        else
                            ++tit;
                    }
                }
            }
        }
    }

    if(gameState == GAMESTATE_MAINTAIN)
        setGameState(GAMESTATE_NORMAL);

    std::clog << "> CLEAN: Removed " << count << " item" << (count != 1 ? "s" : "")
        << " from " << tiles << " tile" << (tiles != 1 ? "s" : "");
    if(marked >= 0)
        std::clog << " (" << marked << " were marked)";

    std::clog << " in " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl;
}

void Game::cleanMap()
{
    uint32_t dummy;
    cleanMapEx(dummy);
}

void Game::proceduralRefresh(RefreshTiles::iterator* it/* = NULL*/)
{
    if(!it)
        it = new RefreshTiles::iterator(refreshTiles.begin());

    // Refresh 250 tiles each cycle
    refreshMap(it, 250);
    if((*it) != refreshTiles.end())
    {
        delete it;
        return;
    }

    // Refresh some items every 100 ms until all tiles has been checked
    // For 100k tiles, this would take 100000/2500 = 40s = half a minute
    Scheduler::getInstance().addEvent(createSchedulerTask(SCHEDULER_MINTICKS,
        boost::bind(&Game::proceduralRefresh, this, it)));
}

void Game::refreshMap(RefreshTiles::iterator* it/* = NULL*/, uint32_t limit/* = 0*/)
{
    RefreshTiles::iterator end = refreshTiles.end();
    if(!it)
    {
        RefreshTiles::iterator begin = refreshTiles.begin();
        it = &begin;
    }

    Tile* tile = NULL;
    TileItemVector* items = NULL;

    Item* item = NULL;
    for(uint32_t cleaned = 0, downItemsSize = 0; (*it) != end &&
        (limit ? (cleaned < limit) : true); ++(*it), ++cleaned)
    {
        if(!(tile = (*it)->first))
            continue;

        if((items = tile->getItemList()))
        {
            downItemsSize = tile->getDownItemCount();
            for(uint32_t i = downItemsSize - 1; i; --i)
            {
                if((item = items->at(i)))
                {
                    #ifndef __DEBUG__
                    internalRemoveItem(NULL, item);
                    #else
                    if(internalRemoveItem(NULL, item) != RET_NOERROR)
                    {
                        std::clog << "> WARNING: Could not refresh item: " << item->getID();
                        std::clog << " at position: " << tile->getPosition() << std::endl;
                    }
                    #endif
                }
            }
        }

        cleanup();
        TileItemVector list = (*it)->second.list;
        for(ItemVector::reverse_iterator it = list.rbegin(); it != list.rend(); ++it)
        {
            Item* item = (*it)->clone();
            if(internalAddItem(NULL, tile, item, INDEX_WHEREEVER, FLAG_NOLIMIT) == RET_NOERROR)
            {
                if(item->getUniqueId())
                    ScriptEnviroment::addUniqueThing(item);

                startDecay(item);
            }
            else
            {
                std::clog << "> WARNING: Could not refresh item: " << item->getID()
                    << " at position: " << tile->getPosition() << std::endl;
                delete item;
            }
        }
    }
}

bool Game::isSwimmingPool(Item* item, const Tile* tile, bool checkProtection) const
{
    if(!tile)
        return false;

    TrashHolder* trashHolder = NULL;
    if(!item)
        trashHolder = tile->getTrashHolder();
    else
        trashHolder = item->getTrashHolder();

    return trashHolder && trashHolder->getEffect() == MAGIC_EFFECT_LOSE_ENERGY && (!checkProtection
        || tile->getZone() == ZONE_PROTECTION || tile->getZone() == ZONE_OPTIONAL);
}

Cylinder* Game::internalGetCylinder(Player* player, const Position& pos)
{
    if(pos.x != 0xFFFF)
        return getTile(pos);

    //container
    if(pos.y & 0x40)
        return player->getContainer((uint8_t)(pos.y & 0x0F));

    return player;
}

Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index,
    uint32_t spriteId/* = 0*/, stackposType_t type/* = STACKPOS_NORMAL*/)
{
    if(pos.x != 0xFFFF)
    {
        Tile* tile = getTile(pos);
        if(!tile)
            return NULL;

        if(type == STACKPOS_LOOK)
            return tile->getTopVisibleThing(player);

        Thing* thing = NULL;
        switch(type)
        {
            case STACKPOS_MOVE:
            {
                Item* item = tile->getTopDownItem();
                if(item && item->isMovable())
                    thing = item;
                else
                    thing = tile->getTopVisibleCreature(player);

                break;
            }

            case STACKPOS_USE:
            {
                thing = tile->getTopDownItem();
                break;
            }

            case STACKPOS_USEITEM:
            {
                thing = tile->getTopDownItem();
                Item* item = tile->getItemByTopOrder(2);
                if(item && g_actions->hasAction(item))
                {
                    const ItemType& it = Item::items[item->getID()];
                    if(!thing || (!it.hasHeight && !it.allowPickupable))
                        thing = item;
                }

                if(!thing)
                    thing = tile->getTopTopItem();

                if(!thing)
                    thing = tile->ground;

                break;
            }

            default:
                thing = tile->__getThing(index);
                break;
        }

        if(player && thing && thing->getItem())
        {
            if(tile->hasProperty(ISVERTICAL))
            {
                if(player->getPosition().x + 1 == tile->getPosition().x)
                    thing = NULL;
            }
            else if(tile->hasProperty(ISHORIZONTAL) && player->getPosition().y + 1 == tile->getPosition().y)
                thing = NULL;
        }

        return thing;
    }
    else if(pos.y & 0x40)
    {
        uint8_t fromCid = pos.y & 0x0F, slot = pos.z;
        if(Container* parentcontainer = player->getContainer(fromCid))
            return parentcontainer->getItem(slot);
    }
    else if(!pos.y && !pos.z)
    {
        const ItemType& it = Item::items.getItemIdByClientId(spriteId);
        if(!it.id)
            return NULL;

        int32_t subType = -1;
        if(it.isFluidContainer() && index < int32_t(sizeof(reverseFluidMap) / sizeof(int8_t)))
            subType = reverseFluidMap[index];

        return findItemOfType(player, it.id, true, subType);
    }

    return player->getInventoryItem((slots_t)static_cast<uint8_t>(pos.y));
}

void Game::internalGetPosition(Item* item, Position& pos, int16_t& stackpos)
{
    if(!item)
        return;

    pos.x = pos.y = pos.z = stackpos = 0;
    if(Cylinder* topParent = item->getTopParent())
    {
        if(Player* player = dynamic_cast<Player*>(topParent))
        {
            pos.x = 0xFFFF;

            Container* container = dynamic_cast<Container*>(item->getParent());
            if(container)
            {
                pos.y = ((uint16_t) ((uint16_t)0x40) | ((uint16_t)player->getContainerID(container)) );
                pos.z = container->__getIndexOfThing(item);
                stackpos = pos.z;
            }
            else
            {
                pos.y = player->__getIndexOfThing(item);
                stackpos = pos.y;
            }
        }
        else if(Tile* tile = topParent->getTile())
        {
            pos = tile->getPosition();
            stackpos = tile->__getIndexOfThing(item);
        }
    }
}

Creature* Game::getCreatureByID(uint32_t id)
{
    if(!id)
        return NULL;

    AutoList<Creature>::iterator it = autoList.find(id);
    if(it != autoList.end() && !it->second->isRemoved())
        return it->second;

    return NULL; //just in case the player doesnt exist
}

Player* Game::getPlayerByID(uint32_t id)
{
    if(!id)
        return NULL;

    AutoList<Player>::iterator it = Player::autoList.find(id);
    if(it != Player::autoList.end() && !it->second->isRemoved())
        return it->second;

    return NULL; //just in case the player doesnt exist
}

Player* Game::getPlayerByGUID(const uint32_t& guid)
{
    if(!guid)
        return NULL;

    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        Player* player = (*it).second;
        if(!player->isRemoved())
        {
            if(guid == player->getGUID())
                return player;
        }
    }
    return NULL;
}

Creature* Game::getCreatureByName(std::string s)
{
    if(s.empty())
        return NULL;

    toLowerCaseString(s);
    for(AutoList<Creature>::iterator it = autoList.begin(); it != autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && asLowerCaseString(it->second->getName()) == s)
            return it->second;
    }

    return NULL; //just in case the creature doesnt exist
}

Player* Game::getPlayerByName(std::string s)
{
    if(s.empty())
        return NULL;

    toLowerCaseString(s);
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && asLowerCaseString(it->second->getName()) == s)
            return it->second;
    }

    return NULL;
}

Player* Game::getPlayerByNameEx(const std::string& s)
{
    Player* player = getPlayerByName(s);
    if(player)
        return player;

    std::string name = s;
    if(!IOLoginData::getInstance()->playerExists(name))
        return NULL;

    player = new Player(name, NULL);
    if(IOLoginData::getInstance()->loadPlayer(player, name))
        return player;

#ifdef __DEBUG__
    std::clog << "[Failure - Game::getPlayerByNameEx] Cannot load player: " << name << std::endl;
#endif
    delete player;
    return NULL;
}

Player* Game::getPlayerByGuid(uint32_t guid)
{
    if(!guid)
        return NULL;

    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && it->second->getGUID() == guid)
            return it->second;
    }

    return NULL;
}

Player* Game::getPlayerByGuidEx(uint32_t guid)
{
    Player* player = getPlayerByGuid(guid);
    if(player)
        return player;

    std::string name;
    if(!IOLoginData::getInstance()->getNameByGuid(guid, name))
        return NULL;

    player = new Player(name, NULL);
    if(IOLoginData::getInstance()->loadPlayer(player, name))
        return player;

#ifdef __DEBUG__
    std::clog << "[Failure - Game::getPlayerByGuidEx] Cannot load player: " << name << std::endl;
#endif
    delete player;
    return NULL;
}

ReturnValue Game::getPlayerByNameWildcard(std::string s, Player*& player)
{
    player = NULL;
    if(s.empty())
        return RET_PLAYERWITHTHISNAMEISNOTONLINE;

    char tmp = *s.rbegin();
    if(tmp != '~' && tmp != '*')
    {
        player = getPlayerByName(s);
        if(!player)
            return RET_PLAYERWITHTHISNAMEISNOTONLINE;

        return RET_NOERROR;
    }

    Player* last = NULL;
    s = s.substr(0, s.length() - 1);

    toLowerCaseString(s);
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(it->second->isRemoved())
            continue;

        std::string name = asLowerCaseString(it->second->getName());
        if(name.substr(0, s.length()) != s)
            continue;

        if(last)
            return RET_NAMEISTOOAMBIGUOUS;

        last = it->second;
    }

    if(!last)
        return RET_PLAYERWITHTHISNAMEISNOTONLINE;

    player = last;
    return RET_NOERROR;
}

Player* Game::getPlayerByAccount(uint32_t acc)
{
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && it->second->getAccount() == acc)
            return it->second;
    }

    return NULL;
}

PlayerVector Game::getPlayersByName(std::string s)
{
    toLowerCaseString(s);
    PlayerVector players;
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && asLowerCaseString(it->second->getName()) == s)
            players.push_back(it->second);
    }

    return players;
}

PlayerVector Game::getPlayersByAccount(uint32_t acc)
{
    PlayerVector players;
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && it->second->getAccount() == acc)
            players.push_back(it->second);
    }

    return players;
}

PlayerVector Game::getPlayersByIP(uint32_t ip, uint32_t mask)
{
    PlayerVector players;
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
    {
        if(!it->second->isRemoved() && (it->second->getIP() & mask) == (ip & mask))
            players.push_back(it->second);
    }

    return players;
}

bool Game::internalPlaceCreature(Creature* creature, const Position& pos, bool extendedPos /*= false*/, bool forced /*= false*/)
{
    if(creature->getParent())
        return false;

    if(!map->placeCreature(pos, creature, extendedPos, forced))
        return false;

    creature->addRef();
    creature->setID();

    autoList[creature->getID()] = creature;
    creature->addList();
    return true;
}

bool Game::placeCreature(Creature* creature, const Position& pos, bool extendedPos /*= false*/, bool forced /*= false*/)
{
    if(!internalPlaceCreature(creature, pos, extendedPos, forced))
        return false;

    Player* tmpPlayer = NULL;
    if((tmpPlayer = creature->getPlayer()) && !tmpPlayer->storedConditionList.empty())
    {
        for(ConditionList::iterator it = tmpPlayer->storedConditionList.begin(); it != tmpPlayer->storedConditionList.end(); ++it)
        {
            if((*it)->getType() == CONDITION_MUTED && (*it)->getTicks() != -1 &&
                (*it)->getTicks() - ((time(NULL) - tmpPlayer->getLastLogout()) * 1000) <= 0)
                continue;

            tmpPlayer->addCondition(*it);
        }

        tmpPlayer->storedConditionList.clear();
    }

    SpectatorVec::iterator it;
    SpectatorVec list;

    getSpectators(list, creature->getPosition(), false, true);
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureAppear(creature);
    }

    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureAppear(creature);

    creature->setLastPosition(pos);
    creature->getParent()->postAddNotification(NULL, creature, NULL, creature->getParent()->__getIndexOfThing(creature));

    addCreatureCheck(creature);
    creature->onPlacedCreature();
    return true;
}

ReturnValue Game::placeSummon(Creature* creature, const std::string& name)
{
    Monster* monster = Monster::createMonster(name);
    if(!monster)
        return RET_NOTPOSSIBLE;

    // Place the monster
    creature->addSummon(monster);
    if(placeCreature(monster, creature->getPosition(), true))
    {
        addMagicEffect(monster->getPosition(), MAGIC_EFFECT_TELEPORT);
        return RET_NOERROR;
    }

    creature->removeSummon(monster);
    return RET_NOTENOUGHROOM;
}

bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/)
{
    if(creature->isRemoved())
        return false;

    Tile* tile = creature->getTile();
    SpectatorVec list;
    getSpectators(list, tile->getPosition(), false, true);

    SpectatorVec::iterator it;
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureDisappear(creature, isLogout);

    if(tile != creature->getTile())
    {
        list.clear();
        tile = creature->getTile();
        getSpectators(list, tile->getPosition(), false, true);
    }

    Player* player = NULL;
    std::vector<uint32_t> oldStackPosVector;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()) && player->canSeeCreature(creature))
            oldStackPosVector.push_back(tile->getClientIndexOfThing(player, creature));
    }

    int32_t oldIndex = tile->__getIndexOfThing(creature);
    if(!map->removeCreature(creature))
        return false;

    //send to client
    uint32_t i = 0;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if(creature != (*it))
            (*it)->updateTileCache(tile);

        if(!(player = (*it)->getPlayer()) || !player->canSeeCreature(creature))
            continue;

        player->setWalkthrough(creature, false);
        player->sendCreatureDisappear(creature, oldStackPosVector);
        ++i;
    }

    creature->getParent()->postRemoveNotification(NULL, creature, NULL, oldIndex, true);
    creature->onRemovedCreature();

    autoList.erase(creature->getID());
    freeThing(creature);

    removeCreatureCheck(creature);
    for(std::list<Creature*>::iterator it = creature->summons.begin(); it != creature->summons.end(); ++it)
        removeCreature(*it);

    return true;
}

bool Game::playerMoveThing(uint32_t playerId, const Position& fromPos,
    uint16_t spriteId, int16_t fromStackpos, const Position& toPos, uint8_t count)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->getNoMove())
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    uint8_t fromIndex = 0;
    if(fromPos.x == 0xFFFF)
    {
        if(fromPos.y & 0x40)
            fromIndex = static_cast<uint8_t>(fromPos.z);
        else
            fromIndex = static_cast<uint8_t>(fromPos.y);
    }
    else
        fromIndex = fromStackpos;

    Thing* thing = internalGetThing(player, fromPos, fromIndex, spriteId, STACKPOS_MOVE);
    Cylinder* toCylinder = internalGetCylinder(player, toPos);
    if(!thing || !toCylinder)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(Creature* movingCreature = thing->getCreature())
        playerMoveCreature(playerId, movingCreature->getID(), movingCreature->getPosition(), toCylinder->getPosition(), true);
    else if(thing->getItem())
        playerMoveItem(playerId, fromPos, spriteId, fromStackpos, toPos, count);

    return true;
}

bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId,
    const Position& movingCreaturePos, const Position& toPos, bool delay)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || player->hasFlag(PlayerFlag_CannotMoveCreatures))
        return false;

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerMoveCreature, this, playerId, movingCreatureId, movingCreaturePos, toPos, true));
        player->setNextActionTask(task);
        return false;
    }

    Creature* movingCreature = getCreatureByID(movingCreatureId);
    if(!movingCreature || movingCreature->isRemoved() || !player->canSeeCreature(movingCreature))
        return false;

    player->setNextActionTask(NULL);
    if(!Position::areInRange<1,1,0>(movingCreaturePos, player->getPosition()) && !player->hasCustomFlag(PlayerCustomFlag_CanMoveFromFar))
    {
        //need to walk to the creature first before moving it
        std::list<Direction> listDir;
        if(getPathToEx(player, movingCreaturePos, listDir, 0, 1, true, true))
        {
            Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                this, player->getID(), listDir)));
            SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                boost::bind(&Game::playerMoveCreature, this, playerId, movingCreatureId, movingCreaturePos, toPos, true));

            player->setNextWalkActionTask(task);
            return true;
        }

        player->sendCancelMessage(RET_THEREISNOWAY);
        return false;
    }
    else if(delay)
    {
        uint32_t delayTime = g_config.getNumber(ConfigManager::PUSH_CREATURE_DELAY);
        if(delayTime > 0)
        {
            SchedulerTask* task = createSchedulerTask(delayTime,
                boost::bind(&Game::playerMoveCreature, this, playerId, movingCreatureId, movingCreaturePos, toPos, false));
            player->setNextActionTask(task);
            return true;
        }
    }

    Tile* toTile = map->getTile(toPos);
    if(!toTile)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(!player->hasFlag(PlayerFlag_CanPushAllCreatures))
    {
        if(!movingCreature->isPushable())
        {
            player->sendCancelMessage(RET_NOTMOVABLE);
            return false;
        }

        if(movingCreature->getNoMove())
        {
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        if(toTile->hasProperty(BLOCKPATH))
        {
            player->sendCancelMessage(RET_NOTENOUGHROOM);
            return false;
        }
    }

    //check throw distance
    const Position& pos = movingCreature->getPosition();
    if(!player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere) && ((std::abs(pos.x - toPos.x) > movingCreature->getThrowRange()) ||
        (std::abs(pos.y - toPos.y) > movingCreature->getThrowRange()) || (std::abs(pos.z - toPos.z) * 4 > movingCreature->getThrowRange())))
    {
        player->sendCancelMessage(RET_DESTINATIONOUTOFREACH);
        return false;
    }

    if(player != movingCreature)
    {
        if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone) && (movingCreature->getZone() == ZONE_PROTECTION
            || movingCreature->getZone() == ZONE_OPTIONAL) && !toTile->hasFlag(TILESTATE_OPTIONALZONE)
            && !toTile->hasFlag(TILESTATE_PROTECTIONZONE))
        {
            player->sendCancelMessage(RET_ACTIONNOTPERMITTEDINANOPVPZONE);
            return false;
        }

        if(!player->hasFlag(PlayerFlag_CanPushAllCreatures))
        {
            if(toTile->getCreatureCount() && !Item::items[
                movingCreature->getTile()->ground->getID()].walkStack)
            {
                player->sendCancelMessage(RET_NOTENOUGHROOM);
                return false;
            }

            if(MagicField* field = toTile->getFieldItem())
            {
                if(field->isUnstepable() || field->isBlocking(movingCreature)
                    || !movingCreature->isImmune(field->getCombatType()))
                {
                    player->sendCancelMessage(RET_NOTPOSSIBLE);
                    return false;
                }
            }

            if(player->isProtected())
            {
                Player* movingPlayer = movingCreature->getPlayer();
                if(movingPlayer && !movingPlayer->isProtected())
                {
                    player->sendCancelMessage(RET_NOTMOVABLE);
                    return false;
                }
            }
        }
    }

    bool deny = false;
    CreatureEventList pushEvents = player->getCreatureEvents(CREATURE_EVENT_PUSH);
    for(CreatureEventList::iterator it = pushEvents.begin(); it != pushEvents.end(); ++it)
    {
        if(!(*it)->executePush(player, movingCreature, toTile) && !deny)
            deny = true;
    }

    if(deny)
        return false;

    ReturnValue ret = internalMoveCreature(player, movingCreature, movingCreature->getTile(), toTile);
    if(ret != RET_NOERROR)
    {
        if(!player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere))
        {
            player->sendCancelMessage(ret);
            return false;
        }

        if(!toTile->ground)
        {
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        internalTeleport(movingCreature, toTile->getPosition(), false);
    }
    else if(Player* movingPlayer = movingCreature->getPlayer())
    {
        uint64_t delay = OTSYS_TIME() + movingPlayer->getStepDuration();
        if(delay > movingPlayer->getNextActionTime(false))
            movingPlayer->setNextAction(delay);
    }

    return true;
}

ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, uint32_t flags/* = 0*/)
{
    const Position& currentPos = creature->getPosition();
    Cylinder* fromTile = creature->getTile();
    Cylinder* toTile = NULL;

    Position destPos = getNextPosition(direction, currentPos);
    if(direction < SOUTHWEST && creature->getPlayer())
    {
        Tile* tmpTile = NULL;
        if(currentPos.z != 8 && creature->getTile()->hasHeight(3)) //try go up
        {
            if((!(tmpTile = map->getTile(Position(currentPos.x, currentPos.y, currentPos.z - 1)))
                || (!tmpTile->ground && !tmpTile->hasProperty(BLOCKSOLID))) &&
                (tmpTile = map->getTile(Position(destPos.x, destPos.y, destPos.z - 1)))
                && tmpTile->ground && !tmpTile->hasProperty(BLOCKSOLID) && !tmpTile->hasProperty(FLOORCHANGEDOWN))
            {
                flags = flags | FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
                destPos.z--;
            }
        }
        else if(currentPos.z != 7 && (!(tmpTile = map->getTile(destPos)) || (!tmpTile->ground &&
            !tmpTile->hasProperty(BLOCKSOLID))) && (tmpTile = map->getTile(Position(
            destPos.x, destPos.y, destPos.z + 1))) && tmpTile->hasHeight(3)) //try go down
        {
            flags = flags | FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
            destPos.z++;
        }
    }

    ReturnValue ret = RET_NOTPOSSIBLE;
    if((toTile = map->getTile(destPos)))
        ret = internalMoveCreature(NULL, creature, fromTile, toTile, flags);

    if(ret == RET_NOERROR)
        return RET_NOERROR;

    Player* player = creature->getPlayer();
    if(!player)
        return ret;

    player->sendCancelMessage(ret);
    player->sendCancelWalk();
    return ret;
}

ReturnValue Game::internalMoveCreature(Creature* actor, Creature* creature, Cylinder* fromCylinder,
    Cylinder* toCylinder, uint32_t flags/* = 0*/, bool forceTeleport/* = false*/)
{
    //check if we can move the creature to the destination
    ReturnValue ret = toCylinder->__queryAdd(0, creature, 1, flags, actor);
    if(ret != RET_NOERROR)
        return ret;

    fromCylinder->getTile()->moveCreature(actor, creature, toCylinder, forceTeleport);
    if(creature->getParent() != toCylinder)
        return RET_NOERROR;

    Item* toItem = NULL;
    Cylinder* subCylinder = NULL;

    int32_t n = 0, tmp = 0;
    while((subCylinder = toCylinder->__queryDestination(tmp, creature, &toItem, flags)) != toCylinder)
    {
        internalCreatureTurn(creature, getDirectionTo(toCylinder->getTile()->getPosition(), subCylinder->getTile()->getPosition(), false));
        toCylinder->getTile()->moveCreature(actor, creature, subCylinder);
        if(creature->getParent() != subCylinder) //could happen if a script move the creature
            break;

        toCylinder = subCylinder;
        flags = 0;
        if(++n >= MAP_MAX_LAYERS) //to prevent infinite loop
            break;
    }

    return RET_NOERROR;
}

bool Game::playerMoveItem(uint32_t playerId, const Position& fromPos,
    uint16_t spriteId, int16_t fromStackpos, const Position& toPos, uint8_t count)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || player->hasFlag(PlayerFlag_CannotMoveItems))
        return false;

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerMoveItem, this, playerId, fromPos, spriteId, fromStackpos, toPos, count));
        player->setNextActionTask(task);
        return false;
    }

    player->setNextActionTask(NULL);
    Cylinder* fromCylinder = internalGetCylinder(player, fromPos);

    uint8_t fromIndex = 0;
    if(fromPos.x == 0xFFFF)
    {
        if(fromPos.y & 0x40)
            fromIndex = static_cast<uint8_t>(fromPos.z);
        else
            fromIndex = static_cast<uint8_t>(fromPos.y);
    }
    else
        fromIndex = fromStackpos;

    Thing* thing = internalGetThing(player, fromPos, fromIndex, spriteId, STACKPOS_MOVE);
    if(!thing || !thing->getItem())
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Item* item = thing->getItem();
    Cylinder* toCylinder = internalGetCylinder(player, toPos);

    uint8_t toIndex = 0;
    if(toPos.x == 0xFFFF)
    {
        if(toPos.y & 0x40)
            toIndex = static_cast<uint8_t>(toPos.z);
        else
            toIndex = static_cast<uint8_t>(toPos.y);
    }

    if(!fromCylinder || !toCylinder || !item || item->getClientID() != spriteId)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(!player->hasCustomFlag(PlayerCustomFlag_CanPushAllItems) && (!item->isPushable() || (item->isLoadedFromMap() &&
        (item->getUniqueId() || (item->getActionId() && item->getContainer())))))
    {
        player->sendCancelMessage(RET_NOTMOVABLE);
        return false;
    }

    const Position &mapToPos = toCylinder->getTile()->getPosition(), &playerPos = player->getPosition(),
        &mapFromPos = fromCylinder->getTile()->getPosition();
    if(playerPos.z > mapFromPos.z && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_FIRSTGOUPSTAIRS);
        return false;
    }

    if(playerPos.z < mapFromPos.z && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_FIRSTGODOWNSTAIRS);
        return false;
    }

    if(!Position::areInRange<1,1,0>(playerPos, mapFromPos) && !player->hasCustomFlag(PlayerCustomFlag_CanMoveFromFar))
    {
        //need to walk to the item first before using it
        std::list<Direction> listDir;
        if(getPathToEx(player, item->getPosition(), listDir, 0, 1, true, true))
        {
            Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                this, player->getID(), listDir)));
            SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                boost::bind(&Game::playerMoveItem, this, playerId, fromPos, spriteId, fromStackpos, toPos, count));

            player->setNextWalkActionTask(task);
            return true;
        }

        player->sendCancelMessage(RET_THEREISNOWAY);
        return false;
    }

    //hangable item specific code
    if(item->isHangable() && toCylinder->getTile()->hasProperty(SUPPORTHANGABLE))
    {
        //destination supports hangable objects so need to move there first
        if(toCylinder->getTile()->hasProperty(ISVERTICAL))
        {
            if(player->getPosition().x + 1 == mapToPos.x)
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }
        }
        else if(toCylinder->getTile()->hasProperty(ISHORIZONTAL))
        {
            if(player->getPosition().y + 1 == mapToPos.y)
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }
        }

        if(!Position::areInRange<1,1,0>(playerPos, mapToPos) && !player->hasCustomFlag(PlayerCustomFlag_CanMoveFromFar))
        {
            Position walkPos = mapToPos;
            if(toCylinder->getTile()->hasProperty(ISVERTICAL))
                walkPos.x -= -1;

            if(toCylinder->getTile()->hasProperty(ISHORIZONTAL))
                walkPos.y -= -1;

            Position itemPos = fromPos;
            int16_t itemStackpos = fromStackpos;
            if(fromPos.x != 0xFFFF && Position::areInRange<1,1,0>(mapFromPos, player->getPosition())
                && !Position::areInRange<1,1,0>(mapFromPos, walkPos))
            {
                //need to pickup the item first
                Item* moveItem = NULL;
                ReturnValue ret = internalMoveItem(player, fromCylinder, player, INDEX_WHEREEVER, item, count, &moveItem);
                if(ret != RET_NOERROR)
                {
                    player->sendCancelMessage(ret);
                    return false;
                }

                //changing the position since its now in the inventory of the player
                internalGetPosition(moveItem, itemPos, itemStackpos);
            }

            std::list<Direction> listDir;
            if(map->getPathTo(player, walkPos, listDir))
            {
                Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                    this, player->getID(), listDir)));
                SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                    boost::bind(&Game::playerMoveItem, this, playerId, itemPos, spriteId, itemStackpos, toPos, count));

                player->setNextWalkActionTask(task);
                return true;
            }

            player->sendCancelMessage(RET_THEREISNOWAY);
            return false;
        }
    }

    if(!player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        if((std::abs(playerPos.x - mapToPos.x) > item->getThrowRange()) ||
            (std::abs(playerPos.y - mapToPos.y) > item->getThrowRange()) ||
            (std::abs(mapFromPos.z - mapToPos.z) * 4 > item->getThrowRange()))
        {
            player->sendCancelMessage(RET_DESTINATIONOUTOFREACH);
            return false;
        }
    }

    if(!canThrowObjectTo(mapFromPos, mapToPos) && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_CANNOTTHROW);
        return false;
    }

    bool deny = false;
    CreatureEventList throwEvents = player->getCreatureEvents(CREATURE_EVENT_THROW);
    for(CreatureEventList::iterator it = throwEvents.begin(); it != throwEvents.end(); ++it)
    {
        if(!(*it)->executeThrow(player, item, fromPos, toPos) && !deny)
            deny = true;
    }

    if(deny)
        return false;

    ReturnValue ret = internalMoveItem(player, fromCylinder, toCylinder, toIndex, item, count, NULL);
    if(ret != RET_NOERROR)
    {
        player->sendCancelMessage(ret);
        return false;
    }

    player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL) - 10);
    return true;
}

ReturnValue Game::internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cylinder* toCylinder,
    int32_t index, Item* item, uint32_t count, Item** _moveItem, uint32_t flags /*= 0*/)
{
    if(!toCylinder)
        return RET_NOTPOSSIBLE;

    if(!item->getParent())
    {
        assert(fromCylinder == item->getParent());
        return internalAddItem(actor, toCylinder, item, INDEX_WHEREEVER, FLAG_NOLIMIT);
    }

    Item* toItem = NULL;
    Cylinder* subCylinder = NULL;

    int32_t floor = 0;
    while((subCylinder = toCylinder->__queryDestination(index, item, &toItem, flags)) != toCylinder)
    {
        flags = 0;
        toCylinder = subCylinder;
        //to prevent infinite loop
        if(++floor >= MAP_MAX_LAYERS)
            break;
    }

    //destination is the same as the source?
    if(item == toItem)
        return RET_NOERROR; //silently ignore move

    //check if we can add this item
    ReturnValue ret = toCylinder->__queryAdd(index, item, count, flags, actor);
    if(ret == RET_NEEDEXCHANGE)
    {
        //check if we can add it to source cylinder
        int32_t fromIndex = fromCylinder->__getIndexOfThing(item);
        if((ret = fromCylinder->__queryAdd(fromIndex, toItem, toItem->getItemCount(), 0, actor)) == RET_NOERROR)
        {
            //check how much we can move
            uint32_t maxExchangeQueryCount = 0;
            ReturnValue retExchangeMaxCount = fromCylinder->__queryMaxCount(INDEX_WHEREEVER, toItem, toItem->getItemCount(), maxExchangeQueryCount, 0);
            if(retExchangeMaxCount != RET_NOERROR && !maxExchangeQueryCount)
                return retExchangeMaxCount;

            if((toCylinder->__queryRemove(toItem, toItem->getItemCount(), flags, actor) == RET_NOERROR) && ret == RET_NOERROR)
            {
                int32_t oldToItemIndex = toCylinder->__getIndexOfThing(toItem);
                toCylinder->__removeThing(toItem, toItem->getItemCount());

                fromCylinder->__addThing(actor, toItem);
                if(oldToItemIndex != -1)
                    toCylinder->postRemoveNotification(actor, toItem, fromCylinder, oldToItemIndex, true);

                int32_t newToItemIndex = fromCylinder->__getIndexOfThing(toItem);
                if(newToItemIndex != -1)
                    fromCylinder->postAddNotification(actor, toItem, toCylinder, newToItemIndex);

                ret = toCylinder->__queryAdd(index, item, count, flags, actor);
                toItem = NULL;
            }
        }
    }

    if(ret != RET_NOERROR)
        return ret;

    //check how much we can move
    uint32_t maxQueryCount = 0;
    ReturnValue retMaxCount = toCylinder->__queryMaxCount(index, item, count, maxQueryCount, flags);
    if(retMaxCount != RET_NOERROR && !maxQueryCount)
        return ret;

    uint32_t m = maxQueryCount;
    if(item->isStackable())
        m = std::min((uint32_t)count, m);

    Item* moveItem = item;
    //check if we can remove this item
    if((ret = fromCylinder->__queryRemove(item, m, flags, actor)) != RET_NOERROR)
        return ret;

    //remove the item
    int32_t itemIndex = fromCylinder->__getIndexOfThing(item);
    fromCylinder->__removeThing(item, m);
    bool isCompleteRemoval = item->isRemoved();

    Item* updateItem = NULL;
    if(item->isStackable())
    {
        uint8_t n = 0;
        if(toItem && toItem->getID() == item->getID())
        {
            n = std::min((uint32_t)100 - toItem->getItemCount(), m);
            toCylinder->__updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
            updateItem = toItem;
        }

        if(m - n > 0)
            moveItem = Item::CreateItem(item->getID(), m - n);
        else
            moveItem = NULL;

        if(item->isRemoved())
            freeThing(item);
    }

    if(moveItem)
        toCylinder->__addThing(actor, index, moveItem);

    if(itemIndex != -1)
        fromCylinder->postRemoveNotification(actor, item, toCylinder, itemIndex, isCompleteRemoval);

    if(moveItem)
    {
        int32_t moveItemIndex = toCylinder->__getIndexOfThing(moveItem);
        if(moveItemIndex != -1)
            toCylinder->postAddNotification(actor, moveItem, fromCylinder, moveItemIndex);
    }

    if(updateItem)
    {
        int32_t updateItemIndex = toCylinder->__getIndexOfThing(updateItem);
        if(updateItemIndex != -1)
            toCylinder->postAddNotification(actor, updateItem, fromCylinder, updateItemIndex);
    }

    if(_moveItem)
    {
        if(moveItem)
            *_moveItem = moveItem;
        else
            *_moveItem = item;
    }

    //we could not move all, inform the player
    if(item->isStackable() && maxQueryCount < count)
        return retMaxCount;

    return ret;
}

ReturnValue Game::internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index /*= INDEX_WHEREEVER*/,
    uint32_t flags/* = 0*/, bool test/* = false*/)
{
    uint32_t remainderCount = 0;
    return internalAddItem(actor, toCylinder, item, index, flags, test, remainderCount);
}

ReturnValue Game::internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index,
    uint32_t flags, bool test, uint32_t& remainderCount)
{
    Item* stackItem = NULL;
    return internalAddItem(actor, toCylinder, item, index, flags, test, remainderCount, &stackItem);
}

ReturnValue Game::internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index,
    uint32_t flags, bool test, uint32_t& remainderCount, Item** stackItem)
{
    *stackItem = NULL;
    remainderCount = 0;
    if(!toCylinder || !item)
        return RET_NOTPOSSIBLE;

    Cylinder* destCylinder = toCylinder;
    toCylinder = toCylinder->__queryDestination(index, item, stackItem, flags);

    //check if we can add this item
    ReturnValue ret = toCylinder->__queryAdd(index, item, item->getItemCount(), flags, actor);
    if(ret != RET_NOERROR)
        return ret;

    uint32_t maxQueryCount = 0;
    ret = destCylinder->__queryMaxCount(INDEX_WHEREEVER, item, item->getItemCount(), maxQueryCount, flags);
    if(ret != RET_NOERROR)
        return ret;

    if(test)
        return RET_NOERROR;

    Item* toItem = *stackItem;
    if(item->isStackable() && toItem)
    {
        uint32_t m = std::min((uint32_t)item->getItemCount(), maxQueryCount), n = 0;
        if(toItem->getID() == item->getID())
        {
            n = std::min((uint32_t)100 - toItem->getItemCount(), m);
            toCylinder->__updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
        }

        uint32_t count = m - n;
        if(count > 0)
        {
            if(item->getItemCount() != count)
            {
                Item* remainderItem = Item::CreateItem(item->getID(), count);
                if((ret = internalAddItem(NULL, toCylinder, remainderItem, INDEX_WHEREEVER, flags, false)) == RET_NOERROR)
                {
                    if(item->getParent() != VirtualCylinder::virtualCylinder)
                    {
                        item->onRemoved();
                        freeThing(item);
                    }

                    return RET_NOERROR;
                }

                delete remainderItem;
                remainderCount = count;
                return ret;
            }
        }
        else
        {
            if(item->getParent() != VirtualCylinder::virtualCylinder)
            {
                item->onRemoved();
                freeThing(item);
            }

            return RET_NOERROR;
        }
    }

    toCylinder->__addThing(NULL, index, item);
    int32_t itemIndex = toCylinder->__getIndexOfThing(item);
    if(itemIndex != -1)
        toCylinder->postAddNotification(actor, item, NULL, itemIndex);

    return RET_NOERROR;
}

ReturnValue Game::internalRemoveItem(Creature* actor, Item* item, int32_t count/* = -1*/, bool test/* = false*/, uint32_t flags/* = 0*/)
{
    Cylinder* cylinder = item->getParent();
    if(!cylinder)
        return RET_NOTPOSSIBLE;

    if(count == -1)
        count = item->getItemCount();

    //check if we can remove this item
    ReturnValue ret = cylinder->__queryRemove(item, count, flags | FLAG_IGNORENOTMOVABLE, actor);
    if(ret != RET_NOERROR)
        return ret;

    if(!item->canRemove())
        return RET_NOTPOSSIBLE;

    if(!test)
    {
        //remove the item
        int32_t index = cylinder->__getIndexOfThing(item);
        cylinder->__removeThing(item, count);

        cylinder->postRemoveNotification(actor, item, NULL, index, item->isRemoved());
        if(item->isRemoved())
            freeThing(item);
    }

    item->onRemoved();
    return RET_NOERROR;
}

ReturnValue Game::internalPlayerAddItem(Creature* actor, Player* player, Item* item,
    bool dropOnMap/* = true*/, slots_t slot/* = SLOT_WHEREEVER*/)
{
    Item* toItem = NULL;
    return internalPlayerAddItem(actor, player, item, dropOnMap, slot, &toItem);
}

ReturnValue Game::internalPlayerAddItem(Creature* actor, Player* player, Item* item,
    bool dropOnMap, slots_t slot, Item** toItem)
{
    uint32_t remainderCount = 0, count = item->getItemCount();
    ReturnValue ret = internalAddItem(actor, player, item, (int32_t)slot, 0, false, remainderCount, toItem);
    if(ret == RET_NOERROR)
        return RET_NOERROR;

    if(dropOnMap)
    {
        if(!remainderCount)
            return internalAddItem(actor, player->getTile(), item, (int32_t)slot, FLAG_NOLIMIT);

        Item* remainderItem = Item::CreateItem(item->getID(), remainderCount);
        if(internalAddItem(actor, player->getTile(), remainderItem, INDEX_WHEREEVER, FLAG_NOLIMIT) == RET_NOERROR)
            return RET_NOERROR;

        delete remainderItem;
    }

    if(remainderCount && toItem)
        transformItem(*toItem, (*toItem)->getID(), ((*toItem)->getItemCount() - (count - remainderCount)));

    return ret;
}

Item* Game::findItemOfType(Cylinder* cylinder, uint16_t itemId,
    bool depthSearch /*= true*/, int32_t subType /*= -1*/)
{
    if(!cylinder)
        return NULL;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;
    Item* item = NULL;

    Thing* thing = NULL;
    for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex();)
    {
        if((thing = cylinder->__getThing(i)) && (item = thing->getItem()))
        {
            if(item->getID() == itemId && (subType == -1 || subType == item->getSubType()))
                return item;

            ++i;
            if(depthSearch && (tmpContainer = item->getContainer()))
                listContainer.push_back(tmpContainer);
        }
        else
            ++i;
    }

    Container* container = NULL;
    while(listContainer.size() > 0)
    {
        container = listContainer.front();
        listContainer.pop_front();
        for(int32_t i = 0; i < (int32_t)container->size(); )
        {
            if((item = container->getItem(i)))
            {
                if(item->getID() == itemId && (subType == -1 || subType == item->getSubType()))
                    return item;

                ++i;
                if((tmpContainer = item->getContainer()))
                    listContainer.push_back(tmpContainer);
            }
            else
                ++i;
        }
    }

    return NULL;
}

bool Game::removeItemOfType(Cylinder* cylinder, uint16_t itemId, int32_t count, int32_t subType/* = -1*/, bool onlyContainers/* = false*/)
{
    if(!cylinder || ((int32_t)cylinder->__getItemTypeCount(itemId, subType) < count))
        return false;

    if(count <= 0)
        return true;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;
    Item* item = NULL;

    Thing* thing = NULL;
    for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex() && count > 0; )
    {
        if((thing = cylinder->__getThing(i)) && (item = thing->getItem()))
        {
            if(!onlyContainers && item->getID() == itemId)
            {
                if(item->isStackable())
                {
                    if(item->getItemCount() > count)
                    {
                        internalRemoveItem(NULL, item, count);
                        count = 0;
                    }
                    else
                    {
                        count -= item->getItemCount();
                        internalRemoveItem(NULL, item);
                    }
                }
                else if(subType == -1 || subType == item->getSubType())
                {
                    --count;
                    internalRemoveItem(NULL, item);
                }
                else
                    ++i;
            }
            else
            {
                ++i;
                if((tmpContainer = item->getContainer()))
                    listContainer.push_back(tmpContainer);
            }
        }
        else
            ++i;
    }

    Container* container = NULL;
    while(listContainer.size() > 0 && count > 0)
    {
        container = listContainer.front();
        listContainer.pop_front();
        for(int32_t i = 0; i < (int32_t)container->size() && count > 0; )
        {
            if((item = container->getItem(i)))
            {
                if(item->getID() == itemId)
                {
                    if(item->isStackable())
                    {
                        if(item->getItemCount() > count)
                        {
                            internalRemoveItem(NULL, item, count);
                            count = 0;
                        }
                        else
                        {
                            count-= item->getItemCount();
                            internalRemoveItem(NULL, item);
                        }
                    }
                    else if(subType == -1 || subType == item->getSubType())
                    {
                        --count;
                        internalRemoveItem(NULL, item);
                    }
                    else
                        ++i;
                }
                else
                {
                    ++i;
                    if((tmpContainer = item->getContainer()))
                        listContainer.push_back(tmpContainer);
                }
            }
            else
                ++i;
        }
    }

    return !count;
}

uint64_t Game::getMoney(const Cylinder* cylinder)
{
    if(!cylinder)
        return 0;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;

    Thing* thing = NULL;
    Item* item = NULL;

    uint64_t moneyCount = 0;
    for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex(); ++i)
    {
        if(!(thing = cylinder->__getThing(i)) || !(item = thing->getItem()))
            continue;

        if((tmpContainer = item->getContainer()))
            listContainer.push_back(tmpContainer);
        else if(item->getWorth())
            moneyCount += item->getWorth();
    }

    Container* container = NULL;
    while(listContainer.size() > 0)
    {
        container = listContainer.front();
        listContainer.pop_front();
        for(ItemList::const_iterator it = container->getItems(); it != container->getEnd(); ++it)
        {
            item = *it;
            if((tmpContainer = item->getContainer()))
                listContainer.push_back(tmpContainer);
            else if(item->getWorth())
                moneyCount += item->getWorth();
        }
    }

    return moneyCount;
}

bool Game::removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    if(!cylinder)
        return false;

    if(money <= 0)
        return true;

    typedef std::multimap<int32_t, Item*, std::less<int32_t> > MoneyMultiMap;
    MoneyMultiMap moneyMap;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;

    Thing* thing = NULL;
    Item* item = NULL;

    int64_t moneyCount = 0;
    for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex(); ++i)
    {
        if(!(thing = cylinder->__getThing(i)) || !(item = thing->getItem()))
            continue;

        if((tmpContainer = item->getContainer()))
            listContainer.push_back(tmpContainer);
        else if(item->getWorth())
        {
            moneyCount += item->getWorth();
            moneyMap.insert(std::make_pair(item->getWorth(), item));
        }
    }

    while(listContainer.size() > 0)
    {
        Container* container = listContainer.front();
        listContainer.pop_front();
        for(int32_t i = 0; i < (int32_t)container->size(); ++i)
        {
            Item* item = container->getItem(i);
            if((tmpContainer = item->getContainer()))
                listContainer.push_back(tmpContainer);
            else if(item->getWorth())
            {
                moneyCount += item->getWorth();
                moneyMap.insert(std::make_pair(item->getWorth(), item));
            }
        }
    }

    // Not enough money
    if(moneyCount < money)
        return false;

    for(MoneyMultiMap::iterator mit = moneyMap.begin(); mit != moneyMap.end() && money > 0; ++mit)
    {
        if(!(item = mit->second))
            continue;

        internalRemoveItem(NULL, item);
        if(mit->first > money)
        {
            // Remove a monetary value from an item
            addMoney(cylinder, (int64_t)(item->getWorth() - money), flags);
            money = 0;
        }
        else
            money -= mit->first;

        mit->second = NULL;
    }

    moneyMap.clear();
    return !money;
}

void Game::addMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    IntegerMap moneyMap = Item::items.getMoneyMap();
    for(IntegerMap::reverse_iterator it = moneyMap.rbegin(); it != moneyMap.rend(); ++it)
    {
        int64_t tmp = money / it->first;
        money -= tmp * it->first;
        if(!tmp)
            continue;

        do
        {
            uint32_t remainderCount = 0;
            Item* item = Item::CreateItem(it->second, std::min<uint16_t>(100, tmp));
            if(internalAddItem(NULL, cylinder, item, INDEX_WHEREEVER, flags, false, remainderCount) != RET_NOERROR)
            {
                if(remainderCount)
                {
                    delete item;
                    item = Item::CreateItem(it->second, remainderCount);
                }

                if(internalAddItem(NULL, cylinder->getTile(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR)
                    delete item;
            }

            tmp -= std::min((int64_t)100, tmp);
        }
        while(tmp > 0);
    }
}

Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount/* = -1*/)
{
    if(item->getID() == newId && (newCount == -1 || (newCount == item->getSubType() && newCount)))
        return item;

    Cylinder* cylinder = item->getParent();
    if(!cylinder)
        return NULL;

    int32_t itemIndex = cylinder->__getIndexOfThing(item);
    if(itemIndex == -1)
    {
#ifdef __DEBUG__
        std::clog << "Error: transformItem, itemIndex == -1" << std::endl;
#endif
        return item;
    }

    if(!item->canTransform())
        return item;

    const ItemType &curType = Item::items[item->getID()], &newType = Item::items[newId];
    if(curType.alwaysOnTop != newType.alwaysOnTop)
    {
        // This only occurs when you transform items on tiles from a downItem to a topItem (or vice versa)
        // Remove the old, and add the new
        ReturnValue ret = internalRemoveItem(NULL, item);
        if(ret != RET_NOERROR)
            return item;

        Item* newItem = NULL;
        if(newCount == -1)
            newItem = Item::CreateItem(newId);
        else
            newItem = Item::CreateItem(newId, newCount);

        if(!newItem)
            return NULL;

        newItem->copyAttributes(item);
        if(internalAddItem(NULL, cylinder, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR)
        {
            delete newItem;
            return NULL;
        }

        newItem->makeUnique(item);
        return newItem;
    }

    if(curType.type == newType.type)
    {
        //Both items has the same type so we can safely change id/subtype
        if(!newCount && (item->isStackable() || item->hasCharges()))
        {
            if(!item->isStackable() && (!item->getDefaultDuration() || item->getDuration() <= 0))
            {
                int16_t tmp = newId;
                if(curType.id == newId)
                    tmp = curType.decayTo;

                if(tmp != -1)
                    return transformItem(item, tmp);
            }

            internalRemoveItem(NULL, item);
            return NULL;
        }

        cylinder->postRemoveNotification(NULL, item, cylinder, itemIndex, false);
        uint16_t tmp = item->getID();
        if(curType.id != newId)
        {
            tmp = newId;
            if(newType.group != curType.group)
                item->setDefaultSubtype();

            if(curType.hasSubType() && !newType.hasSubType())
            {
                item->resetFluidType();
                item->resetCharges();
            }
        }

        int32_t count = item->getSubType();
        if(newCount != -1 && newType.hasSubType())
            count = newCount;

        cylinder->__updateThing(item, tmp, count);
        cylinder->postAddNotification(NULL, item, cylinder, itemIndex);
        return item;
    }

    //Replacing the the old item with the new while maintaining the old position
    Item* newItem = NULL;
    if(newCount == -1)
        newItem = Item::CreateItem(newId);
    else
        newItem = Item::CreateItem(newId, newCount);

    if(!newItem)
        return NULL;

    newItem->copyAttributes(item);
    newItem->makeUnique(item);
    cylinder->__replaceThing(itemIndex, newItem);

    cylinder->postAddNotification(NULL, newItem, cylinder, itemIndex);
    item->setParent(NULL);
    cylinder->postRemoveNotification(NULL, item, cylinder, itemIndex, true);

    freeThing(item);
    return newItem;
}

ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool forceTeleport, uint32_t flags/* = 0*/, bool fullTeleport/* = true*/)
{
    if(newPos == thing->getPosition())
        return RET_NOERROR;

    if(thing->isRemoved())
        return RET_NOTPOSSIBLE;

    if(Tile* toTile = map->getTile(newPos))
    {
        if(Creature* creature = thing->getCreature())
        {
            if(fullTeleport)
                return internalMoveCreature(NULL, creature, creature->getParent(), toTile, flags, forceTeleport);

            creature->getTile()->moveCreature(NULL, creature, toTile, forceTeleport);
            return RET_NOERROR;
        }

        if(Item* item = thing->getItem())
            return internalMoveItem(NULL, item->getParent(), toTile, INDEX_WHEREEVER, item, item->getItemCount(), NULL, flags);
    }

    return RET_NOTPOSSIBLE;
}

bool Game::playerMove(uint32_t playerId, Direction dir)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->setIdleTime(0);
    if(player->getNoMove())
    {
        player->sendCancelWalk();
        return false;
    }

    std::list<Direction> dirs;
    dirs.push_back(dir);

    player->setNextWalkActionTask(NULL);
    return player->startAutoWalk(dirs);
}

bool Game::playerBroadcastMessage(Player* player, MessageClasses type, const std::string& text, uint32_t statementId)
{
    if(!player->hasFlag(PlayerFlag_CanBroadcast) || !((type >= MSG_SPEAK_FIRST && type <= MSG_SPEAK_LAST) ||
            (type >= MSG_SPEAK_MONSTER_FIRST && type <= MSG_SPEAK_MONSTER_LAST)))
        return false;

    Logger::getInstance()->eFile("talkactions/" + player->getName() + ".log", "#b " + text, true);
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
        it->second->sendCreatureSay(player, type, text, NULL, statementId);

    std::clog << "> " << player->getName() << " broadcasted: \"" << text << "\"." << std::endl;
    return true;
}

bool Game::playerCreatePrivateChannel(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->isPremium())
        return false;

    ChatChannel* channel = g_chat.createChannel(player, 0xFFFF);
    if(!channel || !channel->addUser(player))
        return false;

    player->sendCreatePrivateChannel(channel->getId(), channel->getName());
    return true;
}

bool Game::playerChannelInvite(uint32_t playerId, const std::string& name)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
    if(!channel)
        return false;

    Player* invitePlayer = getPlayerByName(name);
    if(!invitePlayer)
        return false;

    channel->invitePlayer(player, invitePlayer);
    return true;
}

bool Game::playerChannelExclude(uint32_t playerId, const std::string& name)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    PrivateChatChannel* channel = g_chat.getPrivateChannel(player);
    if(!channel)
        return false;

    Player* excludePlayer = getPlayerByName(name);
    if(!excludePlayer)
        return false;

    channel->excludePlayer(player, excludePlayer);
    return true;
}

bool Game::playerRequestChannels(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->sendChannelsDialog(g_chat.getChannelList(player));
    player->setSentChat(true);
    return true;
}

bool Game::playerOpenChannel(uint32_t playerId, uint16_t channelId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    bool deny = false;
    CreatureEventList openEvents = player->getCreatureEvents(CREATURE_EVENT_CHANNEL_REQUEST);
    for(CreatureEventList::iterator it = openEvents.begin(); it != openEvents.end(); ++it)
    {
        if(!(*it)->executeChannelRequest(player, asString(channelId), false, !player->hasSentChat()) && !deny)
            deny = true;
    }

    player->setSentChat(false);
    if(deny)
        return false;

    ChatChannel* channel = g_chat.addUserToChannel(player, channelId);
    if(!channel)
    {
        #ifdef __DEBUG_CHAT__
        std::clog << "Game::playerOpenChannel - failed adding user to channel." << std::endl;
        #endif
        return false;
    }

    player->sendChannel(channel->getId(), channel->getName());
    return true;
}

bool Game::playerCloseChannel(uint32_t playerId, uint16_t channelId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    g_chat.removeUserFromChannel(player, channelId);
    return true;
}

bool Game::playerOpenPrivateChannel(uint32_t playerId, std::string& receiver)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    bool deny = false;
    CreatureEventList openEvents = player->getCreatureEvents(CREATURE_EVENT_CHANNEL_REQUEST);
    for(CreatureEventList::iterator it = openEvents.begin(); it != openEvents.end(); ++it)
    {
        if(!(*it)->executeChannelRequest(player, receiver, true, !player->hasSentChat()) && !deny)
            deny = true;
    }

    player->setSentChat(false);
    if(deny)
        return false;

    if(IOLoginData::getInstance()->playerExists(receiver))
        player->sendOpenPrivateChannel(receiver);
    else
        player->sendCancel("A player with this name does not exist.");

    return true;
}

bool Game::playerCloseNpcChannel(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    SpectatorVec list;
    SpectatorVec::iterator it;

    getSpectators(list, player->getPosition());
    Npc* npc = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((npc = (*it)->getNpc()))
            npc->onPlayerCloseChannel(player);
    }

    return true;
}

bool Game::playerReceivePing(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->receivePing();
    return true;
}

bool Game::playerAutoWalk(uint32_t playerId, std::list<Direction>& listDir)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->setIdleTime(0);
    if(player->getNoMove())
    {
        player->sendCancelWalk();
        return false;
    }

    if(player->hasCondition(CONDITION_GAMEMASTER, GAMEMASTER_TELEPORT))
    {
        Position pos = player->getPosition();
        for(std::list<Direction>::iterator it = listDir.begin(); it != listDir.end(); ++it)
            pos = getNextPosition((*it), pos);

        pos = getClosestFreeTile(player, pos, true, false);
        if(!pos.x || !pos.y)
        {
            player->sendCancelWalk();
            return false;
        }

        internalCreatureTurn(player, getDirectionTo(player->getPosition(), pos, false));
        internalTeleport(player, pos, true);
        return true;
    }

    player->setNextWalkTask(NULL);
    return player->startAutoWalk(listDir);
}

bool Game::playerStopAutoWalk(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->stopWalk();
    return true;
}

bool Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, int16_t fromStackpos, uint16_t fromSpriteId,
    const Position& toPos, int16_t toStackpos, uint16_t toSpriteId, bool isHotkey)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(isHotkey && !g_config.getBool(ConfigManager::AIMBOT_HOTKEY_ENABLED))
        return false;

    Thing* thing = internalGetThing(player, fromPos, fromStackpos, fromSpriteId, STACKPOS_USEITEM);
    if(!thing)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Item* item = thing->getItem();
    if(!item || !item->isUsable())
    {
        player->sendCancelMessage(RET_CANNOTUSETHISOBJECT);
        return false;
    }

    Position walkToPos = fromPos;
    ReturnValue ret = g_actions->canUse(player, fromPos);
    if(ret == RET_NOERROR)
    {
        ret = g_actions->canUseEx(player, toPos, item);
        if(ret == RET_TOOFARAWAY)
        {
            if(!player->hasCustomFlag(PlayerCustomFlag_CanUseFar))
                walkToPos = toPos;
            else
                ret = RET_NOERROR;
        }
    }

    if(ret != RET_NOERROR)
    {
        if(ret == RET_TOOFARAWAY)
        {
            Position itemPos = fromPos;
            if(fromPos.x != 0xFFFF && toPos.x != 0xFFFF && Position::areInRange<1,1,0>(fromPos,
                player->getPosition()) && !Position::areInRange<1,1,0>(fromPos, toPos))
            {
                Item* moveItem = NULL;
                ReturnValue retTmp = internalMoveItem(player, item->getParent(), player,
                    INDEX_WHEREEVER, item, item->getItemCount(), &moveItem);
                if(retTmp != RET_NOERROR)
                {
                    player->sendCancelMessage(retTmp);
                    return false;
                }

                //changing the position since its now in the inventory of the player
                internalGetPosition(moveItem, itemPos, fromStackpos);
            }

            if(player->getNoMove())
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }

            std::list<Direction> listDir;
            if(getPathToEx(player, walkToPos, listDir, 0, 1, true, true, 10))
            {
                Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                    this, player->getID(), listDir)));

                SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                    boost::bind(&Game::playerUseItemEx, this, playerId, itemPos, fromStackpos, fromSpriteId, toPos, toStackpos, toSpriteId, isHotkey));

                player->setNextWalkActionTask(task);
                return true;
            }

            ret = RET_THEREISNOWAY;
        }

        player->sendCancelMessage(ret);
        return false;
    }

    if(isHotkey)
        showHotkeyUseMessage(player, item);

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerUseItemEx, this, playerId, fromPos, fromStackpos, fromSpriteId, toPos, toStackpos, toSpriteId, isHotkey));
        player->setNextActionTask(task);
        return false;
    }

    player->setIdleTime(0);
    player->setNextActionTask(NULL);
    return g_actions->useItemEx(player, fromPos, toPos, toStackpos, item, isHotkey);
}

bool Game::playerUseItem(uint32_t playerId, const Position& pos, int16_t stackpos,
    uint8_t index, uint16_t spriteId, bool isHotkey)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(isHotkey && !g_config.getBool(ConfigManager::AIMBOT_HOTKEY_ENABLED))
        return false;

    Thing* thing = internalGetThing(player, pos, stackpos, spriteId, STACKPOS_USEITEM);
    if(!thing)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Item* item = thing->getItem();
    if(!item || item->isUsable())
    {
        player->sendCancelMessage(RET_CANNOTUSETHISOBJECT);
        return false;
    }

    ReturnValue ret = g_actions->canUse(player, pos);
    if(ret == RET_TOOFARAWAY && player->hasCustomFlag(PlayerCustomFlag_CanUseFar))
        ret = RET_NOERROR;

    if(ret != RET_NOERROR)
    {
        if(ret == RET_TOOFARAWAY)
        {
            if(player->getNoMove())
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }

            std::list<Direction> listDir;
            if(getPathToEx(player, pos, listDir, 0, 1, true, true))
            {
                Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                    this, player->getID(), listDir)));

                SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                    boost::bind(&Game::playerUseItem, this, playerId, pos, stackpos, index, spriteId, isHotkey));

                player->setNextWalkActionTask(task);
                return true;
            }

            ret = RET_THEREISNOWAY;
        }

        player->sendCancelMessage(ret);
        return false;
    }

    if(isHotkey)
        showHotkeyUseMessage(player, item);

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerUseItem, this, playerId, pos, stackpos, index, spriteId, isHotkey));
        player->setNextActionTask(task);
        return false;
    }

    player->setIdleTime(0);
    player->setNextActionTask(NULL);
    return g_actions->useItem(player, pos, index, item);
}

bool Game::playerUseBattleWindow(uint32_t playerId, const Position& pos, int16_t stackpos,
    uint32_t creatureId, uint16_t spriteId, bool isHotkey)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Creature* creature = getCreatureByID(creatureId);
    if(!creature)
        return false;

    if(!Position::areInRange<7,5,0>(creature->getPosition(), player->getPosition()))
        return false;

    if(!g_config.getBool(ConfigManager::AIMBOT_HOTKEY_ENABLED) && (creature->getPlayer() || isHotkey))
    {
        player->sendCancelMessage(RET_DIRECTPLAYERSHOOT);
        return false;
    }

    Thing* thing = internalGetThing(player, pos, stackpos, spriteId, STACKPOS_USE);
    if(!thing)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Item* item = thing->getItem();
    if(!item || item->getClientID() != spriteId)
    {
        player->sendCancelMessage(RET_CANNOTUSETHISOBJECT);
        return false;
    }

    ReturnValue ret = g_actions->canUse(player, pos);
    if(ret != RET_NOERROR)
    {
        if(ret == RET_TOOFARAWAY)
        {
            if(player->getNoMove())
            {
                player->sendCancelMessage(RET_NOTPOSSIBLE);
                return false;
            }

            std::list<Direction> listDir;
            if(getPathToEx(player, item->getPosition(), listDir, 0, 1, true, true))
            {
                Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                    this, player->getID(), listDir)));

                SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                    boost::bind(&Game::playerUseBattleWindow, this, playerId, pos, stackpos, creatureId, spriteId, isHotkey));

                player->setNextWalkActionTask(task);
                return true;
            }

            ret = RET_THEREISNOWAY;
        }

        player->sendCancelMessage(ret);
        return false;
    }

    if(isHotkey)
        showHotkeyUseMessage(player, item);

    if(!player->canDoAction())
    {
        SchedulerTask* task = createSchedulerTask(player->getNextActionTime(),
            boost::bind(&Game::playerUseBattleWindow, this, playerId, pos, stackpos, creatureId, spriteId, isHotkey));
        player->setNextActionTask(task);
        return false;
    }

    player->setIdleTime(0);
    player->setNextActionTask(NULL);
    return g_actions->useItemEx(player, pos, creature->getPosition(),
        creature->getParent()->__getIndexOfThing(creature), item, isHotkey, creatureId);
}

bool Game::playerCloseContainer(uint32_t playerId, uint8_t cid)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->closeContainer(cid);
    player->sendCloseContainer(cid);
    return true;
}

bool Game::playerMoveUpContainer(uint32_t playerId, uint8_t cid)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Container* container = player->getContainer(cid);
    if(!container)
        return false;

    Container* parentContainer = dynamic_cast<Container*>(container->getParent());
    if(!parentContainer)
        return false;

    player->addContainer(cid, parentContainer);
    player->sendContainer(cid, parentContainer, (dynamic_cast<const Container*>(parentContainer->getParent()) != NULL));
    return true;
}

bool Game::playerUpdateTile(uint32_t playerId, const Position& pos)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(!player->canSee(pos))
        return false;

    if(Tile* tile = getTile(pos))
        player->sendUpdateTile(tile, pos);

    return true;
}

bool Game::playerUpdateContainer(uint32_t playerId, uint8_t cid)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Container* container = player->getContainer(cid);
    if(!container)
        return false;

    player->sendContainer(cid, container, (dynamic_cast<const Container*>(container->getParent()) != NULL));
    return true;
}

bool Game::playerRotateItem(uint32_t playerId, const Position& pos, int16_t stackpos, const uint16_t spriteId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Thing* thing = internalGetThing(player, pos, stackpos);
    if(!thing)
        return false;

    Item* item = thing->getItem();
    if(!item || item->getClientID() != spriteId || !item->isRoteable() || (item->isLoadedFromMap() &&
        (item->getUniqueId() || (item->getActionId() && item->getContainer()))))
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(pos.x != 0xFFFF && !Position::areInRange<1,1,0>(pos, player->getPosition()))
    {
        if(player->getNoMove())
        {
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        std::list<Direction> listDir;
        if(getPathToEx(player, pos, listDir, 0, 1, true, true))
        {
            Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                this, player->getID(), listDir)));

            SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                boost::bind(&Game::playerRotateItem, this, playerId, pos, stackpos, spriteId));

            player->setNextWalkActionTask(task);
            return true;
        }

        player->sendCancelMessage(RET_THEREISNOWAY);
        return false;
    }

    uint16_t newId = Item::items[item->getID()].rotateTo;
    if(newId)
        transformItem(item, newId);

    return true;
}

bool Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string& text)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    uint16_t maxTextLength = 0;
    uint32_t internalWindowTextId = 0;

    Item* writeItem = player->getWriteItem(internalWindowTextId, maxTextLength);
    if(text.length() > maxTextLength || windowTextId != internalWindowTextId)
        return false;

    if(!writeItem || writeItem->isRemoved())
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Cylinder* topParent = writeItem->getTopParent();
    Player* owner = dynamic_cast<Player*>(topParent);
    if(owner && owner != player)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(!Position::areInRange<1,1,0>(writeItem->getPosition(), player->getPosition()))
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    bool deny = false;
    CreatureEventList textEditEvents = player->getCreatureEvents(CREATURE_EVENT_TEXTEDIT);
    for(CreatureEventList::iterator it = textEditEvents.begin(); it != textEditEvents.end(); ++it)
    {
        if(!(*it)->executeTextEdit(player, writeItem, text))
            deny = true;
    }

    player->setWriteItem(NULL);
    if((Container*)writeItem->getParent() == &player->transferContainer)
    {
        player->transferContainer.setParent(NULL);
        player->transferContainer.__removeThing(writeItem, writeItem->getItemCount());

        freeThing(writeItem);
        return true;
    }

    if(deny)
        return false;

    if(!text.empty())
    {
        if(writeItem->getText() != text)
        {
            writeItem->setText(text);
            writeItem->setWriter(player->getName());
            writeItem->setDate(std::time(NULL));
        }
    }
    else
    {
        writeItem->resetText();
        writeItem->resetWriter();
        writeItem->resetDate();
    }

    uint16_t newId = Item::items[writeItem->getID()].writeOnceItemId;
    if(newId)
        transformItem(writeItem, newId);

    return true;
}

bool Game::playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string& text)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    uint32_t internalWindowTextId = 0, internalListId = 0;
    House* house = player->getEditHouse(internalWindowTextId, internalListId);
    if(!house || internalWindowTextId != windowTextId || listId)
        return true;

    bool deny = false;
    CreatureEventList houseEditEvents = player->getCreatureEvents(CREATURE_EVENT_HOUSEEDIT);
    for(CreatureEventList::iterator it = houseEditEvents.begin(); it != houseEditEvents.end(); ++it)
    {
        if(!(*it)->executeHouseEdit(player, house->getId(), internalListId, text))
            deny = true;
    }

    player->setEditHouse(NULL);
    if(deny)
        return false;

    house->setAccessList(internalListId, text);
    return true;
}

bool Game::playerRequestTrade(uint32_t playerId, const Position& pos, int16_t stackpos,
    uint32_t tradePlayerId, uint16_t spriteId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 8))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditiontrade = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 8))
        player->addCondition(conditiontrade);

    Player* tradePartner = getPlayerByID(tradePlayerId);
    if(!tradePartner || tradePartner == player)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(!Position::areInRange<2,2,0>(tradePartner->getPosition(), player->getPosition()))
    {
        player->sendCancelMessage(RET_DESTINATIONOUTOFREACH);
        return false;
    }

    if(!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())
        && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_CANNOTTHROW);
        return false;
    }

    Item* tradeItem = dynamic_cast<Item*>(internalGetThing(player, pos, stackpos, spriteId, STACKPOS_USE));
    if(!tradeItem || tradeItem->getClientID() != spriteId || !tradeItem->isPickupable() || (tradeItem->isLoadedFromMap()
        && (tradeItem->getUniqueId() || (tradeItem->getActionId() && tradeItem->getContainer()))))
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    if(player->getPosition().z > tradeItem->getPosition().z)
    {
        player->sendCancelMessage(RET_FIRSTGOUPSTAIRS);
        return false;
    }

    if(player->getPosition().z < tradeItem->getPosition().z)
    {
        player->sendCancelMessage(RET_FIRSTGODOWNSTAIRS);
        return false;
    }

    HouseTile* houseTile = dynamic_cast<HouseTile*>(tradeItem->getParent());
    if(houseTile && houseTile->getHouse() && !houseTile->getHouse()->isInvited(player))
    {
        player->sendCancelMessage(RET_PLAYERISNOTINVITED);
        return false;
    }

    if(!Position::areInRange<1,1,0>(tradeItem->getPosition(), player->getPosition()))
    {
        if(player->getNoMove())
        {
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        std::list<Direction> listDir;
        if(getPathToEx(player, pos, listDir, 0, 1, true, true))
        {
            Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk,
                this, player->getID(), listDir)));

            SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()),
                boost::bind(&Game::playerRequestTrade, this, playerId, pos, stackpos, tradePlayerId, spriteId));

            player->setNextWalkActionTask(task);
            return true;
        }

        player->sendCancelMessage(RET_THEREISNOWAY);
        return false;
    }

    const Container* container = NULL;
    for(std::map<Item*, uint32_t>::const_iterator it = tradeItems.begin(); it != tradeItems.end(); ++it)
    {
        if(tradeItem == it->first ||
            ((container = dynamic_cast<const Container*>(tradeItem)) && container->isHoldingItem(it->first)) ||
            ((container = dynamic_cast<const Container*>(it->first)) && container->isHoldingItem(tradeItem)))
        {
            player->sendCancelMessage(RET_YOUAREALREADYTRADING);
            return false;
        }
    }

    Container* tradeContainer = tradeItem->getContainer();
    if(tradeContainer && tradeContainer->getItemHoldingCount() + 1 > (uint32_t)g_config.getNumber(ConfigManager::TRADE_LIMIT))
    {
        player->sendCancelMessage(RET_YOUCANONLYTRADEUPTOX);
        return false;
    }

    bool deny = false;
    CreatureEventList tradeEvents = player->getCreatureEvents(CREATURE_EVENT_TRADE_REQUEST);
    for(CreatureEventList::iterator it = tradeEvents.begin(); it != tradeEvents.end(); ++it)
    {
        if(!(*it)->executeTradeRequest(player, tradePartner, tradeItem))
            deny = true;
    }

    if(deny)
        return false;

    return internalStartTrade(player, tradePartner, tradeItem);
}

bool Game::internalStartTrade(Player* player, Player* tradePartner, Item* tradeItem)
{
    if(player->tradeState != TRADE_NONE && !(player->tradeState == TRADE_ACKNOWLEDGE && player->tradePartner == tradePartner))
    {
        player->sendCancelMessage(RET_YOUAREALREADYTRADING);
        return false;
    }
    else if(tradePartner->tradeState != TRADE_NONE && tradePartner->tradePartner != player)
    {
        player->sendCancelMessage(RET_THISPLAYERISALREADYTRADING);
        return false;
    }

    player->tradePartner = tradePartner;
    player->tradeItem = tradeItem;
    player->tradeState = TRADE_INITIATED;

    tradeItem->addRef();
    tradeItems[tradeItem] = player->getID();

    player->sendTradeItemRequest(player, tradeItem, true);
    if(tradePartner->tradeState == TRADE_NONE)
    {
        char buffer[100];
        sprintf(buffer, "%s wants to trade with you", player->getName().c_str());
        tradePartner->sendTextMessage(MSG_EVENT_ADVANCE, buffer);

        tradePartner->tradeState = TRADE_ACKNOWLEDGE;
        tradePartner->tradePartner = player;
    }
    else
    {
        Item* counterItem = tradePartner->tradeItem;
        player->sendTradeItemRequest(tradePartner, counterItem, false);
        tradePartner->sendTradeItemRequest(player, tradeItem, false);
    }

    return true;
}

bool Game::playerAcceptTrade(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || (player->getTradeState() != TRADE_ACKNOWLEDGE
        && player->getTradeState() != TRADE_INITIATED))
        return false;

    Player* tradePartner = player->tradePartner;
    if(!tradePartner)
        return false;

    if(!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())
        && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere))
    {
        player->sendCancelMessage(RET_CREATUREISNOTREACHABLE);
        return false;
    }

    player->setTradeState(TRADE_ACCEPT);
    if(tradePartner->getTradeState() != TRADE_ACCEPT)
        return false;

    Item* tradeItem1 = player->tradeItem;
    Item* tradeItem2 = tradePartner->tradeItem;

    bool deny = false;
    CreatureEventList tradeEvents = player->getCreatureEvents(CREATURE_EVENT_TRADE_ACCEPT);
    for(CreatureEventList::iterator it = tradeEvents.begin(); it != tradeEvents.end(); ++it)
    {
        if(!(*it)->executeTradeAccept(player, tradePartner, tradeItem1, tradeItem2))
            deny = true;
    }

    if(deny)
        return false;

    player->setTradeState(TRADE_TRANSFER);
    tradePartner->setTradeState(TRADE_TRANSFER);

    std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradeItem1);
    if(it != tradeItems.end())
    {
        freeThing(it->first);
        tradeItems.erase(it);
    }

    it = tradeItems.find(tradeItem2);
    if(it != tradeItems.end())
    {
        freeThing(it->first);
        tradeItems.erase(it);
    }

    ReturnValue ret1 = internalAddItem(player, tradePartner, tradeItem1, INDEX_WHEREEVER, FLAG_IGNOREAUTOSTACK, true);
    ReturnValue ret2 = internalAddItem(tradePartner, player, tradeItem2, INDEX_WHEREEVER, FLAG_IGNOREAUTOSTACK, true);

    bool success = false;
    if(ret1 == RET_NOERROR && ret2 == RET_NOERROR)
    {
        ret1 = internalRemoveItem(tradePartner, tradeItem1, tradeItem1->getItemCount(), true);
        ret2 = internalRemoveItem(player, tradeItem2, tradeItem2->getItemCount(), true);
        if(ret1 == RET_NOERROR && ret2 == RET_NOERROR)
        {
            internalMoveItem(player, tradeItem1->getParent(), tradePartner, INDEX_WHEREEVER,
                tradeItem1, tradeItem1->getItemCount(), NULL, FLAG_IGNOREAUTOSTACK);
            internalMoveItem(tradePartner, tradeItem2->getParent(), player, INDEX_WHEREEVER,
                tradeItem2, tradeItem2->getItemCount(), NULL, FLAG_IGNOREAUTOSTACK);

            tradeItem1->onTradeEvent(ON_TRADE_TRANSFER, tradePartner, player);
            tradeItem2->onTradeEvent(ON_TRADE_TRANSFER, player, tradePartner);
            success = true;
        }
    }

    if(!success)
    {
        std::string error;
        if(tradeItem2)
        {
            error = getTradeErrorDescription(ret1, tradeItem1);
            tradePartner->sendTextMessage(MSG_EVENT_ADVANCE, error);
            tradeItem2->onTradeEvent(ON_TRADE_CANCEL, tradePartner, player);
        }

        if(tradeItem1)
        {
            error = getTradeErrorDescription(ret2, tradeItem2);
            player->sendTextMessage(MSG_EVENT_ADVANCE, error);
            tradeItem1->onTradeEvent(ON_TRADE_CANCEL, player, tradePartner);
        }
    }

    player->setTradeState(TRADE_NONE);
    player->tradeItem = NULL;
    player->tradePartner = NULL;

    tradePartner->setTradeState(TRADE_NONE);
    tradePartner->tradeItem = NULL;
    tradePartner->tradePartner = NULL;

    player->sendTradeClose();
    tradePartner->sendTradeClose();
    return success;
}

std::string Game::getTradeErrorDescription(ReturnValue ret, Item* item)
{
    if(!item)
        return std::string();

    std::stringstream ss;
    if(ret == RET_NOTENOUGHCAPACITY)
    {
        ss << "You do not have enough capacity to carry";
        if(item->isStackable() && item->getItemCount() > 1)
            ss << " these objects.";
        else
            ss << " this object." ;

        ss << std::endl << " " << item->getWeightDescription();
    }
    else if(ret == RET_NOTENOUGHROOM || ret == RET_CONTAINERNOTENOUGHROOM)
    {
        ss << "You do not have enough room to carry";
        if(item->isStackable() && item->getItemCount() > 1)
            ss << " these objects.";
        else
            ss << " this object.";
    }
    else
        ss << "Trade could not be completed.";

    return ss.str().c_str();
}

bool Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, int32_t index)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Player* tradePartner = player->tradePartner;
    if(!tradePartner)
        return false;

    Item* tradeItem = NULL;
    if(lookAtCounterOffer)
        tradeItem = tradePartner->getTradeItem();
    else
        tradeItem = player->getTradeItem();

    if(!tradeItem)
        return false;

    std::stringstream ss;
    ss << "You see ";

    int32_t lookDistance = std::max(std::abs(player->getPosition().x - tradeItem->getPosition().x),
        std::abs(player->getPosition().y - tradeItem->getPosition().y));
    if(!index)
    {
        ss << tradeItem->getDescription(lookDistance);
        if(player->hasCustomFlag(PlayerCustomFlag_CanSeeItemDetails))
        {
            ss << std::endl << "ItemID: [" << tradeItem->getID() << "]";
            if(tradeItem->getActionId() > 0)
                ss << ", ActionID: [" << tradeItem->getActionId() << "]";

            if(tradeItem->getUniqueId() > 0)
                ss << ", UniqueID: [" << tradeItem->getUniqueId() << "]";

            ss << ".";
            const ItemType& it = Item::items[tradeItem->getID()];
            if(it.transformEquipTo)
                ss << std::endl << "TransformTo (onEquip): [" << it.transformEquipTo << "]";
            else if(it.transformDeEquipTo)
                ss << std::endl << "TransformTo (onDeEquip): [" << it.transformDeEquipTo << "]";
            else if(it.decayTo != -1)
                ss << std::endl << "DecayTo: [" << it.decayTo << "]";
        }

        player->sendTextMessage(MSG_INFO_DESCR, ss.str());
        return false;
    }

    Container* tradeContainer = tradeItem->getContainer();
    if(!tradeContainer || index > (int32_t)tradeContainer->getItemHoldingCount())
        return false;

    std::list<const Container*> listContainer;
    listContainer.push_back(tradeContainer);
    ItemList::const_iterator it;

    Container* tmpContainer = NULL;
    while(listContainer.size() > 0)
    {
        const Container* container = listContainer.front();
        listContainer.pop_front();
        for(it = container->getItems(); it != container->getEnd(); ++it)
        {
            if((tmpContainer = (*it)->getContainer()))
                listContainer.push_back(tmpContainer);

            --index;
            if(index)
                continue;

            ss << (*it)->getDescription(lookDistance);
            if(player->hasCustomFlag(PlayerCustomFlag_CanSeeItemDetails))
            {
                ss << std::endl << "ItemID: [" << (*it)->getID() << "]";
                if((*it)->getActionId() > 0)
                    ss << ", ActionID: [" << (*it)->getActionId() << "]";

                if((*it)->getUniqueId() > 0)
                    ss << ", UniqueID: [" << (*it)->getUniqueId() << "]";

                ss << ".";
                const ItemType& iit = Item::items[(*it)->getID()];
                if(iit.transformEquipTo)
                    ss << std::endl << "TransformTo: [" << iit.transformEquipTo << "] (onEquip).";
                else if(iit.transformDeEquipTo)
                    ss << std::endl << "TransformTo: [" << iit.transformDeEquipTo << "] (onDeEquip).";
                else if(iit.decayTo != -1)
                    ss << std::endl << "DecayTo: [" << iit.decayTo << "].";
            }

            player->sendTextMessage(MSG_INFO_DESCR, ss.str());
            return true;
        }
    }

    return false;
}

bool Game::playerCloseTrade(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    return internalCloseTrade(player);
}

bool Game::internalCloseTrade(Player* player)
{
    Player* tradePartner = player->tradePartner;
    if((tradePartner && tradePartner->getTradeState() == TRADE_TRANSFER) || player->getTradeState() == TRADE_TRANSFER)
    {
        std::clog << "[Warning - Game::internalCloseTrade] TradeState == TRADE_TRANSFER, " <<
            player->getName() << " " << player->getTradeState() << ", " <<
            tradePartner->getName() << " " << tradePartner->getTradeState() << std::endl;
        return true;
    }

    std::vector<Item*>::iterator it;
    if(player->getTradeItem())
    {
        std::map<Item*, uint32_t>::iterator it = tradeItems.find(player->getTradeItem());
        if(it != tradeItems.end())
        {
            freeThing(it->first);
            tradeItems.erase(it);
        }

        player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player, tradePartner);
        player->tradeItem = NULL;
    }

    player->setTradeState(TRADE_NONE);
    player->tradePartner = NULL;

    player->sendTextMessage(MSG_STATUS_SMALL, "Trade cancelled.");
    player->sendTradeClose();
    if(tradePartner)
    {
        if(tradePartner->getTradeItem())
        {
            std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradePartner->getTradeItem());
            if(it != tradeItems.end())
            {
                freeThing(it->first);
                tradeItems.erase(it);
            }

            tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner, player);
            tradePartner->tradeItem = NULL;
        }

        tradePartner->setTradeState(TRADE_NONE);
        tradePartner->tradePartner = NULL;

        tradePartner->sendTextMessage(MSG_STATUS_SMALL, "Trade cancelled.");
        tradePartner->sendTradeClose();
    }

    return true;
}

bool Game::playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount,
    bool ignoreCap/* = false*/, bool inBackpacks/* = false*/)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    int32_t onBuy, onSell;
    Npc* merchant = player->getShopOwner(onBuy, onSell);
    if(!merchant)
        return false;

    const ItemType& it = Item::items.getItemIdByClientId(spriteId);
    if(!it.id)
        return false;

    uint8_t subType = count;
    if(it.isFluidContainer() && count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t)))
        subType = reverseFluidMap[count];
    else if(!it.hasSubType())
        subType = 0;

    if(!player->canShopItem(it.id, subType, SHOPEVENT_BUY))
        return false;

    merchant->onPlayerTrade(player, SHOPEVENT_BUY, onBuy, it.id, subType, amount, ignoreCap, inBackpacks);
    return true;
}

bool Game::playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, bool ignoreEquipped)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    int32_t onBuy, onSell;
    Npc* merchant = player->getShopOwner(onBuy, onSell);
    if(!merchant)
        return false;

    const ItemType& it = Item::items.getItemIdByClientId(spriteId);
    if(!it.id)
        return false;

    uint8_t subType = count;
    if(it.isFluidContainer() && count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t)))
        subType = reverseFluidMap[count];
    else if(!it.hasSubType())
        subType = 0;

    if(!player->canShopItem(it.id, subType, SHOPEVENT_SELL))
        return false;

    merchant->onPlayerTrade(player, SHOPEVENT_SELL, onSell, it.id, subType, amount, ignoreEquipped);
    return true;
}

bool Game::playerCloseShop(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(player == NULL || player->isRemoved())
        return false;

    player->closeShopWindow();
    return true;
}

bool Game::playerLookInShop(uint32_t playerId, uint16_t spriteId, uint8_t count)
{
    Player* player = getPlayerByID(playerId);
    if(player == NULL || player->isRemoved())
        return false;

    const ItemType& it = Item::items.getItemIdByClientId(spriteId);
    if(!it.id)
        return false;

    int32_t subType = count;
    if(it.isSplash() || it.isFluidContainer())
    {
        if(subType == 3)
            subType = 11;
        else if(count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t)))
            subType = reverseFluidMap[count];
    }

    std::stringstream ss;
    ss << "You see " << Item::getDescription(it, 1, NULL, subType);
    if(player->hasCustomFlag(PlayerCustomFlag_CanSeeItemDetails))
        ss << std::endl << "ItemID: [" << it.id << "].";

    player->sendTextMessage(MSG_INFO_DESCR, ss.str());
    return true;
}

bool Game::playerLookAt(uint32_t playerId, const Position& pos, uint16_t spriteId, int16_t stackpos)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 7))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionlook = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 7))
        player->addCondition(conditionlook);

    Thing* thing = internalGetThing(player, pos, stackpos, spriteId, STACKPOS_LOOK);
    if(!thing)
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Position thingPos = pos;
    if(pos.x == 0xFFFF)
        thingPos = thing->getPosition();

    if(!player->canSee(thingPos))
    {
        player->sendCancelMessage(RET_NOTPOSSIBLE);
        return false;
    }

    Position playerPos = player->getPosition();
    int32_t lookDistance = -1;
    if(thing != player)
    {
        lookDistance = std::max(std::abs(playerPos.x - thingPos.x), std::abs(playerPos.y - thingPos.y));
        if(playerPos.z != thingPos.z)
            lookDistance += 15;
    }

    bool deny = false;
    CreatureEventList lookEvents = player->getCreatureEvents(CREATURE_EVENT_LOOK);
    for(CreatureEventList::iterator it = lookEvents.begin(); it != lookEvents.end(); ++it)
    {
        if(!(*it)->executeLook(player, thing, thingPos, stackpos, lookDistance))
            deny = true;
    }

    if(deny)
        return false;

    std::stringstream ss;
    ss << "You see " << thing->getDescription(lookDistance);
    if(player->hasCustomFlag(PlayerCustomFlag_CanSeeItemDetails))
    {
        if(Item* item = thing->getItem())
        {
            ss << std::endl << "ItemID: [" << item->getID() << "]";
            if(item->getActionId() > 0)
                ss << ", ActionID: [" << item->getActionId() << "]";

            if(item->getUniqueId() > 0)
                ss << ", UniqueID: [" << item->getUniqueId() << "]";

            ss << ".";
            const ItemType& it = Item::items[item->getID()];
            if(it.transformEquipTo)
                ss << std::endl << "TransformTo: [" << it.transformEquipTo << "] (onEquip).";
            else if(it.transformDeEquipTo)
                ss << std::endl << "TransformTo: [" << it.transformDeEquipTo << "] (onDeEquip).";

            if(it.transformUseTo)
                ss << std::endl << "TransformTo: [" << it.transformUseTo << "] (onUse).";

            if(it.decayTo != -1)
                ss << std::endl << "DecayTo: [" << it.decayTo << "].";
        }
    }

    if(player->hasCustomFlag(PlayerCustomFlag_CanSeeCreatureDetails))
    {
        if(const Creature* creature = thing->getCreature())
        {
            if(!player->hasFlag(PlayerFlag_HideHealth))
            {
                ss << std::endl << "Health: [" << creature->getHealth() << " / " << creature->getMaxHealth() << "]";
                if(creature->getMaxMana() > 0)
                    ss << ", Mana: [" << creature->getMana() << " / " << creature->getMaxMana() << "]";

                ss << ".";
            }

            if(const Player* target = creature->getPlayer())
            {
                ss << std::endl << "IP: " << convertIPAddress(target->getIP());
#if CLIENT_VERSION_MIN != CLIENT_VERSION_MAX
                ss << ", Client: " << target->getClientVersion();
#endif
                ss << ".";
            }

            if(creature->isGhost())
                ss << std::endl << "* Ghost mode *";
        }
    }

    if(player->hasCustomFlag(PlayerCustomFlag_CanSeePosition))
    {
        ss << std::endl << "Position: [X: " << thingPos.x << "] [Y: " << thingPos.y << "] [Z: " << thingPos.z << "]";
        if(Tile* tile = getTile(thingPos))
        {
            if(House* house = tile->getHouse())
                ss << " [House: " << house->getId() << "]";
        }

        ss << ".";
    }

    player->sendTextMessage(MSG_INFO_DESCR, ss.str());
    return true;
}

bool Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Creature* creature = getCreatureByID(creatureId);
    if(!creature || creature->isRemoved())
        return false;

    if(!player->canSeeCreature(creature))
        return false;

    const Position& creaturePos = creature->getPosition();
    if(!player->canSee(creaturePos))
        return false;

    int32_t lookDistance;
    if(creature != player)
    {
        const Position& playerPos = player->getPosition();
        lookDistance = std::max(std::abs(playerPos.x - creaturePos.x), std::abs(playerPos.y - creaturePos.y));
        if(playerPos.z != creaturePos.z)
            lookDistance += 15;
    }
    else
        lookDistance = -1;

    std::ostringstream ss;
    ss << "You see " << creature->getDescription(lookDistance);
    if(player->hasCustomFlag(PlayerCustomFlag_CanSeeCreatureDetails))
    {
        if(!player->hasFlag(PlayerFlag_HideHealth))
        {
            ss << std::endl << "Health: [" << creature->getHealth() << " / " << creature->getMaxHealth() << "]";
            if(creature->getMaxMana() > 0)
                ss << ", Mana: [" << creature->getMana() << " / " << creature->getMaxMana() << "]";

            ss << ".";
        }

        if(const Player* target = creature->getPlayer())
        {
            ss << std::endl << "IP: " << convertIPAddress(target->getIP());
#if CLIENT_VERSION_MIN != CLIENT_VERSION_MAX
            ss << ", Client: " << target->getClientVersion();
#endif
            ss << ".";
        }

        if(creature->isGhost())
            ss << std::endl << "* Ghost mode *";
    }

    if(player->hasCustomFlag(PlayerCustomFlag_CanSeePosition))
    {
        ss << std::endl << "Position: [X: " << creaturePos.x << "] [Y: " << creaturePos.y << "] [Z: " << creaturePos.z << "]";
        ss << ".";
    }

    player->sendTextMessage(MSG_INFO_DESCR, ss.str());
    return true;
}

bool Game::playerQuests(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->sendQuests();
    return true;
}

bool Game::playerQuestInfo(uint32_t playerId, uint16_t questId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Quest* quest = Quests::getInstance()->getQuestById(questId);
    if(!quest)
        return false;

    player->sendQuestInfo(quest);
    return true;
}

bool Game::playerCancelAttackAndFollow(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    playerSetAttackedCreature(playerId, 0);
    playerFollowCreature(playerId, 0);

    player->stopWalk();
    return true;
}

bool Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->getAttackedCreature() && !creatureId)
    {
        player->setAttackedCreature(NULL);
        player->sendCancelTarget();
        return true;
    }

    Creature* attackCreature = getCreatureByID(creatureId);
    if(!attackCreature)
    {
        player->setAttackedCreature(NULL);
        player->sendCancelTarget();
        return false;
    }

    ReturnValue ret = Combat::canTargetCreature(player, attackCreature);
    if(ret != RET_NOERROR)
    {
        if(ret != RET_NEEDEXCHANGE)
            player->sendCancelMessage(ret);

        player->sendCancelTarget();
        player->setAttackedCreature(NULL);
        return false;
    }

    player->setAttackedCreature(attackCreature);
    Dispatcher::getInstance().addTask(createTask(boost::bind(
        &Game::updateCreatureWalk, this, player->getID())));
    return true;
}

bool Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    Creature* followCreature = NULL;
    if(creatureId)
    {
        if(player->getNoMove())
        {
            playerCancelAttackAndFollow(playerId);
            player->sendCancelMessage(RET_NOTPOSSIBLE);
            return false;
        }

        followCreature = getCreatureByID(creatureId);
    }

    player->setAttackedCreature(NULL);
    Dispatcher::getInstance().addTask(createTask(boost::bind(
        &Game::updateCreatureWalk, this, player->getID())));
    return player->setFollowCreature(followCreature);
}

bool Game::playerSetFightModes(uint32_t playerId, fightMode_t fightMode, chaseMode_t chaseMode, secureMode_t secureMode)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->setFightMode(fightMode);
    player->setChaseMode(chaseMode);
    player->setSecureMode(secureMode);

    player->setLastAttack(OTSYS_TIME());
    return true;
}

bool Game::playerRequestAddVip(uint32_t playerId, const std::string& vipName)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->canDoExAction())
        return false;

    uint32_t guid;
    bool specialVip;
    std::string name = vipName;

    player->setNextExAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::CUSTOM_ACTIONS_DELAY_INTERVAL) - 10);
    if(!IOLoginData::getInstance()->getGuidByNameEx(guid, specialVip, name))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "A player with that name does not exist.");
        return false;
    }

    if(specialVip && !player->hasFlag(PlayerFlag_SpecialVIP))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You cannot add this player.");
        return false;
    }

    if(player->hasCondition(CONDITION_EXHAUST, 1))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "Please wait few seconds before adding new player to your vip list.");
        return false;
    }

    bool online = false;
    if(Player* target = getPlayerByName(name))
        online = player->canSeeCreature(target);

    if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 3000, 0, false, 1))
        player->addCondition(condition);

    return player->addVIP(guid, name, online);
}

bool Game::playerRequestRemoveVip(uint32_t playerId, uint32_t guid)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 1))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "Please wait few seconds before deleting next player from your vip list.");
        return false;
    }

    if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 3000, 0, false, 1))
        player->addCondition(condition);

    player->removeVIP(guid);
    return true;
}

bool Game::playerTurn(uint32_t playerId, Direction dir)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(internalCreatureTurn(player, dir))
    {
        player->setIdleTime(0);
        return true;
    }

    if(player->getDirection() != dir || !player->hasCustomFlag(PlayerCustomFlag_CanTurnhop))
        return false;

    Position pos = getNextPosition(dir, player->getPosition());
    Tile* tile = map->getTile(pos);
    if(!tile || !tile->ground)
        return false;

    player->setIdleTime(0);
    ReturnValue ret = tile->__queryAdd(0, player, 1, FLAG_IGNOREBLOCKITEM);
    if(ret != RET_NOTENOUGHROOM && (ret != RET_NOTPOSSIBLE || player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere)))
        return (internalTeleport(player, pos, false, FLAG_NOLIMIT, false) != RET_NOERROR);

    player->sendCancelMessage(ret);
    return false;
}

bool Game::playerRequestOutfit(uint32_t playerId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 4))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionoutfit = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 4))
        player->addCondition(conditionoutfit);

    player->sendOutfitWindow();
    return true;
}

bool Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 4))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionoutfit = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 1000, 0, false, 4))
        player->addCondition(conditionoutfit);

    if(!player->changeOutfit(outfit, true))
        return false;

    player->setIdleTime(0);
    if(!player->hasCondition(CONDITION_OUTFIT, -1))
        internalCreatureChangeOutfit(player, outfit);

    return true;
}

bool Game::playerSay(uint32_t playerId, uint16_t channelId, MessageClasses type, const std::string& receiver, const std::string& text)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    player->setIdleTime(0);

    int32_t muted = 0;
    bool mute = player->isMuted(channelId, type, muted);
    if(muted && mute)
    {
        if(muted > 0)
        {
            char buffer[75];
            sprintf(buffer, "You are still muted for %d seconds.", muted);
            player->sendTextMessage(MSG_STATUS_SMALL, buffer);
        }
        else
            player->sendTextMessage(MSG_STATUS_SMALL, "You are muted permanently.");

        return false;
    }

    if(player->isAccountManager())
    {
        if(mute)
            player->removeMessageBuffer();

        return internalCreatureSay(player, MSG_SPEAK_SAY, text, false);
    }

    if(g_talkActions->onPlayerSay(player, type == MSG_SPEAK_SAY ? (unsigned)CHANNEL_DEFAULT : channelId, text, false))
        return true;

    ReturnValue ret = RET_NOERROR;
    if(!muted)
    {
        ret = g_spells->onPlayerSay(player, text);
        if(ret == RET_NOERROR || (ret == RET_NEEDEXCHANGE &&
            !g_config.getBool(ConfigManager::BUFFER_SPELL_FAILURE)))
            return true;
    }

    if(mute && type != MSG_NPC_TO)
        player->removeMessageBuffer();

    if(ret == RET_NEEDEXCHANGE)
        return true;

    uint32_t statementId = 0;
    if(g_config.getBool(ConfigManager::SAVE_STATEMENT))
        IOLoginData::getInstance()->playerStatement(player, channelId, text, statementId);

    switch(type)
    {
        case MSG_SPEAK_SAY:
            return internalCreatureSay(player, MSG_SPEAK_SAY, text, false, NULL, NULL, statementId);
        case MSG_SPEAK_WHISPER:
            return playerWhisper(player, text, statementId);
        case MSG_SPEAK_YELL:
            return playerYell(player, text, statementId);
        case MSG_PRIVATE:
        case MSG_GAMEMASTER_PRIVATE:
        case MSG_RVR_ANSWER:
            return playerSpeakTo(player, type, receiver, text, statementId);
        case MSG_CHANNEL:
        case MSG_CHANNEL_HIGHLIGHT:
        case MSG_GAMEMASTER_CHANNEL:
        case MSG_GAMEMASTER_ANONYMOUS:
        {
            if(playerSpeakToChannel(player, type, text, channelId, statementId))
                return true;

            return internalCreatureSay(player, MSG_SPEAK_SAY, text, false, NULL, NULL, statementId);
        }
        case MSG_NPC_TO:
            return playerSpeakToNpc(player, text);
        case MSG_GAMEMASTER_BROADCAST:
            return playerBroadcastMessage(player, MSG_GAMEMASTER_BROADCAST, text, statementId);

        default:
            break;
    }

    return false;
}

bool Game::playerWhisper(Player* player, const std::string& text, uint32_t statementId)
{
    SpectatorVec list;
    getSpectators(list, player->getPosition(), false, false, 1, 1);
    internalCreatureSay(player, MSG_SPEAK_WHISPER, text, false, &list, NULL, statementId);
    return true;
}

bool Game::playerYell(Player* player, const std::string& text, uint32_t statementId)
{
    if(player->getLevel() <= 1 && !player->hasFlag(PlayerFlag_CannotBeMuted))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You may not yell as long as you are on level 1.");
        return true;
    }

    if(player->hasCondition(CONDITION_MUTED, 1))
    {
        player->sendCancelMessage(RET_YOUAREEXHAUSTED);
        return true;
    }

    if(!player->hasFlag(PlayerFlag_CannotBeMuted))
    {
        if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, 30000, 0, false, 1))
            player->addCondition(condition);
    }

    internalCreatureSay(player, MSG_SPEAK_YELL, asUpperCaseString(text), false, NULL, NULL, statementId);
    return true;
}

bool Game::playerSpeakTo(Player* player, MessageClasses type, const std::string& receiver,
    const std::string& text, uint32_t statementId)
{
    Player* toPlayer = getPlayerByName(receiver);
    if(!toPlayer || toPlayer->isRemoved())
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "A player with this name is not online.");
        return false;
    }

    bool canSee = player->canSeeCreature(toPlayer);
    if(toPlayer->hasCondition(CONDITION_GAMEMASTER, GAMEMASTER_IGNORE)
        && !player->hasFlag(PlayerFlag_CannotBeMuted))
    {
        char buffer[70];
        if(!canSee)
            sprintf(buffer, "A player with this name is not online.");
        else
            sprintf(buffer, "Sorry, %s is currently ignoring private messages.", toPlayer->getName().c_str());

        player->sendTextMessage(MSG_STATUS_SMALL, buffer);
        return false;
    }

    if(type == MSG_GAMEMASTER_PRIVATE && !player->hasFlag(PlayerFlag_CanTalkRedPrivate))
        type = MSG_PRIVATE;

    toPlayer->sendCreatureSay(player, type, text, NULL, statementId);
    toPlayer->onCreatureSay(player, type, text);
    if(!canSee)
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "A player with this name is not online.");
        return false;
    }

    char buffer[80];
    sprintf(buffer, "Message sent to %s.", toPlayer->getName().c_str());
    player->sendTextMessage(MSG_STATUS_SMALL, buffer);
    return true;
}

bool Game::playerSpeakToChannel(Player* player, MessageClasses type, const std::string& text, uint16_t channelId, uint32_t statementId)
{
    if(player->hasCondition(CONDITION_EXHAUST, 6))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionchannel = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 6))
        player->addCondition(conditionchannel);

    switch(type)
    {
        case MSG_CHANNEL:
        {
            if(channelId == CHANNEL_HELP && player->hasFlag(PlayerFlag_TalkOrangeHelpChannel))
                type = MSG_CHANNEL_HIGHLIGHT;

            break;
        }

        case MSG_GAMEMASTER_CHANNEL:
        {
            if(!player->hasFlag(PlayerFlag_CanTalkRedChannel))
                type = MSG_CHANNEL;
            break;
        }

        case MSG_GAMEMASTER_ANONYMOUS:
        {
            if(!player->hasFlag(PlayerFlag_CanTalkRedChannelAnonymous))
                type = MSG_CHANNEL;
            break;
        }

        case MSG_GAMEMASTER_BROADCAST:
        {
            if(!player->hasFlag(PlayerFlag_CanBroadcast))
                type = MSG_CHANNEL;
            break;
        }

        default:
            break;
    }

    if(text.length() < 251)
        return g_chat.talk(player, type, text, channelId, statementId, false);

    player->sendCancelMessage(RET_NOTPOSSIBLE);
    return false;
}

bool Game::playerSpeakToNpc(Player* player, const std::string& text)
{
    SpectatorVec list;
    SpectatorVec::iterator it;
    getSpectators(list, player->getPosition());

    if(player->hasCondition(CONDITION_EXHAUST, 2))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionnpc = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 300, 0, false, 2))
        player->addCondition(conditionnpc);

    //send to npcs only
    Npc* tmpNpc;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpNpc = (*it)->getNpc()))
            (*it)->onCreatureSay(player, MSG_NPC_TO, text);
    }
    return true;
}

bool Game::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight /*= true*/,
    int32_t rangex/* = Map::maxClientViewportX*/, int32_t rangey/* = Map::maxClientViewportY*/)
{
    return map->canThrowObjectTo(fromPos, toPos, checkLineOfSight, rangex, rangey);
}

bool Game::isSightClear(const Position& fromPos, const Position& toPos, bool floorCheck)
{
    return map->isSightClear(fromPos, toPos, floorCheck);
}

bool Game::internalCreatureTurn(Creature* creature, Direction dir)
{
    bool deny = false;
    CreatureEventList directionEvents = creature->getCreatureEvents(CREATURE_EVENT_DIRECTION);
    for(CreatureEventList::iterator it = directionEvents.begin(); it != directionEvents.end(); ++it)
    {
        if(!(*it)->executeDirection(creature, creature->getDirection(), dir) && !deny)
            deny = true;
    }

    if(deny || creature->getDirection() == dir)
        return false;

    creature->setDirection(dir);
    const SpectatorVec& list = getSpectators(creature->getPosition());
    SpectatorVec::const_iterator it;

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureTurn(creature);
    }

    //event method
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureTurn(creature);

    return true;
}

bool Game::internalCreatureSay(Creature* creature, MessageClasses type, const std::string& text,
    bool ghostMode, SpectatorVec* spectators/* = NULL*/, Position* pos/* = NULL*/, uint32_t statementId/* = 0*/)
{
    Player* player = creature->getPlayer();
    if(player && player->isAccountManager() && !ghostMode)
    {
        player->manageAccount(text);
        return true;
    }

    Position destPos = creature->getPosition();
    if(pos)
        destPos = (*pos);

    SpectatorVec list;
    SpectatorVec::const_iterator it;
    if(!spectators || !spectators->size())
    {
        // This somewhat complex construct ensures that the cached SpectatorVec
        // is used if available and if it can be used, else a local vector is
        // used (hopefully the compiler will optimize away the construction of
        // the temporary when it's not used).
        if(type != MSG_SPEAK_YELL && type != MSG_SPEAK_MONSTER_YELL)
            getSpectators(list, destPos, false, false,
                Map::maxClientViewportX, Map::maxClientViewportX,
                Map::maxClientViewportY, Map::maxClientViewportY);
        else
            getSpectators(list, destPos, false, true, 18, 18, 14, 14);
    }
    else
        list = (*spectators);

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if(!(tmpPlayer = (*it)->getPlayer()))
            continue;

        if(!ghostMode || tmpPlayer->canSeeCreature(creature))
            tmpPlayer->sendCreatureSay(creature, type, text, &destPos, statementId);
    }

    //event method
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureSay(creature, type, text, &destPos);

    return true;
}

bool Game::getPathTo(const Creature* creature, const Position& destPos,
    std::list<Direction>& listDir, int32_t maxSearchDist /*= -1*/)
{
    return map->getPathTo(creature, destPos, listDir, maxSearchDist);
}

bool Game::getPathToEx(const Creature* creature, const Position& targetPos,
    std::list<Direction>& dirList, const FindPathParams& fpp)
{
    return map->getPathMatching(creature, dirList, FrozenPathingConditionCall(targetPos), fpp);
}

bool Game::getPathToEx(const Creature* creature, const Position& targetPos, std::list<Direction>& dirList,
    uint32_t minTargetDist, uint32_t maxTargetDist, bool fullPathSearch/* = true*/,
    bool clearSight/* = true*/, int32_t maxSearchDist/* = -1*/)
{
    FindPathParams fpp;
    fpp.fullPathSearch = fullPathSearch;
    fpp.maxSearchDist = maxSearchDist;
    fpp.clearSight = clearSight;
    fpp.minTargetDist = minTargetDist;
    fpp.maxTargetDist = maxTargetDist;
    return getPathToEx(creature, targetPos, dirList, fpp);
}

bool Game::steerCreature(Creature* creature, const Position& position, uint16_t maxNodes/* = 100*/)
{
    FindPathParams fpp;
    fpp.maxClosedNodes = maxNodes;

    fpp.fullPathSearch = true;
    fpp.maxSearchDist = -1;
    fpp.minTargetDist = 0;
    fpp.maxTargetDist = 1;

    std::list<Direction> dirList;
    if(!getPathToEx(creature, position, dirList, fpp))
        return false;

    if(Player* player = creature->getPlayer())
        player->setNextWalkTask(NULL);

    creature->startAutoWalk(dirList);
    return true;
}

void Game::checkCreatureWalk(uint32_t creatureId)
{
    Creature* creature = getCreatureByID(creatureId);
    if(!creature || creature->getHealth() < 1)
        return;

    creature->onWalk();
    cleanup();
}

void Game::updateCreatureWalk(uint32_t creatureId)
{
    Creature* creature = getCreatureByID(creatureId);
    if(creature && creature->getHealth() > 0)
        creature->goToFollowCreature();
}

void Game::checkCreatureAttack(uint32_t creatureId)
{
    Creature* creature = getCreatureByID(creatureId);
    if(creature && creature->getHealth() > 0)
        creature->onAttacking(0);
}

void Game::addCreatureCheck(Creature* creature)
{
    if(creature->isRemoved())
        return;

    creature->checked = true;
    if(creature->checkVector >= 0) //already in a vector, or about to be added
        return;

    toAddCheckCreatureVector.push_back(creature);
    creature->checkVector = random_range(0, EVENT_CREATURECOUNT - 1);
    creature->addRef();
}

void Game::removeCreatureCheck(Creature* creature)
{
    if(creature->checkVector == -1) //not in any vector
        return;

    creature->checked = false;
}

void Game::checkCreatures()
{
    checkCreatureEvent = Scheduler::getInstance().addEvent(createSchedulerTask(
        EVENT_CHECK_CREATURE_INTERVAL, boost::bind(&Game::checkCreatures, this)));
    if(++checkCreatureLastIndex == EVENT_CREATURECOUNT)
        checkCreatureLastIndex = 0;

    std::vector<Creature*>::iterator it;
#ifndef __GROUPED_ATTACKS__
    std::vector<Creature*> creatureVector;
    for(uint16_t i = 0; i < EVENT_CREATURECOUNT; ++i)
    {
        if(i == checkCreatureLastIndex)
            continue;

        creatureVector = checkCreatureVectors;
        for(it = creatureVector.begin(); it != creatureVector.end(); ++it)
        {
            if((*it)->checked && (*it)->getHealth() > 0)
                (*it)->onAttacking(EVENT_CHECK_CREATURE_INTERVAL);
        }
    }
#endif

    for(it = toAddCheckCreatureVector.begin(); it != toAddCheckCreatureVector.end(); ++it)
        checkCreatureVectors[(*it)->checkVector].push_back(*it);

    toAddCheckCreatureVector.clear();
    std::vector<Creature*>& checkCreatureVector = checkCreatureVectors[checkCreatureLastIndex];
    for(it = checkCreatureVector.begin(); it != checkCreatureVector.end(); )
    {
        if((*it)->checked)
        {
            if((*it)->getHealth() > 0 || !(*it)->onDeath())
                (*it)->onThink(EVENT_CREATURE_THINK_INTERVAL);

            ++it;
        }
        else
        {
            (*it)->checkVector = -1;
            freeThing(*it);
            it = checkCreatureVector.erase(it);
        }
    }

    cleanup();
}

void Game::changeSpeed(Creature* creature, int32_t varSpeed)
{
    if(!creature)
        return;

    creature->setSpeed(creature->getSpeed() - creature->getBaseSpeed() + varSpeed);
    const SpectatorVec& list = getSpectators(creature->getPosition());
    SpectatorVec::const_iterator it;

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((*it) && (tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendChangeSpeed(creature, creature->getStepSpeed());
    }
}

void Game::internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit, bool forced/* = false*/)
{
    if(!forced)
    {
        bool deny = false;
        CreatureEventList outfitEvents = creature->getCreatureEvents(CREATURE_EVENT_OUTFIT);
        for(CreatureEventList::iterator it = outfitEvents.begin(); it != outfitEvents.end(); ++it)
        {
            if(!(*it)->executeOutfit(creature, creature->getCurrentOutfit(), outfit) && !deny)
                deny = true;
        }

        if(deny || creature->getCurrentOutfit() == outfit)
            return;
    }

    creature->setCurrentOutfit(outfit);
    const SpectatorVec& list = getSpectators(creature->getPosition());
    SpectatorVec::const_iterator it;

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureChangeOutfit(creature, outfit);
    }

    //event method
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureChangeOutfit(creature, outfit);
}

void Game::internalCreatureChangeVisible(Creature* creature, Visible_t visible)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());
    SpectatorVec::const_iterator it;

    //send to client
    Player* tmpPlayer = NULL;
    for(it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureChangeVisible(creature, visible);
    }

    //event method
    for(it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureChangeVisible(creature, visible);
}


void Game::changeLight(const Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()) && !tmpPlayer->hasCustomFlag(PlayerCustomFlag_HasFullLight))
            tmpPlayer->sendCreatureLight(creature);
    }
}

bool Game::combatBlockHit(CombatType_t combatType, Creature* attacker, Creature* target,
    int32_t& healthChange, bool checkDefense, bool checkArmor, bool field/* = false*/, bool element/* = false*/)
{
    if(healthChange > 0)
        return false;

    const Position& targetPos = target->getPosition();
    const SpectatorVec& list = getSpectators(targetPos);
    if(Combat::canDoCombat(attacker, target, true) != RET_NOERROR)
    {
        if(!element)
            addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF, target->isGhost());

        return true;
    }

    int32_t damage = -healthChange;
    BlockType_t blockType = target->blockHit(attacker, combatType,
        damage, checkDefense, checkArmor, !field, field, element);

    healthChange = -damage;
    if(blockType == BLOCK_DEFENSE)
    {
        if(!element)
            addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF);

        return true;
    }

    if(blockType == BLOCK_ARMOR)
    {
        if(!element)
            addMagicEffect(list, targetPos, MAGIC_EFFECT_BLOCKHIT);

        return true;
    }

    if(blockType != BLOCK_IMMUNITY)
        return false;

    if(element)
        return true;

    MagicEffect_t effect = MAGIC_EFFECT_NONE;
    switch(combatType)
    {
        case COMBAT_UNDEFINEDDAMAGE:
            break;

        case COMBAT_ENERGYDAMAGE:
        case COMBAT_FIREDAMAGE:
        case COMBAT_PHYSICALDAMAGE:
        case COMBAT_ICEDAMAGE:
        case COMBAT_DEATHDAMAGE:
        case COMBAT_EARTHDAMAGE:
        case COMBAT_HOLYDAMAGE:
        {
            effect = MAGIC_EFFECT_BLOCKHIT;
            break;
        }

        default:
        {
            effect = MAGIC_EFFECT_POFF;
            break;
        }
    }

    addMagicEffect(list, targetPos, effect);
    return true;
}

bool Game::combatChangeHealth(CombatType_t combatType, Creature* attacker, Creature* target, int32_t healthChange,
    MagicEffect_t hitEffect/* = MAGIC_EFFECT_UNKNOWN*/, Color_t hitColor/* = COLOR_UNKNOWN*/, bool force/* = false*/)
{
    CombatParams params;
    params.effects.hit =  hitEffect;
    params.effects.color = hitColor;

    params.combatType = combatType;
    return combatChangeHealth(params, attacker, target, healthChange, force);
}

bool Game::combatChangeHealth(const CombatParams& params, Creature* attacker, Creature* target, int32_t healthChange, bool force)
{
    const Position& targetPos = target->getPosition();
    if(healthChange > 0)
    {
        if(!force && target->getHealth() <= 0)
            return false;

        bool deny = false;
        CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE);
        for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it)
        {
            if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_HEALTHGAIN, params.combatType, healthChange))
                deny = true;
        }

        if(deny)
            return false;

        int32_t oldHealth = target->getHealth();
        target->gainHealth(attacker, healthChange);
        if(oldHealth != target->getHealth() && g_config.getBool(ConfigManager::SHOW_HEALTH_CHANGE) && !target->isGhost() &&
            (g_config.getBool(ConfigManager::SHOW_HEALTH_CHANGE_MONSTER) || !target->getMonster()))
        {
            const SpectatorVec& list = getSpectators(targetPos);
            if(params.combatType != COMBAT_HEALING)
                addMagicEffect(list, targetPos, MAGIC_EFFECT_WRAPS_BLUE);

            SpectatorVec textList;
            for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
            {
                if(!(*it)->getPlayer())
                    continue;

                if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z)
                    textList.push_back(*it);
            }

            healthChange = (target->getHealth() - oldHealth);
            std::string plural = (healthChange != 1 ? "s." : ".");

            std::stringstream ss;
            char buffer[20];
            sprintf(buffer, "+%d", healthChange);
            addAnimatedText(list, targetPos, COLOR_MAYABLUE, buffer);
            if(!textList.empty())
            {
                if(!attacker)
                    ss << ucfirst(target->getNameDescription()) << " is healed for " << healthChange << " hitpoint" << plural;
                else if(attacker != target)
                    ss << ucfirst(attacker->getNameDescription()) << " heals " << target->getNameDescription() << " for " << healthChange << " hitpoint" << plural;
                else
                {
                    ss << ucfirst(attacker->getNameDescription()) << " heals ";
                    if(Player* attackerPlayer = attacker->getPlayer())
                        ss << (attackerPlayer->getSex(false) == PLAYERSEX_FEMALE ? "herself" : "himself") << " for " << healthChange << " hitpoint" << plural;
                    else
                        ss << "itself for " << healthChange << " hitpoint" << plural;
                }

                addStatsMessage(textList, MSG_HEALED_OTHERS, ss.str(), targetPos);
                ss.str("");
            }

            Player* player = NULL;
            if(attacker && (player = attacker->getPlayer()))
            {
                if(attacker != target)
                    ss << "You healed " << target->getNameDescription() << " for " << healthChange << " hitpoint" << plural;
                else
                    ss << "You healed yourself for " << healthChange << " hitpoint" << plural;

                player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos);
                ss.str("");
            }

            if((player = target->getPlayer()) && attacker != target)
            {
                if(attacker)
                    ss << ucfirst(attacker->getNameDescription()) << " heals you for " << healthChange << " hitpoint" << plural;
                else
                    ss << "You are healed for " << healthChange << " hitpoint" << plural;

                player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos);
            }
        }
    }
    else
    {
        const SpectatorVec& list = getSpectators(targetPos);
        if(target->getHealth() < 1 || Combat::canDoCombat(attacker, target, true) != RET_NOERROR)
        {
            addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF);
            return true;
        }

        int32_t elementDamage = 0;
        if(params.element.damage && params.element.type != COMBAT_NONE)
            elementDamage = -params.element.damage;

        int32_t damage = -healthChange;
        if(damage > 0)
        {
            if(target->hasCondition(CONDITION_MANASHIELD) && params.combatType != COMBAT_UNDEFINEDDAMAGE)
            {
                int32_t manaDamage = std::min(target->getMana(), damage + elementDamage);
                damage = std::max((int32_t)0, damage + elementDamage - manaDamage);

                elementDamage = 0; // TODO: I don't know how it works ;(
                if(manaDamage && combatChangeMana(attacker, target, -manaDamage, params.combatType, true))
                    addMagicEffect(list, targetPos, MAGIC_EFFECT_LOSE_ENERGY);
            }

            damage = std::min(target->getHealth(), damage);
            if(damage > 0)
            {
                bool deny = false;
                CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE);
                for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it)
                {
                    if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_HEALTHLOSS, params.combatType, damage))
                        deny = true;
                }

                if(deny)
                    return false;

                target->drainHealth(attacker, params.combatType, damage);
                if(elementDamage)
                    target->drainHealth(attacker, params.element.type, elementDamage);

                Color_t textColor = COLOR_NONE;
                MagicEffect_t magicEffect = MAGIC_EFFECT_NONE;

                addCreatureHealth(list, target);
                if(params.combatType == COMBAT_PHYSICALDAMAGE)
                {
                    Item* splash = NULL;
                    switch(target->getRace())
                    {
                        case RACE_VENOM:
                            textColor = COLOR_LIGHTGREEN;
                            magicEffect = MAGIC_EFFECT_POISON;
                            splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_GREEN);
                            break;

                        case RACE_BLOOD:
                            textColor = COLOR_RED;
                            magicEffect = MAGIC_EFFECT_DRAW_BLOOD;
                            splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_BLOOD);
                            break;

                        case RACE_UNDEAD:
                            textColor = COLOR_GREY;
                            magicEffect = MAGIC_EFFECT_HIT_AREA;
                            break;

                        case RACE_FIRE:
                            textColor = COLOR_ORANGE;
                            magicEffect = MAGIC_EFFECT_DRAW_BLOOD;
                            break;

                        case RACE_ENERGY:
                            textColor = COLOR_PURPLE;
                            magicEffect = MAGIC_EFFECT_PURPLEENERGY;
                            break;

                        default:
                            break;
                    }

                    if(splash)
                    {
                        internalAddItem(NULL, target->getTile(), splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
                        startDecay(splash);
                    }
                }
                else
                    getCombatDetails(params.combatType, magicEffect, textColor);

                if(params.effects.hit != MAGIC_EFFECT_UNKNOWN)
                    magicEffect = params.effects.hit;

                if(params.effects.color != COLOR_UNKNOWN)
                    textColor = params.effects.color;

                if(textColor < COLOR_NONE && magicEffect < MAGIC_EFFECT_NONE)
                {
                    addMagicEffect(list, targetPos, magicEffect);
                    SpectatorVec textList;
                    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
                    {
                        if(!(*it)->getPlayer())
                            continue;

                        if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z)
                            textList.push_back(*it);
                    }

                    MessageDetails* details = new MessageDetails(damage, textColor);
                    if(elementDamage)
                    {
                        getCombatDetails(params.element.type, magicEffect, textColor);
                        details->sub = new MessageDetails(elementDamage, textColor);
                        addMagicEffect(list, targetPos, magicEffect);
                    }

                    std::stringstream ss;
                    int32_t totalDamage = damage + elementDamage;

                    std::string plural = (totalDamage != 1 ? "s" : "");
                    if(!textList.empty())
                    {
                        if(!attacker)
                            ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << ".";
                        else if(attacker != target)
                            ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << " due to an attack by " << attacker->getNameDescription() << ".";
                        else
                            ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << " due to a self attack.";

                        addStatsMessage(textList, MSG_DAMAGE_OTHERS, ss.str(), targetPos, details);
                        ss.str("");
                    }

                    Player* player = NULL;
                    if(attacker && (player = attacker->getPlayer()))
                    {
                        if(attacker != target)
                            ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << " due to your attack.";
                        else
                            ss << "You lose " << totalDamage << " hitpoint" << plural << " due to your attack.";

                        player->sendStatsMessage(MSG_DAMAGE_DEALT, ss.str(), targetPos, details);
                        ss.str("");
                    }

                    if((player = target->getPlayer()) && attacker != target)
                    {
                        if(attacker)
                            ss << "You lose " << totalDamage << " hitpoint" << plural << " due to an attack by " << attacker->getNameDescription() << ".";
                        else
                            ss << "You lose " << totalDamage << " hitpoint" << plural << ".";

                        player->sendStatsMessage(MSG_DAMAGE_RECEIVED, ss.str(), targetPos, details);
                    }

                    if(details->sub)
                        delete details->sub;

                    delete details;
                }
            }
        }
    }

    return true;
}

bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange,
    CombatType_t combatType/* = COMBAT_MANADRAIN*/, bool inherited/* = false*/)
{
    const Position& targetPos = target->getPosition();
    if(manaChange > 0)
    {
        bool deny = false;
        CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE);
        for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it)
        {
            if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_MANAGAIN, COMBAT_HEALING, manaChange))
                deny = true;
        }

        if(deny)
            return false;

        int32_t oldMana = target->getMana();
        target->changeMana(manaChange);
        if(oldMana != target->getMana() && g_config.getBool(ConfigManager::SHOW_MANA_CHANGE) && !target->isGhost() &&
            (g_config.getBool(ConfigManager::SHOW_MANA_CHANGE_MONSTER) || !target->getMonster()))
        {
            const SpectatorVec& list = getSpectators(targetPos);

            SpectatorVec textList;
            for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
            {
                if(!(*it)->getPlayer())
                    continue;

                if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z)
                    textList.push_back(*it);
            }

            manaChange = (target->getMana() - oldMana);
            std::string plural = (manaChange != 1 ? "s." : ".");

            std::stringstream ss;
            char buffer[20];
            sprintf(buffer, "+%d", manaChange);
            addAnimatedText(list, targetPos, COLOR_DARKPURPLE, buffer);
            if(!textList.empty())
            {
                if(!attacker)
                    ss << ucfirst(target->getNameDescription()) << " is regenerated with " << manaChange << " mana" << plural;
                else if(attacker != target)
                    ss << ucfirst(attacker->getNameDescription()) << " regenerates " << target->getNameDescription() << " with " << manaChange << " mana" << plural;
                else
                {
                    ss << ucfirst(attacker->getNameDescription()) << " regenerates ";
                    if(Player* attackerPlayer = attacker->getPlayer())
                        ss << (attackerPlayer->getSex(false) == PLAYERSEX_FEMALE ? "herself" : "himself") << " with " << manaChange << " mana" << plural;
                    else
                        ss << "itself with " << manaChange << " mana" << plural;
                }

                addStatsMessage(textList, MSG_HEALED_OTHERS, ss.str(), targetPos);
                ss.str("");
            }

            Player* player = NULL;
            if(attacker && (player = attacker->getPlayer()))
            {
                if(attacker != target)
                    ss << "You regenerate " << target->getNameDescription() << " with " << manaChange << " mana" << plural;
                else
                    ss << "You regenerate yourself with " << manaChange << " mana" << plural;

                player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos);
                ss.str("");
            }

            if((player = target->getPlayer()) && attacker != target)
            {
                if(attacker)
                    ss << ucfirst(attacker->getNameDescription()) << " regenerates you with " << manaChange << " mana" << plural;
                else
                    ss << "You are regenerated with " << manaChange << " mana" << plural;

                player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos);
            }
        }
    }
    else if(!inherited && Combat::canDoCombat(attacker, target, true) != RET_NOERROR)
    {
        const SpectatorVec& list = getSpectators(targetPos);
        addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF);
        return false;
    }
    else
    {
        int32_t manaLoss = std::min(target->getMana(), -manaChange);
        if(manaLoss > 0)
        {
            bool deny = false;
            CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE);
            for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it)
            {
                if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_MANALOSS, combatType, manaLoss))
                    deny = true;
            }

            if(deny)
                return false;

            target->drainMana(attacker, combatType, manaLoss);
            const SpectatorVec& list = getSpectators(targetPos);

            SpectatorVec textList;
            for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
            {
                if(!(*it)->getPlayer())
                    continue;

                if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z)
                    textList.push_back(*it);
            }

            std::stringstream ss;
            MessageDetails* details = new MessageDetails(manaLoss, COLOR_BLUE);
            if(!textList.empty())
            {
                if(!attacker)
                    ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana.";
                else if(attacker != target)
                    ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana due to an attack by " << attacker->getNameDescription();
                else
                    ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana due to a self attack.";

                addStatsMessage(textList, MSG_DAMAGE_OTHERS, ss.str(), targetPos, details);
                ss.str("");
            }

            Player* player;
            if(attacker && (player = attacker->getPlayer()))
            {
                if(attacker != target)
                    ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana due to your attack.";
                else
                    ss << "You lose " << manaLoss << " mana due to your attack.";

                player->sendStatsMessage(MSG_DAMAGE_DEALT, ss.str(), targetPos, details);
                ss.str("");
            }

            if((player = target->getPlayer()) && attacker != target)
            {
                if(attacker)
                    ss << "You lose " << manaLoss << " mana due to an attack by " << attacker->getNameDescription();
                else
                    ss << "You lose " << manaLoss << " mana.";

                player->sendStatsMessage(MSG_DAMAGE_RECEIVED, ss.str(), targetPos, details);
            }

            delete details;
        }
    }

    return true;
}

void Game::addCreatureHealth(const Creature* target)
{
    const SpectatorVec& list = getSpectators(target->getPosition());
    addCreatureHealth(list, target);
}

void Game::addCreatureHealth(const SpectatorVec& list, const Creature* target)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendCreatureHealth(target);
    }
}

void Game::addCreatureSquare(const Creature* target, uint8_t squareColor)
{
    const SpectatorVec& list = getSpectators(target->getPosition());
    addCreatureSquare(list, target, squareColor);
}

void Game::addCreatureSquare(const SpectatorVec& list, const Creature* target, uint8_t squareColor)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendCreatureSquare(target, squareColor);
    }
}

void Game::addAnimatedText(const Position& pos, uint8_t textColor, const std::string& text)
{
    const SpectatorVec& list = getSpectators(pos);
    addAnimatedText(list, pos, textColor, text);
}

void Game::addAnimatedText(const SpectatorVec& list, const Position& pos, uint8_t textColor,
    const std::string& text)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendAnimatedText(pos, textColor, text);
    }
}

void Game::addMagicEffect(const Position& pos, uint8_t effect, bool ghostMode/* = false*/)
{
    if(ghostMode)
        return;

    const SpectatorVec& list = getSpectators(pos);
    addMagicEffect(list, pos, effect);
}

void Game::addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect,
    bool ghostMode/* = false*/)
{
    if(ghostMode)
        return;

    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendMagicEffect(pos, effect);
    }
}

void Game::addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect)
{
    SpectatorVec list;
    getSpectators(list, fromPos, false);
    getSpectators(list, toPos, true);
    addDistanceEffect(list, fromPos, toPos, effect);
}

void Game::addDistanceEffect(const SpectatorVec& list, const Position& fromPos,
    const Position& toPos, uint8_t effect)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendDistanceShoot(fromPos, toPos, effect);
    }
}

void Game::addStatsMessage(const SpectatorVec& list, MessageClasses mClass, const std::string& message,
    const Position& pos, MessageDetails* details/* = NULL*/)
{
    Player* player = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((player = (*it)->getPlayer()))
            player->sendStatsMessage(mClass, message, pos, details);
    }
}

void Game::startDecay(Item* item)
{
    if(!item || !item->canDecay() || item->getDecaying() == DECAYING_TRUE)
        return;

    if(item->getDuration() > 0)
    {
        item->addRef();
        item->setDecaying(DECAYING_TRUE);
        toDecayItems.push_back(item);
    }
    else
        internalDecayItem(item);
}

void Game::internalDecayItem(Item* item)
{
    const ItemType& it = Item::items.getItemType(item->getID());
    if(it.decayTo)
    {
        Item* newItem = transformItem(item, it.decayTo);
        startDecay(newItem);
    }
    else
    {
        ReturnValue ret = internalRemoveItem(NULL, item);
        if(ret != RET_NOERROR)
            std::clog << "> DEBUG: internalDecayItem failed, error code: " << (int32_t)ret << ", item id: " << item->getID() << std::endl;
    }
}

void Game::checkDecay()
{
    checkDecayEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_DECAYINTERVAL,
        boost::bind(&Game::checkDecay, this)));

    size_t bucket = (lastBucket + 1) % EVENT_DECAYBUCKETS;
    for(DecayList::iterator it = decayItems[bucket].begin(); it != decayItems[bucket].end();)
    {
        Item* item = *it;
        if(!item->canDecay())
        {
            item->setDecaying(DECAYING_FALSE);
            freeThing(item);
            it = decayItems[bucket].erase(it);
            continue;
        }

        int32_t decreaseTime = EVENT_DECAYINTERVAL * EVENT_DECAYBUCKETS;
        if((int32_t)item->getDuration() - decreaseTime < 0)
            decreaseTime = item->getDuration();

        item->decreaseDuration(decreaseTime);

        int32_t dur = item->getDuration();
        if(dur <= 0)
        {
            it = decayItems[bucket].erase(it);
            internalDecayItem(item);
            freeThing(item);
        }
        else if(dur < EVENT_DECAYINTERVAL * EVENT_DECAYBUCKETS)
        {
            it = decayItems[bucket].erase(it);
            size_t newBucket = (bucket + ((dur + EVENT_DECAYINTERVAL / 2) / 1000)) % EVENT_DECAYBUCKETS;
            if(newBucket == bucket)
            {
                internalDecayItem(item);
                freeThing(item);
            }
            else
                decayItems[newBucket].push_back(item);
        }
        else
            ++it;
    }

    lastBucket = bucket;
    cleanup();
}

void Game::checkLight()
{
    checkLightEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL,
        boost::bind(&Game::checkLight, this)));

    lightHour = lightHour + lightHourDelta;
    if(lightHour > 1440)
        lightHour -= 1440;

    if(std::abs(lightHour - SUNRISE) < 2 * lightHourDelta)
        lightState = LIGHT_STATE_SUNRISE;
    else if(std::abs(lightHour - SUNSET) < 2 * lightHourDelta)
        lightState = LIGHT_STATE_SUNSET;

    int32_t newLightLevel = lightLevel;
    bool lightChange = false;
    switch(lightState)
    {
        case LIGHT_STATE_SUNRISE:
        {
            newLightLevel += (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
            lightChange = true;
            break;
        }
        case LIGHT_STATE_SUNSET:
        {
            newLightLevel -= (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
            lightChange = true;
            break;
        }
        default:
            break;
    }

    if(newLightLevel <= LIGHT_LEVEL_NIGHT)
    {
        lightLevel = LIGHT_LEVEL_NIGHT;
        lightState = LIGHT_STATE_NIGHT;
    }
    else if(newLightLevel >= LIGHT_LEVEL_DAY)
    {
        lightLevel = LIGHT_LEVEL_DAY;
        lightState = LIGHT_STATE_DAY;
    }
    else
        lightLevel = newLightLevel;

    if(lightChange)
    {
        LightInfo lightInfo;
        getWorldLightInfo(lightInfo);
        for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
        {
            if(!it->second->hasCustomFlag(PlayerCustomFlag_HasFullLight))
                it->second->sendWorldLight(lightInfo);
        }
    }
}

void Game::checkWars()
{
    //executes every EVENT_WARSINTERVAL
    IOGuild::getInstance()->checkWars();
    if(checkEndingWars)
    {
        //executes every EVENT_WARSINTERVAL*2
        checkEndingWars = false;
        IOGuild::getInstance()->checkEndingWars();
    }
    else
        checkEndingWars = true;

    checkWarsEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_WARSINTERVAL,
        boost::bind(&Game::checkWars, this)));
}

void Game::getWorldLightInfo(LightInfo& lightInfo)
{
    lightInfo.level = lightLevel;
    lightInfo.color = 0xD7;
}

void Game::updateCreatureSkull(Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
         if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureSkull(creature);
    }
}

void Game::updateCreatureShield(Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureShield(creature);
    }
}

void Game::updateCreatureEmblem(Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureEmblem(creature);
    }
}

void Game::updateCreatureWalkthrough(Creature* creature)
{
    const SpectatorVec& list = getSpectators(creature->getPosition());

    //send to client
    Player* tmpPlayer = NULL;
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((tmpPlayer = (*it)->getPlayer()))
            tmpPlayer->sendCreatureWalkthrough(creature, (*it)->canWalkthrough(creature));
    }
}

bool Game::playerInviteToParty(uint32_t playerId, uint32_t invitedId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 5))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionparty = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 1000, 0, false, 5))
        player->addCondition(conditionparty);

    Player* invitedPlayer = getPlayerByID(invitedId);
    if(!invitedPlayer || invitedPlayer->isRemoved() || invitedPlayer->isInviting(player))
        return false;

    if(invitedPlayer->getParty())
    {
        char buffer[90];
        sprintf(buffer, "%s is already in a party.", invitedPlayer->getName().c_str());
        player->sendTextMessage(MSG_PARTY, buffer);
        return false;
    }

    Party* party = player->getParty();
    if(!party)
        party = new Party(player);

    return party->getLeader() == player && party->invitePlayer(invitedPlayer);
}

bool Game::playerJoinParty(uint32_t playerId, uint32_t leaderId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 5))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionparty = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 1000, 0, false, 5))
        player->addCondition(conditionparty);

    Player* leader = getPlayerByID(leaderId);
    if(!leader || leader->isRemoved() || !leader->isInviting(player))
        return false;

    if(!player->getParty())
        return leader->getParty()->join(player);

    player->sendTextMessage(MSG_PARTY, "You are already in a party.");
    return false;
}

bool Game::playerRevokePartyInvitation(uint32_t playerId, uint32_t invitedId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->getParty() || player->getParty()->getLeader() != player)
        return false;

    Player* invitedPlayer = getPlayerByID(invitedId);
    if(!invitedPlayer || invitedPlayer->isRemoved() || !player->isInviting(invitedPlayer))
        return false;

    player->getParty()->revokeInvitation(invitedPlayer);
    return true;
}

bool Game::playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->getParty() || player->getParty()->getLeader() != player)
        return false;

    if(player->hasCondition(CONDITION_EXHAUST, 5))
    {
        player->sendTextMessage(MSG_STATUS_SMALL, "You have to wait a while.");
        return false;
    }

    if(Condition* conditionparty = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 1000, 0, false, 5))
        player->addCondition(conditionparty);

    Player* newLeader = getPlayerByID(newLeaderId);
    if(!newLeader || newLeader->isRemoved() || !newLeader->getParty() || newLeader->getParty() != player->getParty())
        return false;

    return player->getParty()->passLeadership(newLeader);
}

bool Game::playerLeaveParty(uint32_t playerId, bool forced/* = false*/)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved() || !player->getParty() || (player->hasCondition(CONDITION_INFIGHT) && !forced))
        return false;

    return player->getParty()->leave(player);
}

bool Game::playerSharePartyExperience(uint32_t playerId, bool activate)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(!player->getParty() || (!player->hasFlag(PlayerFlag_NotGainInFight)
        && player->hasCondition(CONDITION_INFIGHT)))
        return false;

    return player->getParty()->setSharedExperience(player, activate);
}

bool Game::playerReportBug(uint32_t playerId, std::string comment)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    if(!player->hasFlag(PlayerFlag_CanReportBugs))
        return false;

    CreatureEventList reportBugEvents = player->getCreatureEvents(CREATURE_EVENT_REPORTBUG);
    for(CreatureEventList::iterator it = reportBugEvents.begin(); it != reportBugEvents.end(); ++it)
        (*it)->executeReportBug(player, comment);

    return true;
}

bool Game::playerReportViolation(uint32_t playerId, ReportType_t type, uint8_t reason, const std::string& name,
    const std::string& comment, const std::string& translation, uint32_t statementId)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return false;

    CreatureEventList reportViolationEvents = player->getCreatureEvents(CREATURE_EVENT_REPORTVIOLATION);
    for(CreatureEventList::iterator it = reportViolationEvents.begin(); it != reportViolationEvents.end(); ++it)
        (*it)->executeReportViolation(player, type, reason, name, comment, translation, statementId);

    return true;
}

void Game::kickPlayer(uint32_t playerId, bool displayEffect)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return;

    player->kick(displayEffect, true);
}

bool Game::broadcastMessage(const std::string& text, MessageClasses type)
{
    std::clog << "> Broadcasted message: \"" << text << "\"." << std::endl;
    for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it)
        it->second->sendTextMessage(type, text);

    return true;
}

Position Game::getClosestFreeTile(Creature* creature, Position pos, bool extended/* = false*/, bool ignoreHouse/* = true*/)
{
    PairVector relList;
    relList.push_back(PositionPair(0, 0));
    relList.push_back(PositionPair(-1, -1));
    relList.push_back(PositionPair(-1, 0));
    relList.push_back(PositionPair(-1, 1));
    relList.push_back(PositionPair(0, -1));
    relList.push_back(PositionPair(0, 1));
    relList.push_back(PositionPair(1, -1));
    relList.push_back(PositionPair(1, 0));
    relList.push_back(PositionPair(1, 1));

    if(extended)
    {
        relList.push_back(PositionPair(-2, 0));
        relList.push_back(PositionPair(0, -2));
        relList.push_back(PositionPair(0, 2));
        relList.push_back(PositionPair(2, 0));
    }

    std::random_shuffle(relList.begin() + 1, relList.end());
    if(Player* player = creature->getPlayer())
    {
        for(PairVector::iterator it = relList.begin(); it != relList.end(); ++it)
        {
            Tile* tile = map->getTile(Position((pos.x + it->first), (pos.y + it->second), pos.z));
            if(!tile || !tile->ground)
                continue;

            ReturnValue ret = tile->__queryAdd(0, player, 1, FLAG_IGNOREBLOCKITEM);
            if(ret == RET_NOTENOUGHROOM || (ret == RET_NOTPOSSIBLE && !player->hasCustomFlag(
                PlayerCustomFlag_CanMoveAnywhere)) || (ret == RET_PLAYERISNOTINVITED && !ignoreHouse))
                continue;

            return tile->getPosition();
        }
    }
    else
    {
        for(PairVector::iterator it = relList.begin(); it != relList.end(); ++it)
        {
            Tile* tile = NULL;
            if((tile = map->getTile(Position((pos.x + it->first), (pos.y + it->second), pos.z)))
                && tile->__queryAdd(0, creature, 1, FLAG_IGNOREBLOCKITEM) == RET_NOERROR)
                return tile->getPosition();
        }
    }

    return Position(0, 0, 0);
}

std::string Game::getSearchString(const Position& fromPos, const Position& toPos, bool fromIsCreature/* = false*/, bool toIsCreature/* = false*/)
{
    /*
     * When the position is on same level and 0 to 4 squares away, they are "[toIsCreature: standing] next to you"
     * When the position is on same level and 5 to 100 squares away they are "to the north/west/south/east."
     * When the position is on any level and 101 to 274 squares away they are "far to the north/west/south/east."
     * When the position is on any level and 275+ squares away they are "very far to the north/west/south/east."
     * When the position is not directly north/west/south/east of you they are "((very) far) to the north-west/south-west/south-east/north-east."
     * When the position is on a lower or higher level and 5 to 100 squares away they are "on a lower (or) higher level to the north/west/south/east."
     * When the position is on a lower or higher level and 0 to 4 squares away they are "below (or) above you."
     */

    enum direction_t
    {
        DIR_N, DIR_S, DIR_E, DIR_W,
        DIR_NE, DIR_NW, DIR_SE, DIR_SW
    };

    enum distance_t
    {
        DISTANCE_BESIDE,
        DISTANCE_CLOSE,
        DISTANCE_FAR,
        DISTANCE_VERYFAR
    };

    enum level_t
    {
        LEVEL_HIGHER,
        LEVEL_LOWER,
        LEVEL_SAME
    };

    direction_t direction;
    distance_t distance;
    level_t level;

    int32_t dx = fromPos.x - toPos.x, dy = fromPos.y - toPos.y, dz = fromPos.z - toPos.z;
    if(dz > 0)
        level = LEVEL_HIGHER;
    else if(dz < 0)
        level = LEVEL_LOWER;
    else
        level = LEVEL_SAME;

    if(std::abs(dx) < 5 && std::abs(dy) < 5)
        distance = DISTANCE_BESIDE;
    else
    {
        int32_t tmp = dx * dx + dy * dy;
        if(tmp < 10000)
            distance = DISTANCE_CLOSE;
        else if(tmp < 75625)
            distance = DISTANCE_FAR;
        else
            distance = DISTANCE_VERYFAR;
    }

    float tan;
    if(dx)
        tan = (float)dy / (float)dx;
    else
        tan = 10.;

    if(std::abs(tan) < 0.4142)
    {
        if(dx > 0)
            direction = DIR_W;
        else
            direction = DIR_E;
    }
    else if(std::abs(tan) < 2.4142)
    {
        if(tan > 0)
        {
            if(dy > 0)
                direction = DIR_NW;
            else
                direction = DIR_SE;
        }
        else
        {
            if(dx > 0)
                direction = DIR_SW;
            else
                direction = DIR_NE;
        }
    }
    else
    {
        if(dy > 0)
            direction = DIR_N;
        else
            direction = DIR_S;
    }

    std::stringstream ss;
    switch(distance)
    {
        case DISTANCE_BESIDE:
        {
            switch(level)
            {
                case LEVEL_SAME:
                {
                    ss << "is ";
                    if(toIsCreature)
                        ss << "standing ";

                    ss << "next to you";
                    break;
                }

                case LEVEL_HIGHER:
                {
                    ss << "is above ";
                    if(fromIsCreature)
                        ss << "you";

                    break;
                }

                case LEVEL_LOWER:
                {
                    ss << "is below ";
                    if(fromIsCreature)
                        ss << "you";

                    break;
                }

                default:
                    break;
            }

            break;
        }

        case DISTANCE_CLOSE:
        {
            switch(level)
            {
                case LEVEL_SAME:
                    ss << "is to the";
                    break;
                case LEVEL_HIGHER:
                    ss << "is on a higher level to the";
                    break;
                case LEVEL_LOWER:
                    ss << "is on a lower level to the";
                    break;
                default:
                    break;
            }

            break;
        }

        case DISTANCE_FAR:
            ss << "is far to the";
            break;

        case DISTANCE_VERYFAR:
            ss << "is very far to the";
            break;

        default:
            break;
    }

    if(distance != DISTANCE_BESIDE)
    {
        ss << " ";
        switch(direction)
        {
            case DIR_N:
                ss << "north";
                break;

            case DIR_S:
                ss << "south";
                break;

            case DIR_E:
                ss << "east";
                break;

            case DIR_W:
                ss << "west";
                break;

            case DIR_NE:
                ss << "north-east";
                break;

            case DIR_NW:
                ss << "north-west";
                break;

            case DIR_SE:
                ss << "south-east";
                break;

            case DIR_SW:
                ss << "south-west";
                break;

            default:
                break;
        }
    }

    return ss.str();
}

double Game::getExperienceStage(uint32_t level, double divider/* = 1.*/)
{
    if(!g_config.getBool(ConfigManager::EXPERIENCE_STAGES))
        return g_config.getDouble(ConfigManager::RATE_EXPERIENCE) * divider;

    if(lastStageLevel && level >= lastStageLevel)
        return stages[lastStageLevel] * divider;

    return stages[level] * divider;
}

bool Game::loadExperienceStages()
{
    if(!g_config.getBool(ConfigManager::EXPERIENCE_STAGES))
    {
        if(!stages.empty())
            stages.clear();

        return true;
    }

    xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "stages.xml").c_str());
    if(!doc)
    {
        std::clog << "[Warning - Game::loadExperienceStages] Cannot load stages file."
            << std::endl << getLastXMLError() << std::endl;
        return false;
    }

    xmlNodePtr q, p, root = xmlDocGetRootElement(doc);
    if(xmlStrcmp(root->name, (const xmlChar*)"stages"))
    {
        std::clog << "[Error - Game::loadExperienceStages] Malformed stages file" << std::endl;
        xmlFreeDoc(doc);
        return false;
    }

    int32_t intValue, low = 0, high = 0;
    float floatValue, mul = 1.0f, defStageMultiplier;
    std::string strValue;

    lastStageLevel = 0;
    stages.clear();

    q = root->children;
    while(q)
    {
        if(!xmlStrcmp(q->name, (const xmlChar*)"world"))
        {
            if(readXMLString(q, "id", strValue))
            {
                IntegerVec intVector;
                if(!parseIntegerVec(strValue, intVector) || std::find(intVector.begin(),
                    intVector.end(), g_config.getNumber(ConfigManager::WORLD_ID)) == intVector.end())
                {
                    q = q->next;
                    continue;
                }
            }

            defStageMultiplier = 1.0f;
            if(readXMLFloat(q, "multiplier", floatValue))
                defStageMultiplier = floatValue;

            p = q->children;
            while(p)
            {
                if(!xmlStrcmp(p->name, (const xmlChar*)"stage"))
                {
                    low = 1;
                    if(readXMLInteger(p, "minlevel", intValue) || readXMLInteger(p, "minLevel", intValue))
                        low = intValue;

                    high = 0;
                    if(readXMLInteger(p, "maxlevel", intValue) || readXMLInteger(p, "maxLevel", intValue))
                        high = intValue;
                    else
                        lastStageLevel = low;

                    mul = 1.0f;
                    if(readXMLFloat(p, "multiplier", floatValue))
                        mul = floatValue;

                    mul *= defStageMultiplier;
                    if(lastStageLevel && lastStageLevel == (uint32_t)low)
                        stages[lastStageLevel] = mul;
                    else
                    {
                        for(int32_t i = low; i <= high; ++i)
                            stages = mul;
                    }
                }

                p = p->next;
            }
        }

        if(!xmlStrcmp(q->name, (const xmlChar*)"stage"))
        {
            low = 1;
            if(readXMLInteger(q, "minlevel", intValue))
                low = intValue;
            else

            high = 0;
            if(readXMLInteger(q, "maxlevel", intValue))
                high = intValue;
            else
                lastStageLevel = low;

            mul = 1.0f;
            if(readXMLFloat(q, "multiplier", floatValue))
                mul = floatValue;

            if(lastStageLevel && lastStageLevel == (uint32_t)low)
                stages[lastStageLevel] = mul;
            else
            {
                for(int32_t i = low; i <= high; ++i)
                    stages = mul;
            }
        }

        q = q->next;
    }

    xmlFreeDoc(doc);
    return true;
}

int32_t Game::getMotdId()
{
    if(lastMotd.length() == g_config.getString(ConfigManager::MOTD).length())
    {
        if(lastMotd == g_config.getString(ConfigManager::MOTD))
            return lastMotdId;
    }

    lastMotd = g_config.getString(ConfigManager::MOTD);
    Database* db = Database::getInstance();

    DBQuery query;
    query << "INSERT INTO `server_motd` (`id`, `world_id`, `text`) VALUES (" << lastMotdId + 1 << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << db->escapeString(lastMotd) << ")";
    if(db->query(query.str()))
        ++lastMotdId;

    return lastMotdId;
}

void Game::loadMotd()
{
    Database* db = Database::getInstance();
    DBQuery query;
    query << "SELECT `id`, `text` FROM `server_motd` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " ORDER BY `id` DESC LIMIT 1";

    DBResult* result;
    if(!(result = db->storeQuery(query.str())))
    {
        std::clog << "> ERROR: Failed to load motd!" << std::endl;
        lastMotdId = random_range(5, 500);
        return;
    }

    lastMotdId = result->getDataInt("id");
    lastMotd = result->getDataString("text");
    result->free();
}

void Game::checkPlayersRecord(Player* player)
{
    uint32_t count = getPlayersOnline();
    if(count <= playersRecord)
        return;

    GlobalEventMap recordEvents = g_globalEvents->getEventMap(GLOBALEVENT_RECORD);
    for(GlobalEventMap::iterator it = recordEvents.begin(); it != recordEvents.end(); ++it)
        it->second->executeRecord(count, playersRecord, player);

    playersRecord = count;
}

void Game::loadPlayersRecord()
{
    Database* db = Database::getInstance();
    DBQuery query;
    query << "SELECT `record` FROM `server_record` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " ORDER BY `timestamp` DESC LIMIT 1";

    DBResult* result;
    if(!(result = db->storeQuery(query.str())))
    {
        std::clog << "> ERROR: Failed to load players record!" << std::endl;
        return;
    }

    playersRecord = result->getDataInt("record");
    result->free();
}

bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/, bool completeReload/* = false*/)
{
    bool done = false;
    switch(reload)
    {
        case RELOAD_ACTIONS:
        {
            if(g_actions->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload actions." << std::endl;

            break;
        }

        case RELOAD_CHAT:
        {
            if(g_chat.reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload chat." << std::endl;

            break;
        }

        case RELOAD_CONFIG:
        {
            if(g_config.reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload config." << std::endl;

            break;
        }

        case RELOAD_CREATUREEVENTS:
        {
            if(g_creatureEvents->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload creature events." << std::endl;

            break;
        }

        case RELOAD_GAMESERVERS:
        {
            #ifdef __LOGIN_SERVER__
            if(GameServers::getInstance()->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload game servers." << std::endl;

            #endif
            break;
        }

        case RELOAD_GLOBALEVENTS:
        {
            if(g_globalEvents->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload global events." << std::endl;

            break;
        }

        case RELOAD_GROUPS:
        {
            break;
        }

        case RELOAD_ITEMS:
        {
            std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl;
            done = true;
            break;
        }

        case RELOAD_MODS:
        {
            if(ScriptManager::getInstance()->reloadMods())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload mods." << std::endl;

            break;
        }

        case RELOAD_MONSTERS:
        {
            if(g_monsters.reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload monsters." << std::endl;

            break;
        }

        case RELOAD_MOVEEVENTS:
        {
            if(g_moveEvents->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload move events." << std::endl;

            break;
        }

        case RELOAD_NPCS:
        {
            g_npcs.reload();
            done = true;
            break;
        }

        case RELOAD_OUTFITS:
        {
            std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl;
            done = true;
            break;
        }

        case RELOAD_QUESTS:
        {
            if(Quests::getInstance()->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload quests." << std::endl;

            break;
        }

        case RELOAD_RAIDS:
        {
            if(!Raids::getInstance()->reload())
                std::clog << "[Error - Game::reloadInfo] Failed to reload raids." << std::endl;
            else if(!Raids::getInstance()->startup())
                std::clog << "[Error - Game::reloadInfo] Failed to startup raids when reloading." << std::endl;
            else
                done = true;

            break;
        }

        case RELOAD_SPELLS:
        {
            if(!g_spells->reload())
                std::clog << "[Error - Game::reloadInfo] Failed to reload spells." << std::endl;
            else if(!g_monsters.reload())
                std::clog << "[Error - Game::reloadInfo] Failed to reload monsters when reloading spells." << std::endl;
            else
                done = true;

            break;
        }

        case RELOAD_STAGES:
        {
            if(loadExperienceStages())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload stages." << std::endl;

            break;
        }

        case RELOAD_TALKACTIONS:
        {
            if(g_talkActions->reload())
                done = true;
            else
                std::clog << "[Error - Game::reloadInfo] Failed to reload talk actions." << std::endl;

            break;
        }

        case RELOAD_VOCATIONS:
        {
            break;
        }

        case RELOAD_WEAPONS:
        {
            std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl;
            done = true;
            break;
        }

        case RELOAD_ALL:
        {
            done = true;
            for(int32_t i = RELOAD_FIRST; i <= RELOAD_LAST; ++i)
            {
                if(!reloadInfo((ReloadInfo_t)i, 0, true) && done)
                    done = false;
            }

            if(!ScriptManager::getInstance()->reloadMods() && done)
                done = false;

            break;
        }

        default:
        {
            std::clog << "[Warning - Game::reloadInfo] Reload type not found." << std::endl;
            break;
        }
    }

    if(reload != RELOAD_CONFIG && reload != RELOAD_MODS && !completeReload && !ScriptManager::getInstance()->reloadMods())
        std::clog << "[Error - Game::reloadInfo] Failed to reload mods." << std::endl;

    if(!playerId)
        return done;

    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return done;

    if(done)
    {
        player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Reloaded successfully.");
        return true;
    }

    if(reload == RELOAD_ALL)
        player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Failed to reload some parts.");
    else
        player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Failed to reload.");

    return false;
}

void Game::prepareGlobalSave(uint8_t minutes)
{
    std::clog << "Game::prepareGlobalSave in " << (uint32_t)minutes << " minutes" << std::endl;
    switch(minutes)
    {
        case 5:
            setGameState(GAMESTATE_CLOSING);
            broadcastMessage("Server is going down for a global save within 5 minutes. Please logout.", MSG_STATUS_WARNING);
            Scheduler::getInstance().addEvent(createSchedulerTask(2 * 60000, boost::bind(&Game::prepareGlobalSave, this, 3)));
            break;

        case 3:
            broadcastMessage("Server is going down for a global save within 3 minutes. Please logout.", MSG_STATUS_WARNING);
            Scheduler::getInstance().addEvent(createSchedulerTask(2 * 60000, boost::bind(&Game::prepareGlobalSave, this, 1)));
            break;

        case 1:
            broadcastMessage("Server is going down for a global save in one minute, please logout!", MSG_STATUS_WARNING);
            Scheduler::getInstance().addEvent(createSchedulerTask(60000, boost::bind(&Game::prepareGlobalSave, this, 0)));
            break;

        case 0:
            globalSave();
            break;

        default:
            if(minutes > 5)
                Scheduler::getInstance().addEvent(createSchedulerTask((minutes - 5) * 1000, boost::bind(&Game::prepareGlobalSave, this, 5)));
            break;
    }
}

void Game::globalSave()
{
    bool close = g_config.getBool(ConfigManager::SHUTDOWN_AT_GLOBALSAVE);
    if(!close) // check are we're going to close the server
        Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAMESTATE_CLOSED)));

    // call the global event
    g_globalEvents->execute(GLOBALEVENT_GLOBALSAVE);
    if(close)
    {
        //shutdown server
        Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAMESTATE_SHUTDOWN)));
        return;
    }

    //pay houses
    Houses::getInstance()->check();
    //clean map if configured to
    if(g_config.getBool(ConfigManager::CLEAN_MAP_AT_GLOBALSAVE))
        cleanMap();

    //remove premium days globally if configured to
    if(g_config.getBool(ConfigManager::INIT_PREMIUM_UPDATE))
        IOLoginData::getInstance()->updatePremiumDays();

    //reload everything
    reloadInfo(RELOAD_ALL);
    //prepare for next global save after 24 hours
    Scheduler::getInstance().addEvent(createSchedulerTask(((24 * 60 * 60) - (5 * 60)) * 1000, boost::bind(&Game::prepareGlobalSave, this, 5)));
    //open server
    Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAMESTATE_NORMAL)));
}

void Game::shutdown()
{
    std::clog << "Preparing";
    Scheduler::getInstance().shutdown();
    std::clog << " to";
    Dispatcher::getInstance().shutdown();
    std::clog << " shutdown";
    Spawns::getInstance()->clear();
    std::clog << " the";
    Raids::getInstance()->clear();
    std::clog << " server... ";
    cleanup();
    std::clog << "(done)." << std::endl;
    if(services)
        services->stop();

#if defined(WINDOWS) && !defined(_CONSOLE)
    exit(1);
#endif
}

void Game::cleanup()
{
    //free memory
    for(std::vector<Thing*>::iterator it = releaseThings.begin(); it != releaseThings.end(); ++it)
        (*it)->unRef();

    releaseThings.clear();
    for(DecayList::iterator it = toDecayItems.begin(); it != toDecayItems.end(); ++it)
    {
        int32_t dur = (*it)->getDuration();
        if(dur >= EVENT_DECAYINTERVAL * EVENT_DECAYBUCKETS)
            decayItems[lastBucket].push_back(*it);
        else
            decayItems[(lastBucket + 1 + (*it)->getDuration() / 1000) % EVENT_DECAYBUCKETS].push_back(*it);
    }

    toDecayItems.clear();
}

void Game::freeThing(Thing* thing)
{
    releaseThings.push_back(thing);
}

void Game::showHotkeyUseMessage(Player* player, Item* item)
{
    const ItemType& it = Item::items[item->getID()];
    uint32_t count = player->__getItemTypeCount(item->getID(), item->isFluidContainer() ? item->getFluidType() : -1);

    std::stringstream stream;
    if(!it.showCount)
        stream << "Using one of " << it.name << "...";
    else if(count == 1)
        stream << "Using the last " << it.name.c_str() << "...";
    else
        stream << "Using one of " << count << " " << it.pluralName.c_str() << "...";

    player->sendTextMessage(MSG_HOTKEY_USE, stream.str().c_str());
}

void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer)
{
    Player* player = getPlayerByID(playerId);
    if(!player || player->isRemoved())
        return;

    CreatureEventList extendedOpcodeEvents = player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE);
    for(CreatureEventList::iterator it = extendedOpcodeEvents.begin(); it != extendedOpcodeEvents.end(); ++it)
        (*it)->executeExtendedOpcode(player, opcode, buffer);
}
 

 

 

Parece estar tudo nos conformes, envia spells.cpp.

 

 

 

Nós somos aquilo que fazemos repetidamente. Excelência, não é um modo de agir, mas um hábito.

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

Participe da conversa

Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.

Visitante
Responder

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emojis são permitidos.

×   Seu link foi automaticamente incorporado.   Mostrar como link

×   Seu conteúdo anterior foi restaurado.   Limpar o editor

×   Não é possível colar imagens diretamente. Carregar ou inserir imagens do URL.

  • Quem Está Navegando   0 membros estão online

    Nenhum usuário registrado visualizando esta página.


  • Conteúdo Similar

    • Por cloudrun2023
      CloudRun - Sua Melhor Escolha para Hospedagem de OTServer!
      Você está procurando a solução definitiva para hospedar seu OTServer com desempenho imbatível e segurança inigualável? Não procure mais! Apresentamos a CloudRun, sua parceira confiável em serviços de hospedagem na nuvem.
       
      Recursos Exclusivos - Proteção DDoS Avançada:
      Mantenha seu OTServer online e seguro com nossa robusta proteção DDoS, garantindo uma experiência de jogo ininterrupta para seus jogadores.
       
      Servidores Ryzen 7 Poderosos: Desfrute do poder de processamento superior dos servidores Ryzen 7 para garantir um desempenho excepcional do seu OTServer. Velocidade e estabilidade garantidas!
       
      Armazenamento NVMe de Alta Velocidade:
      Reduza o tempo de carregamento do jogo com nosso armazenamento NVMe ultrarrápido. Seus jogadores vão adorar a rapidez com que podem explorar o mundo do seu OTServer.
       
      Uplink de até 1GB:
      Oferecemos uma conexão de alta velocidade com até 1GB de largura de banda, garantindo uma experiência de jogo suave e livre de lag para todos os seus jogadores, mesmo nos momentos de pico.
       
      Suporte 24 Horas:
      Estamos sempre aqui para você! Nossa equipe de suporte está disponível 24 horas por dia, 7 dias por semana, para resolver qualquer problema ou responder a qualquer pergunta que você possa ter. Sua satisfação é a nossa prioridade.
       
      Fácil e Rápido de Começar:
      Configurar seu OTServer na CloudRun é simples e rápido. Concentre-se no desenvolvimento do seu jogo enquanto cuidamos da hospedagem.
       
      Entre em Contato Agora!
      Website: https://central.cloudrun.com.br/index.php?rp=/store/cloud-ryzen-brasil
      Email: [email protected]
      Telefone: (47) 99902-5147

      Não comprometa a qualidade da hospedagem do seu OTServer. Escolha a CloudRun e ofereça aos seus jogadores a melhor experiência de jogo possível. Visite nosso site hoje mesmo para conhecer nossos planos e começar!
       
      https://central.cloudrun.com.br/index.php?rp=/store/cloud-ryzen-brasil
       
      CloudRun - Onde a Velocidade Encontra a Confiabilidade!
       

    • Por FeeTads
      SALVE rapaziada do TK, esses dias vim pensando em novos scripts pro meu OT, e em um deles eu precisava que determinada area não contasse frag pro player que matasse outros, PORÉM eu precisava que os players que morressem nessa area ainda assim tivessem as penalidades da sua morte, procurei por ai, achei alguns scripts que apenas tiravam o SKULL e não realmente o FRAG do player.

      **script atualizado 22/10/2023** - melhorado e otimizado, levei o script pra puxar as infos por .lua / creatureScripts

      vou disponibilizar o code aqui, e o que fazer pra determinada area não contar frag.

      SOURCE OTX 2 / TFS 0.x, Funciona em TFS 1.x mudando as tags e ajeitando as sintaxes.

      vá em creatureevent.cpp

      procure por:
      else if(type == "preparedeath") _type = CREATURE_EVENT_PREPAREDEATH;
      Adiciona abaixo:
      else if(type == "nocountfrag") _type = CREATURE_EVENT_NOCOUNTFRAG;

      procure por:
      case CREATURE_EVENT_PREPAREDEATH: return "onPrepareDeath";  
      Adicione abaixo: 
      case CREATURE_EVENT_NOCOUNTFRAG: return "noCountFragArea";

      procure por:
      case CREATURE_EVENT_PREPAREDEATH: return "cid, deathList";
      Adicione abaixo:
      case CREATURE_EVENT_NOCOUNTFRAG: return "cid, target";

      agora no mesmo arquivo, vá até o final do arquivo e adicione essa função:
      uint32_t CreatureEvent::executeNoCountFragArea(Creature* creature, Creature* target) { //noCountFragArea(cid, target) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(creature->getPosition()); std::ostringstream scriptstream; scriptstream << "local cid = " << env->addThing(creature) << std::endl; scriptstream << "local target = " << env->addThing(target) << std::endl; if(m_scriptData) scriptstream << *m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ std::ostringstream desc; desc << creature->getName(); env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(creature->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); lua_pushnumber(L, env->addThing(target)); bool result = m_interface->callFunction(2); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - CreatureEvent::noCountFragArea] Call stack overflow." << std::endl; return 0; } }

      agora vá em creatureevent.h

      procure por:
      CREATURE_EVENT_PREPAREDEATH
      adicione abaixo:
      CREATURE_EVENT_NOCOUNTFRAG

      procure por:
      uint32_t executePrepareDeath(Creature* creature, DeathList deathList);
      Adicione abaixo:
      uint32_t executeNoCountFragArea(Creature* creature, Creature* target);

      agora vá em player.cpp

      procure por:
      bool Player::onKilledCreature(Creature* target, DeathEntry& entry)
      abaixo de:
      War_t enemy; if(targetPlayer->getEnemy(this, enemy)) { if(entry.isLast()) IOGuild::getInstance()->updateWar(enemy); entry.setWar(enemy); }
      Adicione o seguinte código:
      if (targetPlayer){ CreatureEventList killEvents = getCreatureEvents(CREATURE_EVENT_NOCOUNTFRAG); for (const auto &event : killEvents) { if (!event->executeNoCountFragArea(this, target)) { return true; } } }

      //

      Feito isso, tudo completo na sua source, agora é necessário adicionar o creaturescript dentro do servidor

      vá até creaturescripts/scripts
      crie um arquivo chamado, "noCountFragInArea.lua"
      e dentro dele cole o código:
       
      --[[ script feito por feetads / TibiaKing ]]-- --[[ discord: feetads / FeeTads#0246 ]]-- -- Add positions here for which you do not want to count frags local areas = { [1] = {from = {x = 91, y = 122, z = 7}, to = {x = 98, y = 127, z = 7}}, -- from = area superior esquerda / to = area inferior direita (formando um quadrado) } local onlyKillerInArea = false -- only killer need to be in area? function noCountFragArea(cid, target) if not isCreature(cid) or not isCreature(target) then return true end local posKiller = getPlayerPosition(cid) local posTarget = getPlayerPosition(target) for i = 1, #areas do local area = areas[i] if isInArea(posKiller, area.from, area.to) then if onlyKillerInArea then return false elseif isInArea(posTarget, area.from, area.to) then return false end end end return true end
      agora em creaturescripts.xml
      <event type="nocountfrag" name="fragarea" event="script" value="noCountFragInArea.lua"/>
      agora em creaturescripts/scripts/login.lua
       procure por OU semelhante a esse:
      registerCreatureEvent(cid, "AdvanceSave")
      e abaixo adicione:
      registerCreatureEvent(cid, "fragarea")

      //


      Agora tudo certo, quando quiser adiciona uma area que não pega frag, vá até o script e apenas coloque a area, igual o demonstrado no script

      Exemplo:
      local areas = { [1] = {from = {x = 91, y = 122, z = 7}, to = {x = 98, y = 127, z = 7}}, [2] = {from = {x = 1000, y = 1000, z = 7}, to = {x = 1100, y = 1100, z = 7}}, }
      assim somente colocando a area no script e abrindo o server ou dando /reload, já funcionará a area como não pegar frag.
      Esse sistema pode ser bom pra areas de pvp ativo, onde você ainda quer que o player que morrer perca os atributos, como se fosse uma morte normal, porém não conta frag pra quem matar.
      Bom pra sistemas tipo castle 48h (guild war), onde há diversas mortes e risco de pegar red, atrapalhando a war.

      Façam bom proveito dos scripts, e deixem os créditos no script rsrs

      **Eu fiz as alterações e o simples código por isso vim disponibilizar, créditos meus**
    • Por Muvuka
      Abri canal a força creaturescript acho que funcione no creaturescript cria script creaturescript
       
      <channel id="9" name="HELP" logged="yes"/>
      <channel id="12" name="Report Bugs" logged="yes"/>
      <channel id="13" name="Loot" logged="yes"/>
      <channel id="14" name="Report Character Rules Tibia Rules" logged="yes"/>
      <channel id="15" name="Death Channel"/>
      <channel id="6548" name="DexSoft" level="1"/>
      <channel id="7" name="Reports" logged="yes"/>
       
      antes de 
              if(lastLogin > 0) then adicione isso:
                      doPlayerOpenChannel(cid, CHANNEL_HELP) doPlayerOpenChannel(cid, 1,  2, 3) = 1,2 ,3 Channels, entendeu? NÃO FUNCIONA EU QUERO UM MEIO DE ABRI SEM USA A SOURCE
       
      EU NÃO CONSEGUI ABRI EU NÃO TENHO SOURCE
       
       
    • Por bolachapancao
      Rapaziada seguinte preciso de um script que ao utilizar uma alavanca para até 4 jogadores.
      Os jogadores serão teleportados para hunt durante uma hora e depois de uma hora os jogadores serão teleportados de volta para o templo.
       
      Observação: caso o jogador morra ou saia da hunt o evento hunt é cancelado.

      Estou a base canary
      GitHub - opentibiabr/canary: Canary Server 13.x for OpenTibia community.
       
    • Por RAJADAO
      .Qual servidor ou website você utiliza como base? 
      Sabrehaven 8.0
      Qual o motivo deste tópico? 
      Ajuda com novos efeitos
       
      Olá amigos, gostaria de ajuda para introduzir os seguintes efeitos no meu servidor (usando o Sabrehaven 8.0 como base), adicionei algumas runas novas (avalanche, icicle, míssil sagrado, stoneshower & Thunderstorm) e alguns novos feitiços (exevo mas san, exori san, exori tera, exori frigo, exevo gran mas frigo, exevo gran mas tera, exevo tera hur, exevo frigo hur) mas nenhum dos efeitos dessas magias parece existir no servidor, alguém tem um link para um tutorial ou algo assim para que eu possa fazer isso funcionar?
      Desculpe pelo mau inglês, sou brasileiro.

      Obrigado!


      AVALANCHE RUNE id:3161 \/
      (COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE)
      (COMBAT_PARAM_EFFECT, CONST_ME_ICEAREA)
      (COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ICE)

      STONESHOWER RUNE id:3175 \/
      (COMBAT_PARAM_TYPE, COMBAT_EARTHDAMAGE)
      (COMBAT_PARAM_EFFECT, CONST_ME_STONES)
      (COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_EARTH)

      THUNDERSTORM RUNE id:3202 \/
      (COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE)
      (COMBAT_PARAM_EFFECT, CONST_ME_E NERGYHIT)
      (COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ENERGYBALL)

      ICICLE RUNE id:3158 \/
      COMBAT_ICEDAMAGE
      CONST_ME_ICEAREA
      CONST_ANI_ICE

      SANTO MÍSSIL RUNA id:3182 \/
      (COMBAT_PARAM_TYPE, COMBAT_HOLYDAMAGE)
      (COMBAT_PARAM_EFFECT, CONST_ME_HOLYDAMAGE)
      (COMBAT_PARAM_EFFECT, CONST_ME_HOLYAREA)
      (COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_HOLY)

      CONST_ME_PLANTATTACK (exevo gran mas tera)
      CONST_ME_ICETORNADO (exevo gran mas frigo)
      CONST_ME_SMALLPLANTS (exevo tera hur)
      CONST_ME_ICEAREA (exevo frigo hur)
      CONST_ME_ICEATTACK (exori frigo)
      CONST_ME_CARNIPHILA (exori tera)

      EXORI SAN \/
      (COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_SMALLHOLY)
      CONST_ME_HOLYDAM IDADE

      EXEVO MAS SAN \/
      CONST_ME_HOLYAREA
×
×
  • Criar Novo...

Informação Importante

Confirmação de Termo