Jump to content

Recommended Posts

Seguinte galera, eu estava utilizando uma base otx2 e achei um script bem bacana onde voce vai na source e edita a linha uint32_t Player::getAttackSpeed() const e com isso você consegue colocar o fist attack para influenciar no attack speed. porem agora estou mudando de source para a base do tfs 1.3 Downgrade para 8.6 porem não estou achando onde editar para que o fist influencie no attack speed.

 

 

 

Aqui vai o Player.cpp

 

Spoiler

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

#include "otpch.h"

#include <bitset>

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

extern ConfigManager g_config;
extern Game g_game;
extern Chat* g_chat;
extern Vocations g_vocations;
extern MoveEvents* g_moveEvents;
extern Weapons* g_weapons;
extern CreatureEvents* g_creatureEvents;
extern Events* g_events;

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 (party) {
        if (lookDistance == -1) {
            s << " Your party has ";
        } else if (sex == PLAYERSEX_FEMALE) {
            s << " She is in a party with ";
        } else {
            s << " He is in a party with ";
        }

        size_t memberCount = party->getMemberCount() + 1;
        if (memberCount == 1) {
            s << "1 member and ";
        } else {
            s << memberCount << " members and ";
        }

        size_t invitationCount = party->getInvitationCount();
        if (invitationCount == 1) {
            s << "1 pending invitation.";
        } else {
            s << invitationCount << " pending invitations.";
        }
    }

    if (!guild || !guildRank) {
        return s.str();
    }

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

    size_t memberCount = guild->getMemberCount();
    if (memberCount == 1) {
        s << ", which has 1 member, " << guild->getMembersOnline().size() << " of them online.";
    } else {
        s << ", which has " << memberCount << " members, " << guild->getMembersOnline().size() << " of them online.";
    }
    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(slots_t slot, bool ignoreAmmo) const
{
    Item* item = inventory[slot];
    if (!item) {
        return nullptr;
    }

    WeaponType_t weaponType = item->getWeaponType();
    if (weaponType == WEAPON_NONE || weaponType == WEAPON_SHIELD || weaponType == WEAPON_AMMO) {
        return nullptr;
    }

    if (!ignoreAmmo && weaponType == WEAPON_DISTANCE) {
        const ItemType& it = Item::items[item->getID()];
        if (it.ammoType != AMMO_NONE) {
            Item* ammoItem = inventory[CONST_SLOT_AMMO];
            if (!ammoItem || ammoItem->getAmmoType() != it.ammoType) {
                return nullptr;
            }
            item = ammoItem;
        }
    }
    return item;
}

Item* Player::getWeapon(bool ignoreAmmo/* = false*/) const
{
    Item* item = getWeapon(CONST_SLOT_LEFT, ignoreAmmo);
    if (item) {
        return item;
    }

    item = getWeapon(CONST_SLOT_RIGHT, ignoreAmmo);
    if (item) {
        return item;
    }
    return nullptr;
}

WeaponType_t Player::getWeaponType() const
{
    Item* item = getWeapon();
    if (!item) {
        return WEAPON_NONE;
    }
    return item->getWeaponType();
}

int32_t Player::getWeaponSkill(const Item* item) const
{
    if (!item) {
        return getSkillLevel(SKILL_FIST);
    }

    int32_t attackSkill;

    WeaponType_t weaponType = item->getWeaponType();
    switch (weaponType) {
        case WEAPON_SWORD: {
            attackSkill = getSkillLevel(SKILL_SWORD);
            break;
        }

        case WEAPON_CLUB: {
            attackSkill = getSkillLevel(SKILL_CLUB);
            break;
        }

        case WEAPON_AXE: {
            attackSkill = getSkillLevel(SKILL_AXE);
            break;
        }

        case WEAPON_DISTANCE: {
            attackSkill = getSkillLevel(SKILL_DISTANCE);
            break;
        }

        default: {
            attackSkill = 0;
            break;
        }
    }
    return attackSkill;
}

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

    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();
        }
    }
    return static_cast<int32_t>(armor * vocation->armorMultiplier);
}

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() const
{
    int32_t defenseSkill = getSkillLevel(SKILL_FIST);
    int32_t defenseValue = 7;
    const Item* weapon;
    const Item* shield;
    getShieldAndWeapon(shield, weapon);

    if (weapon) {
        defenseValue = weapon->getDefense() + weapon->getExtraDefense();
        defenseSkill = getWeaponSkill(weapon);
    }

    if (shield) {
        defenseValue = weapon != nullptr ? shield->getDefense() + weapon->getExtraDefense() : shield->getDefense();
        defenseSkill = getSkillLevel(SKILL_SHIELD);
    }

    if (defenseSkill == 0) {
        switch (fightMode) {
            case FIGHTMODE_ATTACK:
            case FIGHTMODE_BALANCED:
                return 1;

            case FIGHTMODE_DEFENSE:
                return 2;
        }
    }

    return (defenseSkill / 4. + 2.23) * defenseValue * 0.15 * getDefenseFactor() * vocation->defenseMultiplier;
}

float Player::getAttackFactor() const
{
    switch (fightMode) {
        case FIGHTMODE_ATTACK: return 1.0f;
        case FIGHTMODE_BALANCED: return 1.2f;
        case FIGHTMODE_DEFENSE: return 2.0f;
        default: return 1.0f;
    }
}

float Player::getDefenseFactor() const
{
    switch (fightMode) {
        case FIGHTMODE_ATTACK: return (OTSYS_TIME() - lastAttack) < getAttackSpeed() ? 0.5f : 1.0f;
        case FIGHTMODE_BALANCED: return (OTSYS_TIME() - lastAttack) < getAttackSpeed() ? 0.75f : 1.0f;
        case FIGHTMODE_DEFENSE: return 1.0f;
        default: return 1.0f;
    }
}

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

    if (pzLocked) {
        icons |= ICON_REDSWORDS;
    }

    if (tile->hasFlag(TILESTATE_PROTECTIONZONE)) {
        icons |= ICON_PIGEON;

        // Don't show ICON_SWORDS if player is in protection zone.
        if (hasBitSet(ICON_SWORDS, icons)) {
            icons &= ~ICON_SWORDS;
        }
    }

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

    g_events->eventPlayerOnGainSkillTries(this, skill, count);
    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()) {
                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;
}

bool Player::canOpenCorpse(uint32_t ownerId) const
{
    return getID() == ownerId || (party && party->canOpenCorpse(ownerId));
}

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, const bool isLogin/* = false*/)
{
    if (IS_IN_KEYRANGE(key, RESERVED_RANGE)) {
        if (IS_IN_KEYRANGE(key, OUTFITS_RANGE)) {
            outfits.emplace_back(
                value >> 16,
                value & 0xFF
            );
            return;
        } else {
            std::cout << "Warning: unknown reserved key: " << key << " player: " << getName() << std::endl;
            return;
        }
    }

    if (value != -1) {
        int32_t oldValue;
        getStorageValue(key, oldValue);

        storageMap[key] = value;

        if (!isLogin) {
            auto currentFrameTime = g_dispatcher.getDispatcherCycle();
            if (lastQuestlogUpdate != currentFrameTime && g_game.quests.isQuestStorage(key, value, oldValue)) {
                lastQuestlogUpdate = currentFrameTime;
                sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your questlog has been updated.");
            }
        }
    } 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 = -1;
        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;
}

bool Player::canWalkthrough(const Creature* creature) const
{
    if (group->access || creature->isInGhostMode()) {
        return true;
    }

    const Player* player = creature->getPlayer();
    if (!player) {
        return false;
    }

    const Tile* playerTile = player->getTile();
    if (!playerTile || (!playerTile->hasFlag(TILESTATE_PROTECTIONZONE) && player->getLevel() > static_cast<uint32_t>(g_config.getNumber(ConfigManager::PROTECTION_LEVEL)))) {
        return false;
    }

    const Item* playerTileGround = playerTile->getGround();
    if (!playerTileGround || !playerTileGround->hasWalkStack()) {
        return false;
    }

    Player* thisPlayer = const_cast<Player*>(this);
    if ((OTSYS_TIME() - lastWalkthroughAttempt) > 2000) {
        thisPlayer->setLastWalkthroughAttempt(OTSYS_TIME());
        return false;
    }

    if (creature->getPosition() != lastWalkthroughPosition) {
        thisPlayer->setLastWalkthroughPosition(creature->getPosition());
        return false;
    }

    thisPlayer->setLastWalkthroughPosition(creature->getPosition());
    return true;
}

bool Player::canWalkthroughEx(const Creature* creature) const
{
    if (group->access) {
        return true;
    }

    const Player* player = creature->getPlayer();
    if (!player) {
        return false;
    }

    const Tile* playerTile = player->getTile();
    return playerTile && (playerTile->hasFlag(TILESTATE_PROTECTIONZONE) || player->getLevel() <= static_cast<uint32_t>(g_config.getNumber(ConfigManager::PROTECTION_LEVEL)));
}

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

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

    if (!autoCreate) {
        return nullptr;
    }

    DepotChest* depotChest = new DepotChest(ITEM_DEPOT);
    depotChest->incrementReferenceCounter();
    depotChests[depotId] = depotChest;
    return depotChest;
}

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

    DepotLocker* depotLocker = new DepotLocker(ITEM_LOCKER);
    depotLocker->setDepotId(depotId);
    depotLocker->internalAddThing(getDepotChest(depotId, true));
    depotLockerMap[depotId] = depotLocker;
    return depotLocker;
}

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

        Account account = IOLoginData::loadAccount(accountNumber);
        Game::updatePremium(account);

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

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

        for (Condition* condition : getMuteConditions()) {
            condition->setTicks(condition->getTicks());
            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);
        }
    }

    g_game.updateCreatureWalkthrough(this);
    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);
        }

        closeShopWindow();

        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::openShopWindow(const std::list<ShopInfo>& shop)
{
    shopItemList = shop;
    sendShop();
    sendSaleItemList();
}

bool Player::closeShopWindow(bool sendCloseShopWindow /*= true*/)
{
    //unreference callbacks
    int32_t onBuy;
    int32_t onSell;

    Npc* npc = getShopOwner(onBuy, onSell);
    if (!npc) {
        shopItemList.clear();
        return false;
    }

    setShopOwner(nullptr, -1, -1);
    npc->onPlayerEndTrade(this, onBuy, onSell);

    if (sendCloseShopWindow) {
        sendCloseShop();
    }

    shopItemList.clear();
    return true;
}

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 = dynamic_cast<const Container*>(container->getParent()) != nullptr;
    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 (idleTime > (kickAfterMinutes * 60000) + 60000) {
            kickPlayer(true);
        } else if (client && idleTime == 60000 * kickAfterMinutes) {
            std::ostringstream ss;
            ss << "There was no variation in your behaviour for " << kickAfterMinutes << " minutes. You will be disconnected in one minute if there is no change in your actions until then.";
            client->sendTextMessage(TextMessage(MESSAGE_STATUS_WARNING, ss.str()));
        }
    }

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

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)
{
    onAttacked();
    changeMana(-manaLoss);

    if (attacker) {
        addDamagePoints(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;
    }

    g_events->eventPlayerOnGainSkillTries(this, SKILL_MAGLEVEL, amount);
    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(Creature* source, uint64_t exp, bool sendText/* = false*/)
{
    uint64_t currLevelExp = Player::getExpForLevel(level);
    uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
    uint64_t rawExp = exp;
    if (currLevelExp >= nextLevelExp) {
        //player has reached max level
        levelPercent = 0;
        sendStats();
        return;
    }

    g_events->eventPlayerOnGainExperience(this, source, exp, rawExp);
    if (exp == 0) {
        return;
    }

    experience += exp;

    if (sendText) {
        std::string expString = std::to_string(exp) + (exp != 1 ? " experience points." : " experience point.");

        TextMessage message(MESSAGE_STATUS_DEFAULT, "You gained " + expString);
        sendTextMessage(message);

        std::ostringstream strExp;
        strExp << exp;
        g_game.addAnimatedText(strExp.str(), position, TEXTCOLOR_WHITE);

        SpectatorVec spectators;
        g_game.map.getSpectators(spectators, position, false, true);
        spectators.erase(this);
        if (!spectators.empty()) {
            message.type = MESSAGE_STATUS_DEFAULT;
            message.text = getName() + " gained " + expString;
            for (Creature* spectator : spectators) {
                spectator->getPlayer()->sendTextMessage(message);
            }
        }
    }

    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) {
        health = getMaxHealth();
        mana = getMaxMana();

        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, bool sendText/* = false*/)
{
    if (experience == 0 || exp == 0) {
        return;
    }

    g_events->eventPlayerOnLoseExperience(this, exp);
    if (exp == 0) {
        return;
    }

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

    if (sendText) {
        lostExp -= experience;

        std::string expString = std::to_string(lostExp) + (lostExp != 1 ? " experience points." : " experience point.");

        TextMessage message(MESSAGE_STATUS_DEFAULT, "You lost " + expString);
        sendTextMessage(message);

        std::ostringstream strExp;
        strExp << lostExp;
        g_game.addAnimatedText(strExp.str(), position, TEXTCOLOR_RED);

        SpectatorVec spectators;
        g_game.map.getSpectators(spectators, position, false, true);
        spectators.erase(this);
        if (!spectators.empty()) {
            message.type = MESSAGE_STATUS_DEFAULT;
            message.text = getName() + " lost " + expString;
            for (Creature* spectator : spectators) {
                spectator->getPlayer()->sendTextMessage(message);
            }
        }
    }

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

    while (level > 1 && experience < currLevelExp) {
        --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());
        currLevelExp = Player::getExpForLevel(level);
    }

    if (oldLevel != level) {
        health = getMaxHealth();
        mana = getMaxMana();

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

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) {
        damage = 0;
        return BLOCK_ARMOR;
    }

    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) {
            if (damage <= 0) {
                damage = 0;
                return BLOCK_ARMOR;
            }

            continue;
        }

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

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

        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) {
                    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::death(Creature* lastHitCreature)
{
    loginPosition = town->getTemplePosition();

    if (skillLoss) {
        bool lastHitPlayer = Player::lastHitIsPlayer(lastHitCreature);

        //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);
        g_events->eventPlayerOnLoseExperience(this, expLoss);

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

            if (vocation->getId() == VOCATION_NONE || level > 7) {
                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 (lastHitPlayer) {
                bitset.reset(5);
                blessings = bitset.to_ulong();
            } else {
                blessings = 32;
            }
        } else {
            blessings = 0;
        }

        sendStats();
        sendSkills();
        sendReLoginWindow();

        if (getSkull() == SKULL_BLACK) {
            health = 40;
            mana = 0;
        } else {
            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;
            }
        }
    } else {
        setSkillLoss(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::addCombatExhaust(uint32_t ticks)
{
    Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST_COMBAT, ticks, 0);
    addCondition(condition);
}

void Player::addHealExhaust(uint32_t ticks)
{
    Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST_HEAL, ticks, 0);
    addCondition(condition);
}

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

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 (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53
        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 (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53
        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)) {
        if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
            ret = RETURNVALUE_CANNOTBEDRESSED;
        } else {
            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 (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
                    if (item->getWeaponType() != WEAPON_SHIELD) {
                        ret = RETURNVALUE_CANNOTBEDRESSED;
                    } else {
                        const Item* leftItem = inventory[CONST_SLOT_LEFT];
                        if (leftItem) {
                            if ((leftItem->getSlotPosition() | slotPosition) & SLOTP_TWO_HAND) {
                                ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
                            } else {
                                ret = RETURNVALUE_NOERROR;
                            }
                        } else {
                            ret = RETURNVALUE_NOERROR;
                        }
                    }
                } else 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 (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
                    WeaponType_t type = item->getWeaponType();
                    if (type == WEAPON_NONE || type == WEAPON_SHIELD) {
                        ret = RETURNVALUE_CANNOTBEDRESSED;
                    } else if (inventory[CONST_SLOT_RIGHT] && (slotPosition & SLOTP_TWO_HAND)) {
                        ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
                    } else {
                        ret = RETURNVALUE_NOERROR;
                    }
                } else 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: {
            if ((slotPosition & SLOTP_AMMO) || g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
                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) {
        return ret;
    }

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

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

    //need an exchange with source? (destination item is swapped with currently moved item)
    const Item* inventoryItem = getInventoryItem(static_cast<slots_t>(index));
    if (inventoryItem && (!inventoryItem->isStackable() || inventoryItem->getID() != item->getID())) {
        const Cylinder* cylinder = item->getTopParent();
        if (cylinder && (dynamic_cast<const DepotChest*>(cylinder) || dynamic_cast<const Player*>(cylinder))) {
            return RETURNVALUE_NEEDEXCHANGE;
        }

        return RETURNVALUE_NOTENOUGHROOM;
    }
    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--;
                }

                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 (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()) {
            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* oldParent, 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);
    }

    bool requireListUpdate = false;

    if (link == LINK_OWNER || link == LINK_TOPPARENT) {
        const Item* i = (oldParent ? oldParent->getItem() : nullptr);

        // Check if we owned the old container too, so we don't need to do anything,
        // as the list was updated in postRemoveNotification
        assert(i ? i->getContainer() != nullptr : true);

        if (i) {
            requireListUpdate = i->getContainer()->getHoldingPlayer() != this;
        } else {
            requireListUpdate = oldParent != this;
        }

        updateInventoryWeight();
        updateItemsLight();
        sendStats();
    }

    if (const Item* item = thing->getItem()) {
        if (const Container* container = item->getContainer()) {
            onSendContainer(container);
        }

        if (shopOwner && requireListUpdate) {
            updateSaleShopList(item);
        }
    } 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* newParent, 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));
    }

    bool requireListUpdate = false;

    if (link == LINK_OWNER || link == LINK_TOPPARENT) {
        const Item* i = (newParent ? newParent->getItem() : nullptr);

        // Check if we owned the old container too, so we don't need to do anything,
        // as the list was updated in postRemoveNotification
        assert(i ? i->getContainer() != nullptr : true);

        if (i) {
            requireListUpdate = i->getContainer()->getHoldingPlayer() != this;
        } else {
            requireListUpdate = newParent != this;
        }

        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 DepotChest* depotChest = dynamic_cast<const DepotChest*>(topContainer)) {
                    bool isOwner = false;

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

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

        if (shopOwner && requireListUpdate) {
            updateSaleShopList(item);
        }
    }
}

bool Player::updateSaleShopList(const Item* item)
{
    uint16_t itemId = item->getID();
    if (itemId != ITEM_GOLD_COIN && itemId != ITEM_PLATINUM_COIN && itemId != ITEM_CRYSTAL_COIN) {
        auto it = std::find_if(shopItemList.begin(), shopItemList.end(), [itemId](const ShopInfo& shopInfo) { return shopInfo.itemId == itemId && shopInfo.sellPrice != 0; });
        if (it == shopItemList.end()) {
            const Container* container = item->getContainer();
            if (!container) {
                return false;
            }

            const auto& items = container->getItemList();
            return std::any_of(items.begin(), items.end(), [this](const Item* containerItem) {
                return updateSaleShopList(containerItem);
            });
        }
    }

    if (client) {
        client->sendSaleItemList(shopItemList);
    }
    return true;
}

bool Player::hasShopItemForSale(uint32_t itemId, uint8_t subType) const
{
    const ItemType& itemType = Item::items[itemId];
    return std::any_of(shopItemList.begin(), shopItemList.end(), [&](const ShopInfo& shopInfo) {
        return shopInfo.itemId == itemId && shopInfo.buyPrice != 0 && (!itemType.isFluidContainer() || shopInfo.subType == subType);
    });
}

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

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 && 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()) {
        bool result = false;

        Item* tool = getWeapon();
        const Weapon* weapon = g_weapons->getWeapon(tool);
        uint32_t delay = getAttackSpeed();
        bool classicSpeed = g_config.getBoolean(ConfigManager::CLASSIC_ATTACK_SPEED);

        if (weapon) {
            if (!weapon->interruptSwing()) {
                result = weapon->useWeapon(this, tool, attackedCreature);
            } else if (!classicSpeed && !canDoAction()) {
                delay = getNextActionTime();
            } else {
                result = weapon->useWeapon(this, tool, attackedCreature);
            }
        } else {
            result = Weapon::useFist(this, attackedCreature);
        }

        SchedulerTask* task = createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID()));
        if (!classicSpeed) {
            setNextActionTask(task);
        } else {
            g_scheduler.addEvent(task);
        }

        if (result) {
            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(bool mode)
{
    bool prevChaseMode = chaseMode;
    chaseMode = mode;

    if (prevChaseMode != chaseMode) {
        if (chaseMode) {
            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;
}

LightInfo Player::getCreatureLight() const
{
    if (internalLight.level > itemsLight.level) {
        return internalLight;
    }
    return itemsLight;
}

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

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

            if (curLight.level > maxLight.level) {
                maxLight = std::move(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_DROWN:
            sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are drowning.");
            break;

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

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

        case CONDITION_CURSED:
            sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are cursed.");
            break;

        case CONDITION_FREEZING:
            sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are freezing.");
            break;

        case CONDITION_DAZZLED:
            sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are dazzled.");
            break;

        case CONDITION_BLEEDING:
            sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are bleeding.");
            break;

        default:
            break;
    }
}

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

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

        if (getSkull() != SKULL_RED && getSkull() != SKULL_BLACK) {
            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, bool addFightTicks /* = true */)
{
    Creature::onAttackedCreature(target);

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

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

    if (hasFlag(PlayerFlag_NotGainInFight)) {
        return;
    }

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

        targetPlayer->addInFightTicks();

        if (getSkull() == SKULL_NONE && getSkullClient(targetPlayer) == SKULL_YELLOW) {
            addAttacked(targetPlayer);
            targetPlayer->sendCreatureSkull(this);
        } else if (!targetPlayer->hasAttacked(this)) {
            if (!pzLocked) {
                pzLocked = true;
                sendIcons();
            }

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

    if (addFightTicks) {
        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);

    Player* targetPlayer = target->getPlayer();
    if (!targetPlayer) {
        return false;
    }

    if (targetPlayer->getZone() == ZONE_PVP) {
        targetPlayer->setDropLoot(false);
        targetPlayer->setSkillLoss(false);
    } else if (!hasFlag(PlayerFlag_NotGainInFight) && !isPartner(targetPlayer)) {
        if (!Combat::isInPvpZone(this, targetPlayer) && hasAttacked(targetPlayer) && !targetPlayer->hasAttacked(this) && !isGuildMate(targetPlayer) && 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), 0);
                addCondition(condition);
            }
        }
    }

    return unjustified;
}

void Player::gainExperience(uint64_t gainExp, Creature* source)
{
    if (hasFlag(PlayerFlag_NotGainExperience) || gainExp == 0 || staminaMinutes == 0) {
        return;
    }

    addExperience(source, 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, target);
        //We will get a share of the experience through the sharing mechanism
        return;
    }

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

void Player::onGainSharedExperience(uint64_t gainExp, Creature* source)
{
    gainExperience(gainExp, source);
}

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)) {
        if (manaChange > 0) {
            mana += std::min<int32_t>(manaChange, getMaxMana() - mana);
        } else {
            mana = std::max<int32_t>(0, mana + 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, uint8_t addons) const
{
    if (group->access) {
        return true;
    }

    const Outfit* outfit = Outfits::getInstance().getOutfitByLookType(sex, lookType);
    if (!outfit) {
        return false;
    }

    if (outfit->premium && !isPremium()) {
        return false;
    }

    if (outfit->unlocked && addons == 0) {
        return true;
    }

    for (const OutfitEntry& outfitEntry : outfits) {
        if (outfitEntry.lookType != lookType) {
            continue;
        }
        return (outfitEntry.addons & addons) == addons;
    }
    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::genReservedStorageRange()
{
    //generate outfits range
    uint32_t base_key = PSTRG_OUTFITS_RANGE_START;
    for (const OutfitEntry& entry : outfits) {
        storageMap[++base_key] = (entry.lookType << 16) | entry.addons;
    }
}

void Player::addOutfit(uint16_t lookType, uint8_t addons)
{
    for (OutfitEntry& outfitEntry : outfits) {
        if (outfitEntry.lookType == lookType) {
            outfitEntry.addons |= addons;
            return;
        }
    }
    outfits.emplace_back(lookType, addons);
}

bool Player::removeOutfit(uint16_t lookType)
{
    for (auto it = outfits.begin(), end = outfits.end(); it != end; ++it) {
        OutfitEntry& entry = *it;
        if (entry.lookType == lookType) {
            outfits.erase(it);
            return true;
        }
    }
    return false;
}

bool Player::removeOutfitAddon(uint16_t lookType, uint8_t addons)
{
    for (OutfitEntry& outfitEntry : outfits) {
        if (outfitEntry.lookType == lookType) {
            outfitEntry.addons &= ~addons;
            return true;
        }
    }
    return false;
}

bool Player::getOutfitAddons(const Outfit& outfit, uint8_t& addons) const
{
    if (group->access) {
        addons = 3;
        return true;
    }

    if (outfit.premium && !isPremium()) {
        return false;
    }

    for (const OutfitEntry& outfitEntry : outfits) {
        if (outfitEntry.lookType != outfit.lookType) {
            continue;
        }

        addons = outfitEntry.addons;
        return true;
    }

    if (!outfit.unlocked) {
        return false;
    }

    addons = 0;
    return true;
}

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) {
        return Creature::getSkullClient(creature);
    }

    if (isInWar(player)) {
        return SKULL_GREEN;
    }

    if (!player->getGuildWarVector().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->guid) != attackedSet.end();
}

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

    attackedSet.insert(attacked->guid);
}

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

    auto it = attackedSet.find(attacked->guid);
    if (it != attackedSet.end()) {
        attackedSet.erase(it);
    }
}

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

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

    skullTicks += g_config.getNumber(ConfigManager::FRAG_TIME);

    if (getSkull() != SKULL_BLACK) {
        if (g_config.getNumber(ConfigManager::KILLS_TO_BLACK) != 0 && skullTicks > (g_config.getNumber(ConfigManager::KILLS_TO_BLACK) - 1) * static_cast<int64_t>(g_config.getNumber(ConfigManager::FRAG_TIME))) {
            setSkull(SKULL_BLACK);
        } else if (getSkull() != SKULL_RED && g_config.getNumber(ConfigManager::KILLS_TO_RED) != 0 && skullTicks > (g_config.getNumber(ConfigManager::KILLS_TO_RED) - 1) * static_cast<int64_t>(g_config.getNumber(ConfigManager::FRAG_TIME))) {
            setSkull(SKULL_RED);
        }
    }
}

void Player::checkSkullTicks(int64_t ticks)
{
    int64_t newTicks = skullTicks - ticks;
    if (newTicks < 0) {
        skullTicks = 0;
    } else {
        skullTicks = newTicks;
    }

    if ((skull == SKULL_RED || skull == SKULL_BLACK) && skullTicks < 1 && !hasCondition(CONDITION_INFIGHT)) {
        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;
    }

    double percentReduction = 0;
    if (isPromoted()) {
        percentReduction += 30;
    }
    percentReduction += blessingCount * 8;
    return lossPercent * (1 - (percentReduction / 100.)) / 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(guildWarVector.begin(), guildWarVector.end(), guildId) != guildWarVector.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) {
            if (party->isSharedExperienceActive()) {
                if (party->isSharedExperienceEnabled()) {
                    return SHIELD_YELLOW_SHAREDEXP;
                }

                if (party->canUseSharedExperience(player)) {
                    return SHIELD_YELLOW_NOSHAREDEXP;
                }

                return SHIELD_YELLOW_NOSHAREDEXP_BLINK;
            }

            return SHIELD_YELLOW;
        }

        if (player->party == party) {
            if (party->isSharedExperienceActive()) {
                if (party->isSharedExperienceEnabled()) {
                    return SHIELD_BLUE_SHAREDEXP;
                }

                if (party->canUseSharedExperience(player)) {
                    return SHIELD_BLUE_NOSHAREDEXP;
                }

                return SHIELD_BLUE_NOSHAREDEXP_BLINK;
            }

            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 || player == this) {
        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();
}

GuildEmblems_t Player::getGuildEmblem(const Player* player) const
{
    if (!player) {
        return GUILDEMBLEM_NONE;
    }

    const Guild* playerGuild = player->getGuild();
    if (!playerGuild || player->getGuildWarVector().empty()) {
        return GUILDEMBLEM_NONE;
    }

    if (guild == playerGuild) {
        return GUILDEMBLEM_ALLY;
    } else if (isInWar(player)) {
        return GUILDEMBLEM_ENEMY;
    }

    return GUILDEMBLEM_NEUTRAL;
}

uint16_t Player::getHelpers() const
{
    uint16_t helpers;

    if (guild && party) {
        std::unordered_set<Player*> helperSet;

        const auto& guildMembers = guild->getMembersOnline();
        helperSet.insert(guildMembers.begin(), guildMembers.end());

        const auto& partyMembers = party->getMembers();
        helperSet.insert(partyMembers.begin(), partyMembers.end());

        const auto& partyInvitees = party->getInvitees();
        helperSet.insert(partyInvitees.begin(), partyInvitees.end());

        helperSet.insert(party->getLeader());

        helpers = helperSet.size();
    } else if (guild) {
        helpers = guild->getMembersOnline().size();
    } else if (party) {
        helpers = party->getMemberCount() + party->getInvitationCount() + 1;
    } else {
        helpers = 0;
    }

    return helpers;
}

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

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

 

 

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Similar Content

    • By XGaduX
      Alguém sabe como resolver esse problema? o erro é o seguinte, ao executar a magia e trocar de target o effect acompanha o novo target, meu pedido para resolução seria que o effect só pudesse ir ao novo target com o fim da spell ou que o dano acompanhasse o effect.
      local combat1 = createCombatObject()
      setCombatParam(combat1, COMBAT_PARAM_HITCOLOR, 205)
      setCombatParam(combat1, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
      setCombatParam(combat1, COMBAT_PARAM_DISTANCEEFFECT, 1)
      setCombatFormula(combat1, COMBAT_FORMULA_LEVELMAGIC, -99.0, 0, -190.5, 0)
      local function onCastSpell1(parameters)
      doCombat(parameters.cid, parameters.combat1, parameters.var)
      end
      function KiBullet(cid)
          if isCreature(cid) and isCreature(getCreatureTarget(cid)) then
              local position = {x=getThingPosition(getCreatureTarget(cid)).x+1, y=getThingPosition(getCreatureTarget(cid)).y+1, z=getThingPosition(getCreatureTarget(cid)).z}
              doSendMagicEffect(position, 329)
          end
      end
      function onCastSpell(cid, var)
      local waittime = 9 
      local storage = 99
      if exhaustion.check(cid, storage) then
         doPlayerSendCancel(cid, "You are exhausted.")
      doSendMagicEffect(getCreaturePosition(cid), 2)
          return false
      end
      local parameters = {cid = cid, var = var, combat1 = combat1}
      for i = 1, 10 do
          addEvent(function()
              if isCreature(cid) then
              addEvent(onCastSpell1, 100, parameters)
                  addEvent(KiBullet,100,cid)
              end
          end, 1 + ((i-1) * 300))
      end
      exhaustion.set(cid, storage, waittime)
      return true
      end
       

      2021-02-26 14-03-40.mp4
    • By Mikethekingsz
      Bom dia TK, queria saber se alguém pode me dar uma força? No meu servidor tem uma Spell chamada "Muted", eu quero que ela tenha porcentagem em acerta e falhar, entende?
      se alguém poder me ajudar ficarei muito grato.
       
      Script da Magia.
       
      local condition = createConditionObject(CONDITION_MUTED)
      setConditionParam(condition, CONDITION_PARAM_TICKS, 5000)
      local chance = 50
      function silence(tar, text)
          if(isCreature(tar) == true) then
              doSendAnimatedText(getCreaturePosition(tar), text, 215)
              doAddCondition(tar, condition)
          end
          return true
      end
      function onCastSpell(cid, var)
      if not isPlayer(cid) then return true end
          if exhaustion.check(cid, 13117) == TRUE then
              doPlayerSendCancel(cid, "Podera usar novamente dentro de 15 segundos.")
              doSendMagicEffect(getCreaturePosition(cid), 2)
              return false
          end
          local tar = getCreatureTarget(cid)
          if hasCondition(tar, CONDITION_MUTED) == true then
              return false
          else
              if isCreature(tar) == true then
                  if math.random(1,50) <= chance then
                      local text = "SILENCE"
                      silence(tar, text)     
                  else
                      local text = "MISS"
                      doSendAnimatedText(getCreaturePosition(tar), text, 215)
                  end
                  else
                      if isCreature(cid) == true then
                          doPlayerSendCancel(cid, "Silence can be cast only on other creatures.")
                      end
                  return false
              end
          end
          exhaustion.set(cid, 13117, 15.0)
          return true
      end
       
    • By GniusPlay
      Pessoal, preciso de uma script que o player é teleportado quando tiver x porcentagem de vida, e essa script só funciona se ele tiver em uma area especifica...(x.y)
       
    • By Pedrok22
      .Olá galera do TK !!!!
      Estou aqui novamente para pedir ajuda á vcs.
      Estou montando um Otserv 8.60 TFS 0.4.4.
      Minha dúvida é se tem ou alguem sabe me dizer se tem como colocar esse script dessa STAFF por ataque pelo ML (Magic Level)?
      Tentei fazer da seguinte forma:
      function onGetFormulaValues(cid, level, maglevel)
      min = -(maglevel*80)
      max = -(maglevel*90)
      return min, max
      end
      Porem nao tive sucesso. Mudava os ELEMENTOS da STAFF, mais não tirava DANOS...
      Eu peço humildemente ajuda de voces. 
      Está é a script da staff elemental.
      Desde já agradeço á todos voces. Ficarei no aguardo... Vlw Rapaziada !!!!
       
       
    • By XGaduX
      Qual o motivo deste tópico? 
      Como resolve esse erro?
      Está surgindo algum erro? Se sim coloque-o aqui. 
       
      Você tem o código disponível? Se tiver publique-o aqui: 
      --[[ <(Advanced Reset System 2.0)> Autor: MarcelloMkez. Contato: (marcello_dirt@hotmail.com) Versão: 2.0 Testado em: 8.50 e 8.60 TFS: 0.3.6 and 0.4.0 Fórum: http://www.xtibia.com/forum/topic/142463-advanced-reset-system-20/ -=[Características]=- ~( Versão 2.0 )~ - Resets agora Armazenados na DataBase; (Sem Valor de Storage) - Instala o System e cria a Tabela de Resets com o comando "/installreset"; - Resets no Look do jogador ex: 22:10 You see Marcello [Reset 2] (Level 8). He is an elder druid.; - [sTAGES] para "Premium Account" e "Free Account"; ]] --=[Functions]=-- -- installReset()' [instala o Sistema.] -- tableResetInstall()' [Verifica Se o Sistema ja foi instaladao.] -- nowReseting()' [Verifica, retorna o erro ou reseta.] -- getPlayerReset(cid)' [Pega numero de resets do player.] -- checLevelStageReset(cid)' [Verifica o Level para Resetar.] -- newReset(cid)' [Verifica todas as Condições de Reset.] -- addValue(value)' [Adiciona numero de resets.] --=[Comandos de Jogadores]=-- -- "/installreset" -- Só será usado uma vez, para instalar o sistema. -- "!resetar" -- Para Resetar. function onSay(cid, words, param) if words =="!resetar" then --[ Condições de Reset ] -- local coNdConf = { needPz = true, -- Precisa estar em Pz pra resetar? [true, false] needPa = false, -- Precisa ser Premium Account Pra resetar? [true, false] withe = false, -- Players com Pk Withe podem resetar? [true, false] red = false, -- Players com Pk Red pode resetar? [true, false] battle = false, -- Players precisão estar sem battle pra resetar? [true, false] teleport = true, -- Teleportar Player para o templo após resetar? [true, false] look = false, -- Aparecer Resets no Look do Player? [true, false] pid = getPlayerGUID(cid), -- Não Mexer. --[ Configurações do Reset ] -- resetConf = { Level = 350, -- Level Necessário para Resetar. [Valor] backLvl = 8, -- Level que voltará após o Reset. [Valor] time = 5, -- Tempo para o Player deslogar ao resetar, em segundos. [Valor] }, } --[[>> STAGES <<]]-- x=true;X=true -- Não Mexer. local stage = {Abilitar = {x}, Desabilitar = {}, --<< Abilitar Stages?? >>-- [{x};{}] -- [RESETS] | [PREMMY] | [FREE] stage1= {resets= 4, premmy= 330, free= 350}, stage2= {resets= 9, premmy= 340, free= 355}, -- EXPLICANDO e Configurando stages. (Se estiver Abilitado [Abilitar = {x}]) stage3= {resets= 14, premmy= 355, free= 360}, -- resets = Número de resets para o Stage; stage4= {resets= 19, premmy= 360, free= 365}, -- premmy = Level necessário para Premium Accounts; stage5= {resets= 24, premmy= 370, free= 380}, -- free = Level necessário para Free Accounts; stage6= {resets= 29, premmy= 380, free= 390}, -- Coloque em Ordem... stage7= {resets= 35, premmy= 400, free= 410}, -- [OBS: MARQUE UM "X" PARA ABILITAR OU DESABILITAR OS STAGES] stage8= {resets= 40, premmy= 410, free= 440}, -- EX: para desabilitar mude: Abilitar = {}, Desabilitar = {x} stage9= {resets= 45, premmy= 450, free= 450}, stage10={resets= 50, premmy= 465, free= 465}, } --[[>> FIM STAGES <<]]-- --=[Pega Valor de Resets]=-- function getPlayerReset(cid) local qr = db.getResult("SELECT `reset` FROM `players` WHERE `id`= "..coNdConf.pid..";") rss = qr:getDataInt("reset", coNdConf.pid) if rss < 0 then rss = 0 end return rss end local success = " ~~ Sucesso! ~~ \nVocê tem agora "..(getPlayerReset(cid)+1).." resets. \nVocê será deslogado em "..coNdConf.resetConf.time.." segundos." ;err = doPlayerSendTextMessage local qrt = db.getResult("SELECT `reset` FROM `players` WHERE `id`= "..coNdConf.pid..";");rss_db = qrt:getDataInt("reset", coNdConf.pid) local lvl_query = "UPDATE `players` SET `level` = "..(coNdConf.resetConf.backLvl)..", `experience` = 0 WHERE `id`= " .. coNdConf.pid .. ";" local reset_query = "UPDATE `players` SET `reset` = "..(getPlayerReset(cid)+(1)).." WHERE `id`= " .. coNdConf.pid .. ";" local nolook_query = "UPDATE `players` SET `description` = '' WHERE `players`.`id`= " .. coNdConf.pid .. ";" local look_query = "UPDATE `players` SET `description` = ' [Reset "..(getPlayerReset(cid)+(1)).."]' WHERE `players`.`id`= " .. coNdConf.pid .. ";" --=[Reseta]=-- function addValue(value) if coNdConf.look == false then doRemoveCreature(cid) db.executeQuery(lvl_query);db.executeQuery(reset_query);db.executeQuery(nolook_query) else doRemoveCreature(cid) db.executeQuery(lvl_query);db.executeQuery(reset_query);db.executeQuery(look_query) return LUA_NO_ERROR end end function nowReseting() if (getPlayerLevel(cid) < coNdConf.resetConf.Level) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "- Level Necessário Para o Reset ["..coNdConf.resetConf.Level.."]. Faltam "..coNdConf.resetConf.Level-getPlayerLevel(cid).." level's para você Resetar. -") return true end if getPlayerLevel(cid) >= coNdConf.resetConf.Level and (coNdConf.teleport == false) then doPlayerPopupFYI(cid, success) addEvent(addValue, coNdConf.resetConf.time*1000, value) else doPlayerPopupFYI(cid, success) addEvent(doTeleportThing, coNdConf.resetConf.time*900, cid, getTownTemplePosition(getPlayerTown(cid))) addEvent(addValue, coNdConf.resetConf.time*1000, value) return true end end --[sTAGES Nao mexer em nada.]-- function checkLevelStageReset(cid) local stages = { {resets= stage.stage1.resets, premmy= stage.stage1.premmy, free= stage.stage1.free}, {resets= stage.stage2.resets, premmy= stage.stage2.premmy, free= stage.stage2.free}, {resets= stage.stage3.resets, premmy= stage.stage3.premmy, free= stage.stage3.free}, {resets= stage.stage4.resets, premmy= stage.stage4.premmy, free= stage.stage4.free}, {resets= stage.stage5.resets, premmy= stage.stage5.premmy, free= stage.stage5.free}, {resets= stage.stage6.resets, premmy= stage.stage6.premmy, free= stage.stage6.free}, {resets= stage.stage7.resets, premmy= stage.stage7.premmy, free= stage.stage7.free}, {resets= stage.stage8.resets, premmy= stage.stage8.premmy, free= stage.stage8.free}, {resets= stage.stage9.resets, premmy= stage.stage9.premmy, free= stage.stage9.free}, {resets=stage.stage10.resets, premmy=stage.stage10.premmy, free=stage.stage10.free}, } local resets = getPlayerReset(cid) for i, tab in ipairs(stages) do if resets <= tab.resets then coNdConf.resetConf.Level = isPremium(cid) and tab.premmy or tab.free break end end if (getPlayerLevel(cid) < coNdConf.resetConf.Level) then err(cid, MESSAGE_STATUS_CONSOLE_BLUE, "- Level Necessário Para o Reset ["..coNdConf.resetConf.Level.."]. Faltam "..coNdConf.resetConf.Level-getPlayerLevel(cid).." level's para você Resetar. -") return TRUE end if getPlayerLevel(cid) >= coNdConf.resetConf.Level and (coNdConf.teleport == false) then doPlayerPopupFYI(cid, success) addEvent(addValue, coNdConf.resetConf.time*1000, value) else doPlayerPopupFYI(cid, success) addEvent(addValue, coNdConf.resetConf.time*1000, value) addEvent(doTeleportThing, coNdConf.resetConf.time*900, cid, getTownTemplePosition(getPlayerTown(cid))) return true end end function newReset(cid) if(coNdConf.needPz == true) and (getTilePzInfo(getCreaturePosition(cid)) == LUA_ERROR) then err(cid,MESSAGE_STATUS_CONSOLE_BLUE,"- Você Precisa estar em Protection Zone Para Resetar. -") return TRUE end if(coNdConf.needPa == true) and not isPremium(cid) then err(cid,MESSAGE_STATUS_CONSOLE_BLUE,"- Você Precisa ser Premium Account para Resetar. -") return TRUE end if(coNdConf.withe == false) and (getCreatureSkullType(cid) == 3) then err(cid,MESSAGE_STATUS_CONSOLE_BLUE,"- Você não pode resetar em condições de PK Withe. -") return TRUE end if(coNdConf.red == false) and (getCreatureSkullType(cid) == 4) then err(cid,MESSAGE_STATUS_CONSOLE_BLUE,"- Você não pode resetar em condições de PK Red. -") return TRUE end if(coNdConf.battle == true) and (getCreatureCondition(cid, CONDITION_INFIGHT) == TRUE) then err(cid,MESSAGE_STATUS_CONSOLE_BLUE,"- Você Precisa estar sem Battle para Resetar. -") return TRUE end local xy = {true,false} table.insert(stage.Abilitar, false) table.insert(stage.Desabilitar, false) if stage.Abilitar[1] == xy[1] and stage.Desabilitar[1] == xy[2] then checkLevelStageReset(cid) elseif stage.Abilitar[1] == xy[2] and stage.Desabilitar[1] == xy[1] then nowReseting() else doPlayerPopupFYI(cid, "LUA_ERROR; Configure corretamente o Sistema de STAGES!") end return true end function tableResetInstall() print(not rss_db and LUA_ERROR or "Tabela de Resets: Instalada ... [success] ") addEvent(newReset, 1000, cid) return false end if tableResetInstall() then end end --=[install System]=-- function installReset() if db.executeQuery("ALTER TABLE `players` ADD reset INT(11) NOT NULL DEFAULT 0;") then print("[MarcelloMkez] -= Advanced Reset System 2.0 por DataBase =- Instalado com sucesso!") return TRUE end print('[Advanced Reset System/MarcelloMkez] Não foi possível instalar o Sistema.') return FALSE end local tt = { "Preparando Instalação...", "Instalando: TableReset Db...", "Instalando: getPlayerReset()...", "Instalando: addValue()...", "Instalando: checkLevelStageReset()...", "Instalando: newReset() and nowReseting()...", "Finalizando Instalação...", "...", success = { "Iniciando...", "function: TableReset Db... [success]", "function: getPlayerReset()... [success]", "function: addValue(value)... [success]", "function: checkLevelStageReset()... [success]", "function: newReset() and nowReseting()... [success]", "Fim da Instalação. ", "by: ~~ MarcelloMkez ~~ \nContato: marcello_dirt@hotmail.com", inst = {"MarcelloMkez","Advanced Reset System 2.0 por DataBase" }, }, } if words == "/installreset" and getPlayerAccess(cid) >= 3 then function install() if installReset() then print(""..tt.success[7].."") doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE,""..tt.success[8].."") doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "["..tt.success.inst[1].."] -="..tt.success.inst[2].."=- Instalado com sucesso!") else print("["..tt.success.inst[1].."] FALHA NA INSTALAÇÃO! [O sistema ja foi instalado].") doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "["..tt.success.inst[1].."] FALHA NA INSTALAÇÃO! [O sistema ja foi instalado].") end return 1 end function concl(cid) local typetx = MESSAGE_STATUS_CONSOLE_BLUE print(""..tt.success[7].."") print(""..tt[8].."") doPlayerSendTextMessage(cid, typetx,""..tt.success[7].."") doPlayerSendTextMessage(cid, typetx,""..tt[8].."") addEvent(install, 1000,cid) end function finall(cid) local typetx = MESSAGE_STATUS_CONSOLE_BLUE print(""..tt.success[6].."") print(""..tt[7].."") doPlayerSendTextMessage(cid, typetx,""..tt.success[6].."") doPlayerSendTextMessage(cid, typetx,""..tt[7].."") addEvent(concl, 3000,cid) end function installDd(cid) local typetx = MESSAGE_STATUS_CONSOLE_BLUE print(""..tt.success[5].."") print(""..tt[6].."") doPlayerSendTextMessage(cid, typetx,""..tt.success[5].."") doPlayerSendTextMessage(cid, typetx,""..tt[6].."") addEvent(finall, 1000,cid) end function installCc(cid) local typetx = MESSAGE_STATUS_CONSOLE_BLUE print(""..tt.success[4].."") print(""..tt[5].."") doPlayerSendTextMessage(cid, typetx,""..tt.success[4].."") doPlayerSendTextMessage(cid, typetx,""..tt[5].."") addEvent(installDd, 1000,cid) end function installBb(cid) local typetx = MESSAGE_STATUS_CONSOLE_BLUE print(""..tt.success[3].."") print(""..tt[4].."") doPlayerSendTextMessage(cid, typetx,""..tt.success[3].."") doPlayerSendTextMessage(cid, typetx,""..tt[4].."") addEvent(installCc, 1000,cid) end function installAa(cid) local typetx = MESSAGE_STATUS_CONSOLE_BLUE print(""..tt.success[2].."") print(""..tt[3].."") doPlayerSendTextMessage(cid, typetx,""..tt.success[2].."") doPlayerSendTextMessage(cid, typetx,""..tt[3].."") addEvent(installBb, 1000,cid) end function toInstall() local typetx = MESSAGE_STATUS_CONSOLE_BLUE print(""..tt.success[1].."") print(""..tt[2].."") doPlayerSendTextMessage(cid, typetx,""..tt.success[1].."") doPlayerSendTextMessage(cid, typetx,""..tt[2].."") addEvent(installAa, 1000,cid) end function preparation() local typetx = MESSAGE_STATUS_CONSOLE_BLUE print(""..tt[1].."") doPlayerSendTextMessage(cid, typetx,""..tt[1].."") addEvent(toInstall, 3000,cid) end if preparation() then end end return 1 end --=[by: MarcelloMkez]=--
  • Recently Browsing   0 members

    No registered users viewing this page.


×
×
  • Create New...

Important Information

Confirmação de Termo