Ir para conteúdo
  • Cadastre-se

(Resolvido)Monstros nao atacar summon


Ir para solução Resolvido por WooX,

Posts Recomendados

.Qual servidor ou website você utiliza como base? 

TFS 0.4

 

Qual o motivo deste tópico? 

Existe algum script que o monstro não ataque o summon do player?

ajuda noix pls

 

Link para o post
Compartilhar em outros sites

Da pra fazer um script mas o correto é alterar a sources. Se mesmo sabendo disso você quiser o script avisa que faço.

 

 

 

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

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites
Agora, poko360 disse:

@WooX me ajuda plos =/

 

Vou ajudar, to ocupado pra mexer com isso agora, mas vou fazer pra você.

 

 

 

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

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

Agora to com tempo pra fazer, me envia seu combat.cpp

 

 

 

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

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

@WooX
 

Spoiler

////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include "const.h"

#include "combat.h"
#include "tools.h"

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

#include "creature.h"
#include "player.h"
#include "weapons.h"

extern Game g_game;
extern Weapons* g_weapons;
extern ConfigManager g_config;

Combat::Combat()
{
    params.valueCallback = NULL;
    params.tileCallback = NULL;
    params.targetCallback = NULL;
    area = NULL;

    formulaType = FORMULA_UNDEFINED;
    mina = minb = maxa = maxb = minl = maxl = minm = maxm = minc = maxc = 0;
}

Combat::~Combat()
{
    for(std::list<const Condition*>::iterator it = params.conditionList.begin(); it != params.conditionList.end(); ++it)
        delete (*it);

    params.conditionList.clear();
    delete area;

    delete params.valueCallback;
    delete params.tileCallback;
    delete params.targetCallback;
}

bool Combat::getMinMaxValues(Creature* creature, Creature* target, int32_t& min, int32_t& max) const
{
    if(creature)
    {
        if(creature->getCombatValues(min, max))
            return true;

        if(Player* player = creature->getPlayer())
        {
            if(params.valueCallback)
            {
                params.valueCallback->getMinMaxValues(player, min, max, params.useCharges);
                return true;
            }

            min = max = 0;
            switch(formulaType)
            {
                case FORMULA_LEVELMAGIC:
                {
                    min = (int32_t)((player->getLevel() / minl + player->getMagicLevel() * minm) * 1. * mina + minb);
                    max = (int32_t)((player->getLevel() / maxl + player->getMagicLevel() * maxm) * 1. * maxa + maxb);
                    if(minc && std::abs(min) < std::abs(minc))
                        min = minc;

                    if(maxc && std::abs(max) < std::abs(maxc))
                        max = maxc;

                    player->increaseCombatValues(min, max, params.useCharges, true);
                    return true;
                }

                case FORMULA_SKILL:
                {
                    Item* item = player->getWeapon(false);
                    if(const Weapon* weapon = g_weapons->getWeapon(item))
                    {
                        max = (int32_t)(weapon->getWeaponDamage(player, target, item, true) * maxa + maxb);
                        if(params.useCharges && item->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES))
                            g_game.transformItem(item, item->getID(), std::max((int32_t)0, ((int32_t)item->getCharges()) - 1));
                    }
                    else
                        max = (int32_t)maxb;

                    min = (int32_t)minb;
                    if(maxc && std::abs(max) < std::abs(maxc))
                        max = maxc;

                    return true;
                }

                case FORMULA_VALUE:
                {
                    min = (int32_t)minb;
                    max = (int32_t)maxb;
                    return true;
                }

                default:
                    break;
            }

            return false;
        }
    }

    if(formulaType != FORMULA_VALUE)
        return false;

    min = (int32_t)mina;
    max = (int32_t)maxa;
    return true;
}

void Combat::getCombatArea(const Position& centerPos, const Position& targetPos, const CombatArea* area, std::list<Tile*>& list)
{
    if(area)
        area->getList(centerPos, targetPos, list);
    else if(targetPos.z < MAP_MAX_LAYERS)
    {
        Tile* tile = g_game.getTile(targetPos);
        if(!tile)
        {
            tile = new StaticTile(targetPos.x, targetPos.y, targetPos.z);
            g_game.setTile(tile);
        }

        list.push_back(tile);
    }
}

CombatType_t Combat::ConditionToDamageType(ConditionType_t type)
{
    switch(type)
    {
        case CONDITION_FIRE:
            return COMBAT_FIREDAMAGE;

        case CONDITION_ENERGY:
            return COMBAT_ENERGYDAMAGE;

        case CONDITION_POISON:
            return COMBAT_EARTHDAMAGE;

        case CONDITION_FREEZING:
            return COMBAT_ICEDAMAGE;

        case CONDITION_DAZZLED:
            return COMBAT_HOLYDAMAGE;

        case CONDITION_CURSED:
            return COMBAT_DEATHDAMAGE;

        case CONDITION_DROWN:
            return COMBAT_DROWNDAMAGE;

        case CONDITION_PHYSICAL:
            return COMBAT_PHYSICALDAMAGE;

        default:
            break;
    }

    return COMBAT_NONE;
}

ConditionType_t Combat::DamageToConditionType(CombatType_t type)
{
    switch(type)
    {
        case COMBAT_FIREDAMAGE:
            return CONDITION_FIRE;

        case COMBAT_ENERGYDAMAGE:
            return CONDITION_ENERGY;

        case COMBAT_EARTHDAMAGE:
            return CONDITION_POISON;

        case COMBAT_ICEDAMAGE:
            return CONDITION_FREEZING;

        case COMBAT_HOLYDAMAGE:
            return CONDITION_DAZZLED;

        case COMBAT_DEATHDAMAGE:
            return CONDITION_CURSED;

        case COMBAT_PHYSICALDAMAGE:
            return CONDITION_PHYSICAL;

        default:
            break;
    }

    return CONDITION_NONE;
}

ReturnValue Combat::canDoCombat(const Creature* caster, const Tile* tile, bool isAggressive)
{
    if(tile->hasProperty(BLOCKPROJECTILE) || tile->floorChange() || tile->getTeleportItem())
        return RET_NOTENOUGHROOM;

    if(caster)
    {
        bool success = true;
        CreatureEventList combatAreaEvents = const_cast<Creature*>(caster)->getCreatureEvents(CREATURE_EVENT_COMBAT_AREA);
        for(CreatureEventList::iterator it = combatAreaEvents.begin(); it != combatAreaEvents.end(); ++it)
        {
            if(!(*it)->executeCombatArea(const_cast<Creature*>(caster), const_cast<Tile*>(tile), isAggressive) && success)
                success = false;
        }

        if(!success)
            return RET_NOTPOSSIBLE;

        if(caster->getPosition().z < tile->getPosition().z)
            return RET_FIRSTGODOWNSTAIRS;

        if(caster->getPosition().z > tile->getPosition().z)
            return RET_FIRSTGOUPSTAIRS;

        if(!isAggressive)
            return RET_NOERROR;

        const Player* player = caster->getPlayer();
        if(player && player->hasFlag(PlayerFlag_IgnoreProtectionZone))
            return RET_NOERROR;
    }

    return isAggressive && tile->hasFlag(TILESTATE_PROTECTIONZONE) ?
        RET_ACTIONNOTPERMITTEDINPROTECTIONZONE : RET_NOERROR;
}

ReturnValue Combat::canDoCombat(const Creature* attacker, const Creature* target)
{
    if(!attacker)
        return RET_NOERROR;

    bool success = true;
    CreatureEventList combatEvents = const_cast<Creature*>(attacker)->getCreatureEvents(CREATURE_EVENT_COMBAT);
    for(CreatureEventList::iterator it = combatEvents.begin(); it != combatEvents.end(); ++it)
    {
        if(!(*it)->executeCombat(const_cast<Creature*>(attacker), const_cast<Creature*>(target)) && success)
            success = false;
    }

    if(!success)
        return RET_NOTPOSSIBLE;

    bool checkZones = false;
    if(const Player* targetPlayer = target->getPlayer())
    {
        if(!targetPlayer->isAttackable())
            return RET_YOUMAYNOTATTACKTHISPLAYER;

        const Player* attackerPlayer = NULL;
        if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster()))
        {
            checkZones = true;
            if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !Combat::isInPvpZone(attacker, target)
                && !attackerPlayer->isEnemy(targetPlayer, true)
                ) || isProtected(const_cast<Player*>(attackerPlayer), const_cast<Player*>(targetPlayer))
                || (g_config.getBool(ConfigManager::CANNOT_ATTACK_SAME_LOOKFEET)
                && attackerPlayer->getDefaultOutfit().lookFeet == targetPlayer->getDefaultOutfit().lookFeet)
                || !attackerPlayer->canSeeCreature(targetPlayer))
                return RET_YOUMAYNOTATTACKTHISPLAYER;
        }
    }
    else if(target->getMonster())
    {
        if(!target->isAttackable())
            return RET_YOUMAYNOTATTACKTHISCREATURE;

        const Player* attackerPlayer = NULL;
        if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster()))
        {
            if(attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster))
                return RET_YOUMAYNOTATTACKTHISCREATURE;

            if(target->isPlayerSummon())
            {
                checkZones = true;
                if(g_game.getWorldType() == WORLDTYPE_OPTIONAL && !Combat::isInPvpZone(attacker, target)
                    && !attackerPlayer->isEnemy(target->getPlayerMaster(), true)
                )
                    return RET_YOUMAYNOTATTACKTHISCREATURE;
            }
        }
    }

    return checkZones && (target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) ||
        (attacker->getTile()->hasFlag(TILESTATE_OPTIONALZONE)
        && !target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) &&
        !target->getTile()->hasFlag(TILESTATE_PROTECTIONZONE))) ?
        RET_ACTIONNOTPERMITTEDINANOPVPZONE : RET_NOERROR;
}

ReturnValue Combat::canTargetCreature(const Player* player, const Creature* target)
{
    if(player == target)
        return RET_YOUMAYNOTATTACKTHISPLAYER;

    Player* tmpPlayer = const_cast<Player*>(player);
    CreatureEventList targetEvents = tmpPlayer->getCreatureEvents(CREATURE_EVENT_TARGET);

    bool deny = false;
    for(CreatureEventList::iterator it = targetEvents.begin(); it != targetEvents.end(); ++it)
    {
        if(!(*it)->executeTarget(tmpPlayer, const_cast<Creature*>(target)))
            deny = true;
    }

    if(deny)
        return RET_DONTSHOWMESSAGE;

    if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone))
    {
        if(player->getZone() == ZONE_PROTECTION)
            return RET_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE;

        if(target->getZone() == ZONE_PROTECTION)
            return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE;

        if(target->getPlayer() || target->isPlayerSummon())
        {
            if(player->getZone() == ZONE_OPTIONAL)
                return RET_ACTIONNOTPERMITTEDINANOPVPZONE;

            if(target->getZone() == ZONE_OPTIONAL)
                return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE;
        }
    }

    if(player->hasFlag(PlayerFlag_CannotUseCombat) || !target->isAttackable())
        return target->getPlayer() ? RET_YOUMAYNOTATTACKTHISPLAYER : RET_YOUMAYNOTATTACKTHISCREATURE;

    if(target->getPlayer() && !Combat::isInPvpZone(player, target) && player->getSkullType(target->getPlayer()) == SKULL_NONE)
    {
        if(player->getSecureMode() == SECUREMODE_ON)
            return RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS;

        if(player->getSkull() == SKULL_BLACK)
            return RET_YOUMAYNOTATTACKTHISPLAYER;
    }

    return Combat::canDoCombat(player, target);
}

bool Combat::isInPvpZone(const Creature* attacker, const Creature* target)
{
    return attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE;
}

bool Combat::isProtected(Player* attacker, Player* target)
{
    if(attacker->hasFlag(PlayerFlag_CannotAttackPlayer) || !target->isAttackable())
        return true;

    if(attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE && g_config.getBool(ConfigManager::PVP_TILE_IGNORE_PROTECTION))
        return false;

    if(attacker->hasCustomFlag(PlayerCustomFlag_IsProtected) || target->hasCustomFlag(PlayerCustomFlag_IsProtected))
        return true;

    uint32_t protectionLevel = g_config.getNumber(ConfigManager::PROTECTION_LEVEL);
    if(target->getLevel() < protectionLevel || attacker->getLevel() < protectionLevel)
        return true;

    if(!attacker->getVocation()->isAttackable() || !target->getVocation()->isAttackable())
        return true;

    return attacker->checkLoginDelay(target->getID());
}

void Combat::setPlayerCombatValues(formulaType_t _type, double _mina, double _minb, double _maxa, double _maxb, double _minl, double _maxl, double _minm, double _maxm, int32_t _minc, int32_t _maxc)
{
    formulaType = _type; mina = _mina; minb = _minb; maxa = _maxa; maxb = _maxb;
    minl = _minl; maxl = _maxl; minm = _minm; maxm = _maxm; minc = _minc; maxc = _maxc;
}

bool Combat::setParam(CombatParam_t param, uint32_t value)
{
    switch(param)
    {
        case COMBATPARAM_COMBATTYPE:
            params.combatType = (CombatType_t)value;
            return true;

        case COMBATPARAM_EFFECT:
            params.effects.impact = (MagicEffect_t)value;
            return true;

        case COMBATPARAM_DISTANCEEFFECT:
            params.effects.distance = (ShootEffect_t)value;
            return true;

        case COMBATPARAM_BLOCKEDBYARMOR:
            params.blockedByArmor = (value != 0);
            return true;

        case COMBATPARAM_BLOCKEDBYSHIELD:
            params.blockedByShield = (value != 0);
            return true;

        case COMBATPARAM_TARGETCASTERORTOPMOST:
            params.targetCasterOrTopMost = (value != 0);
            return true;

        case COMBATPARAM_TARGETPLAYERSORSUMMONS:
            params.targetPlayersOrSummons = (value != 0);
            return true;

        case COMBATPARAM_DIFFERENTAREADAMAGE:
            params.differentAreaDamage = (value != 0);
            return true;

        case COMBATPARAM_CREATEITEM:
            params.itemId = value;
            return true;

        case COMBATPARAM_AGGRESSIVE:
            params.isAggressive = (value != 0);
            return true;

        case COMBATPARAM_DISPEL:
            params.dispelType = (ConditionType_t)value;
            return true;

        case COMBATPARAM_USECHARGES:
            params.useCharges = (value != 0);
            return true;

        case COMBATPARAM_HITEFFECT:
            params.effects.hit = (MagicEffect_t)value;
            return true;

        case COMBATPARAM_HITCOLOR:
            params.effects.color = (Color_t)value;
            return true;

        default:
            break;
    }

    return false;
}

bool Combat::setCallback(CallBackParam_t key)
{
    switch(key)
    {
        case CALLBACKPARAM_LEVELMAGICVALUE:
        {
            delete params.valueCallback;
            params.valueCallback = new ValueCallback(FORMULA_LEVELMAGIC);
            return true;
        }

        case CALLBACKPARAM_SKILLVALUE:
        {
            delete params.valueCallback;
            params.valueCallback = new ValueCallback(FORMULA_SKILL);
            return true;
        }

        case CALLBACKPARAM_TARGETTILECALLBACK:
        {
            delete params.tileCallback;
            params.tileCallback = new TileCallback();
            break;
        }

        case CALLBACKPARAM_TARGETCREATURECALLBACK:
        {
            delete params.targetCallback;
            params.targetCallback = new TargetCallback();
            break;
        }

        default:
            std::clog << "Combat::setCallback - Unknown callback type: " << (uint32_t)key << std::endl;
            break;
    }

    return false;
}

CallBack* Combat::getCallback(CallBackParam_t key)
{
    switch(key)
    {
        case CALLBACKPARAM_LEVELMAGICVALUE:
        case CALLBACKPARAM_SKILLVALUE:
            return params.valueCallback;

        case CALLBACKPARAM_TARGETTILECALLBACK:
            return params.tileCallback;

        case CALLBACKPARAM_TARGETCREATURECALLBACK:
            return params.targetCallback;

        default:
            break;
    }

    return NULL;
}

bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, void* data)
{
    int32_t change = 0;
    if(Combat2Var* var = (Combat2Var*)data)
    {
        change = var->change;
        if(!change)
            change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);
    }

    if(g_game.combatBlockHit(params.combatType, caster, target, change, params.blockedByShield, params.blockedByArmor))
        return false;

    if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK)
        change = change / 2;

    if(!g_game.combatChangeHealth(params.combatType, caster, target, change, params.effects.hit, params.effects.color))
        return false;

    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}

bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, void* data)
{
    int32_t change = 0;
    if(Combat2Var* var = (Combat2Var*)data)
    {
        change = var->change;
        if(!change)
            change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);
    }

    if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK)
        change = change / 2;

    if(!g_game.combatChangeMana(caster, target, change))
        return false;

    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}

bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, void*)
{
    if(params.conditionList.empty())
        return false;

    bool result = true;
    for(std::list<const Condition*>::const_iterator it = params.conditionList.begin(); it != params.conditionList.end(); ++it)
    {
        if(caster != target && target->isImmune((*it)->getType()))
            continue;

        Condition* tmp = (*it)->clone();
        if(caster)
            tmp->setParam(CONDITIONPARAM_OWNER, caster->getID());

        //TODO: infight condition until all aggressive conditions has ended
        if(!target->addCombatCondition(tmp) && result)
            result = false;
    }

    return result;
}

bool Combat::CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, void*)
{
    if(!target->hasCondition(params.dispelType))
        return false;

    target->removeCondition(caster, params.dispelType);
    return true;
}

bool Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, void*)
{
    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}

void Combat::combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params)
{
    if(params.itemId)
    {
        Player* player = NULL;
        if(caster)
        {
            if(caster->getPlayer())
                player = caster->getPlayer();
            else if(caster->isPlayerSummon())
                player = caster->getPlayerMaster();
        }

        uint32_t itemId = params.itemId;
        if(player)
        {
            bool pzLock = true;
            if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !tile->hasFlag(
                TILESTATE_HARDCOREZONE)) || tile->hasFlag(TILESTATE_OPTIONALZONE))
            {
                switch(itemId)
                {
                    case ITEM_FIREFIELD:
                        itemId = ITEM_FIREFIELD_SAFE;
                        break;
                    case ITEM_POISONFIELD:
                        itemId = ITEM_POISONFIELD_SAFE;
                        break;
                    case ITEM_ENERGYFIELD:
                        itemId = ITEM_ENERGYFIELD_SAFE;
                        break;
                    case ITEM_MAGICWALL:
                        itemId = ITEM_MAGICWALL_SAFE;
                        break;
                    case ITEM_WILDGROWTH:
                        itemId = ITEM_WILDGROWTH_SAFE;
                        break;
                    default:
                        break;
                }
            }
            else if(params.isAggressive && !Item::items[itemId].blockPathFind)
                pzLock = true;

            player->addInFightTicks(pzLock);
        }

        if(Item* item = Item::CreateItem(itemId))
        {
            if(caster)
                item->setOwner(caster->getID());

            if(g_game.internalAddItem(caster, tile, item) == RET_NOERROR)
                g_game.startDecay(item);
            else
                delete item;
        }
    }

    if(params.tileCallback)
        params.tileCallback->onTileCombat(caster, tile);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(list, tile->getPosition(), params.effects.impact);
}

void Combat::postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params)
{
    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), pos, params.effects.distance);
}

void Combat::addDistanceEffect(Creature* caster, const Position& fromPos, const Position& toPos, ShootEffect_t effect)
{
    if(effect == SHOOT_EFFECT_WEAPONTYPE)
    {
        switch(caster->getWeaponType())
        {
            case WEAPON_AXE:
                effect = SHOOT_EFFECT_WHIRLWINDAXE;
                break;

            case WEAPON_SWORD:
                effect = SHOOT_EFFECT_WHIRLWINDSWORD;
                break;

            case WEAPON_CLUB:
                effect = SHOOT_EFFECT_WHIRLWINDCLUB;
                break;

            case WEAPON_FIST:
                effect = SHOOT_EFFECT_LARGEROCK;
                break;

            default:
                effect = SHOOT_EFFECT_NONE;
                break;
        }
    }

    if(caster && effect != SHOOT_EFFECT_NONE)
        g_game.addDistanceEffect(fromPos, toPos, effect);
}

void Combat::CombatFunc(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params, COMBATFUNC func, void* data)
{
    std::list<Tile*> tileList;
    if(caster)
        getCombatArea(caster->getPosition(), pos, area, tileList);
    else
        getCombatArea(pos, pos, area, tileList);

    Combat2Var* var = (Combat2Var*)data;
    if(var && !params.differentAreaDamage)
        var->change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);

    uint32_t maxX = 0, maxY = 0, diff;
    //calculate the max viewable range
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        diff = std::abs((*it)->getPosition().x - pos.x);
        if(diff > maxX)
            maxX = diff;

        diff = std::abs((*it)->getPosition().y - pos.y);
        if(diff > maxY)
            maxY = diff;
    }

    SpectatorVec list;
    g_game.getSpectators(list, pos, false, true, maxX + Map::maxViewportX, maxX + Map::maxViewportX,
        maxY + Map::maxViewportY, maxY + Map::maxViewportY);

    Tile* tile = NULL;
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        if(!(tile = (*it)) || canDoCombat(caster, (*it), params.isAggressive) != RET_NOERROR)
            continue;

        bool skip = true;
        if(CreatureVector* creatures = tile->getCreatures())
        {
            for(CreatureVector::iterator cit = creatures->begin(), cend = creatures->end(); skip && cit != cend; ++cit)
            {
                if(params.targetPlayersOrSummons && !(*cit)->getPlayer() && !(*cit)->isPlayerSummon())
                    continue;

                if(params.targetCasterOrTopMost)
                {
                    if(caster && caster->getTile() == tile)
                    {
                        if((*cit) == caster)
                            skip = false;
                    }
                    else if((*cit) == tile->getTopCreature())
                        skip = false;

                    if(skip)
                        continue;
                }

                if(!params.isAggressive || (caster != (*cit) && Combat::canDoCombat(caster, (*cit)) == RET_NOERROR))
                {
                    func(caster, (*cit), params, (void*)var);
                    if(params.targetCallback)
                        params.targetCallback->onTargetCombat(caster, (*cit));
                }
            }
        }

        combatTileEffects(list, caster, tile, params);
    }

    postCombatEffects(caster, pos, params);
}

void Combat::doCombat(Creature* caster, Creature* target) const
{
    //target combat callback function
    if(params.combatType != COMBAT_NONE)
    {
        int32_t minChange = 0, maxChange = 0;
        getMinMaxValues(caster, target, minChange, maxChange);
        if(params.combatType != COMBAT_MANADRAIN)
            doCombatHealth(caster, target, minChange, maxChange, params);
        else
            doCombatMana(caster, target, minChange, maxChange, params);
    }
    else
        doCombatDefault(caster, target, params);
}

void Combat::doCombat(Creature* caster, const Position& pos) const
{
    //area combat callback function
    if(params.combatType != COMBAT_NONE)
    {
        int32_t minChange = 0, maxChange = 0;
        getMinMaxValues(caster, NULL, minChange, maxChange);
        if(params.combatType != COMBAT_MANADRAIN)
            doCombatHealth(caster, pos, area, minChange, maxChange, params);
        else
            doCombatMana(caster, pos, area, minChange, maxChange, params);
    }
    else
        CombatFunc(caster, pos, area, params, CombatNullFunc, NULL);
}

void Combat::doCombatHealth(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;

    CombatHealthFunc(caster, target, params, (void*)&var);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatHealth(Creature* caster, const Position& pos, const CombatArea* area,
    int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;
    CombatFunc(caster, pos, area, params, CombatHealthFunc, (void*)&var);
}

void Combat::doCombatMana(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;

    CombatManaFunc(caster, target, params, (void*)&var);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatMana(Creature* caster, const Position& pos, const CombatArea* area,
    int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;
    CombatFunc(caster, pos, area, params, CombatManaFunc, (void*)&var);
}

void Combat::doCombatCondition(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params)
{
    CombatFunc(caster, pos, area, params, CombatConditionFunc, NULL);
}

void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    CombatConditionFunc(caster, target, params, NULL);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatDispel(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params)
{
    CombatFunc(caster, pos, area, params, CombatDispelFunc, NULL);
}

void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    CombatDispelFunc(caster, target, params, NULL);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatDefault(Creature* caster, Creature* target, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    const SpectatorVec& list = g_game.getSpectators(target->getTile()->getPosition());
    CombatNullFunc(caster, target, params, NULL);

    combatTileEffects(list, caster, target->getTile(), params);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

//**********************************************************

void ValueCallback::getMinMaxValues(Player* player, int32_t& min, int32_t& max, bool useCharges) const
{
    //"onGetPlayerMinMaxValues"(cid, ...)
    if(!m_interface->reserveEnv())
    {
        std::clog << "[Error - ValueCallback::getMinMaxValues] Callstack overflow." << std::endl;
        return;
    }

    ScriptEnviroment* env = m_interface->getEnv();
    if(!env->setCallbackId(m_scriptId, m_interface))
        return;

    m_interface->pushFunction(m_scriptId);
    lua_State* L = m_interface->getState();
    lua_pushnumber(L, env->addThing(player));

    int32_t parameters = 1;
    switch(type)
    {
        case FORMULA_LEVELMAGIC:
        {
            //"onGetPlayerMinMaxValues"(cid, level, magLevel)
            lua_pushnumber(L, player->getLevel());
            lua_pushnumber(L, player->getMagicLevel());

            parameters += 2;
            break;
        }

        case FORMULA_SKILL:
        {
            //"onGetPlayerMinMaxValues"(cid, level, skill, attack, factor)
            lua_pushnumber(L, player->getLevel());
            if(Item* weapon = player->getWeapon(false))
            {
                lua_pushnumber(L, player->getWeaponSkill(weapon));
                if(useCharges && weapon->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES))
                    g_game.transformItem(weapon, weapon->getID(), std::max(0, weapon->getCharges() - 1));

                lua_pushnumber(L, weapon->getAttack() + weapon->getExtraAttack());
            }
            else
            {
                lua_pushnumber(L, player->getSkill(SKILL_FIST, SKILL_LEVEL));
                lua_pushnumber(L, g_config.getNumber(ConfigManager::FIST_BASE_ATTACK));
            }

            lua_pushnumber(L, player->getAttackFactor());
            parameters += 4;
            break;
        }

        default:
        {
            std::clog << "[Warning - ValueCallback::getMinMaxValues] Unknown callback type" << std::endl;
            return;
        }
    }

    int32_t params = lua_gettop(L);
    if(!lua_pcall(L, parameters, 2, 0))
    {
        min = LuaInterface::popNumber(L);
        max = LuaInterface::popNumber(L);
        player->increaseCombatValues(min, max, useCharges, type != FORMULA_SKILL);
    }
    else
        LuaInterface::error(NULL, std::string(LuaInterface::popString(L)));

    if((lua_gettop(L) + parameters + 1) != params)
        LuaInterface::error(__FUNCTION__, "Stack size changed!");

    env->resetCallback();
    m_interface->releaseEnv();
}

//**********************************************************

void TileCallback::onTileCombat(Creature* creature, Tile* tile) const
{
    //"onTileCombat"(cid, pos)
    if(m_interface->reserveEnv())
    {
        ScriptEnviroment* env = m_interface->getEnv();
        if(!env->setCallbackId(m_scriptId, m_interface))
            return;

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

        lua_pushnumber(L, creature ? env->addThing(creature) : 0);
        m_interface->pushPosition(L, tile->getPosition(), 0);

        m_interface->callFunction(2);
        env->resetCallback();
        m_interface->releaseEnv();
    }
    else
        std::clog << "[Error - TileCallback::onTileCombat] Call stack overflow." << std::endl;
}

//**********************************************************

void TargetCallback::onTargetCombat(Creature* creature, Creature* target) const
{
    //"onTargetCombat"(cid, target)
    if(m_interface->reserveEnv())
    {
        ScriptEnviroment* env = m_interface->getEnv();
        if(!env->setCallbackId(m_scriptId, m_interface))
            return;

        uint32_t cid = 0;
        if(creature)
            cid = env->addThing(creature);

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

        lua_pushnumber(L, cid);
        lua_pushnumber(L, env->addThing(target));

        int32_t size = lua_gettop(L);
        if(lua_pcall(L, 2, 0 /*nReturnValues*/, 0) != 0)
            LuaInterface::error(NULL, std::string(LuaInterface::popString(L)));

        if((lua_gettop(L) + 2 /*nParams*/ + 1) != size)
            LuaInterface::error(__FUNCTION__, "Stack size changed!");

        env->resetCallback();
        m_interface->releaseEnv();
    }
    else
    {
        std::clog << "[Error - TargetCallback::onTargetCombat] Call stack overflow." << std::endl;
        return;
    }
}

//**********************************************************

void CombatArea::clear()
{
    for(CombatAreas::iterator it = areas.begin(); it != areas.end(); ++it)
        delete it->second;

    areas.clear();
}

CombatArea::CombatArea(const CombatArea& rhs)
{
    hasExtArea = rhs.hasExtArea;
    for(CombatAreas::const_iterator it = rhs.areas.begin(); it != rhs.areas.end(); ++it)
        areas[it->first] = new MatrixArea(*it->second);
}

bool CombatArea::getList(const Position& centerPos, const Position& targetPos, std::list<Tile*>& list) const
{
    Tile* tile = g_game.getTile(targetPos);
    const MatrixArea* area = getArea(centerPos, targetPos);
    if(!area)
        return false;

    uint16_t tmpX = targetPos.x, tmpY = targetPos.y, centerY = 0, centerX = 0;
    size_t cols = area->getCols(), rows = area->getRows();
    area->getCenter(centerY, centerX);

    tmpX -= centerX;
    tmpY -= centerY;
    for(size_t y = 0; y < rows; ++y)
    {
        for(size_t x = 0; x < cols; ++x)
        {
            if(area->getValue(y, x) != 0)
            {
                if(targetPos.z < MAP_MAX_LAYERS && g_game.isSightClear(targetPos, Position(tmpX, tmpY, targetPos.z), true))
                {
                    if(!(tile = g_game.getTile(tmpX, tmpY, targetPos.z)))
                    {
                        tile = new StaticTile(tmpX, tmpY, targetPos.z);
                        g_game.setTile(tile);
                    }

                    list.push_back(tile);
                }
            }

            tmpX++;
        }

        tmpX -= cols;
        tmpY++;
    }

    return true;
}

void CombatArea::copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const
{
    uint16_t centerY, centerX;
    input->getCenter(centerY, centerX);
    if(op == MATRIXOPERATION_COPY)
    {
        for(uint32_t y = 0; y < input->getRows(); ++y)
        {
            for(uint32_t x = 0; x < input->getCols(); ++x)
                (*output)[y][x] = (*input)[y][x];
        }

        output->setCenter(centerY, centerX);
    }
    else if(op == MATRIXOPERATION_MIRROR)
    {
        for(uint32_t y = 0; y < input->getRows(); ++y)
        {
            int32_t rx = 0;
            for(int32_t x = input->getCols() - 1; x >= 0; --x)
                (*output)[y][rx++] = (*input)[y][x];
        }

        output->setCenter(centerY, (input->getRows() - 1) - centerX);
    }
    else if(op == MATRIXOPERATION_FLIP)
    {
        for(uint32_t x = 0; x < input->getCols(); ++x)
        {
            int32_t ry = 0;
            for(int32_t y = input->getRows() - 1; y >= 0; --y)
                (*output)[ry++][x] = (*input)[y][x];
        }

        output->setCenter((input->getCols() - 1) - centerY, centerX);
    }
    else //rotation
    {
        uint16_t centerX, centerY;
        input->getCenter(centerY, centerX);

        int32_t rotateCenterX = (output->getCols() / 2) - 1, rotateCenterY = (output->getRows() / 2) - 1, angle = 0;
        switch(op)
        {
            case MATRIXOPERATION_ROTATE90:
                angle = 90;
                break;

            case MATRIXOPERATION_ROTATE180:
                angle = 180;
                break;

            case MATRIXOPERATION_ROTATE270:
                angle = 270;
                break;

            default:
                angle = 0;
                break;
        }

        double angleRad = 3.1416 * angle / 180.0;
        float a = std::cos(angleRad), b = -std::sin(angleRad);
        float c = std::sin(angleRad), d = std::cos(angleRad);

        for(int32_t x = 0; x < (long)input->getCols(); ++x)
        {
            for(int32_t y = 0; y < (long)input->getRows(); ++y)
            {
                //calculate new coordinates using rotation center
                int32_t newX = x - centerX, newY = y - centerY,
                    rotatedX = round(newX * a + newY * b),
                    rotatedY = round(newX * c + newY * d);
                //write in the output matrix using rotated coordinates
                (*output)[rotatedY + rotateCenterY][rotatedX + rotateCenterX] = (*input)[y][x];
            }
        }

        output->setCenter(rotateCenterY, rotateCenterX);
    }
}

MatrixArea* CombatArea::createArea(const std::list<uint32_t>& list, uint32_t rows)
{
    uint32_t cols = list.size() / rows;
    MatrixArea* area = new MatrixArea(rows, cols);

    uint16_t x = 0, y = 0;
    for(std::list<uint32_t>::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if(*it == 1 || *it == 3)
            area->setValue(y, x, true);

        if(*it == 2 || *it == 3)
            area->setCenter(y, x);

        ++x;
        if(cols != x)
            continue;

        x = 0;
        ++y;
    }

    return area;
}

void CombatArea::setupArea(const std::list<uint32_t>& list, uint32_t rows)
{
    //NORTH
    MatrixArea* area = createArea(list, rows);
    areas[NORTH] = area;
    uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2;

    //SOUTH
    MatrixArea* southArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, southArea, MATRIXOPERATION_ROTATE180);
    areas[SOUTH] = southArea;

    //EAST
    MatrixArea* eastArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, eastArea, MATRIXOPERATION_ROTATE90);
    areas[EAST] = eastArea;

    //WEST
    MatrixArea* westArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, westArea, MATRIXOPERATION_ROTATE270);
    areas[WEST] = westArea;
}

void CombatArea::setupArea(int32_t length, int32_t spread)
{
    std::list<uint32_t> list;
    uint32_t rows = length;

    int32_t cols = 1;
    if(spread != 0)
        cols = ((length - length % spread) / spread) * 2 + 1;

    int32_t colSpread = cols;
    for(uint32_t y = 1; y <= rows; ++y)
    {
        int32_t mincol = cols - colSpread + 1, maxcol = cols - (cols - colSpread);
        for(int32_t x = 1; x <= cols; ++x)
        {
            if(y == rows && x == ((cols - cols % 2) / 2) + 1)
                list.push_back(3);
            else if(x >= mincol && x <= maxcol)
                list.push_back(1);
            else
                list.push_back(0);
        }

        if(spread > 0 && y % spread == 0)
            --colSpread;
    }

    setupArea(list, rows);
}

void CombatArea::setupArea(int32_t radius)
{
    int32_t area[13][13] =
    {
        {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 8, 8, 7, 8, 8, 0, 0, 0, 0},
        {0, 0, 0, 8, 7, 6, 6, 6, 7, 8, 0, 0, 0},
        {0, 0, 8, 7, 6, 5, 5, 5, 6, 7, 8, 0, 0},
        {0, 8, 7, 6, 5, 4, 4, 4, 5, 6, 7, 8, 0},
        {0, 8, 6, 5, 4, 3, 2, 3, 4, 5, 6, 8, 0},
        {8, 7, 6, 5, 4, 2, 1, 2, 4, 5, 6, 7, 8},
        {0, 8, 6, 5, 4, 3, 2, 3, 4, 5, 6, 8, 0},
        {0, 8, 7, 6, 5, 4, 4, 4, 5, 6, 7, 8, 0},
        {0, 0, 8, 7, 6, 5, 5, 5, 6, 7, 8, 0, 0},
        {0, 0, 0, 8, 7, 6, 6, 6, 7, 8, 0, 0, 0},
        {0, 0, 0, 0, 8, 8, 7, 8, 8, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0}
    };

    std::list<uint32_t> list;
    for(int32_t y = 0; y < 13; ++y)
    {
        for(int32_t x = 0; x < 13; ++x)
        {
            if(area[y][x] == 1)
                list.push_back(3);
            else if(area[y][x] > 0 && area[y][x] <= radius)
                list.push_back(1);
            else
                list.push_back(0);
        }
    }

    setupArea(list, 13);
}

void CombatArea::setupExtArea(const std::list<uint32_t>& list, uint32_t rows)
{
    if(list.empty())
        return;

    //NORTH-WEST
    MatrixArea* area = createArea(list, rows);
    areas[NORTHWEST] = area;
    uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2;

    //NORTH-EAST
    MatrixArea* neArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, neArea, MATRIXOPERATION_MIRROR);
    areas[NORTHEAST] = neArea;

    //SOUTH-WEST
    MatrixArea* swArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, swArea, MATRIXOPERATION_FLIP);
    areas[SOUTHWEST] = swArea;

    //SOUTH-EAST
    MatrixArea* seArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(swArea, seArea, MATRIXOPERATION_MIRROR);
    areas[SOUTHEAST] = seArea;

    hasExtArea = true;
}

//**********************************************************

bool MagicField::isBlocking(const Creature* creature) const
{
    if(!isUnstepable())
        return Item::isBlocking(creature);

    if(!creature || !creature->getPlayer())
        return true;

    uint32_t ownerId = getOwner();
    if(!ownerId)
        return false;

    if(Creature* owner = g_game.getCreatureByID(ownerId))
        return creature->getPlayer()->getGuildEmblem(owner) != EMBLEM_NONE;

    return false;
}

void MagicField::onStepInField(Creature* creature, bool purposeful/* = true*/)
{

    if(!purposeful)
        return;

    const ItemType& it = items[id];
    if(!it.condition)
        return;

    uint32_t ownerId = getOwner();
    Condition* condition = it.condition->clone();
    if(ownerId && !getTile()->hasFlag(TILESTATE_HARDCOREZONE))
    {
        if(Creature* owner = g_game.getCreatureByID(ownerId))
        {
            bool harmful = true;
            if((g_game.getWorldType() == WORLDTYPE_OPTIONAL || getTile()->hasFlag(TILESTATE_OPTIONALZONE))
                && (owner->getPlayer() || owner->isPlayerSummon()))
                harmful = false;
            else if(Player* targetPlayer = creature->getPlayer())
            {
                if(owner->getPlayer() && Combat::isProtected(owner->getPlayer(), targetPlayer))
                    harmful = false;
            }

            if(!harmful || (OTSYS_TIME() - createTime) <= (uint32_t)g_config.getNumber(
                ConfigManager::FIELD_OWNERSHIP) || creature->hasBeenAttacked(ownerId))
                condition->setParam(CONDITIONPARAM_OWNER, ownerId);
        }
    }

    creature->addCondition(condition);
}
 

 

Link para o post
Compartilhar em outros sites

Testa ai.

 

Spoiler

////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include "const.h"

#include "combat.h"
#include "tools.h"

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

#include "creature.h"
#include "player.h"
#include "weapons.h"

extern Game g_game;
extern Weapons* g_weapons;
extern ConfigManager g_config;

Combat::Combat()
{
    params.valueCallback = NULL;
    params.tileCallback = NULL;
    params.targetCallback = NULL;
    area = NULL;

    formulaType = FORMULA_UNDEFINED;
    mina = minb = maxa = maxb = minl = maxl = minm = maxm = minc = maxc = 0;
}

Combat::~Combat()
{
    for(std::list<const Condition*>::iterator it = params.conditionList.begin(); it != params.conditionList.end(); ++it)
        delete (*it);

    params.conditionList.clear();
    delete area;

    delete params.valueCallback;
    delete params.tileCallback;
    delete params.targetCallback;
}

bool Combat::getMinMaxValues(Creature* creature, Creature* target, int32_t& min, int32_t& max) const
{
    if(creature)
    {
        if(creature->getCombatValues(min, max))
            return true;

        if(Player* player = creature->getPlayer())
        {
            if(params.valueCallback)
            {
                params.valueCallback->getMinMaxValues(player, min, max, params.useCharges);
                return true;
            }

            min = max = 0;
            switch(formulaType)
            {
                case FORMULA_LEVELMAGIC:
                {
                    min = (int32_t)((player->getLevel() / minl + player->getMagicLevel() * minm) * 1. * mina + minb);
                    max = (int32_t)((player->getLevel() / maxl + player->getMagicLevel() * maxm) * 1. * maxa + maxb);
                    if(minc && std::abs(min) < std::abs(minc))
                        min = minc;

                    if(maxc && std::abs(max) < std::abs(maxc))
                        max = maxc;

                    player->increaseCombatValues(min, max, params.useCharges, true);
                    return true;
                }

                case FORMULA_SKILL:
                {
                    Item* item = player->getWeapon(false);
                    if(const Weapon* weapon = g_weapons->getWeapon(item))
                    {
                        max = (int32_t)(weapon->getWeaponDamage(player, target, item, true) * maxa + maxb);
                        if(params.useCharges && item->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES))
                            g_game.transformItem(item, item->getID(), std::max((int32_t)0, ((int32_t)item->getCharges()) - 1));
                    }
                    else
                        max = (int32_t)maxb;

                    min = (int32_t)minb;
                    if(maxc && std::abs(max) < std::abs(maxc))
                        max = maxc;

                    return true;
                }

                case FORMULA_VALUE:
                {
                    min = (int32_t)minb;
                    max = (int32_t)maxb;
                    return true;
                }

                default:
                    break;
            }

            return false;
        }
    }

    if(formulaType != FORMULA_VALUE)
        return false;

    min = (int32_t)mina;
    max = (int32_t)maxa;
    return true;
}

void Combat::getCombatArea(const Position& centerPos, const Position& targetPos, const CombatArea* area, std::list<Tile*>& list)
{
    if(area)
        area->getList(centerPos, targetPos, list);
    else if(targetPos.z < MAP_MAX_LAYERS)
    {
        Tile* tile = g_game.getTile(targetPos);
        if(!tile)
        {
            tile = new StaticTile(targetPos.x, targetPos.y, targetPos.z);
            g_game.setTile(tile);
        }

        list.push_back(tile);
    }
}

CombatType_t Combat::ConditionToDamageType(ConditionType_t type)
{
    switch(type)
    {
        case CONDITION_FIRE:
            return COMBAT_FIREDAMAGE;

        case CONDITION_ENERGY:
            return COMBAT_ENERGYDAMAGE;

        case CONDITION_POISON:
            return COMBAT_EARTHDAMAGE;

        case CONDITION_FREEZING:
            return COMBAT_ICEDAMAGE;

        case CONDITION_DAZZLED:
            return COMBAT_HOLYDAMAGE;

        case CONDITION_CURSED:
            return COMBAT_DEATHDAMAGE;

        case CONDITION_DROWN:
            return COMBAT_DROWNDAMAGE;

        case CONDITION_PHYSICAL:
            return COMBAT_PHYSICALDAMAGE;

        default:
            break;
    }

    return COMBAT_NONE;
}

ConditionType_t Combat::DamageToConditionType(CombatType_t type)
{
    switch(type)
    {
        case COMBAT_FIREDAMAGE:
            return CONDITION_FIRE;

        case COMBAT_ENERGYDAMAGE:
            return CONDITION_ENERGY;

        case COMBAT_EARTHDAMAGE:
            return CONDITION_POISON;

        case COMBAT_ICEDAMAGE:
            return CONDITION_FREEZING;

        case COMBAT_HOLYDAMAGE:
            return CONDITION_DAZZLED;

        case COMBAT_DEATHDAMAGE:
            return CONDITION_CURSED;

        case COMBAT_PHYSICALDAMAGE:
            return CONDITION_PHYSICAL;

        default:
            break;
    }

    return CONDITION_NONE;
}

ReturnValue Combat::canDoCombat(const Creature* caster, const Tile* tile, bool isAggressive)
{
    if(tile->hasProperty(BLOCKPROJECTILE) || tile->floorChange() || tile->getTeleportItem())
        return RET_NOTENOUGHROOM;

    if(caster)
    {
        bool success = true;
        CreatureEventList combatAreaEvents = const_cast<Creature*>(caster)->getCreatureEvents(CREATURE_EVENT_COMBAT_AREA);
        for(CreatureEventList::iterator it = combatAreaEvents.begin(); it != combatAreaEvents.end(); ++it)
        {
            if(!(*it)->executeCombatArea(const_cast<Creature*>(caster), const_cast<Tile*>(tile), isAggressive) && success)
                success = false;
        }

        if(!success)
            return RET_NOTPOSSIBLE;

        if(caster->getPosition().z < tile->getPosition().z)
            return RET_FIRSTGODOWNSTAIRS;

        if(caster->getPosition().z > tile->getPosition().z)
            return RET_FIRSTGOUPSTAIRS;

        if(!isAggressive)
            return RET_NOERROR;

        const Player* player = caster->getPlayer();
        if(player && player->hasFlag(PlayerFlag_IgnoreProtectionZone))
            return RET_NOERROR;
    }

    return isAggressive && tile->hasFlag(TILESTATE_PROTECTIONZONE) ?
        RET_ACTIONNOTPERMITTEDINPROTECTIONZONE : RET_NOERROR;
}

ReturnValue Combat::canDoCombat(const Creature* attacker, const Creature* target)
{
    if(!attacker)
        return RET_NOERROR;

    bool success = true;
    CreatureEventList combatEvents = const_cast<Creature*>(attacker)->getCreatureEvents(CREATURE_EVENT_COMBAT);
    for(CreatureEventList::iterator it = combatEvents.begin(); it != combatEvents.end(); ++it)
    {
        if(!(*it)->executeCombat(const_cast<Creature*>(attacker), const_cast<Creature*>(target)) && success)
            success = false;
    }

    if(!success)
        return RET_NOTPOSSIBLE;

    bool checkZones = false;
    if(const Player* targetPlayer = target->getPlayer())
    {
        if(!targetPlayer->isAttackable())
            return RET_YOUMAYNOTATTACKTHISPLAYER;

        const Player* attackerPlayer = NULL;
        if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster()))
        {
            checkZones = true;
            if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !Combat::isInPvpZone(attacker, target)
                && !attackerPlayer->isEnemy(targetPlayer, true)
                ) || isProtected(const_cast<Player*>(attackerPlayer), const_cast<Player*>(targetPlayer))
                || (g_config.getBool(ConfigManager::CANNOT_ATTACK_SAME_LOOKFEET)
                && attackerPlayer->getDefaultOutfit().lookFeet == targetPlayer->getDefaultOutfit().lookFeet)
                || !attackerPlayer->canSeeCreature(targetPlayer))
                return RET_YOUMAYNOTATTACKTHISPLAYER;
        }
    }
    else if(target->getMonster())
    {
        if(!target->isAttackable())
            return RET_YOUMAYNOTATTACKTHISCREATURE;

        const Player* attackerPlayer = NULL;
        if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster()))
        {
            if(attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster))
                return RET_YOUMAYNOTATTACKTHISCREATURE;

            if(target->isPlayerSummon())
            {
                return RET_YOUMAYNOTATTACKTHISCREATURE;
            }
        }
    }

    return checkZones && (target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) ||
        (attacker->getTile()->hasFlag(TILESTATE_OPTIONALZONE)
        && !target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) &&
        !target->getTile()->hasFlag(TILESTATE_PROTECTIONZONE))) ?
        RET_ACTIONNOTPERMITTEDINANOPVPZONE : RET_NOERROR;
}

ReturnValue Combat::canTargetCreature(const Player* player, const Creature* target)
{
    if(player == target)
        return RET_YOUMAYNOTATTACKTHISPLAYER;

    Player* tmpPlayer = const_cast<Player*>(player);
    CreatureEventList targetEvents = tmpPlayer->getCreatureEvents(CREATURE_EVENT_TARGET);

    bool deny = false;
    for(CreatureEventList::iterator it = targetEvents.begin(); it != targetEvents.end(); ++it)
    {
        if(!(*it)->executeTarget(tmpPlayer, const_cast<Creature*>(target)))
            deny = true;
    }

    if(deny)
        return RET_DONTSHOWMESSAGE;

    if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone))
    {
        if(player->getZone() == ZONE_PROTECTION)
            return RET_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE;

        if(target->getZone() == ZONE_PROTECTION)
            return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE;

        if(target->getPlayer() || target->isPlayerSummon())
        {
            if(player->getZone() == ZONE_OPTIONAL)
                return RET_ACTIONNOTPERMITTEDINANOPVPZONE;

            if(target->getZone() == ZONE_OPTIONAL)
                return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE;
        }
    }

    if(player->hasFlag(PlayerFlag_CannotUseCombat) || !target->isAttackable() || target->isPlayerSummon())
        return target->getPlayer() ? RET_YOUMAYNOTATTACKTHISPLAYER : RET_YOUMAYNOTATTACKTHISCREATURE;

    if(target->getPlayer() && !Combat::isInPvpZone(player, target) && player->getSkullType(target->getPlayer()) == SKULL_NONE)
    {
        if(player->getSecureMode() == SECUREMODE_ON)
            return RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS;

        if(player->getSkull() == SKULL_BLACK)
            return RET_YOUMAYNOTATTACKTHISPLAYER;
    }

    return Combat::canDoCombat(player, target);
}

bool Combat::isInPvpZone(const Creature* attacker, const Creature* target)
{
    return attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE;
}

bool Combat::isProtected(Player* attacker, Player* target)
{
    if(attacker->hasFlag(PlayerFlag_CannotAttackPlayer) || !target->isAttackable())
        return true;

    if(attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE && g_config.getBool(ConfigManager::PVP_TILE_IGNORE_PROTECTION))
        return false;

    if(attacker->hasCustomFlag(PlayerCustomFlag_IsProtected) || target->hasCustomFlag(PlayerCustomFlag_IsProtected))
        return true;

    uint32_t protectionLevel = g_config.getNumber(ConfigManager::PROTECTION_LEVEL);
    if(target->getLevel() < protectionLevel || attacker->getLevel() < protectionLevel)
        return true;

    if(!attacker->getVocation()->isAttackable() || !target->getVocation()->isAttackable())
        return true;

    return attacker->checkLoginDelay(target->getID());
}

void Combat::setPlayerCombatValues(formulaType_t _type, double _mina, double _minb, double _maxa, double _maxb, double _minl, double _maxl, double _minm, double _maxm, int32_t _minc, int32_t _maxc)
{
    formulaType = _type; mina = _mina; minb = _minb; maxa = _maxa; maxb = _maxb;
    minl = _minl; maxl = _maxl; minm = _minm; maxm = _maxm; minc = _minc; maxc = _maxc;
}

bool Combat::setParam(CombatParam_t param, uint32_t value)
{
    switch(param)
    {
        case COMBATPARAM_COMBATTYPE:
            params.combatType = (CombatType_t)value;
            return true;

        case COMBATPARAM_EFFECT:
            params.effects.impact = (MagicEffect_t)value;
            return true;

        case COMBATPARAM_DISTANCEEFFECT:
            params.effects.distance = (ShootEffect_t)value;
            return true;

        case COMBATPARAM_BLOCKEDBYARMOR:
            params.blockedByArmor = (value != 0);
            return true;

        case COMBATPARAM_BLOCKEDBYSHIELD:
            params.blockedByShield = (value != 0);
            return true;

        case COMBATPARAM_TARGETCASTERORTOPMOST:
            params.targetCasterOrTopMost = (value != 0);
            return true;

        case COMBATPARAM_TARGETPLAYERSORSUMMONS:
            params.targetPlayersOrSummons = (value != 0);
            return true;

        case COMBATPARAM_DIFFERENTAREADAMAGE:
            params.differentAreaDamage = (value != 0);
            return true;

        case COMBATPARAM_CREATEITEM:
            params.itemId = value;
            return true;

        case COMBATPARAM_AGGRESSIVE:
            params.isAggressive = (value != 0);
            return true;

        case COMBATPARAM_DISPEL:
            params.dispelType = (ConditionType_t)value;
            return true;

        case COMBATPARAM_USECHARGES:
            params.useCharges = (value != 0);
            return true;

        case COMBATPARAM_HITEFFECT:
            params.effects.hit = (MagicEffect_t)value;
            return true;

        case COMBATPARAM_HITCOLOR:
            params.effects.color = (Color_t)value;
            return true;

        default:
            break;
    }

    return false;
}

bool Combat::setCallback(CallBackParam_t key)
{
    switch(key)
    {
        case CALLBACKPARAM_LEVELMAGICVALUE:
        {
            delete params.valueCallback;
            params.valueCallback = new ValueCallback(FORMULA_LEVELMAGIC);
            return true;
        }

        case CALLBACKPARAM_SKILLVALUE:
        {
            delete params.valueCallback;
            params.valueCallback = new ValueCallback(FORMULA_SKILL);
            return true;
        }

        case CALLBACKPARAM_TARGETTILECALLBACK:
        {
            delete params.tileCallback;
            params.tileCallback = new TileCallback();
            break;
        }

        case CALLBACKPARAM_TARGETCREATURECALLBACK:
        {
            delete params.targetCallback;
            params.targetCallback = new TargetCallback();
            break;
        }

        default:
            std::clog << "Combat::setCallback - Unknown callback type: " << (uint32_t)key << std::endl;
            break;
    }

    return false;
}

CallBack* Combat::getCallback(CallBackParam_t key)
{
    switch(key)
    {
        case CALLBACKPARAM_LEVELMAGICVALUE:
        case CALLBACKPARAM_SKILLVALUE:
            return params.valueCallback;

        case CALLBACKPARAM_TARGETTILECALLBACK:
            return params.tileCallback;

        case CALLBACKPARAM_TARGETCREATURECALLBACK:
            return params.targetCallback;

        default:
            break;
    }

    return NULL;
}

bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, void* data)
{
    int32_t change = 0;
    if(Combat2Var* var = (Combat2Var*)data)
    {
        change = var->change;
        if(!change)
            change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);
    }

    if(g_game.combatBlockHit(params.combatType, caster, target, change, params.blockedByShield, params.blockedByArmor))
        return false;

    if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK)
        change = change / 2;

    if(!g_game.combatChangeHealth(params.combatType, caster, target, change, params.effects.hit, params.effects.color))
        return false;

    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}

bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, void* data)
{
    int32_t change = 0;
    if(Combat2Var* var = (Combat2Var*)data)
    {
        change = var->change;
        if(!change)
            change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);
    }

    if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK)
        change = change / 2;

    if(!g_game.combatChangeMana(caster, target, change))
        return false;

    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}

bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, void*)
{
    if(params.conditionList.empty())
        return false;

    bool result = true;
    for(std::list<const Condition*>::const_iterator it = params.conditionList.begin(); it != params.conditionList.end(); ++it)
    {
        if(caster != target && target->isImmune((*it)->getType()))
            continue;

        Condition* tmp = (*it)->clone();
        if(caster)
            tmp->setParam(CONDITIONPARAM_OWNER, caster->getID());

        //TODO: infight condition until all aggressive conditions has ended
        if(!target->addCombatCondition(tmp) && result)
            result = false;
    }

    return result;
}

bool Combat::CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, void*)
{
    if(!target->hasCondition(params.dispelType))
        return false;

    target->removeCondition(caster, params.dispelType);
    return true;
}

bool Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, void*)
{
    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}

void Combat::combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params)
{
    if(params.itemId)
    {
        Player* player = NULL;
        if(caster)
        {
            if(caster->getPlayer())
                player = caster->getPlayer();
            else if(caster->isPlayerSummon())
                player = caster->getPlayerMaster();
        }

        uint32_t itemId = params.itemId;
        if(player)
        {
            bool pzLock = true;
            if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !tile->hasFlag(
                TILESTATE_HARDCOREZONE)) || tile->hasFlag(TILESTATE_OPTIONALZONE))
            {
                switch(itemId)
                {
                    case ITEM_FIREFIELD:
                        itemId = ITEM_FIREFIELD_SAFE;
                        break;
                    case ITEM_POISONFIELD:
                        itemId = ITEM_POISONFIELD_SAFE;
                        break;
                    case ITEM_ENERGYFIELD:
                        itemId = ITEM_ENERGYFIELD_SAFE;
                        break;
                    case ITEM_MAGICWALL:
                        itemId = ITEM_MAGICWALL_SAFE;
                        break;
                    case ITEM_WILDGROWTH:
                        itemId = ITEM_WILDGROWTH_SAFE;
                        break;
                    default:
                        break;
                }
            }
            else if(params.isAggressive && !Item::items[itemId].blockPathFind)
                pzLock = true;

            player->addInFightTicks(pzLock);
        }

        if(Item* item = Item::CreateItem(itemId))
        {
            if(caster)
                item->setOwner(caster->getID());

            if(g_game.internalAddItem(caster, tile, item) == RET_NOERROR)
                g_game.startDecay(item);
            else
                delete item;
        }
    }

    if(params.tileCallback)
        params.tileCallback->onTileCombat(caster, tile);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(list, tile->getPosition(), params.effects.impact);
}

void Combat::postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params)
{
    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), pos, params.effects.distance);
}

void Combat::addDistanceEffect(Creature* caster, const Position& fromPos, const Position& toPos, ShootEffect_t effect)
{
    if(effect == SHOOT_EFFECT_WEAPONTYPE)
    {
        switch(caster->getWeaponType())
        {
            case WEAPON_AXE:
                effect = SHOOT_EFFECT_WHIRLWINDAXE;
                break;

            case WEAPON_SWORD:
                effect = SHOOT_EFFECT_WHIRLWINDSWORD;
                break;

            case WEAPON_CLUB:
                effect = SHOOT_EFFECT_WHIRLWINDCLUB;
                break;

            case WEAPON_FIST:
                effect = SHOOT_EFFECT_LARGEROCK;
                break;

            default:
                effect = SHOOT_EFFECT_NONE;
                break;
        }
    }

    if(caster && effect != SHOOT_EFFECT_NONE)
        g_game.addDistanceEffect(fromPos, toPos, effect);
}

void Combat::CombatFunc(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params, COMBATFUNC func, void* data)
{
    std::list<Tile*> tileList;
    if(caster)
        getCombatArea(caster->getPosition(), pos, area, tileList);
    else
        getCombatArea(pos, pos, area, tileList);

    Combat2Var* var = (Combat2Var*)data;
    if(var && !params.differentAreaDamage)
        var->change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);

    uint32_t maxX = 0, maxY = 0, diff;
    //calculate the max viewable range
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        diff = std::abs((*it)->getPosition().x - pos.x);
        if(diff > maxX)
            maxX = diff;

        diff = std::abs((*it)->getPosition().y - pos.y);
        if(diff > maxY)
            maxY = diff;
    }

    SpectatorVec list;
    g_game.getSpectators(list, pos, false, true, maxX + Map::maxViewportX, maxX + Map::maxViewportX,
        maxY + Map::maxViewportY, maxY + Map::maxViewportY);

    Tile* tile = NULL;
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        if(!(tile = (*it)) || canDoCombat(caster, (*it), params.isAggressive) != RET_NOERROR)
            continue;

        bool skip = true;
        if(CreatureVector* creatures = tile->getCreatures())
        {
            for(CreatureVector::iterator cit = creatures->begin(), cend = creatures->end(); skip && cit != cend; ++cit)
            {
                if(params.targetPlayersOrSummons && !(*cit)->getPlayer() && !(*cit)->isPlayerSummon())
                    continue;

                if(params.targetCasterOrTopMost)
                {
                    if(caster && caster->getTile() == tile)
                    {
                        if((*cit) == caster)
                            skip = false;
                    }
                    else if((*cit) == tile->getTopCreature())
                        skip = false;

                    if(skip)
                        continue;
                }

                if(!params.isAggressive || (caster != (*cit) && Combat::canDoCombat(caster, (*cit)) == RET_NOERROR))
                {
                    func(caster, (*cit), params, (void*)var);
                    if(params.targetCallback)
                        params.targetCallback->onTargetCombat(caster, (*cit));
                }
            }
        }

        combatTileEffects(list, caster, tile, params);
    }

    postCombatEffects(caster, pos, params);
}

void Combat::doCombat(Creature* caster, Creature* target) const
{
    //target combat callback function
    if(params.combatType != COMBAT_NONE)
    {
        int32_t minChange = 0, maxChange = 0;
        getMinMaxValues(caster, target, minChange, maxChange);
        if(params.combatType != COMBAT_MANADRAIN)
            doCombatHealth(caster, target, minChange, maxChange, params);
        else
            doCombatMana(caster, target, minChange, maxChange, params);
    }
    else
        doCombatDefault(caster, target, params);
}

void Combat::doCombat(Creature* caster, const Position& pos) const
{
    //area combat callback function
    if(params.combatType != COMBAT_NONE)
    {
        int32_t minChange = 0, maxChange = 0;
        getMinMaxValues(caster, NULL, minChange, maxChange);
        if(params.combatType != COMBAT_MANADRAIN)
            doCombatHealth(caster, pos, area, minChange, maxChange, params);
        else
            doCombatMana(caster, pos, area, minChange, maxChange, params);
    }
    else
        CombatFunc(caster, pos, area, params, CombatNullFunc, NULL);
}

void Combat::doCombatHealth(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;

    CombatHealthFunc(caster, target, params, (void*)&var);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatHealth(Creature* caster, const Position& pos, const CombatArea* area,
    int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;
    CombatFunc(caster, pos, area, params, CombatHealthFunc, (void*)&var);
}

void Combat::doCombatMana(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;

    CombatManaFunc(caster, target, params, (void*)&var);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatMana(Creature* caster, const Position& pos, const CombatArea* area,
    int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;
    CombatFunc(caster, pos, area, params, CombatManaFunc, (void*)&var);
}

void Combat::doCombatCondition(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params)
{
    CombatFunc(caster, pos, area, params, CombatConditionFunc, NULL);
}

void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    CombatConditionFunc(caster, target, params, NULL);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatDispel(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params)
{
    CombatFunc(caster, pos, area, params, CombatDispelFunc, NULL);
}

void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    CombatDispelFunc(caster, target, params, NULL);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatDefault(Creature* caster, Creature* target, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    const SpectatorVec& list = g_game.getSpectators(target->getTile()->getPosition());
    CombatNullFunc(caster, target, params, NULL);

    combatTileEffects(list, caster, target->getTile(), params);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

//**********************************************************

void ValueCallback::getMinMaxValues(Player* player, int32_t& min, int32_t& max, bool useCharges) const
{
    //"onGetPlayerMinMaxValues"(cid, ...)
    if(!m_interface->reserveEnv())
    {
        std::clog << "[Error - ValueCallback::getMinMaxValues] Callstack overflow." << std::endl;
        return;
    }

    ScriptEnviroment* env = m_interface->getEnv();
    if(!env->setCallbackId(m_scriptId, m_interface))
        return;

    m_interface->pushFunction(m_scriptId);
    lua_State* L = m_interface->getState();
    lua_pushnumber(L, env->addThing(player));

    int32_t parameters = 1;
    switch(type)
    {
        case FORMULA_LEVELMAGIC:
        {
            //"onGetPlayerMinMaxValues"(cid, level, magLevel)
            lua_pushnumber(L, player->getLevel());
            lua_pushnumber(L, player->getMagicLevel());

            parameters += 2;
            break;
        }

        case FORMULA_SKILL:
        {
            //"onGetPlayerMinMaxValues"(cid, level, skill, attack, factor)
            lua_pushnumber(L, player->getLevel());
            if(Item* weapon = player->getWeapon(false))
            {
                lua_pushnumber(L, player->getWeaponSkill(weapon));
                if(useCharges && weapon->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES))
                    g_game.transformItem(weapon, weapon->getID(), std::max(0, weapon->getCharges() - 1));

                lua_pushnumber(L, weapon->getAttack() + weapon->getExtraAttack());
            }
            else
            {
                lua_pushnumber(L, player->getSkill(SKILL_FIST, SKILL_LEVEL));
                lua_pushnumber(L, g_config.getNumber(ConfigManager::FIST_BASE_ATTACK));
            }

            lua_pushnumber(L, player->getAttackFactor());
            parameters += 4;
            break;
        }

        default:
        {
            std::clog << "[Warning - ValueCallback::getMinMaxValues] Unknown callback type" << std::endl;
            return;
        }
    }

    int32_t params = lua_gettop(L);
    if(!lua_pcall(L, parameters, 2, 0))
    {
        min = LuaInterface::popNumber(L);
        max = LuaInterface::popNumber(L);
        player->increaseCombatValues(min, max, useCharges, type != FORMULA_SKILL);
    }
    else
        LuaInterface::error(NULL, std::string(LuaInterface::popString(L)));

    if((lua_gettop(L) + parameters + 1) != params)
        LuaInterface::error(__FUNCTION__, "Stack size changed!");

    env->resetCallback();
    m_interface->releaseEnv();
}

//**********************************************************

void TileCallback::onTileCombat(Creature* creature, Tile* tile) const
{
    //"onTileCombat"(cid, pos)
    if(m_interface->reserveEnv())
    {
        ScriptEnviroment* env = m_interface->getEnv();
        if(!env->setCallbackId(m_scriptId, m_interface))
            return;

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

        lua_pushnumber(L, creature ? env->addThing(creature) : 0);
        m_interface->pushPosition(L, tile->getPosition(), 0);

        m_interface->callFunction(2);
        env->resetCallback();
        m_interface->releaseEnv();
    }
    else
        std::clog << "[Error - TileCallback::onTileCombat] Call stack overflow." << std::endl;
}

//**********************************************************

void TargetCallback::onTargetCombat(Creature* creature, Creature* target) const
{
    //"onTargetCombat"(cid, target)
    if(m_interface->reserveEnv())
    {
        ScriptEnviroment* env = m_interface->getEnv();
        if(!env->setCallbackId(m_scriptId, m_interface))
            return;

        uint32_t cid = 0;
        if(creature)
            cid = env->addThing(creature);

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

        lua_pushnumber(L, cid);
        lua_pushnumber(L, env->addThing(target));

        int32_t size = lua_gettop(L);
        if(lua_pcall(L, 2, 0 /*nReturnValues*/, 0) != 0)
            LuaInterface::error(NULL, std::string(LuaInterface::popString(L)));

        if((lua_gettop(L) + 2 /*nParams*/ + 1) != size)
            LuaInterface::error(__FUNCTION__, "Stack size changed!");

        env->resetCallback();
        m_interface->releaseEnv();
    }
    else
    {
        std::clog << "[Error - TargetCallback::onTargetCombat] Call stack overflow." << std::endl;
        return;
    }
}

//**********************************************************

void CombatArea::clear()
{
    for(CombatAreas::iterator it = areas.begin(); it != areas.end(); ++it)
        delete it->second;

    areas.clear();
}

CombatArea::CombatArea(const CombatArea& rhs)
{
    hasExtArea = rhs.hasExtArea;
    for(CombatAreas::const_iterator it = rhs.areas.begin(); it != rhs.areas.end(); ++it)
        areas[it->first] = new MatrixArea(*it->second);
}

bool CombatArea::getList(const Position& centerPos, const Position& targetPos, std::list<Tile*>& list) const
{
    Tile* tile = g_game.getTile(targetPos);
    const MatrixArea* area = getArea(centerPos, targetPos);
    if(!area)
        return false;

    uint16_t tmpX = targetPos.x, tmpY = targetPos.y, centerY = 0, centerX = 0;
    size_t cols = area->getCols(), rows = area->getRows();
    area->getCenter(centerY, centerX);

    tmpX -= centerX;
    tmpY -= centerY;
    for(size_t y = 0; y < rows; ++y)
    {
        for(size_t x = 0; x < cols; ++x)
        {
            if(area->getValue(y, x) != 0)
            {
                if(targetPos.z < MAP_MAX_LAYERS && g_game.isSightClear(targetPos, Position(tmpX, tmpY, targetPos.z), true))
                {
                    if(!(tile = g_game.getTile(tmpX, tmpY, targetPos.z)))
                    {
                        tile = new StaticTile(tmpX, tmpY, targetPos.z);
                        g_game.setTile(tile);
                    }

                    list.push_back(tile);
                }
            }

            tmpX++;
        }

        tmpX -= cols;
        tmpY++;
    }

    return true;
}

void CombatArea::copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const
{
    uint16_t centerY, centerX;
    input->getCenter(centerY, centerX);
    if(op == MATRIXOPERATION_COPY)
    {
        for(uint32_t y = 0; y < input->getRows(); ++y)
        {
            for(uint32_t x = 0; x < input->getCols(); ++x)
                (*output)[y][x] = (*input)[y][x];
        }

        output->setCenter(centerY, centerX);
    }
    else if(op == MATRIXOPERATION_MIRROR)
    {
        for(uint32_t y = 0; y < input->getRows(); ++y)
        {
            int32_t rx = 0;
            for(int32_t x = input->getCols() - 1; x >= 0; --x)
                (*output)[y][rx++] = (*input)[y][x];
        }

        output->setCenter(centerY, (input->getRows() - 1) - centerX);
    }
    else if(op == MATRIXOPERATION_FLIP)
    {
        for(uint32_t x = 0; x < input->getCols(); ++x)
        {
            int32_t ry = 0;
            for(int32_t y = input->getRows() - 1; y >= 0; --y)
                (*output)[ry++][x] = (*input)[y][x];
        }

        output->setCenter((input->getCols() - 1) - centerY, centerX);
    }
    else //rotation
    {
        uint16_t centerX, centerY;
        input->getCenter(centerY, centerX);

        int32_t rotateCenterX = (output->getCols() / 2) - 1, rotateCenterY = (output->getRows() / 2) - 1, angle = 0;
        switch(op)
        {
            case MATRIXOPERATION_ROTATE90:
                angle = 90;
                break;

            case MATRIXOPERATION_ROTATE180:
                angle = 180;
                break;

            case MATRIXOPERATION_ROTATE270:
                angle = 270;
                break;

            default:
                angle = 0;
                break;
        }

        double angleRad = 3.1416 * angle / 180.0;
        float a = std::cos(angleRad), b = -std::sin(angleRad);
        float c = std::sin(angleRad), d = std::cos(angleRad);

        for(int32_t x = 0; x < (long)input->getCols(); ++x)
        {
            for(int32_t y = 0; y < (long)input->getRows(); ++y)
            {
                //calculate new coordinates using rotation center
                int32_t newX = x - centerX, newY = y - centerY,
                    rotatedX = round(newX * a + newY * b),
                    rotatedY = round(newX * c + newY * d);
                //write in the output matrix using rotated coordinates
                (*output)[rotatedY + rotateCenterY][rotatedX + rotateCenterX] = (*input)[y][x];
            }
        }

        output->setCenter(rotateCenterY, rotateCenterX);
    }
}

MatrixArea* CombatArea::createArea(const std::list<uint32_t>& list, uint32_t rows)
{
    uint32_t cols = list.size() / rows;
    MatrixArea* area = new MatrixArea(rows, cols);

    uint16_t x = 0, y = 0;
    for(std::list<uint32_t>::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if(*it == 1 || *it == 3)
            area->setValue(y, x, true);

        if(*it == 2 || *it == 3)
            area->setCenter(y, x);

        ++x;
        if(cols != x)
            continue;

        x = 0;
        ++y;
    }

    return area;
}

void CombatArea::setupArea(const std::list<uint32_t>& list, uint32_t rows)
{
    //NORTH
    MatrixArea* area = createArea(list, rows);
    areas[NORTH] = area;
    uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2;

    //SOUTH
    MatrixArea* southArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, southArea, MATRIXOPERATION_ROTATE180);
    areas[SOUTH] = southArea;

    //EAST
    MatrixArea* eastArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, eastArea, MATRIXOPERATION_ROTATE90);
    areas[EAST] = eastArea;

    //WEST
    MatrixArea* westArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, westArea, MATRIXOPERATION_ROTATE270);
    areas[WEST] = westArea;
}

void CombatArea::setupArea(int32_t length, int32_t spread)
{
    std::list<uint32_t> list;
    uint32_t rows = length;

    int32_t cols = 1;
    if(spread != 0)
        cols = ((length - length % spread) / spread) * 2 + 1;

    int32_t colSpread = cols;
    for(uint32_t y = 1; y <= rows; ++y)
    {
        int32_t mincol = cols - colSpread + 1, maxcol = cols - (cols - colSpread);
        for(int32_t x = 1; x <= cols; ++x)
        {
            if(y == rows && x == ((cols - cols % 2) / 2) + 1)
                list.push_back(3);
            else if(x >= mincol && x <= maxcol)
                list.push_back(1);
            else
                list.push_back(0);
        }

        if(spread > 0 && y % spread == 0)
            --colSpread;
    }

    setupArea(list, rows);
}

void CombatArea::setupArea(int32_t radius)
{
    int32_t area[13][13] =
    {
        {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 8, 8, 7, 8, 8, 0, 0, 0, 0},
        {0, 0, 0, 8, 7, 6, 6, 6, 7, 8, 0, 0, 0},
        {0, 0, 8, 7, 6, 5, 5, 5, 6, 7, 8, 0, 0},
        {0, 8, 7, 6, 5, 4, 4, 4, 5, 6, 7, 8, 0},
        {0, 8, 6, 5, 4, 3, 2, 3, 4, 5, 6, 8, 0},
        {8, 7, 6, 5, 4, 2, 1, 2, 4, 5, 6, 7, 8},
        {0, 8, 6, 5, 4, 3, 2, 3, 4, 5, 6, 8, 0},
        {0, 8, 7, 6, 5, 4, 4, 4, 5, 6, 7, 8, 0},
        {0, 0, 8, 7, 6, 5, 5, 5, 6, 7, 8, 0, 0},
        {0, 0, 0, 8, 7, 6, 6, 6, 7, 8, 0, 0, 0},
        {0, 0, 0, 0, 8, 8, 7, 8, 8, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0}
    };

    std::list<uint32_t> list;
    for(int32_t y = 0; y < 13; ++y)
    {
        for(int32_t x = 0; x < 13; ++x)
        {
            if(area[y][x] == 1)
                list.push_back(3);
            else if(area[y][x] > 0 && area[y][x] <= radius)
                list.push_back(1);
            else
                list.push_back(0);
        }
    }

    setupArea(list, 13);
}

void CombatArea::setupExtArea(const std::list<uint32_t>& list, uint32_t rows)
{
    if(list.empty())
        return;

    //NORTH-WEST
    MatrixArea* area = createArea(list, rows);
    areas[NORTHWEST] = area;
    uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2;

    //NORTH-EAST
    MatrixArea* neArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, neArea, MATRIXOPERATION_MIRROR);
    areas[NORTHEAST] = neArea;

    //SOUTH-WEST
    MatrixArea* swArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, swArea, MATRIXOPERATION_FLIP);
    areas[SOUTHWEST] = swArea;

    //SOUTH-EAST
    MatrixArea* seArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(swArea, seArea, MATRIXOPERATION_MIRROR);
    areas[SOUTHEAST] = seArea;

    hasExtArea = true;
}

//**********************************************************

bool MagicField::isBlocking(const Creature* creature) const
{
    if(!isUnstepable())
        return Item::isBlocking(creature);

    if(!creature || !creature->getPlayer())
        return true;

    uint32_t ownerId = getOwner();
    if(!ownerId)
        return false;

    if(Creature* owner = g_game.getCreatureByID(ownerId))
        return creature->getPlayer()->getGuildEmblem(owner) != EMBLEM_NONE;

    return false;
}

void MagicField::onStepInField(Creature* creature, bool purposeful/* = true*/)
{

    if(!purposeful)
        return;

    const ItemType& it = items[id];
    if(!it.condition)
        return;

    uint32_t ownerId = getOwner();
    Condition* condition = it.condition->clone();
    if(ownerId && !getTile()->hasFlag(TILESTATE_HARDCOREZONE))
    {
        if(Creature* owner = g_game.getCreatureByID(ownerId))
        {
            bool harmful = true;
            if((g_game.getWorldType() == WORLDTYPE_OPTIONAL || getTile()->hasFlag(TILESTATE_OPTIONALZONE))
                && (owner->getPlayer() || owner->isPlayerSummon()))
                harmful = false;
            else if(Player* targetPlayer = creature->getPlayer())
            {
                if(owner->getPlayer() && Combat::isProtected(owner->getPlayer(), targetPlayer))
                    harmful = false;
            }

            if(!harmful || (OTSYS_TIME() - createTime) <= (uint32_t)g_config.getNumber(
                ConfigManager::FIELD_OWNERSHIP) || creature->hasBeenAttacked(ownerId))
                condition->setParam(CONDITIONPARAM_OWNER, ownerId);
        }
    }

    creature->addCondition(condition);
}

 

 

 

 

 

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

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites
1 hora atrás, poko360 disse:

@WooX pegou nao =c

Eu entendi errado o que você queria, tinha entendido que era pra players não poder atacar summons. Testa agora.

 

Spoiler

////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include "const.h"

#include "combat.h"
#include "tools.h"

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

#include "creature.h"
#include "player.h"
#include "weapons.h"

extern Game g_game;
extern Weapons* g_weapons;
extern ConfigManager g_config;

Combat::Combat()
{
    params.valueCallback = NULL;
    params.tileCallback = NULL;
    params.targetCallback = NULL;
    area = NULL;

    formulaType = FORMULA_UNDEFINED;
    mina = minb = maxa = maxb = minl = maxl = minm = maxm = minc = maxc = 0;
}

Combat::~Combat()
{
    for(std::list<const Condition*>::iterator it = params.conditionList.begin(); it != params.conditionList.end(); ++it)
        delete (*it);

    params.conditionList.clear();
    delete area;

    delete params.valueCallback;
    delete params.tileCallback;
    delete params.targetCallback;
}

bool Combat::getMinMaxValues(Creature* creature, Creature* target, int32_t& min, int32_t& max) const
{
    if(creature)
    {
        if(creature->getCombatValues(min, max))
            return true;

        if(Player* player = creature->getPlayer())
        {
            if(params.valueCallback)
            {
                params.valueCallback->getMinMaxValues(player, min, max, params.useCharges);
                return true;
            }

            min = max = 0;
            switch(formulaType)
            {
                case FORMULA_LEVELMAGIC:
                {
                    min = (int32_t)((player->getLevel() / minl + player->getMagicLevel() * minm) * 1. * mina + minb);
                    max = (int32_t)((player->getLevel() / maxl + player->getMagicLevel() * maxm) * 1. * maxa + maxb);
                    if(minc && std::abs(min) < std::abs(minc))
                        min = minc;

                    if(maxc && std::abs(max) < std::abs(maxc))
                        max = maxc;

                    player->increaseCombatValues(min, max, params.useCharges, true);
                    return true;
                }

                case FORMULA_SKILL:
                {
                    Item* item = player->getWeapon(false);
                    if(const Weapon* weapon = g_weapons->getWeapon(item))
                    {
                        max = (int32_t)(weapon->getWeaponDamage(player, target, item, true) * maxa + maxb);
                        if(params.useCharges && item->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES))
                            g_game.transformItem(item, item->getID(), std::max((int32_t)0, ((int32_t)item->getCharges()) - 1));
                    }
                    else
                        max = (int32_t)maxb;

                    min = (int32_t)minb;
                    if(maxc && std::abs(max) < std::abs(maxc))
                        max = maxc;

                    return true;
                }

                case FORMULA_VALUE:
                {
                    min = (int32_t)minb;
                    max = (int32_t)maxb;
                    return true;
                }

                default:
                    break;
            }

            return false;
        }
    }

    if(formulaType != FORMULA_VALUE)
        return false;

    min = (int32_t)mina;
    max = (int32_t)maxa;
    return true;
}

void Combat::getCombatArea(const Position& centerPos, const Position& targetPos, const CombatArea* area, std::list<Tile*>& list)
{
    if(area)
        area->getList(centerPos, targetPos, list);
    else if(targetPos.z < MAP_MAX_LAYERS)
    {
        Tile* tile = g_game.getTile(targetPos);
        if(!tile)
        {
            tile = new StaticTile(targetPos.x, targetPos.y, targetPos.z);
            g_game.setTile(tile);
        }

        list.push_back(tile);
    }
}

CombatType_t Combat::ConditionToDamageType(ConditionType_t type)
{
    switch(type)
    {
        case CONDITION_FIRE:
            return COMBAT_FIREDAMAGE;

        case CONDITION_ENERGY:
            return COMBAT_ENERGYDAMAGE;

        case CONDITION_POISON:
            return COMBAT_EARTHDAMAGE;

        case CONDITION_FREEZING:
            return COMBAT_ICEDAMAGE;

        case CONDITION_DAZZLED:
            return COMBAT_HOLYDAMAGE;

        case CONDITION_CURSED:
            return COMBAT_DEATHDAMAGE;

        case CONDITION_DROWN:
            return COMBAT_DROWNDAMAGE;

        case CONDITION_PHYSICAL:
            return COMBAT_PHYSICALDAMAGE;

        default:
            break;
    }

    return COMBAT_NONE;
}

ConditionType_t Combat::DamageToConditionType(CombatType_t type)
{
    switch(type)
    {
        case COMBAT_FIREDAMAGE:
            return CONDITION_FIRE;

        case COMBAT_ENERGYDAMAGE:
            return CONDITION_ENERGY;

        case COMBAT_EARTHDAMAGE:
            return CONDITION_POISON;

        case COMBAT_ICEDAMAGE:
            return CONDITION_FREEZING;

        case COMBAT_HOLYDAMAGE:
            return CONDITION_DAZZLED;

        case COMBAT_DEATHDAMAGE:
            return CONDITION_CURSED;

        case COMBAT_PHYSICALDAMAGE:
            return CONDITION_PHYSICAL;

        default:
            break;
    }

    return CONDITION_NONE;
}

ReturnValue Combat::canDoCombat(const Creature* caster, const Tile* tile, bool isAggressive)
{
    if(tile->hasProperty(BLOCKPROJECTILE) || tile->floorChange() || tile->getTeleportItem())
        return RET_NOTENOUGHROOM;

    if(caster)
    {
        bool success = true;
        CreatureEventList combatAreaEvents = const_cast<Creature*>(caster)->getCreatureEvents(CREATURE_EVENT_COMBAT_AREA);
        for(CreatureEventList::iterator it = combatAreaEvents.begin(); it != combatAreaEvents.end(); ++it)
        {
            if(!(*it)->executeCombatArea(const_cast<Creature*>(caster), const_cast<Tile*>(tile), isAggressive) && success)
                success = false;
        }

        if(!success)
            return RET_NOTPOSSIBLE;

        if(caster->getPosition().z < tile->getPosition().z)
            return RET_FIRSTGODOWNSTAIRS;

        if(caster->getPosition().z > tile->getPosition().z)
            return RET_FIRSTGOUPSTAIRS;

        if(!isAggressive)
            return RET_NOERROR;

        const Player* player = caster->getPlayer();
        if(player && player->hasFlag(PlayerFlag_IgnoreProtectionZone))
            return RET_NOERROR;
    }

    return isAggressive && tile->hasFlag(TILESTATE_PROTECTIONZONE) ?
        RET_ACTIONNOTPERMITTEDINPROTECTIONZONE : RET_NOERROR;
}

ReturnValue Combat::canDoCombat(const Creature* attacker, const Creature* target)
{
    if(!attacker)
        return RET_NOERROR;

    bool success = true;
    CreatureEventList combatEvents = const_cast<Creature*>(attacker)->getCreatureEvents(CREATURE_EVENT_COMBAT);
    for(CreatureEventList::iterator it = combatEvents.begin(); it != combatEvents.end(); ++it)
    {
        if(!(*it)->executeCombat(const_cast<Creature*>(attacker), const_cast<Creature*>(target)) && success)
            success = false;
    }

    if(!success)
        return RET_NOTPOSSIBLE;

    bool checkZones = false;
    if(const Player* targetPlayer = target->getPlayer())
    {
        if(!targetPlayer->isAttackable())
            return RET_YOUMAYNOTATTACKTHISPLAYER;

        const Player* attackerPlayer = NULL;
        if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster()))
        {
            checkZones = true;
            if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !Combat::isInPvpZone(attacker, target)
                && !attackerPlayer->isEnemy(targetPlayer, true)
                ) || isProtected(const_cast<Player*>(attackerPlayer), const_cast<Player*>(targetPlayer))
                || (g_config.getBool(ConfigManager::CANNOT_ATTACK_SAME_LOOKFEET)
                && attackerPlayer->getDefaultOutfit().lookFeet == targetPlayer->getDefaultOutfit().lookFeet)
                || !attackerPlayer->canSeeCreature(targetPlayer))
                return RET_YOUMAYNOTATTACKTHISPLAYER;
        }
    }
    else if(target->getMonster())
    {
        if(attacker->getMonster() && target->isPlayerSummon())
            return RET_YOUMAYNOTATTACKTHISCREATURE;
		
        if(!target->isAttackable())
            return RET_YOUMAYNOTATTACKTHISCREATURE;

        const Player* attackerPlayer = NULL;
        if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster()))
        {
            if(attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster))
                return RET_YOUMAYNOTATTACKTHISCREATURE;

            if(target->isPlayerSummon())
            {
				checkZones = true;
                if(g_game.getWorldType() == WORLDTYPE_OPTIONAL && !Combat::isInPvpZone(attacker, target)
                    && !attackerPlayer->isEnemy(target->getPlayerMaster(), true))
				    return RET_YOUMAYNOTATTACKTHISCREATURE;
            }
        }
    }

    return checkZones && (target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) ||
        (attacker->getTile()->hasFlag(TILESTATE_OPTIONALZONE)
        && !target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) &&
        !target->getTile()->hasFlag(TILESTATE_PROTECTIONZONE))) ?
        RET_ACTIONNOTPERMITTEDINANOPVPZONE : RET_NOERROR;
}

ReturnValue Combat::canTargetCreature(const Player* player, const Creature* target)
{
    if(player == target)
        return RET_YOUMAYNOTATTACKTHISPLAYER;

    Player* tmpPlayer = const_cast<Player*>(player);
    CreatureEventList targetEvents = tmpPlayer->getCreatureEvents(CREATURE_EVENT_TARGET);

    bool deny = false;
    for(CreatureEventList::iterator it = targetEvents.begin(); it != targetEvents.end(); ++it)
    {
        if(!(*it)->executeTarget(tmpPlayer, const_cast<Creature*>(target)))
            deny = true;
    }

    if(deny)
        return RET_DONTSHOWMESSAGE;

    if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone))
    {
        if(player->getZone() == ZONE_PROTECTION)
            return RET_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE;

        if(target->getZone() == ZONE_PROTECTION)
            return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE;

        if(target->getPlayer() || target->isPlayerSummon())
        {
            if(player->getZone() == ZONE_OPTIONAL)
                return RET_ACTIONNOTPERMITTEDINANOPVPZONE;

            if(target->getZone() == ZONE_OPTIONAL)
                return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE;
        }
    }

    if(player->hasFlag(PlayerFlag_CannotUseCombat) || !target->isAttackable() || target->isPlayerSummon())
        return target->getPlayer() ? RET_YOUMAYNOTATTACKTHISPLAYER : RET_YOUMAYNOTATTACKTHISCREATURE;

    if(target->getPlayer() && !Combat::isInPvpZone(player, target) && player->getSkullType(target->getPlayer()) == SKULL_NONE)
    {
        if(player->getSecureMode() == SECUREMODE_ON)
            return RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS;

        if(player->getSkull() == SKULL_BLACK)
            return RET_YOUMAYNOTATTACKTHISPLAYER;
    }

    return Combat::canDoCombat(player, target);
}

bool Combat::isInPvpZone(const Creature* attacker, const Creature* target)
{
    return attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE;
}

bool Combat::isProtected(Player* attacker, Player* target)
{
    if(attacker->hasFlag(PlayerFlag_CannotAttackPlayer) || !target->isAttackable())
        return true;

    if(attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE && g_config.getBool(ConfigManager::PVP_TILE_IGNORE_PROTECTION))
        return false;

    if(attacker->hasCustomFlag(PlayerCustomFlag_IsProtected) || target->hasCustomFlag(PlayerCustomFlag_IsProtected))
        return true;

    uint32_t protectionLevel = g_config.getNumber(ConfigManager::PROTECTION_LEVEL);
    if(target->getLevel() < protectionLevel || attacker->getLevel() < protectionLevel)
        return true;

    if(!attacker->getVocation()->isAttackable() || !target->getVocation()->isAttackable())
        return true;

    return attacker->checkLoginDelay(target->getID());
}

void Combat::setPlayerCombatValues(formulaType_t _type, double _mina, double _minb, double _maxa, double _maxb, double _minl, double _maxl, double _minm, double _maxm, int32_t _minc, int32_t _maxc)
{
    formulaType = _type; mina = _mina; minb = _minb; maxa = _maxa; maxb = _maxb;
    minl = _minl; maxl = _maxl; minm = _minm; maxm = _maxm; minc = _minc; maxc = _maxc;
}

bool Combat::setParam(CombatParam_t param, uint32_t value)
{
    switch(param)
    {
        case COMBATPARAM_COMBATTYPE:
            params.combatType = (CombatType_t)value;
            return true;

        case COMBATPARAM_EFFECT:
            params.effects.impact = (MagicEffect_t)value;
            return true;

        case COMBATPARAM_DISTANCEEFFECT:
            params.effects.distance = (ShootEffect_t)value;
            return true;

        case COMBATPARAM_BLOCKEDBYARMOR:
            params.blockedByArmor = (value != 0);
            return true;

        case COMBATPARAM_BLOCKEDBYSHIELD:
            params.blockedByShield = (value != 0);
            return true;

        case COMBATPARAM_TARGETCASTERORTOPMOST:
            params.targetCasterOrTopMost = (value != 0);
            return true;

        case COMBATPARAM_TARGETPLAYERSORSUMMONS:
            params.targetPlayersOrSummons = (value != 0);
            return true;

        case COMBATPARAM_DIFFERENTAREADAMAGE:
            params.differentAreaDamage = (value != 0);
            return true;

        case COMBATPARAM_CREATEITEM:
            params.itemId = value;
            return true;

        case COMBATPARAM_AGGRESSIVE:
            params.isAggressive = (value != 0);
            return true;

        case COMBATPARAM_DISPEL:
            params.dispelType = (ConditionType_t)value;
            return true;

        case COMBATPARAM_USECHARGES:
            params.useCharges = (value != 0);
            return true;

        case COMBATPARAM_HITEFFECT:
            params.effects.hit = (MagicEffect_t)value;
            return true;

        case COMBATPARAM_HITCOLOR:
            params.effects.color = (Color_t)value;
            return true;

        default:
            break;
    }

    return false;
}

bool Combat::setCallback(CallBackParam_t key)
{
    switch(key)
    {
        case CALLBACKPARAM_LEVELMAGICVALUE:
        {
            delete params.valueCallback;
            params.valueCallback = new ValueCallback(FORMULA_LEVELMAGIC);
            return true;
        }

        case CALLBACKPARAM_SKILLVALUE:
        {
            delete params.valueCallback;
            params.valueCallback = new ValueCallback(FORMULA_SKILL);
            return true;
        }

        case CALLBACKPARAM_TARGETTILECALLBACK:
        {
            delete params.tileCallback;
            params.tileCallback = new TileCallback();
            break;
        }

        case CALLBACKPARAM_TARGETCREATURECALLBACK:
        {
            delete params.targetCallback;
            params.targetCallback = new TargetCallback();
            break;
        }

        default:
            std::clog << "Combat::setCallback - Unknown callback type: " << (uint32_t)key << std::endl;
            break;
    }

    return false;
}

CallBack* Combat::getCallback(CallBackParam_t key)
{
    switch(key)
    {
        case CALLBACKPARAM_LEVELMAGICVALUE:
        case CALLBACKPARAM_SKILLVALUE:
            return params.valueCallback;

        case CALLBACKPARAM_TARGETTILECALLBACK:
            return params.tileCallback;

        case CALLBACKPARAM_TARGETCREATURECALLBACK:
            return params.targetCallback;

        default:
            break;
    }

    return NULL;
}

bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, void* data)
{
    int32_t change = 0;
    if(Combat2Var* var = (Combat2Var*)data)
    {
        change = var->change;
        if(!change)
            change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);
    }

    if(g_game.combatBlockHit(params.combatType, caster, target, change, params.blockedByShield, params.blockedByArmor))
        return false;

    if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK)
        change = change / 2;

    if(!g_game.combatChangeHealth(params.combatType, caster, target, change, params.effects.hit, params.effects.color))
        return false;

    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}

bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, void* data)
{
    int32_t change = 0;
    if(Combat2Var* var = (Combat2Var*)data)
    {
        change = var->change;
        if(!change)
            change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);
    }

    if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK)
        change = change / 2;

    if(!g_game.combatChangeMana(caster, target, change))
        return false;

    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}

bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, void*)
{
    if(params.conditionList.empty())
        return false;

    bool result = true;
    for(std::list<const Condition*>::const_iterator it = params.conditionList.begin(); it != params.conditionList.end(); ++it)
    {
        if(caster != target && target->isImmune((*it)->getType()))
            continue;

        Condition* tmp = (*it)->clone();
        if(caster)
            tmp->setParam(CONDITIONPARAM_OWNER, caster->getID());

        //TODO: infight condition until all aggressive conditions has ended
        if(!target->addCombatCondition(tmp) && result)
            result = false;
    }

    return result;
}

bool Combat::CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, void*)
{
    if(!target->hasCondition(params.dispelType))
        return false;

    target->removeCondition(caster, params.dispelType);
    return true;
}

bool Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, void*)
{
    CombatConditionFunc(caster, target, params, NULL);
    CombatDispelFunc(caster, target, params, NULL);
    return true;
}

void Combat::combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params)
{
    if(params.itemId)
    {
        Player* player = NULL;
        if(caster)
        {
            if(caster->getPlayer())
                player = caster->getPlayer();
            else if(caster->isPlayerSummon())
                player = caster->getPlayerMaster();
        }

        uint32_t itemId = params.itemId;
        if(player)
        {
            bool pzLock = true;
            if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !tile->hasFlag(
                TILESTATE_HARDCOREZONE)) || tile->hasFlag(TILESTATE_OPTIONALZONE))
            {
                switch(itemId)
                {
                    case ITEM_FIREFIELD:
                        itemId = ITEM_FIREFIELD_SAFE;
                        break;
                    case ITEM_POISONFIELD:
                        itemId = ITEM_POISONFIELD_SAFE;
                        break;
                    case ITEM_ENERGYFIELD:
                        itemId = ITEM_ENERGYFIELD_SAFE;
                        break;
                    case ITEM_MAGICWALL:
                        itemId = ITEM_MAGICWALL_SAFE;
                        break;
                    case ITEM_WILDGROWTH:
                        itemId = ITEM_WILDGROWTH_SAFE;
                        break;
                    default:
                        break;
                }
            }
            else if(params.isAggressive && !Item::items[itemId].blockPathFind)
                pzLock = true;

            player->addInFightTicks(pzLock);
        }

        if(Item* item = Item::CreateItem(itemId))
        {
            if(caster)
                item->setOwner(caster->getID());

            if(g_game.internalAddItem(caster, tile, item) == RET_NOERROR)
                g_game.startDecay(item);
            else
                delete item;
        }
    }

    if(params.tileCallback)
        params.tileCallback->onTileCombat(caster, tile);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(list, tile->getPosition(), params.effects.impact);
}

void Combat::postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params)
{
    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), pos, params.effects.distance);
}

void Combat::addDistanceEffect(Creature* caster, const Position& fromPos, const Position& toPos, ShootEffect_t effect)
{
    if(effect == SHOOT_EFFECT_WEAPONTYPE)
    {
        switch(caster->getWeaponType())
        {
            case WEAPON_AXE:
                effect = SHOOT_EFFECT_WHIRLWINDAXE;
                break;

            case WEAPON_SWORD:
                effect = SHOOT_EFFECT_WHIRLWINDSWORD;
                break;

            case WEAPON_CLUB:
                effect = SHOOT_EFFECT_WHIRLWINDCLUB;
                break;

            case WEAPON_FIST:
                effect = SHOOT_EFFECT_LARGEROCK;
                break;

            default:
                effect = SHOOT_EFFECT_NONE;
                break;
        }
    }

    if(caster && effect != SHOOT_EFFECT_NONE)
        g_game.addDistanceEffect(fromPos, toPos, effect);
}

void Combat::CombatFunc(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params, COMBATFUNC func, void* data)
{
    std::list<Tile*> tileList;
    if(caster)
        getCombatArea(caster->getPosition(), pos, area, tileList);
    else
        getCombatArea(pos, pos, area, tileList);

    Combat2Var* var = (Combat2Var*)data;
    if(var && !params.differentAreaDamage)
        var->change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL);

    uint32_t maxX = 0, maxY = 0, diff;
    //calculate the max viewable range
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        diff = std::abs((*it)->getPosition().x - pos.x);
        if(diff > maxX)
            maxX = diff;

        diff = std::abs((*it)->getPosition().y - pos.y);
        if(diff > maxY)
            maxY = diff;
    }

    SpectatorVec list;
    g_game.getSpectators(list, pos, false, true, maxX + Map::maxViewportX, maxX + Map::maxViewportX,
        maxY + Map::maxViewportY, maxY + Map::maxViewportY);

    Tile* tile = NULL;
    for(std::list<Tile*>::iterator it = tileList.begin(); it != tileList.end(); ++it)
    {
        if(!(tile = (*it)) || canDoCombat(caster, (*it), params.isAggressive) != RET_NOERROR)
            continue;

        bool skip = true;
        if(CreatureVector* creatures = tile->getCreatures())
        {
            for(CreatureVector::iterator cit = creatures->begin(), cend = creatures->end(); skip && cit != cend; ++cit)
            {
                if(params.targetPlayersOrSummons && !(*cit)->getPlayer() && !(*cit)->isPlayerSummon())
                    continue;

                if(params.targetCasterOrTopMost)
                {
                    if(caster && caster->getTile() == tile)
                    {
                        if((*cit) == caster)
                            skip = false;
                    }
                    else if((*cit) == tile->getTopCreature())
                        skip = false;

                    if(skip)
                        continue;
                }

                if(!params.isAggressive || (caster != (*cit) && Combat::canDoCombat(caster, (*cit)) == RET_NOERROR))
                {
                    func(caster, (*cit), params, (void*)var);
                    if(params.targetCallback)
                        params.targetCallback->onTargetCombat(caster, (*cit));
                }
            }
        }

        combatTileEffects(list, caster, tile, params);
    }

    postCombatEffects(caster, pos, params);
}

void Combat::doCombat(Creature* caster, Creature* target) const
{
    //target combat callback function
    if(params.combatType != COMBAT_NONE)
    {
        int32_t minChange = 0, maxChange = 0;
        getMinMaxValues(caster, target, minChange, maxChange);
        if(params.combatType != COMBAT_MANADRAIN)
            doCombatHealth(caster, target, minChange, maxChange, params);
        else
            doCombatMana(caster, target, minChange, maxChange, params);
    }
    else
        doCombatDefault(caster, target, params);
}

void Combat::doCombat(Creature* caster, const Position& pos) const
{
    //area combat callback function
    if(params.combatType != COMBAT_NONE)
    {
        int32_t minChange = 0, maxChange = 0;
        getMinMaxValues(caster, NULL, minChange, maxChange);
        if(params.combatType != COMBAT_MANADRAIN)
            doCombatHealth(caster, pos, area, minChange, maxChange, params);
        else
            doCombatMana(caster, pos, area, minChange, maxChange, params);
    }
    else
        CombatFunc(caster, pos, area, params, CombatNullFunc, NULL);
}

void Combat::doCombatHealth(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;

    CombatHealthFunc(caster, target, params, (void*)&var);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatHealth(Creature* caster, const Position& pos, const CombatArea* area,
    int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;
    CombatFunc(caster, pos, area, params, CombatHealthFunc, (void*)&var);
}

void Combat::doCombatMana(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;

    CombatManaFunc(caster, target, params, (void*)&var);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatMana(Creature* caster, const Position& pos, const CombatArea* area,
    int32_t minChange, int32_t maxChange, const CombatParams& params)
{
    Combat2Var var;
    var.minChange = minChange;
    var.maxChange = maxChange;
    CombatFunc(caster, pos, area, params, CombatManaFunc, (void*)&var);
}

void Combat::doCombatCondition(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params)
{
    CombatFunc(caster, pos, area, params, CombatConditionFunc, NULL);
}

void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    CombatConditionFunc(caster, target, params, NULL);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatDispel(Creature* caster, const Position& pos, const CombatArea* area,
    const CombatParams& params)
{
    CombatFunc(caster, pos, area, params, CombatDispelFunc, NULL);
}

void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    CombatDispelFunc(caster, target, params, NULL);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

void Combat::doCombatDefault(Creature* caster, Creature* target, const CombatParams& params)
{
    if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR))
        return;

    const SpectatorVec& list = g_game.getSpectators(target->getTile()->getPosition());
    CombatNullFunc(caster, target, params, NULL);

    combatTileEffects(list, caster, target->getTile(), params);
    if(params.targetCallback)
        params.targetCallback->onTargetCombat(caster, target);

    if(params.effects.impact != MAGIC_EFFECT_NONE && (!caster || !caster->isGhost()
        || g_config.getBool(ConfigManager::GHOST_SPELL_EFFECTS)))
        g_game.addMagicEffect(target->getPosition(), params.effects.impact);

    if(caster && params.effects.distance != SHOOT_EFFECT_NONE)
        addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.effects.distance);
}

//**********************************************************

void ValueCallback::getMinMaxValues(Player* player, int32_t& min, int32_t& max, bool useCharges) const
{
    //"onGetPlayerMinMaxValues"(cid, ...)
    if(!m_interface->reserveEnv())
    {
        std::clog << "[Error - ValueCallback::getMinMaxValues] Callstack overflow." << std::endl;
        return;
    }

    ScriptEnviroment* env = m_interface->getEnv();
    if(!env->setCallbackId(m_scriptId, m_interface))
        return;

    m_interface->pushFunction(m_scriptId);
    lua_State* L = m_interface->getState();
    lua_pushnumber(L, env->addThing(player));

    int32_t parameters = 1;
    switch(type)
    {
        case FORMULA_LEVELMAGIC:
        {
            //"onGetPlayerMinMaxValues"(cid, level, magLevel)
            lua_pushnumber(L, player->getLevel());
            lua_pushnumber(L, player->getMagicLevel());

            parameters += 2;
            break;
        }

        case FORMULA_SKILL:
        {
            //"onGetPlayerMinMaxValues"(cid, level, skill, attack, factor)
            lua_pushnumber(L, player->getLevel());
            if(Item* weapon = player->getWeapon(false))
            {
                lua_pushnumber(L, player->getWeaponSkill(weapon));
                if(useCharges && weapon->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES))
                    g_game.transformItem(weapon, weapon->getID(), std::max(0, weapon->getCharges() - 1));

                lua_pushnumber(L, weapon->getAttack() + weapon->getExtraAttack());
            }
            else
            {
                lua_pushnumber(L, player->getSkill(SKILL_FIST, SKILL_LEVEL));
                lua_pushnumber(L, g_config.getNumber(ConfigManager::FIST_BASE_ATTACK));
            }

            lua_pushnumber(L, player->getAttackFactor());
            parameters += 4;
            break;
        }

        default:
        {
            std::clog << "[Warning - ValueCallback::getMinMaxValues] Unknown callback type" << std::endl;
            return;
        }
    }

    int32_t params = lua_gettop(L);
    if(!lua_pcall(L, parameters, 2, 0))
    {
        min = LuaInterface::popNumber(L);
        max = LuaInterface::popNumber(L);
        player->increaseCombatValues(min, max, useCharges, type != FORMULA_SKILL);
    }
    else
        LuaInterface::error(NULL, std::string(LuaInterface::popString(L)));

    if((lua_gettop(L) + parameters + 1) != params)
        LuaInterface::error(__FUNCTION__, "Stack size changed!");

    env->resetCallback();
    m_interface->releaseEnv();
}

//**********************************************************

void TileCallback::onTileCombat(Creature* creature, Tile* tile) const
{
    //"onTileCombat"(cid, pos)
    if(m_interface->reserveEnv())
    {
        ScriptEnviroment* env = m_interface->getEnv();
        if(!env->setCallbackId(m_scriptId, m_interface))
            return;

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

        lua_pushnumber(L, creature ? env->addThing(creature) : 0);
        m_interface->pushPosition(L, tile->getPosition(), 0);

        m_interface->callFunction(2);
        env->resetCallback();
        m_interface->releaseEnv();
    }
    else
        std::clog << "[Error - TileCallback::onTileCombat] Call stack overflow." << std::endl;
}

//**********************************************************

void TargetCallback::onTargetCombat(Creature* creature, Creature* target) const
{
    //"onTargetCombat"(cid, target)
    if(m_interface->reserveEnv())
    {
        ScriptEnviroment* env = m_interface->getEnv();
        if(!env->setCallbackId(m_scriptId, m_interface))
            return;

        uint32_t cid = 0;
        if(creature)
            cid = env->addThing(creature);

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

        lua_pushnumber(L, cid);
        lua_pushnumber(L, env->addThing(target));

        int32_t size = lua_gettop(L);
        if(lua_pcall(L, 2, 0 /*nReturnValues*/, 0) != 0)
            LuaInterface::error(NULL, std::string(LuaInterface::popString(L)));

        if((lua_gettop(L) + 2 /*nParams*/ + 1) != size)
            LuaInterface::error(__FUNCTION__, "Stack size changed!");

        env->resetCallback();
        m_interface->releaseEnv();
    }
    else
    {
        std::clog << "[Error - TargetCallback::onTargetCombat] Call stack overflow." << std::endl;
        return;
    }
}

//**********************************************************

void CombatArea::clear()
{
    for(CombatAreas::iterator it = areas.begin(); it != areas.end(); ++it)
        delete it->second;

    areas.clear();
}

CombatArea::CombatArea(const CombatArea& rhs)
{
    hasExtArea = rhs.hasExtArea;
    for(CombatAreas::const_iterator it = rhs.areas.begin(); it != rhs.areas.end(); ++it)
        areas[it->first] = new MatrixArea(*it->second);
}

bool CombatArea::getList(const Position& centerPos, const Position& targetPos, std::list<Tile*>& list) const
{
    Tile* tile = g_game.getTile(targetPos);
    const MatrixArea* area = getArea(centerPos, targetPos);
    if(!area)
        return false;

    uint16_t tmpX = targetPos.x, tmpY = targetPos.y, centerY = 0, centerX = 0;
    size_t cols = area->getCols(), rows = area->getRows();
    area->getCenter(centerY, centerX);

    tmpX -= centerX;
    tmpY -= centerY;
    for(size_t y = 0; y < rows; ++y)
    {
        for(size_t x = 0; x < cols; ++x)
        {
            if(area->getValue(y, x) != 0)
            {
                if(targetPos.z < MAP_MAX_LAYERS && g_game.isSightClear(targetPos, Position(tmpX, tmpY, targetPos.z), true))
                {
                    if(!(tile = g_game.getTile(tmpX, tmpY, targetPos.z)))
                    {
                        tile = new StaticTile(tmpX, tmpY, targetPos.z);
                        g_game.setTile(tile);
                    }

                    list.push_back(tile);
                }
            }

            tmpX++;
        }

        tmpX -= cols;
        tmpY++;
    }

    return true;
}

void CombatArea::copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const
{
    uint16_t centerY, centerX;
    input->getCenter(centerY, centerX);
    if(op == MATRIXOPERATION_COPY)
    {
        for(uint32_t y = 0; y < input->getRows(); ++y)
        {
            for(uint32_t x = 0; x < input->getCols(); ++x)
                (*output)[y][x] = (*input)[y][x];
        }

        output->setCenter(centerY, centerX);
    }
    else if(op == MATRIXOPERATION_MIRROR)
    {
        for(uint32_t y = 0; y < input->getRows(); ++y)
        {
            int32_t rx = 0;
            for(int32_t x = input->getCols() - 1; x >= 0; --x)
                (*output)[y][rx++] = (*input)[y][x];
        }

        output->setCenter(centerY, (input->getRows() - 1) - centerX);
    }
    else if(op == MATRIXOPERATION_FLIP)
    {
        for(uint32_t x = 0; x < input->getCols(); ++x)
        {
            int32_t ry = 0;
            for(int32_t y = input->getRows() - 1; y >= 0; --y)
                (*output)[ry++][x] = (*input)[y][x];
        }

        output->setCenter((input->getCols() - 1) - centerY, centerX);
    }
    else //rotation
    {
        uint16_t centerX, centerY;
        input->getCenter(centerY, centerX);

        int32_t rotateCenterX = (output->getCols() / 2) - 1, rotateCenterY = (output->getRows() / 2) - 1, angle = 0;
        switch(op)
        {
            case MATRIXOPERATION_ROTATE90:
                angle = 90;
                break;

            case MATRIXOPERATION_ROTATE180:
                angle = 180;
                break;

            case MATRIXOPERATION_ROTATE270:
                angle = 270;
                break;

            default:
                angle = 0;
                break;
        }

        double angleRad = 3.1416 * angle / 180.0;
        float a = std::cos(angleRad), b = -std::sin(angleRad);
        float c = std::sin(angleRad), d = std::cos(angleRad);

        for(int32_t x = 0; x < (long)input->getCols(); ++x)
        {
            for(int32_t y = 0; y < (long)input->getRows(); ++y)
            {
                //calculate new coordinates using rotation center
                int32_t newX = x - centerX, newY = y - centerY,
                    rotatedX = round(newX * a + newY * b),
                    rotatedY = round(newX * c + newY * d);
                //write in the output matrix using rotated coordinates
                (*output)[rotatedY + rotateCenterY][rotatedX + rotateCenterX] = (*input)[y][x];
            }
        }

        output->setCenter(rotateCenterY, rotateCenterX);
    }
}

MatrixArea* CombatArea::createArea(const std::list<uint32_t>& list, uint32_t rows)
{
    uint32_t cols = list.size() / rows;
    MatrixArea* area = new MatrixArea(rows, cols);

    uint16_t x = 0, y = 0;
    for(std::list<uint32_t>::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if(*it == 1 || *it == 3)
            area->setValue(y, x, true);

        if(*it == 2 || *it == 3)
            area->setCenter(y, x);

        ++x;
        if(cols != x)
            continue;

        x = 0;
        ++y;
    }

    return area;
}

void CombatArea::setupArea(const std::list<uint32_t>& list, uint32_t rows)
{
    //NORTH
    MatrixArea* area = createArea(list, rows);
    areas[NORTH] = area;
    uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2;

    //SOUTH
    MatrixArea* southArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, southArea, MATRIXOPERATION_ROTATE180);
    areas[SOUTH] = southArea;

    //EAST
    MatrixArea* eastArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, eastArea, MATRIXOPERATION_ROTATE90);
    areas[EAST] = eastArea;

    //WEST
    MatrixArea* westArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, westArea, MATRIXOPERATION_ROTATE270);
    areas[WEST] = westArea;
}

void CombatArea::setupArea(int32_t length, int32_t spread)
{
    std::list<uint32_t> list;
    uint32_t rows = length;

    int32_t cols = 1;
    if(spread != 0)
        cols = ((length - length % spread) / spread) * 2 + 1;

    int32_t colSpread = cols;
    for(uint32_t y = 1; y <= rows; ++y)
    {
        int32_t mincol = cols - colSpread + 1, maxcol = cols - (cols - colSpread);
        for(int32_t x = 1; x <= cols; ++x)
        {
            if(y == rows && x == ((cols - cols % 2) / 2) + 1)
                list.push_back(3);
            else if(x >= mincol && x <= maxcol)
                list.push_back(1);
            else
                list.push_back(0);
        }

        if(spread > 0 && y % spread == 0)
            --colSpread;
    }

    setupArea(list, rows);
}

void CombatArea::setupArea(int32_t radius)
{
    int32_t area[13][13] =
    {
        {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0},
        {0, 0, 0, 0, 8, 8, 7, 8, 8, 0, 0, 0, 0},
        {0, 0, 0, 8, 7, 6, 6, 6, 7, 8, 0, 0, 0},
        {0, 0, 8, 7, 6, 5, 5, 5, 6, 7, 8, 0, 0},
        {0, 8, 7, 6, 5, 4, 4, 4, 5, 6, 7, 8, 0},
        {0, 8, 6, 5, 4, 3, 2, 3, 4, 5, 6, 8, 0},
        {8, 7, 6, 5, 4, 2, 1, 2, 4, 5, 6, 7, 8},
        {0, 8, 6, 5, 4, 3, 2, 3, 4, 5, 6, 8, 0},
        {0, 8, 7, 6, 5, 4, 4, 4, 5, 6, 7, 8, 0},
        {0, 0, 8, 7, 6, 5, 5, 5, 6, 7, 8, 0, 0},
        {0, 0, 0, 8, 7, 6, 6, 6, 7, 8, 0, 0, 0},
        {0, 0, 0, 0, 8, 8, 7, 8, 8, 0, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0}
    };

    std::list<uint32_t> list;
    for(int32_t y = 0; y < 13; ++y)
    {
        for(int32_t x = 0; x < 13; ++x)
        {
            if(area[y][x] == 1)
                list.push_back(3);
            else if(area[y][x] > 0 && area[y][x] <= radius)
                list.push_back(1);
            else
                list.push_back(0);
        }
    }

    setupArea(list, 13);
}

void CombatArea::setupExtArea(const std::list<uint32_t>& list, uint32_t rows)
{
    if(list.empty())
        return;

    //NORTH-WEST
    MatrixArea* area = createArea(list, rows);
    areas[NORTHWEST] = area;
    uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2;

    //NORTH-EAST
    MatrixArea* neArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, neArea, MATRIXOPERATION_MIRROR);
    areas[NORTHEAST] = neArea;

    //SOUTH-WEST
    MatrixArea* swArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(area, swArea, MATRIXOPERATION_FLIP);
    areas[SOUTHWEST] = swArea;

    //SOUTH-EAST
    MatrixArea* seArea = new MatrixArea(maxOutput, maxOutput);
    copyArea(swArea, seArea, MATRIXOPERATION_MIRROR);
    areas[SOUTHEAST] = seArea;

    hasExtArea = true;
}

//**********************************************************

bool MagicField::isBlocking(const Creature* creature) const
{
    if(!isUnstepable())
        return Item::isBlocking(creature);

    if(!creature || !creature->getPlayer())
        return true;

    uint32_t ownerId = getOwner();
    if(!ownerId)
        return false;

    if(Creature* owner = g_game.getCreatureByID(ownerId))
        return creature->getPlayer()->getGuildEmblem(owner) != EMBLEM_NONE;

    return false;
}

void MagicField::onStepInField(Creature* creature, bool purposeful/* = true*/)
{

    if(!purposeful)
        return;

    const ItemType& it = items[id];
    if(!it.condition)
        return;

    uint32_t ownerId = getOwner();
    Condition* condition = it.condition->clone();
    if(ownerId && !getTile()->hasFlag(TILESTATE_HARDCOREZONE))
    {
        if(Creature* owner = g_game.getCreatureByID(ownerId))
        {
            bool harmful = true;
            if((g_game.getWorldType() == WORLDTYPE_OPTIONAL || getTile()->hasFlag(TILESTATE_OPTIONALZONE))
                && (owner->getPlayer() || owner->isPlayerSummon()))
                harmful = false;
            else if(Player* targetPlayer = creature->getPlayer())
            {
                if(owner->getPlayer() && Combat::isProtected(owner->getPlayer(), targetPlayer))
                    harmful = false;
            }

            if(!harmful || (OTSYS_TIME() - createTime) <= (uint32_t)g_config.getNumber(
                ConfigManager::FIELD_OWNERSHIP) || creature->hasBeenAttacked(ownerId))
                condition->setParam(CONDITIONPARAM_OWNER, ownerId);
        }
    }

    creature->addCondition(condition);
}

 

 

 

 

 

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

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites
38 minutos atrás, poko360 disse:

@WooX
ainda n funfou :(
os monstro ainda tao pegando target no summon

 

Esqueci dessa parte, pra isso vou precisar que me envie também monster.cpp Mas o dano parou de pegar nos summons né?

 

 

 

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

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

o dano pode deixar normal (pra pegar no summon), tipo eu so queria que o monstro nao peguasse target nele, mas o dano pode deixar normal



monster.cpp

Spoiler

////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"

#include "monster.h"
#include "spawn.h"
#include "monsters.h"

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

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

extern Game g_game;
extern ConfigManager g_config;
extern Monsters g_monsters;

AutoList<Monster>Monster::autoList;
#ifdef __ENABLE_SERVER_DIAGNOSTIC__
uint32_t Monster::monsterCount = 0;
#endif

Monster* Monster::createMonster(MonsterType* mType)
{
    return new Monster(mType);
}

Monster* Monster::createMonster(const std::string& name)
{
    MonsterType* mType = g_monsters.getMonsterType(name);
    if(!mType)
        return NULL;

    return createMonster(mType);
}

Monster::Monster(MonsterType* _mType):
    Creature()
{
    isIdle = true;
    isMasterInRange = false;
    teleportToMaster = false;
    mType = _mType;
    spawn = NULL;
    raid = NULL;
    defaultOutfit = mType->outfit;
    currentOutfit = mType->outfit;

    double multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_HEALTH);
    health = (int32_t)(mType->health * multiplier);
    healthMax = (int32_t)(mType->healthMax * multiplier);

    baseSpeed = mType->baseSpeed;
    internalLight.level = mType->lightLevel;
    internalLight.color = mType->lightColor;
    setSkull(mType->skull);
    setShield(mType->partyShield);
    setEmblem(mType->guildEmblem);

    hideName = mType->hideName, hideHealth = mType->hideHealth;

    minCombatValue = 0;
    maxCombatValue = 0;

    targetTicks = 0;
    targetChangeTicks = 0;
    targetChangeCooldown = 0;
    attackTicks = 0;
    defenseTicks = 0;
    yellTicks = 0;
    extraMeleeAttack = false;
    
    targetPlayers = true; //MS

    // register creature events
    for(StringVec::iterator it = mType->scriptList.begin(); it != mType->scriptList.end(); ++it)
    {
        if(!registerCreatureEvent(*it))
            std::clog << "[Warning - Monster::Monster] Unknown event name - " << *it << std::endl;
    }

#ifdef __ENABLE_SERVER_DIAGNOSTIC__
    monsterCount++;
#endif
}

Monster::~Monster()
{
    clearTargetList();
    clearFriendList();
    namelist.clear(); //MS
#ifdef __ENABLE_SERVER_DIAGNOSTIC__

    monsterCount--;
#endif
    if(raid)
    {
        raid->unRef();
        raid = NULL;
    }
}

void Monster::setAttackPlayer(bool state) //MS
{
    targetPlayers = state;    
    
    if(attackedCreature && attackedCreature->getPlayer() && !targetPlayers) {
        setFollowCreature(NULL);
        setAttackedCreature(NULL); 
        onAttackedCreatureDisappear(true);                   
    }  
    

    updateTargetList(); 
}

void Monster::addAttackList(std::string name) //MS
{
    namelist.push_back(name);
    updateTargetList(); 
}

void Monster::onAttackedCreature(Creature* target)
{
    Creature::onAttackedCreature(target);
    if(isSummon())
        master->onSummonAttackedCreature(this, target);
}

void Monster::onAttackedCreatureDisappear(bool)
{
#ifdef __DEBUG__
    std::clog << "Attacked creature disappeared." << std::endl;
#endif
    attackTicks = 0;
    extraMeleeAttack = true;
}

void Monster::onAttackedCreatureDrain(Creature* target, int32_t points)
{
    Creature::onAttackedCreatureDrain(target, points);
    if(isSummon())
        master->onSummonAttackedCreatureDrain(this, target, points);
}

void Monster::onCreatureAppear(const Creature* creature)
{
    Creature::onCreatureAppear(creature);
    if(creature == this)
    {
        //We just spawned lets look around to see who is there.
        if(isSummon())
            isMasterInRange = canSee(master->getPosition());

        updateTargetList();
        updateIdleStatus();
    }
    else
        onCreatureEnter(const_cast<Creature*>(creature));
}

void Monster::onCreatureDisappear(const Creature* creature, bool isLogout)
{
    Creature::onCreatureDisappear(creature, isLogout);
    if(creature == this)
    {
        if(spawn)
            spawn->startEvent();

        setIdle(true);
    }
    else
        onCreatureLeave(const_cast<Creature*>(creature));
}

void Monster::onCreatureMove(const 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(creature == this)
    {
        if(isSummon())
            isMasterInRange = canSee(master->getPosition());

        updateTargetList();
        updateIdleStatus();
    }
    else
    {
        bool canSeeNewPos = canSee(newPos), canSeeOldPos = canSee(oldPos);
        if(canSeeNewPos && !canSeeOldPos)
            onCreatureEnter(const_cast<Creature*>(creature));
        else if(!canSeeNewPos && canSeeOldPos)
            onCreatureLeave(const_cast<Creature*>(creature));

        if(isSummon() && master == creature && canSeeNewPos) //Turn the summon on again
            isMasterInRange = true;

        updateIdleStatus();
        if(!followCreature && !isSummon() && isOpponent(creature)) //we have no target lets try pick this one
            selectTarget(const_cast<Creature*>(creature));
    }
}

void Monster::updateTargetList()
{
    CreatureList::iterator it;
    for(it = friendList.begin(); it != friendList.end();)
    {
        if((*it)->getHealth() <= 0 || !isFriend(*it) || !canSee((*it)->getPosition())) //MS
        {
            (*it)->unRef();
            it = friendList.erase(it);
        }
        else
            ++it;
    }

    for(it = targetList.begin(); it != targetList.end();)
    {
        if((*it)->getHealth() <= 0 || !isOpponent(*it) || !canSee((*it)->getPosition())) //MS
        {
            (*it)->unRef();
            it = targetList.erase(it);
        }
        else
            ++it;
    }

    const SpectatorVec& list = g_game.getSpectators(getPosition());
    for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it)
    {
        if((*it) != this && canSee((*it)->getPosition()))
            onCreatureFound(*it);
    }
}

void Monster::clearTargetList()
{
    for(CreatureList::iterator it = targetList.begin(); it != targetList.end(); ++it)
        (*it)->unRef();

    targetList.clear();
}

void Monster::clearFriendList()
{
    for(CreatureList::iterator it = friendList.begin(); it != friendList.end(); ++it)
        (*it)->unRef();

    friendList.clear();
}

void Monster::onCreatureFound(Creature* creature, bool pushFront /*= false*/)
{
    if(isFriend(creature))
    {
        assert(creature != this);
        if(std::find(friendList.begin(), friendList.end(), creature) == friendList.end())
        {
            creature->addRef();
            friendList.push_back(creature);
        }
    }

    if(isOpponent(creature))
    {
        assert(creature != this);
        if(std::find(targetList.begin(), targetList.end(), creature) == targetList.end())
        {
            creature->addRef();
            if(pushFront)
                targetList.push_front(creature);
            else
                targetList.push_back(creature);
        }
    }

    updateIdleStatus();
}

void Monster::onCreatureEnter(Creature* creature)
{
    if(master == creature) //Turn the summon on again
    {
        isMasterInRange = true;
        updateIdleStatus();
    }

    onCreatureFound(creature, true);
}

bool Monster::isFriend(const Creature* creature)
{
    if(creature->getMonster() == this) return true; 
    NameList::iterator it = std::find(namelist.begin(), namelist.end(), creature->getName()); //MS
    if(it != namelist.end())
        return false;   
    
    if(creature->getPlayer() && !targetPlayers)
        return true;   
    
    if(!isSummon() || !master->getPlayer())
        return creature->getMonster() && !creature->isSummon();

    const Player* tmpPlayer = NULL;
    if(creature->getPlayer())
        tmpPlayer = creature->getPlayer();
    else if(creature->getPlayerMaster())
        tmpPlayer = creature->getPlayerMaster();

    const Player* masterPlayer = master->getPlayer();
    return tmpPlayer && (tmpPlayer == masterPlayer || masterPlayer->isPartner(tmpPlayer) || masterPlayer->isAlly(tmpPlayer));
}

bool Monster::isOpponent(const Creature* creature)
{
    if(creature->getMonster() == this) return false;
    NameList::iterator it = std::find(namelist.begin(), namelist.end(), creature->getName());
    if(it != namelist.end())
          return true;
          
    if(creature->getPlayer() && !targetPlayers)
        return false;  
            
    return (isSummon() && master->getPlayer() && creature != master) || ((creature->getPlayer()
        && !creature->getPlayer()->hasFlag(PlayerFlag_IgnoredByMonsters)) ||
        (creature->getMaster() && creature->getPlayerMaster()));
}

bool Monster::doTeleportToMaster()
{
    const Position& tmp = getPosition();
    if(g_game.internalTeleport(this, g_game.getClosestFreeTile(this,
        master->getPosition(), true), true) != RET_NOERROR)
        return false;

    g_game.addMagicEffect(tmp, MAGIC_EFFECT_POFF);
    g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_TELEPORT);
    return true;
}

void Monster::onCreatureLeave(Creature* creature)
{
#ifdef __DEBUG__
    std::clog << "onCreatureLeave - " << creature->getName() << std::endl;
#endif
    if(isSummon() && master == creature)
    {
        if(!g_config.getBool(ConfigManager::TELEPORT_SUMMONS) && (!master->getPlayer()
            || !g_config.getBool(ConfigManager::TELEPORT_PLAYER_SUMMONS)))
        {
            //Turn the monster off until its master comes back
            isMasterInRange = false;
            updateIdleStatus();
        }
        else if(!doTeleportToMaster())
            teleportToMaster = true;
    }

    //update friendList
    if(isFriend(creature))
    {
        CreatureList::iterator it = std::find(friendList.begin(), friendList.end(), creature);
        if(it != friendList.end())
        {
            (*it)->unRef();
            friendList.erase(it);
        }
#ifdef __DEBUG__
        else
            std::clog << "Monster: " << creature->getName() << " not found in the friendList." << std::endl;
#endif
    }

    //update targetList
    if(isOpponent(creature))
    {
        CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature);
        if(it != targetList.end())
        {
            (*it)->unRef();
            targetList.erase(it);
            if(targetList.empty())
                updateIdleStatus();
        }
#ifdef __DEBUG__
        else
            std::clog << "Player: " << creature->getName() << " not found in the targetList." << std::endl;
#endif
    }
}

bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAULT*/)
{
#ifdef __DEBUG__
    std::clog << "Searching target... " << std::endl;
#endif

    std::list<Creature*> resultList;
    const Position& myPos = getPosition();
    for(CreatureList::iterator it = targetList.begin(); it != targetList.end(); ++it)
    {
        if(followCreature != (*it) && isTarget(*it) && (searchType == TARGETSEARCH_RANDOM
            || canUseAttack(myPos, *it)))
            resultList.push_back(*it);
    }

    switch(searchType)
    {
        case TARGETSEARCH_NEAREST:
        {
            Creature* target = NULL;
            int32_t range = -1;
            for(CreatureList::iterator it = resultList.begin(); it != resultList.end(); ++it)
            {
                int32_t tmp = std::max(std::abs(myPos.x - (*it)->getPosition().x),
                    std::abs(myPos.y - (*it)->getPosition().y));
                if(range >= 0 && tmp >= range)
                    continue;

                target = *it;
                range = tmp;
            }

            if(target && selectTarget(target))
                return target;

            break;
        }
        default:
        {
            if(!resultList.empty())
            {
                CreatureList::iterator it = resultList.begin();
                std::advance(it, random_range(0, resultList.size() - 1));
#ifdef __DEBUG__

                std::clog << "Selecting target " << (*it)->getName() << std::endl;
#endif
                return selectTarget(*it);
            }

            if(searchType == TARGETSEARCH_ATTACKRANGE)
                return false;

            break;
        }
    }


    //lets just pick the first target in the list
    for(CreatureList::iterator it = targetList.begin(); it != targetList.end(); ++it)
    {
        if(followCreature == (*it) || !selectTarget(*it))
            continue;

#ifdef __DEBUG__
        /*std::clog << "Selecting target " << (*it)->getName() << std::endl;*/ // Caused a strange crash, will look at it later
#endif
        return true;
    }

    return false;
}

void Monster::onFollowCreatureComplete(const Creature* creature)
{
    if(!creature)
        return;

    CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature);
    if(it != targetList.end())
    {
        Creature* target = (*it);
        targetList.erase(it);

        if(hasFollowPath) //push target we have found a path to the front
            targetList.push_front(target);
        else if(!isSummon()) //push target we have not found a path to the back
            targetList.push_back(target);
        else //Since we removed the creature from the targetList (and not put it back) we have to release it too
            target->unRef();
    }
}

BlockType_t Monster::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
    bool checkDefense/* = false*/, bool checkArmor/* = false*/, bool /*reflect = true*/)
{
    BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor);
    if(!damage)
        return blockType;

    int32_t elementMod = 0;
    ElementMap::iterator it = mType->elementMap.find(combatType);
    if(it != mType->elementMap.end())
        elementMod = it->second;

    if(!elementMod)
        return blockType;

    damage = (int32_t)std::ceil(damage * ((float)(100 - elementMod) / 100));
    if(damage > 0)
        return blockType;

    damage = 0;
    blockType = BLOCK_DEFENSE;
    return blockType;
}

bool Monster::isTarget(Creature* creature)
{
    return (!creature->isRemoved() && creature->isAttackable() && creature->getZone() != ZONE_PROTECTION
        && canSeeCreature(creature) && creature->getPosition().z == getPosition().z);
}

bool Monster::selectTarget(Creature* creature)
{
#ifdef __DEBUG__
    std::clog << "Selecting target... " << std::endl;
#endif
    if(!isTarget(creature))
        return false;

    CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature);
    if(it == targetList.end())
    {
        //Target not found in our target list.
#ifdef __DEBUG__
        std::clog << "Target not found in targetList." << std::endl;
#endif
        return false;
    }

    if((isHostile() || isSummon()) && setAttackedCreature(creature) && !isSummon())
        Dispatcher::getInstance().addTask(createTask(
            boost::bind(&Game::checkCreatureAttack, &g_game, getID())));

    return setFollowCreature(creature, true);
}

void Monster::setIdle(bool _idle)
{
    if(isRemoved() || getHealth() <= 0)
        return;

    isIdle = _idle;
    if(isIdle)
    {
        onIdleStatus();
        clearTargetList();
        clearFriendList();
        g_game.removeCreatureCheck(this);
    }
    else
        g_game.addCreatureCheck(this);
}

void Monster::updateIdleStatus()
{
    bool idle = false;
    if(conditions.empty())
    {
        if(isSummon())
        {
            if((!isMasterInRange && !teleportToMaster) || (master->getMonster() && master->getMonster()->getIdleStatus()))
                idle = true;
        }
        else if(targetList.empty() && targetPlayers)
            idle = true;
    }

    setIdle(idle);
}

void Monster::onAddCondition(ConditionType_t type, bool hadCondition)
{
    Creature::onAddCondition(type, hadCondition);
    //the walkCache need to be updated if the monster becomes "resistent" to the damage, see Tile::__queryAdd()
    if(type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON)
        updateMapCache();

    updateIdleStatus();
}

void Monster::onEndCondition(ConditionType_t type)
{
    Creature::onEndCondition(type);
    //the walkCache need to be updated if the monster loose the "resistent" to the damage, see Tile::__queryAdd()
    if(type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON)
        updateMapCache();

    updateIdleStatus();
}

void Monster::onThink(uint32_t interval)
{
    Creature::onThink(interval);
    if(despawn())
    {
        g_game.removeCreature(this, true);
        setIdle(true);
        return;
    }

    updateIdleStatus();
    if(isIdle)
        return;

    if(teleportToMaster && doTeleportToMaster())
        teleportToMaster = false;

    addEventWalk();
    if(isSummon())
    {
        if(!attackedCreature)
        {
            if(master && master->getAttackedCreature()) //This happens if the monster is summoned during combat
                selectTarget(master->getAttackedCreature());
            else if(master != followCreature) //Our master has not ordered us to attack anything, lets follow him around instead.
                setFollowCreature(master);
        }
        else if(attackedCreature == this)
            setFollowCreature(NULL);
        else if(followCreature != attackedCreature) //This happens just after a master orders an attack, so lets follow it aswell.
            setFollowCreature(attackedCreature);
    }
    else if(!targetList.empty())
    {
        if(!followCreature || !hasFollowPath)
            searchTarget();
        else if(isFleeing() && attackedCreature && !canUseAttack(getPosition(), attackedCreature))
            searchTarget(TARGETSEARCH_ATTACKRANGE);
    }

    onThinkTarget(interval);
    onThinkYell(interval);
    onThinkDefense(interval);
}

void Monster::doAttacking(uint32_t interval)
{
    if(!attackedCreature || (isSummon() && attackedCreature == this))
        return;

    bool updateLook = true;
    resetTicks = interval;
    attackTicks += interval;

    const Position& myPos = getPosition();
    for(SpellList::iterator it = mType->spellAttackList.begin(); it != mType->spellAttackList.end(); ++it)
    {
        if(!attackedCreature || attackedCreature->isRemoved())
            break;

        const Position& targetPos = attackedCreature->getPosition();
        if(it->isMelee && isFleeing())
            continue;

        bool inRange = false;
        if(canUseSpell(myPos, targetPos, *it, interval, inRange))
        {
            if(it->chance >= (uint32_t)random_range(1, 100))
            {
                if(updateLook)
                {
                    updateLookDirection();
                    updateLook = false;
                }

                double multiplier;
                if(maxCombatValue > 0) //defense
                    multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_DEFENSE);
                else //attack
                    multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_ATTACK);

                minCombatValue = (int32_t)(it->minCombatValue * multiplier);
                maxCombatValue = (int32_t)(it->maxCombatValue * multiplier);

                it->spell->castSpell(this, attackedCreature);
                if(it->isMelee)
                    extraMeleeAttack = false;
#ifdef __DEBUG__

                static uint64_t prevTicks = OTSYS_TIME();
                std::clog << "doAttacking ticks: " << OTSYS_TIME() - prevTicks << std::endl;
                prevTicks = OTSYS_TIME();
#endif
            }
        }

        if(!inRange && it->isMelee) //melee swing out of reach
            extraMeleeAttack = true;
    }

    if(updateLook)
        updateLookDirection();

    if(resetTicks)
        attackTicks = 0;
}

bool Monster::canUseAttack(const Position& pos, const Creature* target) const
{
    if(!isHostile())
        return true;

    const Position& targetPos = target->getPosition();
    for(SpellList::iterator it = mType->spellAttackList.begin(); it != mType->spellAttackList.end(); ++it)
    {
        if((*it).range != 0 && std::max(std::abs(pos.x - targetPos.x), std::abs(pos.y - targetPos.y)) <= (int32_t)(*it).range)
            return g_game.isSightClear(pos, targetPos, true);
    }

    return false;
}

bool Monster::canUseSpell(const Position& pos, const Position& targetPos,
    const spellBlock_t& sb, uint32_t interval, bool& inRange)
{
    inRange = true;
    if(!sb.isMelee || !extraMeleeAttack)
    {
        if(sb.speed > attackTicks)
        {
            resetTicks = false;
            return false;
        }

        if(attackTicks % sb.speed >= interval) //already used this spell for this round
            return false;
    }

    if(!sb.range || std::max(std::abs(pos.x - targetPos.x), std::abs(pos.y - targetPos.y)) <= (int32_t)sb.range)
        return true;

    inRange = false;
    return false;
}

void Monster::onThinkTarget(uint32_t interval)
{
    if(isSummon() || mType->changeTargetSpeed <= 0)
        return;

    bool canChangeTarget = true;
    if(targetChangeCooldown > 0)
    {
        targetChangeCooldown -= interval;
        if(targetChangeCooldown <= 0)
        {
            targetChangeCooldown = 0;
            targetChangeTicks = (uint32_t)mType->changeTargetSpeed;
        }
        else
            canChangeTarget = false;
    }

    if(!canChangeTarget)
        return;

    targetChangeTicks += interval;
    if(targetChangeTicks < (uint32_t)mType->changeTargetSpeed)
        return;

    targetChangeTicks = 0;
    targetChangeCooldown = (uint32_t)mType->changeTargetSpeed;
    if(mType->changeTargetChance < random_range(1, 100))
        return;

    if(mType->targetDistance <= 1)
        searchTarget(TARGETSEARCH_RANDOM);
    else
        searchTarget(TARGETSEARCH_NEAREST);
}

void Monster::onThinkDefense(uint32_t interval)
{
    resetTicks = true;
    defenseTicks += interval;
    for(SpellList::iterator it = mType->spellDefenseList.begin(); it != mType->spellDefenseList.end(); ++it)
    {
        if(it->speed > defenseTicks)
        {
            if(resetTicks)
                resetTicks = false;

            continue;
        }

        if(defenseTicks % it->speed >= interval) //already used this spell for this round
            continue;

        if((it->chance >= (uint32_t)random_range(1, 100)))
        {
            minCombatValue = it->minCombatValue;
            maxCombatValue = it->maxCombatValue;
            it->spell->castSpell(this, this);
        }
    }

    if(!isSummon())
    {
        if(mType->maxSummons < 0 || (int32_t)summons.size() < mType->maxSummons)
        {
            for(SummonList::iterator it = mType->summonList.begin(); it != mType->summonList.end(); ++it)
            {
                if((int32_t)summons.size() >= mType->maxSummons)
                    break;

                if(it->interval > defenseTicks)
                {
                    if(resetTicks)
                        resetTicks = false;

                    continue;
                }

                if(defenseTicks % it->interval >= interval)
                    continue;

                uint32_t typeCount = 0;
                for(CreatureList::iterator cit = summons.begin(); cit != summons.end(); ++cit)
                {
                    if(!(*cit)->isRemoved() && (*cit)->getMonster() &&
                        (*cit)->getMonster()->getName() == it->name)
                        typeCount++;
                }

                if(typeCount >= it->amount)
                    continue;

                if((it->chance >= (uint32_t)random_range(1, 100)))
                {
                    if(Monster* summon = Monster::createMonster(it->name))
                    {
                        addSummon(summon);
                        if(g_game.placeCreature(summon, getPosition()))
                            g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_WRAPS_BLUE);
                        else
                            removeSummon(summon);
                    }
                }
            }
        }
    }

    if(resetTicks)
        defenseTicks = 0;
}

void Monster::onThinkYell(uint32_t interval)
{
    if(mType->yellSpeedTicks <= 0)
        return;

    yellTicks += interval;
    if(yellTicks < mType->yellSpeedTicks)
        return;

    yellTicks = 0;
    if(mType->voiceVector.empty() || (mType->yellChance < (uint32_t)random_range(1, 100)))
        return;

    const voiceBlock_t& vb = mType->voiceVector[random_range(0, mType->voiceVector.size() - 1)];
    if(vb.yellText)
        g_game.internalCreatureSay(this, SPEAK_MONSTER_YELL, vb.text, false);
    else
        g_game.internalCreatureSay(this, SPEAK_MONSTER_SAY, vb.text, false);
}

bool Monster::pushItem(Item* item, int32_t radius)
{
    const Position& centerPos = item->getPosition();
    PairVector pairVector;
    pairVector.push_back(PositionPair(-1, -1));
    pairVector.push_back(PositionPair(-1, 0));
    pairVector.push_back(PositionPair(-1, 1));
    pairVector.push_back(PositionPair(0, -1));
    pairVector.push_back(PositionPair(0, 1));
    pairVector.push_back(PositionPair(1, -1));
    pairVector.push_back(PositionPair(1, 0));
    pairVector.push_back(PositionPair(1, 1));

    std::random_shuffle(pairVector.begin(), pairVector.end());
    Position tryPos;
    for(int32_t n = 1; n <= radius; ++n)
    {
        for(PairVector::iterator it = pairVector.begin(); it != pairVector.end(); ++it)
        {
            int32_t dx = it->first * n, dy = it->second * n;
            tryPos = centerPos;

            tryPos.x = tryPos.x + dx;
            tryPos.y = tryPos.y + dy;

            Tile* tile = g_game.getTile(tryPos);
            if(tile && g_game.canThrowObjectTo(centerPos, tryPos) && g_game.internalMoveItem(this, item->getParent(),
                tile, INDEX_WHEREEVER, item, item->getItemCount(), NULL) == RET_NOERROR)
                return true;
        }
    }

    return false;
}

void Monster::pushItems(Tile* tile)
{
    TileItemVector* items = tile->getItemList();
    if(!items)
        return;

    //We cannot use iterators here since we can push the item to another tile
    //which will invalidate the iterator.
    //start from the end to minimize the amount of traffic
    int32_t moveCount = 0, removeCount = 0, downItemsSize = tile->getDownItemCount();
    Item* item = NULL;
    for(int32_t i = downItemsSize - 1; i >= 0; --i)
    {
        assert(i >= 0 && i < downItemsSize);
        if((item = items->at(i)) && item->hasProperty(MOVEABLE) &&
            (item->hasProperty(BLOCKPATH) || item->hasProperty(BLOCKSOLID)))
        {
            if(moveCount < 20 && pushItem(item, 1))
                moveCount++;
            else if(g_game.internalRemoveItem(this, item) == RET_NOERROR)
                ++removeCount;
        }
    }

    if(removeCount > 0)
        g_game.addMagicEffect(tile->getPosition(), MAGIC_EFFECT_POFF);
}

bool Monster::pushCreature(Creature* creature)
{
    DirVector dirVector;
    dirVector.push_back(NORTH);
    dirVector.push_back(SOUTH);
    dirVector.push_back(WEST);
    dirVector.push_back(EAST);

    std::random_shuffle(dirVector.begin(), dirVector.end());
    Position monsterPos = creature->getPosition();

    Tile* tile = NULL;
    for(DirVector::iterator it = dirVector.begin(); it != dirVector.end(); ++it)
    {
        if((tile = g_game.getTile(Spells::getCasterPosition(creature, (*it)))) && !tile->hasProperty(
            BLOCKPATH) && g_game.internalMoveCreature(creature, (*it)) == RET_NOERROR)
            return true;
    }

    return false;
}

void Monster::pushCreatures(Tile* tile)
{
    CreatureVector* creatures = tile->getCreatures();
    if(!creatures)
        return;

    bool effect = false;
    Monster* monster = NULL;
    for(uint32_t i = 0; i < creatures->size();)
    {
        if((monster = creatures->at(i)->getMonster()) && monster->isPushable())
        {
            if(pushCreature(monster))
                continue;

            monster->setDropLoot(LOOT_DROP_NONE);
            monster->changeHealth(-monster->getHealth());
            if(!effect)
                effect = true;
        }

        ++i;
    }

    if(effect)
        g_game.addMagicEffect(tile->getPosition(), MAGIC_EFFECT_BLOCKHIT);
}

bool Monster::getNextStep(Direction& dir, uint32_t& flags)
{
    if(isIdle || getHealth() <= 0)
    {
        //we dont have anyone watching might aswell stop walking
        eventWalk = 0;
        return false;
    }

    bool result = false;
    if((!followCreature || !hasFollowPath) && !isSummon())
    {
        if(followCreature || getTimeSinceLastMove() > 1000) //choose a random direction
            result = getRandomStep(getPosition(), dir);
    }
    else if(isSummon() || followCreature)
    {
        result = Creature::getNextStep(dir, flags);
        if(!result)
        {
            //target dancing
            if(attackedCreature && attackedCreature == followCreature)
            {
                if(isFleeing())
                    result = getDanceStep(getPosition(), dir, false, false);
                else if(mType->staticAttackChance < (uint32_t)random_range(1, 100))
                    result = getDanceStep(getPosition(), dir);
            }
        }
        else
            flags |= FLAG_PATHFINDING;
    }

    if(result && (canPushItems() || canPushCreatures()))
    {
        if(Tile* tile = g_game.getTile(Spells::getCasterPosition(this, dir)))
        {
            if(canPushItems())
                pushItems(tile);

            if(canPushCreatures())
                pushCreatures(tile);
        }
#ifdef __DEBUG__
        else
            std::clog << "[Warning - Monster::getNextStep] no tile found." << std::endl;
#endif
    }

    return result;
}

bool Monster::getRandomStep(const Position& creaturePos, Direction& dir)
{
    DirVector dirVector;
    dirVector.push_back(NORTH);
    dirVector.push_back(SOUTH);
    dirVector.push_back(WEST);
    dirVector.push_back(EAST);

    std::random_shuffle(dirVector.begin(), dirVector.end());
    for(DirVector::iterator it = dirVector.begin(); it != dirVector.end(); ++it)
    {
        if(!canWalkTo(creaturePos, *it))
            continue;

        dir = *it;
        return true;
    }

    return false;
}

bool Monster::getDanceStep(const Position& creaturePos, Direction& dir,    bool keepAttack /*= true*/, bool keepDistance /*= true*/)
{
    assert(attackedCreature);
    bool canDoAttackNow = canUseAttack(creaturePos, attackedCreature);
    const Position& centerPos = attackedCreature->getPosition();

    uint32_t tmpDist, centerToDist = std::max(std::abs(creaturePos.x - centerPos.x), std::abs(creaturePos.y - centerPos.y));
    DirVector dirVector;
    if(!keepDistance || creaturePos.y - centerPos.y >= 0)
    {
        tmpDist = std::max(std::abs((creaturePos.x) - centerPos.x), std::abs((creaturePos.y - 1) - centerPos.y));
        if(tmpDist == centerToDist && canWalkTo(creaturePos, NORTH))
        {
            bool result = true;
            if(keepAttack)
                result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x, creaturePos.y - 1, creaturePos.z), attackedCreature));

            if(result)
                dirVector.push_back(NORTH);
        }
    }

    if(!keepDistance || creaturePos.y - centerPos.y <= 0)
    {
        tmpDist = std::max(std::abs((creaturePos.x) - centerPos.x), std::abs((creaturePos.y + 1) - centerPos.y));
        if(tmpDist == centerToDist && canWalkTo(creaturePos, SOUTH))
        {
            bool result = true;
            if(keepAttack)
                result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x, creaturePos.y + 1, creaturePos.z), attackedCreature));

            if(result)
                dirVector.push_back(SOUTH);
        }
    }

    if(!keepDistance || creaturePos.x - centerPos.x >= 0)
    {
        tmpDist = std::max(std::abs((creaturePos.x + 1) - centerPos.x), std::abs((creaturePos.y) - centerPos.y));
        if(tmpDist == centerToDist && canWalkTo(creaturePos, EAST))
        {
            bool result = true;
            if(keepAttack)
                result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x + 1, creaturePos.y, creaturePos.z), attackedCreature));

            if(result)
                dirVector.push_back(EAST);
        }
    }

    if(!keepDistance || creaturePos.x - centerPos.x <= 0)
    {
        tmpDist = std::max(std::abs((creaturePos.x - 1) - centerPos.x), std::abs((creaturePos.y) - centerPos.y));
        if(tmpDist == centerToDist && canWalkTo(creaturePos, WEST))
        {
            bool result = true;
            if(keepAttack)
                result = (!canDoAttackNow || canUseAttack(Position(creaturePos.x - 1, creaturePos.y, creaturePos.z), attackedCreature));

            if(result)
                dirVector.push_back(WEST);
        }
    }

    if(dirVector.empty())
        return false;

    std::random_shuffle(dirVector.begin(), dirVector.end());
    dir = dirVector[random_range(0, dirVector.size() - 1)];
    return true;
}

bool Monster::isInSpawnRange(const Position& toPos)
{
    return masterRadius == -1 || !inDespawnRange(toPos);
}

bool Monster::canWalkTo(Position pos, Direction dir)
{
    if(getNoMove())
        return false;

    switch(dir)
    {
        case NORTH:
            pos.y += -1;
            break;
        case WEST:
            pos.x += -1;
            break;
        case EAST:
            pos.x += 1;
            break;
        case SOUTH:
            pos.y += 1;
            break;
        default:
            break;
    }

    if(!isInSpawnRange(pos) || !getWalkCache(pos))
        return false;

    Tile* tile = g_game.getTile(pos);
    if(!tile || g_game.isSwimmingPool(NULL, getTile(), false) != g_game.isSwimmingPool(NULL, tile, false)) // prevent monsters entering/exiting to swimming pool
        return false;

    return !tile->getTopVisibleCreature(this) && tile->__queryAdd(
        0, this, 1, FLAG_PATHFINDING) == RET_NOERROR;
}

bool Monster::onDeath()
{
    if(!Creature::onDeath())
        return false;

    destroySummons();
    clearTargetList();
    clearFriendList();

    setAttackedCreature(NULL);
    onIdleStatus();
    if(raid)
    {
        raid->unRef();
        raid = NULL;
    }

    g_game.removeCreature(this, false);
    return true;
}

Item* Monster::createCorpse(DeathList deathList)
{
    Item* corpse = Creature::createCorpse(deathList);
    if(!corpse)
        return NULL;

    if(mType->corpseUnique)
        corpse->setUniqueId(mType->corpseUnique);

    if(mType->corpseAction)
        corpse->setActionId(mType->corpseAction, false);

    if(deathList[0].isNameKill())
        return corpse;

    Creature* _owner = deathList[0].getKillerCreature();
    if(deathList.size() > 1 && deathList[1].getDamage() > deathList[0].getDamage())
        _owner = deathList[1].getKillerCreature();

    if(!_owner)
        return corpse;

    Player* owner = NULL;
    if(_owner->getPlayer())
        owner = _owner->getPlayer();
    else if(_owner->isPlayerSummon())
        owner = _owner->getPlayerMaster();

    if(!owner)
        return corpse;

    uint64_t stamina = g_config.getNumber(ConfigManager::STAMINA_DESTROY_LOOT);
    if(stamina && owner->getStamina() <= (stamina * STAMINA_MULTIPLIER))
        lootDrop = LOOT_DROP_NONE;

    corpse->setCorpseOwner(owner->getGUID());
    return corpse;
}

bool Monster::inDespawnRange(const Position& pos)
{
    if(!spawn || mType->isLureable)
        return false;

    int32_t radius = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRADIUS);
    if(!radius)
        return false;

    if(!Spawns::getInstance()->isInZone(masterPosition, radius, pos))
        return true;

    int32_t range = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRANGE);
    if(!range)
        return false;

    return std::abs(pos.z - masterPosition.z) > range;
}

bool Monster::despawn()
{
    return inDespawnRange(getPosition());
}

bool Monster::getCombatValues(int32_t& min, int32_t& max)
{
    if(!minCombatValue && !maxCombatValue)
        return false;

    double multiplier;
    if(maxCombatValue > 0) //defense
        multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_DEFENSE);
    else //attack
        multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_ATTACK);

    min = (int32_t)(minCombatValue * multiplier);
    max = (int32_t)(maxCombatValue * multiplier);
    return true;
}

void Monster::updateLookDirection()
{
    Direction newDir = getDirection();
    if(attackedCreature)
    {
        const Position& pos = getPosition();
        const Position& attackedCreaturePos = attackedCreature->getPosition();

        int32_t dx = attackedCreaturePos.x - pos.x, dy = attackedCreaturePos.y - pos.y;
        if(std::abs(dx) > std::abs(dy))
        {
            //look EAST/WEST
            if(dx < 0)
                newDir = WEST;
            else
                newDir = EAST;
        }
        else if(std::abs(dx) < std::abs(dy))
        {
            //look NORTH/SOUTH
            if(dy < 0)
                newDir = NORTH;
            else
                newDir = SOUTH;
        }
        else if(dx < 0 && dy < 0)
        {
            if(getDirection() == SOUTH)
                newDir = WEST;
            else if(getDirection() == EAST)
                newDir = NORTH;
        }
        else if(dx < 0 && dy > 0)
        {
            if(getDirection() == NORTH)
                newDir = WEST;
            else if(getDirection() == EAST)
                newDir = SOUTH;
        }
        else if(dx > 0 && dy < 0)
        {
            if(getDirection() == SOUTH)
                newDir = EAST;
            else if(getDirection() == WEST)
                newDir = NORTH;
        }
        else if(getDirection() == NORTH)
            newDir = EAST;
        else if(getDirection() == WEST)
            newDir = SOUTH;
    }

    g_game.internalCreatureTurn(this, newDir);
}

void Monster::dropLoot(Container* corpse)
{
    if(corpse && lootDrop == LOOT_DROP_FULL)
        mType->dropLoot(corpse);
}

bool Monster::isImmune(CombatType_t type) const
{
    ElementMap::const_iterator it = mType->elementMap.find(type);
    if(it == mType->elementMap.end())
        return Creature::isImmune(type);

    return it->second >= 100;
}

void Monster::setNormalCreatureLight()
{
    internalLight.level = mType->lightLevel;
    internalLight.color = mType->lightColor;
}

void Monster::drainHealth(Creature* attacker, CombatType_t combatType, int32_t damage)
{
    Creature::drainHealth(attacker, combatType, damage);
    if(isInvisible())
        removeCondition(CONDITION_INVISIBLE);
}

void Monster::changeHealth(int32_t healthChange)
{
    //In case a player with ignore flag set attacks the monster
    setIdle(false);
    Creature::changeHealth(healthChange);
}

bool Monster::challengeCreature(Creature* creature)
{
    if(isSummon() || !selectTarget(creature))
        return false;

    targetChangeCooldown = 8000;
    targetChangeTicks = 0;
    return true;
}

bool Monster::convinceCreature(Creature* creature)
{
    Player* player = creature->getPlayer();
    if(player && !player->hasFlag(PlayerFlag_CanConvinceAll) && !mType->isConvinceable)
        return false;

    Creature* oldMaster = NULL;
    if(isSummon())
        oldMaster = master;

    if(oldMaster)
    {
        if(oldMaster->getPlayer() || oldMaster == creature)
            return false;

        oldMaster->removeSummon(this);
    }

    setFollowCreature(NULL);
    setAttackedCreature(NULL);
    destroySummons();

    creature->addSummon(this);
    updateTargetList();
    updateIdleStatus();

    //Notify surrounding about the change
    SpectatorVec list;
    g_game.getSpectators(list, getPosition(), false, true);
    g_game.getSpectators(list, creature->getPosition(), true, true);

    isMasterInRange = true;
    for(SpectatorVec::iterator it = list.begin(); it != list.end(); ++it)
        (*it)->onCreatureConvinced(creature, this);

    if(spawn)
    {
        spawn->removeMonster(this);
        spawn = NULL;
        masterRadius = -1;
    }

    if(raid)
    {
        raid->unRef();
        raid = NULL;
    }

    return true;
}

void Monster::onCreatureConvinced(const Creature* convincer, const Creature* creature)
{
    if(convincer == this || (!isFriend(creature) && !isOpponent(creature)))
        return;

    updateTargetList();
    updateIdleStatus();
}

void Monster::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const
{
    Creature::getPathSearchParams(creature, fpp);
    fpp.minTargetDist = 1;
    fpp.maxTargetDist = mType->targetDistance;
    if(isSummon())
    {
        if(master == creature)
        {
            fpp.maxTargetDist = 2;
            fpp.fullPathSearch = true;
        }
        else if(mType->targetDistance <= 1)
            fpp.fullPathSearch = true;
        else
            fpp.fullPathSearch = !canUseAttack(getPosition(), creature);
    }
    else if(isFleeing())
    {
        //Distance should be higher than the client view range (Map::maxClientViewportX/Map::maxClientViewportY)
        fpp.maxTargetDist = Map::maxViewportX;
        fpp.clearSight = fpp.fullPathSearch = false;
        fpp.keepDistance = true;
    }
    else if(mType->targetDistance <= 1)
        fpp.fullPathSearch = true;
    else
        fpp.fullPathSearch = !canUseAttack(getPosition(), creature);
}
 

 

Editado por poko360 (veja o histórico de edições)
Link para o post
Compartilhar em outros sites
  • Solução
Em 19/03/2019 em 17:36, poko360 disse:

o dano pode deixar normal (pra pegar no summon), tipo eu so queria que o monstro nao peguasse target nele, mas o dano pode deixar normal

Volta seu combat.cpp para o original pro dano voltar a pegar. Pra não pegar target nos summons faz essa alteração.

 

Em monster.cpp onde está:

bool Monster::isTarget(Creature* creature)
{
    return (!creature->isRemoved() && creature->isAttackable() && creature->getZone() != ZONE_PROTECTION
        && canSeeCreature(creature) && creature->getPosition().z == getPosition().z);
}

Muda para:

bool Monster::isTarget(Creature* creature)
{
    return (!creature->isRemoved() && creature->isAttackable() && !creature->isPlayerSummon() && creature->getZone() != ZONE_PROTECTION
        && canSeeCreature(creature) && creature->getPosition().z == getPosition().z);
}

 

 

 

 

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

                                                                                                                                                                                                                                        Aristóteles 

Link para o post
Compartilhar em outros sites

Participe da conversa

Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.

Visitante
Responder

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emojis são permitidos.

×   Seu link foi automaticamente incorporado.   Mostrar como link

×   Seu conteúdo anterior foi restaurado.   Limpar o editor

×   Não é possível colar imagens diretamente. Carregar ou inserir imagens do URL.

×
×
  • Criar Novo...

Informação Importante

Confirmação de Termo