Ir para conteúdo
  • Cadastre-se

Programação [TFS 1.2] Ghost Mode ADM não atravessavél :(


Posts Recomendados

.Qual servidor ou website você utiliza como base? 

tfs 1.2

 

 

Qual o motivo deste tópico? 

quando um gm usa o modo ghost, fica invisivel mas um player não pode usar aquele sqm, atravessar..

 

gostaria de que o gm no modo ghost ficasse atravessável, como um sqm realmente vazio

 

 

 

 

dei uma caçada nos foruns, nas sources mas não encontrei a solução, quem puder dar uma luz!

 

 

Link para o post
Compartilhar em outros sites
9 horas atrás, Trunksontibia disse:

.Qual servidor ou website você utiliza como base? 

tfs 1.2

 

 

Qual o motivo deste tópico? 

quando um gm usa o modo ghost, fica invisivel mas um player não pode usar aquele sqm, atravessar..

 

gostaria de que o gm no modo ghost ficasse atravessável, como um sqm realmente vazio

 

 

 

 

dei uma caçada nos foruns, nas sources mas não encontrei a solução, quem puder dar uma luz!

 

 

Como está seu script do comando /ghost?

Link para o post
Compartilhar em outros sites
14 hours ago, DiigooMix said:

Como está seu script do comando /ghost?

obrigado pela atenção irmaozim, mas não é no talkactions, ele seta certinho o modo ghost..
acredito que seja nas sources do proprio modo ghost.. pelo visto é o mesmo ghost de um warlock, e voce esbarra nele..

in game.cpp:
 

Quote

if (CreatureVector* tileCreatures = toTile->getCreatures()) {
                for (Creature* tileCreature : *tileCreatures) {
                    if (!tileCreature->isInGhostMode()) {
                        player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
                        return;

possivelmente aqui..

Link para o post
Compartilhar em outros sites
59 minutos atrás, Trunksontibia disse:

obrigado pela atenção irmaozim, mas não é no talkactions, ele seta certinho o modo ghost..
acredito que seja nas sources do proprio modo ghost.. pelo visto é o mesmo ghost de um warlock, e voce esbarra nele..

in game.cpp:
 

possivelmente aqui..

Isso está normal. O problema vai estar no bool do walkthrought (player.cpp)

 

Possivelmente está faltando essa parte em seu script: https://github.com/otland/forgottenserver/blob/master/src/player.cpp#L783-L787

 

Ou então, poste esse bool inteiro, em spoiler, para que alguém possa ajudá-lo.

Editado por Toruk
revert (veja o histórico de edições)
Link para o post
Compartilhar em outros sites
22 hours ago, Toruk said:

Isso é normal. O problema vai estar no bool do walkthrought (player.cpp)

 

Possivelmente está faltando essa parte em seu script:  https://github.com/otland/forgottenserver/blob/master/src/player.cpp#L783-L787

 

Ou então, poste esse bool inteiro, em spoiler, para que alguém possa ajudá-lo.

é, realmente falta o bool.. passei ontem pesquisando e cheguei a esta conclusao.
minha distro é tfs1.2 mas nao tem essa bool.. caso eu queria adiciona-la nao é somente ai né, sabe como proceder?

Editado por Trunksontibia
palavra errada, e pq o post está hiden? (veja o histórico de edições)
Link para o post
Compartilhar em outros sites

é, realmente falta o bool.. passei ontem pesquisando e cheguei a esta conclusao.
minha distro é tfs1.2 mas nao tem essa bool.. caso eu queria adiciona-la nao é somente ai né, sabe como proceder?


-- editei outro post pois o anterior apareceu hidden e não sei oq fiz rs---

 

 

aqui está meu player.cpp inteiro:

 

Quote

/**
 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
 * Copyright (C) 2017  Alejandro Mujica <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "otpch.h"

#include <bitset>

#include "bed.h"
#include "chat.h"
#include "combat.h"
#include "configmanager.h"
#include "creatureevent.h"
#include "game.h"
#include "iologindata.h"
#include "monster.h"
#include "movement.h"
#include "scheduler.h"

extern ConfigManager g_config;
extern Game g_game;
extern Chat* g_chat;
extern Vocations g_vocations;
extern MoveEvents* g_moveEvents;
extern CreatureEvents* g_creatureEvents;

MuteCountMap Player::muteCountMap;

uint32_t Player::playerAutoID = 0x10000000;

Player::Player(ProtocolGame_ptr p) :
    Creature(), lastPing(OTSYS_TIME()), lastPong(lastPing), client(std::move(p))
{
}

Player::~Player()
{
    for (Item* item : inventory) {
        if (item) {
            item->setParent(nullptr);
            item->decrementReferenceCounter();
        }
    }

    for (const auto& it : depotLockerMap) {
        it.second->decrementReferenceCounter();
    }

    setWriteItem(nullptr);
    setEditHouse(nullptr);
}

bool Player::setVocation(uint16_t vocId)
{
    Vocation* voc = g_vocations.getVocation(vocId);
    if (!voc) {
        return false;
    }
    vocation = voc;

    Condition* condition = getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT);
    if (condition) {
        condition->setParam(CONDITION_PARAM_HEALTHGAIN, vocation->getHealthGainAmount());
        condition->setParam(CONDITION_PARAM_HEALTHTICKS, vocation->getHealthGainTicks() * 1000);
        condition->setParam(CONDITION_PARAM_MANAGAIN, vocation->getManaGainAmount());
        condition->setParam(CONDITION_PARAM_MANATICKS, vocation->getManaGainTicks() * 1000);
    }
    return true;
}

bool Player::isPushable() const
{
    if (hasFlag(PlayerFlag_CannotBePushed)) {
        return false;
    }
    return Creature::isPushable();
}

std::string Player::getDescription(int32_t lookDistance) const
{
    std::ostringstream s;

    if (lookDistance == -1) {
        s << "yourself.";

        if (group->access) {
            s << " You are " << group->name << '.';
        } else if (vocation->getId() != VOCATION_NONE) {
            s << " You are " << vocation->getVocDescription() << '.';
        } else {
            s << " You have no vocation.";
        }
    } else {
        s << name;
        if (!group->access) {
            s << " (Level " << level << ')';
        }
        s << '.';

        if (sex == PLAYERSEX_FEMALE) {
            s << " She";
        } else {
            s << " He";
        }

        if (group->access) {
            s << " is " << group->name << '.';
        } else if (vocation->getId() != VOCATION_NONE) {
            s << " is " << vocation->getVocDescription() << '.';
        } else {
            s << " has no vocation.";
        }
    }

    if (guild && guildRank) {
        if (lookDistance == -1) {
            s << " You are ";
        } else if (sex == PLAYERSEX_FEMALE) {
            s << " She is ";
        } else {
            s << " He is ";
        }

        s << guildRank->name << " of the " << guild->getName();
        if (!guildNick.empty()) {
            s << " (" << guildNick << ')';
        }

        s << ".";
    }

    return s.str();
}

Item* Player::getInventoryItem(slots_t slot) const
{
    if (slot < CONST_SLOT_FIRST || slot > CONST_SLOT_LAST) {
        return nullptr;
    }
    return inventory[slot];
}

void Player::addConditionSuppressions(uint32_t conditions)
{
    conditionSuppressions |= conditions;
}

void Player::removeConditionSuppressions(uint32_t conditions)
{
    conditionSuppressions &= ~conditions;
}

Item* Player::getWeapon() const
{
    Item* item = inventory[CONST_SLOT_LEFT];
    if (item && item->getWeaponType() != WEAPON_NONE && item->getWeaponType() != WEAPON_SHIELD && item->getWeaponType() != WEAPON_AMMO) {
        return item;
    }

    item = inventory[CONST_SLOT_RIGHT];
    if (item && item->getWeaponType() != WEAPON_NONE && item->getWeaponType() != WEAPON_SHIELD && item->getWeaponType() != WEAPON_AMMO) {
        return item;
    }

    return nullptr;
}

Item* Player::getAmmunition() const
{
    return inventory[CONST_SLOT_AMMO];
}

int32_t Player::getArmor() const
{
    int32_t armor = 0; // base armor

    static const slots_t armorSlots[] = { CONST_SLOT_HEAD, CONST_SLOT_NECKLACE, CONST_SLOT_ARMOR, CONST_SLOT_LEGS, CONST_SLOT_FEET, CONST_SLOT_RING };
    for (slots_t slot : armorSlots) {
        Item* inventoryItem = inventory[slot];
        if (inventoryItem) {
            armor += inventoryItem->getArmor();
        }
    }

    if (armor > 1) {
        armor = rand() % (armor >> 1) + (armor >> 1);
    }

    return armor;
}

void Player::getShieldAndWeapon(const Item*& shield, const Item*& weapon) const
{
    shield = nullptr;
    weapon = nullptr;

    for (uint32_t slot = CONST_SLOT_RIGHT; slot <= CONST_SLOT_LEFT; slot++) {
        Item* item = inventory[slot];
        if (!item) {
            continue;
        }

        switch (item->getWeaponType()) {
            case WEAPON_NONE:
            break;

            case WEAPON_SHIELD: {
                if (!shield || item->getDefense() > shield->getDefense()) {
                    shield = item;
                }
                break;
            }

            default: { // weapons that are not shields
                weapon = item;
                break;
            }
        }
    }
}

int32_t Player::getDefense()
{
    int32_t totalDefense = 5;
    int32_t defenseSkill = getSkillLevel(SKILL_FIST);

    const Item* weapon;
    const Item* shield;
    getShieldAndWeapon(shield, weapon);

    if (weapon) {
        totalDefense = weapon->getDefense() + 1;

        switch (weapon->getWeaponType()) {
            case WEAPON_AXE:
                defenseSkill = SKILL_AXE;
                break;
            case WEAPON_SWORD:
                defenseSkill = SKILL_SWORD;
                break;
            case WEAPON_CLUB:
                defenseSkill = SKILL_CLUB;
                break;
            case WEAPON_DISTANCE:
            case WEAPON_AMMO:
                defenseSkill = SKILL_DISTANCE;
                break;
            default:
                break;
        }

        defenseSkill = getSkillLevel(defenseSkill);
    }

    if (shield) {
        totalDefense = shield->getDefense() + 1;
        defenseSkill = getSkillLevel(SKILL_SHIELD);
    }

    fightMode_t attackMode = getFightMode();

    if ((followCreature || !attackedCreature) && earliestAttackTime <= OTSYS_TIME()) {
        attackMode = FIGHTMODE_DEFENSE;
    }

    if (attackMode == FIGHTMODE_ATTACK) {
        totalDefense -= 4 * totalDefense / 10;
    } else if (attackMode == FIGHTMODE_DEFENSE) {
        totalDefense += 8 * totalDefense / 10;
    }

    if (totalDefense) {
        int32_t formula = (5 * (defenseSkill) + 50) * totalDefense;
        int32_t randresult = rand() % 100;

        totalDefense = formula * ((rand() % 100 + randresult) / 2) / 10000.;
    }

    return totalDefense;
}

fightMode_t Player::getFightMode() const
{
    return fightMode;
}

uint16_t Player::getClientIcons() const
{
    uint16_t icons = 0;
    for (Condition* condition : conditions) {
        if (!isSuppress(condition->getType())) {
            icons |= condition->getIcons();
        }
    }

    // Game client debugs with 10 or more icons
    // so let's prevent that from happening.
    std::bitset<20> icon_bitset(static_cast<uint64_t>(icons));
    for (size_t pos = 0, bits_set = icon_bitset.count(); bits_set >= 10; ++pos) {
        if (icon_bitset[pos]) {
            icon_bitset.reset(pos);
            --bits_set;
        }
    }
    return icon_bitset.to_ulong();
}

void Player::updateInventoryWeight()
{
    if (hasFlag(PlayerFlag_HasInfiniteCapacity)) {
        return;
    }

    inventoryWeight = 0;
    for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
        const Item* item = inventory[i];
        if (item) {
            inventoryWeight += item->getWeight();
        }
    }
}

void Player::addSkillAdvance(skills_t skill, uint64_t count)
{
    uint64_t currReqTries = vocation->getReqSkillTries(skill, skills[skill].level);
    uint64_t nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
    if (currReqTries >= nextReqTries) {
        //player has reached max skill
        return;
    }

    if (skill == SKILL_MAGLEVEL) {
        count *= g_config.getNumber(g_config.RATE_MAGIC);
    } else {
        count *= g_config.getNumber(g_config.RATE_SKILL);
    }

    if (count == 0) {
        return;
    }

    bool sendUpdateSkills = false;
    while ((skills[skill].tries + count) >= nextReqTries) {
        count -= nextReqTries - skills[skill].tries;
        skills[skill].level++;
        skills[skill].tries = 0;
        skills[skill].percent = 0;

        std::ostringstream ss;
        ss << "You advanced to " << getSkillName(skill) << " level " << skills[skill].level << '.';
        sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());

        g_creatureEvents->playerAdvance(this, skill, (skills[skill].level - 1), skills[skill].level);

        sendUpdateSkills = true;
        currReqTries = nextReqTries;
        nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
        if (currReqTries >= nextReqTries) {
            count = 0;
            break;
        }
    }

    skills[skill].tries += count;

    uint32_t newPercent;
    if (nextReqTries > currReqTries) {
        newPercent = Player::getPercentLevel(skills[skill].tries, nextReqTries);
    } else {
        newPercent = 0;
    }

    if (skills[skill].percent != newPercent) {
        skills[skill].percent = newPercent;
        sendUpdateSkills = true;
    }

    if (sendUpdateSkills) {
        sendSkills();
    }
}

void Player::setVarStats(stats_t stat, int32_t modifier)
{
    varStats[stat] += modifier;

    switch (stat) {
        case STAT_MAXHITPOINTS: {
            if (getHealth() > getMaxHealth()) {
                Creature::changeHealth(getMaxHealth() - getHealth());
            } else {
                g_game.addCreatureHealth(this);
            }
            break;
        }

        case STAT_MAXMANAPOINTS: {
            if (getMana() > getMaxMana()) {
                Creature::changeMana(getMaxMana() - getMana());
            }
            break;
        }

        default: {
            break;
        }
    }
}

int32_t Player::getDefaultStats(stats_t stat) const
{
    switch (stat) {
        case STAT_MAXHITPOINTS: return healthMax;
        case STAT_MAXMANAPOINTS: return manaMax;
        case STAT_MAGICPOINTS: return getBaseMagicLevel();
        default: return 0;
    }
}

void Player::addContainer(uint8_t cid, Container* container)
{
    if (cid > 0xF) {
        return;
    }

    auto it = openContainers.find(cid);
    if (it != openContainers.end()) {
        OpenContainer& openContainer = it->second;
        openContainer.container = container;
        openContainer.index = 0;
    } else {
        OpenContainer openContainer;
        openContainer.container = container;
        openContainer.index = 0;
        openContainers[cid] = openContainer;
    }
}

void Player::closeContainer(uint8_t cid)
{
    auto it = openContainers.find(cid);
    if (it == openContainers.end()) {
        return;
    }

    openContainers.erase(it);
}

void Player::setContainerIndex(uint8_t cid, uint16_t index)
{
    auto it = openContainers.find(cid);
    if (it == openContainers.end()) {
        return;
    }
    it->second.index = index;
}

Container* Player::getContainerByID(uint8_t cid)
{
    auto it = openContainers.find(cid);
    if (it == openContainers.end()) {
        return nullptr;
    }
    return it->second.container;
}

int8_t Player::getContainerID(const Container* container) const
{
    for (const auto& it : openContainers) {
        if (it.second.container == container) {
            return it.first;
        }
    }
    return -1;
}

uint16_t Player::getContainerIndex(uint8_t cid) const
{
    auto it = openContainers.find(cid);
    if (it == openContainers.end()) {
        return 0;
    }
    return it->second.index;
}

uint16_t Player::getLookCorpse() const
{
    if (sex == PLAYERSEX_FEMALE) {
        return ITEM_FEMALE_CORPSE;
    } else {
        return ITEM_MALE_CORPSE;
    }
}

void Player::addStorageValue(const uint32_t key, const int32_t value)
{
    if (value != -1) {
        storageMap[key] = value;
    } else {
        storageMap.erase(key);
    }
}

bool Player::getStorageValue(const uint32_t key, int32_t& value) const
{
    auto it = storageMap.find(key);
    if (it == storageMap.end()) {
        value = 0;
        return false;
    }

    value = it->second;
    return true;
}

bool Player::canSee(const Position& pos) const
{
    if (!client) {
        return false;
    }
    return client->canSee(pos);
}

bool Player::canSeeCreature(const Creature* creature) const
{
    if (creature == this) {
        return true;
    }

    if (creature->isInGhostMode() && !group->access) {
        return false;
    }

    if (!creature->getPlayer() && !canSeeInvisibility() && creature->isInvisible()) {
        return false;
    }

    return true;
}

void Player::onReceiveMail() const
{
    if (isNearDepotBox()) {
        sendTextMessage(MESSAGE_EVENT_ADVANCE, "New mail has arrived.");
    }
}

bool Player::isNearDepotBox() const
{
    const Position& pos = getPosition();
    for (int32_t cx = -1; cx <= 1; ++cx) {
        for (int32_t cy = -1; cy <= 1; ++cy) {
            Tile* tile = g_game.map.getTile(pos.x + cx, pos.y + cy, pos.z);
            if (!tile) {
                continue;
            }

            if (tile->hasFlag(TILESTATE_DEPOT)) {
                return true;
            }
        }
    }
    return false;
}

DepotLocker* Player::getDepotLocker(uint32_t depotId, bool autoCreate)
{
    auto it = depotLockerMap.find(depotId);
    if (it != depotLockerMap.end()) {
        return it->second;
    }

    if (autoCreate) {
        DepotLocker* depotLocker = new DepotLocker(ITEM_LOCKER1);
        depotLocker->setDepotId(depotId);
        Item* depotItem = Item::CreateItem(ITEM_DEPOT);
        if (depotItem) {
            depotLocker->internalAddThing(depotItem);
        }
        depotLockerMap[depotId] = depotLocker;
        return depotLocker;
    }

    return nullptr;
}

void Player::sendCancelMessage(ReturnValue message) const
{
    sendCancelMessage(getReturnMessage(message));
}

void Player::sendStats()
{
    if (client) {
        client->sendStats();
    }
}

void Player::sendPing()
{
    int64_t timeNow = OTSYS_TIME();

    bool hasLostConnection = false;
    if ((timeNow - lastPing) >= 5000) {
        lastPing = timeNow;
        if (client) {
            client->sendPing();
        } else {
            hasLostConnection = true;
        }
    }

    int64_t noPongTime = timeNow - lastPong;
    if ((hasLostConnection || noPongTime >= 7000) && attackedCreature && attackedCreature->getPlayer()) {
        setAttackedCreature(nullptr);
    }

    if (noPongTime >= 60000 && canLogout()) {
        if (g_creatureEvents->playerLogout(this)) {
            if (client) {
                client->logout(true, true);
            } else {
                g_game.removeCreature(this, true);
            }
        }
    }
}

Item* Player::getWriteItem(uint32_t& windowTextId, uint16_t& maxWriteLen)
{
    windowTextId = this->windowTextId;
    maxWriteLen = this->maxWriteLen;
    return writeItem;
}

void Player::setWriteItem(Item* item, uint16_t maxWriteLen /*= 0*/)
{
    windowTextId++;

    if (writeItem) {
        writeItem->decrementReferenceCounter();
    }

    if (item) {
        writeItem = item;
        this->maxWriteLen = maxWriteLen;
        writeItem->incrementReferenceCounter();
    } else {
        writeItem = nullptr;
        this->maxWriteLen = 0;
    }
}

House* Player::getEditHouse(uint32_t& windowTextId, uint32_t& listId)
{
    windowTextId = this->windowTextId;
    listId = this->editListId;
    return editHouse;
}

void Player::setEditHouse(House* house, uint32_t listId /*= 0*/)
{
    windowTextId++;
    editHouse = house;
    editListId = listId;
}

void Player::sendHouseWindow(House* house, uint32_t listId) const
{
    if (!client) {
        return;
    }

    std::string text;
    if (house->getAccessList(listId, text)) {
        client->sendHouseWindow(windowTextId, text);
    }
}

//container
void Player::sendAddContainerItem(const Container* container, const Item* item)
{
    if (!client) {
        return;
    }

    for (const auto& it : openContainers) {
        const OpenContainer& openContainer = it.second;
        if (openContainer.container != container) {
            continue;
        }

        if (openContainer.index >= container->capacity()) {
            item = container->getItemByIndex(openContainer.index - 1);
        }
        client->sendAddContainerItem(it.first, item);
    }
}

void Player::sendUpdateContainerItem(const Container* container, uint16_t slot, const Item* newItem)
{
    if (!client) {
        return;
    }

    for (const auto& it : openContainers) {
        const OpenContainer& openContainer = it.second;
        if (openContainer.container != container) {
            continue;
        }

        if (slot < openContainer.index) {
            continue;
        }

        uint16_t pageEnd = openContainer.index + container->capacity();
        if (slot >= pageEnd) {
            continue;
        }

        client->sendUpdateContainerItem(it.first, slot, newItem);
    }
}

void Player::sendRemoveContainerItem(const Container* container, uint16_t slot)
{
    if (!client) {
        return;
    }

    for (auto& it : openContainers) {
        OpenContainer& openContainer = it.second;
        if (openContainer.container != container) {
            continue;
        }

        uint16_t& firstIndex = openContainer.index;
        if (firstIndex > 0 && firstIndex >= container->size() - 1) {
            firstIndex -= container->capacity();
            sendContainer(it.first, container, false, firstIndex);
        }

        client->sendRemoveContainerItem(it.first, std::max<uint16_t>(slot, firstIndex));
    }
}

void Player::onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem,
    const ItemType& oldType, const Item* newItem, const ItemType& newType)
{
    Creature::onUpdateTileItem(tile, pos, oldItem, oldType, newItem, newType);

    if (oldItem != newItem) {
        onRemoveTileItem(tile, pos, oldType, oldItem);
    }

    if (tradeState != TRADE_TRANSFER) {
        if (tradeItem && oldItem == tradeItem) {
            g_game.internalCloseTrade(this);
        }
    }
}

void Player::onRemoveTileItem(const Tile* tile, const Position& pos, const ItemType& iType,
    const Item* item)
{
    Creature::onRemoveTileItem(tile, pos, iType, item);

    if (tradeState != TRADE_TRANSFER) {
        checkTradeState(item);

        if (tradeItem) {
            const Container* container = item->getContainer();
            if (container && container->isHoldingItem(tradeItem)) {
                g_game.internalCloseTrade(this);
            }
        }
    }
}

void Player::onCreatureAppear(Creature* creature, bool isLogin)
{
    Creature::onCreatureAppear(creature, isLogin);

    if (isLogin && creature == this) {
        for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
            Item* item = inventory[slot];
            if (item) {
                item->startDecaying();
                g_moveEvents->onPlayerEquip(this, item, static_cast<slots_t>(slot), false);
            }
        }

        for (Condition* condition : storedConditionList) {
            addCondition(condition);
        }
        storedConditionList.clear();

        BedItem* bed = g_game.getBedBySleeper(guid);
        if (bed) {
            bed->wakeUp(this);
        }

        std::cout << name << " has logged in." << std::endl;

        if (guild) {
            guild->addMember(this);
        }

        int32_t offlineTime;
        if (getLastLogout() != 0) {
            // Not counting more than 21 days to prevent overflow when multiplying with 1000 (for milliseconds).
            offlineTime = std::min<int32_t>(time(nullptr) - getLastLogout(), 86400 * 21);
        } else {
            offlineTime = 0;
        }

        for (Condition* condition : getMuteConditions()) {
            condition->setTicks(condition->getTicks() - (offlineTime * 1000));
            if (condition->getTicks() <= 0) {
                removeCondition(condition);
            }
        }

        g_game.checkPlayersRecord();
        IOLoginData::updateOnlineStatus(guid, true);
    }
}

void Player::onAttackedCreatureDisappear(bool isLogout)
{
    sendCancelTarget();

    if (!isLogout) {
        sendTextMessage(MESSAGE_STATUS_SMALL, "Target lost.");
    }
}

void Player::onFollowCreatureDisappear(bool isLogout)
{
    sendCancelTarget();

    if (!isLogout) {
        sendTextMessage(MESSAGE_STATUS_SMALL, "Target lost.");
    }
}

void Player::onChangeZone(ZoneType_t zone)
{
    if (zone == ZONE_PROTECTION) {
        if (attackedCreature && !hasFlag(PlayerFlag_IgnoreProtectionZone)) {
            setAttackedCreature(nullptr);
            onAttackedCreatureDisappear(false);
        }
    }

    sendIcons();
}

void Player::onAttackedCreatureChangeZone(ZoneType_t zone)
{
    if (zone == ZONE_PROTECTION) {
        if (!hasFlag(PlayerFlag_IgnoreProtectionZone)) {
            setAttackedCreature(nullptr);
            onAttackedCreatureDisappear(false);
        }
    } else if (zone == ZONE_NOPVP) {
        if (attackedCreature->getPlayer()) {
            if (!hasFlag(PlayerFlag_IgnoreProtectionZone)) {
                setAttackedCreature(nullptr);
                onAttackedCreatureDisappear(false);
            }
        }
    } else if (zone == ZONE_NORMAL) {
        //attackedCreature can leave a pvp zone if not pzlocked
        if (g_game.getWorldType() == WORLD_TYPE_NO_PVP) {
            if (attackedCreature->getPlayer()) {
                setAttackedCreature(nullptr);
                onAttackedCreatureDisappear(false);
            }
        }
    }
}

void Player::onRemoveCreature(Creature* creature, bool isLogout)
{
    Creature::onRemoveCreature(creature, isLogout);

    if (creature == this) {
        if (isLogout) {
            loginPosition = getPosition();
        }

        lastLogout = time(nullptr);

        if (eventWalk != 0) {
            setFollowCreature(nullptr);
        }

        if (tradePartner) {
            g_game.internalCloseTrade(this);
        }

        clearPartyInvitations();

        if (party) {
            party->leaveParty(this);
        }

        g_chat->removeUserFromAllChannels(*this);

        std::cout << getName() << " has logged out." << std::endl;

        if (guild) {
            guild->removeMember(this);
        }

        IOLoginData::updateOnlineStatus(guid, false);

        bool saved = false;
        for (uint32_t tries = 0; tries < 3; ++tries) {
            if (IOLoginData::savePlayer(this)) {
                saved = true;
                break;
            }
        }

        if (!saved) {
            std::cout << "Error while saving player: " << getName() << std::endl;
        }
    }
}

void Player::onWalk(Direction& dir)
{
    Creature::onWalk(dir);
    setNextActionTask(nullptr);
    setNextAction(OTSYS_TIME() + getStepDuration(dir));
}

void Player::onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos,
    const Tile* oldTile, const Position& oldPos, bool teleport)
{
    Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport);

    if (hasFollowPath && (creature == followCreature || (creature == this && followCreature))) {
        isUpdatingPath = false;
        g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, &g_game, getID())));
    }

    if (creature != this) {
        return;
    }

    if (tradeState != TRADE_TRANSFER) {
        //check if we should close trade
        if (tradeItem && !Position::areInRange<1, 1, 0>(tradeItem->getPosition(), getPosition())) {
            g_game.internalCloseTrade(this);
        }

        if (tradePartner && !Position::areInRange<2, 2, 0>(tradePartner->getPosition(), getPosition())) {
            g_game.internalCloseTrade(this);
        }
    }

    if (party) {
        party->updateSharedExperience();
    }

    if (teleport || oldPos.z != newPos.z) {
        int32_t ticks = g_config.getNumber(ConfigManager::STAIRHOP_DELAY);
        if (ticks > 0) {
            if (Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks, 0)) {
                addCondition(condition);
            }
        }
    }
}

//container
void Player::onAddContainerItem(const Item* item)
{
    checkTradeState(item);
}

void Player::onUpdateContainerItem(const Container* container, const Item* oldItem, const Item* newItem)
{
    if (oldItem != newItem) {
        onRemoveContainerItem(container, oldItem);
    }

    if (tradeState != TRADE_TRANSFER) {
        checkTradeState(oldItem);
    }
}

void Player::onRemoveContainerItem(const Container* container, const Item* item)
{
    if (tradeState != TRADE_TRANSFER) {
        checkTradeState(item);

        if (tradeItem) {
            if (tradeItem->getParent() != container && container->isHoldingItem(tradeItem)) {
                g_game.internalCloseTrade(this);
            }
        }
    }
}

void Player::onCloseContainer(const Container* container)
{
    if (!client) {
        return;
    }

    for (const auto& it : openContainers) {
        if (it.second.container == container) {
            client->sendCloseContainer(it.first);
        }
    }
}

void Player::onSendContainer(const Container* container)
{
    if (!client) {
        return;
    }

    bool hasParent = container->hasParent();
    for (const auto& it : openContainers) {
        const OpenContainer& openContainer = it.second;
        if (openContainer.container == container) {
            client->sendContainer(it.first, container, hasParent, openContainer.index);
        }
    }
}

//inventory
void Player::onUpdateInventoryItem(Item* oldItem, Item* newItem)
{
    if (oldItem != newItem) {
        onRemoveInventoryItem(oldItem);
    }

    if (tradeState != TRADE_TRANSFER) {
        checkTradeState(oldItem);
    }
}

void Player::onRemoveInventoryItem(Item* item)
{
    if (tradeState != TRADE_TRANSFER) {
        checkTradeState(item);

        if (tradeItem) {
            const Container* container = item->getContainer();
            if (container && container->isHoldingItem(tradeItem)) {
                g_game.internalCloseTrade(this);
            }
        }
    }
}

void Player::checkTradeState(const Item* item)
{
    if (!tradeItem || tradeState == TRADE_TRANSFER) {
        return;
    }

    if (tradeItem == item) {
        g_game.internalCloseTrade(this);
    } else {
        const Container* container = dynamic_cast<const Container*>(item->getParent());
        while (container) {
            if (container == tradeItem) {
                g_game.internalCloseTrade(this);
                break;
            }

            container = dynamic_cast<const Container*>(container->getParent());
        }
    }
}

void Player::setNextWalkActionTask(SchedulerTask* task)
{
    if (walkTaskEvent != 0) {
        g_scheduler.stopEvent(walkTaskEvent);
        walkTaskEvent = 0;
    }

    delete walkTask;
    walkTask = task;
}

void Player::setNextWalkTask(SchedulerTask* task)
{
    if (nextStepEvent != 0) {
        g_scheduler.stopEvent(nextStepEvent);
        nextStepEvent = 0;
    }

    if (task) {
        nextStepEvent = g_scheduler.addEvent(task);
        resetIdleTime();
    }
}

void Player::setNextActionTask(SchedulerTask* task)
{
    if (actionTaskEvent != 0) {
        g_scheduler.stopEvent(actionTaskEvent);
        actionTaskEvent = 0;
    }

    if (task) {
        actionTaskEvent = g_scheduler.addEvent(task);
        resetIdleTime();
    }
}

uint32_t Player::getNextActionTime() const
{
    return std::max<int64_t>(SCHEDULER_MINTICKS, nextAction - OTSYS_TIME());
}

void Player::onThink(uint32_t interval)
{
    Creature::onThink(interval);

    sendPing();

    MessageBufferTicks += interval;
    if (MessageBufferTicks >= 1500) {
        MessageBufferTicks = 0;
        addMessageBuffer();
    }

    if (!getTile()->hasFlag(TILESTATE_NOLOGOUT) && !isAccessPlayer()) {
        idleTime += interval;
        const int32_t kickAfterMinutes = g_config.getNumber(ConfigManager::KICK_AFTER_MINUTES);
        if ((!pzLocked && OTSYS_TIME() - lastPong >= 60000) || idleTime > (kickAfterMinutes * 60000) + 60000) {
            kickPlayer(true);
        } else if (client && idleTime == 60000 * kickAfterMinutes) {
            std::ostringstream ss;
            ss << "You have been idle for " << kickAfterMinutes << " minutes. You will be disconnected in one minute if you are still idle then.";
            client->sendTextMessage(TextMessage(MESSAGE_STATUS_WARNING, ss.str()));
        }
    }

    if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
        checkSkullTicks();
    }
}

uint32_t Player::isMuted() const
{
    if (hasFlag(PlayerFlag_CannotBeMuted)) {
        return 0;
    }

    int32_t muteTicks = 0;
    for (Condition* condition : conditions) {
        if (condition->getType() == CONDITION_MUTED && condition->getTicks() > muteTicks) {
            muteTicks = condition->getTicks();
        }
    }
    return static_cast<uint32_t>(muteTicks) / 1000;
}

void Player::addMessageBuffer()
{
    if (MessageBufferCount > 0 && g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER) != 0 && !hasFlag(PlayerFlag_CannotBeMuted)) {
        --MessageBufferCount;
    }
}

void Player::removeMessageBuffer()
{
    if (hasFlag(PlayerFlag_CannotBeMuted)) {
        return;
    }

    const int32_t maxMessageBuffer = g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER);
    if (maxMessageBuffer != 0 && MessageBufferCount <= maxMessageBuffer + 1) {
        if (++MessageBufferCount > maxMessageBuffer) {
            uint32_t muteCount = 1;
            auto it = muteCountMap.find(guid);
            if (it != muteCountMap.end()) {
                muteCount = it->second;
            }

            uint32_t muteTime = 5 * muteCount * muteCount;
            muteCountMap[guid] = muteCount + 1;
            Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, muteTime * 1000, 0);
            addCondition(condition);

            std::ostringstream ss;
            ss << "You are muted for " << muteTime << " seconds.";
            sendTextMessage(MESSAGE_STATUS_SMALL, ss.str());
        }
    }
}

void Player::drainHealth(Creature* attacker, int32_t damage)
{
    Creature::drainHealth(attacker, damage);
    sendStats();
}

void Player::drainMana(Creature* attacker, int32_t manaLoss)
{
    Creature::drainMana(attacker, manaLoss);
    sendStats();
}

void Player::addManaSpent(uint64_t amount)
{
    if (hasFlag(PlayerFlag_NotGainMana)) {
        return;
    }

    uint64_t currReqMana = vocation->getReqMana(magLevel);
    uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
    if (currReqMana >= nextReqMana) {
        //player has reached max magic level
        return;
    }

    amount *= g_config.getNumber(g_config.RATE_MAGIC);

    if (amount == 0) {
        return;
    }

    bool sendUpdateStats = false;
    while ((manaSpent + amount) >= nextReqMana) {
        amount -= nextReqMana - manaSpent;

        magLevel++;
        manaSpent = 0;

        std::ostringstream ss;
        ss << "You advanced to magic level " << magLevel << '.';
        sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());

        g_creatureEvents->playerAdvance(this, SKILL_MAGLEVEL, magLevel - 1, magLevel);

        sendUpdateStats = true;
        currReqMana = nextReqMana;
        nextReqMana = vocation->getReqMana(magLevel + 1);
        if (currReqMana >= nextReqMana) {
            return;
        }
    }

    manaSpent += amount;

    uint8_t oldPercent = magLevelPercent;
    if (nextReqMana > currReqMana) {
        magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
    } else {
        magLevelPercent = 0;
    }

    if (oldPercent != magLevelPercent) {
        sendUpdateStats = true;
    }

    if (sendUpdateStats) {
        sendStats();
    }
}

void Player::addExperience(uint64_t exp, bool sendText/* = false*/, bool applyStages/* = true*/)
{
    uint64_t currLevelExp = Player::getExpForLevel(level);
    uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
    if (currLevelExp >= nextLevelExp) {
        //player has reached max level
        levelPercent = 0;
        sendStats();
        return;
    }

    if (getSoul() < getVocation()->getSoulMax() && exp >= level) {
        Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SOUL, 4 * 60 * 1000, 0);
        condition->setParam(CONDITION_PARAM_SOULGAIN, 1);
        condition->setParam(CONDITION_PARAM_SOULTICKS, vocation->getSoulGainTicks() * 1000);
        addCondition(condition);
    }

    if (applyStages) {
        exp *= g_game.getExperienceStage(level);
    }

    if (exp == 0) {
        return;
    }

    experience += exp;

    if (sendText) {
        g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(exp));
    }

    uint32_t prevLevel = level;
    while (experience >= nextLevelExp) {
        ++level;
        healthMax += vocation->getHPGain();
        health += vocation->getHPGain();
        manaMax += vocation->getManaGain();
        mana += vocation->getManaGain();
        capacity += vocation->getCapGain();

        currLevelExp = nextLevelExp;
        nextLevelExp = Player::getExpForLevel(level + 1);
        if (currLevelExp >= nextLevelExp) {
            //player has reached max level
            break;
        }
    }

    if (prevLevel != level) {
        updateBaseSpeed();
        setBaseSpeed(getBaseSpeed());

        g_game.changeSpeed(this, 0);
        g_game.addCreatureHealth(this);

        if (party) {
            party->updateSharedExperience();
        }

        g_creatureEvents->playerAdvance(this, SKILL_LEVEL, prevLevel, level);

        std::ostringstream ss;
        ss << "You advanced from Level " << prevLevel << " to Level " << level << '.';
        sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
    }

    if (nextLevelExp > currLevelExp) {
        levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
    } else {
        levelPercent = 0;
    }
    sendStats();
}

void Player::removeExperience(uint64_t exp)
{
    if (experience == 0 || exp == 0) {
        return;
    }

    experience = std::max<int64_t>(0, experience - exp);

    uint32_t oldLevel = level;
    uint64_t currLevelExp = Player::getExpForLevel(level);

    while (level > 1 && experience < currLevelExp) {
        --level;
        healthMax = std::max<int32_t>(150, std::max<int32_t>(0, healthMax - vocation->getHPGain()));
        manaMax = std::max<int32_t>(0, manaMax - vocation->getManaGain());
        capacity = std::max<int32_t>(400, std::max<int32_t>(0, capacity - vocation->getCapGain()));
        currLevelExp = Player::getExpForLevel(level);
    }

    if (oldLevel != level) {
        health = healthMax;
        mana = manaMax;

        updateBaseSpeed();
        setBaseSpeed(getBaseSpeed());

        g_game.changeSpeed(this, 0);
        g_game.addCreatureHealth(this);

        if (party) {
            party->updateSharedExperience();
        }

        std::ostringstream ss;
        ss << "You were downgraded from Level " << oldLevel << " to Level " << level << '.';
        sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
    }

    uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
    if (nextLevelExp > currLevelExp) {
        levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
    } else {
        levelPercent = 0;
    }
    sendStats();
}

uint8_t Player::getPercentLevel(uint64_t count, uint64_t nextLevelCount)
{
    if (nextLevelCount == 0) {
        return 0;
    }

    uint8_t result = (count * 100) / nextLevelCount;
    if (result > 100) {
        return 0;
    }
    return result;
}

uint16_t Player::getDropLootPercent()
{
    return 10;
}

void Player::onBlockHit()
{
    if (shieldBlockCount > 0) {
        --shieldBlockCount;

        if (hasShield()) {
            addSkillAdvance(SKILL_SHIELD, 1);
        }
    }
}

void Player::onAttackedCreatureBlockHit(BlockType_t blockType)
{
    lastAttackBlockType = blockType;

    switch (blockType) {
        case BLOCK_NONE: {
            addAttackSkillPoint = true;
            bloodHitCount = 30;
            shieldBlockCount = 30;
            break;
        }

        case BLOCK_DEFENSE:
        case BLOCK_ARMOR: {
            //need to draw blood every 30 hits
            if (bloodHitCount > 0) {
                addAttackSkillPoint = true;
                --bloodHitCount;
            } else {
                addAttackSkillPoint = false;
            }
            break;
        }

        default: {
            addAttackSkillPoint = false;
            break;
        }
    }
}

bool Player::hasShield() const
{
    Item* item = inventory[CONST_SLOT_LEFT];
    if (item && item->getWeaponType() == WEAPON_SHIELD) {
        return true;
    }

    item = inventory[CONST_SLOT_RIGHT];
    if (item && item->getWeaponType() == WEAPON_SHIELD) {
        return true;
    }
    return false;
}

BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
    bool checkDefense /* = false*/, bool checkArmor /* = false*/, bool field /* = false*/)
{
    BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor, field);

    if (attacker) {
        sendCreatureSquare(attacker, SQ_COLOR_BLACK);
    }

    if (blockType != BLOCK_NONE) {
        return blockType;
    }

    if (damage > 0) {
        for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
            if (!isItemAbilityEnabled(static_cast<slots_t>(slot))) {
                continue;
            }

            Item* item = inventory[slot];
            if (!item) {
                continue;
            }

            const ItemType& it = Item::items[item->getID()];
            if (it.abilities) {
                const int16_t& absorbPercent = it.abilities->absorbPercent[combatTypeToIndex(combatType)];
                if (absorbPercent != 0) {
                    damage -= std::round(damage * (absorbPercent / 100.));

                    uint16_t charges = item->getCharges() - 1;
                    if (charges != 0) {
                        g_game.transformItem(item, item->getID(), charges);
                    } else {
                        g_game.internalRemoveItem(item);
                    }
                }

                if (field) {
                    const int16_t& fieldAbsorbPercent = it.abilities->fieldAbsorbPercent[combatTypeToIndex(combatType)];
                    if (fieldAbsorbPercent != 0) {
                        damage -= std::round(damage * (fieldAbsorbPercent / 100.));

                        uint16_t charges = item->getCharges();
                        if (charges != 0) {
                            if (charges - 1 == 0) {
                                g_game.internalRemoveItem(item);
                            } else {
                                g_game.transformItem(item, item->getID(), charges - 1);
                            }
                        }
                    }
                }
            }
        }

        if (damage <= 0) {
            damage = 0;
            blockType = BLOCK_ARMOR;
        }
    }

    return blockType;
}

uint32_t Player::getIP() const
{
    if (client) {
        return client->getIP();
    }

    return 0;
}

void Player::dropLoot(Container* corpse, Creature*)
{
    if (corpse && lootDrop) {
        Skulls_t playerSkull = getSkull();
        if (inventory[CONST_SLOT_NECKLACE] && inventory[CONST_SLOT_NECKLACE]->getID() == ITEM_AMULETOFLOSS && playerSkull != SKULL_RED) {
            g_game.internalRemoveItem(inventory[CONST_SLOT_NECKLACE], 1);
        } else {
            for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
                Item* item = inventory[i];
                if (item) {
                    if (playerSkull == SKULL_RED || item->getContainer() || uniform_random(1, 100) <= getDropLootPercent()) {
                        g_game.internalMoveItem(this, corpse, INDEX_WHEREEVER, item, item->getItemCount(), 0);
                        sendInventoryItem(static_cast<slots_t>(i), nullptr);
                    }
                }
            }
        }
    }
}

void Player::death(Creature* lastHitCreature)
{
    loginPosition = town->getTemplePosition();

    if (skillLoss) {
        //Magic level loss
        uint64_t sumMana = 0;
        uint64_t lostMana = 0;

        //sum up all the mana
        for (uint32_t i = 1; i <= magLevel; ++i) {
            sumMana += vocation->getReqMana(i);
        }

        sumMana += manaSpent;

        double deathLossPercent = getLostPercent();

        lostMana = static_cast<uint64_t>(sumMana * deathLossPercent);

        while (lostMana > manaSpent && magLevel > 0) {
            lostMana -= manaSpent;
            manaSpent = vocation->getReqMana(magLevel);
            magLevel--;
        }

        manaSpent -= lostMana;

        uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
        if (nextReqMana > vocation->getReqMana(magLevel)) {
            magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
        } else {
            magLevelPercent = 0;
        }

        //Skill loss
        for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { //for each skill
            uint64_t sumSkillTries = 0;
            for (uint16_t c = 11; c <= skills[i].level; ++c) { //sum up all required tries for all skill levels
                sumSkillTries += vocation->getReqSkillTries(i, c);
            }

            sumSkillTries += skills[i].tries;

            uint32_t lostSkillTries = static_cast<uint32_t>(sumSkillTries * deathLossPercent);
            while (lostSkillTries > skills[i].tries) {
                lostSkillTries -= skills[i].tries;

                if (skills[i].level <= 10) {
                    skills[i].level = 10;
                    skills[i].tries = 0;
                    lostSkillTries = 0;
                    break;
                }

                skills[i].tries = vocation->getReqSkillTries(i, skills[i].level);
                skills[i].level--;
            }

            skills[i].tries = std::max<int32_t>(0, skills[i].tries - lostSkillTries);
            skills[i].percent = Player::getPercentLevel(skills[i].tries, vocation->getReqSkillTries(i, skills[i].level));
        }

        //Level loss
        uint64_t expLoss = static_cast<uint64_t>(experience * deathLossPercent);

        if (expLoss != 0) {
            uint32_t oldLevel = level;

            experience -= expLoss;

            while (level > 1 && experience < Player::getExpForLevel(level)) {
                --level;
                healthMax = std::max<int32_t>(0, healthMax - vocation->getHPGain());
                manaMax = std::max<int32_t>(0, manaMax - vocation->getManaGain());
                capacity = std::max<int32_t>(0, capacity - vocation->getCapGain());
            }

            if (oldLevel != level) {
                std::ostringstream ss;
                ss << "You were downgraded from Level " << oldLevel << " to Level " << level << '.';
                sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
            }

            uint64_t currLevelExp = Player::getExpForLevel(level);
            uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
            if (nextLevelExp > currLevelExp) {
                levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
            } else {
                levelPercent = 0;
            }
        }

        std::bitset<6> bitset(blessings);
        if (bitset[5]) {
            if (Player::lastHitIsPlayer(lastHitCreature)) {
                bitset.reset(5);
                blessings = bitset.to_ulong();
            } else {
                blessings = 32;
            }
        } else {
            blessings = 0;
        }

        sendStats();
        sendSkills();

        health = healthMax;
        mana = manaMax;

        auto it = conditions.begin(), end = conditions.end();
        while (it != end) {
            Condition* condition = *it;
            if (condition->isPersistent()) {
                it = conditions.erase(it);

                condition->endCondition(this);
                onEndCondition(condition->getType());
                delete condition;
            } else {
                ++it;
            }
        }

        // Teleport newbies to newbie island
        if (g_config.getBoolean(ConfigManager::TELEPORT_NEWBIES)) {
            if (getVocationId() != VOCATION_NONE && level <= static_cast<uint32_t>(g_config.getNumber(ConfigManager::NEWBIE_LEVEL_THRESHOLD))) {
                Town* newbieTown = g_game.map.towns.getTown(g_config.getNumber(ConfigManager::NEWBIE_TOWN));
                if (newbieTown) {
                    // Restart stats
                    level = 1;
                    experience = 0;
                    levelPercent = 0;
                    capacity = 400;
                    health = 150;
                    healthMax = 150;
                    mana = 0;
                    manaMax = 0;
                    magLevel = 0;
                    magLevelPercent = 0;
                    manaSpent = 0;
                    setVocation(0);

                    // Restart skills
                    for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { //for each skill
                        skills[i].level = 10;
                        skills[i].tries = 0;
                        skills[i].percent = 0;
                    }

                    // Restart town
                    setTown(newbieTown);
                    loginPosition = getTemplePosition();

                    // Restart first items
                    addStorageValue(30017, 1);

                    // Restart items
                    for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; slot++)
                    {
                        Item* item = inventory[slot];
                        if (item) {
                            g_game.internalRemoveItem(item, item->getItemCount());
                        }
                    }
                } else {
                    std::cout << "[Warning - Player:death] Newbie teletransportation is enabled, newbie town does not exist." << std::endl;
                }
            }
        }
    } else {
        setLossSkill(true);

        auto it = conditions.begin(), end = conditions.end();
        while (it != end) {
            Condition* condition = *it;
            if (condition->isPersistent()) {
                it = conditions.erase(it);

                condition->endCondition(this);
                onEndCondition(condition->getType());
                delete condition;
            }
            else {
                ++it;
            }
        }

        health = healthMax;
        g_game.internalTeleport(this, getTemplePosition(), true);
        g_game.addCreatureHealth(this);
        onThink(EVENT_CREATURE_THINK_INTERVAL);
        onIdleStatus();
        sendStats();
    }
}

bool Player::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified)
{
    if (getZone() != ZONE_PVP || !Player::lastHitIsPlayer(lastHitCreature)) {
        return Creature::dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
    }

    setDropLoot(true);
    return false;
}

Item* Player::getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature)
{
    Item* corpse = Creature::getCorpse(lastHitCreature, mostDamageCreature);
    if (corpse && corpse->getContainer()) {
        std::ostringstream ss;
        if (lastHitCreature) {
            ss << "You recognize " << getNameDescription() << ". " << (getSex() == PLAYERSEX_FEMALE ? "She" : "He") << " was killed by " << lastHitCreature->getNameDescription() << '.';
        } else {
            ss << "You recognize " << getNameDescription() << '.';
        }

        corpse->setSpecialDescription(ss.str());
    }
    return corpse;
}

void Player::addInFightTicks(bool pzlock /*= false*/)
{
    if (hasFlag(PlayerFlag_NotGainInFight)) {
        return;
    }

    if (pzlock) {
        pzLocked = true;
    }

    Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_config.getNumber(ConfigManager::PZ_LOCKED), 0);
    addCondition(condition);
}

void Player::removeList()
{
    g_game.removePlayer(this);

    for (const auto& it : g_game.getPlayers()) {
        it.second->notifyStatusChange(this, VIPSTATUS_OFFLINE);
    }
}

void Player::addList()
{
    for (const auto& it : g_game.getPlayers()) {
        it.second->notifyStatusChange(this, VIPSTATUS_ONLINE);
    }

    g_game.addPlayer(this);
}

void Player::kickPlayer(bool displayEffect)
{
    g_creatureEvents->playerLogout(this);
    if (client) {
        client->logout(displayEffect, true);
    } else {
        g_game.removeCreature(this);
    }
}

void Player::notifyStatusChange(Player* loginPlayer, VipStatus_t status)
{
    if (!client) {
        return;
    }

    auto it = VIPList.find(loginPlayer->guid);
    if (it == VIPList.end()) {
        return;
    }

    client->sendUpdatedVIPStatus(loginPlayer->guid, status);

    if (status == VIPSTATUS_ONLINE) {
        client->sendTextMessage(TextMessage(MESSAGE_STATUS_SMALL, loginPlayer->getName() + " has logged in."));
    } else if (status == VIPSTATUS_OFFLINE) {
        client->sendTextMessage(TextMessage(MESSAGE_STATUS_SMALL, loginPlayer->getName() + " has logged out."));
    }
}

bool Player::removeVIP(uint32_t vipGuid)
{
    if (VIPList.erase(vipGuid) == 0) {
        return false;
    }

    IOLoginData::removeVIPEntry(accountNumber, vipGuid);
    return true;
}

bool Player::addVIP(uint32_t vipGuid, const std::string& vipName, VipStatus_t status)
{
    if (guid == vipGuid) {
        sendTextMessage(MESSAGE_STATUS_SMALL, "You cannot add yourself.");
        return false;
    }

    if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 100) {
        sendTextMessage(MESSAGE_STATUS_SMALL, "You cannot add more buddies.");
        return false;
    }

    auto result = VIPList.insert(vipGuid);
    if (!result.second) {
        sendTextMessage(MESSAGE_STATUS_SMALL, "This player is already in your list.");
        return false;
    }

    IOLoginData::addVIPEntry(accountNumber, vipGuid);
    if (client) {
        client->sendVIP(vipGuid, vipName, status);
    }
    return true;
}

bool Player::addVIPInternal(uint32_t vipGuid)
{
    if (guid == vipGuid) {
        return false;
    }

    if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 100) {
        return false;
    }

    return VIPList.insert(vipGuid).second;
}

//close container and its child containers
void Player::autoCloseContainers(const Container* container)
{
    std::vector<uint32_t> closeList;
    for (const auto& it : openContainers) {
        Container* tmpContainer = it.second.container;
        while (tmpContainer) {
            if (tmpContainer->isRemoved() || tmpContainer == container) {
                closeList.push_back(it.first);
                break;
            }

            tmpContainer = dynamic_cast<Container*>(tmpContainer->getParent());
        }
    }

    for (uint32_t containerId : closeList) {
        closeContainer(containerId);
        if (client) {
            client->sendCloseContainer(containerId);
        }
    }
}

bool Player::hasCapacity(const Item* item, uint32_t count) const
{
    if (hasFlag(PlayerFlag_CannotPickupItem)) {
        return false;
    }

    if (hasFlag(PlayerFlag_HasInfiniteCapacity) || item->getTopParent() == this) {
        return true;
    }

    uint32_t itemWeight = item->getContainer() != nullptr ? item->getWeight() : item->getBaseWeight();
    if (item->isStackable()) {
        itemWeight *= count;
    }
    return itemWeight <= getFreeCapacity();
}

ReturnValue Player::queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature*) const
{
    const Item* item = thing.getItem();
    if (item == nullptr) {
        return RETURNVALUE_NOTPOSSIBLE;
    }

    bool childIsOwner = hasBitSet(FLAG_CHILDISOWNER, flags);
    if (childIsOwner) {
        //a child container is querying the player, just check if enough capacity
        bool skipLimit = hasBitSet(FLAG_NOLIMIT, flags);
        if (skipLimit || hasCapacity(item, count)) {
            return RETURNVALUE_NOERROR;
        }
        return RETURNVALUE_NOTENOUGHCAPACITY;
    }

    if (!item->isPickupable()) {
        return RETURNVALUE_CANNOTPICKUP;
    }

    ReturnValue ret = RETURNVALUE_NOERROR;

    const int32_t& slotPosition = item->getSlotPosition();
    if ((slotPosition & SLOTP_HEAD) || (slotPosition & SLOTP_NECKLACE) ||
        (slotPosition & SLOTP_BACKPACK) || (slotPosition & SLOTP_ARMOR) ||
        (slotPosition & SLOTP_LEGS) || (slotPosition & SLOTP_FEET) ||
        (slotPosition & SLOTP_RING)) {
        ret = RETURNVALUE_CANNOTBEDRESSED;
    } else if (slotPosition & SLOTP_TWO_HAND) {
        ret = RETURNVALUE_PUTTHISOBJECTINBOTHHANDS;
    } else if ((slotPosition & SLOTP_RIGHT) || (slotPosition & SLOTP_LEFT)) {
        ret = RETURNVALUE_PUTTHISOBJECTINYOURHAND;
    }

    switch (index) {
        case CONST_SLOT_HEAD: {
            if (slotPosition & SLOTP_HEAD) {
                ret = RETURNVALUE_NOERROR;
            }
            break;
        }

        case CONST_SLOT_NECKLACE: {
            if (slotPosition & SLOTP_NECKLACE) {
                ret = RETURNVALUE_NOERROR;
            }
            break;
        }

        case CONST_SLOT_BACKPACK: {
            if (slotPosition & SLOTP_BACKPACK) {
                ret = RETURNVALUE_NOERROR;
            }
            break;
        }

        case CONST_SLOT_ARMOR: {
            if (slotPosition & SLOTP_ARMOR) {
                ret = RETURNVALUE_NOERROR;
            }
            break;
        }

        case CONST_SLOT_RIGHT: {
            if (slotPosition & SLOTP_RIGHT) {
                if (slotPosition & SLOTP_TWO_HAND) {
                    if (inventory[CONST_SLOT_LEFT] && inventory[CONST_SLOT_LEFT] != item) {
                        ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
                    } else {
                        ret = RETURNVALUE_NOERROR;
                    }
                } else if (inventory[CONST_SLOT_LEFT]) {
                    const Item* leftItem = inventory[CONST_SLOT_LEFT];
                    WeaponType_t type = item->getWeaponType(), leftType = leftItem->getWeaponType();

                    if (leftItem->getSlotPosition() & SLOTP_TWO_HAND) {
                        ret = RETURNVALUE_DROPTWOHANDEDITEM;
                    } else if (item == leftItem && count == item->getItemCount()) {
                        ret = RETURNVALUE_NOERROR;
                    } else if (leftType == WEAPON_SHIELD && type == WEAPON_SHIELD) {
                        ret = RETURNVALUE_CANONLYUSEONESHIELD;
                    } else if (leftType == WEAPON_NONE || type == WEAPON_NONE ||
                        leftType == WEAPON_SHIELD || leftType == WEAPON_AMMO
                        || type == WEAPON_SHIELD || type == WEAPON_AMMO) {
                        ret = RETURNVALUE_NOERROR;
                    } else {
                        ret = RETURNVALUE_CANONLYUSEONEWEAPON;
                    }
                } else {
                    ret = RETURNVALUE_NOERROR;
                }
            }
            break;
        }

        case CONST_SLOT_LEFT: {
            if (slotPosition & SLOTP_LEFT) {
                if (slotPosition & SLOTP_TWO_HAND) {
                    if (inventory[CONST_SLOT_RIGHT] && inventory[CONST_SLOT_RIGHT] != item) {
                        ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
                    } else {
                        ret = RETURNVALUE_NOERROR;
                    }
                } else if (inventory[CONST_SLOT_RIGHT]) {
                    const Item* rightItem = inventory[CONST_SLOT_RIGHT];
                    WeaponType_t type = item->getWeaponType(), rightType = rightItem->getWeaponType();

                    if (rightItem->getSlotPosition() & SLOTP_TWO_HAND) {
                        ret = RETURNVALUE_DROPTWOHANDEDITEM;
                    } else if (item == rightItem && count == item->getItemCount()) {
                        ret = RETURNVALUE_NOERROR;
                    } else if (rightType == WEAPON_SHIELD && type == WEAPON_SHIELD) {
                        ret = RETURNVALUE_CANONLYUSEONESHIELD;
                    } else if (rightType == WEAPON_NONE || type == WEAPON_NONE ||
                        rightType == WEAPON_SHIELD || rightType == WEAPON_AMMO
                        || type == WEAPON_SHIELD || type == WEAPON_AMMO) {
                        ret = RETURNVALUE_NOERROR;
                    } else {
                        ret = RETURNVALUE_CANONLYUSEONEWEAPON;
                    }
                } else {
                    ret = RETURNVALUE_NOERROR;
                }
            }
            break;
        }

        case CONST_SLOT_LEGS: {
            if (slotPosition & SLOTP_LEGS) {
                ret = RETURNVALUE_NOERROR;
            }
            break;
        }

        case CONST_SLOT_FEET: {
            if (slotPosition & SLOTP_FEET) {
                ret = RETURNVALUE_NOERROR;
            }
            break;
        }

        case CONST_SLOT_RING: {
            if (slotPosition & SLOTP_RING) {
                ret = RETURNVALUE_NOERROR;
            }
            break;
        }

        case CONST_SLOT_AMMO: {
            ret = RETURNVALUE_NOERROR;
            break;
        }

        case CONST_SLOT_WHEREEVER:
        case -1:
        ret = RETURNVALUE_NOTENOUGHROOM;
        break;

        default:
        ret = RETURNVALUE_NOTPOSSIBLE;
        break;
    }

    if (ret == RETURNVALUE_NOERROR || ret == RETURNVALUE_NOTENOUGHROOM) {
        //need an exchange with source?
        const Item* inventoryItem = getInventoryItem(static_cast<slots_t>(index));
        if (inventoryItem && (!inventoryItem->isStackable() || inventoryItem->getID() != item->getID())) {
            return RETURNVALUE_NEEDEXCHANGE;
        }

        //check if enough capacity
        if (!hasCapacity(item, count)) {
            return RETURNVALUE_NOTENOUGHCAPACITY;
        }

        if (!g_moveEvents->onPlayerEquip(const_cast<Player*>(this), const_cast<Item*>(item), static_cast<slots_t>(index), true)) {
            return RETURNVALUE_CANNOTBEDRESSED;
        }
    }
    return ret;
}

ReturnValue Player::queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount,
    uint32_t flags) const
{
    const Item* item = thing.getItem();
    if (item == nullptr) {
        maxQueryCount = 0;
        return RETURNVALUE_NOTPOSSIBLE;
    }

    if (index == INDEX_WHEREEVER) {
        uint32_t n = 0;
        for (int32_t slotIndex = CONST_SLOT_FIRST; slotIndex <= CONST_SLOT_LAST; ++slotIndex) {
            Item* inventoryItem = inventory[slotIndex];
            if (inventoryItem) {
                if (Container* subContainer = inventoryItem->getContainer()) {
                    uint32_t queryCount = 0;
                    subContainer->queryMaxCount(INDEX_WHEREEVER, *item, item->getItemCount(), queryCount, flags);
                    n += queryCount;

                    //iterate through all items, including sub-containers (deep search)
                    for (ContainerIterator it = subContainer->iterator(); it.hasNext(); it.advance()) {
                        if (Container* tmpContainer = (*it)->getContainer()) {
                            queryCount = 0;
                            tmpContainer->queryMaxCount(INDEX_WHEREEVER, *item, item->getItemCount(), queryCount, flags);
                            n += queryCount;
                        }
                    }
                } else if (inventoryItem->isStackable() && item->equals(inventoryItem) && inventoryItem->getItemCount() < 100) {
                    uint32_t remainder = (100 - inventoryItem->getItemCount());

                    if (queryAdd(slotIndex, *item, remainder, flags) == RETURNVALUE_NOERROR) {
                        n += remainder;
                    }
                }
            } else if (queryAdd(slotIndex, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) { //empty slot
                if (item->isStackable()) {
                    n += 100;
                } else {
                    ++n;
                }
            }
        }

        maxQueryCount = n;
    } else {
        const Item* destItem = nullptr;

        const Thing* destThing = getThing(index);
        if (destThing) {
            destItem = destThing->getItem();
        }

        if (destItem) {
            if (destItem->isStackable() && item->equals(destItem) && destItem->getItemCount() < 100) {
                maxQueryCount = 100 - destItem->getItemCount();
            } else {
                maxQueryCount = 0;
            }
        } else if (queryAdd(index, *item, count, flags) == RETURNVALUE_NOERROR) { //empty slot
            if (item->isStackable()) {
                maxQueryCount = 100;
            } else {
                maxQueryCount = 1;
            }

            return RETURNVALUE_NOERROR;
        }
    }

    if (maxQueryCount < count) {
        return RETURNVALUE_NOTENOUGHROOM;
    } else {
        return RETURNVALUE_NOERROR;
    }
}

ReturnValue Player::queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const
{
    int32_t index = getThingIndex(&thing);
    if (index == -1) {
        return RETURNVALUE_NOTPOSSIBLE;
    }

    const Item* item = thing.getItem();
    if (item == nullptr) {
        return RETURNVALUE_NOTPOSSIBLE;
    }

    if (count == 0 || (item->isStackable() && count > item->getItemCount())) {
        return RETURNVALUE_NOTPOSSIBLE;
    }

    if (!item->isMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags)) {
        return RETURNVALUE_NOTMOVEABLE;
    }

    return RETURNVALUE_NOERROR;
}

Cylinder* Player::queryDestination(int32_t& index, const Thing& thing, Item** destItem,
    uint32_t& flags)
{
    if (index == 0 /*drop to capacity window*/ || index == INDEX_WHEREEVER) {
        *destItem = nullptr;

        const Item* item = thing.getItem();
        if (item == nullptr) {
            return this;
        }

        bool autoStack = !((flags & FLAG_IGNOREAUTOSTACK) == FLAG_IGNOREAUTOSTACK);
        bool isStackable = item->isStackable();

        std::vector<Container*> containers;

        for (uint32_t slotIndex = CONST_SLOT_FIRST; slotIndex <= CONST_SLOT_LAST; ++slotIndex) {
            Item* inventoryItem = inventory[slotIndex];
            if (inventoryItem) {
                if (inventoryItem == tradeItem) {
                    continue;
                }

                if (inventoryItem == item) {
                    continue;
                }

                if (autoStack && isStackable) {
                    //try find an already existing item to stack with
                    if (queryAdd(slotIndex, *item, item->getItemCount(), 0) == RETURNVALUE_NOERROR) {
                        if (inventoryItem->equals(item) && inventoryItem->getItemCount() < 100) {
                            index = slotIndex;
                            *destItem = inventoryItem;
                            return this;
                        }
                    }

                    if (Container* subContainer = inventoryItem->getContainer()) {
                        containers.push_back(subContainer);
                    }
                } else if (Container* subContainer = inventoryItem->getContainer()) {
                    containers.push_back(subContainer);
                }
            } else if (queryAdd(slotIndex, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) { //empty slot
                index = slotIndex;
                *destItem = nullptr;
                return this;
            }
        }

        size_t i = 0;
        while (i < containers.size()) {
            Container* tmpContainer = containers[i++];
            if (!autoStack || !isStackable) {
                //we need to find first empty container as fast as we can for non-stackable items
                uint32_t n = tmpContainer->capacity() - tmpContainer->size();
                while (n) {
                    if (tmpContainer->queryAdd(tmpContainer->capacity() - n, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) {
                        index = tmpContainer->capacity() - n;
                        *destItem = nullptr;
                        return tmpContainer;
                    }

                    n--;
                }

                if (g_config.getBoolean(ConfigManager::QUERY_PLAYER_CONTAINERS)) {
                    for (Item* tmpContainerItem : tmpContainer->getItemList()) {
                        if (Container* subContainer = tmpContainerItem->getContainer()) {
                            containers.push_back(subContainer);
                        }
                    }
                }

                continue;
            }

            uint32_t n = 0;

            for (Item* tmpItem : tmpContainer->getItemList()) {
                if (tmpItem == tradeItem) {
                    continue;
                }

                if (tmpItem == item) {
                    continue;
                }

                //try find an already existing item to stack with
                if (tmpItem->equals(item) && tmpItem->getItemCount() < 100) {
                    index = n;
                    *destItem = tmpItem;
                    return tmpContainer;
                }

                if (g_config.getBoolean(ConfigManager::QUERY_PLAYER_CONTAINERS)) {
                    if (Container* subContainer = tmpItem->getContainer()) {
                        containers.push_back(subContainer);
                    }
                }

                n++;
            }

            if (n < tmpContainer->capacity() && tmpContainer->queryAdd(n, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) {
                index = n;
                *destItem = nullptr;
                return tmpContainer;
            }
        }

        return this;
    }

    Thing* destThing = getThing(index);
    if (destThing) {
        *destItem = destThing->getItem();
    }

    Cylinder* subCylinder = dynamic_cast<Cylinder*>(destThing);
    if (subCylinder) {
        index = INDEX_WHEREEVER;
        *destItem = nullptr;
        return subCylinder;
    } else {
        return this;
    }
}

void Player::addThing(int32_t index, Thing* thing)
{
    if (index < CONST_SLOT_FIRST || index > CONST_SLOT_LAST) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    Item* item = thing->getItem();
    if (!item) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    item->setParent(this);
    inventory[index] = item;

    //send to client
    sendInventoryItem(static_cast<slots_t>(index), item);
}

void Player::updateThing(Thing* thing, uint16_t itemId, uint32_t count)
{
    int32_t index = getThingIndex(thing);
    if (index == -1) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    Item* item = thing->getItem();
    if (!item) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    item->setID(itemId);
    item->setSubType(count);

    //send to client
    sendInventoryItem(static_cast<slots_t>(index), item);

    //event methods
    onUpdateInventoryItem(item, item);
}

void Player::replaceThing(uint32_t index, Thing* thing)
{
    if (index > CONST_SLOT_LAST) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    Item* oldItem = getInventoryItem(static_cast<slots_t>(index));
    if (!oldItem) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    Item* item = thing->getItem();
    if (!item) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    //send to client
    sendInventoryItem(static_cast<slots_t>(index), item);

    //event methods
    onUpdateInventoryItem(oldItem, item);

    item->setParent(this);

    inventory[index] = item;
}

void Player::removeThing(Thing* thing, uint32_t count)
{
    Item* item = thing->getItem();
    if (!item) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    int32_t index = getThingIndex(thing);
    if (index == -1) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    if (item->isStackable()) {
        if (count == item->getItemCount()) {
            //send change to client
            sendInventoryItem(static_cast<slots_t>(index), nullptr);

            //event methods
            onRemoveInventoryItem(item);

            item->setParent(nullptr);
            inventory[index] = nullptr;
        } else {
            uint8_t newCount = static_cast<uint8_t>(std::max<int32_t>(0, item->getItemCount() - count));
            item->setItemCount(newCount);

            //send change to client
            sendInventoryItem(static_cast<slots_t>(index), item);

            //event methods
            onUpdateInventoryItem(item, item);
        }
    } else {
        //send change to client
        sendInventoryItem(static_cast<slots_t>(index), nullptr);

        //event methods
        onRemoveInventoryItem(item);

        item->setParent(nullptr);
        inventory[index] = nullptr;
    }
}

int32_t Player::getThingIndex(const Thing* thing) const
{
    for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
        if (inventory[i] == thing) {
            return i;
        }
    }
    return -1;
}

size_t Player::getFirstIndex() const
{
    return CONST_SLOT_FIRST;
}

size_t Player::getLastIndex() const
{
    return CONST_SLOT_LAST + 1;
}

uint32_t Player::getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) const
{
    uint32_t count = 0;
    for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
        Item* item = inventory[i];
        if (!item) {
            continue;
        }

        if (item->getID() == itemId) {
            count += Item::countByType(item, subType);
        }

        if (Container* container = item->getContainer()) {
            for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
                if ((*it)->getID() == itemId) {
                    count += Item::countByType(*it, subType);
                }
            }
        }
    }
    return count;
}

bool Player::removeItemOfType(uint16_t itemId, uint32_t amount, int32_t subType, bool ignoreEquipped/* = false*/) const
{
    if (amount == 0) {
        return true;
    }

    std::vector<Item*> itemList;

    uint32_t count = 0;
    for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
        Item* item = inventory[i];
        if (!item) {
            continue;
        }

        if (!ignoreEquipped && item->getID() == itemId) {
            uint32_t itemCount = Item::countByType(item, subType);
            if (itemCount == 0) {
                continue;
            }

            itemList.push_back(item);

            count += itemCount;
            if (count >= amount) {
                g_game.internalRemoveItems(std::move(itemList), amount, Item::items[itemId].stackable);
                return true;
            }
        }
        else if (Container* container = item->getContainer()) {
            if (container->getID() == itemId) {
                uint32_t itemCount = Item::countByType(item, subType);
                if (itemCount == 0) {
                    continue;
                }

                itemList.push_back(item);

                count += itemCount;
                if (count >= amount) {
                    g_game.internalRemoveItems(std::move(itemList), amount, Item::items[itemId].stackable);
                    return true;
                }
            }

            for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
                Item* containerItem = *it;
                if (containerItem->getID() == itemId) {
                    uint32_t itemCount = Item::countByType(containerItem, subType);
                    if (itemCount == 0) {
                        continue;
                    }

                    itemList.push_back(containerItem);

                    count += itemCount;
                    if (count >= amount) {
                        g_game.internalRemoveItems(std::move(itemList), amount, Item::items[itemId].stackable);
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

std::map<uint32_t, uint32_t>& Player::getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const
{
    for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
        Item* item = inventory[i];
        if (!item) {
            continue;
        }

        countMap[item->getID()] += Item::countByType(item, -1);

        if (Container* container = item->getContainer()) {
            for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
                countMap[(*it)->getID()] += Item::countByType(*it, -1);
            }
        }
    }
    return countMap;
}

Thing* Player::getThing(size_t index) const
{
    if (index >= CONST_SLOT_FIRST && index <= CONST_SLOT_LAST) {
        return inventory[index];
    }
    return nullptr;
}

void Player::postAddNotification(Thing* thing, const Cylinder*, int32_t index, cylinderlink_t link /*= LINK_OWNER*/)
{
    if (link == LINK_OWNER) {
        //calling movement scripts
        g_moveEvents->onPlayerEquip(this, thing->getItem(), static_cast<slots_t>(index), false);
    }

    if (link == LINK_OWNER || link == LINK_TOPPARENT) {
        updateInventoryWeight();
        updateItemsLight();
        sendStats();
    }

    if (const Item* item = thing->getItem()) {
        if (const Container* container = item->getContainer()) {
            onSendContainer(container);
        }
    } else if (const Creature* creature = thing->getCreature()) {
        if (creature == this) {
            //check containers
            std::vector<Container*> containers;

            for (const auto& it : openContainers) {
                Container* container = it.second.container;
                if (!Position::areInRange<1, 1, 0>(container->getPosition(), getPosition())) {
                    containers.push_back(container);
                }
            }

            for (const Container* container : containers) {
                autoCloseContainers(container);
            }
        }
    }
}

void Player::postRemoveNotification(Thing* thing, const Cylinder*, int32_t index, cylinderlink_t link /*= LINK_OWNER*/)
{
    if (link == LINK_OWNER) {
        //calling movement scripts
        g_moveEvents->onPlayerDeEquip(this, thing->getItem(), static_cast<slots_t>(index));
    }

    if (link == LINK_OWNER || link == LINK_TOPPARENT) {
        updateInventoryWeight();
        updateItemsLight();
        sendStats();
    }

    if (const Item* item = thing->getItem()) {
        if (const Container* container = item->getContainer()) {
            if (container->isRemoved() || !Position::areInRange<1, 1, 0>(getPosition(), container->getPosition())) {
                autoCloseContainers(container);
            } else if (container->getTopParent() == this) {
                onSendContainer(container);
            } else if (const Container* topContainer = dynamic_cast<const Container*>(container->getTopParent())) {
                if (const DepotLocker* depotLocker = dynamic_cast<const DepotLocker*>(topContainer)) {
                    bool isOwner = false;

                    for (const auto& it : depotLockerMap) {
                        if (it.second == depotLocker) {
                            isOwner = true;
                            onSendContainer(container);
                        }
                    }

                    if (!isOwner) {
                        autoCloseContainers(container);
                    }
                } else {
                    onSendContainer(container);
                }
            } else {
                autoCloseContainers(container);
            }
        }
    }
}

void Player::internalAddThing(Thing* thing)
{
    internalAddThing(0, thing);
}

void Player::internalAddThing(uint32_t index, Thing* thing)
{
    Item* item = thing->getItem();
    if (!item) {
        return;
    }

    //index == 0 means we should equip this item at the most appropiate slot (no action required here)
    if (index > 0 && index < 11) {
        if (inventory[index]) {
            return;
        }

        inventory[index] = item;
        item->setParent(this);
    }
}

uint32_t Player::checkPlayerKilling()
{
    time_t today = std::time(nullptr);
    uint32_t lastDay = 0;
    uint32_t lastWeek = 0;
    uint32_t lastMonth = 0;
    uint64_t egibleMurders = 0;

    time_t dayTimestamp = today - (24 * 60 * 60);
    time_t weekTimestamp = today - (7 * 24 * 60 * 60);
    time_t monthTimestamp = today - (30 * 24 * 60 * 60);

    for (time_t currentMurderTimestamp : murderTimeStamps) {
        if (currentMurderTimestamp > dayTimestamp) {
            lastDay++;
        }

        if (currentMurderTimestamp > weekTimestamp) {
            lastWeek++;
        }

        egibleMurders = lastMonth + 1;

        if (currentMurderTimestamp <= monthTimestamp) {
            egibleMurders = lastMonth;
        }

        lastMonth = egibleMurders;
    }

    if (lastDay >= g_config.getNumber(ConfigManager::KILLS_DAY_BANISHMENT) ||
        lastWeek >= g_config.getNumber(ConfigManager::KILLS_WEEK_BANISHMENT) ||
        lastMonth >= g_config.getNumber(ConfigManager::KILLS_MONTH_BANISHMENT)) {
        return 2; // banishment!
    }

    if (lastDay >= g_config.getNumber(ConfigManager::KILLS_DAY_RED_SKULL) || 
        lastWeek >= g_config.getNumber(ConfigManager::KILLS_WEEK_RED_SKULL) || 
        lastMonth >= g_config.getNumber(ConfigManager::KILLS_MONTH_RED_SKULL)) {
        return 1; // red skull!
    }

    return 0;
}

bool Player::setFollowCreature(Creature* creature)
{
    if (!Creature::setFollowCreature(creature)) {
        setFollowCreature(nullptr);
        setAttackedCreature(nullptr);

        sendCancelMessage(RETURNVALUE_THEREISNOWAY);
        sendCancelTarget();
        stopWalk();
        return false;
    }
    return true;
}

bool Player::setAttackedCreature(Creature* creature)
{
    if (!Creature::setAttackedCreature(creature)) {
        sendCancelTarget();
        return false;
    }

    if (chaseMode == CHASEMODE_FOLLOW && creature) {
        if (followCreature != creature) {
            //chase opponent
            setFollowCreature(creature);
        }
    } else if (followCreature) {
        setFollowCreature(nullptr);
    }

    if (creature) {
        g_dispatcher.addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game, getID())));
    }
    return true;
}

void Player::goToFollowCreature()
{
    if (!walkTask) {
        if ((OTSYS_TIME() - lastFailedFollow) < 2000) {
            return;
        }

        Creature::goToFollowCreature();

        if (followCreature && !hasFollowPath) {
            lastFailedFollow = OTSYS_TIME();
        }
    }
}

void Player::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const
{
    Creature::getPathSearchParams(creature, fpp);
    fpp.fullPathSearch = true;
}

void Player::doAttacking(uint32_t)
{
    if (lastAttack == 0) {
        lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
    }

    if (hasCondition(CONDITION_PACIFIED)) {
        return;
    }

    if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
        if (Combat::attack(this, attackedCreature)) {
            earliestAttackTime = OTSYS_TIME() + 2000;
            lastAttack = OTSYS_TIME();
        }
    }
}

uint64_t Player::getGainedExperience(Creature* attacker) const
{
    if (g_config.getBoolean(ConfigManager::EXPERIENCE_FROM_PLAYERS)) {
        Player* attackerPlayer = attacker->getPlayer();
        if (attackerPlayer && attackerPlayer != this && skillLoss && std::abs(static_cast<int32_t>(attackerPlayer->getLevel() - level)) <= g_config.getNumber(ConfigManager::EXP_FROM_PLAYERS_LEVEL_RANGE)) {
            return std::max<uint64_t>(0, std::floor(getLostExperience() * getDamageRatio(attacker) * 0.75));
        }
    }
    return 0;
}

void Player::onFollowCreature(const Creature* creature)
{
    if (!creature) {
        stopWalk();
    }
}

void Player::setChaseMode(chaseMode_t mode)
{
    chaseMode_t prevChaseMode = chaseMode;
    chaseMode = mode;

    if (prevChaseMode != chaseMode) {
        if (chaseMode == CHASEMODE_FOLLOW) {
            if (!followCreature && attackedCreature) {
                //chase opponent
                setFollowCreature(attackedCreature);
            }
        } else if (attackedCreature) {
            setFollowCreature(nullptr);
            cancelNextWalk = true;
        }
    }
}

void Player::onWalkAborted()
{
    setNextWalkActionTask(nullptr);
    sendCancelWalk();
}

void Player::onWalkComplete()
{
    if (walkTask) {
        walkTaskEvent = g_scheduler.addEvent(walkTask);
        walkTask = nullptr;
    }
}

void Player::stopWalk()
{
    cancelNextWalk = true;
}

void Player::getCreatureLight(LightInfo& light) const
{
    if (internalLight.level > itemsLight.level) {
        light = internalLight;
    } else {
        light = itemsLight;
    }
}

void Player::updateItemsLight(bool internal /*=false*/)
{
    LightInfo maxLight;
    LightInfo curLight;

    for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
        Item* item = inventory[i];
        if (item) {
            item->getLight(curLight);

            if (curLight.level > maxLight.level) {
                maxLight = curLight;
            }
        }
    }

    if (itemsLight.level != maxLight.level || itemsLight.color != maxLight.color) {
        itemsLight = maxLight;

        if (!internal) {
            g_game.changeLight(this);
        }
    }
}

void Player::onAddCondition(ConditionType_t type)
{
    Creature::onAddCondition(type);
    sendIcons();
}

void Player::onAddCombatCondition(ConditionType_t type)
{
    switch (type) {
        case CONDITION_POISON:
        sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are poisoned.");
        break;

        case CONDITION_PARALYZE:
        sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are paralyzed.");
        break;

        case CONDITION_DRUNK:
        sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are drunk.");
        break;

        default:
        break;
    }
}

void Player::onEndCondition(ConditionType_t type)
{
    Creature::onEndCondition(type);

    if (type == CONDITION_INFIGHT) {
        onIdleStatus();
        pzLocked = false;
        clearAttacked();

        if (getSkull() != SKULL_RED) {
            setSkull(SKULL_NONE);
        }
    }

    sendIcons();
}

void Player::onCombatRemoveCondition(Condition* condition)
{
    //Creature::onCombatRemoveCondition(condition);
    if (condition->getId() > 0) {
        //Means the condition is from an item, id == slot
        if (g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) {
            Item* item = getInventoryItem(static_cast<slots_t>(condition->getId()));
            if (item) {
                //25% chance to destroy the item
                if (25 >= uniform_random(1, 100)) {
                    g_game.internalRemoveItem(item);
                }
            }
        }
    } else {
        if (!canDoAction()) {
            const uint32_t delay = getNextActionTime();
            const int32_t ticks = delay - (delay % EVENT_CREATURE_THINK_INTERVAL);
            if (ticks < 0) {
                removeCondition(condition);
            } else {
                condition->setTicks(ticks);
            }
        } else {
            removeCondition(condition);
        }
    }
}

void Player::onAttackedCreature(Creature* target)
{
    Creature::onAttackedCreature(target);

    if (target->getZone() == ZONE_PVP) {
        return;
    }

    if (target == this) {
        addInFightTicks();
        return;
    }

    if (hasFlag(PlayerFlag_NotGainInFight)) {
        return;
    }

    Player* targetPlayer = target->getPlayer();
    if (targetPlayer) {
        if (!pzLocked && g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
            pzLocked = true;
            sendIcons();
        }

        if (!isPartner(targetPlayer)) {
            if (getSkull() == SKULL_NONE && getSkullClient(targetPlayer) == SKULL_YELLOW) {
                addAttacked(targetPlayer);
                targetPlayer->sendCreatureSkull(this);
            } else {
                if (!targetPlayer->hasAttacked(this)) {
                    if (!Combat::isInPvpZone(this, targetPlayer) && !isInWar(targetPlayer)) {
                        addAttacked(targetPlayer);
                        if (targetPlayer->getSkull() == SKULL_NONE && getSkull() == SKULL_NONE) {
                            setSkull(SKULL_WHITE);
                        }
                    }
    
                    if (getSkull() == SKULL_NONE) {
                        targetPlayer->sendCreatureSkull(this);
                    }
                }
            }
        }
    }

    addInFightTicks();
}

void Player::onAttacked()
{
    Creature::onAttacked();

    addInFightTicks();
}

void Player::onIdleStatus()
{
    Creature::onIdleStatus();

    if (party) {
        party->clearPlayerPoints(this);
    }
}

void Player::onPlacedCreature()
{
    //scripting event - onLogin
    if (!g_creatureEvents->playerLogin(this)) {
        kickPlayer(true);
    }
}

void Player::onAttackedCreatureDrainHealth(Creature* target, int32_t points)
{
    Creature::onAttackedCreatureDrainHealth(target, points);

    if (target) {
        if (party && !Combat::isPlayerCombat(target)) {
            Monster* tmpMonster = target->getMonster();
            if (tmpMonster && tmpMonster->isHostile()) {
                //We have fulfilled a requirement for shared experience
                party->updatePlayerTicks(this, points);
            }
        }
    }
}

void Player::onTargetCreatureGainHealth(Creature* target, int32_t points)
{
    if (target && party) {
        Player* tmpPlayer = nullptr;

        if (target->getPlayer()) {
            tmpPlayer = target->getPlayer();
        } else if (Creature* targetMaster = target->getMaster()) {
            if (Player* targetMasterPlayer = targetMaster->getPlayer()) {
                tmpPlayer = targetMasterPlayer;
            }
        }

        if (isPartner(tmpPlayer)) {
            party->updatePlayerTicks(this, points);
        }
    }
}

bool Player::onKilledCreature(Creature* target, bool lastHit/* = true*/)
{
    bool unjustified = false;

    if (hasFlag(PlayerFlag_NotGenerateLoot)) {
        target->setDropLoot(false);
    }

    Creature::onKilledCreature(target, lastHit);

    if (Player* targetPlayer = target->getPlayer()) {
        if (targetPlayer && targetPlayer->getZone() == ZONE_PVP) {
            targetPlayer->setDropLoot(false);
            targetPlayer->setLossSkill(false);
        } else if (!hasFlag(PlayerFlag_NotGainInFight) && !isPartner(targetPlayer)) {
            if (!Combat::isInPvpZone(this, targetPlayer) && hasAttacked(targetPlayer) && !targetPlayer->hasAttacked(this) && targetPlayer != this) {
                if (targetPlayer->getSkull() == SKULL_NONE && !isInWar(targetPlayer)) {
                    unjustified = true;
                    addUnjustifiedDead(targetPlayer);
                }
            }
        }

        if (lastHit && hasCondition(CONDITION_INFIGHT)) {
            pzLocked = true;
            Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_config.getNumber(ConfigManager::WHITE_SKULL_TIME) * 1000, 0);
            addCondition(condition);
        }
    }

    return unjustified;
}

void Player::gainExperience(uint64_t gainExp)
{
    if (hasFlag(PlayerFlag_NotGainExperience) || gainExp == 0) {
        return;
    }

    addExperience(gainExp, true);
}

void Player::onGainExperience(uint64_t gainExp, Creature* target)
{
    if (hasFlag(PlayerFlag_NotGainExperience)) {
        return;
    }

    if (target && !target->getPlayer() && party && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
        party->shareExperience(gainExp);
        //We will get a share of the experience through the sharing mechanism
        return;
    }

    Creature::onGainExperience(gainExp, target);
    gainExperience(gainExp);
}

void Player::onGainSharedExperience(uint64_t gainExp)
{
    gainExperience(gainExp);
}

bool Player::isImmune(CombatType_t type) const
{
    if (hasFlag(PlayerFlag_CannotBeAttacked)) {
        return true;
    }
    return Creature::isImmune(type);
}

bool Player::isImmune(ConditionType_t type) const
{
    if (hasFlag(PlayerFlag_CannotBeAttacked)) {
        return true;
    }
    return Creature::isImmune(type);
}

bool Player::isAttackable() const
{
    return !hasFlag(PlayerFlag_CannotBeAttacked);
}

bool Player::lastHitIsPlayer(Creature* lastHitCreature)
{
    if (!lastHitCreature) {
        return false;
    }

    if (lastHitCreature->getPlayer()) {
        return true;
    }

    Creature* lastHitMaster = lastHitCreature->getMaster();
    return lastHitMaster && lastHitMaster->getPlayer();
}

void Player::changeHealth(int32_t healthChange, bool sendHealthChange/* = true*/)
{
    Creature::changeHealth(healthChange, sendHealthChange);
    sendStats();
}

void Player::changeMana(int32_t manaChange)
{
    if (!hasFlag(PlayerFlag_HasInfiniteMana)) {
        Creature::changeMana(manaChange);
    }

    sendStats();
}

void Player::changeSoul(int32_t soulChange)
{
    if (soulChange > 0) {
        soul += std::min<int32_t>(soulChange, vocation->getSoulMax() - soul);
    } else {
        soul = std::max<int32_t>(0, soul + soulChange);
    }

    sendStats();
}

bool Player::canWear(uint32_t lookType) const
{
    if (group->access) {
        return true;
    }

    if (getSex() == PLAYERSEX_MALE) {
        if (lookType >= 132 && lookType <= 134 && isPremium()) {
            return true;
        } else if (lookType >= 128 && lookType <= 131) {
            return true;
        }
    } else if (getSex() == PLAYERSEX_FEMALE) {
        if (lookType >= 140 && lookType <= 142 && isPremium()) {
            return true;
        } else if (lookType >= 136 && lookType <= 139) {
            return true;
        }
    }

    return false;
}

bool Player::canLogout()
{
    if (isConnecting) {
        return false;
    }

    if (getTile()->hasFlag(TILESTATE_NOLOGOUT)) {
        return false;
    }

    if (getTile()->hasFlag(TILESTATE_PROTECTIONZONE)) {
        return true;
    }

    return !isPzLocked() && !hasCondition(CONDITION_INFIGHT);
}

void Player::setSex(PlayerSex_t newSex)
{
    sex = newSex;
}

Skulls_t Player::getSkull() const
{
    if (hasFlag(PlayerFlag_NotGainInFight)) {
        return SKULL_NONE;
    }
    return skull;
}

Skulls_t Player::getSkullClient(const Creature* creature) const
{
    if (!creature || g_game.getWorldType() != WORLD_TYPE_PVP) {
        return SKULL_NONE;
    }

    const Player* player = creature->getPlayer();
    if (player && player->getSkull() == SKULL_NONE) {
        if (isInWar(player)) {
            return SKULL_GREEN;
        }

        if (!player->getGuildWarList().empty() && guild == player->getGuild()) {
            return SKULL_GREEN;
        }

        if (player->hasAttacked(this)) {
            return SKULL_YELLOW;
        }

        if (isPartner(player)) {
            return SKULL_GREEN;
        }
    }
    return Creature::getSkullClient(creature);
}

bool Player::hasAttacked(const Player* attacked) const
{
    if (hasFlag(PlayerFlag_NotGainInFight) || !attacked) {
        return false;
    }

    return attackedSet.find(attacked->id) != attackedSet.end();
}

void Player::addAttacked(const Player* attacked)
{
    if (hasFlag(PlayerFlag_NotGainInFight) || !attacked || attacked == this) {
        return;
    }

    attackedSet.insert(attacked->id);
}

void Player::clearAttacked()
{
    attackedSet.clear();
}

void Player::addUnjustifiedDead(const Player* attacked)
{
    if (hasFlag(PlayerFlag_NotGainInFight) || attacked == this || g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) {
        return;
    }

    // current unjustified kill!
    murderTimeStamps.push_back(std::time(nullptr));

    sendTextMessage(MESSAGE_STATUS_WARNING, "Warning! The murder of " + attacked->getName() + " was not justified.");

    if (playerKillerEnd == 0) {
        // white skull time, it only sets on first kill!
        playerKillerEnd = std::time(nullptr) + g_config.getNumber(ConfigManager::WHITE_SKULL_TIME);
    }

    uint32_t murderResult = checkPlayerKilling();
    if (murderResult >= 1) {
        // red skull player
        playerKillerEnd = std::time(nullptr) + g_config.getNumber(ConfigManager::RED_SKULL_TIME);
        setSkull(SKULL_RED);

        if (murderResult == 2) {
            // banishment for too many unjustified kills
            Database* db = Database::getInstance();

            std::ostringstream ss;
            ss << "INSERT INTO `account_bans` (`account_id`, `reason`, `banned_at`, `expires_at`, `banned_by`) VALUES (";
            ss << getAccount() << ", ";
            ss << db->escapeString("Too many unjustified kills") << ", ";
            ss << std::time(nullptr) << ", ";
            ss << std::time(nullptr) + g_config.getNumber(ConfigManager::BAN_LENGTH) << ", ";
            ss << "4);";

            db->executeQuery(ss.str());

            g_game.addMagicEffect(getPosition(), CONST_ME_GREEN_RINGS);
            g_game.removeCreature(this);
            disconnect();
        }
    }
}

void Player::checkSkullTicks()
{
    time_t today = std::time(nullptr);

    if (!hasCondition(CONDITION_INFIGHT) && ((skull == SKULL_RED && today >= playerKillerEnd) || (skull == SKULL_WHITE))) {
        setSkull(SKULL_NONE);
    }
}

bool Player::isPromoted() const
{
    uint16_t promotedVocation = g_vocations.getPromotedVocation(vocation->getId());
    return promotedVocation == VOCATION_NONE && vocation->getId() != promotedVocation;
}

double Player::getLostPercent() const
{
    int32_t blessingCount = std::bitset<5>(blessings).count();

    int32_t deathLosePercent = g_config.getNumber(ConfigManager::DEATH_LOSE_PERCENT);
    if (deathLosePercent != -1) {
        if (isPromoted()) {
            deathLosePercent -= 3;
        }

        deathLosePercent -= blessingCount;
        return std::max<int32_t>(0, deathLosePercent) / 100.;
    }

    double lossPercent;
    if (level >= 25) {
        double tmpLevel = level + (levelPercent / 100.);
        lossPercent = static_cast<double>((tmpLevel + 50) * 50 * ((tmpLevel * tmpLevel) - (5 * tmpLevel) + 8)) / experience;
    } else {
        lossPercent = 10;
    }

    if (isPromoted()) {
        lossPercent *= 0.7;
    }

    return lossPercent * pow(0.92, blessingCount) / 100;
}

void Player::learnInstantSpell(const std::string& spellName)
{
    if (!hasLearnedInstantSpell(spellName)) {
        learnedInstantSpellList.push_front(spellName);
    }
}

void Player::forgetInstantSpell(const std::string& spellName)
{
    learnedInstantSpellList.remove(spellName);
}

bool Player::hasLearnedInstantSpell(const std::string& spellName) const
{
    if (hasFlag(PlayerFlag_CannotUseSpells)) {
        return false;
    }

    if (hasFlag(PlayerFlag_IgnoreSpellCheck)) {
        return true;
    }

    for (const auto& learnedSpellName : learnedInstantSpellList) {
        if (strcasecmp(learnedSpellName.c_str(), spellName.c_str()) == 0) {
            return true;
        }
    }
    return false;
}

bool Player::isInWar(const Player* player) const
{
    if (!player || !guild) {
        return false;
    }

    const Guild* playerGuild = player->getGuild();
    if (!playerGuild) {
        return false;
    }

    return isInWarList(playerGuild->getId()) && player->isInWarList(guild->getId());
}

bool Player::isInWarList(uint32_t guildId) const
{
    return std::find(guildWarList.begin(), guildWarList.end(), guildId) != guildWarList.end();
}

bool Player::isPremium() const
{
    if (g_config.getBoolean(ConfigManager::FREE_PREMIUM) || hasFlag(PlayerFlag_IsAlwaysPremium)) {
        return true;
    }

    return premiumDays > 0;
}

void Player::setPremiumDays(int32_t v)
{
    premiumDays = v;
}

PartyShields_t Player::getPartyShield(const Player* player) const
{
    if (!player) {
        return SHIELD_NONE;
    }

    if (party) {
        if (party->getLeader() == player) {
            return SHIELD_YELLOW;
        }

        if (player->party == party) {
            return SHIELD_BLUE;
        }

        if (isInviting(player)) {
            return SHIELD_WHITEBLUE;
        }
    }

    if (player->isInviting(this)) {
        return SHIELD_WHITEYELLOW;
    }

    return SHIELD_NONE;
}

bool Player::isInviting(const Player* player) const
{
    if (!player || !party || party->getLeader() != this) {
        return false;
    }
    return party->isPlayerInvited(player);
}

bool Player::isPartner(const Player* player) const
{
    if (!player || !party) {
        return false;
    }
    return party == player->party;
}

bool Player::isGuildMate(const Player* player) const
{
    if (!player || !guild) {
        return false;
    }
    return guild == player->guild;
}

void Player::sendPlayerPartyIcons(Player* player)
{
    sendCreatureShield(player);
    sendCreatureSkull(player);
}

bool Player::addPartyInvitation(Party* party)
{
    auto it = std::find(invitePartyList.begin(), invitePartyList.end(), party);
    if (it != invitePartyList.end()) {
        return false;
    }

    invitePartyList.push_front(party);
    return true;
}

void Player::removePartyInvitation(Party* party)
{
    invitePartyList.remove(party);
}

void Player::clearPartyInvitations()
{
    for (Party* invitingParty : invitePartyList) {
        invitingParty->removeInvite(*this, false);
    }
    invitePartyList.clear();
}

void Player::sendClosePrivate(uint16_t channelId)
{
    if (channelId == CHANNEL_GUILD || channelId == CHANNEL_PARTY) {
        g_chat->removeUserFromChannel(*this, channelId);
    }

    if (client) {
        client->sendClosePrivate(channelId);
    }
}

uint64_t Player::getMoney() const
{
    std::vector<const Container*> containers;
    uint64_t moneyCount = 0;

    for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
        Item* item = inventory[i];
        if (!item) {
            continue;
        }

        const Container* container = item->getContainer();
        if (container) {
            containers.push_back(container);
        } else {
            moneyCount += item->getWorth();
        }
    }

    size_t i = 0;
    while (i < containers.size()) {
        const Container* container = containers[i++];
        for (const Item* item : container->getItemList()) {
            const Container* tmpContainer = item->getContainer();
            if (tmpContainer) {
                containers.push_back(tmpContainer);
            } else {
                moneyCount += item->getWorth();
            }
        }
    }
    return moneyCount;
}

size_t Player::getMaxVIPEntries() const
{
    if (group->maxVipEntries != 0) {
        return group->maxVipEntries;
    } else if (isPremium()) {
        return 100;
    }
    return 20;
}

size_t Player::getMaxDepotItems() const
{
    if (group->maxDepotItems != 0) {
        return group->maxDepotItems;
    } else if (isPremium()) {
        return 2000;
    }

    return 1000;
}

std::forward_list<Condition*> Player::getMuteConditions() const
{
    std::forward_list<Condition*> muteConditions;
    for (Condition* condition : conditions) {
        if (condition->getTicks() <= 0) {
            continue;
        }

        ConditionType_t type = condition->getType();
        if (type != CONDITION_MUTED && type != CONDITION_CHANNELMUTEDTICKS && type != CONDITION_YELLTICKS) {
            continue;
        }

        muteConditions.push_front(condition);
    }
    return muteConditions;
}

void Player::setGuild(Guild* guild)
{
    if (guild == this->guild) {
        return;
    }

    Guild* oldGuild = this->guild;

    this->guildNick.clear();
    this->guild = nullptr;
    this->guildRank = nullptr;

    if (guild) {
        const GuildRank* rank = guild->getRankByLevel(1);
        if (!rank) {
            return;
        }

        this->guild = guild;
        this->guildRank = rank;
        guild->addMember(this);
    }

    if (oldGuild) {
        oldGuild->removeMember(this);
    }
}
 


desde ja, obrigado a quem dedicar uma atenção <3

Editado por Trunksontibia (veja o histórico de edições)
Link para o post
Compartilhar em outros sites
2 hours ago, Toruk said:

Hmmm... Não sabia que esse tfs não tem esse bool, então é no tile.cpp.

Poste ai para eu dar uma olhada. Preciso ver o "ReturnValue Tile::queryAdd"

tentei antes de voltar aqui adicionar tudo que tinha bool com walkthrough.. ai na compilacao chamou um haswalkstack.. adicionei 2 coisinhas.. 
acontece que eu logo, e da erro como se fossem de sprites.. tentei mexer aqui ali e nada.. mas parece q funcionou o walkthrough.
ai eu tentei varias outras sprites ate desistir..
voltei dos souces, e aqui esta meu title.cpp:
 

Quote

/**
 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
 * Copyright (C) 2017  Alejandro Mujica <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "otpch.h"

#include <boost/range/adaptor/reversed.hpp>

#include "tile.h"

#include "creature.h"
#include "combat.h"
#include "game.h"
#include "mailbox.h"
#include "monster.h"
#include "movement.h"
#include "teleport.h"

extern Game g_game;
extern MoveEvents* g_moveEvents;

StaticTile real_nullptr_tile(0xFFFF, 0xFFFF, 0xFF);
Tile& Tile::nullptr_tile = real_nullptr_tile;

bool Tile::hasProperty(ITEMPROPERTY prop) const
{
    if (ground && ground->hasProperty(prop)) {
        return true;
    }

    if (const TileItemVector* items = getItemList()) {
        for (const Item* item : *items) {
            if (item->hasProperty(prop)) {
                return true;
            }
        }
    }
    return false;
}

bool Tile::hasProperty(const Item* exclude, ITEMPROPERTY prop) const
{
    assert(exclude);

    if (ground && exclude != ground && ground->hasProperty(prop)) {
        return true;
    }

    if (const TileItemVector* items = getItemList()) {
        for (const Item* item : *items) {
            if (item != exclude && item->hasProperty(prop)) {
                return true;
            }
        }
    }

    return false;
}

bool Tile::hasHeight(uint32_t n) const
{
    uint32_t height = 0;

    if (ground) {
        if (ground->hasProperty(CONST_PROP_HASHEIGHT)) {
            ++height;
        }

        if (n == height) {
            return true;
        }
    }

    if (const TileItemVector* items = getItemList()) {
        for (const Item* item : *items) {
            if (item->hasProperty(CONST_PROP_HASHEIGHT)) {
                ++height;
            }

            if (n == height) {
                return true;
            }
        }
    }
    return false;
}

size_t Tile::getCreatureCount() const
{
    if (const CreatureVector* creatures = getCreatures()) {
        return creatures->size();
    }
    return 0;
}

size_t Tile::getItemCount() const
{
    if (const TileItemVector* items = getItemList()) {
        return items->size();
    }
    return 0;
}

uint32_t Tile::getTopItemCount() const
{
    if (const TileItemVector* items = getItemList()) {
        return items->getTopItemCount();
    }
    return 0;
}

uint32_t Tile::getDownItemCount() const
{
    if (const TileItemVector* items = getItemList()) {
        return items->getDownItemCount();
    }
    return 0;
}

std::string Tile::getDescription(int32_t) const
{
    return "You dont know why, but you cant see anything!";
}

Teleport* Tile::getTeleportItem() const
{
    if (!hasFlag(TILESTATE_TELEPORT)) {
        return nullptr;
    }

    if (const TileItemVector* items = getItemList()) {
        for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
            if ((*it)->getTeleport()) {
                return (*it)->getTeleport();
            }
        }
    }
    return nullptr;
}

MagicField* Tile::getFieldItem() const
{
    if (!hasFlag(TILESTATE_MAGICFIELD)) {
        return nullptr;
    }

    if (ground && ground->getMagicField()) {
        return ground->getMagicField();
    }

    if (const TileItemVector* items = getItemList()) {
        for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
            if ((*it)->getMagicField()) {
                return (*it)->getMagicField();
            }
        }
    }
    return nullptr;
}

Mailbox* Tile::getMailbox() const
{
    if (!hasFlag(TILESTATE_MAILBOX)) {
        return nullptr;
    }

    if (ground && ground->getMailbox()) {
        return ground->getMailbox();
    }

    if (const TileItemVector* items = getItemList()) {
        for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
            if ((*it)->getMailbox()) {
                return (*it)->getMailbox();
            }
        }
    }
    return nullptr;
}

BedItem* Tile::getBedItem() const
{
    if (!hasFlag(TILESTATE_BED)) {
        return nullptr;
    }

    if (ground && ground->getBed()) {
        return ground->getBed();
    }

    if (const TileItemVector* items = getItemList()) {
        for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
            if ((*it)->getBed()) {
                return (*it)->getBed();
            }
        }
    }
    return nullptr;
}

Creature* Tile::getTopCreature() const
{
    if (const CreatureVector* creatures = getCreatures()) {
        if (!creatures->empty()) {
            return *creatures->begin();
        }
    }
    return nullptr;
}

const Creature* Tile::getBottomCreature() const
{
    if (const CreatureVector* creatures = getCreatures()) {
        if (!creatures->empty()) {
            return *creatures->rbegin();
        }
    }
    return nullptr;
}

Creature* Tile::getTopVisibleCreature(const Creature* creature) const
{
    if (const CreatureVector* creatures = getCreatures()) {
        if (creature) {
            const Player* player = creature->getPlayer();
            if (player && player->isAccessPlayer()) {
                return getTopCreature();
            }

            for (Creature* tileCreature : *creatures) {
                if (creature->canSeeCreature(tileCreature)) {
                    return tileCreature;
                }
            }
        } else {
            for (Creature* tileCreature : *creatures) {
                if (!tileCreature->isInvisible()) {
                    const Player* player = tileCreature->getPlayer();
                    if (!player || !player->isInGhostMode()) {
                        return tileCreature;
                    }
                }
            }
        }
    }
    return nullptr;
}

const Creature* Tile::getBottomVisibleCreature(const Creature* creature) const
{
    if (const CreatureVector* creatures = getCreatures()) {
        if (creature) {
            const Player* player = creature->getPlayer();
            if (player && player->isAccessPlayer()) {
                return getBottomCreature();
            }

            for (auto it = creatures->rbegin(), end = creatures->rend(); it != end; ++it) {
                if (creature->canSeeCreature(*it)) {
                    return *it;
                }
            }
        } else {
            for (auto it = creatures->rbegin(), end = creatures->rend(); it != end; ++it) {
                if (!(*it)->isInvisible()) {
                    const Player* player = (*it)->getPlayer();
                    if (!player || !player->isInGhostMode()) {
                        return *it;
                    }
                }
            }
        }
    }
    return nullptr;
}

Item* Tile::getTopDownItem() const
{
    if (const TileItemVector* items = getItemList()) {
        return items->getTopDownItem();
    }
    return nullptr;
}

Item* Tile::getTopTopItem() const
{
    if (const TileItemVector* items = getItemList()) {
        return items->getTopTopItem();
    }
    return nullptr;
}

Item* Tile::getItemByTopOrder(int32_t topOrder)
{
    //topOrder:
    //1: borders
    //2: ladders, signs, splashes
    //3: doors etc
    //4: creatures
    if (TileItemVector* items = getItemList()) {
        for (auto it = ItemVector::const_reverse_iterator(items->getEndTopItem()), end = ItemVector::const_reverse_iterator(items->getBeginTopItem()); it != end; ++it) {
            if (Item::items[(*it)->getID()].alwaysOnTopOrder == topOrder) {
                return (*it);
            }
        }
    }
    return nullptr;
}

Thing* Tile::getTopVisibleThing(const Creature* creature)
{
    Thing* thing = getTopVisibleCreature(creature);
    if (thing) {
        return thing;
    }

    TileItemVector* items = getItemList();
    if (items) {
        for (ItemVector::const_iterator it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
            return (*it);
        }

        for (auto it = ItemVector::const_reverse_iterator(items->getEndTopItem()), end = ItemVector::const_reverse_iterator(items->getBeginTopItem()); it != end; ++it) {
            return (*it);
        }
    }

    return ground;
}

void Tile::onAddTileItem(Item* item)
{
    setTileFlags(item);

    const Position& cylinderMapPos = getPosition();

    SpectatorVec list;
    g_game.map.getSpectators(list, cylinderMapPos, true);

    //send to client
    for (Creature* spectator : list) {
        if (Player* tmpPlayer = spectator->getPlayer()) {
            tmpPlayer->sendAddTileItem(this, cylinderMapPos, item);
        }
    }

    //event methods
    for (Creature* spectator : list) {
        spectator->onAddTileItem(this, cylinderMapPos);
    }
}

void Tile::onUpdateTileItem(Item* oldItem, const ItemType& oldType, Item* newItem, const ItemType& newType)
{
    const Position& cylinderMapPos = getPosition();

    SpectatorVec list;
    g_game.map.getSpectators(list, cylinderMapPos, true);

    //send to client
    for (Creature* spectator : list) {
        if (Player* tmpPlayer = spectator->getPlayer()) {
            tmpPlayer->sendUpdateTileItem(this, cylinderMapPos, newItem);
        }
    }

    //event methods
    for (Creature* spectator : list) {
        spectator->onUpdateTileItem(this, cylinderMapPos, oldItem, oldType, newItem, newType);
    }
}

void Tile::onRemoveTileItem(const SpectatorVec& list, const std::vector<int32_t>& oldStackPosVector, Item* item)
{
    resetTileFlags(item);

    const Position& cylinderMapPos = getPosition();
    const ItemType& iType = Item::items[item->getID()];

    //send to client
    size_t i = 0;
    for (Creature* spectator : list) {
        if (Player* tmpPlayer = spectator->getPlayer()) {
            tmpPlayer->sendRemoveTileThing(cylinderMapPos, oldStackPosVector[i++]);
        }
    }

    //event methods
    for (Creature* spectator : list) {
        spectator->onRemoveTileItem(this, cylinderMapPos, iType, item);
    }
}

void Tile::onUpdateTile(const SpectatorVec& list)
{
    const Position& cylinderMapPos = getPosition();

    //send to clients
    for (Creature* spectator : list) {
        spectator->getPlayer()->sendUpdateTile(this, cylinderMapPos);
    }
}

ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags, Creature*) const
{
    if (const Creature* creature = thing.getCreature()) {
        if (hasBitSet(FLAG_NOLIMIT, flags)) {
            return RETURNVALUE_NOERROR;
        }

        if (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH)) {
            return RETURNVALUE_NOTPOSSIBLE;
        }

        if (ground == nullptr) {
            return RETURNVALUE_NOTPOSSIBLE;
        }

        if (const Monster* monster = creature->getMonster()) {
            if (hasFlag(TILESTATE_PROTECTIONZONE | TILESTATE_TELEPORT)) {
                return RETURNVALUE_NOTPOSSIBLE;
            }

            if (hasFlag(TILESTATE_IMMOVABLEBLOCKPATH | TILESTATE_IMMOVABLENOFIELDBLOCKPATH)) {
                return RETURNVALUE_NOTPOSSIBLE;
            }

            if (hasBitSet(FLAG_PLACECHECK, flags) && hasFlag(TILESTATE_BLOCKSOLID)) {
                return RETURNVALUE_NOTPOSSIBLE;
            }

            const CreatureVector* creatures = getCreatures();
            if (monster->canPushCreatures() && !monster->isSummon()) {
                if (creatures) {
                    for (Creature* tileCreature : *creatures) {
                        if (tileCreature->getPlayer() && tileCreature->getPlayer()->isInGhostMode()) {
                            continue;
                        }

                        const Monster* creatureMonster = tileCreature->getMonster();
                        if (!creatureMonster || !tileCreature->isPushable() ||
                            (creatureMonster->isSummon() && creatureMonster->getMaster()->getPlayer())) {
                            return RETURNVALUE_NOTPOSSIBLE;
                        }
                    }
                }
            } else if (creatures && !creatures->empty()) {
                for (const Creature* tileCreature : *creatures) {
                    if (!tileCreature->isInGhostMode()) {
                        return RETURNVALUE_NOTENOUGHROOM;
                    }
                }
            }

            if (hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID)) {
                return RETURNVALUE_NOTPOSSIBLE;
            }

            if (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH)) {
                return RETURNVALUE_NOTPOSSIBLE;
            }

            if (hasFlag(TILESTATE_BLOCKSOLID) || (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_NOFIELDBLOCKPATH))) {
                if (!(monster->canPushItems() || hasBitSet(FLAG_IGNOREBLOCKITEM, flags))) {
                    return RETURNVALUE_NOTPOSSIBLE;
                }
            }

            if (!monster->hasCondition(CONDITION_AGGRESSIVE) &&
                !hasBitSet(FLAG_IGNOREFIELDDAMAGE, flags)) {
                if (hasFlag(TILESTATE_FIREDAMAGE) && !monster->isImmune(COMBAT_FIREDAMAGE)) {
                    return RETURNVALUE_NOTPOSSIBLE;
                }

                if (hasFlag(TILESTATE_POISONDAMAGE) && !monster->isImmune(COMBAT_EARTHDAMAGE)) {
                    return RETURNVALUE_NOTPOSSIBLE;
                }

                if (hasFlag(TILESTATE_ENERGYDAMAGE) && !monster->isImmune(COMBAT_ENERGYDAMAGE)) {
                    return RETURNVALUE_NOTPOSSIBLE;
                }
            }

            return RETURNVALUE_NOERROR;
        }

        const CreatureVector* creatures = getCreatures();
        if (const Player* player = creature->getPlayer()) {
            if (creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags) && !player->isAccessPlayer()) {
                return RETURNVALUE_NOTPOSSIBLE;
            }

            if (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_BLOCKPATH)) {
                return RETURNVALUE_NOTPOSSIBLE;
            }

            if (player->getParent() == nullptr && hasFlag(TILESTATE_NOLOGOUT)) {
                //player is trying to login to a "no logout" tile
                return RETURNVALUE_NOTPOSSIBLE;
            }

            const Tile* playerTile = player->getTile();
            if (playerTile && player->isPzLocked()) {
                if (!playerTile->hasFlag(TILESTATE_PVPZONE)) {
                    //player is trying to enter a pvp zone while being pz-locked
                    if (hasFlag(TILESTATE_PVPZONE)) {
                        return RETURNVALUE_PLAYERISPZLOCKEDENTERPVPZONE;
                    }
                } else if (!hasFlag(TILESTATE_PVPZONE)) {
                    // player is trying to leave a pvp zone while being pz-locked
                    return RETURNVALUE_PLAYERISPZLOCKEDLEAVEPVPZONE;
                }

                if ((!playerTile->hasFlag(TILESTATE_NOPVPZONE) && hasFlag(TILESTATE_NOPVPZONE)) ||
                    (!playerTile->hasFlag(TILESTATE_PROTECTIONZONE) && hasFlag(TILESTATE_PROTECTIONZONE))) {
                    // player is trying to enter a non-pvp/protection zone while being pz-locked
                    return RETURNVALUE_PLAYERISPZLOCKED;
                }
            }
        } else if (creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags)) {
            for (const Creature* tileCreature : *creatures) {
                if (!tileCreature->isInGhostMode()) {
                    return RETURNVALUE_NOTENOUGHROOM;
                }
            }
        }

        if (!hasBitSet(FLAG_IGNOREBLOCKITEM, flags)) {
            //If the FLAG_IGNOREBLOCKITEM bit isn't set we dont have to iterate every single item
            if (hasFlag(TILESTATE_BLOCKSOLID)) {
                return RETURNVALUE_NOTENOUGHROOM;
            }
        } else {
            //FLAG_IGNOREBLOCKITEM is set
            if (ground) {
                const ItemType& iiType = Item::items[ground->getID()];
                if (iiType.blockSolid) {
                    return RETURNVALUE_NOTPOSSIBLE;
                }
            }

            if (const auto items = getItemList()) {
                for (const Item* item : *items) {
                    const ItemType& iiType = Item::items[item->getID()];
                    if (iiType.blockSolid && !iiType.moveable) {
                        return RETURNVALUE_NOTPOSSIBLE;
                    }
                }
            }
        }
    } else if (const Item* item = thing.getItem()) {
        const TileItemVector* items = getItemList();
        if (items && items->size() >= 0xFFFF) {
            return RETURNVALUE_NOTPOSSIBLE;
        }

        if (hasBitSet(FLAG_NOLIMIT, flags)) {
            return RETURNVALUE_NOERROR;
        }

        bool itemIsHangable = item->isHangable();
        if (ground == nullptr && !itemIsHangable) {
            return RETURNVALUE_NOTPOSSIBLE;
        }

        const CreatureVector* creatures = getCreatures();
        if (creatures && !creatures->empty() && item->isBlocking() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags)) {
            for (const Creature* tileCreature : *creatures) {
                if (!tileCreature->isInGhostMode()) {
                    return RETURNVALUE_NOTENOUGHROOM;
                }
            }
        }

        if (item->isMagicField() && hasProperty(CONST_PROP_IMMOVABLENOFIELDBLOCKPATH)) {
            return RETURNVALUE_NOTENOUGHROOM;
        }

        if (itemIsHangable && hasFlag(TILESTATE_SUPPORTS_HANGABLE)) {
            if (items) {
                for (const Item* tileItem : *items) {
                    if (tileItem->isHangable()) {
                        return RETURNVALUE_NEEDEXCHANGE;
                    }
                }
            }
        } else {
            if (ground) {
                const ItemType& iiType = Item::items[ground->getID()];
                if (iiType.blockSolid) {
                    if (!iiType.allowPickupable || item->isMagicField() || item->isBlocking()) {
                        if (!item->isPickupable()) {
                            return RETURNVALUE_NOTENOUGHROOM;
                        }

                        if (!iiType.hasHeight || iiType.pickupable || iiType.isBed()) {
                            return RETURNVALUE_NOTENOUGHROOM;
                        }
                    }
                }
            }

            if (items) {
                for (const Item* tileItem : *items) {
                    const ItemType& iiType = Item::items[tileItem->getID()];
                    if (!iiType.blockSolid) {
                        continue;
                    }

                    if (iiType.allowPickupable && !item->isMagicField() && !item->isBlocking()) {
                        continue;
                    }

                    if (!item->isPickupable()) {
                        return RETURNVALUE_NOTENOUGHROOM;
                    }

                    if (!iiType.hasHeight || iiType.pickupable || iiType.isBed()) {
                        return RETURNVALUE_NOTENOUGHROOM;
                    }
                }
            }
        }
    }
    return RETURNVALUE_NOERROR;
}

ReturnValue Tile::queryMaxCount(int32_t, const Thing&, uint32_t count, uint32_t& maxQueryCount, uint32_t) const
{
    maxQueryCount = std::max<uint32_t>(1, count);
    return RETURNVALUE_NOERROR;
}

ReturnValue Tile::queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const
{
    int32_t index = getThingIndex(&thing);
    if (index == -1) {
        return RETURNVALUE_NOTPOSSIBLE;
    }

    const Item* item = thing.getItem();
    if (item == nullptr) {
        return RETURNVALUE_NOTPOSSIBLE;
    }

    if (count == 0 || (item->isStackable() && count > item->getItemCount())) {
        return RETURNVALUE_NOTPOSSIBLE;
    }

    if (!item->isMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags)) {
        return RETURNVALUE_NOTMOVEABLE;
    }

    return RETURNVALUE_NOERROR;
}

Tile* Tile::queryDestination(int32_t&, const Thing&, Item** destItem, uint32_t&)
{
    Thing* destThing = getTopDownItem();
    if (destThing) {
        *destItem = destThing->getItem();
    }

    return this;
}

void Tile::addThing(Thing* thing)
{
    addThing(0, thing);
}

void Tile::addThing(int32_t, Thing* thing)
{
    Creature* creature = thing->getCreature();
    if (creature) {
        g_game.map.clearSpectatorCache();
        creature->setParent(this);
        CreatureVector* creatures = makeCreatures();
        creatures->insert(creatures->end(), creature);
    } else {
        Item* item = thing->getItem();
        if (item == nullptr) {
            return /*RETURNVALUE_NOTPOSSIBLE*/;
        }

        TileItemVector* items = getItemList();
        if (items && items->size() >= 0xFFFF) {
            return /*RETURNVALUE_NOTPOSSIBLE*/;
        }

        item->setParent(this);

        const ItemType& itemType = Item::items[item->getID()];
        if (itemType.isGroundTile()) {
            if (ground == nullptr) {
                ground = item;
                onAddTileItem(item);
            } else {
                const ItemType& oldType = Item::items[ground->getID()];

                Item* oldGround = ground;
                ground->setParent(nullptr);
                g_game.ReleaseItem(ground);
                ground = item;
                resetTileFlags(oldGround);
                setTileFlags(item);
                onUpdateTileItem(oldGround, oldType, item, itemType);
                postRemoveNotification(oldGround, nullptr, 0);
            }
        } else if (itemType.alwaysOnTop) {
            if (itemType.isSplash() && items) {
                //remove old splash if exists
                for (ItemVector::const_iterator it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
                    Item* oldSplash = *it;
                    if (!Item::items[oldSplash->getID()].isSplash()) {
                        continue;
                    }

                    removeThing(oldSplash, 1);
                    oldSplash->setParent(nullptr);
                    g_game.ReleaseItem(oldSplash);
                    postRemoveNotification(oldSplash, nullptr, 0);
                    break;
                }
            }

            bool isInserted = false;

            if (items) {
                for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
                    //Note: this is different from internalAddThing
                    if (itemType.alwaysOnTopOrder <= Item::items[(*it)->getID()].alwaysOnTopOrder) {
                        items->insert(it, item);
                        isInserted = true;
                        break;
                    }
                }
            } else {
                items = makeItemList();
            }

            if (!isInserted) {
                items->push_back(item);
            }

            onAddTileItem(item);
        } else {
            if (itemType.isMagicField()) {
                //remove old field item if exists
                if (items) {
                    for (ItemVector::const_iterator it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
                        MagicField* oldField = (*it)->getMagicField();
                        if (oldField) {
                            if (oldField->isReplaceable()) {
                                removeThing(oldField, 1);

                                oldField->setParent(nullptr);
                                g_game.ReleaseItem(oldField);
                                postRemoveNotification(oldField, nullptr, 0);
                                break;
                            } else {
                                //This magic field cannot be replaced.
                                item->setParent(nullptr);
                                g_game.ReleaseItem(item);
                                return;
                            }
                        }
                    }
                }
            }

            items = makeItemList();
            items->insert(items->getBeginDownItem(), item);
            items->addDownItemCount(1);
            onAddTileItem(item);
        }
    }
}

void Tile::updateThing(Thing* thing, uint16_t itemId, uint32_t count)
{
    int32_t index = getThingIndex(thing);
    if (index == -1) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    Item* item = thing->getItem();
    if (item == nullptr) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    const ItemType& oldType = Item::items[item->getID()];
    const ItemType& newType = Item::items[itemId];
    resetTileFlags(item);
    item->setID(itemId);
    item->setSubType(count);
    setTileFlags(item);
    onUpdateTileItem(item, oldType, item, newType);
}

void Tile::replaceThing(uint32_t index, Thing* thing)
{
    int32_t pos = index;

    Item* item = thing->getItem();
    if (item == nullptr) {
        return /*RETURNVALUE_NOTPOSSIBLE*/;
    }

    Item* oldItem = nullptr;
    bool isInserted = false;

    if (ground) {
        if (pos == 0) {
            oldItem = ground;
            ground = item;
            isInserted = true;
        }

        --pos;
    }

    TileItemVector* items = getItemList();
    if (items && !isInserted) {
        int32_t topItemSize = getTopItemCount();
        if (pos < topItemSize) {
            auto it = items->getBeginTopItem();
            it += pos;

            oldItem = (*it);
            it = items->erase(it);
            items->insert(it, item);
            isInserted = true;
        }

        pos -= topItemSize;
    }

    CreatureVector* creatures = getCreatures();
    if (creatures) {
        if (!isInserted && pos < static_cast<int32_t>(creatures->size())) {
            return /*RETURNVALUE_NOTPOSSIBLE*/;
        }

        pos -= static_cast<uint32_t>(creatures->size());
    }

    if (items && !isInserted) {
        int32_t downItemSize = getDownItemCount();
        if (pos < downItemSize) {
            auto it = items->getBeginDownItem() + pos;
            oldItem = *it;
            it = items->erase(it);
            items->insert(it, item);
            isInserted = true;
        }
    }

    if (isInserted) {
        item->setParent(this);

        resetTileFlags(oldItem);
        setTileFlags(item);
        const ItemType& oldType = Item::items[oldItem->getID()];
        const ItemType& newType = Item::items[item->getID()];
        onUpdateTileItem(oldItem, oldType, item, newType);

        oldItem->setParent(nullptr);
        return /*RETURNVALUE_NOERROR*/;
    }
}

void Tile::removeThing(Thing* thing, uint32_t count)
{
    Creature* creature = thing->getCreature();
    if (creature) {
        CreatureVector* creatures = getCreatures();
        if (creatures) {
            auto it = std::find(creatures->begin(), creatures->end(), thing);
            if (it != creatures->end()) {
                g_game.map.clearSpectatorCache();
                creatures->erase(it);
            }
        }
        return;
    }

    Item* item = thing->getItem();
    if (!item) {
        return;
    }

    int32_t index = getThingIndex(item);
    if (index == -1) {
        return;
    }

    if (item == ground) {
        ground->setParent(nullptr);
        ground = nullptr;

        SpectatorVec list;
        g_game.map.getSpectators(list, getPosition(), true);
        onRemoveTileItem(list, std::vector<int32_t>(list.size(), 0), item);
        return;
    }

    TileItemVector* items = getItemList();
    if (!items) {
        return;
    }

    const ItemType& itemType = Item::items[item->getID()];
    if (itemType.alwaysOnTop) {
        auto it = std::find(items->getBeginTopItem(), items->getEndTopItem(), item);
        if (it == items->getEndTopItem()) {
            return;
        }

        std::vector<int32_t> oldStackPosVector;

        SpectatorVec list;
        g_game.map.getSpectators(list, getPosition(), true);
        for (Creature* spectator : list) {
            if (Player* tmpPlayer = spectator->getPlayer()) {
                oldStackPosVector.push_back(getStackposOfItem(tmpPlayer, item));
            }
        }

        item->setParent(nullptr);
        items->erase(it);
        onRemoveTileItem(list, oldStackPosVector, item);
    } else {
        auto it = std::find(items->getBeginDownItem(), items->getEndDownItem(), item);
        if (it == items->getEndDownItem()) {
            return;
        }

        if (itemType.stackable && count != item->getItemCount()) {
            uint8_t newCount = static_cast<uint8_t>(std::max<int32_t>(0, static_cast<int32_t>(item->getItemCount() - count)));
            item->setItemCount(newCount);
            onUpdateTileItem(item, itemType, item, itemType);
        } else {
            std::vector<int32_t> oldStackPosVector;

            SpectatorVec list;
            g_game.map.getSpectators(list, getPosition(), true);
            for (Creature* spectator : list) {
                if (Player* tmpPlayer = spectator->getPlayer()) {
                    oldStackPosVector.push_back(getStackposOfItem(tmpPlayer, item));
                }
            }

            item->setParent(nullptr);
            items->erase(it);
            items->addDownItemCount(-1);
            onRemoveTileItem(list, oldStackPosVector, item);
        }
    }
}

void Tile::removeCreature(Creature* creature)
{
    g_game.map.getQTNode(tilePos.x, tilePos.y)->removeCreature(creature);
    removeThing(creature, 0);
}

int32_t Tile::getThingIndex(const Thing* thing) const
{
    int32_t n = -1;
    if (ground) {
        if (ground == thing) {
            return 0;
        }
        ++n;
    }

    const TileItemVector* items = getItemList();
    if (items) {
        const Item* item = thing->getItem();
        if (item && item->isAlwaysOnTop()) {
            for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
                ++n;
                if (*it == item) {
                    return n;
                }
            }
        } else {
            n += items->getTopItemCount();
        }
    }

    if (const CreatureVector* creatures = getCreatures()) {
        if (thing->getCreature()) {
            for (Creature* creature : *creatures) {
                ++n;
                if (creature == thing) {
                    return n;
                }
            }
        } else {
            n += creatures->size();
        }
    }

    if (items) {
        const Item* item = thing->getItem();
        if (item && !item->isAlwaysOnTop()) {
            for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
                ++n;
                if (*it == item) {
                    return n;
                }
            }
        }
    }
    return -1;
}

int32_t Tile::getClientIndexOfCreature(const Player* player, const Creature* creature) const
{
    int32_t n;
    if (ground) {
        n = 1;
    } else {
        n = 0;
    }

    const TileItemVector* items = getItemList();
    if (items) {
        n += items->getTopItemCount();
    }

    if (const CreatureVector* creatures = getCreatures()) {
        for (const Creature* c : boost::adaptors::reverse(*creatures)) {
            if (c == creature) {
                return n;
            } else if (player->canSeeCreature(c)) {
                ++n;
            }
        }
    }
    return -1;
}

int32_t Tile::getStackposOfCreature(const Player* player, const Creature* creature) const
{
    int32_t n;
    if (ground) {
        n = 1;
    } else {
        n = 0;
    }

    const TileItemVector* items = getItemList();
    if (items) {
        n += items->getTopItemCount();
        if (n >= 10) {
            return -1;
        }
    }

    if (const CreatureVector* creatures = getCreatures()) {
        for (const Creature* c : boost::adaptors::reverse(*creatures)) {
            if (c == creature) {
                return n;
            } else if (player->canSeeCreature(c)) {
                if (++n >= 10) {
                    return -1;
                }
            }
        }
    }
    return -1;
}

int32_t Tile::getStackposOfItem(const Player* player, const Item* item) const
{
    int32_t n = 0;
    if (ground) {
        if (ground == item) {
            return n;
        }
        ++n;
    }

    const TileItemVector* items = getItemList();
    if (items) {
        if (item->isAlwaysOnTop()) {
            for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
                if (*it == item) {
                    return n;
                } else if (++n == 10) {
                    return -1;
                }
            }
        } else {
            n += items->getTopItemCount();
            if (n >= 10) {
                return -1;
            }
        }
    }

    if (const CreatureVector* creatures = getCreatures()) {
        for (const Creature* creature : *creatures) {
            if (player->canSeeCreature(creature)) {
                if (++n >= 10) {
                    return -1;
                }
            }
        }
    }

    if (items && !item->isAlwaysOnTop()) {
        for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
            if (*it == item) {
                return n;
            } else if (++n >= 10) {
                return -1;
            }
        }
    }
    return -1;
}

size_t Tile::getFirstIndex() const
{
    return 0;
}

size_t Tile::getLastIndex() const
{
    return getThingCount();
}

uint32_t Tile::getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) const
{
    uint32_t count = 0;
    if (ground && ground->getID() == itemId) {
        count += Item::countByType(ground, subType);
    }

    const TileItemVector* items = getItemList();
    if (items) {
        for (const Item* item : *items) {
            if (item->getID() == itemId) {
                count += Item::countByType(item, subType);
            }
        }
    }
    return count;
}

Thing* Tile::getThing(size_t index) const
{
    if (ground) {
        if (index == 0) {
            return ground;
        }

        --index;
    }

    const TileItemVector* items = getItemList();
    if (items) {
        uint32_t topItemSize = items->getTopItemCount();
        if (index < topItemSize) {
            return items->at(items->getDownItemCount() + index);
        }
        index -= topItemSize;
    }

    if (const CreatureVector* creatures = getCreatures()) {
        if (index < creatures->size()) {
            return (*creatures)[index];
        }
        index -= creatures->size();
    }

    if (items && index < items->getDownItemCount()) {
        return items->at(index);
    }
    return nullptr;
}

void Tile::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link /*= LINK_OWNER*/)
{
    SpectatorVec list;
    g_game.map.getSpectators(list, getPosition(), true, true);
    for (Creature* spectator : list) {
        spectator->getPlayer()->postAddNotification(thing, oldParent, index, LINK_NEAR);
    }

    //add a reference to this item, it may be deleted after being added (mailbox for example)
    Creature* creature = thing->getCreature();
    Item* item;
    if (creature) {
        creature->incrementReferenceCounter();
        item = nullptr;
    } else {
        item = thing->getItem();
        if (item) {
            item->incrementReferenceCounter();
        }
    }

    if (link == LINK_OWNER) {
        if (hasFlag(TILESTATE_TELEPORT)) {
            Teleport* teleport = getTeleportItem();
            if (teleport) {
                teleport->addThing(thing);
            }
        } else if (hasFlag(TILESTATE_MAILBOX)) {
            Mailbox* mailbox = getMailbox();
            if (mailbox) {
                mailbox->addThing(thing);
            }
        }

        //calling movement scripts
        if (creature) {
            g_moveEvents->onCreatureMove(creature, this, MOVE_EVENT_STEP_IN);
        } else if (item) {
            g_moveEvents->onItemMove(item, this, true);
        }
    }

    //release the reference to this item onces we are finished
    if (creature) {
        g_game.ReleaseCreature(creature);
    } else if (item) {
        g_game.ReleaseItem(item);
    }
}

void Tile::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t)
{
    SpectatorVec list;
    g_game.map.getSpectators(list, getPosition(), true, true);

    if (getThingCount() > ? {
        onUpdateTile(list);
    }

    for (Creature* spectator : list) {
        spectator->getPlayer()->postRemoveNotification(thing, newParent, index, LINK_NEAR);
    }

    //calling movement scripts
    Creature* creature = thing->getCreature();
    if (creature) {
        g_moveEvents->onCreatureMove(creature, this, MOVE_EVENT_STEP_OUT);
    } else {
        Item* item = thing->getItem();
        if (item) {
            g_moveEvents->onItemMove(item, this, false);
        }
    }
}

void Tile::internalAddThing(Thing* thing)
{
    internalAddThing(0, thing);
}

void Tile::internalAddThing(uint32_t, Thing* thing)
{
    thing->setParent(this);

    Creature* creature = thing->getCreature();
    if (creature) {
        g_game.map.clearSpectatorCache();
        CreatureVector* creatures = makeCreatures();
        creatures->insert(creatures->end(), creature);
    } else {
        Item* item = thing->getItem();
        if (item == nullptr) {
            return;
        }

        const ItemType& itemType = Item::items[item->getID()];
        if (itemType.isGroundTile()) {
            if (ground == nullptr) {
                ground = item;
                setTileFlags(item);
            }
            return;
        }

        TileItemVector* items = makeItemList();
        if (items->size() >= 0xFFFF) {
            return /*RETURNVALUE_NOTPOSSIBLE*/;
        }

        if (itemType.alwaysOnTop) {
            bool isInserted = false;
            for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
                if (Item::items[(*it)->getID()].alwaysOnTopOrder > itemType.alwaysOnTopOrder) {
                    items->insert(it, item);
                    isInserted = true;
                    break;
                }
            }

            if (!isInserted) {
                items->push_back(item);
            }
        } else {
            items->insert(items->getBeginDownItem(), item);
            items->addDownItemCount(1);
        }

        setTileFlags(item);
    }
}

void Tile::setTileFlags(const Item* item)
{
    if (item->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID)) {
        setFlag(TILESTATE_IMMOVABLEBLOCKSOLID);
    }

    if (item->hasProperty(CONST_PROP_BLOCKPATH)) {
        setFlag(TILESTATE_BLOCKPATH);
    }

    if (item->hasProperty(CONST_PROP_NOFIELDBLOCKPATH)) {
        setFlag(TILESTATE_NOFIELDBLOCKPATH);
    }

    if (item->hasProperty(CONST_PROP_IMMOVABLENOFIELDBLOCKPATH)) {
        setFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH);
    }

    if (item->getTeleport()) {
        setFlag(TILESTATE_TELEPORT);
    }

    if (item->getMagicField()) {
        setFlag(TILESTATE_MAGICFIELD);
    }

    if (item->getMailbox()) {
        setFlag(TILESTATE_MAILBOX);
    }

    if (item->hasProperty(CONST_PROP_BLOCKSOLID)) {
        setFlag(TILESTATE_BLOCKSOLID);
    }

    if (item->getBed()) {
        setFlag(TILESTATE_BED);
    }

    if (item->getCombatType() == COMBAT_FIREDAMAGE) {
        setFlag(TILESTATE_FIREDAMAGE);
    }

    if (item->getCombatType() == COMBAT_ENERGYDAMAGE) {
        setFlag(TILESTATE_ENERGYDAMAGE);
    }

    if (item->getCombatType() == COMBAT_EARTHDAMAGE) {
        setFlag(TILESTATE_POISONDAMAGE);
    }

    const Container* container = item->getContainer();
    if (container && container->getDepotLocker()) {
        setFlag(TILESTATE_DEPOT);
    }

    if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) {
        setFlag(TILESTATE_SUPPORTS_HANGABLE);
    }
}

void Tile::resetTileFlags(const Item* item)
{
    if (item->hasProperty(CONST_PROP_BLOCKSOLID) && !hasProperty(item, CONST_PROP_BLOCKSOLID)) {
        resetFlag(TILESTATE_BLOCKSOLID);
    }

    if (item->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID) && !hasProperty(item, CONST_PROP_IMMOVABLEBLOCKSOLID)) {
        resetFlag(TILESTATE_IMMOVABLEBLOCKSOLID);
    }

    if (item->hasProperty(CONST_PROP_BLOCKPATH) && !hasProperty(item, CONST_PROP_BLOCKPATH)) {
        resetFlag(TILESTATE_BLOCKPATH);
    }

    if (item->hasProperty(CONST_PROP_NOFIELDBLOCKPATH) && !hasProperty(item, CONST_PROP_NOFIELDBLOCKPATH)) {
        resetFlag(TILESTATE_NOFIELDBLOCKPATH);
    }

    if (item->hasProperty(CONST_PROP_IMMOVABLEBLOCKPATH) && !hasProperty(item, CONST_PROP_IMMOVABLEBLOCKPATH)) {
        resetFlag(TILESTATE_IMMOVABLEBLOCKPATH);
    }

    if (item->hasProperty(CONST_PROP_IMMOVABLENOFIELDBLOCKPATH) && !hasProperty(item, CONST_PROP_IMMOVABLENOFIELDBLOCKPATH)) {
        resetFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH);
    }

    if (item->getTeleport()) {
        resetFlag(TILESTATE_TELEPORT);
    }

    if (item->getMagicField()) {
        resetFlag(TILESTATE_MAGICFIELD);
    }

    if (item->getMailbox()) {
        resetFlag(TILESTATE_MAILBOX);
    }

    if (item->getBed()) {
        resetFlag(TILESTATE_BED);
    }

    if (item->getCombatType() == COMBAT_FIREDAMAGE) {
        resetFlag(TILESTATE_FIREDAMAGE);
    }

    if (item->getCombatType() == COMBAT_ENERGYDAMAGE) {
        resetFlag(TILESTATE_ENERGYDAMAGE);
    }

    if (item->getCombatType() == COMBAT_EARTHDAMAGE) {
        resetFlag(TILESTATE_POISONDAMAGE);
    }

    const Container* container = item->getContainer();
    if (container && container->getDepotLocker()) {
        resetFlag(TILESTATE_DEPOT);
    }

    if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) {
        resetFlag(TILESTATE_SUPPORTS_HANGABLE);
    }
}

bool Tile::isMoveableBlocking() const
{
    return !ground || hasFlag(TILESTATE_BLOCKSOLID);
}

Item* Tile::getUseItem() const
{
    const TileItemVector* items = getItemList();
    if (!items || items->size() == 0) {
        return ground;
    }

    for (Item* item : boost::adaptors::reverse(*items)) {
        if (Item::items[item->getID()].forceUse) {
            return item;
        }
    }

    Item* item = items->getTopDownItem();
    if (!item) {
        item = items->getTopTopItem();
    }
    return item;
}
 

 

Link para o post
Compartilhar em outros sites

Mude essa parte:

if (creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags) && !player->isAccessPlayer()) {
	return RETURNVALUE_NOTPOSSIBLE;
}

 

 

Para:

if (creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags) && !player->isAccessPlayer()) {
	for (const Creature* tileCreature : *creatures) {
		if (!tileCreature->isInGhostMode()) {
			return RETURNVALUE_NOTPOSSIBLE;
		}
	}
}

 

Editado por Toruk (veja o histórico de edições)
Link para o post
Compartilhar em outros sites
  • 2 weeks later...

@Toruk irmão, será que consegue me ajudar novamente?

Notei que quando estou em ghost (agora atravessavél :P), se um player entra no meu sqm e tenta usar uma uh ou mana fluid, pega em mim. (nele brilha como se usado mas não heala ele.)
Quando ele anda ele fica no "topo" ou na a "baixo" do empilhamento, nao consegui entender direito mas acho que ele fica em cima, e cai na uh trap.
tentei editar esse trecho, para ignorar o ghost mode no empilhamento.. mas da erro na source qndo vou compilar.. não estou codando certo.


 

Quote

 

Creature* Tile::getTopCreature() const
{
    if (const CreatureVector* creatures = getCreatures()) {
        if (!creatures->empty() && !creatures->isInGhostMode()) { //editei aqui
            return *creatures->begin();
        }
    }
    return nullptr;
}

const Creature* Tile::getBottomCreature() const
{
    if (const CreatureVector* creatures = getCreatures()) {
        if (!creatures->empty() && !creatures->isInGhostMode()) {  //editei aqui
            return *creatures->rbegin();
        }
    }
    return nullptr;

 

Quote

src/tile.cpp:229:56: error: ‘const CreatureVector’ {aka ‘const class std::vector<Creature*>’} has no member named ‘isInGhostMode’
  229 |                 if (!creatures->empty() && !creatures->isInGhostMode())

 

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 Muvuka
      Alguem tem anti-nuker igual a esse 
       

       
    • Por Muvuka
      [SQLite] -=[TFS]=- 0.4 8.60 Alguem faz apk mobile pra mim ip: dexsoft.ddns.net
       
      pra mim
       
      https://www.mediafire.com/file/5klqnyy6k7jda0u/OTClientV8.rar/file
       
      TA TUDO AI
    • Por yuriowns
      Salve rapazes, estou precisando de um client próprio para o meu servidor 7.4, preciso que algum programador experiente e com referências faça um client do jeito que eu procuro. Responda aqui para fazermos um orçamento, obrigado!

      Não sei se estou no lugar certo, se não me desculpem e peço que movam por gentileza!
    • Por paulo thush
      Pessoal to com um grande problema, estou com um servidor TFS 1.4x 10.98, recentemente começou dar um problema, sempre quando falava "trade" com o npc dava um erros, com qual quer npc, o erro e o seguinte.
       
       
      me falaram que o problema e nas sourcer que precisava mudar umas coisas me passaram um link no github esse aqui 
      https://github.com/otland/forgottenserver/pull/3996/files
       
      porem eu vi vídeos no youtube ensinando a compilar, já vi muitos tópicos como compilar a sourcer, ai quando vou compilar da esse erro
      já tentei instalar, desinstala muitas coisas, alterar também não vai, minha sourcer e essa 
      https://github.com/otland/forgottenserver
       
       
      Alguém poderia me ajuda com esse erro, ou ate compilar 100% as sourcer. os Tópicos que eu tentei para compilar e esse daqui, se não poder o link me desculpe.
      https://forums.otserv.com.br/index.php?/forums/topic/169234-windowsvc2019-compilando-sources-tfs-14-vcpkg/
       
      alguém me da uma luz por favor kkk
    • Por Ryzek
      Uso tfs 0.4 trunk3884 bem simples.
      Queria acrescentar magic effects para 255 pois o meu só vai até 69. Encontrei um tópico que falava sobre porém parece ter sido removido, não consigo acessar!
×
×
  • Criar Novo...

Informação Importante

Confirmação de Termo