/*
 * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "AreaTriggerAI.h"
#include "Anticheat.h"
#include "Battlefield.h"
#include "BattlefieldMgr.h"
#include "Battleground.h"
#include "BattlegroundScore.h"
#include "CellImpl.h"
#include "Challenge.h"
#include "CharmInfo.h"
#include "Chat.h"
#include "ChatPackets.h"
#include "CombatLogPackets.h"
#include "CombatPackets.h"
#include "Common.h"
#include "ConditionMgr.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "CreatureGroups.h"
#include "Formulas.h"
#include "Garrison.h"
#include "GridNotifiersImpl.h"
#include "Group.h"
#include "InstancePackets.h"
#include "InstanceSaveMgr.h"
#include "InstanceScript.h"
#include "Log.h"
#include "LootPackets.h"
#include "MapManager.h"
#include "MiscPackets.h"
#include "MovementPackets.h"
#include "MoveSpline.h"
#include "MoveSplineInit.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "ObjectVisitors.hpp"
#include "Opcodes.h"
#include "OutdoorPvPMgr.h"
#include "PartyPackets.h"
#include "PassiveAI.h"
#include "Pet.h"
#include "PetAI.h"
#include "PetPackets.h"
#include "Player.h"
#include "PlayerDefines.h"
#include "QuestDef.h"
#include "ScenarioMgr.h"
#include "SpectatorAddon.h"
#include "Spell.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "SpellPackets.h"
#include "TemporarySummon.h"
#include "Totem.h"
#include "Transport.h"
#include "Unit.h"
#include "UpdateFieldFlags.h"
#include "UpdatePackets.h"
#include "Util.h"
#include "Vehicle.h"
#include "VehiclePackets.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include <numeric>

float baseMoveSpeed[MAX_MOVE_TYPE] =
{
    2.5f,                  // MOVE_WALK
    7.0f,                  // MOVE_RUN
    4.5f,                  // MOVE_RUN_BACK
    4.722222f,             // MOVE_SWIM
    2.5f,                  // MOVE_SWIM_BACK
    3.141594f,             // MOVE_TURN_RATE
    7.0f,                  // MOVE_FLIGHT
    4.5f,                  // MOVE_FLIGHT_BACK
    3.14f                  // MOVE_PITCH_RATE
};

float playerBaseMoveSpeed[MAX_MOVE_TYPE] =
{
    2.5f,                  // MOVE_WALK
    7.0f,                  // MOVE_RUN
    4.5f,                  // MOVE_RUN_BACK
    4.722222f,             // MOVE_SWIM
    2.5f,                  // MOVE_SWIM_BACK
    3.141594f,             // MOVE_TURN_RATE
    7.0f,                  // MOVE_FLIGHT
    4.5f,                  // MOVE_FLIGHT_BACK
    3.14f                  // MOVE_PITCH_RATE
};

// Used for prepare can/can`t triggr aura
static bool InitTriggerAuraData();
// Define can trigger auras
static bool isTriggerAura[TOTAL_AURAS];
// Define can't trigger auras (need for disable second trigger)
static bool isNonTriggerAura[TOTAL_AURAS];
// Triggered always, even from triggered spells
static bool isAlwaysTriggeredAura[TOTAL_AURAS];
// Has cap to damage
static bool isDamageCapAura[TOTAL_AURAS];
// Prepare lists
// ReSharper disable CppDeclaratorNeverUsed
static bool procPrepared = InitTriggerAuraData();
// ReSharper restore CppDeclaratorNeverUsed

DamageInfo::DamageInfo(Unit* _attacker, Unit* _victim, uint32 _damage, SpellInfo const* _spellInfo, SpellSchoolMask _schoolMask, DamageEffectType _damageType, uint32 m_damageBeforeHit) :
    m_attacker(_attacker), m_victim(_victim), m_damage(_damage), m_overHeal{ 0 }, m_spellInfo(_spellInfo), m_schoolMask(_schoolMask), m_damageType(_damageType), m_attackType(BASE_ATTACK), m_damageBeforeHit(m_damageBeforeHit)
{
    m_absorb = 0;
    m_resist = 0;
    m_block = 0;
    m_cleanDamage = 0;
    m_addpower = 0;
    m_addptype = -1;
    m_startTime = 0;
    targetInfoMask = 0;
}

DamageInfo::DamageInfo(CalcDamageInfo& dmgInfo) : m_attacker(dmgInfo.attacker), m_victim(dmgInfo.target), m_damage(dmgInfo.damage), m_overHeal{ 0 }, m_spellInfo(nullptr),
m_schoolMask(SpellSchoolMask(dmgInfo.damageSchoolMask)), m_damageType(DIRECT_DAMAGE), m_attackType(dmgInfo.attackType), m_absorb(dmgInfo.absorb), m_resist(dmgInfo.resist),
m_block(dmgInfo.blocked_amount), m_cleanDamage(dmgInfo.cleanDamage), m_damageBeforeHit(dmgInfo.damageBeforeHit), m_addpower(0), m_addptype(-1), targetInfoMask(0)
{
}

DamageInfo::DamageInfo(SpellNonMeleeDamage& dmgInfo, SpellInfo const* _spellInfo) : m_attacker(dmgInfo.attacker), m_victim(dmgInfo.target), m_damage(dmgInfo.damage), m_overHeal{ 0 },
m_spellInfo(_spellInfo), m_schoolMask(SpellSchoolMask(dmgInfo.schoolMask)), m_damageType(DIRECT_DAMAGE), m_attackType(BASE_ATTACK), m_absorb(dmgInfo.absorb), m_resist(dmgInfo.resist),
m_block(dmgInfo.blocked), m_cleanDamage(dmgInfo.cleanDamage), m_damageBeforeHit(dmgInfo.damageBeforeHit), m_addpower(0), m_addptype(-1), targetInfoMask(0)
{
}

void DamageInfo::ModifyDamage(int32 amount)
{
    amount = std::min(amount, int32(GetDamage()));
    m_damage += amount;
}

void DamageInfo::AbsorbDamage(int32 amount)
{
    amount = std::min(amount, int32(GetDamage()));
    m_absorb += amount;
    m_damage -= amount;
}

void DamageInfo::ResistDamage(uint32 amount)
{
    amount = std::min(amount, GetDamage());
    m_resist += amount;
    m_damage -= amount;
}

void DamageInfo::BlockDamage(uint32 amount)
{
    amount = std::min(amount, GetDamage());
    m_block += amount;
    m_damage -= amount;
}

void DamageInfo::SetTargetInfoMask(uint32 Mask)
{
    targetInfoMask = Mask;
}

bool DamageInfo::HasTargetInfoMask(uint32 Mask)
{
    return targetInfoMask & Mask;
}

ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo)
:_actor(actor), _actionTarget(actionTarget), _procTarget(procTarget), _typeMask(typeMask), _spellTypeMask(spellTypeMask), _spellPhaseMask(spellPhaseMask),
_hitMask(hitMask), _spell(spell), _damageInfo(damageInfo), _healInfo(healInfo)
{
}

SpellNonMeleeDamage::SpellNonMeleeDamage(Unit* _attacker, Unit* _target, uint32 spellID, uint32 spellXSpellVisualID, uint32 _schoolMask, ObjectGuid castGuid /*= ObjectGuid::Empty*/): damageBeforeHit(0)
{
    target = _target;
    attacker = _attacker;
    CastGuid = castGuid;
    SpellID = spellID;
    damage = 0;
    schoolMask = _schoolMask;
    absorb = 0;
    resist = 0;
    blocked = 0;
    HitInfo = 0;
    cleanDamage = 0;
    preHitHealth = _target ? _target->GetHealth(_attacker) : 0;
    periodicLog = false;
    SpellXSpellVisualID = spellXSpellVisualID;
}

// we can disable this warning for this since it only
// causes undefined behavior when passed to the base class constructor
#ifdef _MSC_VER
#pragma warning(disable:4355)
#endif
Unit::Unit(bool isWorldObject): WorldObject(isWorldObject), m_movedPlayer(nullptr), m_lastSanctuaryTime(0), IsAIEnabled(false), NeedChangeAI(false), m_ControlledByPlayer(false),
movespline(new Movement::MoveSpline()), i_AI(nullptr), i_disabledAI(nullptr), m_AutoRepeatFirstCast(false), m_procDeep(0), m_castCounter(0), m_removedAurasCount(0), i_motionMaster(this),
m_regenTimer{0}, isCasterPet{false}, m_ThreatManager(this), m_vehicle(nullptr), m_vehicleKit(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_rootTimes{0}, m_HostileRefManager(this), _delayInterruptFlag(0), _lastDamagedTime(0), damageTrackingTimer_(),
playerDamageTaken_(), npcDamageTaken_()
{
#ifdef _MSC_VER
#pragma warning(default:4355)
#endif
    m_objectType |= TYPEMASK_UNIT;
    m_objectTypeId = TYPEID_UNIT;

    m_updateFlag = UPDATEFLAG_LIVING;

    m_attackTimer[BASE_ATTACK] = 0;
    m_attackTimer[OFF_ATTACK] = 0;
    m_attackTimer[RANGED_ATTACK] = 0;
    m_attackTimerFraction[BASE_ATTACK] = 0;
    m_attackTimerFraction[OFF_ATTACK] = 0;
    m_attackTimerFraction[RANGED_ATTACK] = 0;
    m_modAttackSpeedPct[BASE_ATTACK] = 1.0f;
    m_modAttackSpeedPct[OFF_ATTACK] = 1.0f;
    m_modAttackSpeedPct[RANGED_ATTACK] = 1.0f;
    m_attackDist = MELEE_RANGE;
    Zliquid_status = LIQUID_MAP_NO_WATER;

    m_extraAttacks = 0;
    countCrit = 0;
    m_canDualWield = false;

    m_sequenceIndex = 0;

    m_timeForSpline = 0;

    m_state = 0;
    m_deathState = ALIVE;

    for (uint8 i = 0; i < CURRENT_MAX_SPELL; ++i)
        m_currentSpells[i] = nullptr;

    for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
        m_SummonSlot[i].Clear();

    for (uint8 i = 0; i < MAX_GAMEOBJECT_SLOT; ++i)
        m_ObjectSlot[i].Clear();

    m_auraUpdateIterator = m_ownedAuras.end();

    m_interruptMask.fill(0);
    m_transform = 0;
    m_canModifyStats = false;

    for (uint8 i = 0; i < MAX_SPELL_IMMUNITY; ++i)
        m_spellImmune[i].clear();

    for (uint8 i = 0; i < UNIT_MOD_END; ++i)
    {
        m_auraModifiersGroup[i][BASE_PCT_EXCLUDE_CREATE] = 1.0f;
        m_auraModifiersGroup[i][BASE_VALUE] = 0.0f;
        m_auraModifiersGroup[i][BASE_PCT] = 1.0f;
        m_auraModifiersGroup[i][TOTAL_VALUE] = 0.0f;
        m_auraModifiersGroup[i][TOTAL_PCT] = 1.0f;
    }

    // implement 50% base damage from offhand
    m_auraModifiersGroup[UNIT_MOD_DAMAGE_OFFHAND][TOTAL_PCT] = 0.5f;

    for (uint8 i = 0; i < MAX_ATTACK; ++i)
    {
        m_weaponDamage[i][MINDAMAGE] = BASE_MINDAMAGE;
        m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE;
    }

    m_overrideAutoattack[BASE_ATTACK] = 0;
    m_overrideAutoattack[OFF_ATTACK]  = 0;

    for (uint8 i = 0; i < MAX_STATS; ++i)
        m_createStats[i] = 0.0f;

    m_attacking = nullptr;

    m_modMeleeHitChance = 0.0f;
    m_modRangedHitChance = 0.0f;
    m_modSpellHitChance = 0.0f;

    m_expertise = 0.0f;
    m_baseSpellCritChance = 5;

    m_CombatTimer = 0;

    m_addPower.resize(MAX_POWERS + 1, 0);
    m_everyPower.resize(MAX_POWERS + 1, 0);
    m_powerRegenTimer.resize(MAX_POWERS + 1, 0);
    m_powerCombatTimer.resize(MAX_POWERS + 1, 0);
    m_powerFraction.resize(MAX_POWERS_PER_CLASS + 1, 0.0f);

    for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
        m_threatModifier[i] = 1.0f;

    m_isSorted = true;

    for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
        m_speed_rate[i] = 1.0f;

    m_charmInfo = nullptr;
    m_reducedThreatPercent = 0;
    m_misdirectionTargetGUID.Clear();
    m_curTargetGUID.Clear();

    // remove aurastates allowing special moves
    for (uint8 i = 0; i < MAX_REACTIVE; ++i)
        m_reactiveTimer[i] = 0;

    m_cleanupDone = false;
    m_aura_is_lock = false;
    m_duringRemoveFromWorld = false;

    m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE);

    _focusSpell = nullptr;
    _lastLiquid = nullptr;
    
    _customDisplayId = 0;
    
    _isWalkingBeforeCharm = false;

    m_IsInKillingProcess = false;
    m_AINotifyScheduled = false;
    m_VisibilityUpdateScheduled = false;
    m_SpecialTarget.Clear();

    m_baseRHastRatingPct = 1.0f;
    m_baseMHastRatingPct = 1.0f;
    m_baseHastRatingPct = 1.0f;
    m_modForHolyPowerSpell = 0;

    for (uint8 i = 0; i < MAX_COMBAT_RATING; i++)
        m_baseRatingValue[i] = 0;

    m_comboSavePoints = 0;

    _aiAnimKitId = 0;
    _movementAnimKitId = 0;
    _meleeAnimKitId = 0;

    ClearMirrorImageData();

    memset(m_modAuras, NULL, sizeof(m_modAuras));
    memset(m_auraTypeCount, 0, sizeof(m_auraTypeCount));

    m_powerCost.assign(MAX_POWERS + 1, 0);

    waitOnSeat = false;
    _mount = nullptr;
    needUpdateDynamicFlags = false;

    _targetCount = 0;
    _castCount = 0;
    _eventCount = 0;
    _functionCount = 0;
}

////////////////////////////////////////////////////////////
// Methods of class GlobalCooldownMgr
uint32 GlobalCooldownMgr::GetGlobalCooldownEnd(SpellInfo const * spellInfo)
{
    GlobalCooldownList::const_iterator itr = m_GlobalCooldowns.find(spellInfo->Categories.StartRecoveryCategory);
    if (itr != m_GlobalCooldowns.end() && itr->second.duration)
        return itr->second.duration + itr->second.cast_time;

    return 0;
}

bool GlobalCooldownMgr::HasGlobalCooldown(SpellInfo const* spellInfo)
{
    GlobalCooldownList::const_iterator itr = m_GlobalCooldowns.find(spellInfo->Categories.StartRecoveryCategory);
    return itr != m_GlobalCooldowns.end() && itr->second.duration && getMSTimeDiff(itr->second.cast_time, getMSTime()) < itr->second.duration;
}

void GlobalCooldownMgr::AddGlobalCooldown(SpellInfo const* spellInfo, uint32 gcd, uint32 time/* = 0*/)
{
    m_GlobalCooldowns[spellInfo->Categories.StartRecoveryCategory] = GlobalCooldown(gcd, time ? time : (getMSTime() - 120));
}

void GlobalCooldownMgr::CancelGlobalCooldown(SpellInfo const* spellInfo)
{
    m_GlobalCooldowns[spellInfo->Categories.StartRecoveryCategory].duration = 0;
}

////////////////////////////////////////////////////////////
// Methods of class Unit
Unit::~Unit()
{
    // set current spells as deletable
    for (uint8 i = 0; i < CURRENT_MAX_SPELL; ++i)
        if (m_currentSpells[i])
        {
            m_currentSpells[i]->SetReferencedFromCurrent(false);
            m_currentSpells[i] = nullptr;
        }

    _DeleteRemovedAuras();

    while (!m_sharedVision.empty())
    {
        if (auto player = m_sharedVision.front())
        {
            m_sharedVision.pop_front();

            if (player->IsSpectator() && player->GetSpectateFrom())
                player->SetViewpoint(player->GetSpectateFrom(), false);

            //player->SetViewpoint(this, false);
        }
    }

    delete m_charmInfo;
    delete movespline;

    memset(m_modAuras, NULL, sizeof(m_modAuras));
    for (AuraEffectListMap::iterator iter = m_modMapAuras.begin(); iter != m_modMapAuras.end(); ++iter)
        delete iter->second;
    m_modMapAuras.clear();
}

void Unit::Update(uint32 p_time)
{
    _castCount = 0;
    _targetCount = 0;

    // WARNING! Order of execution here is important, do not change.
    // Spells must be processed with event system BEFORE they go to _UpdateSpells.
    // Or else we may have some SPELL_STATE_FINISHED spells stalled in pointers, that is bad.
    if (!m_cleanupDone) // May be crashed
    {
        _eventCount = m_Events.Size();
        _functionCount = m_Functions.Size() + m_CombatFunctions.Size();

        m_Events.Update(p_time);
        m_Functions.Update(p_time);
        m_CombatFunctions.Update(p_time);
    }

    if (!IsInWorld() || IsDelete() || IsPreDelete())
        return;

    // This is required for GetDamageTakenInPastSecs()
    damageTrackingTimer_ -= p_time;

    while (damageTrackingTimer_ <= 0)
    {
        std::copy(npcDamageTaken_.begin(), npcDamageTaken_.end() - 1, npcDamageTaken_.begin() + 1);
        std::copy(playerDamageTaken_.begin(), playerDamageTaken_.end() - 1, playerDamageTaken_.begin() + 1);

        npcDamageTaken_[0] = 0;
        playerDamageTaken_[0] = 0;

        damageTrackingTimer_ += DAMAGE_TRACKING_UPDATE_INTERVAL;
    }

    _UpdateSpells(p_time);

    // If this is set during update SetCantProc(false) call is missing somewhere in the code
    // Having this would prevent spells from being proced, so let's crash
    //ASSERT(!m_procDeep);
    if (m_procDeep)
        return;

    if (CanHaveThreatList() && getThreatManager().isNeedUpdateToClient(p_time))
        SendThreatUpdate();

    // update combat timer only for players and pets (only pets with PetAI)
    if (isInCombat() && (IsPlayer() || (ToCreature()->isPet() && IsControlledByPlayer())))
    {
        // Check UNIT_STATE_MELEE_ATTACKING or UNIT_STATE_CHASE (without UNIT_STATE_FOLLOW in this case) so pets can reach far away
        // targets without stopping half way there and running off.
        // These flags are reset after target dies or another command is given.
        if (m_CombatTimer <= p_time) // m_CombatTimer set at aura start and it will be freeze until aura removing
        {
            if (m_HostileRefManager.isEmpty())
                ClearInCombat();
            else if (!GetMap()->IsDungeon())
            {
                m_CombatTimer = 5000;

                HostileRefManager& refManager = getHostileRefManager();
                HostileReference* ref = refManager.getFirst();
                float dist{};

                while (ref)
                {
                    auto unit = ref->getSource()->getOwner();
                    ref = ref->next();
                    if (unit)
                    {
                        if (auto creature = unit->ToCreature())
                        {
                            dist = std::max(creature->GetAttackDistance(this), sWorld->getFloatConfig(CONFIG_THREAT_RADIUS)) + creature->m_CombatDistance;
                            if (GetDistance(creature) > dist || creature->isTrainingDummy())
                                refManager.deleteReference(creature);
                        }
                    }
                }
                if (m_HostileRefManager.isEmpty())
                {
                    ClearInCombat();
                    m_CombatTimer = 0;
                }
            }
            else
            {
                m_CombatTimer = 100;
            }
        }
        else
            m_CombatTimer -= p_time;
    }
    else if (isInCombat() && IsCreature() && ToCreature()->GetCreatureType() == CREATURE_TYPE_CRITTER)
    {
        if (m_CombatTimer <= p_time)
        {
            CombatStop();
            DeleteThreatList();
            m_CombatTimer = 0;
        }
        else
            m_CombatTimer -= p_time;
    }

    // not implemented before 3.0.2
    for (uint8 i = 0; i < MAX_ATTACK; i++)
    {
        SetAttackTimerFraction(WeaponAttackType(i), 0);

        if (uint32 attTimer = getAttackTimer(WeaponAttackType(i)))
        {
            uint32 timer = 0;

            if (p_time > attTimer)
            {
                SetAttackTimerFraction(WeaponAttackType(i), p_time - attTimer);
            }
            else
            {
                timer = attTimer - p_time;
            }
            setAttackTimer(WeaponAttackType(i), timer);
        }
    }

    // update abilities available only for fraction of time
    UpdateReactives(p_time);

    if (isAlive())
    {
        ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, HealthBelowPct(20));
        ModifyAuraState(AURA_STATE_HEALTHLESS_25_PERCENT, HealthBelowPct(25));
        ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, HealthBelowPct(35));
        ModifyAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, HealthAbovePct(75));
    }

    UpdateSplineMovement(p_time);
    i_motionMaster.UpdateMotion(p_time);
}

void Unit::SetAttackTimerFraction(WeaponAttackType type, uint32 time)
{
    m_attackTimerFraction[type] = time;
}

uint32 Unit::GetAttackTimerFraction(WeaponAttackType type) const
{
    return m_attackTimerFraction[type];
}

bool Unit::haveOffhandWeapon() const
{
    if (IsPlayer())
        return ToPlayer()->GetWeaponForAttack(OFF_ATTACK, true) != nullptr;
    return m_canDualWield;
}

void Unit::MonsterSmothMoveWithSpeed(Position pos, float speed, bool generatePath /*= false*/, bool forceDestination /*= false*/)
{
    Movement::MoveSplineInit init(*this);
    init.MoveTo(pos, generatePath, forceDestination);
    init.SetVelocity(speed);
    init.SetSmooth();
    init.Launch();
}

void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath, bool forceDestination)
{
    Movement::MoveSplineInit init(*this);
    init.MoveTo(x, y, z, generatePath, forceDestination);
    init.SetVelocity(speed);
    init.Launch();
}

uint32 const positionUpdateDelay = 400;

void Unit::UpdateSplineMovement(uint32 t_diff)
{
    if (movespline->Finalized())
    {
        waitOnSeat = false;
        return;
    }

    // Prevent crash on empty spline
    if (!movespline->Initialized())
    {
        movespline->_Interrupt();
        DisableSpline();
        waitOnSeat = false;
        return;
    }

    movespline->updateState(t_diff);
    bool arrived = movespline->Finalized();

    if (arrived)
    {
        waitOnSeat = false;
        DisableSpline();
    }

    m_movesplineTimer.Update(t_diff);
    if (m_movesplineTimer.Passed() || arrived)
        UpdateSplinePosition();

    /*_flightSplineSyncTimer.Update(t_diff);
    if (_flightSplineSyncTimer.Passed())
    {
        float percent = 1.0f;
        float totalTime = movespline->GetSpline().length();
        if (totalTime > 0.0f)
            percent = (float)movespline->GetTimePassed() / totalTime;

        SendFlightSplineSync(percent);
        _flightSplineSyncTimer.Reset(std::chrono::seconds(5).count());
    }*/
}

void Unit::UpdateSplineSpeed()
{
    if (movespline->Finalized())
        return;

    // Prevent crash on empty spline
    if (!movespline->Initialized())
        return;

    movespline->UpdateVelocity(this);
}

void Unit::UpdateSplinePosition(bool stop/* = false*/)
{
    if (waitOnSeat) // Prvent bug if passanger in progress on seat, waite while passengare is full seat
        return;

    m_movesplineTimer.Reset(HasUnitState(UNIT_STATE_CHARGING | UNIT_STATE_LONG_JUMP) ? 50 : positionUpdateDelay);
    Movement::Location loc = movespline->ComputePosition();
    if (GetTransGUID() && !movespline->isTransportExit())
    {

        Position& pos = m_movementInfo.transport.Pos;

        pos.m_positionX = loc.x;
        pos.m_positionY = loc.y;
        pos.m_positionZ = loc.z;
        pos.SetOrientation(loc.orientation);

        if (GetVehicleBase())
        {
            if (TransportBase* transport = GetDirectTransport())
                transport->CalculatePassengerPosition(loc.x, loc.y, loc.z, &loc.orientation);
        }
        else if (movespline->isTransportEnter())
        {
            if (Transport* transport = GetTransport())
                transport->CalculatePassengerPosition(loc.x, loc.y, loc.z, &loc.orientation);
        }
        else // if we have transport guid, but not have transport, not set transport position to real
            loc = movespline->ComputePosition();
    }

    if (HasUnitState(UNIT_STATE_CANNOT_TURN))
        loc.orientation = GetOrientation();

    UpdatePosition(loc.x, loc.y, loc.z, loc.orientation, false, stop);
}

void Unit::DisableSpline(bool clearFlags)
{
    if (clearFlags)
        m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_FORWARD);
    movespline->_Interrupt();
}

void Unit::resetAttackTimer(WeaponAttackType type)
{
    uint32 baseAttackTime = RoundingFloatValue(GetAttackTime(type) * m_modAttackSpeedPct[type]);
    uint32 fraction = GetAttackTimerFraction(type);

    if (baseAttackTime < fraction)
    {
        baseAttackTime = 0;
    }
    else
    {
        baseAttackTime -= fraction;
    }

    SetAttackTimerFraction(type, 0);
    m_attackTimer[type] = baseAttackTime;
}

bool Unit::IsWithinCombatRange(const Unit* obj, float dist2compare) const
{
    if (!obj || !IsInMap(obj) || !InSamePhase(obj))
        return false;

    float dx = GetPositionX() - obj->GetPositionX();
    float dy = GetPositionY() - obj->GetPositionY();
    float dz = GetPositionZH() - obj->GetPositionZH();
    float distsq = dx * dx + dy * dy + dz * dz;

    float sizefactor = GetCombatReach() + obj->GetCombatReach();
    float maxdist = dist2compare + sizefactor;

    return distsq < maxdist * maxdist;
}

bool Unit::IsWithinMeleeRange(const Unit* obj, float dist) const
{
    if (!obj || !IsInMap(obj) || !InSamePhase(obj))
        return false;

    float dx = GetPositionX() - obj->GetPositionX();
    float dy = GetPositionY() - obj->GetPositionY();
    float dz = GetPositionZH() - obj->GetPositionZH();
    float distsq = dx*dx + dy*dy + dz*dz;

    float sizefactor = GetMeleeReach() + obj->GetMeleeReach();
    float maxdist = dist + sizefactor;

    return distsq < maxdist * maxdist;
}

void Unit::UpdateInterruptMask()
{
    m_interruptMask.fill(0);
    for (AuraApplicationList::const_iterator itr = m_interruptableAuras.begin(); itr != m_interruptableAuras.end(); ++itr)
        for (std::size_t i = 0; i < m_interruptMask.size(); ++i)
            m_interruptMask[i] |= (*itr)->GetBase()->GetSpellInfo()->AuraInterruptFlags[i];

    if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
        if (spell->getState() == SPELL_STATE_CASTING)
            for (std::size_t i = 0; i < m_interruptMask.size(); ++i)
                m_interruptMask[i] |= spell->m_spellInfo->ChannelInterruptFlags[i];
}

void Unit::AddInterruptMask(std::array<uint32, 2> const& mask)
{
    for (std::size_t i = 0; i < m_interruptMask.size(); ++i)
        m_interruptMask[i] |= mask[i];
}

bool Unit::HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint32 familyFlags) const
{
    if (!HasAuraType(auraType))
        return false;

    if (AuraEffectList const* auras = GetAuraEffectsByType(auraType))
        for (AuraEffectList::const_iterator itr = auras->begin(); itr != auras->end(); ++itr)
            if (SpellInfo const* iterSpellProto = (*itr)->GetSpellInfo())
                if (iterSpellProto->ClassOptions.SpellClassSet == familyName && iterSpellProto->ClassOptions.SpellClassMask[0] & familyFlags)
                    return true;
    return false;
}

bool Unit::HasCrowdControlAuraType(AuraType type, uint32 excludeAura) const
{
    if (AuraEffectList const* auras = GetAuraEffectsByType(type))
        for (AuraEffectList::const_iterator itr = auras->begin(); itr != auras->end(); ++itr)
            if ((!excludeAura || excludeAura != (*itr)->GetSpellInfo()->Id) && //Avoid self interrupt of channeled Crowd Control spells like Seduction
                ((*itr)->GetSpellInfo()->HasAttribute(SPELL_ATTR0_BREAKABLE_BY_DAMAGE) || (*itr)->GetSpellInfo()->HasAuraInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE)))
                return true;
    return false;
}

bool Unit::HasCrowdControlAura(Unit* excludeCasterChannel) const
{
    uint32 excludeAura = 0;
    if (Spell* currentChanneledSpell = excludeCasterChannel ? excludeCasterChannel->GetCurrentSpell(CURRENT_CHANNELED_SPELL) : nullptr)
        excludeAura = currentChanneledSpell->GetSpellInfo()->Id; //Avoid self interrupt of channeled Crowd Control spells like Seduction

    return ( HasCrowdControlAuraType(SPELL_AURA_MOD_CONFUSE, excludeAura)
            //|| HasCrowdControlAuraType(SPELL_AURA_MOD_FEAR, excludeAura)
            //|| HasCrowdControlAuraType(SPELL_AURA_MOD_FEAR_2, excludeAura)
            || HasCrowdControlAuraType(SPELL_AURA_MOD_STUN, excludeAura)
            /*|| HasCrowdControlAuraType(SPELL_AURA_MOD_ROOT, excludeAura)
            || HasCrowdControlAuraType(SPELL_AURA_TRANSFORM, excludeAura)*/);
}

void Unit::DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb, SpellInfo const* spellProto, bool addMythicMod)
{
    if (!victim || !victim->isAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->IsCreature() && victim->ToCreature()->IsInEvadeMode()))
    {
        if (absorb)
            *absorb += damage;
        damage = 0;
    }
    else if (victim && victim->isAlive())
    {
        if (isPet())
            damage *= GetTotalAuraMultiplier(SPELL_AURA_PET_DAMAGE_MULTI);

        damage = GetDamageFromLevelScale(victim, damage);

        //Challenge Mythic+
        if (spellProto && spellProto->ClassOptions.SpellClassSet == SPELLFAMILY_GENERIC && victim->IsPlayer())
        {
            //Temp hack
            bool valid = false;

            switch (spellProto->Id)
            {
                case 196517: // Swirling Scythe
                    valid = true;
                    break;
                case 209862: // Volcanic Plume
                    return;
                default:
                    break;
            }

            if (!valid)
            {
                if (IsPlayer() && (spellProto->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS) || spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)))
                    return;

                if (IsCreature() && !ToCreature()->isTrigger())
                    return;
            }

            if (auto progress = sScenarioMgr->GetScenario(GetInstanceId()))
            {
                auto const& challenge = progress->GetChallenge();
                if (!challenge)
                    return;

                GtChallengeModeDamageEntry const* gtDamage = sChallengeModeDamageTable.GetRow(challenge->GetChallengeLevel());
                if (!gtDamage)
                    return;

                float modDamage = gtDamage->Scalar;

                if (IsCreature())
                {
                    if (ToCreature()->GetAnyOwner())
                    {
                        if (challenge->HasAffix(Affixes::Tyrannical))
                            modDamage *= 1.15f;
                    }
                    else if (challenge->HasAffix(Affixes::Fortified))
                        modDamage *= 1.3f;
                }
                damage *= modDamage;
            }
        }
    }
}

uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss)
{
    if (victim->IsAIEnabled)
    {
        victim->GetAI()->DamageTaken(this, damage, damagetype);
        if (InstanceScript* instance = GetInstanceScript())
            if (instance->CheckDamageManager())
                instance->UpdateDamageManager(victim->GetGUID(), damage);
    }

    if (IsAIEnabled)
        GetAI()->DamageDealt(victim, damage, damagetype);

    // special temporary logs for find and ban bagusers and exploiters -> Anticheat
    auto check = [&]()
    {
        if (IsPlayer() || m_ControlledByPlayer)
        {
            if (Map* map = GetMap())
            {
                // need for exclude non-legion heroic/mythic raids and some other
                if ((map->IsMythicRaid() || map->IsHeroicRaid()) && (map->GetId() == 1530 || map->GetId() == 1676))
                    return true;

                // keystone 10+
                if (map->isChallenge())
                    if (InstanceScript* instance = GetInstanceScript())
                        if (Challenge* c = instance->GetChallenge())
                            if (c->GetChallengeLevel() >= 10)
                                return true;
            }
        }

        return false;
    };

    if (check())
    {
        if (Player* plr = ToPlayer())
        {
            if (damage > 3300000)
                plr->SpecialDamageLogs(victim, damage, damagetype, spellProto);
        }
        else
        {
            if (Unit* ownerUnit = GetAnyOwner())
            {
                if (Player* owner = ownerUnit->ToPlayer())
                {
                    if (damage > 400000)
                    {
                        std::string damageDesc = damage >= 800000 ? "cheat" : "suspicious";

                        sLog->outWarden("AnticheatSpecial: Controlled unit %u (ownerName: %s, ownerGUID: %u) dealed %s damage %u by spell %u at victim with entry %u (healthPct %f) on map %u", GetEntry(), owner->GetName(), owner->GetGUIDLow(),
                            damageDesc.c_str(), damage, spellProto ? spellProto->Id : 0, victim->GetEntry(), victim->GetHealthPct(), GetMapId());
                    }
                }
            }
        }
    }

    if (!IsPlayer())
        victim->npcDamageTaken_[0] += damage + (cleanDamage ? cleanDamage->absorbed_damage : 0);
    else
    {
        victim->playerDamageTaken_[0] += damage + (cleanDamage ? cleanDamage->absorbed_damage : 0);
        // victim->m_playerTotalDamage[GetGUID()] += damage;

        if (GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == SPEC_HUNTER_MARKSMAN)
        {
            if (!victim->HasAura(236641, GetGUID()))
                AddAura(236641, victim);
        }
    }

    if (victim->IsPlayer())
    {
        if (victim->ToPlayer()->GetCommandStatus(CHEAT_GOD))
            return 0;
        
        // Signal to pets that their owner was attacked
        Pet* pet = victim->ToPlayer()->GetPet();

        if (pet && pet->isAlive())
            pet->AI()->OwnerDamagedBy(this);
    }

    if (!victim->IsPlayer() && !IsPlayer() && !victim->isAnySummons() && !isAnySummons() && victim->ToCreature()->IsNoDamage() && victim->HealthBelowPct(85))
        damage = 0;

    if (damagetype != NODAMAGE)
    {
        // interrupting auras with AURA_INTERRUPT_FLAG_DAMAGE before checking !damage (absorbed damage breaks that type of auras)
        if (spellProto)
        {
            if (!(spellProto->HasAttribute(SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS)))
                if (damagetype == DOT && damage != 0 || damagetype != DOT)
                    victim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, spellProto->Id, spellProto->Id);
        }
        else if (damagetype != SELF_DAMAGE)
            victim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE);

        // We're going to call functions which can modify content of the list during iteration over it's elements
        // Let's copy the list so we can prevent iterator invalidation
        // copy damage to casters of this aura
        if (AuraEffectList const* vCopyDamageCopy = victim->GetAuraEffectsByType(SPELL_AURA_SHARE_DAMAGE_PCT))
        for (AuraEffectList::const_iterator i = vCopyDamageCopy->begin(); i != vCopyDamageCopy->end(); ++i)
        {
            // Check if aura was removed during iteration - we don't need to work on such auras
            if (!((*i)->GetBase()->IsAppliedOnTarget(victim->GetGUID())))
                continue;
            // check damage school mask
            if (((*i)->GetMiscValue() & damageSchoolMask) == 0)
                continue;

            Unit* shareDamageTarget = (*i)->GetCaster();
            if (!shareDamageTarget)
                continue;
            SpellInfo const* spell = (*i)->GetSpellInfo();

            uint32 share = CalculatePct(damage, (*i)->GetAmount());

            // TODO: check packets if damage is done by victim, or by attacker of victim
            DealDamageMods(shareDamageTarget, share, nullptr);
            DealDamage(shareDamageTarget, share, nullptr, NODAMAGE, spell->GetSchoolMask(), spell, false);
        }
    }

    // Rage from Damage made (only from direct weapon damage)
    if (cleanDamage && damagetype == DIRECT_DAMAGE && this != victim && getPowerType() == POWER_RAGE)
    {
        if (cleanDamage->hitOutCome == MELEE_HIT_NORMAL || cleanDamage->hitOutCome == MELEE_HIT_CRIT)
        {
            if (Player* player = ToPlayer())
            {
                float rage = 3.5f;

                uint8 wSlot = EQUIPMENT_SLOT_MAINHAND;
                float mod = 2.f;

                if (cleanDamage->attackType == OFF_ATTACK)
                {
                    wSlot = EQUIPMENT_SLOT_OFFHAND;
                    mod = 4.f;
                }

                if (Item* weapon = player->GetItemByPos(INVENTORY_SLOT_BAG_0, wSlot))
                {
                    if (ItemTemplate const* temp = weapon->GetTemplate())
                        rage = rage * float(temp->GetDelay()) / 1000.f / mod;
                }

                if (cleanDamage->hitOutCome == MELEE_HIT_CRIT)
                    AddPct(rage, 50.f);

                RewardRage(rage, true);
            }
        }
    }

    if (damagetype != NODAMAGE && (damage || (cleanDamage && cleanDamage->absorbed_damage) ))
    {
        if (victim != this && victim->IsPlayer()) // does not support creature push_back
        {
            if (damagetype != DOT || (spellProto && spellProto->IsChanneled()))
            {
                if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL])
                    if (spell->getState() == SPELL_STATE_PREPARING)
                    {
                        uint32 interruptFlags = spell->m_spellInfo->InterruptFlags;
                        if (interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG)
                            victim->InterruptNonMeleeSpells(false);
                    }
            }
        }
    }
    
    if (IsPlayer() && damage > 0 && (!spellProto || spellProto->Id != 143924))
    {
        if (float heal = CalculatePct(damage, static_cast<float>(GetFloatValue(PLAYER_FIELD_LIFESTEAL))))
        {
            AddDelayedEvent(10, [heal, this]() -> void
            {
                if (!this)
                    return;
                CastCustomSpell(this, 143924, &heal, nullptr, nullptr, true);
            });
        }
    }

    uint32 realDamage = damage + (cleanDamage ? cleanDamage->absorbed_damage : 0);

    if (IsPlayer() && this != victim)
    {
        Player* killer = ToPlayer();

        // in bg, count dmg if victim is also a player
        if (victim->IsPlayer())
            if (Battleground* bg = killer->GetBattleground())
                bg->UpdatePlayerScore(killer, SCORE_DAMAGE_DONE, realDamage);

        killer->UpdateAchievementCriteria(CRITERIA_TYPE_DAMAGE_DONE, realDamage, 0, 0, victim);
        killer->UpdateAchievementCriteria(CRITERIA_TYPE_HIGHEST_HIT_DEALT, realDamage);
    }
    else if (IsCreature() && this != victim && isPet())
    {
        if (GetOwner() && GetOwner()->ToPlayer())
        {
            Player* killerOwner = GetOwner()->ToPlayer();

            if (victim->IsPlayer())
                if (Battleground* bg = killerOwner->GetBattleground())
                    bg->UpdatePlayerScore(killerOwner, SCORE_DAMAGE_DONE, realDamage);
        }
    }

    if (!damage)
    {
        // Rage from absorbed damage
        if (cleanDamage && cleanDamage->absorbed_damage && victim->getPowerType() == POWER_RAGE)
            victim->RewardRage(cleanDamage->absorbed_damage + cleanDamage->mitigated_damage, false);

        return 0;
    }

    // TC_LOG_DEBUG(LOG_FILTER_UNITS, "DealDamageStart");

    uint64 health = victim->GetHealth(this);
    // TC_LOG_INFO(LOG_FILTER_UNITS, "deal dmg:%i to health:%u to %s", damage, health, GetGUID().ToString().c_str());

    // duel ends when player has 1 or less hp
    bool duel_hasEnded = false;
    bool duel_wasMounted = false;
    if (victim->IsPlayer() && victim->ToPlayer()->duel && damage >= (health-1))
    {
        // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
        if (victim->ToPlayer()->duel->opponent == GetGUID() || victim->ToPlayer()->duel->opponent == GetOwnerGUID())
            damage = health - 1;

        duel_hasEnded = true;
    }
    else if (victim->IsVehicle() && damage >= (health-1) && victim->GetCharmer() && victim->GetCharmer()->IsPlayer())
    {
        Player* victimRider = victim->GetCharmer()->ToPlayer();

        if (victimRider && victimRider->duel && victimRider->duel->isMounted)
        {
            // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
            if (victimRider->duel->opponent == GetGUID() || victimRider->duel->opponent == GetCharmerGUID())
                damage = health - 1;

            duel_wasMounted = true;
            duel_hasEnded = true;
        }
    }

    if (victim->IsPlayer())
        victim->ToPlayer()->UpdateAchievementCriteria(CRITERIA_TYPE_HIGHEST_HIT_RECEIVED, damage);
    else if (!victim->IsControlledByPlayer() || victim->IsVehicle())
    {
        if (!victim->ToCreature()->hasLootRecipient())
            victim->ToCreature()->SetLootRecipient(this);

        if (IsControlledByPlayer())
            victim->ToCreature()->LowerPlayerDamageReq(health < damage ?  health : damage);
    }

    if (health <= damage)
    {
        if (victim->IsPlayer() && victim != this)
        {
            victim->ToPlayer()->UpdateAchievementCriteria(CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED, health);

            // call before auras are removed
            if (Player* killer = GetCharmerOrOwnerPlayerOrPlayerItself())
                killer->UpdateAchievementCriteria(CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, 0, victim);
        }

       // if (spellProto && (spellProto->HasAttribute(SPELL_ATTR9_UNK28)))
       //     victim->SetHealth(1);
       // else
            Kill(victim, durabilityLoss, spellProto ? spellProto : nullptr);
    }
    else
    {
        if (victim->IsPlayer())
            victim->ToPlayer()->UpdateAchievementCriteria(CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED, damage);

        victim->ModifyHealth(-static_cast<int32>(damage), this);

        if (damagetype == DIRECT_DAMAGE || damagetype == SPELL_DIRECT_DAMAGE)
            victim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_DIRECT_DAMAGE, spellProto ? spellProto->Id : 0, spellProto ? spellProto->Id : 0);

        if (!victim->IsPlayer())
            victim->AddThreat(this, float(damage), damageSchoolMask, spellProto);
        else                                                // victim is a player
        {
            // random durability for items (HIT TAKEN)
            if (roll_chance_f(sWorld->getRate(RATE_DURABILITY_LOSS_DAMAGE)))
                victim->ToPlayer()->DurabilityPointLossForEquipSlot(EquipmentSlots(urand(0, EQUIPMENT_SLOT_END - 1)));
        }

        // Rage from damage received
        if (this != victim && victim->getPowerType() == POWER_RAGE)
            victim->RewardRage(damage + (cleanDamage ? (cleanDamage->absorbed_damage + cleanDamage->mitigated_damage) : 0), false);

        if (IsPlayer())
        {
            // random durability for items (HIT DONE)
            if (roll_chance_f(sWorld->getRate(RATE_DURABILITY_LOSS_DAMAGE)))
                ToPlayer()->DurabilityPointLossForEquipSlot(EquipmentSlots(urand(0, EQUIPMENT_SLOT_END - 1)));
        }

        if (damagetype != NODAMAGE && damage)
        {
            if (victim != this && victim->IsPlayer() && (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->IsTargetingArea() || (cleanDamage ? cleanDamage->isAoe : false))))
            {
                if (damagetype != DOT)
                {
                    if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL])
                        if (spell->getState() == SPELL_STATE_PREPARING)
                        {
                            uint32 interruptFlags = spell->m_spellInfo->InterruptFlags;
                            if (interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG)
                                victim->InterruptNonMeleeSpells(false);
                            else if (interruptFlags & SPELL_INTERRUPT_FLAG_PUSH_BACK)
                                spell->Delayed();
                        }
                }
            }
        }

        // last damage from duel opponent
        if (duel_hasEnded)
        {
            Player* he = duel_wasMounted ? victim->GetCharmer()->ToPlayer() : victim->ToPlayer();

            ASSERT(he && he->duel);

            auto dueler = ObjectAccessor::FindPlayer(he->duel->opponent);

            if (duel_wasMounted) // In this case victim==mount
                victim->SetHealth(1);
            else
                he->SetHealth(1);

            he->CombatStopWithPets(true);
            dueler->CombatStopWithPets(true);

            he->CastSpell(he, 7267, true); // beg
            he->AddDelayedEvent(1000, [he]() -> void
            {
                he->DuelComplete(DUEL_FINISHED);
            });
        }
    }

    // TC_LOG_DEBUG(LOG_FILTER_UNITS, "DealDamageEnd returned %d damage", damage);

    return damage;
}

uint32 Unit::CalcStaggerDamage(uint32 damage, SpellSchoolMask damageSchoolMask, SpellInfo const* spellInfo, bool full)
{
    if (IsPlayer())
    {
        uint32 staggerBleed  = 124255;
        uint32 staggerGreen  = 124275;
        uint32 staggerYellow = 124274;
        uint32 staggerRed    = 124273;
        uint32 staggerDebuf  = 124275;
        uint32 fermentation  = 243943;
        float stagger = 0.0f;
        float bp0 = 0;
        float bp1 = 0;
        bool hasBonusHast = HasAura(196737);
        bool hasFermentation = HasAura(205147);
        float bp2 = hasBonusHast ? 5.f : 0.f;

		if (Player* plr = ToPlayer())
		{
			uint64 health = GetHealth(this);
			if (plr->duel && damage >= health)
			{
				//Remove stagger damage when duel is finished
				RemoveAurasDueToSpell(staggerBleed);
				RemoveAurasDueToSpell(staggerGreen);
				RemoveAurasDueToSpell(staggerYellow);
				RemoveAurasDueToSpell(staggerRed);
				RemoveAurasDueToSpell(fermentation);
				return health - 1;
			}
		}

        if (spellInfo && spellInfo->Id == staggerBleed)
        {
            if (Aura* staggerBleedAura = GetAura(staggerBleed))
            {
                AuraEffect* eff0 = staggerBleedAura->GetEffect(EFFECT_0);
                AuraEffect* eff1 = staggerBleedAura->GetEffect(EFFECT_1);

                if (staggerBleedAura->GetCustomData() > 0)
                {
                    staggerBleedAura->ModCustomData(-1);
                    damage = 0;
                }
                else
                    bp0 = eff0->GetAmount();

                bp1 = eff1->GetAmount() - damage;

                if (bp1 <= 0)
                {
                    RemoveAurasDueToSpell(staggerBleed);
                    RemoveAurasDueToSpell(fermentation);
                    return damage;
                }
                eff1->SetAmount(bp1);

                if (bp1 > int32(CountPctFromMaxHealth(60)))
                {
                    staggerDebuf = staggerRed;
                    if (hasBonusHast) bp2 = 15.f;
                }
                else if (bp1 > int32(CountPctFromMaxHealth(30)))
                {
                    staggerDebuf = staggerYellow;
                    if (hasBonusHast) bp2 = 10.f;
                }
            }

            if (Aura* staggerGreenAura = GetAura(staggerGreen))
            {
                if (staggerDebuf != staggerGreen || bp1 <= 0.f)
                {
                    int32 olddur = staggerGreenAura->GetDuration();
                    RemoveAurasDueToSpell(staggerGreen);
                    CastCustomSpell(this, staggerDebuf, &bp0, &bp1, &bp2, true);

                    if (Aura* staggerDebufAura = GetAura(staggerDebuf))
                    {
                        staggerDebufAura->SetDuration(olddur);
                        staggerDebufAura->SetMaxDuration(olddur);
                    }

                    if (hasFermentation)
                    {
                        float fbp0 = 10.f;
                        float fbp1 = -5.f;
                        RemoveAurasDueToSpell(fermentation);
                        CastCustomSpell(this, fermentation, &fbp0, &fbp1, nullptr, true);
                        if (Aura* fermentationAura = GetAura(fermentation))
                        {
                            fermentationAura->SetDuration(olddur);
                            fermentationAura->SetMaxDuration(olddur);
                        }
                    }
                }
                else
                    staggerGreenAura->GetEffect(EFFECT_1)->SetAmount(bp1);
            }
            else if (Aura* staggerYellowAura = GetAura(staggerYellow))
            {
                if (staggerDebuf != staggerYellow || bp1 <= 0)
                {
                    int32 olddur = staggerYellowAura->GetDuration();
                    RemoveAurasDueToSpell(staggerYellow);
                    CastCustomSpell(this, staggerDebuf, &bp0, &bp1, &bp2, true);

                    if (Aura* staggerDebufAura = GetAura(staggerDebuf))
                    {
                        staggerDebufAura->SetDuration(olddur);
                        staggerDebufAura->SetMaxDuration(olddur);
                    }

                    if (hasFermentation)
                    {
                        float fbp0 = 20.f;
                        float fbp1 = -10.f;
                        RemoveAurasDueToSpell(fermentation);
                        CastCustomSpell(this, fermentation, &fbp0, &fbp1, nullptr, true);
                        if (Aura* fermentationAura = GetAura(fermentation))
                        {
                            fermentationAura->SetDuration(olddur);
                            fermentationAura->SetMaxDuration(olddur);
                        }
                    }
                }
                else
                    staggerYellowAura->GetEffect(EFFECT_1)->SetAmount(bp1);
            }
            else if (Aura* staggerRedAura = GetAura(staggerRed))
            {
                if (staggerDebuf != staggerRed || bp1 <= 0)
                {
                    int32 olddur = staggerRedAura->GetDuration();
                    RemoveAurasDueToSpell(staggerRed);
                    CastCustomSpell(this, staggerDebuf, &bp0, &bp1, &bp2, true);

                    if (Aura* staggerDebufAura = GetAura(staggerDebuf))
                    {
                        staggerDebufAura->SetDuration(olddur);
                        staggerDebufAura->SetMaxDuration(olddur);
                    }

                    if (hasFermentation)
                    {
                        float fbp0 = 30.f;
                        float fbp1 = -15.f;
                        RemoveAurasDueToSpell(fermentation);
                        CastCustomSpell(this, fermentation, &fbp0, &fbp1, nullptr, true);
                        if (Aura* fermentationAura = GetAura(fermentation))
                        {
                            fermentationAura->SetDuration(olddur);
                            fermentationAura->SetMaxDuration(olddur);
                        }
                    }
                }
                else
                    staggerRedAura->GetEffect(EFFECT_1)->SetAmount(bp1);
            }
        }
        else if (damage > 0)
        {
            if (Aura* Stance_of_the_Sturdy_OxAura = GetAura(115069)) // Stagger
            {
                float forStagger = float(Stance_of_the_Sturdy_OxAura->GetSpellInfo()->Effects[EFFECT_8]->BasePoints);
                if (damageSchoolMask != SPELL_SCHOOL_MASK_NORMAL)
                {
                    if (AuraEffect const* aurEff = Stance_of_the_Sturdy_OxAura->GetEffect(EFFECT_0))
                        stagger += CalculatePct(forStagger, aurEff->GetAmount());
                }
                else
                    stagger += forStagger;

                if (AuraEffect const* Fortifying_Brew = GetAuraEffect(120954, EFFECT_4)) // Fortifying Brew
                    stagger += float(Fortifying_Brew->GetAmount());

                if (AuraEffect const* auraEffect = GetAuraEffect(196737, EFFECT_0)) // High Tolerance
                    stagger += float(auraEffect->GetAmount());

                if (AuraEffect const* auraEffect = GetAuraEffect(215479, EFFECT_0)) // Ironskin Brew
                    stagger += float(auraEffect->GetAmount());
            }
            else
                return damage;

            int32 _duration = 10000;
            if (AuraEffect const* auraEffect = GetAuraEffect(208881, EFFECT_0)) // Gift of Veri'thas
                _duration += CalculatePct(_duration, auraEffect->GetAmount());

            int32 _tickCount = _duration / 500;
            if (stagger > 100.0f || full)
                stagger = 100.0f;

            bp0 = RoundingFloatValue(float(CalculatePct(damage, stagger) / _tickCount));
            bp1 = bp0 * _tickCount;

            damage -= bp1;

            if (Aura* staggerBleedAura = GetAura(staggerBleed))
            {
                AuraEffect* eff0 = staggerBleedAura->GetEffect(EFFECT_0);
                AuraEffect* eff1 = staggerBleedAura->GetEffect(EFFECT_1);

                bp0 = RoundingFloatValue(float(bp1 + eff1->GetAmount()) / _tickCount);
                bp1 = bp0 * _tickCount;

                if (staggerBleedAura->GetCustomData())
                    bp0 = 0;

                if (bp1 > int32(CountPctFromMaxHealth(60)))
                {
                    staggerDebuf = staggerRed;
                    if (hasBonusHast) bp2 = 15;
                }
                else if (bp1 > int32(CountPctFromMaxHealth(30)))
                {
                    staggerDebuf = staggerYellow;
                    if (hasBonusHast) bp2 = 10;
                }

                eff0->SetAmount(bp0);
                eff1->SetAmount(bp1);

                if (HasAura(staggerGreen))
                {
                    if (staggerDebuf != staggerGreen)
                    {
                        RemoveAurasDueToSpell(staggerGreen);
                        CastCustomSpell(this, staggerDebuf, &bp0, &bp1, &bp2, true);

                        if (hasFermentation)
                        {
                            float fbp0 = 10.f;
                            float fbp1 = -5.f;
                            RemoveAurasDueToSpell(fermentation);
                            CastCustomSpell(this, fermentation, &fbp0, &fbp1, nullptr, true);
                        }
                    }
                }
                else if (HasAura(staggerYellow))
                {
                    if (staggerDebuf != staggerYellow)
                    {
                        RemoveAurasDueToSpell(staggerYellow);
                        CastCustomSpell(this, staggerDebuf, &bp0, &bp1, &bp2, true);

                        if (hasFermentation)
                        {
                            float fbp0 = 20.f;
                            float fbp1 = -10.f;
                            RemoveAurasDueToSpell(fermentation);
                            CastCustomSpell(this, fermentation, &fbp0, &fbp1, nullptr, true);
                        }
                    }
                }
                else if (HasAura(staggerRed))
                {
                    if (staggerDebuf != staggerRed)
                    {
                        RemoveAurasDueToSpell(staggerRed);
                        CastCustomSpell(this, staggerDebuf, &bp0, &bp1, &bp2, true);

                        if (hasFermentation)
                        {
                            float fbp0 = 30.f;
                            float fbp1 = -15.f;
                            RemoveAurasDueToSpell(fermentation);
                            CastCustomSpell(this, fermentation, &fbp0, &fbp1, nullptr, true);
                        }
                    }
                }

                if (Aura* staggerDebufAura = GetAura(staggerDebuf))
                {
                    staggerDebufAura->GetEffect(EFFECT_0)->SetAmount(bp0);
                    staggerDebufAura->GetEffect(EFFECT_1)->SetAmount(bp1);
                    int32 dur = _duration - eff0->GetPeriod() + eff0->GetPeriodicTimer();
                    staggerDebufAura->SetMaxDuration(dur);
                    staggerDebufAura->SetDuration(dur);
                    if (hasFermentation)
                    {
                        if (Aura* fermentationAura = GetAura(fermentation))
                        {
                            fermentationAura->SetDuration(dur);
                            fermentationAura->SetMaxDuration(dur);
                        }
                    }
                }
            }
            else
            {
                if (bp1 > int32(CountPctFromMaxHealth(60)))
                {
                    staggerDebuf = staggerRed;
                    if (hasBonusHast) bp2 = 15;
                }
                else if (bp1 > int32(CountPctFromMaxHealth(30)))
                {
                    staggerDebuf = staggerYellow;
                    if (hasBonusHast) bp2 = 10;
                }

                if (bp0 > 0)
                    CastCustomSpell(this, staggerBleed, &bp0, &bp1, &bp2, true);

                CastCustomSpell(this, staggerDebuf, &bp0, &bp1, &bp2, true);
                if (Aura* staggerDebufAura = GetAura(staggerDebuf))
                {
                    _duration = 10000;
                    if (AuraEffect const* auraEffect = GetAuraEffect(208881, EFFECT_0)) // Gift of Veri'thas
                        _duration += CalculatePct(_duration, auraEffect->GetAmount());

                    staggerDebufAura->SetMaxDuration(_duration);
                    staggerDebufAura->SetDuration(_duration);
                    if (hasFermentation)
                    {
                        float fbp0 = 10.f;
                        float fbp1 = -5.f;
                        if (staggerDebuf == staggerYellow)
                        {
                            fbp0 = 20.f;
                            fbp1 = -10.f;
                        }
                        if (staggerDebuf == staggerRed)
                        {
                            fbp0 = 30.f;
                            fbp1 = -15.f;
                        }
                        CastCustomSpell(this, fermentation, &fbp0, &fbp1, nullptr, true);
                        if (Aura* fermentationAura = GetAura(fermentation))
                        {
                            fermentationAura->SetDuration(_duration);
                            fermentationAura->SetMaxDuration(_duration);
                        }
                    }
                }
            }
        }
    }
    return damage;
}

int32 Unit::InterceptionOfDamage(Unit* victim, uint32 damage, SpellSchoolMask damageSchoolMask)
{
    return damage;
}

void Unit::CastStop(uint32 except_spellid, bool interruptStun /*= false*/)
{
    for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
        if (m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id != except_spellid && !(interruptStun && m_currentSpells[i]->m_spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_STUNNED)))
            InterruptSpell(CurrentSpellTypes(i), false);
}

void Unit::CastSpellDuration(Unit* victim, uint32 spellId, bool triggered, uint32 duration, uint32 stack, uint32 timeCast, AuraEffect const* triggeredByAura)
{
    if (!this || m_cleanupDone || !victim || victim->m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
        return;

    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetUnitTarget(victim);

    TriggerCastData triggerData;
    triggerData.triggerFlags = triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE;
    triggerData.auraDuration = duration;
    triggerData.setSTack = stack;
    triggerData.triggeredByAura = triggeredByAura;
    if (triggeredByAura && triggeredByAura->GetSpellInfo() && !triggeredByAura->GetSpellInfo()->IsPassive())
        triggerData.SubType = SPELL_CAST_TYPE_FROM_AURA;
    if (timeCast)
        triggerData.casttime = timeCast;

    CastSpell(targets, spellInfo, nullptr, triggerData);
}

void Unit::CastSpellTime(Unit* victim, uint32 spellId, bool triggered, uint32 castTime)
{
    if (!this || m_cleanupDone || !victim || victim->m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
        return;

    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetUnitTarget(victim);

    TriggerCastData triggerData;
    triggerData.triggerFlags = triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE;

    if (castTime)
        triggerData.casttime = castTime;

    CastSpell(targets, spellInfo, nullptr, triggerData);
}

void Unit::CastSpellTime(float x, float y, float z, uint32 spellId, bool triggered, uint32 castTime)
{
    if (!this || m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
        return;

    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetDst(x, y, z, GetOrientation());

    TriggerCastData triggerData;
    triggerData.triggerFlags = triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE;

    if (castTime)
        triggerData.casttime = castTime;

    CastSpell(targets, spellInfo, nullptr, triggerData);
}

void Unit::CastSpell(SpellCastTargets const& targets, SpellInfo const* spellInfo, CustomSpellValues const* value, TriggerCastData& triggerData)
{
    if(!this || m_cleanupDone)
        return;

    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell by caster: %s %u)", (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }

    // TODO: this is a workaround - not needed anymore, but required for some scripts :(
    //! by cyberbrest: i remove it as all prock spell by arrea with NeedsToBeTriggeredByCaster check are not work correct. ispesially new.
    //! if u uncomment it - force originalcaster guid on HandlePeriodicTriggerSpellAuraTick
    //if (!originalCaster && triggeredByAura)
    //    originalCaster = triggeredByAura->GetCasterGUID();

    Spell* spell = new Spell(this, spellInfo, triggerData);
    if (value)
        for (CustomSpellValues::const_iterator itr = value->begin(); itr != value->end(); ++itr)
            spell->SetSpellValue(itr->first, itr->second, itr->second);

    spell->prepare(&targets);
}

void Unit::CastSpell(SpellCastTargets const& targets, SpellInfo const* spellInfo, CustomSpellValues const* value, TriggerCastFlags triggerFlags, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if(!this || m_cleanupDone)
        return;

    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell by caster: %s %u)", (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }

    // TODO: this is a workaround - not needed anymore, but required for some scripts :(
    //! by cyberbrest: i remove it as all prock spell by arrea with NeedsToBeTriggeredByCaster check are not work correct. ispesially new.
    //! if u uncomment it - force originalcaster guid on HandlePeriodicTriggerSpellAuraTick
    //if (!originalCaster && triggeredByAura)
    //    originalCaster = triggeredByAura->GetCasterGUID();

    // uint32 _s = getMSTime();
    TriggerCastData triggerData;
    triggerData.triggerFlags = triggerFlags;
    triggerData.originalCaster = originalCaster;
    triggerData.castItem = castItem;
    triggerData.triggeredByAura = triggeredByAura;
    if (triggeredByAura && triggeredByAura->GetSpellInfo() && !triggeredByAura->GetSpellInfo()->IsPassive())
        triggerData.SubType = SPELL_CAST_TYPE_FROM_AURA;

    Spell* spell = new Spell(this, spellInfo, triggerData);
    if (value)
        for (CustomSpellValues::const_iterator itr = value->begin(); itr != value->end(); ++itr)
            spell->SetSpellValue(itr->first, itr->second, itr->second);

    // uint32 _ms = getMSTimeDiff(_s, getMSTime());
    spell->prepare(&targets);
    // _ms = getMSTimeDiff(_s, getMSTime());
    // if (_ms > 50)
        // sLog->outDiff("CastSpell 2 Entry %u: Diff - %ums Id %u XYZ(%f %f %f) mapId %u", GetEntry(), _ms, spellInfo->Id, GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId());
}

void Unit::CastSpell(Unit* victim, uint32 spellId, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if (!this || m_cleanupDone || !victim || victim->m_cleanupDone)
        return;

    CastSpell(victim, spellId, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpell(Unit* victim, uint32 spellId, TriggerCastFlags triggerFlags /*= TRIGGER_NONE*/, Item* castItem /*= NULL*/, AuraEffect const* triggeredByAura /*= NULL*/, ObjectGuid originalCaster /*= 0*/)
{
    if (!this || m_cleanupDone || !victim || victim->m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell id %u by caster: %s %u)", spellId, (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }

    CastSpell(victim, spellInfo, triggerFlags, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpell(Unit* victim, SpellInfo const* spellInfo, bool triggered, Item* castItem/*= NULL*/, AuraEffect const* triggeredByAura /*= NULL*/, ObjectGuid originalCaster /*= 0*/)
{
    if (!this || m_cleanupDone || !victim || victim->m_cleanupDone)
        return;

    CastSpell(victim, spellInfo, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpell(Unit* victim, SpellInfo const* spellInfo, TriggerCastFlags triggerFlags, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if (!this || m_cleanupDone || !victim || victim->m_cleanupDone)
        return;

    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetUnitTarget(victim);
    CastSpell(targets, spellInfo, nullptr, triggerFlags, castItem, triggeredByAura, originalCaster);
}

void Unit::CastCustomSpell(Unit* target, uint32 spellId, float const* bp0, float const* bp1, float const* bp2, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if(!this || m_cleanupDone)
        return;

    CustomSpellValues values;
    if (bp0)
        values.AddSpellMod(SPELLVALUE_BASE_POINT0, *bp0);
    if (bp1)
        values.AddSpellMod(SPELLVALUE_BASE_POINT1, *bp1);
    if (bp2)
        values.AddSpellMod(SPELLVALUE_BASE_POINT2, *bp2);
    CastCustomSpell(spellId, values, target, triggered, castItem, triggeredByAura, originalCaster);
}

void Unit::CastCustomSpell(Unit * Victim, uint32 spellId, float const * bp0, float const * bp1, float const * bp2, TriggerCastFlags triggeredCastFlags, Item * castItem, AuraEffect const * triggeredByAura, ObjectGuid originalCaster)
{
    if (m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell id %u by caster: %s %u)", spellId, (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }

    CustomSpellValues values;
    if (bp0)
        values.AddSpellMod(SPELLVALUE_BASE_POINT0, *bp0);
    if (bp1)
        values.AddSpellMod(SPELLVALUE_BASE_POINT1, *bp1);
    if (bp2)
        values.AddSpellMod(SPELLVALUE_BASE_POINT2, *bp2);

    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetUnitTarget(Victim);

    CastSpell(targets, spellInfo, &values, triggeredCastFlags, castItem, triggeredByAura, originalCaster);
}

void Unit::CastCustomSpell(float x, float y, float z, uint32 spellId, float const* bp0, float const* bp1, float const* bp2, TriggerCastFlags triggeredCastFlags, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if (!this || m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell id %u by caster: %s %u)", spellId, (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }

    CustomSpellValues values;
    if (bp0)
        values.AddSpellMod(SPELLVALUE_BASE_POINT0, *bp0);
    if (bp1)
        values.AddSpellMod(SPELLVALUE_BASE_POINT1, *bp1);
    if (bp2)
        values.AddSpellMod(SPELLVALUE_BASE_POINT2, *bp2);

    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetDst(x, y, z, GetOrientation());

    CastSpell(targets, spellInfo, &values, triggeredCastFlags, castItem, triggeredByAura, originalCaster);
}

void Unit::CastCustomSpell(Unit* target, uint32 spellId, float const* bp0, float const* bp1, float const* bp2, float const* bp3, float const* bp4, float const* bp5, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if(!this || m_cleanupDone)
        return;

    CustomSpellValues values;
    if (bp0)
        values.AddSpellMod(SPELLVALUE_BASE_POINT0, *bp0);
    if (bp1)
        values.AddSpellMod(SPELLVALUE_BASE_POINT1, *bp1);
    if (bp2)
        values.AddSpellMod(SPELLVALUE_BASE_POINT2, *bp2);
    if (bp3)
        values.AddSpellMod(SPELLVALUE_BASE_POINT3, *bp3);
    if (bp4)
        values.AddSpellMod(SPELLVALUE_BASE_POINT4, *bp4);
    if (bp5)
        values.AddSpellMod(SPELLVALUE_BASE_POINT5, *bp5);
    CastCustomSpell(spellId, values, target, triggered, castItem, triggeredByAura, originalCaster);
}

void Unit::CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* target, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if (!this || m_cleanupDone)
        return;

    CustomSpellValues values;
    values.AddSpellMod(mod, value);
    CastCustomSpell(spellId, values, target, triggered, castItem, triggeredByAura, originalCaster);
}

void Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const& value, Unit* victim, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if (!this || m_cleanupDone || !victim || victim->m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell id %u by caster: %s %u)", spellId, (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }
    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetUnitTarget(victim);

    CastSpell(targets, spellInfo, &value, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if(!this || m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell id %u by caster: %s %u)", spellId, (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }
    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetDst(x, y, z, GetOrientation());

    CastSpell(targets, spellInfo, nullptr, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpell(WorldLocation const* loc, uint32 spellId, bool triggered, Item* castItem /*= nullptr*/, AuraEffect const* triggeredByAura /*= nullptr*/, ObjectGuid originalCaster /*= ObjectGuid::Empty*/)
{
    CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), spellId, triggered, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpell(G3D::Vector3 pos, uint32 spellId, bool triggered, Item* castItem /*= nullptr*/, AuraEffect const* triggeredByAura /*= nullptr*/, ObjectGuid originalCaster /*= ObjectGuid::Empty*/)
{
    CastSpell(pos.x, pos.y, pos.z, spellId, triggered, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpell(Position pos, uint32 spellId, bool triggered, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if(!this || m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell id %u by caster: %s %u)", spellId, (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }

    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetDst(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation());
    CastSpell(targets, spellInfo, nullptr, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpell(float x, float y, float z, uint32 spellId, TriggerCastFlags triggeredCastFlags, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if(!this || m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell id %u by caster: %s %u)", spellId, (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }
    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetDst(x, y, z, GetOrientation());

    CastSpell(targets, spellInfo, nullptr, triggeredCastFlags, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpell(GameObject* go, uint32 spellId, bool triggered, Item* castItem, AuraEffect* triggeredByAura, ObjectGuid originalCaster)
{
    if(!this || m_cleanupDone)
        return;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "CastSpell: unknown spell id %u by caster: %s %u)", spellId, (IsPlayer() ? "player (GUID:" : "creature (Entry:"), (IsPlayer() ? GetGUIDLow() : GetEntry()));
        return;
    }
    SpellCastTargets targets;
    targets.SetCaster(this);
    targets.SetGOTarget(go);

    CastSpell(targets, spellInfo, nullptr, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster);
}

void Unit::CastSpellOnAttackablTarget(uint32 spellId, float dist, Unit* exclude, uint32 targetCount) // not used
{
    std::list<Unit*> targetList;
    GetAttackableUnitListInRange(targetList, dist);

    if (exclude)
        targetList.remove(exclude);

    if (!targetList.empty())
    {
        std::list<Unit*> saveList = targetList;
        for (std::list<Unit*>::const_iterator itr = targetList.begin(); itr != targetList.end(); ++itr)
        {
            if (!(*itr)->IsWithinLOSInMap(this) || IsFriendlyTo(*itr) || (*itr)->HasAura(spellId))
                saveList.remove(*itr);
        }

        if (saveList.empty())
            return;

        if (targetCount && saveList.size() > targetCount)
            saveList.resize(targetCount);

        for (std::list<Unit*>::const_iterator itr = saveList.begin(); itr != saveList.end(); ++itr)
            CastSpell((*itr), spellId, true);
    }
}

void Unit::CastSpellDelay(Unit* victim, uint32 spellId, bool triggered, uint32 delay, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if (m_cleanupDone)
        return;

    ObjectGuid targetGUID = victim->GetGUID();
    AddDelayedCombat(delay, [this, spellId, triggered, castItem, triggeredByAura, originalCaster, targetGUID]() -> void
    {
        Unit* target = ObjectAccessor::GetUnit(*this, targetGUID);
        if (!target)
            return;

        CastSpell(target, spellId, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster);
    });
}

void Unit::CastSpellDelay(Position pos, uint32 spellId, bool triggered, uint32 delay, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
    if (m_cleanupDone)
        return;

    if (!delay)
        CastSpell(pos, spellId, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster);
    else
    {
        AddDelayedCombat(delay, [this, pos, spellId, triggered, castItem, triggeredByAura, originalCaster]() -> void
        {
            CastSpell(pos, spellId, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE, castItem, triggeredByAura, originalCaster);
        });
    }
}

void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, float damage, SpellInfo const* spellInfo, uint32 effectMask, WeaponAttackType attackType, float critMod, bool crit)
{
    if (damage < 0)
        return;

    Unit* victim = damageInfo->target;
    if (!victim || !victim->isAlive())
        return;

    SpellSchoolMask damageSchoolMask = SpellSchoolMask(damageInfo->schoolMask);

    if (IsDamageReducedByArmor(damageSchoolMask, spellInfo, effectMask))
        damage = CalcArmorReducedDamage(damageInfo->attacker, victim, damage, spellInfo);

    if (Player* spellModOwner = victim->GetSpellModOwner())
        damage -= CalculatePct(damage, (spellModOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY) + spellModOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY_BONUS)) / 2.f);

    int32 sourceDamage = damage;

    bool blocked = false;
    // Per-school calc
    switch (spellInfo->Categories.DefenseType)
    {
        // Melee and Ranged Spells
        case SPELL_DAMAGE_CLASS_RANGED:
        case SPELL_DAMAGE_CLASS_MELEE:
        {
            blocked = isSpellBlocked(victim, spellInfo, attackType);

            if (crit)
            {
                damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT;
                damage *= critMod ? critMod : SpellCriticalDamageBonus(spellInfo, victim);
            }

            // Spell weapon based damage CAN BE crit & blocked at same time
            if (blocked)
            {
                // double blocked amount if block is critical
                uint32 value = victim->GetBlockPercent();
                if (victim->isBlockCritical())
                    value *= 2; // double blocked percent
                damageInfo->blocked = CalculatePct(damage, value);
                damage -= damageInfo->blocked;
                sourceDamage -= damageInfo->blocked;
            }

            ApplyResilience(victim, &damage);
            break;
        }
        // Magical Attacks
        case SPELL_DAMAGE_CLASS_NONE:
        case SPELL_DAMAGE_CLASS_MAGIC:
        {
            // If crit add critical bonus
            if (crit)
            {
                damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT;
                damage *= critMod ? critMod : SpellCriticalDamageBonus(spellInfo, victim);
            }

            ApplyResilience(victim, &damage);
            break;
        }
        default:
            break;
    }

    if (victim->getClass() == CLASS_MONK)
        damage = victim->CalcStaggerDamage(damage, damageSchoolMask, spellInfo);

    if (spellInfo && spellInfo->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS))
    {
        if (sourceDamage > 0)
        {
            CalcAbsorbResist(victim, damageSchoolMask, SPELL_DIRECT_DAMAGE, sourceDamage, &damageInfo->absorb, &damageInfo->resist, spellInfo);
            sourceDamage -= damageInfo->absorb + damageInfo->resist;
        }
        damageInfo->damage = sourceDamage;
        return;
    }

    // Calculate absorb resist
    if (damage > 0)
    {
        CalcAbsorbResist(victim, damageSchoolMask, SPELL_DIRECT_DAMAGE, damage, &damageInfo->absorb, &damageInfo->resist, spellInfo);
        damage -= damageInfo->absorb + damageInfo->resist;
    }
    else
        damage = 0;

    damage = InterceptionOfDamage(victim, damage, damageSchoolMask);
    damageInfo->damage = damage;
}

void Unit::DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss)
{
    if (damageInfo == nullptr)
        return;

    Unit* victim = damageInfo->target;

    if (!victim)
        return;

    if (!victim->isAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->IsCreature() && victim->ToCreature()->IsInEvadeMode()))
        return;

    SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(damageInfo->SpellID);
    if (spellProto == nullptr)
    {
        TC_LOG_DEBUG(LOG_FILTER_UNITS, "Unit::DealSpellDamage has wrong damageInfo->SpellID: %u", damageInfo->SpellID);
        return;
    }

    // Call default DealDamage
    CleanDamage cleanDamage(damageInfo->cleanDamage, damageInfo->absorb, BASE_ATTACK, MELEE_HIT_NORMAL, damageInfo->isAoe);
    DealDamage(victim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss);
}

// TODO for melee need create structure as in
void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* damageInfo, WeaponAttackType attackType, SpellSchoolMask schoolMask)
{
    damageInfo->attacker         = this;
    damageInfo->target           = victim;
    damageInfo->damageSchoolMask = schoolMask;
    damageInfo->attackType       = attackType;
    damageInfo->damage           = 0;
    damageInfo->cleanDamage      = 0;
    damageInfo->damageBeforeHit  = 0;
    damageInfo->absorb           = 0;
    damageInfo->resist           = 0;
    damageInfo->blocked_amount   = 0;

    damageInfo->TargetState      = 0;
    damageInfo->HitInfo          = 0;
    damageInfo->procAttacker     = PROC_FLAG_NONE;
    damageInfo->procVictim       = PROC_FLAG_NONE;
    damageInfo->procEx           = PROC_EX_NONE;
    damageInfo->hitOutCome       = MELEE_HIT_EVADE;

    if (!victim)
        return;

    if (!isAlive() || !victim->isAlive())
        return;

    // Select HitInfo/procAttacker/procVictim flag based on attack type
    switch (attackType)
    {
        case BASE_ATTACK:
            damageInfo->procAttacker = PROC_FLAG_DONE_MELEE_AUTO_ATTACK | PROC_FLAG_DONE_MAINHAND_ATTACK;
            damageInfo->procVictim   = PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK;
            break;
        case OFF_ATTACK:
            damageInfo->procAttacker = PROC_FLAG_DONE_MELEE_AUTO_ATTACK | PROC_FLAG_DONE_OFFHAND_ATTACK;
            damageInfo->procVictim   = PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK;
            damageInfo->HitInfo      = HITINFO_OFFHAND;
            break;
        default:
            return;
    }

    // Physical Immune check
    if (damageInfo->target->IsImmunedToDamage(SpellSchoolMask(damageInfo->damageSchoolMask)))
    {
        damageInfo->HitInfo |= HITINFO_NORMALSWING;
        damageInfo->TargetState = VICTIMSTATE_IS_IMMUNE;

        damageInfo->procEx |= PROC_EX_IMMUNE;
        damageInfo->damage = 0;
        damageInfo->cleanDamage = 0;
        return;
    }

    damage += CalculateDamage(damageInfo->attackType, false, true, victim);

    // Add melee damage bonus
    damage = MeleeDamageBonusDone(damageInfo->target, damage, damageInfo->attackType);
    damageInfo->damageBeforeHit = damage;
    damage = damageInfo->target->MeleeDamageBonusTaken(this, damage, damageInfo->attackType);

    // Calculate armor reduction
    if (IsDamageReducedByArmor(static_cast<SpellSchoolMask>(damageInfo->damageSchoolMask)))
    {
        uint32 damageReduced = CalcArmorReducedDamage(damageInfo->attacker, damageInfo->target, damage, nullptr);
        damageInfo->cleanDamage += damage - damageReduced;
        damage = damageReduced;
    }

    damageInfo->damage = damage;

    if (Unit* owner = GetAnyOwner()) //For pets chance calc from owner
        damageInfo->hitOutCome = owner->RollMeleeOutcomeAgainst(damageInfo->target, damageInfo->attackType);
    else
        damageInfo->hitOutCome = RollMeleeOutcomeAgainst(damageInfo->target, damageInfo->attackType);

    switch (damageInfo->hitOutCome)
    {
        case MELEE_HIT_EVADE:
            damageInfo->HitInfo        |= HITINFO_MISS | HITINFO_SWINGNOHITSOUND;
            damageInfo->TargetState     = VICTIMSTATE_EVADES;
            damageInfo->procEx         |= PROC_EX_EVADE;
            damageInfo->damage = 0;
            damageInfo->cleanDamage = 0;
            return;
        case MELEE_HIT_MISS:
            damageInfo->HitInfo        |= HITINFO_MISS;
            damageInfo->TargetState     = VICTIMSTATE_INTACT;
            damageInfo->procEx         |= PROC_EX_MISS;
            damageInfo->damage          = 0;
            damageInfo->cleanDamage     = 0;
            break;
        case MELEE_HIT_NORMAL:
            damageInfo->TargetState     = VICTIMSTATE_HIT;
            damageInfo->procEx         |= PROC_EX_NORMAL_HIT;
            break;
        case MELEE_HIT_CRIT:
        {
            damageInfo->HitInfo        |= HITINFO_CRITICAL_HIT;
            damageInfo->TargetState     = VICTIMSTATE_HIT;

            damageInfo->procEx         |= PROC_EX_CRITICAL_HIT;

            uint32 critDamage = damageInfo->damage * 2.f;
            uint32 critDamageBH = damageInfo->damageBeforeHit * 2.f;

            float mod = GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, damageInfo->damageSchoolMask);

            critDamage *= mod;
            critDamageBH *= mod;

            if ((IsPlayer() || HasUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER)) && victim &&
                victim != this && (victim->IsPlayer() || victim->HasUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER)))
            {
                critDamage -= damageInfo->damage;
                critDamageBH -= damageInfo->damageBeforeHit;

                critDamage = CalculatePct(critDamage, 50);
                critDamageBH = CalculatePct(critDamageBH, 50);

                damageInfo->damage += critDamage;
                damageInfo->damageBeforeHit += critDamageBH;
            }
            else
            {
                damageInfo->damage = critDamage;
                damageInfo->damageBeforeHit = critDamageBH;
            }

            break;
        }
        case MELEE_HIT_PARRY:
            damageInfo->TargetState  = VICTIMSTATE_PARRY;
            damageInfo->procEx      |= PROC_EX_PARRY;
            damageInfo->cleanDamage += damageInfo->damage;
            damageInfo->damage = 0;
            break;
        case MELEE_HIT_DODGE:
            damageInfo->TargetState  = VICTIMSTATE_DODGE;
            damageInfo->procEx      |= PROC_EX_DODGE;
            damageInfo->cleanDamage += damageInfo->damage;
            damageInfo->damage = 0;
            break;
        case MELEE_HIT_BLOCK:
            damageInfo->TargetState = VICTIMSTATE_HIT;
            damageInfo->HitInfo    |= HITINFO_BLOCK;
            damageInfo->procEx     |= PROC_EX_BLOCK | PROC_EX_NORMAL_HIT;
            // 30% damage blocked, double blocked amount if block is critical
            damageInfo->blocked_amount = CalculatePct(damageInfo->damage, damageInfo->target->isBlockCritical() ? damageInfo->target->GetBlockPercent() * 2 : damageInfo->target->GetBlockPercent());
            damageInfo->damage      -= damageInfo->blocked_amount;
            damageInfo->cleanDamage += damageInfo->blocked_amount;
            break;
        case MELEE_HIT_GLANCING:
        {
            damageInfo->HitInfo     |= HITINFO_GLANCING;
            damageInfo->TargetState  = VICTIMSTATE_HIT;
            damageInfo->procEx      |= PROC_EX_NORMAL_HIT;
            int32 leveldif = int32(victim->getLevelForTarget(this)) - int32(getLevelForTarget(victim));
            if (leveldif > 3)
                leveldif = 3;
            float reducePercent = 1 - leveldif * 0.1f;
            damageInfo->cleanDamage += damageInfo->damage - uint32(reducePercent * damageInfo->damage);
            damageInfo->damage = uint32(reducePercent * damageInfo->damage);
            break;
        }
        case MELEE_HIT_CRUSHING:
            damageInfo->HitInfo     |= HITINFO_CRUSHING;
            damageInfo->TargetState  = VICTIMSTATE_HIT;
            damageInfo->procEx      |= PROC_EX_NORMAL_HIT;
            // 150% normal damage
            damageInfo->damage += (damageInfo->damage / 2);
            break;
        default:
            break;
    }

    // Always apply HITINFO_AFFECTS_VICTIM in case its not a miss
    if (!(damageInfo->HitInfo & HITINFO_MISS))
        damageInfo->HitInfo |= HITINFO_AFFECTS_VICTIM;

    float resilienceReduction = damageInfo->damage;
    ApplyResilience(victim, &resilienceReduction);
    resilienceReduction = damageInfo->damage - resilienceReduction;
    damageInfo->damage      -= resilienceReduction;
    damageInfo->cleanDamage += resilienceReduction;

    if (victim->getClass() == CLASS_MONK)
        damageInfo->damage = victim->CalcStaggerDamage(damageInfo->damage, SpellSchoolMask(damageInfo->damageSchoolMask));

    // Calculate absorb resist
    if (int32(damageInfo->damage) > 0)
    {
        damageInfo->procVictim |= PROC_FLAG_TAKEN_DAMAGE;
        // Calculate absorb & resists
        CalcAbsorbResist(damageInfo->target, SpellSchoolMask(damageInfo->damageSchoolMask), DIRECT_DAMAGE, damageInfo->damage, &damageInfo->absorb, &damageInfo->resist);

        if (damageInfo->absorb)
        {
            damageInfo->HitInfo |= (damageInfo->damage - damageInfo->absorb == 0 ? HITINFO_FULL_ABSORB : HITINFO_PARTIAL_ABSORB);
            damageInfo->procEx  |= PROC_EX_ABSORB;
        }

        if (damageInfo->resist)
            damageInfo->HitInfo |= (damageInfo->damage - damageInfo->resist == 0 ? HITINFO_FULL_RESIST : HITINFO_PARTIAL_RESIST);

        damageInfo->damage -= damageInfo->absorb + damageInfo->resist;
    }
    else // Impossible get negative result but....
        damageInfo->damage = 0;

    damageInfo->damage = InterceptionOfDamage(victim, damageInfo->damage, SpellSchoolMask(damageInfo->damageSchoolMask));
}

void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
{
    Unit* victim = damageInfo->target;

    if (!victim->isAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->IsCreature() && victim->ToCreature()->IsInEvadeMode()))
        return;

    // Hmmmm dont like this emotes client must by self do all animations
    if (damageInfo->HitInfo & HITINFO_CRITICAL_HIT)
        victim->HandleEmoteCommand(EMOTE_ONESHOT_WOUND_CRITICAL);
    if (damageInfo->blocked_amount && damageInfo->TargetState != VICTIMSTATE_BLOCKS)
        victim->HandleEmoteCommand(EMOTE_ONESHOT_PARRY_SHIELD);

    if (damageInfo->TargetState == VICTIMSTATE_PARRY)
    {
        // Get attack timers
        float offtime  = float(victim->getAttackTimer(OFF_ATTACK));
        float basetime = float(victim->getAttackTimer(BASE_ATTACK));
        // Reduce attack time
        if (victim->haveOffhandWeapon() && offtime < basetime)
        {
            float percent20 = victim->GetAttackTime(OFF_ATTACK) * 0.20f;
            float percent60 = 3.0f * percent20;
            if (offtime > percent20 && offtime <= percent60)
                victim->setAttackTimer(OFF_ATTACK, uint32(percent20));
            else if (offtime > percent60)
            {
                offtime -= 2.0f * percent20;
                victim->setAttackTimer(OFF_ATTACK, uint32(offtime));
            }
        }
        else
        {
            float percent20 = victim->GetAttackTime(BASE_ATTACK) * 0.20f;
            float percent60 = 3.0f * percent20;
            if (basetime > percent20 && basetime <= percent60)
                victim->setAttackTimer(BASE_ATTACK, uint32(percent20));
            else if (basetime > percent60)
            {
                basetime -= 2.0f * percent20;
                victim->setAttackTimer(BASE_ATTACK, uint32(basetime));
            }
        }
    }

    // Call default DealDamage
    CleanDamage cleanDamage(damageInfo->cleanDamage, damageInfo->absorb, damageInfo->attackType, damageInfo->hitOutCome, damageInfo->isAoe);
    DealDamage(victim, damageInfo->damage, &cleanDamage, DIRECT_DAMAGE, SpellSchoolMask(damageInfo->damageSchoolMask), nullptr, durabilityLoss);

    bool isTank = victim->IsPlayer() && victim->ToPlayer()->isInTankSpec();

    // If this is a creature and it attacks from behind it has a probability to daze it's victim
    if ((damageInfo->hitOutCome == MELEE_HIT_CRIT || damageInfo->hitOutCome == MELEE_HIT_CRUSHING || damageInfo->hitOutCome == MELEE_HIT_NORMAL || damageInfo->hitOutCome == MELEE_HIT_GLANCING)
        && !IsPlayer() && !ToCreature()->IsControlledByPlayer() && !victim->HasInArc(M_PI, this)
        && (victim->IsPlayer() || !victim->ToCreature()->isWorldBoss()) && !isTank)
    {
        // -probability is between 0% and 40%
        // 20% base chance
        float Probability = 20.0f;

        // there is a newbie protection, at level 10 just 7% base chance; assuming linear function
        if (victim->getLevelForTarget(this) < 30)
            Probability = 0.65f * victim->getLevelForTarget(this) + 0.5f;

        if (Probability > 40.0f)
            Probability = 40.0f;

        if (roll_chance_f(Probability))
            CastSpell(victim, 1604, true);
    }

    if (IsPlayer())
        ToPlayer()->CastItemCombatSpell(victim, damageInfo->attackType, damageInfo->procVictim, damageInfo->procEx);

    // Do effect if any damage done to target
    if (damageInfo->damage)
    {
        // We're going to call functions which can modify content of the list during iteration over it's elements
        // Let's copy the list so we can prevent iterator invalidation
        if (AuraEffectList const* vDamageShieldsCopy = victim->GetAuraEffectsByType(SPELL_AURA_DAMAGE_SHIELD))
        for (AuraEffectList::const_iterator dmgShieldItr = vDamageShieldsCopy->begin(); dmgShieldItr != vDamageShieldsCopy->end(); ++dmgShieldItr)
        {
            SpellInfo const* i_spellProto = (*dmgShieldItr)->GetSpellInfo();
            // Damage shield can be resisted...
            if (SpellMissInfo missInfo = victim->SpellHitResult(this, i_spellProto, false, 1 << (*dmgShieldItr)->GetEffIndex()))
            {
                victim->SendSpellMiss(this, i_spellProto->Id, missInfo);
                continue;
            }

            // ...or immuned
            if (IsImmunedToDamage(i_spellProto))
            {
                victim->SendSpellDamageImmune(this, i_spellProto->Id, false);
                continue;
            }

            uint32 damage = (*dmgShieldItr)->GetAmount();

            if (Unit* caster = (*dmgShieldItr)->GetCaster())
            {
                std::vector<uint32> ExcludeAuraList;
                damage = caster->SpellDamageBonusDone(this, i_spellProto, damage, SPELL_DIRECT_DAMAGE, ExcludeAuraList, static_cast<SpellEffIndex>((*dmgShieldItr)->GetEffIndex()));
                damage = this->SpellDamageBonusTaken(caster, i_spellProto, damage);
            }

            uint32 absorb = 0, resist = 0;
            victim->CalcAbsorbResist(this, SpellSchoolMask(i_spellProto->GetMisc(m_spawnMode)->MiscData.SchoolMask), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist, i_spellProto);
            victim->DealDamageMods(this, damage, nullptr);

            WorldPackets::CombatLog::SpellDamageShield damageShield;
            damageShield.Attacker = victim->GetGUID();
            damageShield.Defender = GetGUID();
            damageShield.SpellID = i_spellProto->Id;
            damageShield.TotalDamage = damage;
            damageShield.OverKill = std::max(int32(damage) - int32(GetHealth(victim)), 0);
            damageShield.SchoolMask = i_spellProto->GetMisc(m_spawnMode)->MiscData.SchoolMask;
            damageShield.LogAbsorbed = absorb;
            damageShield.LogData.Initialize(this);
            victim->SendCombatLogMessage(&damageShield);

            victim->DealDamage(this, damage, nullptr, SPELL_DIRECT_DAMAGE, i_spellProto->GetSchoolMask(), i_spellProto, true);
        }
    }
}

void Unit::HandleEmoteCommand(uint32 anim_id)
{
    WorldPackets::Chat::Emote packet;
    packet.Guid = GetGUID();
    packet.EmoteID = anim_id;
    SendMessageToSet(packet.Write(), true);
}

bool Unit::IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo const* spellInfo, uint32 effectMask)
{
    // only physical spells damage gets reduced by armor
    if (schoolMask != SPELL_SCHOOL_MASK_NORMAL)
        return false;

    if (spellInfo)
    {
        // there are spells with no specific attribute but they have "ignores armor" in tooltip
        if (spellInfo->AttributesCu[0] & SPELL_ATTR0_CU_IGNORE_ARMOR)
            return false;

        // Ignore magic class spells
        if (spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_RESISTANCES) && spellInfo->Categories.DefenseType == SPELL_DAMAGE_CLASS_MAGIC)
            return false;

        for (int i = 0; i < MAX_SPELL_EFFECTS; ++i)
        {
            if (spellInfo->EffectMask < uint32(1 << i))
                break;

            if ((effectMask & (1 << i)) && (spellInfo->Effects[i]->ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE || spellInfo->Effects[i]->Effect == SPELL_EFFECT_SCHOOL_DAMAGE))
                if (spellInfo->GetEffectMechanicMask(i) & (1 << MECHANIC_BLEED))
                    return false;
        }
    }
    return true;
}

uint32 Unit::CalcArmorReducedDamage(Unit* attacker, Unit* victim, uint32 const damage, SpellInfo const* spellInfo)
{
    float armor = float(victim->GetArmor(this));

    // bypass enemy armor by SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
    int32 armorBypassPct = 0;
    if (AuraEffectList const* reductionAuras = victim->GetAuraEffectsByType(SPELL_AURA_BYPASS_ARMOR_FOR_CASTER))
    for (AuraEffectList::const_iterator i = reductionAuras->begin(); i != reductionAuras->end(); ++i)
        if ((*i)->GetCasterGUID() == GetGUID())
        {
            if (spellInfo && spellInfo->Id == (*i)->GetId())
                continue;

            armorBypassPct += (*i)->GetAmount();
        }
    armor = CalculatePct(armor, 100 - std::min(armorBypassPct, 100));

    // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
    armor += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, SPELL_SCHOOL_MASK_NORMAL);

    if (spellInfo)
        if (Player* modOwner = GetSpellModOwner())
            modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_IGNORE_ARMOR, armor);

    if (AuraEffectList const* armorPenetrationPct = GetAuraEffectsByType(SPELL_AURA_MOD_ARMOR_PENETRATION_PCT))
    for (AuraEffectList::const_iterator j = armorPenetrationPct->begin(); j != armorPenetrationPct->end(); ++j)
    {
        if ((*j)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)
            armor -= CalculatePct(armor, (*j)->GetAmount());
    }

    if (G3D::fuzzyLe(armor, 0.0f))
        return damage;

    if (GtArmorMitigationByLvlEntry const* ambl = sArmorMitigationByLvlGameTable.GetRow(std::min(uint32(attacker->GetEffectiveLevel()), sArmorMitigationByLvlGameTable.GetTableRowCount())))
        return std::max<uint32>(damage * (1.0f - std::min(armor / (armor + ambl->Mitigation), 0.85f)), 1);

    return damage;
}

void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffectType damagetype, uint32 const damage, uint32 *absorb, uint32 *resist, SpellInfo const* spellInfo)
{
    if (!victim || !victim->isAlive() || !damage)
        return;

    DamageInfo dmgInfo = DamageInfo(this, victim, damage, spellInfo, schoolMask, damagetype, damage);

    // Magic damage, check for resists
    // Ignore spells that cant be resisted
    if ((schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0 && (!spellInfo || (spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_RESISTANCES)) == 0))
    {
        float victimResistance = float(victim->GetResistance(schoolMask));
        victimResistance += float(GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));

        if (Player* player = ToPlayer())
            victimResistance -= float(player->GetSpellPenetrationItemMod());

        // Resistance can't be lower then 0.
        if (victimResistance < 0.0f)
            victimResistance = 0.0f;

        static uint32 const BOSS_LEVEL = 83;
        static float const BOSS_RESISTANCE_CONSTANT = 510.0f;
        uint32 level = victim->getLevelForTarget(this);
        float resistanceConstant = 0.0f;

        if (level == BOSS_LEVEL)
            resistanceConstant = BOSS_RESISTANCE_CONSTANT;
        else
            resistanceConstant = level * 5.0f;

        float averageResist = victimResistance / (victimResistance + resistanceConstant);
        float discreteResistProbability[11];
        for (uint32 i = 0; i < 11; ++i)
        {
            discreteResistProbability[i] = 0.5f - 2.5f * fabs(0.1f * i - averageResist);
            if (discreteResistProbability[i] < 0.0f)
                discreteResistProbability[i] = 0.0f;
        }

        if (averageResist <= 0.1f)
        {
            discreteResistProbability[0] = 1.0f - 7.5f * averageResist;
            discreteResistProbability[1] = 5.0f * averageResist;
            discreteResistProbability[2] = 2.5f * averageResist;
        }

        float r = float(rand_norm());
        uint32 i = 0;
        float probabilitySum = discreteResistProbability[0];

        while (r >= probabilitySum && i < 10)
            probabilitySum += discreteResistProbability[++i];

        float damageResisted = float(damage * i / 10);

        dmgInfo.ResistDamage(uint32(damageResisted));
    }

    // Ignore Absorption Auras
    float auraAbsorbMod = 0;
    if (AuraEffectList const* AbsIgnoreAurasA = GetAuraEffectsByType(SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL))
    for (AuraEffectList::const_iterator itr = AbsIgnoreAurasA->begin(); itr != AbsIgnoreAurasA->end(); ++itr)
    {
        if (!((*itr)->GetMiscValue() & schoolMask))
            continue;

        if ((*itr)->GetAmount() > auraAbsorbMod)
            auraAbsorbMod = (*itr)->GetAmount();
    }
    RoundToInterval(auraAbsorbMod, 0.0f, 100.0f);

    // Ignore absorb
    if (spellInfo && !spellInfo->CanTriggerAbsorb())
    {
        *resist = dmgInfo.GetResist();
        *absorb = dmgInfo.GetAbsorb();
        return;
    }

    // We're going to call functions which can modify content of the list during iteration over it's elements
    // Let's copy the list so we can prevent iterator invalidation
    // absorb without mana cost
    if (AuraEffectList const* vSchoolAbsorbCopy = victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB))
    for (AuraEffectList::const_iterator itr = vSchoolAbsorbCopy->begin(); (itr != vSchoolAbsorbCopy->end()) && (dmgInfo.GetDamage() > 0); ++itr)
    {
        AuraEffect* absorbAurEff = *itr;
        // Check if aura was removed during iteration - we don't need to work on such auras
        AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(victim->GetGUID());
        if (!aurApp)
            continue;

        uint8 _mask = schoolMask;
        _mask &= ~absorbAurEff->GetMiscValue();

        if (_mask != 0)
            continue;

        // get amount which can be still absorbed by the aura
        float currentAbsorb = absorbAurEff->GetAmount();
        // aura with infinite absorb amount - let the scripts handle absorbtion amount, set here to 0 for safety
        if (currentAbsorb < 0)
            currentAbsorb = 0;

        bool defaultPrevented = false;

        absorbAurEff->GetBase()->CallScriptEffectAbsorbHandlers(absorbAurEff, aurApp, dmgInfo, currentAbsorb, defaultPrevented);

        // Apply absorb mod auras
        AddPct(currentAbsorb, -auraAbsorbMod);

        // absorb must be smaller than the damage itself
        currentAbsorb = RoundToInterval(currentAbsorb, 0.f, float(dmgInfo.GetDamage()));

        dmgInfo.AbsorbDamage(currentAbsorb);

        absorbAurEff->GetBase()->CallScriptEffectAfterAbsorbHandlers(absorbAurEff, aurApp, dmgInfo, currentAbsorb);

        if (currentAbsorb)
        {
            if (Unit* caster = absorbAurEff->GetCaster())
            {
                if (Player* player = caster->ToPlayer())
                {
                    if (Battleground* bg = player->GetBattleground())
                        bg->UpdatePlayerScore(player, SCORE_HEALING_DONE, currentAbsorb);

                    player->UpdateAchievementCriteria(CRITERIA_TYPE_HEALING_DONE, currentAbsorb, 0, 0, victim);
                    player->UpdateAchievementCriteria(CRITERIA_TYPE_HIGHEST_HEAL_CASTED, currentAbsorb);
                }
            }
        }

        if (defaultPrevented)
            continue;

        // Check if our aura is using amount to count damage
        if (absorbAurEff->GetAmount() >= 0 && currentAbsorb && !absorbAurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR6_NOT_LIMIT_ABSORB))
        {
            if(absorbAurEff->GetSpellInfo()->IsPassive())
            {
                if (absorbAurEff->GetAmount() > 0)
                    absorbAurEff->SetAmount(absorbAurEff->GetAmount() - currentAbsorb);
                continue;
            }
            // Reduce shield amount
            absorbAurEff->SetAmount(RoundingFloatValue(absorbAurEff->GetAmount()) - currentAbsorb);
            // Aura cannot absorb anything more - remove it
            if (absorbAurEff->GetAmount() <= 0) // Custom MoP Script - Stance of the Sturdy Ox shoudn't be removed at any damage
                absorbAurEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
        }
    }

    // absorb by mana cost
    AuraEffectList* vManaShieldCopy(victim->GetAuraEffectsByType(SPELL_AURA_MANA_SHIELD));
    if (vManaShieldCopy)
    for (AuraEffectList::iterator itr = vManaShieldCopy->begin(); (itr != vManaShieldCopy->end()) && (dmgInfo.GetDamage() > 0); ++itr)
    {
        AuraEffect* absorbAurEff = *itr;
        // Check if aura was removed during iteration - we don't need to work on such auras
        AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(victim->GetGUID());
        if (!aurApp)
            continue;
        // check damage school mask
        if (!(absorbAurEff->GetMiscValue() & schoolMask))
            continue;

        // get amount which can be still absorbed by the aura
        float currentAbsorb = absorbAurEff->GetAmount();
        // aura with infinite absorb amount - let the scripts handle absorbtion amount, set here to 0 for safety
        if (currentAbsorb < 0)
            currentAbsorb = 0;

        float tempAbsorb = currentAbsorb;

        bool defaultPrevented = false;

        absorbAurEff->GetBase()->CallScriptEffectManaShieldHandlers(absorbAurEff, aurApp, dmgInfo, tempAbsorb, defaultPrevented);
        currentAbsorb = tempAbsorb;

        if (defaultPrevented)
            continue;

        AddPct(currentAbsorb, -auraAbsorbMod);

        // absorb must be smaller than the damage itself
        currentAbsorb = RoundToInterval(currentAbsorb, 0.f, float(dmgInfo.GetDamage()));

        int32 manaReduction = currentAbsorb;

        // lower absorb amount by talents
        if (float manaMultiplier = absorbAurEff->GetSpellInfo()->GetEffect(absorbAurEff->GetEffIndex(), GetSpawnMode())->CalcValueMultiplier(absorbAurEff->GetCaster()))
            manaReduction = int32(float(manaReduction) * manaMultiplier);

        int32 manaTaken = -victim->ModifyPower(POWER_MANA, -manaReduction);

        // take case when mana has ended up into account
        currentAbsorb = currentAbsorb ? int32(float(currentAbsorb) * (float(manaTaken) / float(manaReduction))) : 0;

        dmgInfo.AbsorbDamage(currentAbsorb);

        tempAbsorb = currentAbsorb;
        absorbAurEff->GetBase()->CallScriptEffectAfterManaShieldHandlers(absorbAurEff, aurApp, dmgInfo, tempAbsorb);

        // Check if our aura is using amount to count damage
        if (absorbAurEff->GetAmount() >= 0)
        {
            absorbAurEff->SetAmount(absorbAurEff->GetAmount() - currentAbsorb);
            if ((absorbAurEff->GetAmount() <= 0))
                absorbAurEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
        }
    }

    // split damage auras - only when not damaging self
    if (victim != this)
    {
        // We're going to call functions which can modify content of the list during iteration over it's elements
        // Let's copy the list so we can prevent iterator invalidation
        AuraEffectList* vSplitDamagePctCopy(victim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_PCT));
        if (vSplitDamagePctCopy)
        for (AuraEffectList::iterator itr = vSplitDamagePctCopy->begin(); (itr != vSplitDamagePctCopy->end()) &&  (dmgInfo.GetDamage() > 0); ++itr)
        {
            AuraEffect* splitAurEff = *itr;
            // Check if aura was removed during iteration - we don't need to work on such auras
            AuraApplication const* aurApp = splitAurEff->GetBase()->GetApplicationOfTarget(victim->GetGUID());
            if (!aurApp)
                continue;

            // Check if aura was removed during iteration - we don't need to work on such auras
            if (!(splitAurEff->GetBase()->IsAppliedOnTarget(victim->GetGUID())))
                continue;
            // check damage school mask
            if (!(splitAurEff->GetMiscValue() & schoolMask))
                continue;

            // Damage can be splitted only if aura has an alive caster
            Unit* caster = splitAurEff->GetCaster();
            if (!caster || (caster == victim) || !caster->IsInWorld() || !caster->isAlive())
                continue;

            int32 splitDamage = CalculatePct(dmgInfo.GetDamage(), splitAurEff->GetAmount());

            // absorb must be smaller than the damage itself
            splitDamage = RoundToInterval(splitDamage, 0, int32(dmgInfo.GetDamage()));

            dmgInfo.AbsorbDamage(splitDamage);

            uint32 splitted = splitDamage;

            auto const& aurEffInfo = splitAurEff->GetSpellInfo();

            SpellNonMeleeDamage damageInfo(this, caster, aurEffInfo->Id, aurEffInfo->GetSpellXSpellVisualId(caster), schoolMask);

            if (caster->IsImmunedToDamage(schoolMask))
            {
                damageInfo.resist = splitted;
                splitted = 0;
            }

            damageInfo.damage = splitted;
            DealDamageMods(caster, damageInfo.damage, &damageInfo.absorb);

            uint32 m_procVictim = PROC_FLAG_DONE_MELEE_AUTO_ATTACK | PROC_FLAG_DONE_MAINHAND_ATTACK;
            uint32 m_procAttacker = PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK;
            if(spellInfo)
            {
                switch (spellInfo->Categories.DefenseType)
                {
                    case SPELL_DAMAGE_CLASS_MELEE:
                        m_procVictim   = PROC_FLAG_TAKEN_SPELL_MELEE_DMG_CLASS;
                        m_procAttacker = PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS;
                        m_procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_POS_NEG;
                        m_procAttacker |= PROC_FLAG_DONE_MAINHAND_ATTACK;
                        break;
                    case SPELL_DAMAGE_CLASS_MAGIC:
                        m_procAttacker = PROC_FLAG_DONE_SPELL_MAGIC_DMG_POS_NEG;
                        m_procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG;
                        m_procVictim   = PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG;
                        break;
                    case SPELL_DAMAGE_CLASS_NONE:
                        m_procVictim   = PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG;
                    break;
                    case SPELL_DAMAGE_CLASS_RANGED:
                        m_procAttacker = PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS;
                        m_procVictim   = PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS;
                        break;
                }
            }

            DamageInfo dmgInfoProc = DamageInfo(damageInfo, aurEffInfo);
            ProcDamageAndSpell(caster, m_procAttacker, m_procVictim, PROC_EX_NORMAL_HIT, &dmgInfoProc, BASE_ATTACK, aurEffInfo);

            SendSpellNonMeleeDamageLog(&damageInfo);

            CleanDamage cleanDamage = CleanDamage(splitted, 0, BASE_ATTACK, MELEE_HIT_NORMAL);
            DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, aurEffInfo, false);

            float tempAbsorb = splitDamage;
            splitAurEff->GetBase()->CallScriptEffectSplitDamageHandlers(splitAurEff, aurApp, dmgInfoProc, tempAbsorb);
        }
    }

    *resist = dmgInfo.GetResist();
    *absorb = dmgInfo.GetAbsorb();
}

void Unit::CalcHealAbsorb(Unit* victim, const SpellInfo* healSpell, uint32 &healAmount, uint32 &absorb)
{
    if (!healAmount)
        return;

    int32 RemainingHeal = healAmount;

    // Need remove expired auras after
    bool existExpired = false;

    DamageInfo dmgInfo = DamageInfo(this, victim, healAmount, healSpell, SpellSchoolMask(healSpell->GetMisc(m_spawnMode)->MiscData.SchoolMask), SPELL_DIRECT_DAMAGE, 0);

    // absorb without mana cost
    AuraEffectList const* vHealAbsorb = victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_HEAL_ABSORB);
    if (vHealAbsorb)
    for (AuraEffectList::const_iterator i = vHealAbsorb->begin(); i != vHealAbsorb->end() && RemainingHeal > 0; ++i)
    {
        AuraEffect* absorbAurEff = *i;
        AuraApplication const* aurApp = absorbAurEff->GetBase()->GetApplicationOfTarget(victim->GetGUID());
        if (!aurApp)
            continue;

        if (!((*i)->GetMiscValue() & healSpell->GetMisc(m_spawnMode)->MiscData.SchoolMask))
            continue;

        // Max Amount can be absorbed by this aura
        float currentAbsorb = (*i)->GetAmount();

        float tempAbsorb = currentAbsorb;
        bool defaultPrevented = false;
        absorbAurEff->GetBase()->CallScriptEffectAbsorbHandlers(absorbAurEff, aurApp, dmgInfo, tempAbsorb, defaultPrevented);
        currentAbsorb = tempAbsorb;

        // currentAbsorb - damage can be absorbed by shield
        // If need absorb less damage
        if (RemainingHeal < currentAbsorb)
            currentAbsorb = RemainingHeal;

        RemainingHeal -= currentAbsorb;

        dmgInfo.AbsorbDamage(currentAbsorb);

        tempAbsorb = currentAbsorb;
        absorbAurEff->GetBase()->CallScriptEffectAfterAbsorbHandlers(absorbAurEff, aurApp, dmgInfo, tempAbsorb);

        // Reduce shield amount
        if ((*i)->GetAmount() >= 0)
        {
            (*i)->SetAmount((*i)->GetAmount() - currentAbsorb);
            // Need remove it later
            if ((*i)->GetAmount() <= 0)
                existExpired = true;
        }
    }

    // Remove all expired absorb auras
    if (existExpired && vHealAbsorb)
    {
        for (AuraEffectList::const_iterator i = vHealAbsorb->begin(); i != vHealAbsorb->end();)
        {
            AuraEffect* auraEff = *i;
            ++i;
            if (auraEff->GetAmount() <= 0)
            {
                uint32 removedAuras = victim->m_removedAurasCount;
                auraEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL);
                if (removedAuras+1 < victim->m_removedAurasCount)
                    i = vHealAbsorb->begin();
            }
        }
    }

    absorb = RemainingHeal > 0 ? (healAmount - RemainingHeal) : healAmount;
    healAmount = RemainingHeal;
}

float Unit::CalcAbsorb(Unit* victim, SpellInfo const* spellProto, float amount)
{
    if (!victim || !spellProto)
        return amount;

    if (spellProto->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS) || spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS) || spellProto->HasAttribute(SPELL_ATTR6_NOT_LIMIT_ABSORB))
        return amount;

    if (SpellBonusEntry const* bonus = sSpellMgr->GetSpellBonusData(spellProto->Id))
    {
        if (bonus->direct_damage > 0)
            amount += bonus->direct_damage * GetSpellPowerDamage(spellProto->GetSchoolMask());
        else if (bonus->ap_bonus > 0)
            amount += bonus->ap_bonus * GetTotalAttackPowerValue(BASE_ATTACK);
    }

    return amount;
}

void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra, uint32 replacementAttackTrigger, uint32 replacementAttackAura, SpellSchoolMask schoolMask)
{
    if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) || HasAuraType(SPELL_AURA_DISABLE_AUTO_ATTACK) || HasAuraType(SPELL_AURA_DISABLE_ATTACK_AND_CAST))
        return;

    if (!victim->isAlive())
        return;

    if (!IsAIEnabled || !GetMap()->Instanceable())
        if ((attType == BASE_ATTACK || attType == OFF_ATTACK) && !IsWithinLOSInMap(victim))
            return;

    CombatStart(victim);
    RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MELEE_ATTACK);

    if (attType != BASE_ATTACK && attType != OFF_ATTACK)
        return;                                             // ignore ranged case

    if (replacementAttackTrigger)
        CastSpell(victim, replacementAttackTrigger, true);
    else
    {
        // melee attack spell casted at main hand attack only - no normal melee dmg dealt
        if (attType == BASE_ATTACK && m_currentSpells[CURRENT_MELEE_SPELL] && !extra)
            m_currentSpells[CURRENT_MELEE_SPELL]->cast();
        else
        {
            // attack can be redirected to another target
            Unit* redirectVictim = GetMeleeHitRedirectTarget(victim);

            CalcDamageInfo damageInfo;
            CalculateMeleeDamage(redirectVictim, 0, &damageInfo, attType, schoolMask);
            DealDamageMods(redirectVictim, damageInfo.damage, &damageInfo.absorb);

            //TriggerAurasProcOnEvent(damageInfo);
            DamageInfo dmgInfoProc = DamageInfo(damageInfo);
            ProcDamageAndSpell(victim, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, &dmgInfoProc, damageInfo.attackType);
            if(redirectVictim != victim)
            {
                dmgInfoProc.SetMagnet(victim->GetGUID());
                ProcDamageAndSpell(redirectVictim, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, &dmgInfoProc, damageInfo.attackType);
            }

            if (IsCreature() && ToCreature()->IsDespawn())
                return;

            // Send log damage message to client
            SendAttackStateUpdate(&damageInfo);
            DealMeleeDamage(&damageInfo, true);

            if (IsPlayer())
                TC_LOG_DEBUG(LOG_FILTER_UNITS, "AttackerStateUpdate: (Player) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
                    GetGUIDLow(), redirectVictim->GetGUIDLow(), redirectVictim->GetTypeId(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist);
            else
                TC_LOG_DEBUG(LOG_FILTER_UNITS, "AttackerStateUpdate: (NPC)    %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
                    GetGUIDLow(), redirectVictim->GetGUIDLow(), redirectVictim->GetTypeId(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist);
            if (replacementAttackAura)
                RemoveAura(replacementAttackAura);
        }
    }
}

void Unit::HandleProcExtraAttackFor(Unit* victim)
{
    while (m_extraAttacks)
    {
        AttackerStateUpdate(victim, BASE_ATTACK, true);
        --m_extraAttacks;
    }
}

MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType) const
{
    if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode())
        return MELEE_HIT_EVADE;

    // Miss chance based on melee
    float miss_chance = MeleeSpellMissChance(victim, attType, 0);

    // Critical hit chance
    float crit_chance = GetUnitCriticalChance(attType, victim);

    float dodge_chance = GetUnitDodgeChance(attType, victim);
    float block_chance = GetUnitBlockChance(attType, victim);
    float parry_chance = GetUnitParryChance(attType, victim);

    // melee attack table implementation
    // outcome priority:
    //   1. >    2. >    3. >       4. >    5. >   6. >       7. >  8.
    // MISS > DODGE > PARRY > GLANCING > BLOCK > CRIT > CRUSHING > HIT

    float roll_chance = (float)rand_chance();
    float base_chance = 0.0f;

    int32 attackerLevel = getLevelForTarget(victim);
    int32 victimLevel = getLevelForTarget(this);

    // check if attack comes from behind, nobody can parry or block if attacker is behind
    bool canParryOrBlock = victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION);

    // only creatures can dodge if attacker is behind
    bool canDodge = !victim->IsPlayer() || canParryOrBlock;

    // if victim is casting or cc'd it can't avoid attacks
    if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED))
    {
        canDodge = false;
        canParryOrBlock = false;
    }

    // 1. MISS
    base_chance += miss_chance;
    if (miss_chance > 0.0f)
        if (base_chance > roll_chance)
            return MELEE_HIT_MISS;

    // always crit against a sitting target (except 0 crit chance)
    if (victim->IsPlayer() && crit_chance > 0 && !victim->IsStandState())
        return MELEE_HIT_CRIT;

    // 2. DODGE
    if (canDodge && dodge_chance > 0.0f)
    {
        base_chance += dodge_chance;
        if (base_chance > roll_chance)
            return MELEE_HIT_DODGE;
    }

    // 3. PARRY
    if (canParryOrBlock && parry_chance > 0.0f)
    {
        base_chance += parry_chance;
        if (base_chance > roll_chance)
            return MELEE_HIT_PARRY;
    }

    // 4. GLANCING
    // Max 40% chance to score a glancing blow against mobs that are higher level (can do only players and pets and not with ranged weapon)
    if ((IsPlayer() || isPet()) && !victim->IsPlayer() && !victim->isPet() && attackerLevel + 3 < victimLevel)
    {
        // cap possible value (with bonuses > max skill)
        float glancing_chance = 10 + 10 * (victimLevel - attackerLevel);

        base_chance += glancing_chance;
        if (base_chance > roll_chance)
            return MELEE_HIT_GLANCING;
    }

    // 5. BLOCK
    if (canParryOrBlock && block_chance > 0.0f)
    {
        base_chance += block_chance;
        if (base_chance > roll_chance)
            return MELEE_HIT_BLOCK;
    }

    // 6.CRIT
    if (crit_chance > 0.0f)
    {
        base_chance += crit_chance;
        if (base_chance > roll_chance)
            return MELEE_HIT_CRIT;
    }

    // 7. CRUSHING
    // mobs can score crushing blows if they're 4 or more levels above victim
    if (attackerLevel >= victimLevel + 4 &&
        // can be from by creature (if can) or from controlled player that considered as creature
        !IsControlledByPlayer() &&
        !(GetTypeId() == TYPEID_UNIT && ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH))
    {
        // add 10% chance per level, min. is 15%
        float crushing_chance = (attackerLevel - victimLevel) * 10.0f - 15.0f;

        base_chance += crushing_chance;
        if (base_chance > roll_chance)
            return MELEE_HIT_CRUSHING;
    }

    // 8. HIT
    return MELEE_HIT_NORMAL;
}

bool Unit::isVendor() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR);
}

bool Unit::isTrainer() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER);
}

bool Unit::isQuestGiver() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER);
}

bool Unit::isGossip() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
}

bool Unit::isTaxi() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_FLIGHTMASTER);
}

bool Unit::isGuildMaster() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_PETITIONER);
}

bool Unit::isBattleMaster() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_BATTLEMASTER);
}

bool Unit::isBanker() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_BANKER);
}

bool Unit::isInnkeeper() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_INNKEEPER);
}

bool Unit::isSpiritHealer() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER);
}

bool Unit::isSpiritGuide() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITGUIDE);
}

bool Unit::isTabardDesigner()const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_TABARDDESIGNER);
}

bool Unit::isAuctioner() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_AUCTIONEER);
}

bool Unit::isArmorer() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_REPAIR);
}

bool Unit::isWildBattlePet() const
{
    return HasFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_WILD_BATTLE_PET);
}

bool Unit::isServiceProvider() const
{
    uint32 constexpr flags = UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_FLIGHTMASTER | UNIT_NPC_FLAG_PETITIONER | UNIT_NPC_FLAG_BATTLEMASTER |
        UNIT_NPC_FLAG_BANKER | UNIT_NPC_FLAG_INNKEEPER | UNIT_NPC_FLAG_SPIRITHEALER | UNIT_NPC_FLAG_SPIRITGUIDE | UNIT_NPC_FLAG_TABARDDESIGNER | UNIT_NPC_FLAG_AUCTIONEER;
    return HasFlag(UNIT_FIELD_NPC_FLAGS, flags);
}

bool Unit::isSpiritService() const
{
    uint32 constexpr flags = UNIT_NPC_FLAG_SPIRITHEALER | UNIT_NPC_FLAG_SPIRITGUIDE;
    return HasFlag(UNIT_FIELD_NPC_FLAGS, flags);
}

bool Unit::isInFlight() const
{
    return HasUnitState(UNIT_STATE_IN_FLIGHT);
}

bool Unit::isInCombat() const
{
    return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
}

uint32 Unit::CalculateDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, Unit* victim)
{
    float min_damage, max_damage = 0.0f;

    if (IsPlayer() && (normalized || !addTotalPct))
        ToPlayer()->CalculateMinMaxDamage(attType, normalized, addTotalPct, min_damage, max_damage);
    else if (Creature* creature = ToCreature())
    {
        float weapon_mindamage = 0;
        float weapon_maxdamage = 0;

        uint8 level = getLevelForTarget(victim);
        CreatureLevelStat const* scaleStat = creature->GetScaleLevelStat(level);
        if (!CanUseAttackType(attType))
        {
            weapon_mindamage = 0;
            weapon_maxdamage = 0;
        }
        else if (scaleStat)
        {
            weapon_mindamage = scaleStat->baseMinDamage;
            weapon_maxdamage = scaleStat->baseMaxDamage;
        }
        else
        {
            weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
            weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
        }

        switch (attType)
        {
            case RANGED_ATTACK:
            case BASE_ATTACK:
                min_damage = ((m_base_value + weapon_mindamage) * m_dmg_multiplier * m_base_pct + m_total_value) * (addTotalPct ? m_total_pct : 1.0f);
                max_damage = ((m_base_value + weapon_maxdamage) * m_dmg_multiplier * m_base_pct + m_total_value) * (addTotalPct ? m_total_pct : 1.0f);
                break;
            case OFF_ATTACK:
                min_damage = (((m_base_value + weapon_mindamage) * m_dmg_multiplier * m_base_pct + m_total_value) * (addTotalPct ? m_total_pct : 1.0f)) / 2;
                max_damage = (((m_base_value + weapon_maxdamage) * m_dmg_multiplier * m_base_pct + m_total_value) * (addTotalPct ? m_total_pct : 1.0f)) / 2;
                break;
                // Just for good manner
            default:
                min_damage = 0.0f;
                max_damage = 0.0f;
                break;
        }
    }
    else
    {
        switch (attType)
        {
            case RANGED_ATTACK:
                min_damage = GetFloatValue(UNIT_FIELD_MIN_RANGED_DAMAGE);
                max_damage = GetFloatValue(UNIT_FIELD_MAX_RANGED_DAMAGE);
                break;
            case BASE_ATTACK:
                min_damage = GetFloatValue(UNIT_FIELD_MIN_DAMAGE);
                max_damage = GetFloatValue(UNIT_FIELD_MAX_DAMAGE);
                break;
            case OFF_ATTACK:
                min_damage = GetFloatValue(UNIT_FIELD_MIN_OFF_HAND_DAMAGE);
                max_damage = GetFloatValue(UNIT_FIELD_MAX_OFF_HAND_DAMAGE);
                break;
                // Just for good manner
            default:
                min_damage = 0.0f;
                max_damage = 0.0f;
                break;
        }
    }

    if (min_damage < 0 || min_damage >= std::numeric_limits<uint32>::max())
        min_damage = 1.0f;

    if (max_damage < 0 || max_damage >= std::numeric_limits<uint32>::max())
        max_damage = 1.0f;

    if (max_damage == 0.0f)
        max_damage = 5.0f;

    if (min_damage > max_damage)
        std::swap(min_damage, max_damage);

    return urand((uint32)min_damage, (uint32)max_damage);
}

void Unit::SendMeleeAttackStart(Unit* victim)
{
    WorldPackets::Combat::AttackStart packet;
    packet.Attacker = GetGUID();
    packet.Victim = victim->GetGUID();
    SendMessageToSet(packet.Write(), true);
}

void Unit::SendMeleeAttackStop(Unit* victim)
{
    WorldPackets::Combat::SAttackStop packet;
    packet.Attacker = GetGUID();
    if (victim)
    {
        packet.Victim = victim->GetGUID();
        packet.Dead = victim->isDead();
    }

    SendMessageToSet(packet.Write(), true);

    if (victim)
        TC_LOG_INFO(LOG_FILTER_UNITS, "%s %u stopped attacking %s %u", (IsPlayer() ? "Player" : "Creature"), GetGUIDLow(), (victim->IsPlayer() ? "player" : "creature"), victim->GetGUIDLow());
    else
        TC_LOG_INFO(LOG_FILTER_UNITS, "%s %u stopped attacking", (IsPlayer() ? "Player" : "Creature"), GetGUIDLow());
}

bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attType)
{
    if (spellProto && !spellProto->GetAuraOptions(GetSpawnMode())->IsProcAura && IsCreature() && ToCreature()->IsDungeonBoss() && GetMap()->IsRaid())
        return false;

    // These spells can't be blocked
    if (spellProto && (spellProto->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK) || spellProto->HasAttribute(SPELL_ATTR0_CU_IGNORE_AVOID_MECHANIC)))
        return false;

    if (spellProto->Categories.DefenseType == SPELL_DAMAGE_CLASS_RANGED)
        return false;

    // Can't block when casting/controlled
    if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED))
        return false;

    if (victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION) || victim->HasInArc(M_PI, this))
    {
        // Check creatures flags_extra for disable block
        if (victim->IsCreature() && victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK)
            return false;

        if (roll_chance_f(GetUnitBlockChance(attType, victim)))
            return true;
    }
    return false;
}

bool Unit::isBlockCritical()
{
    if (roll_chance_i(GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_CRIT_CHANCE)))
    {
        // Critical Blocks enrage the warrior
        if (HasAura(76857))
            CastSpell(this, 12880, true);
        return true;
    }

    return false;
}

int32 Unit::GetMechanicResistChance(SpellInfo const* spellInfo) const
{
    if (!spellInfo)
        return 0;

    int32 resist_mech = 0;
    for (uint8 eff = 0; eff < MAX_SPELL_EFFECTS; ++eff)
    {
        if (spellInfo->EffectMask < uint32(1 << eff))
            break;

        if (!spellInfo->GetEffect(eff, GetSpawnMode())->IsEffect())
           break;

        int32 effect_mech = spellInfo->GetEffectMechanic(eff);
        if (effect_mech)
        {
            int32 temp = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, effect_mech);
            if (resist_mech < temp)
                resist_mech = temp;
        }
    }

    return std::max(resist_mech, 0);
}

bool Unit::CanUseAttackType(uint8 attacktype) const
{
    switch (attacktype)
    {
        case BASE_ATTACK:
            return !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED);
        case OFF_ATTACK:
            return !HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_OFFHAND);
        case RANGED_ATTACK:
            return !HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_RANGED);
    }
    return true;
}

// Melee based spells hit result calculations
SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const
{
    // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will additionally fully ignore
    // resist and deflect chances
    if (spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT) || spellInfo->HasAttribute(SPELL_ATTR8_CANT_MISS))
        return SPELL_MISS_NONE;

    WeaponAttackType attType = BASE_ATTACK;

    // Check damage class instead of attack type to correctly handle judgements
    // - they are meele, but can't be dodged/parried/deflected because of ranged dmg class
    if (spellInfo->Categories.DefenseType == SPELL_DAMAGE_CLASS_RANGED)
        attType = RANGED_ATTACK;

    float base_chance = 0.0f;
    float roll_chance = (float)rand_chance();
    float miss_chance = MeleeSpellMissChance(victim, attType, spellInfo->Id);

    // Roll miss
    base_chance = miss_chance;
    if (miss_chance > 0.0f)
        if (roll_chance_f(miss_chance))
            return SPELL_MISS_MISS;

    // Chance resist mechanic
    float resist_chance = victim->GetMechanicResistChance(spellInfo);
    base_chance += resist_chance;
    if (resist_chance > 0.0f)
        if (base_chance > roll_chance)
            return SPELL_MISS_RESIST;

    // Same spells cannot be parried/dodged
    if (spellInfo->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK))
        return SPELL_MISS_NONE;

    if (!spellInfo->GetAuraOptions(GetSpawnMode())->IsProcAura && IsCreature() && ToCreature()->IsDungeonBoss() && GetMap()->IsRaid())
        return SPELL_MISS_NONE;

    bool canDodge = true;
    bool canParry = true;
    bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL);

    // if victim is casting or cc'd it can't avoid attacks
    if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED))
    {
        canDodge = false;
        canParry = false;
        canBlock = false;
    }

    // Ranged attacks can only miss, resist and deflect
    if (attType == RANGED_ATTACK)
    {
        canParry = false;
        canDodge = true;

        // only if in front
        if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)))
        {
            float deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS);
            base_chance += deflect_chance;
            if (deflect_chance > 0.0f)
                if (base_chance > roll_chance)
                    return SPELL_MISS_DEFLECT;
        }
    }

    if (spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_AVOID_MECHANIC))
        return SPELL_MISS_NONE;

    // Check for attack from behind
    if (!victim->HasInArc(float(M_PI), this))
    {
        if (!victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))
        {
            // Can't dodge from behind in PvP (but its possible in PvE)
            if (victim->IsPlayer())
                canDodge = false;
            // Can't parry or block
            canParry = false;
            canBlock = false;
        }
        else // Only deterrence as of 3.3.5
        {
            if (spellInfo->HasAttribute(SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET))
                canParry = false;
        }
    }

    if (spellInfo->Id == 247675) // Hammer of Reckoning
        canParry = false;

    if (canDodge)
    {
        // Roll dodge
        float dodge_chance = GetUnitDodgeChance(attType, victim);
        if (dodge_chance < 0.0f)
            dodge_chance = 0.0f;

        base_chance += dodge_chance;
        if (dodge_chance > 0.0f)
            if (base_chance > roll_chance)
                return SPELL_MISS_DODGE;
    }

    if (canParry)
    {
        // Roll parry
        float parry_chance = GetUnitParryChance(attType, victim);
        if (parry_chance < 0.0f)
            parry_chance = 0.0f;

        base_chance += parry_chance;
        if (parry_chance > 0.0f)
            if (base_chance > roll_chance)
                return SPELL_MISS_PARRY;
    }

    if (canBlock)
    {
        float block_chance = GetUnitBlockChance(attType, victim);
        if (block_chance < 0.0f)
            block_chance = 0.0f;

        base_chance += block_chance;
        if (block_chance > 0.0f)
            if (base_chance > roll_chance)
                return SPELL_MISS_BLOCK;
    }

    return SPELL_MISS_NONE;
}

/// @todo need use unit spell resistances in calculations
SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const
{
    // Can`t miss on dead target (on skinning for example)
    if ((!victim->isAlive() && !victim->IsPlayer()) || spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT))
        return SPELL_MISS_NONE;

    SpellSchoolMask schoolMask = spellInfo->GetSchoolMask();
    float miss_chance = victim->GetUnitMissChance(this, spellInfo->Id);

    // Base hit chance from attacker and victim levels
    float hit_chance = 100.0f - miss_chance;

    // Spellmod from SPELLMOD_RESIST_MISS_CHANCE
    if (Player* modOwner = GetSpellModOwner())
        modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_MISS_CHANCE, hit_chance);

    // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects
    if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT))
    {
        // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras
        int32 bestVal = 0;
        bool firstCheck = false;
        for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
            if (schoolMask & (1 << i))
            {
                int32 amount = victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, (1 << i)); // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras
                if (bestVal < amount || !firstCheck)
                {
                    bestVal = amount;
                    firstCheck = true;
                }
            }

        hit_chance += bestVal;
    }

    // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings
    hit_chance += m_modSpellHitChance;

    if (IsCreature() && ToCreature()->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_BOSS)
        hit_chance = 100.0f;

    RoundToInterval(hit_chance, 0.0f, 100.0f);

    float base_chance = 100.0f - hit_chance;
    float roll_chance = (float)rand_chance();

    if (roll_chance_f(base_chance))
        return SPELL_MISS_MISS;

    // Chance resist mechanic (select max value from every mechanic spell effect)
    float resist_chance = victim->GetMechanicResistChance(spellInfo);

    // Roll chance
    base_chance += resist_chance;
    if (resist_chance > 0.0f)
        if (base_chance > roll_chance)
            return SPELL_MISS_RESIST;

    // cast by caster in front of victim
    if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)))
    {
        float deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS);
        base_chance += deflect_chance;
        if (deflect_chance > 0.0f)
            if (base_chance > roll_chance)
                return SPELL_MISS_DEFLECT;
    }

    return SPELL_MISS_NONE;
}

// Calculate spell hit result can be:
// Every spell can: Evade/Immune/Reflect/Sucesful hit
// For melee based spells:
//   Miss
//   Dodge
//   Parry
// For spells
//   Resist
SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool CanReflect, uint32 effectMask)
{
    if (this == victim)
        return SPELL_MISS_NONE;

    // Check for immune
    if (victim->IsImmunedToSpell(spellInfo))
        return SPELL_MISS_IMMUNE;

    // All positive spells can`t miss
    /// @todo client not show miss log for this spells - so need find info for this in dbc and use it!
    bool positive = false;
    for (uint32 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
    {
        if (spellInfo->EffectMask < uint32(1 << effIndex))
            break;

        if (effectMask & (1 << effIndex))
            positive = spellInfo->IsPositiveEffect(effIndex);
    }

    if (positive && !IsHostileTo(victim)) // prevent from affecting enemy by "positive" spell
        return SPELL_MISS_NONE;

    // Check for immune
    //if (victim->IsImmunedToDamage(spellInfo))
    //    return SPELL_MISS_IMMUNE;

    // Return evade for units in evade mode
    if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode())
        return SPELL_MISS_EVADE;

    // Try victim reflect spell
    if (CanReflect)
    {
        int32 reflectchance = victim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS);
        if (Unit::AuraEffectList const* mReflectSpellsSchool = victim->GetAuraEffectsByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL))
        for (Unit::AuraEffectList::const_iterator i = mReflectSpellsSchool->begin(); i != mReflectSpellsSchool->end(); ++i)
            if ((*i)->GetMiscValue() & spellInfo->GetSchoolMask())
                reflectchance += (*i)->GetAmount();

        if (reflectchance > 0 && roll_chance_i(reflectchance))
            return SPELL_MISS_REFLECT;
    }

    Unit* checker = (IsCreature() && GetAnyOwner()) ? GetAnyOwner() : this;
    switch (spellInfo->Categories.DefenseType)
    {
        case SPELL_DAMAGE_CLASS_RANGED:
        case SPELL_DAMAGE_CLASS_MELEE:
            return checker->MeleeSpellHitResult(victim, spellInfo);
        case SPELL_DAMAGE_CLASS_NONE:
            return SPELL_MISS_NONE;
        case SPELL_DAMAGE_CLASS_MAGIC:
            return checker->MagicSpellHitResult(victim, spellInfo);
    }
    return SPELL_MISS_NONE;
}

// Calculate spell hit result can be:
// Every spell can: Evade/Immune/Reflect/Sucesful hit
// For melee based spells:
//   Miss
//   Dodge
//   Parry
// For spells
//   Resist

float Unit::GetUnitMissChance(Unit const* attacker, uint32 spellId /*=0*/) const
{
    uint8 thisLevel = attacker->getLevelForTarget(this);
    uint8 targetLevel = getLevelForTarget(attacker);
    int32 leveldif = 0;
    float miss_chance = 0.0f;

    if (attacker->IsCreature())
    {
        if (spellId)
            thisLevel += 3;
        leveldif = thisLevel - targetLevel;
        miss_chance = 9.0f - float(leveldif * 3);
    }
    else if (targetLevel >= thisLevel)
    {
        leveldif = targetLevel - thisLevel;

        if (leveldif > 3)
            miss_chance = float(leveldif);
    }

    return miss_chance;
}

float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const
{
    if (!victim->HasAuraWithAttribute(10, SPELL_ATTR10_CAN_DODGE_ON_CAST) && (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)))
        return 0.0f;

    int32 const levelDiff = victim->getLevelForTarget(this) - getLevelForTarget(victim);

    float chance = 0.0f;
    float levelBonus = 0.0f;
    if (victim->IsPlayer())
        chance = victim->GetFloatValue(PLAYER_FIELD_DODGE_PERCENTAGE);
    else
    {
        if (!victim->isTotem())
        {
            chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);

            if (levelDiff > 3)
            {
                levelBonus = 1.5f * (levelDiff - 3);
            }
        }
    }

    chance += levelBonus;

    // reduce dodge by SPELL_AURA_MOD_ENEMY_DODGE
    chance += GetTotalAuraModifier(SPELL_AURA_MOD_ENEMY_DODGE);

    // Reduce dodge chance by attacker expertise rating
    if (IsPlayer())
        chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType);
    else
        chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f;
    return std::max(chance, 0.0f);
}

float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const
{
    if (!victim->HasAuraWithAttribute(10, SPELL_ATTR10_CAN_PARRY_ON_CAST) && (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)))
        return 0.0f;

    int32 const levelDiff = victim->getLevelForTarget(this) - getLevelForTarget(victim);

    float chance = 0.0f;
    float levelBonus = 0.0f;
    if (Player const* playerVictim = victim->ToPlayer())
    {
        if (playerVictim->CanParry())
        {
            chance = playerVictim->GetFloatValue(PLAYER_FIELD_PARRY_PERCENTAGE);
        }
    }
    else
    {
        if (!victim->isTotem())
        {
            if (Creature const* cre = victim->ToCreature())
            {
                if (!(cre->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY))
                {
                    chance = cre->isWorldBoss() ? 3.f : 0.f;
                    chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);

                    if (levelDiff > 3)
                        levelBonus = 1.5f * (levelDiff - 3);
                }
            }
        }
    }

    chance += levelBonus;

    // Reduce parry chance by attacker expertise rating
    if (IsPlayer())
        chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType);
    else
        chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f;
    return std::max(chance, 0.0f);
}

float Unit::GetUnitBlockChance(WeaponAttackType /*attType*/, Unit const* victim) const
{
    if (!victim->HasAuraWithAttribute(10, SPELL_ATTR10_CAN_PARRY_ON_CAST) && (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)))
        return 0.0f;

    int32 const levelDiff = victim->getLevelForTarget(this) - getLevelForTarget(victim);

    float chance = 0.0f;
    float levelBonus = 0.0f;
    if (Player const* playerVictim = victim->ToPlayer())
    {
        if (playerVictim->CanBlock())
        {
            Item* tmpitem = playerVictim->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
            if (tmpitem && !tmpitem->IsBroken() && tmpitem->GetTemplate()->GetInventoryType() == INVTYPE_SHIELD)
                chance = playerVictim->GetFloatValue(PLAYER_FIELD_BLOCK_PERCENTAGE);
        }
    }
    else
    {
        if (!victim->isTotem() && !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK))
        {
            chance = 3.0f;
            chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);

            if (levelDiff > 0)
                levelBonus = 1.5f * levelDiff;
        }
    }

    chance += levelBonus;
    return std::max(chance, 0.0f);
}

float Unit::GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const
{
    float chance = 0.0f;
    if (IsPlayer())
    {
        switch (attackType)
        {
            case BASE_ATTACK:
                chance = GetFloatValue(PLAYER_FIELD_CRIT_PERCENTAGE);
                break;
            case OFF_ATTACK:
                chance = GetFloatValue(PLAYER_FIELD_OFFHAND_CRIT_PERCENTAGE);
                break;
            case RANGED_ATTACK:
                chance = GetFloatValue(PLAYER_FIELD_RANGED_CRIT_PERCENTAGE);
                break;
                // Just for good manner
            default:
                chance = 0.0f;
                break;
        }
    }
    else
    {
        if (!(ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRIT))
        {
            if (!HasUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER))
                chance = 5.0f;

            chance += GetTotalAuraModifier(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT);
            chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT);
        }
    }

    // flat aura mods
    if (attackType == RANGED_ATTACK)
        chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE);
    else
        chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE);

    if (AuraEffectList const* critChanceForCaster = victim->GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER))
    for (AuraEffect const* aurEff : *critChanceForCaster)
    {
        if (aurEff->GetCasterGUID() != GetGUID())
            continue;

        chance += aurEff->GetAmount();
    }

    chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);

    return std::max(chance, 0.0f);
}

void Unit::_DeleteRemovedAuras()
{
    if (m_removedAuras.empty())
        return;

    m_aura_is_lock = true;
    m_aura_lock.lock();
    while (!m_removedAuras.empty())
    {
        delete m_removedAuras.front();
        m_removedAuras.pop_front();
    }
    m_aura_lock.unlock();
    m_aura_is_lock = false;
}

void Unit::DestroyForPlayer(Player* target) const
{
    if (Battleground* bg = target->GetBattleground())
    {
        if (bg->IsArena())
        {
            WorldPackets::Update::DestroyArenaUnit destroyArenaUnit;
            destroyArenaUnit.Guid = GetGUID();
            target->SendDirectMessage(destroyArenaUnit.Write());
        }
    }

    WorldObject::DestroyForPlayer(target);
}

void Unit::_UpdateSpells(uint32 time)
{
    if (!IsInWorld() || m_cleanupDone || m_aura_is_lock)
        return;

    if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
        _UpdateAutoRepeatSpell();

    // remove finished spells from current pointers
    for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
    {
        if (m_currentSpells[i] && m_currentSpells[i]->getState() == SPELL_STATE_FINISHED)
        {
            m_currentSpells[i]->SetReferencedFromCurrent(false);
            m_currentSpells[i] = nullptr;                      // remove pointer
        }
    }

    // remove expired auras - do that after updates(used in scripts?)
    for (AuraMap::iterator i = m_ownedAuras.begin(); i != m_ownedAuras.end();)
    {
        if (i->second->IsExpired())
            RemoveOwnedAura(i, AURA_REMOVE_BY_EXPIRE);
        else
            ++i;
    }

    // m_auraUpdateIterator can be updated in indirect called code at aura remove to skip next planned to update but removed auras
    for (m_auraUpdateIterator = m_ownedAuras.begin(); m_auraUpdateIterator != m_ownedAuras.end();)
    {
        if (Aura* i_aura = m_auraUpdateIterator->second)
        {
            ++m_auraUpdateIterator;                            // need shift to next for allow update if need into aura update
            if (i_aura && !i_aura->IsRemoved())
                i_aura->UpdateOwner(time, this);
        }
    }

    for (AuraApplication* visibleAura : m_visibleAurasToUpdate)
        visibleAura->ClientUpdate();

    m_visibleAurasToUpdate.clear();

    _DeleteRemovedAuras();

    if (!m_gameObj.empty())
    {
        for (GameObjectList::iterator itr = m_gameObj.begin(); itr != m_gameObj.end();)
        {
            GameObject* gObj = *itr;
            if (itr == m_gameObj.end() || !gObj)
            {
                ++itr;
                continue;
            }

            if (!gObj->isSpawned())
            {
                gObj->SetOwnerGUID(ObjectGuid::Empty);
                gObj->SetRespawnTime(0);
                gObj->Delete();
                m_gameObj.erase(itr++);
            }
            else
                ++itr;
        }
    }
}

void Unit::_UpdateAutoRepeatSpell()
{
    if (HasAuraType(SPELL_AURA_DISABLE_ATTACK_AND_CAST) || HasAuraType(SPELL_AURA_DISABLE_AUTO_ATTACK))
    {
        InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
        m_AutoRepeatFirstCast = true;
        return;
    }

    // check "real time" interrupts
    // don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect
    SpellInfo const* spellProto = nullptr;
    if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
        spellProto = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo;

    if (!spellProto)
        return;

    if (((IsPlayer() && ToPlayer()->isMoving()) || spellProto && IsNonMeleeSpellCast(false, false, true, spellProto->Id == 75)) && 
        !HasAuraCastWhileWalking(spellProto))
    {
        // cancel wand shoot
        InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
        m_AutoRepeatFirstCast = true;
        return;
    }

    // apply delay (Auto Shot - 75 not affected)
    if (m_AutoRepeatFirstCast && getAttackTimer(RANGED_ATTACK) < 500 && spellProto->Id != 75)
        setAttackTimer(RANGED_ATTACK, 500);
    m_AutoRepeatFirstCast = false;

    // castroutine
    if (isAttackReady(RANGED_ATTACK))
    {
        if (Unit* curspellTarget = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets.GetUnitTarget())
            UpdateVictim(curspellTarget);

        float max_range = GetSpellMaxRangeForTarget(m_attacking, spellProto);

        if (m_attacking && IsWithinLOSInMap(m_attacking) && HasInArc(float(M_PI), m_attacking) && IsWithinMeleeRange(m_attacking, max_range))
        {
            TriggerCastData triggerData;
            triggerData.triggerFlags = TRIGGERED_FULL_MASK;

            // we want to shoot
            Spell* spell = new Spell(this, spellProto, triggerData);
            spell->prepare(&(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets));

            // all went good, reset attack
            resetAttackTimer(RANGED_ATTACK);
        }
    }
}

void Unit::SetCurrentCastedSpell(Spell* pSpell)
{
    ASSERT(pSpell);                                         // NULL may be never passed here, use InterruptSpell or InterruptNonMeleeSpells

    CurrentSpellTypes CSpellType = pSpell->GetCurrentContainer();

    if (pSpell == m_currentSpells[CSpellType])             // avoid breaking self
        return;

    if (m_currentSpells[CSpellType] && pSpell->m_spellInfo->HasAttribute(SPELL_ATTR4_TRIGGERED))
        return;

    // break same type spell if it is not delayed
    InterruptSpell(CSpellType, false, true, pSpell->m_spellInfo->Id);

    // special breakage effects:
    switch (CSpellType)
    {
        case CURRENT_GENERIC_SPELL:
        {
            // generic spells always break channeled not delayed spells
            if (m_currentSpells[CURRENT_CHANNELED_SPELL])
            {
                if (m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->CanInterruptChannel(pSpell->m_spellInfo))
                    InterruptSpell(CURRENT_CHANNELED_SPELL, false, true, pSpell->m_spellInfo->Id);
            }

            // autorepeat breaking
            if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
            {
                // break autorepeat if not Auto Shot
                if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id != 75)
                    InterruptSpell(CURRENT_AUTOREPEAT_SPELL, true, pSpell->m_spellInfo->Id);
                m_AutoRepeatFirstCast = true;
            }
            if (pSpell->m_spellInfo->CalcCastTime(this) > 0)
            {
                AddUnitState(UNIT_STATE_CASTING);

                if (pSpell->GetCaster()->IsCreature() && !(pSpell->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && !pSpell->m_spellInfo->HasChannelInterruptFlag(CHANNEL_INTERRUPT_FLAG_MOVE))
                    AddUnitState(UNIT_STATE_MOVE_IN_CASTING);
            }
            break;
        }
        case CURRENT_CHANNELED_SPELL:
        {
            // channel spells always break generic non-delayed and any channeled spells
            InterruptSpell(CURRENT_GENERIC_SPELL, false, true, pSpell->m_spellInfo->Id);
            InterruptSpell(CURRENT_CHANNELED_SPELL, true, true, pSpell->m_spellInfo->Id);

            // it also does break autorepeat if not Auto Shot
            if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id != 75)
                InterruptSpell(CURRENT_AUTOREPEAT_SPELL, true, true, pSpell->m_spellInfo->Id);

            if (!pSpell->m_spellInfo->HasAura(SPELL_AURA_FIXATE))
                AddUnitState(UNIT_STATE_CASTING);

            if (pSpell->GetCaster()->IsCreature() && !pSpell->m_spellInfo->HasChannelInterruptFlag(CHANNEL_INTERRUPT_FLAG_MOVE))
                AddUnitState(UNIT_STATE_MOVE_IN_CASTING);
            break;
        }
        case CURRENT_AUTOREPEAT_SPELL:
        {
            // only Auto Shoot does not break anything
            if (pSpell->m_spellInfo->Id != 75)
            {
                // generic autorepeats break generic non-delayed and channeled non-delayed spells
                InterruptSpell(CURRENT_GENERIC_SPELL, false, true, pSpell->m_spellInfo->Id);
                InterruptSpell(CURRENT_CHANNELED_SPELL, false, true, pSpell->m_spellInfo->Id);
            }
            // special action: set first cast flag
            m_AutoRepeatFirstCast = true;
            break;
        }
        default:
            break; // other spell types don't break anything now
    }

    // current spell (if it is still here) may be safely deleted now
    if (m_currentSpells[CSpellType])
        m_currentSpells[CSpellType]->SetReferencedFromCurrent(false);

    // set new current spell
    m_currentSpells[CSpellType] = pSpell;
    pSpell->SetReferencedFromCurrent(true);

    pSpell->m_selfContainer = &(m_currentSpells[pSpell->GetCurrentContainer()]);
}

void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool withInstant, uint32 spellID)
{
    ASSERT(spellType < CURRENT_MAX_SPELL);

    // TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Interrupt spell %u for unit %u spellType %u", spellID, GetEntry(), spellType);

    Spell* spell = m_currentSpells[spellType];
    if (spell && (withDelayed || spell->getState() != SPELL_STATE_DELAYED) && (withInstant || spell->GetCastTime() > 0) && spell->m_spellInfo->CanInterrupt(spellID, AURA_INTERRUPT_FLAG_CAST, this))
    {
        // for example, do not let self-stun aura interrupt itself
        if (!spell->IsInterruptable())
            return;

        auto curSpellInfo = spell->m_spellInfo;

        // send autorepeat cancel message for autorepeat spells
        if (spellType == CURRENT_AUTOREPEAT_SPELL)
            if (IsPlayer())
                ToPlayer()->SendCancelAutoRepeat(this);

        if (spell->getState() != SPELL_STATE_FINISHED)
            spell->cancel();

        m_currentSpells[spellType] = nullptr;
        spell->SetReferencedFromCurrent(false);

        if (auto tmpPlayer = ToPlayer())
            if (curSpellInfo->Misc.CastTimes.Base && !curSpellInfo->IsPassive() && tmpPlayer->HaveSpectators())
            {
                SpectatorAddonMsg msg;
                msg.SetPlayer(tmpPlayer->GetGUID());
                msg.CastSpell(curSpellInfo->Id, 99999);
                tmpPlayer->SendSpectatorAddonMsgToBG(msg);
            }
    }
}

void Unit::FinishSpell(CurrentSpellTypes spellType, bool ok /*= true*/)
{
    Spell* spell = m_currentSpells[spellType];
    if (!spell)
        return;

    if (spellType == CURRENT_CHANNELED_SPELL)
        spell->SendChannelUpdate(0);

    spell->finish(ok);
}

bool Unit::IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled, bool skipAutorepeat, bool isAutoshoot, bool skipInstant) const
{
    // We don't do loop here to explicitly show that melee spell is excluded.
    // Maybe later some special spells will be excluded too.

    // if skipInstant then instant spells shouldn't count as being casted
    if (skipInstant && m_currentSpells[CURRENT_GENERIC_SPELL] && !m_currentSpells[CURRENT_GENERIC_SPELL]->GetCastTime())
        return false;

    // generic spells are casted when they are not finished and not delayed
    if (m_currentSpells[CURRENT_GENERIC_SPELL] && (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) &&
        (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED))
    {
        if (!isAutoshoot || !(m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)))
            return true;
    }
    // channeled spells may be delayed, but they are still considered casted
    else if (!skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] && (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED))
    {
        if (!skipAutorepeat && m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR10_CAN_PARRY_ON_CAST))
            return false;

        if (!isAutoshoot || !(m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)))
            return true;
    }
    // autorepeat spells may be finished or delayed, but they are still considered casted
    else if (!skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
        return true;

    return false;
}

void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id, bool withInstant)
{
    // generic spells are interrupted if they are not finished or delayed
    if (m_currentSpells[CURRENT_GENERIC_SPELL] && (!spell_id || m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Id == spell_id))
        InterruptSpell(CURRENT_GENERIC_SPELL, withDelayed, withInstant);

    // autorepeat spells are interrupted if they are not finished or delayed
    if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && (!spell_id || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id == spell_id))
        InterruptSpell(CURRENT_AUTOREPEAT_SPELL, withDelayed, withInstant);

    // channeled spells are interrupted if they are not finished, even if they are delayed
    if (m_currentSpells[CURRENT_CHANNELED_SPELL] && (!spell_id || m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id == spell_id))
        InterruptSpell(CURRENT_CHANNELED_SPELL, true, true);
}

Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const
{
    for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
        if (m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id == spell_id)
            return m_currentSpells[i];
    return nullptr;
}

int32 Unit::GetCurrentSpellCastTime(uint32 spell_id) const
{
    if (Spell const* spell = FindCurrentSpellBySpellId(spell_id))
        return spell->GetCastTime();
    return 0;
}

bool Unit::isInFrontInMap(Unit const* target, float distance,  float arc) const
{
    return IsWithinDistInMap(target, distance) && HasInArc(arc, target);
}

bool Unit::isInBackInMap(Unit const* target, float distance, float arc) const
{
    return IsWithinDistInMap(target, distance) && !HasInArc(2 * M_PI - arc, target);
}

bool Unit::isInAccessiblePlaceFor(Creature const* c) const
{
    if(!c)
        return false;

    if (IsInWater())
        return c->CanSwim();
    return c->CanWalk() || c->CanFly();
}

bool Unit::IsInWater() const
{
    return Zliquid_status & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER);
}

bool Unit::IsUnderWater() const
{
    return Zliquid_status & LIQUID_MAP_UNDER_WATER;
}

void Unit::UpdateUnderwaterState(Map* m, float x, float y, float z)
{
    if (!isPet() && !IsVehicle())
        return;

    Zliquid_status = m->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquid_status);
    if (!Zliquid_status)
    {
        if (_lastLiquid && _lastLiquid->SpellID)
            RemoveAurasDueToSpell(_lastLiquid->SpellID);

        RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
        _lastLiquid = nullptr;
        return;
    }

    if (uint32 liqEntry = liquid_status.entry)
    {
        LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry);
        if (_lastLiquid && _lastLiquid->SpellID && _lastLiquid->ID != liqEntry)
            RemoveAurasDueToSpell(_lastLiquid->SpellID);

        if (liquid && liquid->SpellID)
        {
            if (Zliquid_status & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER))
            {
                if (!HasAura(liquid->SpellID))
                    CastSpell(this, liquid->SpellID, true);
            }
            else
                RemoveAurasDueToSpell(liquid->SpellID);
        }

        RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_ABOVEWATER);
        _lastLiquid = liquid;
    }
    else if (_lastLiquid && _lastLiquid->SpellID)
    {
        RemoveAurasDueToSpell(_lastLiquid->SpellID);
        RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
        _lastLiquid = nullptr;
    }
}

void Unit::DeMorph()
{
    SetDisplayId(GetNativeDisplayId());
    if (IsCreature())
        if (this->ToCreature()->IsMirrorImage())
            new MirrorImageUpdate(this->ToCreature());
}

Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint32 effMask, Unit* caster, float* baseAmount /*= NULL*/, Item* castItem /*= NULL*/, ObjectGuid casterGUID /*= 0*/)
{
    ASSERT(casterGUID || caster);
    if (casterGUID.IsEmpty())
    {
        if(!caster->ToCreature() || caster->isAnySummons() || isAnySummons())
            casterGUID = caster->GetGUID();
        else if(newAura->GetAuraOptions(GetSpawnMode())->CumulativeAura < 2)
            casterGUID = caster->GetGUID();
    }

    // passive and Incanter's Absorption and auras with different type can stack with themselves any number of times
    if (!newAura->IsMultiSlotAura())
    {
        // check if cast item changed
        ObjectGuid castItemGUID;
        if (castItem)
            castItemGUID = castItem->GetGUID();

        // find current aura from spell and change it's stackamount, or refresh it's duration
        Aura* foundAura = GetOwnedAura(newAura->Id, casterGUID, (newAura->AttributesCu[0] & SPELL_ATTR0_CU_ENCHANT_PROC) ? castItemGUID : ObjectGuid::Empty, 0);
        if (foundAura != nullptr)
        {
            // effect masks do not match
            // extremely rare case
            // let's just recreate aura
            if (effMask != foundAura->GetEffectMask())
                return nullptr;

            // update basepoints with new values - effect amount will be recalculated in ModStackAmount
            for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
            {
                if (!foundAura->HasEffect(i))
                    continue;

                int bp;
                if (baseAmount)
                    bp = *(baseAmount + i);
                else
                    bp = foundAura->GetSpellInfo()->GetEffect(i, GetSpawnMode())->BasePoints;

                float* oldBP = const_cast<float*>(&(foundAura->GetEffect(i)->m_baseAmount));
                float* oldBPget = const_cast<float*>(&(foundAura->GetEffect(i)->m_amount));
                float* oldBPSave = const_cast<float*>(&(foundAura->GetEffect(i)->m_oldbaseAmount));
                Unit** savetarget = const_cast<Unit**>(&(foundAura->GetEffect(i)->saveTarget));
                *savetarget = this;
                *oldBPSave = *oldBPget;
                *oldBP = bp;
            }

            // correct cast item guid if needed
            if (castItemGUID != foundAura->GetCastItemGUID())
            {
                ObjectGuid* oldGUID = const_cast<ObjectGuid *>(&foundAura->m_castItemGuid);
                *oldGUID = castItemGUID;
            }
            return foundAura;
        }
    }

    return nullptr;
}

void Unit::_AddAura(UnitAura* aura, Unit* caster)
{
    //ASSERT(!m_cleanupDone);
    if(m_cleanupDone)
        return;

    m_aura_is_lock = true;
    m_aura_lock.lock();
    m_ownedAuras.insert(std::make_pair(aura->GetId(), aura));
    m_aura_lock.unlock();
    m_aura_is_lock = false;

    _RemoveNoStackAurasDueToAura(aura);

    if (aura->IsRemoved())
        return;

    aura->SetAuraAttribute(AURA_ATTR_IS_SINGLE_TARGET, (caster && (aura->GetSpellInfo()->IsSingleTarget(caster, this) || aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE))));
    if (aura->HasAuraAttribute(AURA_ATTR_IS_SINGLE_TARGET))
    {
        ASSERT((IsInWorld() && !IsDuringRemoveFromWorld()) || (aura->GetCasterGUID() == GetGUID()) || (isBeingLoaded() && aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE)));
        /* @HACK: Player is not in world during loading auras.
         *        Single target auras are not saved or loaded from database
         *        but may be created as a result of aura links (player mounts with passengers)
         */

        // register single target aura
        caster->GetSingleCastAuras().push_back(aura);
        // remove other single target auras
        Unit::AuraList& scAuras = caster->GetSingleCastAuras();
        for (Unit::AuraList::iterator itr = scAuras.begin(); itr != scAuras.end();)
        {
            if ((*itr) != aura && (*itr)->IsSingleTargetWith(aura))
            {
                (*itr)->Remove();
                itr = scAuras.begin();
            }
            else
                ++itr;
        }
    }
    if (aura->IsMultiSingleTarget())
    {
        if (caster->GetMultiSingleTargetAuras().size() >= aura->GetMultiSingleTargetCount())
            if (Aura* auraGB = caster->GetMultiSingleTargetAuras().front())
                auraGB->Remove();

        caster->GetMultiSingleTargetAuras().push_back(aura);
    }
    if(caster)
        caster->AddMyCastAuras(aura);
}

// creates aura application instance and registers it in lists
// aura application effects are handled separately to prevent aura list corruption
AuraApplication * Unit::_CreateAuraApplication(Aura* aura, uint32 effMask)
{
    // can't apply aura on unit which is going to be deleted - to not create a memory leak
    // ASSERT(!m_cleanupDone);
    if(m_cleanupDone)
        return nullptr;

    // aura musn't be removed
    // ASSERT(!aura->IsRemoved());
    if(aura->IsRemoved())
        return nullptr;

    // aura mustn't be already applied on target
    // ASSERT (!aura->IsAppliedOnTarget(GetGUID()) && "Unit::_CreateAuraApplication: aura musn't be applied on target");
    if (aura->IsAppliedOnTarget(GetGUID()))
        return nullptr;

    SpellInfo const* aurSpellInfo = aura->GetSpellInfo();
    uint32 aurId = aurSpellInfo->Id;

    // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load)
    if (!isAlive() && !aurSpellInfo->IsDeathPersistent() && !(aurSpellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD)) &&
        (!IsPlayer() || !ToPlayer()->GetSession()->PlayerLoading()))
        return nullptr;

    Unit* caster = aura->GetCaster();

    m_aura_lock.lock();
    AuraApplicationPtr aurApp = std::make_shared<AuraApplication>(this, caster, aura, effMask);
    m_appliedAuras.insert(std::make_pair(aurId, aurApp));

    if (aurSpellInfo->GetAuraOptions(GetSpawnMode())->IsProcAura)
        m_procAuras.insert(std::make_pair(aurId, aurApp));
    m_aura_lock.unlock();

    if (aurSpellInfo->HasAnyAuraInterruptFlag())
    {
        m_interruptableAuras.push_back(aurApp);
        AddInterruptMask(aurSpellInfo->AuraInterruptFlags);
    }

    if (AuraStateType aState = aura->GetSpellInfo()->GetAuraState())
        m_auraStateAuras.insert(std::make_pair(aState, aurApp.get()));

    aura->_ApplyForTarget(this, caster, aurApp);
    return aurApp.get();
}

void Unit::_ApplyAuraEffect(Aura* aura, uint32 effIndex)
{
    ASSERT(aura);
    ASSERT(aura->HasEffect(effIndex));
    AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID());
    ASSERT(aurApp);
    if (!aurApp->GetEffectMask())
        _ApplyAura(aurApp, 1<<effIndex);
    else
        aurApp->_HandleEffect(effIndex, true);
}

// handles effects of aura application
// should be done after registering aura in lists
void Unit::_ApplyAura(AuraApplication * aurApp, uint32 effMask)
{
    Aura* aura = aurApp->GetBase();
    Unit* caster = aura->GetCaster();
    SpellInfo const* spellInfo = aura->GetSpellInfo();

    _RemoveNoStackAurasDueToAura(aura);

    if (aurApp->GetRemoveMode())
        return;

    // Update target aura state flag
    if (AuraStateType aState = spellInfo->GetAuraState())
        ModifyAuraState(aState, true);

    if (caster)
    {
        if (Player* plr = caster->ToPlayer())
            plr->ModifyExcludeCasterAuraSpell(spellInfo->Id, true);
    }

    if (aurApp->GetRemoveMode())
        return;

    // Sitdown on apply aura req seated
    if (spellInfo->HasAuraInterruptFlag(AURA_INTERRUPT_FLAG_NOT_SEATED) && !IsSitState())
        SetStandState(UNIT_STAND_STATE_SIT);

    if (aurApp->GetRemoveMode())
        return;

    aura->HandleAuraSpecificMods(aurApp, caster, true, false);

    if (aura->GetCasterGUID() != GetGUID() && caster)
        caster->m_whoHasMyAuras[GetGUID()].push_back(spellInfo->Id);

    // apply effects of the aura
    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
    {
        if (effMask & 1<<i && (!aurApp->GetRemoveMode()))
            aurApp->_HandleEffect(i, true);
    }
}

// removes aura application from lists and unapplies effects
void Unit::_UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode)
{
    AuraApplicationPtr aurApp = i->second;
    if(!aurApp)
        return;
    if(aurApp->GetRemoveMode())
        return;
    ASSERT(aurApp->GetTarget() == this);

    m_aura_lock.lock();
    m_aura_is_lock = true;

    aurApp->SetRemoveMode(removeMode);
    Aura* aura = aurApp->GetBase();
    SpellInfo const* spellInfo = aura->GetSpellInfo();
    TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Aura %u now is remove mode %d", aura->GetId(), removeMode);

    // dead loop is killing the server probably
    ASSERT(m_removedAurasCount < 0xFFFFFFFF);

    ++m_removedAurasCount;

    Unit* caster = aura->GetCaster();

    // Remove all pointers from lists here to prevent possible pointer invalidation on spellcast/auraapply/auraremove
    m_procAuras.erase(aura->GetId());
    m_appliedAuras.erase(i);

    if (spellInfo->HasAnyAuraInterruptFlag())
    {
        m_interruptableAuras.remove(aurApp);
        UpdateInterruptMask();
    }

    bool auraStateFound = false;
    AuraStateType auraState = spellInfo->GetAuraState();
    if (auraState)
    {
        bool canBreak = false;
        // Get mask of all aurastates from remaining auras
        for (AuraStateAurasMap::iterator itr = m_auraStateAuras.lower_bound(auraState); itr != m_auraStateAuras.upper_bound(auraState) && !(auraStateFound && canBreak);)
        {
            if (itr->second == aurApp.get())
            {
                m_auraStateAuras.erase(itr);
                itr = m_auraStateAuras.lower_bound(auraState);
                canBreak = true;
                continue;
            }
            auraStateFound = true;
            ++itr;
        }
    }

    aurApp->_Remove();
    aura->_UnapplyForTarget(this, caster, aurApp);

    bool hasGravityEffect = false;
    // remove effects of the spell - needs to be done after removing aura from lists
    for (uint8 itr = 0; itr < MAX_SPELL_EFFECTS; ++itr)
    {
        if (aurApp->HasEffect(itr))
        {
            aurApp->_HandleEffect(itr, false);

            if (spellInfo->Effects[itr]->ApplyAuraName == SPELL_AURA_DISABLE_GRAVITY)
                hasGravityEffect = true;
        }
    }

    if (hasGravityEffect)
        SetDisableGravity(false);

    m_aura_lock.unlock();
    m_aura_is_lock = false;
    // all effect mustn't be applied
    if (aurApp->GetEffectMask())
        return;
    // ASSERT(!aurApp->GetEffectMask());

    // Remove totem at next update if totem loses its aura
    if ((aurApp->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE || aurApp->GetRemoveMode() == AURA_REMOVE_BY_DROP_CHARGERS) && IsCreature() && ToCreature()->isTotem() && ToTotem()->GetSummonerGUID() == aura->GetCasterGUID())
    {
        if (ToTotem()->GetSpell() == aura->GetId() && ToTotem()->GetTotemType() == TOTEM_PASSIVE)
            ToTotem()->setDeathState(JUST_DIED);
    }

    // Remove aurastates only if were not found
    if (!auraStateFound)
        ModifyAuraState(auraState, false);

    if (caster)
    {
        if (Player* plr = caster->ToPlayer())
            plr->ModifyExcludeCasterAuraSpell(aura->GetId(), false);

        aura->HandleAuraSpecificMods(aurApp.get(), caster, false, false);

        caster->RemoveIdInWHMAList(GetGUID(), spellInfo->Id);
    }

    // only way correctly remove all auras from list
    //if (removedAuras != m_removedAurasCount) new aura may be added
        i = m_appliedAuras.begin();
}

void Unit::_UnapplyAura(AuraApplication* aurApp, AuraRemoveMode removeMode)
{
    // aura can be removed from unit only if it's applied on it, shouldn't happen
    ASSERT(aurApp->GetBase()->GetApplicationOfTarget(GetGUID()) == aurApp);
    uint32 spellId = aurApp->GetBase()->GetId();
    for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
    {
        if (iter->second.get() == aurApp)
        {
            _UnapplyAura(iter, removeMode);
            return;
        }
        ++iter;
    }
    // ASSERT(false);
}

void Unit::_RemoveNoStackAurasDueToAura(Aura* aura)
{
    SpellInfo const* spellProto = aura->GetSpellInfo();

    if(!spellProto)
        return;

    // passive spell special case (only non stackable with ranks)
    //if (spellProto->IsPassiveStackableWithRanks())
    //    return;

    bool remove = false;
    for (AuraApplicationMap::iterator i = m_appliedAuras.begin(); i != m_appliedAuras.end(); ++i)
    {
        if (!i->second)
            continue;

        if (remove)
        {
            remove = false;
            i = m_appliedAuras.begin();
        }

        if (Aura* auraBase = i->second->GetBase())
        {
            if (aura->CanStackWith(auraBase))
                continue;

            // Hack fix remove seal by consecration
            if (spellProto->Id == 26573)
                continue;

            RemoveAura(i, AURA_REMOVE_BY_DEFAULT);
            if (i == m_appliedAuras.end())
                break;
            remove = true;
        }
    }
}

void Unit::_RegisterAuraEffect(AuraEffect* aurEff, bool apply)
{
    if (!aurEff)
        return;

    AuraType auraType = aurEff->GetAuraType();
    if (apply)
    {
        RecursiveGuard guard(i_auraEff_lock);
        if (!m_modAuras[auraType])
        {
            m_modAuras[auraType] = new AuraEffectList;
            m_modMapAuras[auraType] = m_modAuras[auraType];
        }

        if (m_modAuras[auraType]->insert(aurEff))
            m_auraTypeCount[auraType]++;
    }
    else if (m_modAuras[auraType])
    {
        RecursiveGuard guard(i_auraEff_lock);
        if (m_modAuras[auraType]->erase(aurEff))
            if (m_auraTypeCount[auraType])
                m_auraTypeCount[auraType]--;
    }
}

// All aura base removes should go threw this function!
void Unit::RemoveOwnedAura(AuraMap::iterator &i, AuraRemoveMode removeMode)
{
    m_aura_is_lock = true;
    m_aura_lock.lock();
    Aura* aura = i->second;
    if(aura->IsRemoved()) // Allready run remove aura
        return;

    // if unit currently update aura list then make safe update iterator shift to next
    if (m_auraUpdateIterator == i)
        ++m_auraUpdateIterator;

    m_ownedAuras.erase(i);

    // Unregister single target aura
    if (aura->HasAuraAttribute(AURA_ATTR_IS_SINGLE_TARGET))
        aura->UnregisterSingleTarget();

    if (aura->IsMultiSingleTarget())
        aura->UnregisterMultiSingleTarget();

    aura->_Remove(removeMode);

    m_removedAuras.push_back(aura);

    aura->UnregisterCasterAuras(removeMode);

    i = m_ownedAuras.begin();
    m_aura_lock.unlock();
    m_aura_is_lock = false;
}

void Unit::RemoveOwnedAura(uint32 spellId, ObjectGuid casterGUID, uint32 reqEffMask, AuraRemoveMode removeMode)
{
    for (AuraMap::iterator itr = m_ownedAuras.lower_bound(spellId); itr != m_ownedAuras.upper_bound(spellId);)
        if (((itr->second->GetEffectMask() & reqEffMask) == reqEffMask) && (casterGUID.IsEmpty() || itr->second->GetCasterGUID() == casterGUID))
        {
            RemoveOwnedAura(itr, removeMode);
            itr = m_ownedAuras.lower_bound(spellId);
        }
        else
            ++itr;
}

void Unit::RemoveOwnedAura(Aura* aura, AuraRemoveMode removeMode)
{
    if (aura->IsRemoved())
        return;

    if(aura->GetOwner() != this)
        return;

    uint32 spellId = aura->GetId();
    for (AuraMap::iterator itr = m_ownedAuras.lower_bound(spellId); itr != m_ownedAuras.upper_bound(spellId); ++itr)
        if (itr->second == aura)
        {
            RemoveOwnedAura(itr, removeMode);
            return;
        }
}

// All aura base removes should go threw this function!
void Unit::RemoveOwnedAuraAll()
{
    AuraMap _ownedAuras;
    std::swap(_ownedAuras, m_ownedAuras);
    m_auraUpdateIterator = m_ownedAuras.end();

    for (AuraMap::iterator iter = _ownedAuras.begin(); iter != _ownedAuras.end(); ++iter)
    {
        if (Aura* aura = iter->second)
        {
            if(aura->IsRemoved())
                continue;

            // Unregister single target aura
            if (aura->HasAuraAttribute(AURA_ATTR_IS_SINGLE_TARGET))
                aura->UnregisterSingleTarget();

            if (aura->IsMultiSingleTarget())
                aura->UnregisterMultiSingleTarget();

            aura->_Remove(AURA_REMOVE_BY_DEFAULT);

            m_removedAuras.push_back(aura);
            aura->UnregisterCasterAuras(AURA_REMOVE_BY_DEFAULT);
        }
    }
    _ownedAuras.clear();

    AuraApplicationMap _ownedAuraApplications;
    std::swap(_ownedAuraApplications, m_appliedAuras);

    for (AuraApplicationMap::iterator iter = _ownedAuraApplications.begin(); iter != _ownedAuraApplications.end(); ++iter)
    {
        if (AuraApplicationPtr aurApp = iter->second)
        {
            ASSERT(aurApp);
            ASSERT(!aurApp->GetRemoveMode());
            ASSERT(aurApp->GetTarget() == this);

            aurApp->SetRemoveMode(AURA_REMOVE_BY_DEFAULT);
            Aura* aura = aurApp->GetBase();
            TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Aura %u now is remove mode %d", aura->GetId(), AURA_REMOVE_BY_DEFAULT);

            // dead loop is killing the server probably
            ASSERT(m_removedAurasCount < 0xFFFFFFFF);

            ++m_removedAurasCount;

            Unit* caster = aura->GetCaster();

            if (aura->GetSpellInfo()->HasAnyAuraInterruptFlag())
            {
                m_interruptableAuras.remove(aurApp);
                UpdateInterruptMask();
            }

            bool auraStateFound = false;
            AuraStateType auraState = aura->GetSpellInfo()->GetAuraState();
            if (auraState)
            {
                bool canBreak = false;
                // Get mask of all aurastates from remaining auras
                for (AuraStateAurasMap::iterator itr = m_auraStateAuras.lower_bound(auraState); itr != m_auraStateAuras.upper_bound(auraState) && !(auraStateFound && canBreak);)
                {
                    if (itr->second == aurApp.get())
                    {
                        m_auraStateAuras.erase(itr);
                        itr = m_auraStateAuras.lower_bound(auraState);
                        canBreak = true;
                        continue;
                    }
                    auraStateFound = true;
                    ++itr;
                }
            }

            aurApp->_Remove();
            aura->_UnapplyForTarget(this, caster, aurApp);

            // remove effects of the spell - needs to be done after removing aura from lists
            for (uint8 itr = 0; itr < MAX_SPELL_EFFECTS; ++itr)
            {
                if (aurApp->HasEffect(itr))
                    aurApp->_HandleEffect(itr, false);
            }

            // all effect mustn't be applied
            if (aurApp->GetEffectMask())
                continue;

            // Remove totem at next update if totem loses its aura
            if ((aurApp->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE || aurApp->GetRemoveMode() == AURA_REMOVE_BY_DROP_CHARGERS) && IsCreature() && ToCreature()->isTotem() && ToTotem()->GetSummonerGUID() == aura->GetCasterGUID())
            {
                if (ToTotem()->GetSpell() == aura->GetId() && ToTotem()->GetTotemType() == TOTEM_PASSIVE)
                    ToTotem()->setDeathState(JUST_DIED);
            }

            // Remove aurastates only if were not found
            if (!auraStateFound)
                ModifyAuraState(auraState, false);

            if (caster)
            {
                if (Player* plr = caster->ToPlayer())
                    plr->ModifyExcludeCasterAuraSpell(aura->GetId(), false);

                aura->HandleAuraSpecificMods(aurApp.get(), caster, false, false);

                caster->RemoveIdInWHMAList(GetGUID(), aura->GetId());
            }
        }
    }
    _ownedAuraApplications.clear();
}

Aura* Unit::GetOwnedAura(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask, Aura* except) const
{
    for (AuraMap::const_iterator itr = m_ownedAuras.lower_bound(spellId); itr != m_ownedAuras.upper_bound(spellId); ++itr)
        if (((itr->second->GetEffectMask() & reqEffMask) == reqEffMask) && (casterGUID.IsEmpty() || itr->second->GetCasterGUID() == casterGUID) && (itemCasterGUID.IsEmpty() || itr->second->GetCastItemGUID() == itemCasterGUID) && (!except || except != itr->second))
            return itr->second;
    return nullptr;
}

void Unit::RemoveAura(AuraApplicationMap::iterator &i, AuraRemoveMode mode)
{
    AuraApplicationPtr aurApp = i->second;
    // Do not remove aura which is already being removed
    if (aurApp->GetRemoveMode())
        return;
    Aura* aura = aurApp->GetBase();
    _UnapplyAura(i, mode);
    // Remove aura - for Area and Target auras
    if (aura->GetOwner() == this)
        aura->Remove(mode);
}

void Unit::RemoveAura(uint32 spellId, ObjectGuid caster, uint32 reqEffMask, AuraRemoveMode removeMode)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
    {
        Aura const* aura = iter->second->GetBase();
        if (((aura->GetEffectMask() & reqEffMask) == reqEffMask) && (caster.IsEmpty() || aura->GetCasterGUID() == caster))
        {
            RemoveAura(iter, removeMode);
            return;
        }
        ++iter;
    }
}

void Unit::RemoveAura(AuraApplication * aurApp, AuraRemoveMode mode)
{
    // we've special situation here, RemoveAura called while during aura removal
    // this kind of call is needed only when aura effect removal handler
    // or event triggered by it expects to remove
    // not yet removed effects of an aura
    if (aurApp->GetRemoveMode())
    {
        // remove remaining effects of an aura
        for (uint8 itr = 0; itr < MAX_SPELL_EFFECTS; ++itr)
        {
            if (aurApp->HasEffect(itr))
                aurApp->_HandleEffect(itr, false);
        }
        return;
    }
    // no need to remove
    if (aurApp->GetBase()->GetApplicationOfTarget(GetGUID()) != aurApp || aurApp->GetBase()->IsRemoved())
        return;
    uint32 spellId = aurApp->GetBase()->GetId();

    if (spellId == 51713 && mode != AURA_REMOVE_BY_EXPIRE)
        return;

    for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
    {
        if (aurApp == iter->second.get())
        {
            RemoveAura(iter, mode);
            return;
        }
        ++iter;
    }
}

void Unit::RemoveAura(Aura* aura, AuraRemoveMode mode)
{
    if (aura->IsRemoved())
        return;
    if (AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID()))
        RemoveAura(aurApp, mode);
}

void Unit::RemoveOwnedAuras(std::function<bool(Aura const*)> const& check)
{
    for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
    {
        if (check(iter->second))
        {
            RemoveOwnedAura(iter);
            continue;
        }
        ++iter;
    }
}

void Unit::RemoveOwnedAuras(uint32 spellId, std::function<bool(Aura const*)> const& check)
{
    for (AuraMap::iterator iter = m_ownedAuras.lower_bound(spellId); iter != m_ownedAuras.upper_bound(spellId);)
    {
        if (check(iter->second))
        {
            RemoveOwnedAura(iter);
            continue;
        }
        ++iter;
    }
}

void Unit::RemoveAppliedAuras(std::function<bool(AuraApplicationPtr)> const& check)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        if (check(iter->second))
        {
            RemoveAura(iter);
            continue;
        }
        ++iter;
    }
}

void Unit::RemoveAppliedAuras(uint32 spellId, std::function<bool(AuraApplicationPtr)> const& check, AuraRemoveMode removeMode /* = AURA_REMOVE_BY_DEFAULT */)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
    {
        if (check(iter->second))
        {
            RemoveAura(iter, removeMode);
            continue;
        }
        ++iter;
    }
}

void Unit::RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID, uint32 reqEffMask, AuraRemoveMode removeMode)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
    {
        Aura const* aura = iter->second->GetBase();
        if (((aura->GetEffectMask() & reqEffMask) == reqEffMask) && (casterGUID.IsEmpty() || aura->GetCasterGUID() == casterGUID))
        {
            RemoveAura(iter, removeMode);
            iter = m_appliedAuras.lower_bound(spellId);
        }
        else
            ++iter;
    }
}

void Unit::RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID, AuraRemoveMode removeMode, int32 num)
{
    for (AuraMap::iterator iter = m_ownedAuras.lower_bound(spellId); iter != m_ownedAuras.upper_bound(spellId);)
    {
        Aura* aura = iter->second;
        if ((aura->GetType() == UNIT_AURA_TYPE) && (casterGUID.IsEmpty() || aura->GetCasterGUID() == casterGUID))
        {
            aura->ModStackAmount(-num, removeMode);
            return;
        }
        ++iter;
    }
}

void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint32 dispellerSpellId, ObjectGuid casterGUID, Unit* dispeller, uint8 chargesRemoved/*= 1*/)
{
    for (AuraMap::iterator iter = m_ownedAuras.lower_bound(spellId); iter != m_ownedAuras.upper_bound(spellId);)
    {
        Aura* aura = iter->second;
        if (aura->GetCasterGUID() == casterGUID)
        {
            DispelInfo dispelInfo(dispeller, dispellerSpellId, chargesRemoved);

            // Call OnDispel hook on AuraScript
            aura->CallScriptDispel(&dispelInfo);

            if (aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES))
                aura->ModCharges(-dispelInfo.GetRemovedCharges(), AURA_REMOVE_BY_ENEMY_SPELL);
            else
                aura->ModStackAmount(-dispelInfo.GetRemovedCharges(), AURA_REMOVE_BY_ENEMY_SPELL);

            // Call AfterDispel hook on AuraScript
            aura->CallScriptAfterDispel(&dispelInfo);

            return;
        }
        ++iter;
    }
}

void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, Unit* stealer)
{
    for (AuraMap::iterator iter = m_ownedAuras.lower_bound(spellId); iter != m_ownedAuras.upper_bound(spellId);)
    {
        Aura* aura = iter->second;
        if (aura->GetCasterGUID() == casterGUID)
        {
            int32 damage[MAX_SPELL_EFFECTS];
            float baseDamage[MAX_SPELL_EFFECTS];
            uint32 effMask = 0;
            uint32 recalculateMask = 0;
            Unit* caster = aura->GetCaster();
            for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
            {
                if (aura->GetEffect(i))
                {
                    baseDamage[i] = aura->GetEffect(i)->GetBaseAmount();
                    damage[i] = aura->GetEffect(i)->GetAmount();
                    effMask |= (1<<i);
                    if (aura->GetEffect(i)->CanBeRecalculated())
                        recalculateMask |= (1<<i);
                }
                else
                {
                    baseDamage[i] = 0;
                    damage[i] = 0;
                }
            }

            bool stealCharge = (aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES)) != 0;
            // Cast duration to unsigned to prevent permanent aura's such as Righteous Fury being permanently added to caster
            uint32 dur = std::min(2u * MINUTE * IN_MILLISECONDS, uint32(aura->GetDuration()));

            Aura* oldAura = stealer->GetAura(aura->GetId(), aura->GetCasterGUID());
            if (oldAura != nullptr)
            {
                if (stealCharge)
                    oldAura->ModCharges(1);
                else
                    oldAura->ModStackAmount(1);
                oldAura->SetDuration(int32(dur));
            }
            else
            {
                // single target state must be removed before aura creation to preserve existing single target aura
                if (aura->HasAuraAttribute(AURA_ATTR_IS_SINGLE_TARGET))
                    aura->UnregisterSingleTarget();

                if (aura->IsMultiSingleTarget())
                    aura->UnregisterMultiSingleTarget();

                auto* spellInfo = aura->GetId() == 221414 ? sSpellMgr->GetSpellInfo(221299) : aura->GetSpellInfo(); // change spell for apply

                Aura* newAura = Aura::TryRefreshStackOrCreate(spellInfo, effMask, stealer, nullptr, &baseDamage[0], nullptr, aura->GetCasterGUID());
                if (newAura != nullptr)
                {
                    // created aura must not be single target aura,, so stealer won't loose it on recast
                    if (newAura->HasAuraAttribute(AURA_ATTR_IS_SINGLE_TARGET))
                    {
                        newAura->UnregisterSingleTarget();
                        // bring back single target aura status to the old aura
                        aura->SetAuraAttribute(AURA_ATTR_IS_SINGLE_TARGET);
                        caster->GetSingleCastAuras().push_back(aura);
                    }
                    if (newAura->IsMultiSingleTarget())
                    {
                        newAura->UnregisterMultiSingleTarget();
                        caster->GetMultiSingleTargetAuras().push_back(aura);
                    }
                    // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate
                    newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? 1 : aura->GetCharges(), 1, recalculateMask, &damage[0]);
                    newAura->ApplyForTargets();

                    if (newAura->GetId() == 1022)
                        stealer->AddAura(25771, stealer);
                }
            }

            if (stealCharge)
                aura->ModCharges(-1, AURA_REMOVE_BY_ENEMY_SPELL);
            else
                aura->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL);

            return;
        }
        ++iter;
    }
}

void Unit::RemoveAurasDueToItemSpell(Item* castItem, uint32 spellId)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);)
    {
        if (!castItem || iter->second->GetBase()->GetCastItemGUID() == castItem->GetGUID())
        {
            RemoveAura(iter);
            iter = m_appliedAuras.upper_bound(spellId);          // overwrite by more appropriate
        }
        else
            ++iter;
    }
}

void Unit::RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID, Aura* except, bool negative, bool positive)
{
    if (!HasAuraType(auraType))
        return;

    if (AuraEffectList const* auras = GetAuraEffectsByType(auraType))
    for (Unit::AuraEffectList::const_iterator itr = auras->begin(); itr != auras->end(); ++itr)
    {
        AuraEffect const* auraEff = *itr;
        if (!auraEff)
            continue;

        Aura* aura = auraEff->GetBase();
        if (!aura)
            continue;

        AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID());
        if (!aurApp)
        {
            printf("CRASH ALERT : Unit::RemoveAurasByType no AurApp pointer for Aura Id %u\n", aura->GetId());
            continue;
        }

        if (aura != except && (!casterGUID || aura->GetCasterGUID() == casterGUID) && ((negative && !aurApp->IsPositive()) || (positive && aurApp->IsPositive())))
            RemoveAura(aurApp);
    }
}

void Unit::RemoveAurasByType(AuraType auraType, std::function<bool(AuraApplication const*)> const& check)
{
    if (!m_modAuras[auraType])
        return;

    for (AuraEffectList::iterator iter = m_modAuras[auraType]->begin(); iter != m_modAuras[auraType]->end();)
    {
        Aura* aura = (*iter)->GetBase();
        AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID());
        if(!aurApp)
            continue;

        ++iter;
        if (check(aurApp))
        {
            uint32 removedAuras = m_removedAurasCount;
            RemoveAura(aurApp);
            if (m_removedAurasCount > removedAuras + 1)
                iter = m_modAuras[auraType]->begin();
        }
    }
}

void Unit::RemoveAurasWithAttribute(uint32 flags)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo();
        if (spell && spell->GetMisc(m_spawnMode)->MiscData.Attributes[0] & flags)
            RemoveAura(iter);
        else
            ++iter;
    }
}

void Unit::RemoveNotOwnSingleTargetAuras(uint32 newPhase)
{
    if (m_cleanupDone && isSummon())
        return;

    if (Map* mapInfo = GetMap())
        if (mapInfo->IsMapUnload())
            return;

    m_aura_is_lock = true;
    m_aura_lock.lock();

    // single target auras from other casters
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        AuraApplicationPtr aurApp = iter->second;
        Aura const* aura = aurApp->GetBase();
        Unit* caster = aura->GetCaster();

        if (aura->GetCasterGUID() != GetGUID() && aura->GetSpellInfo()->IsSingleTarget(caster, this))
        {
            if (!newPhase)
                RemoveAura(iter);
            else
            {
                if (!caster || !caster->InSamePhase(newPhase))
                    RemoveAura(iter);
                else
                    ++iter;
            }
        }
        else
            ++iter;
    }

    // single target auras at other targets
    // if (newPhase)
    {
        AuraList& scAuras = GetSingleCastAuras();
        for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();)
        {
            Aura* aura = *iter;
            if (aura)
            {
                WorldObject* _owner = aura->GetOwner();
                if (!_owner || !_owner->ToUnit() || _owner == this || _owner->ToUnit()->isSummon())
                {
                    ++iter;
                    continue;
                }

                if (!_owner->InSamePhase(newPhase))
                {
                    if (Unit* _unit = _owner->ToUnit())
                        _unit->RemoveAurasDueToSpell(aura->GetId());

                    scAuras.remove(aura);
                    iter = scAuras.begin();
                }
                else
                    ++iter;
            }
            else
                ++iter;
        }
        RemoveMultiSingleTargetAuras(newPhase);
    }
    m_aura_lock.unlock();
    m_aura_is_lock = false;
}
template <typename InterruptFlags>
uint32 Unit::RemoveAurasWithInterruptFlags(InterruptFlags flag, uint32 spellID, uint32 except)
{
    if (!(m_interruptMask[AuraInterruptFlagIndex<InterruptFlags>::value] & flag))
        return 0;

    TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "RemoveAurasWithInterruptFlags flag %u except %u, spellID %u", AsUnderlyingType(flag), except, spellID);

    uint32 count = 0;
    // interrupt auras
    for (AuraApplicationList::iterator iter = m_interruptableAuras.begin(); iter != m_interruptableAuras.end();)
    {
        Aura* aura = (*iter)->GetBase();
        ++iter;
        if (aura->GetSpellInfo()->AuraInterruptFlags[AuraInterruptFlagIndex<InterruptFlags>::value] & flag && (!except || aura->GetId() != except) && ((flag & SpellAuraInterruptFlags(AURA_INTERRUPT_FLAG_MOVING)) == 0 || !HasAuraCastWhileWalking(aura->GetSpellInfo())))
        {
            uint32 removedAuras = m_removedAurasCount;
            RemoveAura(aura, AURA_REMOVE_BY_INTERRUPT);
            count++;
            if (m_removedAurasCount > removedAuras + 1)
                iter = m_interruptableAuras.begin();
        }
    }

    // interrupt channeled spell
    if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
    {
        if (spell->getState() == SPELL_STATE_CASTING && (spell->GetSpellInfo()->ChannelInterruptFlags[AuraInterruptFlagIndex<InterruptFlags>::value] & flag) && spell->m_spellInfo->Id != except && spell->m_spellInfo->CanInterrupt(spellID, flag, this) &&
            !(flag & AURA_INTERRUPT_FLAG_MOVE && GetForceGUID()) && ((flag & SpellAuraInterruptFlags(AURA_INTERRUPT_FLAG_MOVING)) == 0 || !HasAuraCastWhileWalking(spell->m_spellInfo)))
            InterruptNonMeleeSpells(false, spellID);
    }

    UpdateInterruptMask();
    return count;
}

template uint32 Unit::RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags flag, uint32 spellID, uint32 except);
template uint32 Unit::RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2 flag, uint32 spellID, uint32 except);

void Unit::RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, ObjectGuid casterGUID)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        Aura const* aura = iter->second->GetBase();
        if (casterGUID.IsEmpty() || aura->GetCasterGUID() == casterGUID)
        {
            SpellInfo const* spell = aura->GetSpellInfo();
            if (spell->ClassOptions.SpellClassSet == uint32(family) && spell->ClassOptions.SpellClassMask.HasFlag(familyFlag1, familyFlag2, familyFlag3))
            {
                RemoveAura(iter);
                continue;
            }
        }
        ++iter;
    }
}

void Unit::RemoveMovementImpairingEffects()
{
    std::vector<uint32> removeList;

    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        AuraApplicationPtr appAura = iter->second;

        if (SpellInfo const* spellInfo = appAura->GetBase()->GetSpellInfo())
        {
            if (!spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_AVOID_MECHANIC))
            {
                uint32 effMask = appAura->GetEffectMask();
                uint32 remMask = 0;

                for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
                {
                    if (effMask < uint32(1 << i))
                        break;

                    if (effMask & (1 << i))
                    {
                        if (spellInfo->GetEffectMechanicMask(i) & ((1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)))
                            remMask |= (1 << i);
                    }
                }

                if (remMask)
                {
                    effMask &= ~remMask;

                    if (!effMask)
                    {
                        removeList.push_back(spellInfo->Id);
                    }
                    else
                    {
                        for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
                        {
                            if (remMask < uint32(1 << j))
                                break;

                            if (remMask & (1 << j))
                                appAura->_HandleEffect(j, false);
                        }
                    }
                }
            }
        }
        ++iter;
    }

    if (!removeList.empty())
    {
        for (auto itr : removeList)
            RemoveAurasDueToSpell(itr);
    }
}

void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode, uint32 except)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        Aura const* aura = iter->second->GetBase();
        if ((!except || aura->GetId() != except) && !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_IGNORE_AVOID_MECHANIC))
        {
            if (aura->GetSpellInfo()->GetAllEffectsMechanicMask() & mechanic_mask)
            {
                RemoveAura(iter, removemode);
                continue;
            }
        }
        ++iter;
    }
}

bool Unit::HasAurasWithMechanic(uint32 mechanic_mask, ObjectGuid caster)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
        if (Aura* aura = iter->second->GetBase())
            if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & mechanic_mask) && (caster.IsEmpty() || aura->GetCasterGUID() == caster))
                return true;
    return false;
}

void Unit::RemoveAreaAurasDueToLeaveWorld()
{
    // make sure that all area auras not applied on self are removed - prevent access to deleted pointer later
    for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
    {
        Aura* aura = iter->second;
        ++iter;

        if (!aura || aura->IsRemoved())
            continue;

        Aura::ApplicationMap const& appMap = aura->GetApplicationMap();
        if (appMap.empty())
            continue;

        for (Aura::ApplicationMap::const_iterator itr = appMap.begin(); itr != appMap.end(); ++itr)
        {
            AuraApplicationPtr aurApp = itr->second;
            if (!aurApp)
                continue;
            Unit* target = aurApp->GetTarget();
            if (!target || target == this)
                continue;
            target->RemoveAura(aurApp.get());
            // things linked on aura remove may apply new area aura - so start from the beginning
            iter = m_ownedAuras.begin();
        }
    }

    // remove area auras owned by others
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        if (iter->second->GetBase()->GetOwner() != this)
            RemoveAura(iter);
        else
            ++iter;
    }
}

void Unit::RemoveAllAuras()
{
    m_aura_is_lock = true;
    m_aura_lock.lock();
    // this may be a dead loop if some events on aura remove will continiously apply aura on remove
    // we want to have all auras removed, so use your brain when linking events
    while (!m_appliedAuras.empty() || !m_ownedAuras.empty())
        RemoveOwnedAuraAll();
    m_aura_lock.unlock();
    m_aura_is_lock = false;
}

void Unit::RemoveNonPassivesAuras()
{
    // this may be a dead loop if some events on aura remove will continiously apply aura on remove
    // we want to have all auras removed, so use your brain when linking events
    for (AuraApplicationMap::iterator aurAppIter = m_appliedAuras.begin(); aurAppIter != m_appliedAuras.end();)
    {
        if (!aurAppIter->second->GetBase()->IsPassive())
            _UnapplyAura(aurAppIter, AURA_REMOVE_BY_DEFAULT);
        else
            ++aurAppIter;
    }

    for (AuraMap::iterator aurIter = m_ownedAuras.begin(); aurIter != m_ownedAuras.end();)
    {
        if (!aurIter->second->IsPassive())
            RemoveOwnedAura(aurIter);
        else
            ++aurIter;
    }
}

void Unit::RemoveArenaAuras()
{
    // in join, remove positive buffs, on end, remove negative
    // used to remove positive visible auras in arenas
    RemoveAppliedAuras([](AuraApplicationPtr const aurApp)
    {
        Aura const* aura = aurApp->GetBase();
        return (!(aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_UNK21)) && !aura->IsPassive() && (aurApp->IsPositive() || !(aura->GetSpellInfo()->HasAttribute(SPELL_ATTR3_DEATH_PERSISTENT))) || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR2_UNK3));
    });
}

void Unit::RemoveAurasAllDots()
{
    RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
    RemoveAurasByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
    RemoveAurasByType(SPELL_AURA_PERIODIC_LEECH);
}

void Unit::RecalcArenaAuras(bool hasPvPScaling)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        if (Aura* aura = iter->second->GetBase())
        {
            if (SpellInfo const* spellInfo = aura->GetSpellInfo())
            {
                if ((spellInfo->HasAttribute(SPELL_ATTR8_NOT_IN_BG_OR_ARENA) || spellInfo->HasAttribute(SPELL_ATTR4_NOT_USABLE_IN_ARENA_OR_RATED_BG)) && !aura->GetCastItemGUID())
                {
                    aura->RecalculateAmountOfEffects(true);
                    aura->SetAuraAttribute(AURA_ATTR_IS_NOT_ACTIVE, hasPvPScaling);
                }
            }
        }

        ++iter;
    }
}

void Unit::RemoveAllAurasOnDeath()
{
    // used just after dieing to remove all visible auras
    // and disable the mods for the passive ones
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        Aura const* aura = iter->second->GetBase();
        if (!aura->IsPassive() && !aura->IsDeathPersistent())
            _UnapplyAura(iter, AURA_REMOVE_BY_DEATH);
        else
            ++iter;
    }

    for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
    {
        Aura* aura = iter->second;
        if (!aura->IsPassive() && !aura->IsDeathPersistent())
            RemoveOwnedAura(iter, AURA_REMOVE_BY_DEATH);
        else
            ++iter;
    }
}

void Unit::RemoveAllAurasRequiringDeadTarget()
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        Aura const* aura = iter->second->GetBase();
        if (!aura->IsPassive() && aura->GetSpellInfo()->IsRequiringDeadTarget())
            _UnapplyAura(iter, AURA_REMOVE_BY_DEFAULT);
        else
            ++iter;
    }

    for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
    {
        Aura* aura = iter->second;
        if (!aura->IsPassive() && aura->GetSpellInfo()->IsRequiringDeadTarget())
            RemoveOwnedAura(iter, AURA_REMOVE_BY_DEFAULT);
        else
            ++iter;
    }
}

void Unit::RemoveAllAurasExceptType(AuraType type)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        Aura const* aura = iter->second->GetBase();
        if (!aura->GetSpellInfo()->HasAura(type))
            _UnapplyAura(iter, AURA_REMOVE_BY_DEFAULT);
        else
            ++iter;
    }

    for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
    {
        Aura* aura = iter->second;
        if (!aura->GetSpellInfo()->HasAura(type))
            RemoveOwnedAura(iter, AURA_REMOVE_BY_DEFAULT);
        else
            ++iter;
    }
}

void Unit::RemoveAllAurasByType(AuraType type)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        Aura const* aura = iter->second->GetBase();
        if (aura->GetSpellInfo()->HasAura(type))
            _UnapplyAura(iter, AURA_REMOVE_BY_DEFAULT);
        else
            ++iter;
    }

    for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
    {
        Aura* aura = iter->second;
        if (aura->GetSpellInfo()->HasAura(type))
            RemoveOwnedAura(iter, AURA_REMOVE_BY_DEFAULT);
        else
            ++iter;
    }
}

void Unit::RemoveAurasAllNotOwned(ObjectGuid caster)
{
    // used just after dieing to remove all visible auras
    // and disable the mods for the passive ones
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
    {
        Aura const* aura = iter->second->GetBase();
        if (aura->GetCasterGUID() != caster && aura->GetCasterGUID() != GetGUID() && !aura->GetSpellInfo()->HasAura(SPELL_AURA_CONTROL_VEHICLE))
            _UnapplyAura(iter, AURA_REMOVE_BY_DEFAULT);
        else
            ++iter;
    }

    for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();)
    {
        Aura* aura = iter->second;
        if (aura->GetCasterGUID() != caster && aura->GetCasterGUID() != GetGUID() && !aura->GetSpellInfo()->HasAura(SPELL_AURA_CONTROL_VEHICLE))
            RemoveOwnedAura(iter, AURA_REMOVE_BY_DEFAULT);
        else
            ++iter;
    }
}

void Unit::DelayOwnedAuras(uint32 spellId, ObjectGuid caster, int32 delaytime)
{
    for (AuraMap::iterator iter = m_ownedAuras.lower_bound(spellId); iter != m_ownedAuras.upper_bound(spellId);++iter)
    {
        Aura* aura = iter->second;
        if (caster.IsEmpty() || aura->GetCasterGUID() == caster)
        {
            if (aura->GetDuration() < delaytime)
                aura->SetDuration(0);
            else
                aura->SetDuration(aura->GetDuration() - delaytime);

            // update for out of range group members (on 1 slot use)
            aura->SetNeedClientUpdateForTargets();
            TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Aura %u partially interrupted on unit %u, new duration: %u ms", aura->GetId(), GetGUIDLow(), aura->GetDuration());
        }
    }
}

void Unit::_RemoveAllAuraStatMods()
{
    for (AuraApplicationMap::iterator i = m_appliedAuras.begin(); i != m_appliedAuras.end(); ++i)
        (*i).second->GetBase()->HandleAllEffects(i->second.get(), AURA_EFFECT_HANDLE_STAT, false);
}

void Unit::_ApplyAllAuraStatMods()
{
    for (AuraApplicationMap::iterator i = m_appliedAuras.begin(); i != m_appliedAuras.end(); ++i)
        (*i).second->GetBase()->HandleAllEffects(i->second.get(), AURA_EFFECT_HANDLE_STAT, true);
}

void Unit::GetAuraEffectsByMechanic(uint32 mechanic_mask, AuraList& auraList, ObjectGuid caster)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
        if (Aura* aura = iter->second->GetBase())
            if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & mechanic_mask) && (caster.IsEmpty() || aura->GetCasterGUID() == caster))
                auraList.push_back(aura);
}

AuraEffect* Unit::GetAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid caster) const
{
    for (AuraApplicationMap::const_iterator itr = m_appliedAuras.lower_bound(spellId); itr != m_appliedAuras.upper_bound(spellId); ++itr)
        if (itr->second->HasEffect(effIndex) && (caster.IsEmpty() || itr->second->GetBase()->GetCasterGUID() == caster))
            return itr->second->GetBase()->GetEffect(effIndex);
    return nullptr;
}

AuraEffect* Unit::GetAuraEffectOfRankedSpell(uint32 spellId, uint8 effIndex, ObjectGuid caster) const
{
    uint32 rankSpell = sSpellMgr->GetFirstSpellInChain(spellId);
    while (rankSpell)
    {
        if (auto aurEff = GetAuraEffect(rankSpell, effIndex, caster))
            return aurEff;

        rankSpell = sSpellMgr->GetNextSpellInChain(rankSpell);
    }

    return nullptr;
}

AuraEffect* Unit::GetAuraEffect(AuraType type, SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, ObjectGuid casterGUID)
{
    if (auto const* auras = GetAuraEffectsByType(type))
    for (auto i = auras->cbegin(); i != auras->cend(); ++i)
    {
        auto spell = (*i)->GetSpellInfo();
        if (spell->ClassOptions.SpellClassSet == uint32(family) && spell->ClassOptions.SpellClassMask.HasFlag(familyFlag1, familyFlag2, familyFlag3))
        {
            if (!casterGUID.IsEmpty() && (*i)->GetCasterGUID() != casterGUID)
                continue;

            return (*i);
        }
    }

    return nullptr;
}

AuraApplication * Unit::GetAuraApplication(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask, AuraApplication * except) const
{
    for (AuraApplicationMap::const_iterator itr = m_appliedAuras.lower_bound(spellId); itr != m_appliedAuras.upper_bound(spellId); ++itr)
    {
        Aura const* aura = itr->second->GetBase();
        if (((aura->GetEffectMask() & reqEffMask) == reqEffMask) && (casterGUID.IsEmpty() || aura->GetCasterGUID() == casterGUID) && (itemCasterGUID.IsEmpty() || aura->GetCastItemGUID() == itemCasterGUID) && (!except || except != itr->second.get()))
            return itr->second.get();
    }
    return nullptr;
}

Aura* Unit::GetAura(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask) const
{
    if (!this)
        return nullptr;

    AuraApplication * aurApp = GetAuraApplication(spellId, casterGUID, itemCasterGUID, reqEffMask);
    return aurApp ? aurApp->GetBase() : nullptr;
}

AuraApplication * Unit::GetAuraApplicationOfRankedSpell(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask, AuraApplication* except) const
{
    uint32 rankSpell = sSpellMgr->GetFirstSpellInChain(spellId);
    while (rankSpell)
    {
        if (AuraApplication * aurApp = GetAuraApplication(rankSpell, casterGUID, itemCasterGUID, reqEffMask, except))
            return aurApp;
        rankSpell = sSpellMgr->GetNextSpellInChain(rankSpell);
    }
    return nullptr;
}

Aura* Unit::GetAuraOfRankedSpell(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask) const
{
    AuraApplication * aurApp = GetAuraApplicationOfRankedSpell(spellId, casterGUID, itemCasterGUID, reqEffMask);
    return aurApp ? aurApp->GetBase() : nullptr;
}

void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelChargesList& dispelList)
{
    AuraMap const& auras = GetOwnedAuras();
    for (AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
    {
        Aura* aura = itr->second;
        AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID());
        if (!aurApp)
            continue;

        // don't try to remove passive auras
        if (aura->IsPassive())
            continue;

        if (aura->GetSpellInfo()->GetDispelMask() & dispelMask)
        {
            if (aura->GetSpellInfo()->Categories.DispelType == DISPEL_MAGIC)
            {
                // do not remove positive auras if friendly target
                //               negative auras if non-friendly target
                if (aurApp->IsPositive() == IsFriendlyTo(caster) && aura->GetId() != 605)
                    continue;
            }

            // The charges / stack amounts don't count towards the total number of auras that can be dispelled.
            // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell
            // Polymorph instead of 1 / (5 + 1) -> 16%.
            bool dispel_charges = (aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES)) != 0;
            uint16 charges = dispel_charges ? aura->GetCharges() : aura->GetStackAmount();
            if (charges > 0)
                dispelList.push_back(std::make_pair(aura, charges));
        }
    }
}

bool Unit::HasAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid caster) const
{
    for (AuraApplicationMap::const_iterator itr = m_appliedAuras.lower_bound(spellId); itr != m_appliedAuras.upper_bound(spellId); ++itr)
        if (itr->second->HasEffect(effIndex) && (caster.IsEmpty() || itr->second->GetBase()->GetCasterGUID() == caster))
            return true;
    return false;
}

uint32 Unit::GetAuraCount(uint32 spellId) const
{
    uint32 count = 0;
    for (AuraApplicationMap::const_iterator itr = m_appliedAuras.lower_bound(spellId); itr != m_appliedAuras.upper_bound(spellId); ++itr)
    {
        if (!itr->second->GetBase()->GetStackAmount())
            count++;
        else
            count += itr->second->GetBase()->GetStackAmount();
    }
    return count;
}

bool Unit::HasAura(uint32 spellId, ObjectGuid casterGUID, ObjectGuid itemCasterGUID, uint32 reqEffMask) const
{
    if (GetAuraApplication(spellId, casterGUID, itemCasterGUID, reqEffMask))
        return true;
    return false;
}

bool Unit::HasAuraType(AuraType auraType) const
{
    return m_auraTypeCount[auraType] > 0;
}

uint32 Unit::GetAuraTypeCount(AuraType auraType) const
{
    return m_auraTypeCount[auraType];
}

bool Unit::HasAuraTypeWithCaster(AuraType auratype, ObjectGuid caster) const
{
    if (!HasAuraType(auratype))
        return false;

    if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(auratype))
    for (AuraEffectList::const_iterator i = mTotalAuraList->begin(); i != mTotalAuraList->end(); ++i)
    {
        if (caster == (*i)->GetCasterGUID())
            return true;
    }
    return false;
}

bool Unit::HasAuraTypeWithMiscvalue(AuraType auratype, int32 miscvalue) const
{
    if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(auratype))
    for (AuraEffectList::const_iterator i = mTotalAuraList->begin(); i != mTotalAuraList->end(); ++i)
    {
        if (miscvalue == (*i)->GetMiscValue())
            return true;
    }
    return false;
}

bool Unit::HasAuraTypeWithAffectMask(AuraType auratype, SpellInfo const* affectedSpell) const
{
    if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(auratype))
    for (AuraEffectList::const_iterator i = mTotalAuraList->begin(); i != mTotalAuraList->end(); ++i)
    {
        if ((*i)->IsAffectingSpell(affectedSpell))
            return true;
    }
    return false;
}

bool Unit::HasAuraTypeWithValue(AuraType auratype, int32 value) const
{
    if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(auratype))
    for (AuraEffectList::const_iterator i = mTotalAuraList->begin(); i != mTotalAuraList->end(); ++i)
    {
        if (value == (*i)->GetAmount())
            return true;
    }
    return false;
}

bool Unit::HasAuraWithSchoolMask(AuraType auratype, SpellSchoolMask schoolMask)
{
    if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(auratype))
    for (AuraEffectList::const_iterator i = mTotalAuraList->begin(); i != mTotalAuraList->end(); ++i)
    {
        if ((*i)->GetSpellInfo()->GetSchoolMask() & schoolMask)
            return true;
    }
    return false;
}

void Unit::GetAuraSet(std::set<uint32>& auraList)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
        auraList.insert(iter->first);
}

template <typename InterruptFlags>
bool Unit::HasNegativeAuraWithInterruptFlag(InterruptFlags flag, ObjectGuid guid) const
{
    if (!(m_interruptMask[AuraInterruptFlagIndex<InterruptFlags>::value] & flag))
        return false;

    for (AuraApplicationList::const_iterator iter = m_interruptableAuras.begin(); iter != m_interruptableAuras.end(); ++iter)
    {
        if (!(*iter)->IsPositive() && (*iter)->GetBase()->GetSpellInfo()->AuraInterruptFlags[AuraInterruptFlagIndex<InterruptFlags>::value] & flag && (!guid || (*iter)->GetBase()->GetCasterGUID() == guid))
            return true;
    }
    return false;
}

template bool Unit::HasNegativeAuraWithInterruptFlag(SpellAuraInterruptFlags flag, ObjectGuid guid) const;
template bool Unit::HasNegativeAuraWithInterruptFlag(SpellAuraInterruptFlags2 flag, ObjectGuid guid) const;

bool Unit::HasNegativeAuraWithAttribute(uint32 flag, ObjectGuid guid)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
    {
        Aura const* aura = iter->second->GetBase();
        if (!iter->second->IsPositive() && aura->GetSpellInfo()->GetMisc(m_spawnMode)->MiscData.Attributes[0] & flag && (guid.IsEmpty() || aura->GetCasterGUID() == guid))
            return true;
    }
    return false;
}

bool Unit::HasAuraWithAttribute(uint32 Attributes, uint32 flag) const
{
    for (AuraApplicationMap::const_iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
    {
        Aura const* aura = iter->second->GetBase();
        if (aura->GetSpellInfo()->GetMisc(m_spawnMode)->MiscData.Attributes[Attributes] & flag)
            return true;
    }
    return false;
}

bool Unit::HasAuraWithMechanic(uint32 mechanicMask)
{
    for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
    {
        SpellInfo const* spellInfo  = iter->second->GetBase()->GetSpellInfo();
        if (spellInfo->Categories.Mechanic && (mechanicMask & (1 << spellInfo->Categories.Mechanic)))
            return true;

        if (spellInfo->EffectMechanicMask & mechanicMask)
            return true;
    }

    return false;
}

bool Unit::HasAuraCastWhileWalking(SpellInfo const* spellInfo)
{
    return (spellInfo->HasAttribute(SPELL_ATTR5_USABLE_WHILE_MOVING) || HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, spellInfo) || HasAuraType(SPELL_AURA_CAST_WHILE_WALKING2));
}

bool Unit::IsActiveMitigation() const
{
    for (AuraApplicationMap::const_iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
    {
        Aura const* aura = iter->second->GetBase();
        if (!aura)
            continue;

        switch (aura->GetId())
        {
            case 77535:  // DK: Blood Shield
            case 132403: // Paladin: Shield of the Righteous
            case 132404: // Warrior: Shield Block
            case 192081: // Druid: Ironfur
            case 203819: // DH: Demon Spikes
            case 215479: // Monk: Ironskin Brew
                return true;
        }
    }
    return false;
}

AuraEffect* Unit::IsScriptOverriden(SpellInfo const* spell, int32 script) const
{
    if (AuraEffectList const* auras = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS))
    for (AuraEffectList::const_iterator i = auras->begin(); i != auras->end(); ++i)
    {
        if ((*i)->GetMiscValue() == script)
            if ((*i)->IsAffectingSpell(spell))
                return (*i);
    }
    return nullptr;
}

uint32 Unit::GetDiseasesByCaster(ObjectGuid casterGUID, bool remove)
{
    static const AuraType diseaseAuraTypes[] =
    {
        SPELL_AURA_PERIODIC_DAMAGE, // Frost Fever and Blood Plague
        SPELL_AURA_LINKED,          // Crypt Fever and Ebon Plague
        SPELL_AURA_NONE
    };

    uint32 diseases = 0;
    for (AuraType const* itr = &diseaseAuraTypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
    {
        if (!m_modAuras[*itr])
            continue;

        for (AuraEffectList::iterator i = m_modAuras[*itr]->begin(); i != m_modAuras[*itr]->end();)
        {
            // Get auras with disease dispel type by caster
            if ((*i)->GetSpellInfo()->Categories.DispelType == DISPEL_DISEASE && (*i)->GetCasterGUID() == casterGUID)
            {
                ++diseases;

                if (remove)
                {
                    RemoveAura((*i)->GetId(), (*i)->GetCasterGUID());
                    i = m_modAuras[*itr]->begin();
                    continue;
                }
            }
            ++i;
        }
    }
    return diseases;
}

uint32 Unit::GetDoTsByCaster(ObjectGuid casterGUID) const
{
    static const AuraType diseaseAuraTypes[] =
    {
        SPELL_AURA_PERIODIC_DAMAGE,
        SPELL_AURA_PERIODIC_DAMAGE_PERCENT,
        SPELL_AURA_NONE
    };

    uint32 dots = 0;
    for (AuraType const* itr = &diseaseAuraTypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
    {
        if (Unit::AuraEffectList const* auras = GetAuraEffectsByType(*itr))
        for (AuraEffectList::const_iterator i = auras->begin(); i != auras->end(); ++i)
        {
            // Get auras by caster
            if ((*i)->GetCasterGUID() == casterGUID)
                ++dots;
        }
    }
    return dots;
}

void Unit::GetTotalNotStuckAuraEffectByType(AuraType auratype, AuraEffectList& EffectList, std::vector<uint32>& ExcludeAuraList)
{
    std::multimap<SpellGroup, AuraEffect*> SameEffectSpellGroup;

    if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(auratype))
    for (AuraEffectList::const_iterator i = mTotalAuraList->begin(); i != mTotalAuraList->end(); ++i)
    {
        bool exclude = false;

        if (!ExcludeAuraList.empty())
        {
            for (auto itr : ExcludeAuraList)
            {
                if ((*i)->GetId() == itr)
                {
                    exclude = true;
                    break;
                }
            }
        }

        if (!exclude)
        {
            if (AuraEffect* eff = (*i))
                if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(eff->GetSpellInfo(), eff, SameEffectSpellGroup))
                    EffectList.insert(eff);
        }
    }

    for (std::map<SpellGroup, AuraEffect*>::const_iterator itr = SameEffectSpellGroup.begin(); itr != SameEffectSpellGroup.end(); ++itr)
        EffectList.insert(itr->second);
}

void Unit::GetAuraEffectsByListType(std::list<AuraType>* auratypelist, AuraEffectList& EffectList)
{
    for (std::list<AuraType>::iterator auratype = auratypelist->begin(); auratype!= auratypelist->end(); ++auratype)
    {
        if (AuraEffectList const* swaps = GetAuraEffectsByType(*auratype))
            for (AuraEffectList::const_iterator i = swaps->begin(); i != swaps->end(); ++i)
                EffectList.insert(*i);
    }
}

int32 Unit::GetTotalAuraModifier(AuraType auratype, std::function<bool(AuraEffect const*)> const& predicate, bool raid /*= false*/) const
{
    std::map<SpellGroup, int32> SameEffectSpellGroup;
    int32 modifier = 0;
    int32 raidModifier = 0;

    if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(auratype))
    for (auto const& auraEffect : *mTotalAuraList)
    {
        if (predicate(auraEffect))
        {
            if (raid && (auraEffect->GetSpellInfo()->HasAttribute(SPELL_ATTR7_CONSOLIDATED_RAID_BUFF)))
            {
                if (auraEffect->GetAmount() > raidModifier)
                    raidModifier = auraEffect->GetAmount();
            }
            else if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(auraEffect->GetSpellInfo(), auraEffect->GetAmount(), SameEffectSpellGroup))
                modifier += auraEffect->GetAmount();
        }
    }

    for (auto const& k : SameEffectSpellGroup)
        modifier += k.second;

    return modifier + raidModifier;
}

float Unit::GetTotalAuraMultiplier(AuraType auratype, std::function<bool(AuraEffect const*)> const& predicate) const
{
    std::map<SpellGroup, int32> SameEffectSpellGroup;
    float multiplier = 1.0f;

    if (auto const* mTotalAuraList = GetAuraEffectsByType(auratype))
        for (auto const& auraEffect : *mTotalAuraList)
            if (predicate(auraEffect))
                if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(auraEffect->GetSpellInfo(), auraEffect->GetAmount(), SameEffectSpellGroup))
                    AddPct(multiplier, auraEffect->GetAmount());

    for (auto const& k : SameEffectSpellGroup)
        AddPct(multiplier, k.second);

    return multiplier;
}

int32 Unit::GetMaxNegativeAuraModifier(AuraType auratype, std::function<bool(AuraEffect const*)> const& predicate) const
{
    int32 modifier = 0;

    if (auto const* mTotalAuraList = GetAuraEffectsByType(auratype))
        for (auto const& auraEffect : *mTotalAuraList)
            if (predicate(auraEffect))
                if (auraEffect->GetAmount() < modifier)
                    modifier = auraEffect->GetAmount();

    return modifier;
}

int32 Unit::GetMaxPositiveAuraModifier(AuraType auratype, std::function<bool(AuraEffect const*)> const& predicate) const
{
    int32 modifier = 0;

    if (auto const* mTotalAuraList = GetAuraEffectsByType(auratype))
        for (auto const& auraEffect : *mTotalAuraList)
            if (predicate(auraEffect))
                if (auraEffect->GetAmount() > modifier)
                    modifier = auraEffect->GetAmount();

    return modifier;
}

int32 Unit::GetTotalAuraModifier(AuraType auratype, bool raid) const
{
    return GetTotalAuraModifier(auratype, [](AuraEffect const* /*aurEff*/) { return true; }, raid);
}

float Unit::GetTotalAuraMultiplier(AuraType auratype) const
{
    return GetTotalAuraMultiplier(auratype, [](AuraEffect const* /*aurEff*/) { return true; });
}

int32 Unit::GetMaxPositiveAuraModifier(AuraType auratype) const
{
    return GetMaxPositiveAuraModifier(auratype, [](AuraEffect const* /*aurEff*/) { return true; });
}

int32 Unit::GetMaxNegativeAuraModifier(AuraType auratype) const
{
    return GetMaxNegativeAuraModifier(auratype, [](AuraEffect const* /*aurEff*/) { return true; });
}

int32 Unit::GetTotalForAurasModifier(std::list<AuraType> *auratypelist) const
{
    std::map<SpellGroup, int32> SameEffectSpellGroup;
    int32 modifier = 0;

    AuraEffectList mTotalAuraList;
    for (std::list<AuraType>::iterator auratype = auratypelist->begin(); auratype!= auratypelist->end(); ++auratype)
    {
        if (AuraEffectList const* swaps = GetAuraEffectsByType(*auratype))
        for (AuraEffectList::const_iterator i = swaps->begin(); i != swaps->end(); ++i)
            mTotalAuraList.insert(*i);
    }

    if (!mTotalAuraList.empty())
    {
        for (AuraEffectList::iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i)
            if (AuraEffect* eff = (*i))
                if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(eff->GetSpellInfo(), eff->GetAmount(), SameEffectSpellGroup))
                    modifier += eff->GetAmount();

        for (std::map<SpellGroup, int32>::const_iterator itr = SameEffectSpellGroup.begin(); itr != SameEffectSpellGroup.end(); ++itr)
            modifier += itr->second;
    }

    return modifier;
}

float Unit::GetTotalForAurasMultiplier(std::list<AuraType> *auratypelist) const
{
    std::map<SpellGroup, int32> SameEffectSpellGroup;
    float multiplier = 1.0f;

    AuraEffectList mTotalAuraList;
    for (std::list<AuraType>::iterator auratype = auratypelist->begin(); auratype!= auratypelist->end(); ++auratype)
    {
        if (AuraEffectList const* swaps = GetAuraEffectsByType(*auratype))
        for (AuraEffectList::const_iterator i = swaps->begin(); i != swaps->end(); ++i)
            mTotalAuraList.insert(*i);
    }

    for (AuraEffectList::iterator i = mTotalAuraList.begin(); i != mTotalAuraList.end(); ++i)
        if (AuraEffect* eff = (*i))
            if (!sSpellMgr->AddSameEffectStackRuleSpellGroups(eff->GetSpellInfo(), eff->GetAmount(), SameEffectSpellGroup))
                AddPct(multiplier, eff->GetAmount());

    for (std::map<SpellGroup, int32>::const_iterator itr = SameEffectSpellGroup.begin(); itr != SameEffectSpellGroup.end(); ++itr)
        AddPct(multiplier, itr->second);

    return multiplier;
}

float Unit::GetTotalPositiveAuraMultiplierByMiscMask(AuraType auratype, uint32 miscMask) const
{
    return GetTotalAuraMultiplier(auratype, [miscMask](AuraEffect const* aurEff) -> bool
    {
        if ((aurEff->GetMiscValue() & miscMask) && aurEff->GetAmount() > 0)
            return true;
        return false;
    });
}

int32 Unit::GetTotalAuraDurationByType(AuraType auratype, bool firstAuraInList /*= false*/) const
{
    int32 duration = 0;

    if (auto const* mTotalAuraList = GetAuraEffectsByType(auratype))
    for (auto i = mTotalAuraList->cbegin(); i != mTotalAuraList->cend(); ++i)
    {
        if (auto eff = *i)
            if (auto spellInfo = eff->GetSpellInfo())
            {
                duration += spellInfo->GetDuration();
                if (firstAuraInList)
                    return duration;
            }
    }

    return duration;
}

int32 Unit::GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 miscMask) const
{
    return GetTotalAuraModifier(auratype, [miscMask](AuraEffect const* aurEff) -> bool
    {
        if ((aurEff->GetMiscValue() & miscMask) != 0)
            return true;
        return false;
    });
}

float Unit::GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 miscMask) const
{
    return GetTotalAuraMultiplier(auratype, [miscMask](AuraEffect const* aurEff) -> bool
    {
        if ((aurEff->GetMiscValue() & miscMask) != 0)
            return true;
        return false;
    });
}

float Unit::GetTotalAuraMultiplierByMiscMaskB(AuraType auratype, uint32 miscMaskB) const
{
    return GetTotalAuraMultiplier(auratype, [miscMaskB](AuraEffect const* aurEff) -> bool
    {
        if ((aurEff->GetMiscValueB() & miscMaskB) != 0)
            return true;
        return false;
    });
}

int32 Unit::GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 miscMask, AuraEffect const* except) const
{
    return GetMaxPositiveAuraModifier(auratype, [miscMask, except](AuraEffect const* aurEff) -> bool
    {
        if (except != aurEff && (aurEff->GetMiscValue() & miscMask) != 0)
            return true;
        return false;
    });
}

int32 Unit::GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 miscMask) const
{
    return GetMaxNegativeAuraModifier(auratype, [miscMask](AuraEffect const* aurEff) -> bool
    {
        if ((aurEff->GetMiscValue() & miscMask) != 0)
            return true;
        return false;
    });
}

int32 Unit::GetTotalAuraModifierByMiscValue(AuraType auratype, int32 miscValue) const
{
    return GetTotalAuraModifier(auratype, [miscValue](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->GetMiscValue() == miscValue)
            return true;
        return false;
    });
}

int32 Unit::GetTotalAuraModifierByMiscValues(AuraType auratype, int32 miscValue, int32 miscValueB) const
{
    return GetTotalAuraModifier(auratype, [miscValue, miscValueB](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->GetMiscValue() == miscValue && aurEff->GetMiscValueB() == miscValueB) // Currently need for all data ==
            return true;
        return false;
    });
}

float Unit::GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 miscValue, bool mainStats) const
{
    return GetTotalAuraMultiplier(auratype, [miscValue, mainStats](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->GetMiscValue() == miscValue || (mainStats && aurEff->GetMiscValue() == -1))
            return true;
        return false;
    });
}

float Unit::GetTotalAuraMultiplierByMiscValueB(AuraType auratype, int32 miscValueB) const
{
    return GetTotalAuraMultiplier(auratype, [miscValueB](AuraEffect const* aurEff) -> bool
    {
        if (!aurEff->GetMiscValueB() || aurEff->GetMiscValueB() == miscValueB)
            return true;
        return false;
    });
}

float Unit::GetTotalAuraMultiplierByMiscValues(AuraType auratype, int32 miscValue, int32 miscValueB) const
{
    return GetTotalAuraMultiplier(auratype, [miscValue, miscValueB](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->GetMiscValue() == miscValue)
            if (!aurEff->GetMiscValueB() || aurEff->GetMiscValueB() == miscValueB)
                return true;
        return false;
    });
}

int32 Unit::GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 miscValue) const
{
    return GetMaxPositiveAuraModifier(auratype, [miscValue](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->GetMiscValue() == miscValue)
            return true;
        return false;
    });
}

int32 Unit::GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 miscValue) const
{
    return GetMaxNegativeAuraModifier(auratype, [miscValue](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->GetMiscValue() == miscValue)
            return true;
        return false;
    });
}

int32 Unit::GetTotalAuraModifierByAffectMask(AuraType auratype, SpellInfo const* affectedSpell) const
{
    return GetTotalAuraModifier(auratype, [affectedSpell](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->IsAffectingSpell(affectedSpell))
            return true;
        return false;
    });
}

float Unit::GetTotalAuraMultiplierByAffectMask(AuraType auratype, SpellInfo const* affectedSpell) const
{
    return GetTotalAuraMultiplier(auratype, [affectedSpell](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->IsAffectingSpell(affectedSpell))
            return true;
        return false;
    });
}

int32 Unit::GetMaxPositiveAuraModifierByAffectMask(AuraType auratype, SpellInfo const* affectedSpell) const
{
    return GetMaxPositiveAuraModifier(auratype, [affectedSpell](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->IsAffectingSpell(affectedSpell))
            return true;
        return false;
    });
}

int32 Unit::GetMaxNegativeAuraModifierByAffectMask(AuraType auratype, SpellInfo const* affectedSpell) const
{
    return GetMaxNegativeAuraModifier(auratype, [affectedSpell](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->IsAffectingSpell(affectedSpell))
            return true;
        return false;
    });
}

void Unit::_RegisterDynObject(DynamicObject* dynObj)
{
    m_dynObj.push_back(dynObj);
}

void Unit::_UnregisterDynObject(DynamicObject* dynObj)
{
    m_dynObj.remove(dynObj);
}

DynamicObject* Unit::GetDynObject(uint32 spellId)
{
    if (m_dynObj.empty())
        return nullptr;
    for (DynObjectList::const_iterator i = m_dynObj.begin(); i != m_dynObj.end();++i)
    {
        DynamicObject* dynObj = *i;
        if (dynObj->GetSpellId() == spellId)
            return dynObj;
    }
    return nullptr;
}

int32 Unit::CountDynObject(uint32 spellId)
{
    int32 count = 0;

    if (m_dynObj.empty())
        return 0;
    for (DynObjectList::const_iterator i = m_dynObj.begin(); i != m_dynObj.end();++i)
    {
        DynamicObject* dynObj = *i;
        if (dynObj->GetSpellId() == spellId)
            count++;
    }
    return count;
}

void Unit::GetDynObjectList(std::list<DynamicObject*> &list, uint32 spellId)
{
    if (m_dynObj.empty())
        return;
    for (DynObjectList::const_iterator i = m_dynObj.begin(); i != m_dynObj.end();++i)
    {
        DynamicObject* dynObj = *i;
        if (dynObj->GetSpellId() == spellId)
            list.push_back(dynObj);
    }
}

void Unit::RemoveDynObject(uint32 spellId)
{
    if (m_dynObj.empty())
        return;
    for (DynObjectList::iterator i = m_dynObj.begin(); i != m_dynObj.end();)
    {
        DynamicObject* dynObj = *i;
        if (dynObj->GetSpellId() == spellId)
        {
            dynObj->Remove();
            i = m_dynObj.begin();
        }
        else
            ++i;
    }
}

void Unit::RemoveAllDynObjects()
{
    while (!m_dynObj.empty())
        m_dynObj.front()->Remove();
}

/*AreaTrigger*/
void Unit::_RegisterAreaObject(AreaTrigger* areaObj)
{
    m_AreaObj.push_back(areaObj);
}

void Unit::_UnregisterAreaObject(AreaTrigger* areaObj)
{
    m_AreaObj.remove(areaObj);
}

AreaTrigger* Unit::GetAreaObject(uint32 spellId)
{
    if (m_AreaObj.empty())
        return nullptr;
    for (AreaObjectList::const_iterator i = m_AreaObj.begin(); i != m_AreaObj.end();++i)
    {
        AreaTrigger* areaObj = *i;
        if (areaObj->GetSpellId() == spellId)
            return areaObj;
    }
    return nullptr;
}

int32 Unit::CountAreaObject(uint32 spellId)
{
    int32 count = 0;

    if (m_AreaObj.empty())
        return 0;
    for (AreaObjectList::const_iterator i = m_AreaObj.begin(); i != m_AreaObj.end();++i)
    {
        AreaTrigger* areaObj = *i;
        if (areaObj->GetSpellId() == spellId)
            count++;
    }
    return count;
}

void Unit::GetAreaObjectList(std::list<AreaTrigger*> &list, uint32 spellId)
{
    if (m_AreaObj.empty())
        return;
    for (AreaObjectList::const_iterator i = m_AreaObj.begin(); i != m_AreaObj.end();++i)
    {
        AreaTrigger* areaObj = *i;
        if (areaObj->GetSpellId() == spellId)
            list.push_back(areaObj);
    }
}

void Unit::GetAreaObjectList(std::list<AreaTrigger*>& list, std::vector<uint32>& spellIdList)
{
    if (m_AreaObj.empty() || spellIdList.empty())
        return;

    for (AreaObjectList::const_iterator i = m_AreaObj.begin(); i != m_AreaObj.end(); ++i)
    {
        AreaTrigger* areaObj = *i;

        for (auto itr : spellIdList)
        {
            if (areaObj->GetSpellId() == itr)
            {
                list.push_back(areaObj);
                break;
            }
        }
    }
}

void Unit::RemoveAreaObject(uint32 spellId, uint32 entry/* = 0*/)
{
    if (m_AreaObj.empty())
        return;

    for (AreaObjectList::iterator i = m_AreaObj.begin(); i != m_AreaObj.end();)
    {
        AreaTrigger* areaObj = *i;

        if (areaObj->GetSpellId() == spellId || (entry && areaObj->GetRealEntry() == entry))
        {
            areaObj->Despawn();
            i = m_AreaObj.begin();
        }
        else
            ++i;
    }
}

void Unit::RemoveAllAreaObjects()
{
    while (!m_AreaObj.empty())
    {
        m_AreaObj.front()->Despawn();
        // m_AreaObj.pop_front();
    }
}

void Unit::ReCreateAreaTriggerObjects()
{
    if (AuraEffectList* areaTriggerAuras = GetAuraEffectsByType(SPELL_AURA_CREATE_AREATRIGGER))
    for (AuraEffectList::iterator i = areaTriggerAuras->begin(); i != areaTriggerAuras->end(); ++i)
    {
        Aura* aura = (*i)->GetBase();
        if (!aura)
            continue;

        Unit* _caster = (*i)->GetCaster();
        if (!_caster || _caster->GetMap() != GetMap())
            continue;

        AreaTrigger* areaTrigger = ObjectAccessor::GetAreaTrigger(*this, aura->GetSpellAreaTrigger());
        if (areaTrigger)
            continue;

        Position pos;
        GetPosition(&pos);
        uint32 triggerEntry = (*i)->GetMiscValue();

        areaTrigger = new AreaTrigger;
        if (!areaTrigger->CreateAreaTrigger(sObjectMgr->GetGenerator<HighGuid::AreaTrigger>()->Generate(), triggerEntry, _caster, aura->GetSpellInfo(), pos, pos, nullptr, GetGUID()))
        {
            delete areaTrigger;
            return;
        }

        aura->SetSpellAreaTrigger(areaTrigger->GetGUID());
    }
}

/*Conversation*/
void Unit::_RegisterConversationObject(Conversation* conversObj)
{
    m_ConversationObj.push_back(conversObj);
}

void Unit::_UnregisterConversationObject(Conversation* conversObj)
{
    m_ConversationObj.remove(conversObj);
}

Conversation* Unit::GetConversationObject(uint32 spellId)
{
    if (m_ConversationObj.empty())
        return nullptr;

    for (ConversationObjectList::const_iterator i = m_ConversationObj.begin(); i != m_ConversationObj.end();++i)
    {
        Conversation* conversObj = *i;
        if (conversObj->GetSpellId() == spellId)
            return conversObj;
    }
    return nullptr;
}

void Unit::GetConversationObjectList(std::list<Conversation*> &list, uint32 spellId)
{
    if (m_ConversationObj.empty())
        return;
    for (ConversationObjectList::const_iterator i = m_ConversationObj.begin(); i != m_ConversationObj.end();++i)
    {
        Conversation* conversObj = *i;
        if (conversObj->GetSpellId() == spellId)
            list.push_back(conversObj);
    }
}

void Unit::RemoveConversationObject(uint32 spellId, uint32 entry/* = 0*/)
{
    if (m_ConversationObj.empty())
        return;

    for (ConversationObjectList::iterator i = m_ConversationObj.begin(); i != m_ConversationObj.end();)
    {
        Conversation* conversObj = *i;

        if (conversObj->GetSpellId() == spellId || (entry && conversObj->GetEntry() == entry))
        {
            conversObj->Remove();
            i = m_ConversationObj.begin();
        }
        else
            ++i;
    }
}

void Unit::RemoveAllConversationObjects()
{
    while (!m_ConversationObj.empty())
    {
        m_ConversationObj.front()->Remove();
        // m_AreaObj.pop_front();
    }
}

GameObject* Unit::GetGameObject(uint32 spellId) const
{
    for (GameObjectList::const_iterator i = m_gameObj.begin(); i != m_gameObj.end(); ++i)
        if (GameObject* gObj = *i)
            if (gObj->GetSpellId() == spellId)
                return *i;

    return nullptr;
}

GameObject* Unit::GetGameObjectbyId(uint32 entry) const
{
    for (GameObjectList::const_iterator i = m_gameObj.begin(); i != m_gameObj.end(); ++i)
        if (GameObject* gObj = *i)
            if (gObj->GetEntry() == entry)
                return *i;

    return nullptr;
}


void Unit::AddGameObject(GameObject* gameObj)
{
    if (!gameObj || !gameObj->GetOwnerGUID() == 0)
        return;

    m_gameObj.push_back(gameObj);
    gameObj->SetOwnerGUID(GetGUID());

    if (IsPlayer() && gameObj->GetSpellId())
    {
        SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(gameObj->GetSpellId());
        // Need disable spell use for owner
        if (createBySpell && createBySpell->HasAttribute(SPELL_ATTR0_DISABLED_WHILE_ACTIVE))
            // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases)
            ToPlayer()->AddSpellAndCategoryCooldowns(createBySpell, 0, nullptr, true);
    }
}

void Unit::RemoveGameObject(GameObject* gameObj, bool del)
{
    if (!gameObj || gameObj->GetOwnerGUID() != GetGUID())
        return;

    gameObj->SetOwnerGUID(ObjectGuid::Empty);

    for (uint8 i = 0; i < MAX_GAMEOBJECT_SLOT; ++i)
    {
        if (m_ObjectSlot[i] == gameObj->GetGUID())
        {
            m_ObjectSlot[i].Clear();
            break;
        }
    }

    // GO created by some spell
    if (uint32 spellid = gameObj->GetSpellId())
    {
        SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(spellid);

        if (!createBySpell->HasAttribute(SPELL_ATTR1_IS_FISHING))
            RemoveAurasDueToSpell(spellid);

        if (IsPlayer())
        {
            // Need activate spell use for owner
            if (createBySpell && createBySpell->HasAttribute(SPELL_ATTR0_DISABLED_WHILE_ACTIVE))
                // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases)
                ToPlayer()->SendCooldownEvent(createBySpell);
        }
    }

    m_gameObj.remove(gameObj);

    if (del)
    {
        gameObj->SetRespawnTime(0);
        gameObj->Delete();
    }
}

void Unit::RemoveGameObject(uint32 spellid, bool del)
{
    if (m_gameObj.empty())
        return;

    GameObjectList::iterator next;
     for (GameObjectList::iterator i = m_gameObj.begin(); i != m_gameObj.end(); i = next)
    {
        next = i;
        if (!*i)
        {
            ++next;
            continue;
        }

        if (spellid == 0 || (*i)->GetSpellId() == spellid)
        {
            (*i)->SetOwnerGUID(ObjectGuid::Empty);
            if (del)
            {
                (*i)->SetRespawnTime(0);
                (*i)->Delete();
            }

            next = m_gameObj.erase(i);
        }
        else
            ++next;
    }
}

void Unit::RemoveAllGameObjects()
{
    // remove references to unit
    while (!m_gameObj.empty())
    {
        GameObjectList::iterator i = m_gameObj.begin();
        (*i)->SetOwnerGUID(ObjectGuid::Empty);
        (*i)->SetRespawnTime(0);
        (*i)->Delete();
        m_gameObj.erase(i);
    }
}

void Unit::SendSpellNonMeleeDamageLog(SpellNonMeleeDamage* log)
{
    WorldPackets::CombatLog::SpellNonMeleeDamageLog packet;
    packet.Me = log->target->GetGUID();
    packet.CasterGUID = log->attacker->GetGUID();
    packet.CastID = log->CastGuid;
    packet.SpellID = log->SpellID;
    packet.Damage = log->damage;
    if (packet.Damage > log->preHitHealth)
        packet.Overkill = packet.Damage - log->preHitHealth;
    packet.SchoolMask = log->schoolMask;
    packet.ShieldBlock = log->blocked;
    packet.Resisted = log->resist;
    packet.Absorbed = log->absorb;
    packet.Periodic = log->periodicLog;
    packet.Flags = log->HitInfo;
    packet.SpellXSpellVisualID = log->SpellXSpellVisualID;
     
    WorldPackets::Spells::SandboxScalingData sandboxScalingData;
    if (sandboxScalingData.GenerateDataForUnits(log->attacker, log->target))
        packet.SandboxScaling = sandboxScalingData;

    SendCombatLogMessage(&packet);
}

void Unit::SendSpellNonMeleeDamageLog(Unit* target, uint32 SpellID, uint32 Damage, SpellSchoolMask damageSchoolMask, uint32 AbsorbedDamage, uint32 Resist, bool PhysicalDamage, uint32 Blocked, uint32 spellXSpellVisualID, bool CriticalHit)
{
    SpellNonMeleeDamage log(this, target, SpellID, spellXSpellVisualID, damageSchoolMask);
    log.damage = Damage;
    log.absorb = AbsorbedDamage;
    log.resist = Resist;
    log.periodicLog = PhysicalDamage;
    log.blocked = Blocked;
    log.HitInfo = SPELL_HIT_TYPE_CRIT_DEBUG | SPELL_HIT_TYPE_HIT_DEBUG | SPELL_HIT_TYPE_ATTACK_TABLE_DEBUG;
    if (CriticalHit)
        log.HitInfo |= SPELL_HIT_TYPE_CRIT;
    SendSpellNonMeleeDamageLog(&log);
}

void Unit::ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, DamageInfo* dmgInfoProc, WeaponAttackType attType, SpellInfo const* procSpell, SpellInfo const* procAura, Uint32Set* AppliedProcMods, Spell* spell)
{
     // Not much to do if no flags are set.
    if (procAttacker)
        ProcDamageAndSpellFor(false, victim, procAttacker, procExtra, attType, procSpell, dmgInfoProc, procAura, AppliedProcMods, spell);

    // Now go on with a victim's events'n'auras
    // Not much to do if no flags are set or there is no victim
    if (victim && victim->isAlive() && procVictim)
        victim->ProcDamageAndSpellFor(true, this, procVictim, procExtra, attType, procSpell, dmgInfoProc, procAura, AppliedProcMods, spell);
}

void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo* info)
{
    AuraEffect const* aura = info->auraEff;
    WorldPackets::CombatLog::SpellPeriodicAuraLog data;
    data.TargetGUID = GetGUID();
    data.CasterGUID = aura->GetCasterGUID();
    data.SpellID = aura->GetId();
    data.LogData.Initialize(this);

    WorldPackets::CombatLog::SpellPeriodicAuraLog::SpellLogEffect spellLogEffect;
    spellLogEffect.Effect = aura->GetAuraType();
    spellLogEffect.Amount = info->damage;
    spellLogEffect.OverHealOrKill = info->overDamage;
    spellLogEffect.SchoolMaskOrPower = aura->GetSpellInfo()->GetSchoolMask();
    spellLogEffect.AbsorbedOrAmplitude = info->absorb;
    spellLogEffect.Resisted = info->resist;
    spellLogEffect.Crit = info->critical; 

    WorldPackets::Spells::SandboxScalingData sandboxScalingData;
    if (auto caster = ObjectAccessor::GetUnit(*this, aura->GetCasterGUID()))
        if (sandboxScalingData.GenerateDataForUnits(caster, this))
            spellLogEffect.SandboxScaling = sandboxScalingData;

    data.Effects.push_back(spellLogEffect);
    SendCombatLogMessage(&data);
}

void Unit::SendSpellMiss(Unit* target, uint32 spellID, SpellMissInfo missInfo)
{
    WorldPackets::CombatLog::SpellMissLog spellMissLog;
    spellMissLog.SpellID = spellID;
    spellMissLog.Caster = GetGUID();
    spellMissLog.Entries.emplace_back(target->GetGUID(), missInfo);
    SendMessageToSet(spellMissLog.Write(), true);
}

void Unit::SendSpellDamageResist(Unit* target, uint32 spellId)
{
    WorldPackets::CombatLog::ProcResist procResist;
    procResist.Caster = GetGUID();
    procResist.SpellID = spellId;
    procResist.Target = target->GetGUID();
    SendMessageToSet(procResist.Write(), true);
}

void Unit::SendSpellDamageImmune(Unit* target, uint32 spellId, bool isPeriodic)
{
    WorldPackets::CombatLog::SpellOrDamageImmune spellOrDamageImmune;
    spellOrDamageImmune.CasterGUID = GetGUID();
    spellOrDamageImmune.VictimGUID = target->GetGUID();
    spellOrDamageImmune.SpellID = spellId;
    spellOrDamageImmune.IsPeriodic = isPeriodic;
    SendMessageToSet(spellOrDamageImmune.Write(), true);
}

void Unit::SendAttackStateUpdate(CalcDamageInfo* damageInfo)
{
    WorldPackets::CombatLog::AttackerStateUpdate packet;
    packet.HitInfo = damageInfo->HitInfo;
    packet.AttackerGUID = damageInfo->attacker->GetGUID();
    packet.VictimGUID = damageInfo->target->GetGUID();
    packet.Damage = damageInfo->damage;
    packet.OverDamage = damageInfo->damage > damageInfo->target->GetHealth(damageInfo->attacker) ? damageInfo->damage - damageInfo->target->GetHealth(damageInfo->attacker) : -1;
    
    packet.SubDmg = boost::in_place();
    packet.SubDmg->SchoolMask = damageInfo->damageSchoolMask;
    packet.SubDmg->FDamage = damageInfo->damage;
    packet.SubDmg->Damage = damageInfo->damage;
    packet.SubDmg->Absorbed = damageInfo->absorb;
    packet.SubDmg->Resisted = damageInfo->resist;

    packet.VictimState = damageInfo->TargetState;
    packet.BlockAmount = damageInfo->blocked_amount;

    packet.LogData.Initialize(damageInfo->attacker); 
    WorldPackets::Spells::SandboxScalingData sandboxScalingData;
    if (sandboxScalingData.GenerateDataForUnits(damageInfo->attacker, damageInfo->target))
        packet.SandboxScaling = sandboxScalingData;

    SendCombatLogMessage(&packet);
}

void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount)
{
    CalcDamageInfo dmgInfo;
    dmgInfo.HitInfo = HitInfo;
    dmgInfo.attacker = this;
    dmgInfo.target = target;
    dmgInfo.damage = Damage - AbsorbDamage - Resist - BlockedAmount;
    dmgInfo.damageSchoolMask = damageSchoolMask;
    dmgInfo.absorb = AbsorbDamage;
    dmgInfo.resist = Resist;
    dmgInfo.TargetState = TargetState;
    dmgInfo.blocked_amount = BlockedAmount;
    SendAttackStateUpdate(&dmgInfo);
}

bool Unit::HandleHasteAuraProc(Unit* victim, DamageInfo* dmgInfoProc, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, double cooldown)
{
    SpellInfo const* hasteSpell = triggeredByAura->GetSpellInfo();

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer() ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    uint32 triggered_spell_id = 0;
    Unit* target = victim;
    float basepoints0 = 0;

    // processed charge only counting case
    if (!triggered_spell_id)
        return true;

    if (!sSpellMgr->GetSpellInfo(triggered_spell_id))
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleHasteAuraProc: Spell %u has non-existing triggered spell %u", hasteSpell->Id, triggered_spell_id);
        return false;
    }

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(triggered_spell_id))
        return false;

    if (basepoints0)
        CastCustomSpell(target, triggered_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura);
    else
        CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(triggered_spell_id, 0, getPreciseTime() + cooldown);

    return true;
}

bool Unit::HandleProcTriggerSpellCopy(Unit* victim, DamageInfo* dmgInfoProc, AuraEffect* triggeredByAura, SpellInfo const* procSpell, double cooldown)
{
    if (!procSpell || !victim)
        return false;

    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
    {
        if (procSpell->EffectMask < uint32(1 << i))
            break;

        if (procSpell->Effects[i]->Effect)
            if (procSpell->Effects[i]->HasRadius())
                return false;
    }

    Aura* triggerAura = triggeredByAura->GetBase();

    if (procSpell->HasPower(POWER_COMBO_POINTS) && triggerAura)
        triggerAura->SetCustomData(GetComboPoints());

    int32 triggeredByAuraId = triggeredByAura->GetId();
    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(triggeredByAuraId))
        return false;

    Item* castItem = triggerAura->GetCastItemGUID() && IsPlayer() ? ToPlayer()->GetItemByGuid(triggerAura->GetCastItemGUID()) : nullptr;
    CastSpell(victim, procSpell->Id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(triggeredByAuraId, 0, getPreciseTime() + cooldown);

    return true;
}

bool Unit::HandleSpellCritChanceAuraProc(Unit* victim, DamageInfo* /*dmgInfoProc*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, double cooldown)
{
    SpellInfo const* triggeredByAuraSpell = triggeredByAura->GetSpellInfo();

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer()
        ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    uint32 triggered_spell_id = 0;
    Unit* target = victim;
    float basepoints0 = 0;

    // processed charge only counting case
    if (!triggered_spell_id)
        return true;

    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);

    if (!triggerEntry)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleSpellCritChanceAuraProc: Spell %u has non-existing triggered spell %u", triggeredByAuraSpell->Id, triggered_spell_id);
        return false;
    }

    // default case
    if (!target || (target != this && !target->isAlive()))
        return false;

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(triggered_spell_id))
        return false;

    if (basepoints0)
        CastCustomSpell(target, triggered_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura);
    else
        CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(triggered_spell_id, 0, getPreciseTime() + cooldown);

    return true;
}

//victim may be NULL
bool Unit::HandleDummyAuraProc(Unit* victim, DamageInfo* dmgInfoProc, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, double cooldown, Spell* spell)
{
    uint32 damage = dmgInfoProc->GetDamage() + dmgInfoProc->GetAbsorb();
    SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();
    uint32 effIndex = triggeredByAura->GetEffIndex();
    float triggerAmount = triggeredByAura->GetAmount();
    Aura* triggerAura = triggeredByAura->GetBase();

    Item* castItem = triggerAura->GetCastItemGUID() && IsPlayer()
        ? ToPlayer()->GetItemByGuid(triggerAura->GetCastItemGUID()) : nullptr;

    uint32 triggered_spell_id = triggeredByAura->GetTriggerSpell() ? triggeredByAura->GetTriggerSpell(): 0;
    uint32 cooldown_spell_id = 0; // for random trigger, will be one of the triggered spell to avoid repeatable triggers
                                  // otherwise, it's the triggered_spell_id by default
    Unit* target = victim;
    float basepoints0 = 0.f;
    float basepoints1 = 0.f;
    float basepoints2 = 0.f;
    ObjectGuid originalCaster;

    switch (dummySpell->ClassOptions.SpellClassSet)
    {
        case SPELLFAMILY_GENERIC:
        {
            switch (dummySpell->Id)
            {
                case 195606: // Inner Renewal (Honor Talent)
                {
                    if (this != target || !procSpell || !spell || !damage)
                        return false;

                    SpellPowerData powerData;
                    if (!procSpell->GetSpellPowerByCasterPower(this, powerData))
                        return false;

                    int32 manaCost = 0;

                    for (SpellPowerEntry const* power : powerData)
                    {
                        if (power->PowerType == POWER_MANA)
                        {
                            float pwr = HasAura(137012) ? power->PowerCostPct / 5 : power->PowerCostPct; // Hack: for Restroration Dru - wrong real powerCost-> example powerCost 30800, real handle powerCost 154000 oO

                            if (pwr)
                            {
                                if (uint32 curMana = GetMaxPower(POWER_MANA))
                                    manaCost = CalculatePct(curMana, pwr);
                            }
                            else if (power->ManaCost)
                                manaCost = power->ManaCost;

                            break;
                        }
                    }

                    if (!manaCost)
                        return false;

                    basepoints0 = CalculatePct(manaCost, triggerAmount);
                    break;
                }
                case 214027: // Adaptation
                {
                    if (procSpell->HasAura(SPELL_AURA_MOD_CONFUSE) || procSpell->HasAura(SPELL_AURA_MOD_FEAR) || procSpell->HasAura(SPELL_AURA_MOD_FEAR_2) || 
                        procSpell->HasAura(SPELL_AURA_MOD_STUN) || procSpell->HasAura(SPELL_AURA_STRANGULATE) || procSpell->HasAura(SPELL_AURA_MOD_ROOTED) || procSpell->HasAura(SPELL_AURA_TRANSFORM))
                    {
                        if (Aura* aura = GetAura(procSpell->Id))
                        {
                            if (aura->GetDuration() >= triggerAmount * IN_MILLISECONDS)
                            {
                                CastSpell(this, 195845, true);

                                triggerAura->SetAuraAttribute(AURA_ATTR_IS_NOT_ACTIVE);

                                if (Player* plr = ToPlayer())
                                {
                                    if (!plr->InArena())
                                        return true;

                                    uint32 timeSync = plr->GetTimeSyncClient();
                                    uint32 cool = 60000;

                                    if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(195901))
                                        cool = spellInfo->GetDuration();

                                    PlayerDynamicFieldArenaCooldowns aCool = PlayerDynamicFieldArenaCooldowns(dummySpell->Id, timeSync, timeSync + cool);
                                    plr->SetDynamicStructuredValue(PLAYER_DYNAMIC_FIELD_ARENA_COOLDOWNS, 0, &aCool);
                                }
                            }
                        }
                    }
                    return false;
                }
                case 228695: // PvP Rules Enabled for Dummy
                {
					if (!sWorld->getBoolConfig(CONFIG_PLAYER_ALLOW_PVP_TALENTS_ALL_THE_TIME))
						if (Player* plr = victim->ToPlayer())
						{
							if (plr->getLevel() >= 110)
							{
								if (plr->HasAura(SPELL_PRINCIPLES_OF_WAR))
									return false;

								if (Aura* aura = plr->GetAura(SPELL_PRINCIPLES_OF_WAR_FROM_DUMMY))
									aura->SetDuration(aura->GetMaxDuration());
								else
									plr->AddAura(SPELL_PRINCIPLES_OF_WAR_FROM_DUMMY, plr);
								plr->EnablePvpRules(false);
								return true;
							}
						}
                    break;
                }
                case 228696: // Principles of War from Dummy
                    if (victim->IsPlayer())
                        RemoveAurasDueToSpell(SPELL_PRINCIPLES_OF_WAR_FROM_DUMMY);
                    else
                        return false;
                    break;
                case 235966: // Velen's Future Sight
                {
                    if (effIndex != EFFECT_0)
                        return false;

                    if (Unit* caster = triggeredByAura->GetCaster())
                    {
                        if (AuraEffect* aurEff = caster->GetAuraEffect(235966, EFFECT_0))
                        {
                            basepoints0 = CalculatePct(dmgInfoProc->GetOverHeal(), dummySpell->Effects[EFFECT_3]->BasePoints);
                            aurEff->SetAmount(triggerAmount + basepoints0);
                        }
                    }
                    break;
                }
                case 256374: // Entropic Embrace (Racial Passive)
                {
                    float basepoints0 = CalculatePct(dmgInfoProc->GetDamage(), dummySpell->Effects[EFFECT_0]->BasePoints);

                    if (IsFriendlyTo(victim))
                        CastCustomSpell(victim, 259760, &basepoints0, nullptr, nullptr, true);
                    else
                        CastCustomSpell(victim, 259756, &basepoints0, nullptr, nullptr, true);
                    break;
                }
                case 251949: // Hammer-Forged
                {
                    if (Player* plr = ToPlayer())
                    {
                        if (Aura* aura = GetAura(251952))
                            aura->ModStackAmount(-1);

                        plr->ModifySpellCooldown(251952, -triggerAmount);
                    }
                    return true;
                }
                case 252207: // Refractive Shell
                {
                    CastCustomSpell(this, 252208, &triggerAmount, nullptr, nullptr, true);
                    break;
                }
                case 252875: // Shadowbind
                {
                    if (victim == this)
                        return false;

                    CastCustomSpell(victim, 252879, &triggerAmount, nullptr, nullptr, true);
                    break;
                }
                case 252888: // Chaotic Darkness
                {
                    if (victim == this)
                        return false;

                    float bp0 = urand(1, 5) * triggerAmount;
                    CastCustomSpell(victim, 252896, &bp0, nullptr, nullptr, true);
                    CastCustomSpell(this, 252897, &bp0, nullptr, nullptr, true);
                    break;
                }
                case 252906: // Torment the Weak
                {
                    if (victim == this)
                        return false;

                    CastCustomSpell(victim, 252907, &triggerAmount, nullptr, nullptr, true);
                    break;
                }
                case 252922: // Sorrow
                {
                    if (victim == this)
                        return false;

                    CastCustomSpell(victim, 252921, &triggerAmount, nullptr, nullptr, true);
                    break;
                }
                case 253070: // Secure in the Light
                {
                    if (IsFriendlyTo(victim))
                        CastCustomSpell(this, 253072, &triggerAmount, nullptr, nullptr, true);
                    else
                        CastCustomSpell(victim, 253073, &triggerAmount, nullptr, nullptr, true);
                    break;
                }
                case 253093: // Infusion of Light
                {
                    if (IsFriendlyTo(victim))
                        CastCustomSpell(victim, 253099, &triggerAmount, nullptr, nullptr, true);
                    else
                        CastCustomSpell(victim, 253098, &triggerAmount, nullptr, nullptr, true);
                    break;
                }
                case 253111: // Light's Embrace
                {
                    CastCustomSpell(this, 253216, &triggerAmount, nullptr, nullptr, true);
                    break;
                }
                case 152278: // Anger Management
                {
                    if (effIndex != EFFECT_0)
                        return false;

                    if (procSpell->Power.PowerType == POWER_RAGE)
                    {
                        if (procSpell->Power.PowerCost > 0)
                        {
                            if (Player* plr = ToPlayer())
                            {
                                int32 modColdown = std::max(procSpell->Power.PowerCost, m_powerCost[POWER_RAGE]) / 2;

                                switch (plr->GetSpecializationId())
                                {
                                    case SPEC_WARRIOR_PROTECTION:
                                    {
                                        plr->ModifySpellCooldown(1719, dummySpell->Effects[EFFECT_0]->BasePoints * -modColdown);
                                        plr->ModifySpellCooldown(871, dummySpell->Effects[EFFECT_0]->BasePoints * -modColdown);
                                        plr->ModifySpellCooldown(1160, dummySpell->Effects[EFFECT_0]->BasePoints * -modColdown);
                                        plr->ModifySpellCooldown(12975, dummySpell->Effects[EFFECT_0]->BasePoints * -modColdown);
                                        break;
                                    }
                                    case SPEC_WARRIOR_ARMS:
                                    {
                                        plr->ModifySpellCooldown(1719, dummySpell->Effects[EFFECT_1]->BasePoints * -modColdown);
                                        plr->ModifySpellCooldown(227847, dummySpell->Effects[EFFECT_1]->BasePoints * -modColdown);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    break;
                }
                case 233766: // Control the Mists
                {
                    for (uint8 i = 0; i < MAX_POWERS_FOR_SPELL; ++i)
                    {
                        if (procSpell->spellPower[i] && procSpell->spellPower[i]->PowerType == POWER_CHI && procSpell->spellPower[i]->ManaCost > 0)
                        {
                            if (triggerAura)
                            {
                                if (AuraEffect* aurEff = GetAuraEffect(233765, EFFECT_0)) // Control the Mists (Tal)
                                    triggerAura->SetDuration(triggerAura->GetDuration() - procSpell->spellPower[i]->ManaCost * 1000 * aurEff->GetAmount());
                                    if (triggerAura->GetDuration() < 1000)
                                        triggerAura->Remove();
                            }
                            return true;
                        }
                    }
                    return false;
                }
                case 207692: // Cinidaria, the Symbiote
                {
                    if (effIndex != EFFECT_0 || !damage)
                        return false;

                    if (victim->GetHealthPct() < dummySpell->Effects[EFFECT_1]->BasePoints)
                        return false;

                    basepoints0 = CalculatePct(dmgInfoProc->GetDamage(), triggerAmount);
                    triggered_spell_id = 207694;
                    break;
                }
                case 215938: // Soul Sap
                {
                    if (effIndex != EFFECT_0 || !damage)
                        return false;

                    bool needRemove = false;
                    float amount = CalculatePct(int32(damage), triggerAmount);
                    if (triggerAura && victim->HasAura(215936, GetGUID()))
                    {
                        if (AuraEffect* aurEff = triggerAura->GetEffect(EFFECT_1))
                        {
                            if (!aurEff->GetAmount())
                                return false;

                            aurEff->SetAmount(aurEff->GetAmount() - amount);
                            if (aurEff->GetAmount() <= 0)
                                needRemove = true;
                        }
                        if (needRemove)
                        {
                            triggerAura->SetDuration(100);
                            victim->RemoveAurasDueToSpell(215936, GetGUID());
                        }
                        CastCustomSpell(this, 215940, &amount, nullptr, nullptr, true, castItem, triggeredByAura);
                    }
                    return false;
                }
                case 225778: // Backlash
                {
                    if (effIndex != EFFECT_0 || !damage)
                        return false;

                    bool needRemove = false;
                    float amount = CalculatePct(int32(damage), triggerAmount);
                    if (triggerAura)
                    {
                        if (AuraEffect* aurEff = triggerAura->GetEffect(EFFECT_1))
                        {
                            if (!aurEff->GetAmount())
                                return false;

                            aurEff->SetAmount(aurEff->GetAmount() - amount);
                            if (aurEff->GetAmount() <= 0)
                                needRemove = true;
                        }
                        if (needRemove)
                            triggerAura->SetDuration(100);
                    }
                    CastCustomSpell(this, 229528, &amount, nullptr, nullptr, true, castItem, triggeredByAura);
                    return false;
                }
                case 215672: // Taint of the Sea
                {
                    if (effIndex != EFFECT_1 || !damage)
                        return false;

                    bool needRemove = false;
                    float amount = CalculatePct(dmgInfoProc->GetDamageBeforeHit(), triggerAmount);
                    if (triggerAura)
                    {
                        if (AuraEffect* aurEff = triggerAura->GetEffect(EFFECT_0))
                        {
                            if (!aurEff->GetAmount())
                                return false;

                            aurEff->SetAmount(aurEff->GetAmount() - amount * (10.f / 3.f));
                            if (aurEff->GetAmount() <= 0)
                                needRemove = true;
                        }
                        if (needRemove)
                        {
                            triggerAura->SetDuration(100);
                            victim->RemoveAurasDueToSpell(215670, GetGUID());
                        }                          
                    }
                    CastCustomSpell(victim, 215695, &amount, nullptr, nullptr, true, castItem, triggeredByAura);
                    return false;
                }
                case 206151: // Challenger's Burden
                {
                    switch (effIndex)
                    {
                        case EFFECT_1:
                            if (!triggerAmount)
                                return false;
                            if (uint32 overHeal = dmgInfoProc->GetOverHeal())
                            {
                                triggered_spell_id = ChallengerOverflowing;
                                basepoints0 = overHeal * 3;
                            }
                            break;
                    }
                    break;
                }
                case 206150: // Challenger's Might
                {
                    switch (effIndex)
                    {
                        case EFFECT_2:
                            if (!triggerAmount || GetHealthPct() > 30.0f)
                                return false;
                            break;
                        case EFFECT_8:
                            if (!triggerAmount)
                                return false;
                            triggered_spell_id = 209858;
                            break;
                    }
                    break;
                }
                // Atonement
                case 195178:
                {
                    Unit* owner = GetAnyOwner();
                    if (!owner)
                        return false;

                    std::list<Unit*> targetList;
                    std::vector<uint32> aura = {194384, 214206};

                    owner->TargetsWhoHasMyAuras(targetList, aura);
                    if (owner->HasAura(194384, owner->GetGUID()) || owner->HasAura(214206, owner->GetGUID()))
                        targetList.push_back(owner);

                    if (targetList.empty())
                        return false;

                    int32 perc = 40;
                    if (AuraEffect* aurEff = owner->GetAuraEffect(81749, EFFECT_0))
                        perc = aurEff->GetAmount();

                    // heal amount
                    float amount = CalculatePct(int32(damage), perc);
                    for (auto target: targetList)
                        CastCustomSpell(target, 81751, &amount, nullptr, nullptr, true, castItem, triggeredByAura);
                    return true;
                }
                case 146200: // Spirit of Chi-Ji
                {
                    if(!target)
                        return false;

                    basepoints0 = damage - (target->GetMaxHealth(this) - target->GetHealth(this));

                    if (basepoints0 > 0)
                        triggered_spell_id = 148009;
                    break;
                }
                case 146136: // Cleave
                {
                    int32 rollchance = urand(0, 10000);

                    if (rollchance > triggerAmount)
                        return false;

                    if (!damage)
                        return false;
                        
                    triggered_spell_id = 146137;
                    basepoints0 = damage;
                        
                    if (!procSpell)
                        break;

                    if (procSpell->GetSchoolMask() == SPELL_SCHOOL_MASK_NORMAL)
                        break;
                        
                    if (Player* _player = ToPlayer())
                    {
                        uint32 spec = _player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID);

                        switch (_player->getClass())
                        {
                            case CLASS_MAGE:
                            {
                                if (spec == SPEC_MAGE_ARCANE)
                                    triggered_spell_id = 146166;
                                else
                                    triggered_spell_id = 146160;
                                break;
                            }
                            case CLASS_DRUID:
                            {
                                if (spec == SPEC_DRUID_BALANCE || spec == SPEC_DRUID_RESTORATION)
                                    triggered_spell_id = 146158;
                                break;
                            }
                            case CLASS_PRIEST:
                            {
                                if (spec == SPEC_PRIEST_SHADOW)
                                    triggered_spell_id = 146159;
                                else
                                    triggered_spell_id = 146157;
                                break;
                            }
                            case CLASS_PALADIN:
                            {
                                triggered_spell_id = 146157;
                                break;
                            }
                            case CLASS_WARLOCK:
                            case CLASS_DEATH_KNIGHT:
                            {
                                triggered_spell_id = 146159;
                                break;
                            }
                            case CLASS_MONK:
                            {
                                triggered_spell_id = 146172;
                                break;
                            }
                            case CLASS_SHAMAN:
                            {
                                triggered_spell_id = 146171;
                                break;
                            }
                            case CLASS_HUNTER:
                            {
                                triggered_spell_id =  146162;
                                break;
                            }
                            default:
                                break;
                        }
                    }
                    break;
                }
                case 146059: // Multistrike
                {
                    int32 rollchance = urand(0, 1000);

                    if (rollchance > triggerAmount)
                        return false;

                    if (!damage)
                        return false;
                        
                    triggered_spell_id = 146061;
                    basepoints0 = damage / 3;
                        
                    if (!procSpell)
                        break;

                    if (procSpell->GetSchoolMask() == SPELL_SCHOOL_MASK_NORMAL)
                        break;
                        
                    if (Player* _player = ToPlayer())
                    {
                        uint32 spec = _player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID);

                        switch (_player->getClass())
                        {
                            case CLASS_MAGE:
                            {
                                if (spec == SPEC_MAGE_ARCANE)
                                    triggered_spell_id = 146070;
                                else
                                    triggered_spell_id = 146067;
                                break;
                            }
                            case CLASS_DRUID:
                            {
                                if (spec == SPEC_DRUID_BALANCE || spec == SPEC_DRUID_RESTORATION)
                                    triggered_spell_id = 146064;
                                break;
                            }
                            case CLASS_PRIEST:
                            {
                                if (spec == SPEC_PRIEST_SHADOW)
                                    triggered_spell_id = 146065;
                                else
                                    triggered_spell_id = 146063;
                                break;
                            }
                            case CLASS_PALADIN:
                            {
                                triggered_spell_id = 146063;
                                break;
                            }
                            case CLASS_WARLOCK:
                            case CLASS_DEATH_KNIGHT:
                            {
                                triggered_spell_id = 146065;
                                break;
                            }
                            case CLASS_MONK:
                            {
                                triggered_spell_id = 146075;
                                break;
                            }
                            case CLASS_SHAMAN:
                            {
                                triggered_spell_id = 146071;
                                break;
                            }
                            case CLASS_HUNTER:
                            {
                                triggered_spell_id =  146069;
                                break;
                            }
                            default:
                                break;
                        }
                    }
                    break;
                }
                case 148233: // Cleave Heal
                {
                    int32 rollchance = urand(0, 10000);

                    if (rollchance > triggerAmount)
                        return false;
                        
                    triggered_spell_id = 148234;
                        
                    if (!procSpell)
                        break;
                    
                    if (procSpell->GetSchoolMask() & SPELL_SCHOOL_MASK_NATURE)
                        triggered_spell_id = 148235;

                    basepoints0 = damage;
                    break;
                }
                case 146176: // Multistrike Heal
                {
                    int32 rollchance = urand(0, 1000);

                    if (rollchance > triggerAmount)
                        return false;
                        
                    triggered_spell_id = 146177;
                        
                    if (!procSpell)
                        break;
                    
                    if (procSpell->GetSchoolMask() & SPELL_SCHOOL_MASK_NATURE)
                        triggered_spell_id = 146178;

                    basepoints0 = damage / 3;
                    break;
                }
                case 104561: // Windsong
                {
                    triggered_spell_id = RAND(104423, 104510, 104509);
                    break;
                }
                case 104441: // River's Song
                {
                    triggered_spell_id = 116660;
                    break;
                }
                case 120033: // Jade Spirit (Passive)
                case 142536: // Spirit of Conquest (Passive)
                {
                    triggered_spell_id = 104993;
                    break;
                }
                case 134732: // Battle Fatigue
                {
                    if (Player* plr = ToPlayer())
                        if ((plr->InArena() || plr->InRBG()) && plr->HasPvpRulesEnabled())
                            return false;

                    if (Unit* owner = victim->GetOwner())
                        victim = owner;

                    if (victim->IsFriendlyTo(this) || victim->IsInPartyWith(this) || !victim->IsPlayer() || GetMap()->IsDungeon())
                        return false;

                    break;
                }
                // Concentration, Majordomo Staghelm
                case 98229:
                    SetPower(POWER_ALTERNATE, 0);
                    break;
                case 97138: // Matrix Restabilizer (391)
                case 96976: // Matrix Restabilizer (384)
                {

                    uint32 crit = this->ToPlayer()->GetUInt32Value(PLAYER_FIELD_COMBAT_RATINGS + CR_CRIT_MELEE);
                    uint32 mastery = this->ToPlayer()->GetUInt32Value(PLAYER_FIELD_COMBAT_RATINGS + CR_MASTERY);
                    uint32 haste = this->ToPlayer()->GetUInt32Value(PLAYER_FIELD_COMBAT_RATINGS + CR_HASTE_MELEE);
                    
                    bool isheroic = (dummySpell->Id == 97138);
                    if (crit > mastery && crit > haste)
                        triggered_spell_id = isheroic ? 97140: 96978;
                    else if (haste > mastery && haste > crit)
                        triggered_spell_id = isheroic ? 97139: 96977;
                    else if (mastery > haste && mastery > crit)
                        triggered_spell_id = isheroic ? 97141: 96979;
                    break;
                }
                // Item - Dragon Soul - Proc - Agi Melee 1H
                case 109866:
                case 109873:
                case 107786:
                {
                    switch (dummySpell->Id)
                    {
                        case 109866:    // LFR
                            triggered_spell_id = RAND(109867, 109869, 109871);
                            break;
                        case 109873:    // Heroic
                            triggered_spell_id = RAND(109868, 109870, 109872);
                            break;
                        case 107786:
                            triggered_spell_id = RAND(107785, 107789, 107787);
                            break;
                        default:
                            break;
                    }
                    break;
                }
                case 99262:
                {
                    if(target->HasAura(99252) && !HasAura(99263))
                    {
                        float newBp = 10.f;
                        newBp += triggerAura->GetStackAmount() * 5;
                        CastCustomSpell(this, 99263, &newBp, nullptr, nullptr, true, nullptr, nullptr, GetGUID());
                    }
                    return false;
                }
                // Sweeping Strikes
                case 18765:
                case 35429:
                {
                    target = SelectNearbyTarget(victim);
                    if (!target)
                        return false;

                    triggered_spell_id = 26654;
                    break;
                }
                // Obsidian Armor (Justice Bearer`s Pauldrons shoulder)
                case 27539:
                {
                    if (!procSpell)
                        return false;

                    switch (GetFirstSchoolInMask(procSpell->GetSchoolMask()))
                    {
                        case SPELL_SCHOOL_NORMAL:
                            return false;                   // ignore
                        case SPELL_SCHOOL_HOLY:   triggered_spell_id = 27536; break;
                        case SPELL_SCHOOL_FIRE:   triggered_spell_id = 27533; break;
                        case SPELL_SCHOOL_NATURE: triggered_spell_id = 27538; break;
                        case SPELL_SCHOOL_FROST:  triggered_spell_id = 27534; break;
                        case SPELL_SCHOOL_SHADOW: triggered_spell_id = 27535; break;
                        case SPELL_SCHOOL_ARCANE: triggered_spell_id = 27540; break;
                        default:
                            return false;
                    }

                    target = this;
                    break;
                }
                // Mark of Malice
                case 33493:
                {
                    // Cast finish spell at last charge
                    if (triggerAura->GetCharges() > 1)
                        return false;

                    target = this;
                    triggered_spell_id = 33494;
                    break;
                }
                // Twisted Reflection (boss spell)
                case 21063:
                    triggered_spell_id = 21064;
                    break;
                // Vampiric Aura (boss spell)
                case 38196:
                {
                    basepoints0 = 3 * damage;               // 300%
                    if (basepoints0 < 0)
                        return false;

                    triggered_spell_id = 31285;
                    target = this;
                    break;
                }
                // Aura of Madness (Darkmoon Card: Madness trinket)
                //=====================================================
                // 39511 Sociopath: +35 strength (Paladin, Rogue, Druid, Warrior)
                // 40997 Delusional: +70 attack power (Rogue, Hunter, Paladin, Warrior, Druid)
                // 40998 Kleptomania: +35 agility (Warrior, Rogue, Paladin, Hunter, Druid)
                // 40999 Megalomania: +41 damage/healing (Druid, Shaman, Priest, Warlock, Mage, Paladin)
                // 41002 Paranoia: +35 spell/melee/ranged crit strike rating (All classes)
                // 41005 Manic: +35 haste (spell, melee and ranged) (All classes)
                // 41009 Narcissism: +35 intellect (Druid, Shaman, Priest, Warlock, Mage, Paladin, Hunter)
                // 41011 Martyr Complex: +35 stamina (All classes)
                // 41406 Dementia: Every 5 seconds either gives you +5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin)
                // 41409 Dementia: Every 5 seconds either gives you -5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin)
                case 39446:
                {
                    if (!IsPlayer() || !isAlive())
                        return false;

                    // Select class defined buff
                    switch (getClass())
                    {
                        case CLASS_PALADIN:                 // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409
                        case CLASS_DRUID:                   // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409
                            triggered_spell_id = RAND(39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409);
                            cooldown_spell_id = 39511;
                            break;
                        case CLASS_ROGUE:                   // 39511, 40997, 40998, 41002, 41005, 41011
                        case CLASS_WARRIOR:                 // 39511, 40997, 40998, 41002, 41005, 41011
                        case CLASS_DEATH_KNIGHT:
                            triggered_spell_id = RAND(39511, 40997, 40998, 41002, 41005, 41011);
                            cooldown_spell_id = 39511;
                            break;
                        case CLASS_PRIEST:                  // 40999, 41002, 41005, 41009, 41011, 41406, 41409
                        case CLASS_SHAMAN:                  // 40999, 41002, 41005, 41009, 41011, 41406, 41409
                        case CLASS_MAGE:                    // 40999, 41002, 41005, 41009, 41011, 41406, 41409
                        case CLASS_WARLOCK:                 // 40999, 41002, 41005, 41009, 41011, 41406, 41409
                            triggered_spell_id = RAND(40999, 41002, 41005, 41009, 41011, 41406, 41409);
                            cooldown_spell_id = 40999;
                            break;
                        case CLASS_HUNTER:                  // 40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409
                            triggered_spell_id = RAND(40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409);
                            cooldown_spell_id = 40997;
                            break;
                        default:
                            return false;
                    }

                    target = this;
                    if (roll_chance_i(10))
                        ToPlayer()->Say("This is Madness!", LANG_UNIVERSAL); // TODO: It should be moved to database, shouldn't it?
                    break;
                }
                // Sunwell Exalted Caster Neck (??? neck)
                // cast ??? Light's Wrath if Exalted by Aldor
                // cast ??? Arcane Bolt if Exalted by Scryers
                case 46569:
                    return false;                           // old unused version
                // Sunwell Exalted Caster Neck (Shattered Sun Pendant of Acumen neck)
                // cast 45479 Light's Wrath if Exalted by Aldor
                // cast 45429 Arcane Bolt if Exalted by Scryers
                case 45481:
                {
                    if (!IsPlayer())
                        return false;

                    // Get Aldor reputation rank
                    if (ToPlayer()->GetReputationRank(932) == REP_EXALTED)
                    {
                        target = this;
                        triggered_spell_id = 45479;
                        break;
                    }
                    // Get Scryers reputation rank
                    if (ToPlayer()->GetReputationRank(934) == REP_EXALTED)
                    {
                        // triggered at positive/self casts also, current attack target used then
                        if (target && IsFriendlyTo(target))
                        {
                            target = getVictim();
                            if (!target)
                            {
                                ObjectGuid selected_guid = ToPlayer()->GetSelection();
                                target = ObjectAccessor::GetUnit(*this, selected_guid);
                                if (!target)
                                    return false;
                            }
                            if (IsFriendlyTo(target))
                                return false;
                        }

                        triggered_spell_id = 45429;
                        break;
                    }
                    return false;
                }
                // Sunwell Exalted Melee Neck (Shattered Sun Pendant of Might neck)
                // cast 45480 Light's Strength if Exalted by Aldor
                // cast 45428 Arcane Strike if Exalted by Scryers
                case 45482:
                {
                    if (!IsPlayer())
                        return false;

                    // Get Aldor reputation rank
                    if (ToPlayer()->GetReputationRank(932) == REP_EXALTED)
                    {
                        target = this;
                        triggered_spell_id = 45480;
                        break;
                    }
                    // Get Scryers reputation rank
                    if (ToPlayer()->GetReputationRank(934) == REP_EXALTED)
                    {
                        triggered_spell_id = 45428;
                        break;
                    }
                    return false;
                }
                // Sunwell Exalted Tank Neck (Shattered Sun Pendant of Resolve neck)
                // cast 45431 Arcane Insight if Exalted by Aldor
                // cast 45432 Light's Ward if Exalted by Scryers
                case 45483:
                {
                    if (!IsPlayer())
                        return false;

                    // Get Aldor reputation rank
                    if (ToPlayer()->GetReputationRank(932) == REP_EXALTED)
                    {
                        target = this;
                        triggered_spell_id = 45432;
                        break;
                    }
                    // Get Scryers reputation rank
                    if (ToPlayer()->GetReputationRank(934) == REP_EXALTED)
                    {
                        target = this;
                        triggered_spell_id = 45431;
                        break;
                    }
                    return false;
                }
                // Sunwell Exalted Healer Neck (Shattered Sun Pendant of Restoration neck)
                // cast 45478 Light's Salvation if Exalted by Aldor
                // cast 45430 Arcane Surge if Exalted by Scryers
                case 45484:
                {
                    if (!IsPlayer())
                        return false;

                    // Get Aldor reputation rank
                    if (ToPlayer()->GetReputationRank(932) == REP_EXALTED)
                    {
                        target = this;
                        triggered_spell_id = 45478;
                        break;
                    }
                    // Get Scryers reputation rank
                    if (ToPlayer()->GetReputationRank(934) == REP_EXALTED)
                    {
                        triggered_spell_id = 45430;
                        break;
                    }
                    return false;
                }
                case 48504: // Living Seed
                {
                    if (Unit* caster = triggerAura->GetCaster())
                    {
                        basepoints0 = triggerAmount;
                        CastCustomSpell(this, 48503, &basepoints0, NULL, NULL, true, nullptr, triggeredByAura, caster->GetGUID());
                        return true;
                    }
                    CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints1, &basepoints2, true, castItem, triggeredByAura, originalCaster);
                    return false;
                }
                // Purified Shard of the Scale - Onyxia 10 Caster Trinket
                case 69755:
                {
                    triggered_spell_id = (procFlag & PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS) ? 69733 : 69729;
                    break;
                }
                // Shiny Shard of the Scale - Onyxia 25 Caster Trinket
                case 69739:
                {
                    triggered_spell_id = (procFlag & PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS) ? 69734 : 69730;
                    break;
                }
                case 71519: // Deathbringer's Will Normal
                {
                    if (!IsPlayer())
                        return false;

                    std::vector<uint32> RandomSpells;
                    switch (getClass())
                    {
                        case CLASS_WARRIOR:
                        case CLASS_PALADIN:
                        case CLASS_DEATH_KNIGHT:
                            RandomSpells.push_back(71484);
                            RandomSpells.push_back(71491);
                            RandomSpells.push_back(71492);
                            break;
                        case CLASS_SHAMAN:
                        case CLASS_ROGUE:
                            RandomSpells.push_back(71486);
                            RandomSpells.push_back(71485);
                            RandomSpells.push_back(71492);
                            break;
                        case CLASS_DRUID:
                            RandomSpells.push_back(71484);
                            RandomSpells.push_back(71485);
                            RandomSpells.push_back(71492);
                            break;
                        case CLASS_HUNTER:
                            RandomSpells.push_back(71486);
                            RandomSpells.push_back(71491);
                            RandomSpells.push_back(71485);
                            break;
                        default:
                            return false;
                    }
                    if (RandomSpells.empty()) // shouldn't happen
                        return false;

                    uint8 rand_spell = irand(0, (RandomSpells.size() - 1));
                    CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster);
                    for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr)
                    {
                        if (!HasSpellCooldown(*itr))
                            AddSpellCooldown(*itr, 0, getPreciseTime() + cooldown);
                    }
                    break;
                }
                case 71562: // Deathbringer's Will Heroic
                {
                    if (!IsPlayer())
                        return false;

                    std::vector<uint32> RandomSpells;
                    switch (getClass())
                    {
                        case CLASS_WARRIOR:
                        case CLASS_PALADIN:
                        case CLASS_DEATH_KNIGHT:
                            RandomSpells.push_back(71561);
                            RandomSpells.push_back(71559);
                            RandomSpells.push_back(71560);
                            break;
                        case CLASS_SHAMAN:
                        case CLASS_ROGUE:
                            RandomSpells.push_back(71558);
                            RandomSpells.push_back(71556);
                            RandomSpells.push_back(71560);
                            break;
                        case CLASS_DRUID:
                            RandomSpells.push_back(71561);
                            RandomSpells.push_back(71556);
                            RandomSpells.push_back(71560);
                            break;
                        case CLASS_HUNTER:
                            RandomSpells.push_back(71558);
                            RandomSpells.push_back(71559);
                            RandomSpells.push_back(71556);
                            break;
                        default:
                            return false;
                    }
                    if (RandomSpells.empty()) // shouldn't happen
                        return false;

                    uint8 rand_spell = irand(0, (RandomSpells.size() - 1));
                    CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster);
                    for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr)
                    {
                        if (!HasSpellCooldown(*itr))
                            AddSpellCooldown(*itr, 0, getPreciseTime() + cooldown);
                    }
                    break;
                }
                case 71875: // Item - Black Bruise: Necrotic Touch Proc
                case 71877:
                {
                    basepoints0 = CalculatePct(int32(damage), triggerAmount);
                    triggered_spell_id = 71879;
                    break;
                }
                // Item - Shadowmourne Legendary
                case 71903:
                {
                    if (!victim || !victim->isAlive() || HasAura(73422))  // cant collect shards while under effect of Chaos Bane buff
                        return false;

                    CastSpell(this, 71905, true, nullptr, triggeredByAura);

                    // this can't be handled in AuraScript because we need to know victim
                    Aura const* dummy = GetAura(71905);
                    if (!dummy || dummy->GetStackAmount() < 10)
                        return false;

                    RemoveAurasDueToSpell(71905);
                    triggered_spell_id = 71904;
                    target = victim;
                    break;
                }
                // Shadow's Fate (Shadowmourne questline)
                case 71169:
                {
                    target = triggeredByAura->GetCaster();
                    if (!target)
                        return false;
                    Player* player = target->ToPlayer();
                    if (!player)
                        return false;
                    // not checking Infusion auras because its in targetAuraSpell of credit spell
                    if (player->GetQuestStatus(24749) == QUEST_STATUS_INCOMPLETE)       // Unholy Infusion
                    {
                        if (GetEntry() != 36678)                                        // Professor Putricide
                            return false;
                        CastSpell(target, 71518, true);                                 // Quest Credit
                        return true;
                    }
                    if (player->GetQuestStatus(24756) == QUEST_STATUS_INCOMPLETE)  // Blood Infusion
                    {
                        if (GetEntry() != 37955)                                        // Blood-Queen Lana'thel
                            return false;
                        CastSpell(target, 72934, true);                                 // Quest Credit
                        return true;
                    }
                    if (player->GetQuestStatus(24757) == QUEST_STATUS_INCOMPLETE)  // Frost Infusion
                    {
                        if (GetEntry() != 36853)                                        // Sindragosa
                            return false;
                        CastSpell(target, 72289, true);                                 // Quest Credit
                        return true;
                    }
                    if (player->GetQuestStatus(24547) == QUEST_STATUS_INCOMPLETE)  // A Feast of Souls
                        triggered_spell_id = 71203;
                    break;
                }
                // Essence of the Blood Queen
                case 70871:
                {
                    basepoints0 = CalculatePct(int32(damage), triggerAmount);
                    CastCustomSpell(70872, SPELLVALUE_BASE_POINT0, basepoints0, this);
                    return true;
                }
                case 65032: // Boom aura (321 Boombot)
                {
                    if (victim->GetEntry() != 33343)   // Scrapbot
                        return false;

                    InstanceScript* instance = GetInstanceScript();
                    if (!instance)
                        return false;

                    instance->DoCastSpellOnPlayers(65037);  // Achievement criteria marker
                    break;
                }
                // Dark Hunger (The Lich King encounter)
                case 69383:
                {
                    basepoints0 = CalculatePct(int32(damage), 50);
                    triggered_spell_id = 69384;
                    break;
                }
                case 47020: // Enter vehicle XT-002 (Scrapbot)
                {
                    if (!IsCreature())
                        return false;

                    Unit* vehicleBase = GetVehicleBase();
                    if (!vehicleBase)
                        return false;

                    // Todo: Check if this amount is blizzlike
                    vehicleBase->ModifyHealth(int32(vehicleBase->CountPctFromMaxHealth(1)));
                    break;
                }
            }
            break;
        }
        case SPELLFAMILY_MAGE:
        {
            // Incanter's Regalia set (add trigger chance to Mana Shield)
            if (dummySpell->ClassOptions.SpellClassMask[0] & 0x8000)
            {
                if (!IsPlayer())
                    return false;

                target = this;
                triggered_spell_id = 37436;
                break;
            }
            switch (dummySpell->Id)
            {
                case 190447: // Brain Freeze
                {
                    if (HasAura(190446))
                    {
                        CastSpellDelay(this, 190446, true, 300);
                    }
                    else
                    {
                        CastSpell(this, 190446, true);
                    }
                    return true;
                }
                case 210824: // Touch of the Magi
                {
                    Unit* caster = triggeredByAura->GetCaster();
                    if (!caster || victim != caster)
                        return false;

                    AuraEffect* aurEff = caster->GetAuraEffect(210725, EFFECT_0); // Touch of the Magi
                    if (!aurEff)
                        return false;

                    triggeredByAura->SetAmount(triggerAmount + CalculatePct(damage, aurEff->GetAmount()));
                    return false;
                }
                case 235365: // Blazing Soul
                {
                    if (effIndex != EFFECT_0 || !victim || GetDistance(victim) > dummySpell->Effects[EFFECT_1]->BasePoints)
                        return false;

                    basepoints0 = CalculatePct(int32(damage), triggerAmount);
                    if (AuraEffect* aurEff = GetAuraEffect(235313, EFFECT_0))
                    {
                        int32 amountSet = basepoints0 + aurEff->GetAmount();
                        if (amountSet > aurEff->GetCalcAmount())
                            amountSet = aurEff->GetCalcAmount();
                        aurEff->ChangeAmount(amountSet);
                    }

                    return false;
                }
                // Mastery: Ignite
                case 12846:
                {
                    if (!procSpell)
                        return false;

                    if (effIndex != EFFECT_0)
                        return false;

                    float percent = triggerAmount;
                    if ((procSpell->Id == 11366 || procSpell->Id == 2120) && !dmgInfoProc->GetCastTime())
                        percent *= 2;

                    basepoints0 = CalculatePct(int32(damage), percent) / 9;
                    triggered_spell_id = 12654;
                    break;
                }
                case 44448: // Pyroblast Clearcasting Driver
                {
                    if (!procSpell)
                        return false;

                    if (HasAura(48108))
                        return false;

                    if (!damage && !(procEx & PROC_EX_ABSORB))
                        return false;

                    if (procEx & PROC_EX_INTERNAL_DOT)
                        return false;

                    /// Prevent proc on victim fire mage
                    if (procFlag & PROC_FLAG_TAKEN_DAMAGE)
                        return false;

                    if (procSpell->IsAffectingArea() || dmgInfoProc->HasTargetInfoMask(TARGET_INFO_IS_JUMP_TARGET))
                        return false;

                    if (procEx & PROC_EX_CRITICAL_HIT)
                    {
                        bool RemoveHeatingUp = HasAura(48107) ? true: false;
                        CastSpell(this, RemoveHeatingUp ? 48108: 48107, true);

                        if (RemoveHeatingUp)
                            RemoveAura(48107);
                    }
                    else
                    {
                        if (Aura* HeatingUp = GetAura(48107, GetGUID()))
                            HeatingUp->SetDuration(200);
                    }
                    return false;
                }
                // Blessing of Ancient Kings (Val'anyr, Hammer of Ancient Kings)
                case 64411:
                {
                    if (!victim)
                        return false;
                    basepoints0 = int32(CalculatePct(damage, 15));
                    if (AuraEffect* aurEff = victim->GetAuraEffect(64413, 0, GetGUID()))
                    {
                        // The shield can grow to a maximum size of 20, 000 damage absorbtion
                        aurEff->SetAmount(std::min<int32>(aurEff->GetAmount() + basepoints0, 20000));

                        // Refresh and return to prevent replacing the aura
                        aurEff->GetBase()->RefreshDuration();
                        return true;
                    }
                    target = victim;
                    triggered_spell_id = 64413;
                    break;
                }
            }
            break;
        }
        case SPELLFAMILY_WARRIOR:
        {
            switch (dummySpell->Id)
            {
                case 200986: // Odyn's Champion
                {
                    if (Player* plr = ToPlayer())
                    {
                        if (!procSpell)
                            return false;

                        if (procSpell->Id != 50622 && procSpell->HasAttribute(SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2))
                            return false;

                        std::vector<uint32> spellId;

                        for (auto itr : plr->GetSpellCooldownMap())
                            spellId.push_back(itr.first);
                        
                        for (auto itr : spellId)
                        {
                            if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr))
                            {
                                if (spellInfo->Id == procSpell->Id)
                                    continue;

                                if (spellInfo->ClassOptions.SpellClassSet != SPELLFAMILY_WARRIOR)
                                    continue;

                                if (spellInfo->Cooldowns.RecoveryTime < 10000 && spellInfo->Cooldowns.CategoryRecoveryTime < 10000)
                                    continue;

                                plr->ModifySpellCooldown(itr, -1000);
                            }
                        }

                        std::vector<uint32> chargeSpellId;
                        SpellChargeDataMap* charge_list = plr->GetSpellChargeDatas();

                        for (SpellChargeDataMap::iterator itr = charge_list->begin(); itr != charge_list->end(); ++itr)
                        {
                            SpellChargeData& data = itr->second;

                            if (!data.chargeRegenTime || !data.spellInfo || data.chargeRegenTime < 10000)
                                continue;

                            if (data.spellInfo->ClassOptions.SpellClassSet != SPELLFAMILY_WARRIOR)
                                continue;

                            chargeSpellId.push_back(data.spellInfo->Id);
                        }

                        for (auto itr : chargeSpellId)
                            plr->ModSpellChargeCooldown(itr, 1000);
                    }
                    break;
                }
                case 215090: // Destiny Driver
                {
                    if (!dmgInfoProc->GetMagnet())
                        return false;

                    float bp0 = CalculatePct(damage, triggerAmount);

                    if (Unit* friends = GetUnit(*this, dmgInfoProc->GetMagnet()))
                        CastCustomSpell(friends, 215157, &bp0, nullptr, nullptr, true);

                    AddDelayedEvent(100, [this, bp0]() -> void
                    {
                        if (this)
                            CastCustomSpell(this, 215157, &bp0, nullptr, nullptr, true);
                    });
                    return false;
                }
                case 208908: // Mannoroth's Bloodletting Manacles
                {
                    if (effIndex != EFFECT_0)
                        return false;

                    if (procSpell->Power.PowerType == POWER_RAGE)
                    {
                        if (procSpell->Power.PowerCost > 0)
                        {
                            float ragemod = std::max(procSpell->Power.PowerCost, m_powerCost[POWER_RAGE]) / 100.f;
                            CastCustomSpell(this, 208909, &ragemod, nullptr, nullptr, true);
                        }
                    }
                    break;
                }
                case 242298: // Item - Warrior T20 Arms 2P Bonus
                {
                    if (effIndex != EFFECT_0)
                        return false;

                    if (Player* plr = ToPlayer())
                    {
                        plr->ModifySpellCooldown(227847, dummySpell->Effects[EFFECT_0]->BasePoints * -100);
                        plr->ModifySpellCooldown(152277, dummySpell->Effects[EFFECT_1]->BasePoints * -100);
                    }
                    break;
                }
                // Battle Trance (Honor Talent)
                case 213857:
                {
                    if (!procSpell || victim == this)
                        return false;

                    triggered_spell_id = 213858;
                    if (triggerAura->GetEffectTargets().empty())
                    {
                        triggerAura->AddEffectTarget(victim->GetGUID());
                        return false;
                    }
                    if (victim->GetGUID() != triggerAura->GetRndEffectTarget())
                    {
                        if (Aura* aura = GetAura(triggered_spell_id))
                            aura->Remove();
                        triggerAura->ClearEffectTarget();
                        return false;
                    }
                    triggerAura->ClearEffectTarget();
                    break;
                }
                // Victorious
                case 32216:
                {
                    RemoveAura(dummySpell->Id);
                    return false;
                }
            }
            break;
        }
        case SPELLFAMILY_WARLOCK:
        {
            switch (dummySpell->Id)
            {
                case 212580: // Eye of the Observer
                {
                    if (procSpell->Categories.DefenseType == SPELL_DAMAGE_CLASS_MELEE ||
                        procSpell->Categories.DefenseType == SPELL_DAMAGE_CLASS_RANGED)
                        return false;

                    if (!procSpell->Categories.StartRecoveryCategory)
                        return false;

                    if (procSpell->GetSchoolMask() & SPELL_SCHOOL_MASK_NORMAL)
                        return false;

                    if (Unit* _caster = triggeredByAura->GetCaster())
                    {
                        basepoints0 = CalculatePct(GetMaxHealth(), 5);

                        _caster->CastCustomSpell(this, 212529, &basepoints0, nullptr, nullptr, true);
                        return true;
                    }
                    
                    return false;
                }
                case 248113: // The Master Harvester
                {
                    if (effIndex != EFFECT_0)
                        return false;

                    if (procSpell->Power.PowerType == POWER_SOUL_SHARDS)
                    {
                        uint32 pwr = procSpell->Power.PowerCost / 10.f;
                        if (HasAura(137043)) // affliction
                        {
                            float chance = dummySpell->Effects[EFFECT_0]->BasePoints * pwr;
                            if (!roll_chance_f(chance))
                                return false;

                            if (Aura* aur = GetAura(196098))
                                aur->SetDuration(aur->GetDuration() + dummySpell->Effects[EFFECT_3]->BasePoints * IN_MILLISECONDS);
                            else
                                CastSpellDuration(this, 196098, true, dummySpell->Effects[EFFECT_3]->BasePoints * IN_MILLISECONDS);
                            break;
                        }
                        if (HasAura(137044)) // demonology
                        {
                            float chance2 = dummySpell->Effects[EFFECT_1]->BasePoints * pwr;
                            if (!roll_chance_f(chance2))
                                return false;

                            if (Aura* aur = GetAura(196098))
                                aur->SetDuration(aur->GetDuration() + dummySpell->Effects[EFFECT_3]->BasePoints * IN_MILLISECONDS);
                            else
                                CastSpellDuration(this, 196098, true, dummySpell->Effects[EFFECT_3]->BasePoints * IN_MILLISECONDS);
                            break;
                        }
                        if (HasAura(137046)) // destruction
                        {
                            float chance3 = dummySpell->Effects[EFFECT_2]->BasePoints * pwr;
                            if (!roll_chance_f(chance3))
                                return false;

                            if (Aura* aur = GetAura(196098))
                                aur->SetDuration(aur->GetDuration() + dummySpell->Effects[EFFECT_3]->BasePoints * IN_MILLISECONDS);
                            else
                                CastSpellDuration(this, 196098, true, dummySpell->Effects[EFFECT_3]->BasePoints * IN_MILLISECONDS);
                            break;
                        }
                    }
                    return false;
                }
                case 108370: // Soul Leech
                {
                    if (!damage || triggeredByAura->GetEffIndex() != EFFECT_0 || victim == this)
                        return false;

                    if (dummySpell->IsTargetingArea())
                        return false;

                    Unit* warlock = nullptr;
                    Unit* pet = nullptr;

                    if (Player* plr = ToPlayer())
                    {
                        warlock = this;

                        if (Unit* _pet = plr->GetPet())
                            pet = _pet;
                    }
                    else
                    {
                        pet = this;

                        if (Unit* owner = GetOwner())
                            warlock = owner;
                    }

                    if (!warlock)
                        return false;

                    triggered_spell_id = 108366;

                    uint8 capPct = 15;

                    if (AuraEffect* auraEff = warlock->GetAuraEffect(219272, EFFECT_1))
                    {
                        capPct = auraEff->GetAmount();
                    }
                    else if (AuraEffect* auraEff = warlock->GetAuraEffect(108370, EFFECT_1))
                        capPct = auraEff->GetAmount();

                    int32 hasabsorb = 0;
                    basepoints0 = CalculatePct(damage, triggerAmount);

                    if (Unit::AuraEffectList const* mAbsorbtionPercent = warlock->GetAuraEffectsByType(SPELL_AURA_MOD_ABSORB_AMOUNT))
                        for (Unit::AuraEffectList::const_iterator i = mAbsorbtionPercent->begin(); i != mAbsorbtionPercent->end(); ++i)
                            AddPct(basepoints0, (*i)->GetAmount());

                    if (pet)
                    {
                        if (AuraEffect* auraEff = pet->GetAuraEffect(triggered_spell_id, EFFECT_0))
                            hasabsorb = auraEff->GetAmount();

                        float maxAbsorb = pet->CountPctFromMaxHealth(capPct);
                        float bp0 = basepoints0 + hasabsorb;

                        if (bp0 > maxAbsorb)
                            bp0 = maxAbsorb;

                        pet->AddDelayedEvent(10, [basepoints0, triggered_spell_id, pet]() -> void
                        {
                            if (!pet)
                                return;
                            pet->CastCustomSpell(pet, triggered_spell_id, &basepoints0, nullptr, nullptr, true);
                        });
                        hasabsorb = 0;
                    }

                    if (AuraEffect* auraEff = warlock->GetAuraEffect(triggered_spell_id, EFFECT_0))
                        hasabsorb = auraEff->GetAmount();

                    float maxAbsorb = warlock->CountPctFromMaxHealth(capPct);
                    basepoints0 += hasabsorb;

                    if (basepoints0 > maxAbsorb)
                        basepoints0 = maxAbsorb;

                    warlock->AddDelayedEvent(10, [basepoints0, triggered_spell_id, warlock]() -> void
                    {
                        if (!warlock)
                            return;
                        warlock->CastCustomSpell(warlock, triggered_spell_id, &basepoints0, nullptr, nullptr, true);
                    });
                    return true;
                }
            }
            break;
        }
        case SPELLFAMILY_PRIEST:
        {
            switch (dummySpell->Id)
            {
                case 77485: // Mastery: Echo of Light
                {
                    if (effIndex != EFFECT_0)
                        return false;

                    if (Player* plr = ToPlayer())
                    {
                        float perc = plr->GetFloatValue(PLAYER_FIELD_MASTERY) * dummySpell->Effects[EFFECT_0]->BonusCoefficient / 2;
                        if (AuraEffect const* aurEff = GetAuraEffect(211638, EFFECT_0)) // Item - Priest T19 Holy 2P Bonus
                        {
                            if (procSpell && procSpell->Id == 34861)
                                AddPct(perc, aurEff->GetAmount());
                        }
                        basepoints0 = CalculatePct(dmgInfoProc->GetDamage(), perc);
                        CastCustomSpell(victim, 77489, &basepoints0, nullptr, nullptr, true);
                        return false;
                    }
                }
                case 214633: // Dejahna's Inspiration
                {
                    uint8 targetCount = 1;
                    ObjectGuid casterGUID = GetGUID();
                    ObjectGuid targetGUID = target->GetGUID();

                    for (auto itr : m_whoHasMyAuras)
                    {
                        if (itr.first == targetGUID)
                            continue;

                        if (TargetHasMyAura(itr.first, 194384))
                            targetCount++;
                    }

                    if (targetGUID != casterGUID && HasAura(194384, casterGUID))
                        targetCount++;

                    if (Aura* aura = GetAura(214637))
                    {
                        aura->SetStackAmount(targetCount);
                        aura->RefreshDuration();
                    }
                    else
                    {
                        AddAura(214637, this, nullptr, targetCount);
                    }
                    return false;
                }
                case 206950: // PH Holy Words Trigger Serendipity On Other Holy Words
                {
                    if (!IsPlayer())
                        return false;

                    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(2050);
                    if (!spellInfo)
                        return false;

                    if (effIndex == EFFECT_1)
                        if (ToPlayer()->HasChargesForSpell(spellInfo))
                            CastSpell(this, 211440, true, castItem, triggeredByAura, originalCaster);

                    if (effIndex == EFFECT_2)
                        if (!ToPlayer()->HasSpellCooldown(34861))
                            CastSpell(this, 211442, true, castItem, triggeredByAura, originalCaster);

                    if (effIndex == EFFECT_3)
                        if (!ToPlayer()->HasSpellCooldown(88625))
                            CastSpell(this, 211443, true, castItem, triggeredByAura, originalCaster);

                    return true;
                }
                case 242271: // Item - Priest T20 Holy 4P Bonus
                {
                    if (!procSpell)
                        return false;

                    if (Player* plr = ToPlayer())
                    {
                        int32 perc1 = CalculatePct(plr->GetChargesCooldown(2050), triggerAmount);
                        int32 perc2 = CalculatePct(plr->GetSpellCooldownDelay(34861), triggerAmount);
                        int32 perc3 = CalculatePct(plr->GetSpellCooldownDelay(88625), triggerAmount);

                        switch (procSpell->Id)
                        {
                            case 2050:  plr->ModSpellChargeCooldown(2050, perc1);                  break;
                            case 34861: plr->ModifySpellCooldown(34861, -perc2 * IN_MILLISECONDS); break;
                            case 88625: plr->ModifySpellCooldown(88625, -perc3 * IN_MILLISECONDS); break;
                        }
                        break;
                    }
                    return true;
                }
                // Sphere of Insanity
                case 194230:
                {
                    if (!damage)
                        return false;

                    int32 _perc = 0;
                    if (SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(194182))
                        _perc = triggerEntry->GetEffect(EFFECT_2)->CalcValue(this);

                    if (!_perc)
                        return false;

                    if (GuidList* summonList = GetSummonList(98680))
                        for (GuidList::const_iterator iter = summonList->begin(); iter != summonList->end(); ++iter)
                            if(Unit* summon = ObjectAccessor::GetUnit(*this, (*iter)))
                                if (AuraEffect* aurEff = summon->GetAuraEffect(194200, EFFECT_0))
                                    aurEff->SetAmount(aurEff->GetAmount() + CalculatePct(damage, _perc));

                    return false;
                }
                // Times and Measures
                case 238100:
                {
                    if (!damage)
                        return false;

                    if (roll_chance_f(damage * 33.f / GetMaxHealth()))
                        if (Player* player = ToPlayer())
                            player->RemoveSpellCooldown(19236, true);

                    return false;
                }
                // Atonement
                case 81749:
                {
                    if (!damage || victim->IsFriendlyTo(this))
                        return false;

                    std::list<Unit*> targetList;
                    std::vector<uint32> aura = {194384, 214206};
                    
                    TargetsWhoHasMyAuras(targetList, aura);

                    if (HasAura(194384, GetGUID()) || HasAura(214206, GetGUID()))
                        targetList.push_back(this);

                    if (targetList.empty())
                        return false;

                    // heal amount
                    float perc = triggerAmount;
                    if (AuraEffect* aurEff = GetAuraEffect(235966, EFFECT_1)) // Velen's Future Sight
                        AddPct(perc, aurEff->GetAmount());

                    if (AuraEffect* aurEff2 = GetAuraEffect(197862, EFFECT_0)) // Archangel (Honor Talent)
                        AddPct(perc, aurEff2->GetAmount());

                    if (AuraEffect* aurEff3 = GetAuraEffect(195488, EFFECT_0)) // Vim and Vigor (Honor Talent)
                        AddPct(perc, aurEff3->GetAmount());

                    float amount = CalculatePct(damage, perc);

                    for (auto target : targetList)
                        CastCustomSpell(target, 81751, &amount, nullptr, nullptr, true, castItem, triggeredByAura);
                    return true;
                }
                // Vampiric Embrace
                case 15286:
                {
                    if (!victim || !victim->isAlive() || procSpell->ClassOptions.SpellClassMask[1] & 0x80000)
                        return false;

                    // heal amount
                    float amount = CalculatePct(int32(damage), triggerAmount);
                    CastCustomSpell(this, 15290, &amount, &amount, nullptr, true, castItem, triggeredByAura);
                    return true;                                // no hidden cooldown
                }
            }
            break;
        }
        case SPELLFAMILY_DRUID:
        {
            switch (dummySpell->Id)
            {
                case 208199: // Manipulated Fel Energy
                {
                    if (procSpell->Power.PowerType == POWER_LUNAR_POWER)
                    {
                        if (Player* plr = ToPlayer())
                        {
                            float _mod = triggerAmount * 10;
                            float countMod = float(procSpell->Power.PowerCost) / _mod;
                            int32 modColdown = RoundingFloatValue(countMod * -1000);
                            plr->ModifySpellCooldown(194223, modColdown); // Celestial Alignment
                            plr->ModifySpellCooldown(102560, modColdown); // Incarnation: Chosen of Elune (Talent, Shapeshift)
                            return true;
                        }
                    }
                    return false;
                }
                case 242237: // Item - Druid T20 Guardian 4P Bonus
                {
                    if (procSpell->Power.PowerType == POWER_RAGE)
                    {
                        if (procSpell->Power.PowerCost > 0)
                        {
                            if (Player* plr = ToPlayer())
                            {
                                float countMod = float(procSpell->Power.PowerCost) / triggerAmount * 100.f;
                                plr->ModSpellChargeCooldown(22842, countMod);
                            }
                        }
                    }
                    break;
                }
                case 248081: // Behemoth Headdress
                {
                    if (!GetComboPoints())
                        return false;

                    if (Aura* aura = GetAura(5217)) // Tiger's Fury
                    {
                        int32 duration = aura->GetDuration();
                        duration += triggerAmount * 100 * GetComboPoints();
                        aura->SetDuration(duration);
                    }
                    break;
                }
                case 248163: // Radiant Moonlight
                {
                    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(248163))
                        return false;

                    AddDelayedEvent(250, [this]() -> void
                    {
                        if (this)
                        {
                            if (Player* plr = ToPlayer())
                            {
                                CastSpell(this, 202788, true);
                                plr->ModSpellCharge(202771, 1); 
                            }
                        }
                    });
                    AddSpellCooldown(248163, 0, getPreciseTime() + cooldown);
                    return true;
                }
                case 210702: // Ashamane's Bite
                {
                    if (Aura* aura = victim->GetAura(1079, GetGUID()))
                    {
                        int32 duration = aura->GetDuration();
                        CastSpellDuration(victim, 210705, true, duration);
                    }
                    break;
                }
                case 102351: // Cenarion Ward
                {
                    Unit* caster = triggeredByAura->GetCaster();
                    if (!caster)
                        return false;

                    caster->CastSpell(this, triggered_spell_id, true, castItem, triggeredByAura, originalCaster);
                    return true;
                }
                case 124974: // Nature's Vigil
                {
                    if (!procSpell)
                        return false;

                    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
                        if (procSpell->Effects[i]->Effect)
                            if (procSpell->Effects[i]->HasRadius())
                                return false;
                    return true;
                }
                case 48500: // Living Seed
                {
                    triggered_spell_id = 48504;
                    basepoints0 = CalculatePct(int32(damage), triggerAmount);
                    if (AuraEffect* aurEff = GetAuraEffect(48504, EFFECT_0))
                        basepoints0 += aurEff->GetAmount();
                    if (basepoints0 > victim->GetMaxHealth()) // Cap, not sure
                        basepoints0 = victim->GetMaxHealth();
                    break;
                }
            }
            break;
        }
        case SPELLFAMILY_ROGUE:
        {
            switch (dummySpell->Id)
            {
                case 212219: // Control is King	
                {
                    if (!procSpell->HasAura(SPELL_AURA_MOD_STUN) && !procSpell->HasAura(SPELL_AURA_MOD_SILENCE) && 
                        !procSpell->HasAura(SPELL_AURA_TRANSFORM) && !procSpell->HasAura(SPELL_AURA_STRANGULATE))
                        return false;

                    if (!triggerAura)
                        return false;

                    if (Unit* caster = triggerAura->GetCaster())
                    {
                        if (Aura* aura = caster->GetAura(13750)) // Adrenaline Rush
                        {
                            if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(212217))
                            {
                                int32 dur = aura->GetDuration() + spellInfo->Effects[EFFECT_1]->BasePoints * IN_MILLISECONDS;
                                aura->SetMaxDuration(dur);
                                aura->SetDuration(dur);
                                return true;
                            }
                        }
                    }
                    return false;
                }
                case 58423: // Relentless Strikes
                {
                    if (!GetComboPoints())
                        return false;

                    triggered_spell_id = 98440;
                    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
                    basepoints0 = triggerEntry->GetEffect(EFFECT_0)->CalcValue(this) * GetComboPoints();
                    break;
                }
                case 185314: // Deepening Shadows
                {
                    if (!GetComboPoints())
                        return false;
                        
                    int32 perc = triggerAmount * 100 * GetComboPoints();
                    if (Player* plr = ToPlayer())
                    {
                        plr->ModSpellChargeCooldown(185313, perc);
                    }
                    return true;
                }
                case 208895: // Duskwalker Footpads
                {
                    for (uint8 i = 0; i < MAX_POWERS_FOR_SPELL; ++i)
                    {
                        if (procSpell->spellPower[i] && procSpell->spellPower[i]->PowerType == POWER_ENERGY && procSpell->spellPower[i]->ManaCost > 0)
                        {
                            if (Player* plr = ToPlayer())
                            {
                                float countMod = procSpell->spellPower[i]->ManaCost / triggerAmount;
                                int32 modColdown = RoundingFloatValue(countMod * -1000);
                                plr->ModifySpellCooldown(79140, modColdown); // Vendetta
                            }
                            return true;
                        }
                    }
                    return false;
                }
                case 208892: // Denial of the Half-Giants
                {
                    if (!GetComboPoints())
                        return false;

                    if(Aura* aura = GetAura(121471)) // Shadow Blades
                    {
                        int32 duration = aura->GetDuration();
                        duration += triggerAmount * 100 * GetComboPoints();
                        aura->SetDuration(duration);
                    }
                    break;
                }
                case 208436: // Shadow Satyr's Walk
                {
                    if (effIndex != EFFECT_0 || !victim || victim == this)
                        return false;

                    basepoints0 = triggerAmount + (GetDistance(victim) * dummySpell->Effects[EFFECT_0]->BasePoints / dummySpell->Effects[EFFECT_1]->BasePoints);
                    if (basepoints0 <= 0)
                        return false;

                    triggered_spell_id = 208440;
                    CastSpell(this, 224914, true);
                    break;
                }
                case 206317: // Unfair Advantage (Honor Talent)
                {
                    if (!victim || victim == this || GetHealthPct() < victim->GetHealthPct())
                        return false;

                    triggered_spell_id = 209417;
                    break;
                }
                case 211671: // Item - Rogue T19 Assassination 2P Bonus
                {
                    basepoints0 = CalculatePct(dmgInfoProc->GetDamageBeforeHit(), triggerAmount) / 4;
                    triggered_spell_id = 211672;
                    break;
                }
                case 57934: // Tricks of the Trade
                {
                    Unit* redirectTarget = GetMisdirectionTarget();
                    RemoveAura(57934);
                    if (!redirectTarget)
                        break;
                    CastSpell(this,59628,true);
                    CastSpell(redirectTarget,57933,true);
                    break;
                }
                case 76806: // Main Gauche
                {
                    if (effIndex != EFFECT_0 || victim == this)
                        return false;
                    int32 chance = triggerAmount;
                    if (procSpell && (procSpell->Id == 193315 || procSpell->Id == 197834))
                        if (AuraEffect* aurEff = GetAuraEffect(211667, EFFECT_0)) // Item - Rogue T19 Outlaw 2P Bonus
                            chance += aurEff->GetAmount();

                    if (!roll_chance_i(chance))
                        return false;

                    triggered_spell_id = 86392;
                    break;
                }
            }
            break;
        }
        case SPELLFAMILY_HUNTER:
        {
            switch (dummySpell->Id)
            {
                case 211331: // Item - Hunter T19 Marksmanship 2P Bonus
                {
                    if (procSpell->Power.PowerType == POWER_FOCUS && procSpell->Power.PowerCost > 0)
                    {
                        float countMod = procSpell->Power.PowerCost / triggerAmount;

                        if (Spell* spell = m_currentSpells[CURRENT_GENERIC_SPELL])
                        {
                            if (spell->GetSpellInfo()->Id == 198670)
                                countMod = spell->GetPowerCost(POWER_FOCUS) / triggerAmount;
                        }
                        int32 modColdown = RoundingFloatValue(countMod * -1000.f);

                        if (Player* plr = ToPlayer())
                            plr->ModifySpellCooldown(193526, modColdown); // Trueshot
                    }
                    break;
                }
                case 118455: // Beast Cleave
                {
                    basepoints0 = CalculatePct(dmgInfoProc->GetDamageBeforeHit(), triggerAmount);
                    triggered_spell_id = 118459;
                    break;
                }
                case 238087: // Thunderslash
                {
                    Unit* owner = GetAnyOwner();
                    if (!owner || !owner->HasAura(193530)) // Aspect of the Wild
                        break;

                    CastSpell(victim, 243234, true);
                    break;
                }
                case 197354: // Surge of the Stormgod
                {
                    if (Player* player = ToPlayer())
                    {
                        float bp0 = GetTotalAttackPowerValue(RANGED_ATTACK) * 2;
                        if (Pet* pet = player->GetPet())
                            CastCustomSpell(pet, 197465, &bp0, nullptr, nullptr, true);

                        if (Unit* hati = GetHati())
                            CastCustomSpell(hati, 197465, &bp0, nullptr, nullptr, true);
                    }
                    break;
                }
                case 202797: // Viper Sting (Honor Talent)
                {
                    if (procSpell && procSpell->CalcCastTime() != 0 && triggerAura->GetDuration() > 100)
                        triggerAura->SetDuration(100);
                    break;
                }
                case 193533: // Steady Focus
                {
                    if (!procSpell)
                        return false;

                    if (procSpell->Id != 185358 && procSpell->Id != 2643)
                    {
                        triggerAura->SetCustomData(0);
                        return false;
                    }

                    if (triggerAura->GetCustomData() < 1)
                    {
                        triggerAura->ModCustomData(1);
                        return false;
                    }
                    triggerAura->SetCustomData(0);
                    triggered_spell_id = 193534;
                    break;
                }
                case 242242: // Item - Hunter T20 Marksmanship 2P Bonus
                {
                    if (!procSpell)
                        return false;

                    if (procSpell->Id != 19434)
                    {
                        triggerAura->SetCustomData(0);
                        return false;
                    }

                    if (triggerAura->GetCustomData() < 1)
                    {
                        triggerAura->ModCustomData(1);
                        return false;
                    }
                    triggerAura->SetCustomData(0);
                    triggered_spell_id = 242243;
                    break;
                }
                case 246126: // Item - Hunter T20 Beast Mastery 2P Bonus Driver
                {
                    basepoints0 = triggerAmount / 10.f;
                    if (AuraEffect* aurEff = GetAuraEffect(19574, EFFECT_0))
                    {
                        float setbp = basepoints0 + aurEff->GetAmount();
                        aurEff->ChangeAmount(setbp);

						if (Player* player = ToPlayer())
						{
							if (Pet* pet = player->GetPet())
								if (AuraEffect* petAurEff = pet->GetAuraEffect(207033, EFFECT_0))
								{
									float petSetbp = basepoints0 + petAurEff->GetAmount();
									petAurEff->ChangeAmount(petSetbp);
								}

							if (Unit* hati = GetHati())
								if (AuraEffect* petAurEff = hati->GetAuraEffect(207033, EFFECT_0))
								{
									float petSetbp = basepoints0 + petAurEff->GetAmount();
									petAurEff->ChangeAmount(petSetbp);
								}
						}
                    }
                    return false;
                }
            }
            break;
        }
        case SPELLFAMILY_PALADIN:
        {
            switch (dummySpell->Id)
            {
                case 219562: // Light of the Martyr
                {
                    basepoints0 = CalculatePct(dmgInfoProc->GetDamage(), triggerAmount);
                    CastCustomSpell(this, 196917, &basepoints0, nullptr, nullptr, true);
                    break;
                }
                case 234299: // Fist of Justice
                {
                    if (procSpell->Power.PowerType == POWER_HOLY_POWER)
                    {
                        if (Player* plr = ToPlayer())
                        {
                            if (uint8 cost = procSpell->Power.PowerCost)
                                plr->ModifySpellCooldown(853, -(triggerAmount * 100 * cost));
                        }
                    }
                    return true;
                }
                case 231895: // Sanctified Wrath
                {
                    if (effIndex != EFFECT_1)
                        return false;

                    if (procSpell->Power.PowerType == POWER_HOLY_POWER)
                    {
                        if (uint8 cost = procSpell->Power.PowerCost)
                            if (Aura* aura = GetAura(231895)) // Sanctified Wrath
                                aura->ModStackAmount(cost);
                    }
                    return true;
                }
                case 209389: // Bulwark of Order
                {
                    basepoints0 = CalculatePct(dmgInfoProc->GetDamage(), triggerAmount);
                    break;
                }
                case 199441: // Avenging Light
                {
                    if (victim->IsFullHealth())
                        return false;

                    basepoints0 = CalculatePct(dmgInfoProc->GetDamage(), triggerAmount);
                    triggered_spell_id = 199443;
                    break;
                }
                case 186788: // Echo of the Highlord
                {
                    triggered_spell_id = procSpell->Id == 85256 ? 186811 : 186825;
                    break;
                }
                case 210540: // Pure of Heart (Honor Talent)
                {
                    if (IsFullHealth() || procSpell->IsAffectingArea())
                        return false;

                    if (Unit* _caster = triggeredByAura->GetCaster())
                        if (_caster == victim)
                            _caster->CastSpell(this, 199427, true);
                    return true;
                }
                case 203538: // Greater Blessing of Kings
                case 203539: // Greater Blessing of Wisdom
                {
                    if (Unit* _caster = triggeredByAura->GetCaster())
                    {
                        if (!_caster->HasSpell(247675)) // Hammer of Reckoning (Honor Talent)
                            return false;
                       
                        if (Aura* aura = _caster->GetAura(247677))
                            if (aura->GetStackAmount() == 50)
                                _caster->CastSpell(this, 247676, true);

                        _caster->CastSpell(this, 247677, true);
                    }
                    break;
                }
                case 204914: // Divine Punisher (Honor Talent)
                {
                    if (!procSpell)
                        return false;

                    if (!IsPlayer())
                        return false;

                    if (ToPlayer()->HasSpellCooldown(204914))
                        return false;

                    if (triggerAura->GetEffectTargets().empty())
                    {
                        triggerAura->AddEffectTarget(victim->GetGUID());
                        return false;
                    }
                    if (victim->GetGUID() != triggerAura->GetRndEffectTarget())
                    {
                        triggerAura->ClearEffectTarget();
                        return false;
                    }
                    triggered_spell_id = 216762;
                    cooldown = 0.5;
                    cooldown_spell_id = 204914;
                    triggerAura->ClearEffectTarget();
                    break;
                }
                case 204934: // Law and Order (Honor Talent)
                {
                    if (procSpell->Id == 198137)
                    {
                        if (AuraEffect* auraEff = GetAuraEffect(198034, EFFECT_0))
                        {
                            if (auraEff->GetTickNumber() != 1)
                                return false;
                        }
                    }

                    if(Aura* aura = target->GetAura(183218))
                    {
                        if (aura->GetDuration() < dummySpell->Effects[EFFECT_1]->BasePoints * 1000)
                            aura->SetDuration(dummySpell->Effects[EFFECT_1]->BasePoints * 1000);
                    }
                    else
                        CastSpellDuration(target, 183218, true, dummySpell->Effects[EFFECT_1]->BasePoints * 1000);
                    return true;
                }
                case 203797: // Retribution Aura
                {
                    if (Unit* _caster = triggeredByAura->GetCaster())
                        _caster->CastSpell(target, 204011, true);
                    return true;
                }
                // Ancient Crusader (guardian)
                case 86703:
                {
                    if (!GetOwner() || !GetOwner()->IsPlayer())
                        return false;

                    GetOwner()->CastSpell(this, 86700, true);
                    return true;
                }
                // Light's Beacon - Beacon of Light
                case 53651:
                {
                    if (!damage || m_whoHasMyAuras.empty())
                        return false;

                    if (procSpell->Id == 183998 && !HasAura(234862))
                        return false;

                    int32 perc = HasMyAura(156910) ? triggerAmount * 0.8f : triggerAmount;
                    if (AuraEffect* aurEff = GetAuraEffect(251863, EFFECT_0)) // Item - Paladin T21 Holy 2P Bonus
                    {
                        if (procSpell && (procSpell->Id == 19750 || procSpell->Id == 82326))
                            perc += aurEff->GetAmount();
                    }

                    basepoints0 = CalculatePct(damage, perc);

                    std::list<Unit*> targetList;
                    std::vector<uint32> auraList = {53563, 156910, 200025};

                    TargetsWhoHasMyAuras(targetList, auraList);

                    for (auto j : auraList)
                    {
                        if (HasAura(j, GetGUID()))
                        {
                            targetList.push_back(this);
                            break;
                        }
                    }

                    targetList.remove(target);

                    for (auto beaconTarget : targetList)
                    {
                        if (beaconTarget->IsWithinLOSInMap(this))
                            CastCustomSpell(beaconTarget, 53652, &basepoints0, nullptr, nullptr, true);
                    }
                    return false;
                }
            }
            break;
        }
        case SPELLFAMILY_SHAMAN:
        {
            switch (dummySpell->Id)
            {
                case 170374: // Earthen Rage
                {
                    if (!target)
                        return false;

                    m_anyDataContainer.Set("EarthenRageTarget", target->GetGUID());
                    triggerAura->SetAuraAttribute(AURA_ATTR_IS_NOT_ACTIVE);
                    CastSpell(this, 170377, true);
                    return true;
                }
                case 195222: // Stormlash
                {
                    if (effIndex != EFFECT_0 || !damage || !IsPlayer())
                        return false;

                    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(195222))
                        return false;

                    if (triggerAura)
                    {
                        if (Unit* shaman = triggerAura->GetCaster())
                        {
                            float AP = shaman->GetTotalAttackPowerValue(BASE_ATTACK) * 2;
                            basepoints0 = dmgInfoProc->GetDamage() * 0.1f;
                            if (basepoints0 > AP)
                                basepoints0 = AP;

                            shaman->CastCustomSpell(target, 195256, &basepoints0, nullptr, nullptr, true, nullptr);
                            AddSpellCooldown(195222, 0, getPreciseTime() + cooldown);
                        }
                    }
                    return true;
                }
                case 211062: // Static Cling (Honor Talent)
                {
                    if (effIndex != EFFECT_0 || victim == this)
                        return false;

                    if (!procSpell || HasAura(211400))
                        return false;

                    if (procSpell->Id != 17364 && procSpell->Id != 115356)
                        return false;

                    if (triggerAura->GetEffectTargets().empty())
                    {
                        triggerAura->AddEffectTarget(victim->GetGUID());
                        return false;
                    }

                    if (victim->GetGUID() != triggerAura->GetRndEffectTarget())
                    {
                        triggerAura->ClearEffectTarget();
                        return false;
                    }

                    CastSpell(victim, 210927, true);
                    CastSpell(this, 211400, true);
                    triggerAura->ClearEffectTarget();
                    return true;
                }
                case 215864:  // Rainfall
                {
                    if (effIndex != EFFECT_2)
                        return false;

                    if(triggerAura)
                    {
                        if (AuraEffect* aurEff = triggerAura->GetEffect(EFFECT_3))
                        {
                            if (!aurEff->GetAmount())
                                return false;
                            aurEff->SetAmount(aurEff->GetAmount() - triggerAmount);
                        }
                        int32 _duration = int32(triggerAura->GetDuration() + int32(triggerAmount * 1000));
                        if(30000 < _duration)
                            _duration = 30000;

                        triggerAura->SetDuration(_duration, true);
                        if (_duration > triggerAura->GetMaxDuration())
                            triggerAura->SetMaxDuration(_duration);
                    }
                    break;
                }
                // Resurgence
                case 16196: // Resurgence
                {
                    if (!victim)
                        return false;

                    AuraEffect const* aurEff = GetAuraEffect(197467, EFFECT_0); // Bottomless Depths
                    if (!aurEff && !(procEx & PROC_EX_CRITICAL_HIT))
                        return false;

                    if (aurEff)
                        if (victim->GetHealthPct() > aurEff->GetAmount() && !(procEx & PROC_EX_CRITICAL_HIT))
                            return false;

                    triggered_spell_id = 101033;
                    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
                    float coeff = 1.0f;

                    basepoints0 = triggerEntry->GetEffect(EFFECT_0)->CalcValue(this);

                    switch (procSpell->Id)
                    {
                        case 8004: case 61295: case 73685:
                        {
                            coeff = 0.6f;
                            break;
                        }
                        case 1064:
                        {
                            coeff = 0.25f;

                            AuraEffect const* aurEff2 = GetAuraEffect(207356, EFFECT_0); // Refreshing Currents
                            if (aurEff2 && (procEx & PROC_EX_CRITICAL_HIT))
                                AddPct(coeff, aurEff2->GetAmount());
                            break;
                        }
                        default:
                            break;
                    }
                    basepoints0 *= coeff;

                    break;
                }
                // Windfury Weapon (Passive)
                case 33757:
                {
                    Player* player = ToPlayer();
                    if (!damage || !player || !victim || !victim->isAlive())
                        return false;

                    // Attack Twice
                    for (uint32 i = 0; i < 2; ++i)
                        CastSpell(victim, 25504, true);

                    return true;
                }
                case 209385:
                {
                    Player* player = ToPlayer();
                    if (!damage || !player || !victim || !victim->isAlive())
                        return false;

                    // Attack thrice
                    for (uint32 i = 0; i < 3; ++i)
                        CastSpell(victim, 232056, true);

                    return true;
                }
                case 248029: // Smoldering Heart
                {
                    if (effIndex != EFFECT_0)
                        return false;

                    if (procSpell->Power.PowerType == POWER_MAELSTROM)
                    {
                        if (uint32 pwr = std::max(procSpell->Power.PowerCost, m_powerCost[POWER_MAELSTROM]))
                        {
                            if (HasAura(137040)) // proc for Elemental spec
                            {
                                float chance = dummySpell->Effects[EFFECT_1]->BasePoints / 100.f * pwr;
                                if (!roll_chance_f(chance))
                                    return false;

                                if (Aura* aur = GetAura(114050))
                                    aur->SetDuration(aur->GetDuration() + dummySpell->Effects[EFFECT_0]->BasePoints);
                                else
                                    CastSpellDuration(this, 114050, true, dummySpell->Effects[EFFECT_0]->BasePoints);
                                break;
                            }
                            if (HasAura(137041)) // proc for Enchacement spec
                            {
                                float chance2 = dummySpell->Effects[EFFECT_2]->BasePoints / 100.f * pwr;
                                if (!roll_chance_f(chance2))
                                    return false;

                                if (Aura* aur2 = GetAura(114051))
                                    aur2->SetDuration(aur2->GetDuration() + dummySpell->Effects[EFFECT_0]->BasePoints);
                                else
                                    CastSpellDuration(this, 114051, true, dummySpell->Effects[EFFECT_0]->BasePoints);
                                break;
                            }
                        }
                    }
                    return false;
                }
                case 210707: // Aftershock
                {
                    basepoints0 = CalculatePct(GetPowerCost(procSpell->Power.PowerType), triggerAmount);
                    CastCustomSpell(this, 210712, &basepoints0, nullptr, nullptr, true);
                    break;
                }
                case 214131: // The Deceiver's Blood Pact
                {
                    basepoints0 = GetPowerCost(procSpell->Power.PowerType);
                    CastCustomSpell(this, 214134, &basepoints0, nullptr, nullptr, true);
                    break;
                }
            }
        }
        case SPELLFAMILY_DEATHKNIGHT:
        {
            switch (dummySpell->Id)
            {
                case 194912: // Gathering Storm
                {
                    if (procSpell->Id == 196770)
                        return false;

                    for (uint8 i = 0; i < MAX_POWERS_FOR_SPELL; ++i)
                    {
                        if (procSpell->spellPower[i] && procSpell->spellPower[i]->PowerType == POWER_RUNES && procSpell->spellPower[i]->ManaCost > 0)
                        {
                            if (Aura* aura = GetAura(196770))
                            {
                                uint8 count = procSpell->spellPower[i]->ManaCost;

                                for (uint8 i = 0; i < count; i++)
                                    CastSpell(this, 211805, true);

                                aura->SetDuration(aura->GetDuration() + count * 500);
                                return true;
                            }
                        }
                    }
                    return false;
                }
                case 242225: // Item - Death Knight T20 Unholy 4P Bonus
                {
                    if (Player* plr = ToPlayer())
                    {
                        for (uint8 i = 0; i < MAX_POWERS_FOR_SPELL; ++i)
                        {
                            if (procSpell->spellPower[i] && procSpell->spellPower[i]->PowerType == POWER_RUNES && procSpell->spellPower[i]->ManaCost > 0)
                            {
                                uint8 count = procSpell->spellPower[i]->ManaCost;
                                plr->ModifySpellCooldown(42650, dummySpell->Effects[EFFECT_0]->BasePoints * count * -100);
                                return true;
                            }
                        }
                    }
                    return false;
                }
                case 251875: // Item - Death Knight T21 Frost 4P Bonus
                {
                    Player* player = ToPlayer();
                    if (!damage || !player || !victim || !victim->isAlive())
                        return false;

                    for (uint32 i = 0; i < 3; ++i)
                        CastSpell(victim, 253590, true);

                    return true;
                }
                case 189097: // Over-Powered
                {
                    basepoints0 = triggerAmount;
                    break;
                }
                case 209228: // Shackles of Bryndaor
                {
                    if (effIndex != EFFECT_0)
                        return false;

                    if (!damage || damage < CountPctFromMaxHealth(dummySpell->Effects[EFFECT_1]->BasePoints))
                        return false;

                    basepoints0 = CalculatePct(450, triggerAmount);
                    if (!basepoints0)
                        return false;

                    triggered_spell_id = 209232;
                    break;
                }
                // Souldrinker
                case 238114:
                {
                    uint32 overHeal = dmgInfoProc->GetOverHeal();
                    if (!overHeal)
                        return false;

                    basepoints0 = overHeal * triggerAmount / GetMaxHealth();
                    if (AuraEffect* aurEff = GetAuraEffect(240558, EFFECT_0)) // Souldrinker
                        basepoints0 += aurEff->GetAmount();

                    if (basepoints0 > dummySpell->Effects[EFFECT_1]->BasePoints)
                        basepoints0 = dummySpell->Effects[EFFECT_1]->BasePoints;

                    if (!basepoints0)
                        return false;

                    triggered_spell_id = 240558;
                    break;
                }
                // Festering Wound
                case 194310:
                {
                    if (!damage)
                        return false;

                    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(194310))
                        return false;

                    if (Unit* caster = triggeredByAura->GetCaster())
                    {
                        if (caster->HasAura(199722))
                        {
                            caster->CastSpell(this, 199723, true);
                            AddSpellCooldown(194310, 0, getPreciseTime() + cooldown);
                        }
                    }

                    return false;
                }
                // Abomination's Might
                case 207161:
                {
                    if (!victim)
                        return false;
                    if (victim->ToCreature())
                        triggered_spell_id = 207165;
                    else
                        triggered_spell_id = 211831;
                    break;
                }
                // Dancing Rune Weapon
                case 49028:
                case 192566:
                {
                    // 1 dummy aura for dismiss rune blade
                    if (effIndex != EFFECT_1)
                        return false;

                    Unit* pPet = nullptr;
                    for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) // Find Rune Weapon
                    {
                        if(Unit* unit = ObjectAccessor::GetUnit(*this, *itr))
                        {
                            if (unit->GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL) == dummySpell->Id)
                            {
                                pPet = unit;
                                break;
                            }
                        }
                    }

                    if (pPet && pPet->getVictim() && damage)
                    {
                        uint32 procDmg = CalculatePct(damage, 35);
                        if (procSpell)
                        {
                            if (!sSpellMgr->IsTalent(procSpell->Id)) // Not sure tests are needed
                                pPet->CastSpell(pPet->getVictim(), procSpell->Id, true);
                        }
                        else
                            pPet->DealDamage(pPet->getVictim(), procDmg);
                        break;
                    }
                    return false;
                }
            }
            break;
        }
        case SPELLFAMILY_POTION:
        {
            // alchemist's stone
            if (dummySpell->Id == 17619)
            {
                if (procSpell->ClassOptions.SpellClassSet == SPELLFAMILY_POTION)
                {
                    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++)
                    {
                        if (procSpell->EffectMask < uint32(1 << i))
                            break;

                        if (procSpell->GetEffect(i, GetSpawnMode())->Effect == SPELL_EFFECT_HEAL)
                        {
                            triggered_spell_id = 21399;
                        }
                        else if (procSpell->GetEffect(i, GetSpawnMode())->Effect == SPELL_EFFECT_ENERGIZE)
                        {
                            triggered_spell_id = 21400;
                        }
                        else
                            continue;

                        basepoints0 = int32(CalculateSpellDamage(this, procSpell, i) * 0.4f);
                        CastCustomSpell(this, triggered_spell_id, &basepoints0, nullptr, nullptr, true, nullptr, triggeredByAura);
                    }
                    return true;
                }
            }
            break;
        }
        case SPELLFAMILY_MONK:
        {
            switch (dummySpell->Id)
            {
                case 202090: // Teachings of the Monastery
                {
                    if (triggerAura)
                    {
                        uint8 stack = triggerAura->GetStackAmount();

                        if (HasAura(210802)) // Spirit of the Crane
                        {
                            triggered_spell_id = 210803;
                            SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
                            basepoints0 = triggerEntry->GetEffect(EFFECT_0)->CalcValue(this) / 100.f;
                        }
                        CastCustomSpell(this, triggered_spell_id, &basepoints0, nullptr, nullptr, true, nullptr, triggeredByAura);
                        CastSpellDelay(target, procSpell->Id, true, 300);
                        triggerAura->ModStackAmount(-1);
                    }
                    return false;
                }
                case 196082: // Tornado Kicks
                {
                    CastSpellDelay(target, 185099, true, 400, nullptr, triggeredByAura, GetGUID());
                    return true;
                }
                case 209256: // Drinking Horn Cover
                {
                    if (!procSpell)
                        return true;

                    if (effIndex != EFFECT_0)
                        return false;

                    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(209256))
                        return false;

                    SpellPowerData powerData;
                    if (!procSpell->GetSpellPowerByCasterPower(this, powerData))
                        return false;

                    int32 chiCost = 0;
                    for (SpellPowerEntry const* power : powerData)
                        if (power->PowerType == POWER_CHI)
                            chiCost = power->ManaCost;

                    if (!chiCost)
                        return false;

                    basepoints0 = triggerAmount * 1000 / 10 * chiCost;
                    basepoints1 = triggerAmount * 75;
					if (Aura* aura = GetAura(137639)) // Storm, Earth, and Fire
					{
						aura->SetDuration(aura->GetDuration() + basepoints0);
						for (Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
							if (Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr))
								if (creature->GetEntry() == 69791 || creature->GetEntry() == 69792)
									if (TempSummon* summon = creature->ToTempSummon())
										summon->AddDuration(basepoints0);
					}

                    if (Aura* aura = GetAura(152173)) // Serenity
                        aura->SetDuration(aura->GetDuration() + basepoints1);

                    if (G3D::fuzzyGt(cooldown, 0.0))
                    {
                        if (IsPlayer())
                            ToPlayer()->ApplySpellMod(cooldown_spell_id, SPELLMOD_COOLDOWN, cooldown);
                        AddSpellCooldown(209256, 0, getPreciseTime() + cooldown);
                    }
                    return true;
                }
                case 195403: // Gale Burst
                {
                    if (uint32 dmg = dmgInfoProc->GetDamageBeforeHit())
                    {
                        if (Unit* caster = triggeredByAura->GetCaster())
                        {
                            if (caster == victim)
                            {
                                if (AuraEffect* aurEff = caster->GetAuraEffect(195399, EFFECT_0))
                                    if (AuraEffect* aurEff2 = GetAuraEffect(115080, EFFECT_0, caster->GetGUID()))
                                        aurEff2->SetAmount(CalculatePct(dmg, aurEff->GetAmount()) + aurEff2->GetAmount());
                            }
                        }
                    }
                    break;
                }
                case 122280: // Healing Elixir
                {
                    Player* player = ToPlayer();
                    if (!player)
                        return false;

                    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(122281);
                    if (!spellInfo)
                        return false;

                    if (GetHealth() - dmgInfoProc->GetDamage() >= CountPctFromMaxHealth(triggerAmount))
                        return false;

                    if (!player->HasChargesForSpell(spellInfo))
                        return false;

                    TriggerCastFlags castFlags = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD);
                    CastSpell(this, spellInfo->Id, castFlags, nullptr, triggeredByAura, GetGUID());
                    return true;
                }
                case 220357: // Cyclone Strikes
                {
                    if (!victim || victim == this)
                        return false;

                    if (Aura* aura = GetAura(116847)) // Rushing Jade Wind
                    {
                        int32 maxCount = aura->GetSpellInfo()->Effects[EFFECT_1]->BasePoints;
                        if (aura->GetCustomData() >= maxCount)
                            return false;

                        aura->ModCustomData(1);
                    }

                    if (Aura* aur = victim->GetAura(228287, GetGUID()))
                    {
                        if (Aura* aur2 = GetAura(220358))
                        {
                            aur->RefreshTimers();
                            aur2->RefreshTimers();
                        }
                    }
                    else
                        CastSpell(this, 220358, true);
                        CastSpell(victim, 228287, true);
                    return true;
                }
                case 238094: // Effusive Mists
                {
                    if (!isInCombat())
                        return false;

                    CastSpell(this, 214501, true);
                    break;
                }
                case 137639: // Storm, Earth, and Fire
                {
                    if (!procSpell)
                        return true;

                    for (uint8 i = 3; i < 5; ++i)
                    {
                        if (m_SummonSlot[i])
                        {
                            if (Creature* crt = GetMap()->GetCreature(m_SummonSlot[i]))
                                if (!crt->IsDespawn())
                                {
                                    if (Unit* cloneUnit = crt->ToUnit())
                                        if (cloneUnit->HasUnitState(UNIT_STATE_CASTING))
                                            continue;

                                    if (procSpell->Id != 113656 && procSpell->Id != 101546 && procSpell->Id != 116847)
                                        if (Unit* cloneTarget = crt->getVictim())
                                            if (target == cloneTarget)
                                                continue;

                                    if (Unit* cloneTarget = crt->getVictim())
                                        crt->CastSpell(cloneTarget, procSpell->Id, true);
                                }
                        }
                    }
                    return true;
                }
                case 124489: // Restless Pursuit
                {
                    RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED);
                    break;
                }
            }
            break;
        }
        case SPELLFAMILY_DEMON_HUNTER:
        {
            switch (dummySpell->Id)
            {
                case 201472: // Rage of the Illidari
                {
                    if (AreaTrigger* AT = GetAreaObject(201467))
                    {
                        if (AreaTriggerAI* ATAI = AT->AI())
                            ATAI->CallSpecialFunction(damage);
                    }
                    break;
                }
                case 209354: // Delusions of Grandeur
                {
                    if (effIndex != EFFECT_1)
                        return false;

                    if (procSpell && procSpell->Power.PowerType == POWER_FURY && procSpell->Power.PowerCost > 0)
                    {
                        if (Player* plr = ToPlayer())
                        {
                            float countMod = procSpell->Power.PowerCost / triggerAmount;
                            int32 modColdown = RoundingFloatValue(countMod * -1000);
                            plr->ModifySpellCooldown(191427, modColdown);
                            plr->ModifySpellCooldown(191428, modColdown);
                        }
                    }
                    return false;
                }
                case 205598: // Awaken the Demon Within (Honor Talent)
                {
                    if (GetHealthPct() <= dummySpell->Effects[EFFECT_1]->BasePoints) // 20% HP
                    {
                        if (Aura* aura = GetAura(162264))
                        {
                            if (aura->GetDuration() < dummySpell->Effects[EFFECT_2]->BasePoints * IN_MILLISECONDS)
                            {
                                aura->SetDuration(dummySpell->Effects[EFFECT_2]->BasePoints * IN_MILLISECONDS);
                                CastSpell(this, 207128, true);
                            }
                        }
                        break;
                    }
                    if (GetHealthPct() <= dummySpell->Effects[EFFECT_0]->BasePoints) // 50% HP
                    {
                        if (!HasAura(162264))
                        {
                            CastSpellDuration(this, 162264, true, dummySpell->Effects[EFFECT_2]->BasePoints * IN_MILLISECONDS);
                            CastSpell(this, 207128, true);
                        }
                        break;
                    }
                    break;
                }
                case 247456: // Frailty
                {
                    if (Unit* caster = triggeredByAura->GetCaster())
                        if (caster == victim)
                            if (AuraEffect* aurEff = caster->GetAuraEffect(247456, EFFECT_1))
                                aurEff->SetAmount(CalculatePct(damage, triggeredByAura->GetAmount()) + aurEff->GetAmount());
                    break;
                }
            }
            break;
        }
        default:
            break;
    }

    // processed charge only counting case
    if (!triggered_spell_id)
        return true;

    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
    if (!triggerEntry)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleDummyAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id);
        return false;
    }

    if (cooldown_spell_id == 0)
        cooldown_spell_id = triggered_spell_id;

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(cooldown_spell_id))
        return false;

    if (basepoints0 || basepoints1 || basepoints2)
        CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints1, &basepoints2, true, castItem, triggeredByAura, originalCaster);
    else
        CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura, originalCaster);

    if (G3D::fuzzyGt(cooldown, 0.0))
    {
        if (IsPlayer())
            ToPlayer()->ApplySpellMod(cooldown_spell_id, SPELLMOD_COOLDOWN, cooldown);
        AddSpellCooldown(cooldown_spell_id, 0, getPreciseTime() + cooldown);
    }

    return true;
}

bool Unit::HandleObsModEnergyAuraProc(Unit* victim, DamageInfo* /*dmgInfoProc*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, double cooldown)
{
    SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();
    //uint32 effIndex = triggeredByAura->GetEffIndex();
    //int32  triggerAmount = triggeredByAura->GetAmount();

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer() ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    uint32 triggered_spell_id = 0;
    Unit* target = victim;
    float basepoints0 = 0;

    /*
    switch (dummySpell->ClassOptions.SpellClassSet)
    {

    }
    */
    // processed charge only counting case
    if (!triggered_spell_id)
        return true;

    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);

    // Try handle unknown trigger spells
    if (!triggerEntry)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleObsModEnergyAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id);
        return false;
    }

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(triggered_spell_id))
        return false;
    if (basepoints0)
        CastCustomSpell(target, triggered_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura);
    else
        CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(triggered_spell_id, 0, getPreciseTime() + cooldown);
    return true;
}
bool Unit::HandleModDamagePctTakenAuraProc(Unit* victim, DamageInfo* dmgInfoProc, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, double cooldown)
{
    SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();
    //uint32 effIndex = triggeredByAura->GetEffIndex();
    //int32  triggerAmount = triggeredByAura->GetAmount();

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer()
        ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    uint32 triggered_spell_id = 0;
    Unit* target = victim;
    float basepoints0 = 0;
    uint32 damage = dmgInfoProc->GetDamage() + dmgInfoProc->GetAbsorb();

    switch (dummySpell->ClassOptions.SpellClassSet)
    {
        case SPELLFAMILY_DEMON_HUNTER:
        {
            switch (dummySpell->Id)
            {
                case 203513: // Demonic Wards
                {
                    triggered_spell_id = 207014;
                    basepoints0 = (50.f * 1.75f * float(damage) / float(GetMaxHealth())) * 10.f;
                    break;
                }
                default:
                    break;
            }
            break;
        }
        case SPELLFAMILY_WARRIOR:
        {
            switch (dummySpell->Id)
            {
                case 71: // Defensive Stance
                {
                    triggered_spell_id = 195707;
                    basepoints0 = (10.f * float(damage) / float(GetMaxHealth())) * 100.f;
                    break;
                }
                default:
                    break;
            }
            break;
        }
    }

    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
    if (!triggerEntry)
    {
        if(SpellProcTriggered(victim, dmgInfoProc, triggeredByAura, procSpell, procFlags, procEx, cooldown))
            return true;
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleModDamagePctTakenAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id);
        return false;
    }

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(triggered_spell_id))
        return false;

    if (basepoints0)
        CastCustomSpell(target, triggered_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura);
    else
        CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(triggered_spell_id, 0, getPreciseTime() + cooldown);

    return true;
}

// Used in case when access to whole aura is needed
// All procs should be handled like this...
bool Unit::HandleAuraProc(Unit* victim, DamageInfo* /*dmgInfoProc*/, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 /*procEx*/, double cooldown, bool * handled)
{
    SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();

    switch (dummySpell->ClassOptions.SpellClassSet)
    {
        case SPELLFAMILY_GENERIC:
            switch (dummySpell->Id)
            {
                // Nevermelting Ice Crystal
                case 71564:
                    RemoveAuraFromStack(71564);
                    *handled = true;
                    break;
                // Gaseous Bloat
                case 70672:
                case 72455:
                case 72832:
                case 72833:
                {
                    *handled = true;
                    uint16 stack = triggeredByAura->GetStackAmount();
                    int32 const mod = (GetMap()->GetSpawnMode() & 1) ? 1500 : 1250;
                    int32 dmg = 0;
                    for (uint16 i = 1; i < stack; ++i)
                        dmg += mod * stack;
                    if (Unit* caster = triggeredByAura->GetCaster())
                        caster->CastCustomSpell(70701, SPELLVALUE_BASE_POINT0, dmg);
                    break;
                }
                // Ball of Flames Proc
                case 71756:
                    RemoveAuraFromStack(dummySpell->Id);
                    *handled = true;
                    break;
                // Discerning Eye of the Beast
                case 59915:
                {
                    CastSpell(this, 59914, true);   // 59914 already has correct basepoints in DBC, no need for custom bp
                    *handled = true;
                    break;
                }
                // Swift Hand of Justice
                case 59906:
                {
                    float bp0 = CalculatePct(GetMaxHealth(), dummySpell->GetEffect(EFFECT_0, GetSpawnMode())->CalcValue());
                    CastCustomSpell(this, 59913, &bp0, nullptr, nullptr, true);
                    *handled = true;
                    break;
                }
            }
            break;
        case SPELLFAMILY_MAGE:
            switch (dummySpell->Id)
            {
                case 235870: // Alexstrasza's Fury
                {
                    if (HasAura(48108))
                        return false;

                    if (procSpell->Id == 31661)
                    {
                        bool RemoveHeatingUp = HasAura(48107) ? true : false;
                        CastSpell(this, RemoveHeatingUp ? 48108 : 48107, true);

                        if (RemoveHeatingUp)
                            RemoveAura(48107);
                    }
                    return true;
                }
            }
            break;
    }
    return false;
}

bool Unit::HandleProcTriggerSpell(Unit* victim, DamageInfo* dmgInfoProc, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, double cooldown)
{
    // Get triggered aura spell info
    SpellInfo const* auraSpellInfo = triggeredByAura->GetSpellInfo();

    // Basepoints of trigger aura
    float triggerAmount = triggeredByAura->GetAmount();

    // Set trigger spell id, target, custom basepoints
    uint32 trigger_spell_id = auraSpellInfo->GetEffect(triggeredByAura->GetEffIndex(), GetSpawnMode())->TriggerSpell;

    Unit*  target = nullptr;
    float  basepoints0 = 0;
    uint32 damage = dmgInfoProc->GetDamage();
    uint16 stack_for_trigger = 0;

    if (triggeredByAura->GetAuraType() == SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE)
        basepoints0 = triggerAmount;

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer() ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    // Try handle unknown trigger spells
    if (sSpellMgr->GetSpellInfo(trigger_spell_id) == nullptr)
    {
        switch (auraSpellInfo->ClassOptions.SpellClassSet)
        {
            case SPELLFAMILY_GENERIC:
                switch (auraSpellInfo->Id)
                {
                    case 239042: // Concordance of the Legionfall
                    {
                        if (GetMap() && (GetMap()->GetLootDifficulty() == DIFFICULTY_TIMEWALKING || GetMap()->GetLootDifficulty() == DIFFICULTY_TIMEWALKING_RAID))
                            return false;

                        if (HasAura(162697))
                            trigger_spell_id = 242584;
                        else if (HasAura(162698))
                            trigger_spell_id = 242583;
                        else if (HasAura(162699) || HasAura(162701))
                            trigger_spell_id = 242586;
                        else if (HasAura(162700) || HasAura(162702))
                            trigger_spell_id = 243096;
                        else
                            return false;

                        basepoints0 = triggerAmount;
                        break;
                    }
                    case 23780:             // Aegis of Preservation (Aegis of Preservation trinket)
                        trigger_spell_id = 23781;
                        break;
                    case 33896:             // Desperate Defense (Stonescythe Whelp, Stonescythe Alpha, Stonescythe Ambusher)
                        trigger_spell_id = 33898;
                        break;
                    case 43820:             // Charm of the Witch Doctor (Amani Charm of the Witch Doctor trinket)
                        // Pct value stored in dummy
                        basepoints0 = victim->GetCreateHealth() * auraSpellInfo->GetEffect(1, GetSpawnMode())->CalcValue() / 100;
                        target = victim;
                        break;
                    case 188026:            // Infernal alchemist's stone
                    case 57345:             // Darkmoon Card: Greatness
                    {
                        float stat = 0.0f;
                        // strength
                        if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 60229;stat = GetStat(STAT_STRENGTH); }
                        // agility
                        if (GetStat(STAT_AGILITY)  > stat) { trigger_spell_id = 60233;stat = GetStat(STAT_AGILITY);  }
                        // intellect
                        if (GetStat(STAT_INTELLECT)> stat) { trigger_spell_id = 60234;stat = GetStat(STAT_INTELLECT);}
                        break;
                    }
                    case 64568:             // Blood Reserve
                    {
                        if (HealthBelowPctDamaged(35, damage))
                        {
                            CastCustomSpell(this, 64569, &triggerAmount, nullptr, nullptr, true);
                            RemoveAura(64568);
                        }
                        return false;
                    }
                    case 67702:             // Death's Choice, Item - Coliseum 25 Normal Melee Trinket
                    {
                        float stat = 0.0f;
                        // strength
                        if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67708;stat = GetStat(STAT_STRENGTH); }
                        // agility
                        if (GetStat(STAT_AGILITY)  > stat) { trigger_spell_id = 67703;                               }
                        break;
                    }
                    case 67771:             // Death's Choice (heroic), Item - Coliseum 25 Heroic Melee Trinket
                    {
                        float stat = 0.0f;
                        // strength
                        if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67773;stat = GetStat(STAT_STRENGTH); }
                        // agility
                        if (GetStat(STAT_AGILITY)  > stat) { trigger_spell_id = 67772;                               }
                        break;
                    }
                    // Mana Drain Trigger
                    case 27522:
                    case 40336:
                    {
                        // On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target.
                        if (this && isAlive())
                            CastSpell(this, 29471, true, castItem, triggeredByAura);
                        if (victim && victim->isAlive())
                            CastSpell(victim, 27526, true, castItem, triggeredByAura);
                        return true;
                    }
                    // Evasive Maneuvers
                    case 50240:
                    {
                        // Remove a Evasive Charge
                        if(Aura* charge = GetAura(50241))
                            if (charge->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL))
                                RemoveAurasDueToSpell(50240);
                        break;
                    }
                    case 146068: //Blade of the hundred steps (tank buff)[SO]
                    {
                        CastSpell(this, 146081, true);
                        return true;
                    }
                    case 146189: //Eminence[SO]
                    {
                        if (damage)
                        {
                            CastCustomSpell(146190, SPELLVALUE_BASE_POINT0, damage*1.5, this, true);
                            return true;
                        }
                        return false;
                    }
                }
                break;
            case SPELLFAMILY_DRUID:
            {
                switch (auraSpellInfo->Id)
                {
                    // Druid Forms Trinket
                    case 37336:
                    {
                        switch (GetShapeshiftForm())
                        {
                            case FORM_NONE:     trigger_spell_id = 37344; break;
                            case FORM_CAT:      trigger_spell_id = 37341; break;
                            case FORM_BEAR:     trigger_spell_id = 37340; break;
                            case FORM_TREE:     trigger_spell_id = 37342; break;
                            case FORM_MOONKIN:  trigger_spell_id = 37343; break;
                            default:
                                return false;
                        }
                        break;
                    }
                    default:
                        break;
                }
                break;
            }
            case SPELLFAMILY_PALADIN:
            {
                switch (auraSpellInfo->Id)
                {
                    // Healing Discount
                    case 37705:
                    {
                        trigger_spell_id = 37706;
                        target = this;
                        break;
                    }
                    // Soul Preserver
                    case 60510:
                    {
                        switch (getClass())
                        {
                            case CLASS_DRUID:
                                trigger_spell_id = 60512;
                                break;
                            case CLASS_PALADIN:
                                trigger_spell_id = 60513;
                                break;
                            case CLASS_PRIEST:
                                trigger_spell_id = 60514;
                                break;
                            case CLASS_SHAMAN:
                                trigger_spell_id = 60515;
                                break;
                        }

                        target = this;
                        break;
                    }
                    case 37657: // Lightning Capacitor
                    case 54841: // Thunder Capacitor
                    case 67712: // Item - Coliseum 25 Normal Caster Trinket
                    case 67758: // Item - Coliseum 25 Heroic Caster Trinket
                    {
                        if (!victim || !victim->isAlive() || !IsPlayer())
                            return false;

                        uint32 stack_spell_id = 0;
                        switch (auraSpellInfo->Id)
                        {
                            case 37657:
                                stack_spell_id = 37658;
                                trigger_spell_id = 37661;
                                break;
                            case 54841:
                                stack_spell_id = 54842;
                                trigger_spell_id = 54843;
                                break;
                            case 67712:
                                stack_spell_id = 67713;
                                trigger_spell_id = 67714;
                                break;
                            case 67758:
                                stack_spell_id = 67759;
                                trigger_spell_id = 67760;
                                break;
                        }

                        CastSpell(this, stack_spell_id, true, nullptr, triggeredByAura);

                        Aura* dummy = GetAura(stack_spell_id);
                        if (!dummy || dummy->GetStackAmount() < triggerAmount)
                            return false;

                        RemoveAurasDueToSpell(stack_spell_id);
                        target = victim;
                        break;
                    }
                    default:
                        break;
                }
                break;
            }
            case SPELLFAMILY_SHAMAN:
            {
                switch (auraSpellInfo->Id)
                {
                    // Damage from Lightning Shield (The Ten Storms set)
                    case 23552:
                    {
                        trigger_spell_id = 27635;
                        break;
                    }
                    case 207835: // Stormlash
                    {
                        if (!damage || !IsPlayer())
                            return false;

                        if (Unit* shaman = triggeredByAura->GetCaster())
                            shaman->CastSpell(victim, 213307, true);
                        break;
                    }
                }
                break;
            }
            default:
                 break;
        }
    }

    // All ok. Check current trigger spell
    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(trigger_spell_id);
    if (triggerEntry == nullptr)
    {
        // Don't cast unknown spell
        // TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleProcTriggerSpell: Spell %u has 0 in EffectTriggered[%d]. Unhandled custom case?", auraSpellInfo->Id, triggeredByAura->GetEffIndex());
        if(SpellProcTriggered(victim, dmgInfoProc, triggeredByAura, procSpell, procFlags, procEx, cooldown))
            return true;
        return false;
    }

    // not allow proc extra attack spell at extra attack
    if (m_extraAttacks && triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
        return false;

    // Custom requirements (not listed in procEx) Warning! damage dealing after this
    // Custom triggered spells
    switch (auraSpellInfo->Id)
    {
        case 192759: // Kingsbane
        {
            if (!victim->HasAura(192759, GetGUID()))
                return false;

            break;
        }
        case 238111: // Soul of the Slaughter
        {
            if (procSpell->Power.PowerType == POWER_RAGE && procSpell->Power.PowerCost > 0)
            {
                uint32 _rage = std::max(procSpell->Power.PowerCost, m_powerCost[POWER_RAGE]);
                float chance = triggerAmount / 100.f * _rage;

                if (!roll_chance_f(chance))
                    return false;

                break;
            }
            return false;
        }
        case 184783: // Tactician
        {
            if (procSpell->Power.PowerType == POWER_RAGE && procSpell->Power.PowerCost > 0)
            {
                uint32 _rage = std::max(procSpell->Power.PowerCost, m_powerCost[POWER_RAGE]);

                if (AuraEffect const* aurEff1 = GetAuraEffect(209494, EFFECT_0)) // Exploit the Weakness
                    _rage += CalculatePct(_rage, aurEff1->GetAmount());

                float chance = triggerAmount / 1000.f * _rage;

                if (!roll_chance_f(chance))
                    return false;

                break;
            }
            return false;
        }
        case 157642: // Enhanced Pyrotechnics
        {
            if (procEx & PROC_EX_CRITICAL_HIT)
            {
                RemoveAurasDueToSpell(trigger_spell_id);
            }
            else
            {
                AddAura(trigger_spell_id, this);
            }
            return true;
        }
        case 208051: // Sephuz's Secret
        {
            Unit* unit = this;

            if (Unit* owner = GetOwner())
                unit = owner;

            if (unit->HasAura(226262))
                return false;

            if (procSpell->HasAura(SPELL_AURA_MOD_CONFUSE) || procSpell->HasAura(SPELL_AURA_MOD_FEAR) || procSpell->HasAura(SPELL_AURA_MOD_FEAR_2) ||
                procSpell->HasAura(SPELL_AURA_MOD_STUN) || procSpell->HasAura(SPELL_AURA_STRANGULATE) || procSpell->HasAura(SPELL_AURA_MOD_ROOTED) || 
                procSpell->HasAura(SPELL_AURA_MOD_SILENCE) || procSpell->Categories.Mechanic == MECHANIC_POLYMORPH || procSpell->Categories.Mechanic == MECHANIC_GRIP ||
                (procEx & PROC_EX_INTERRUPT) || (procEx & PROC_EX_DISPEL))
            {
                unit->CastSpell(unit, 226262, true);
                unit->CastSpell(unit, trigger_spell_id, true);
                return true;
            }
                
            return false;
        }
        case 243120: // Deathstrike Venom
        {
            if (Unit* owner = GetAnyOwner())
                CastSpell(victim, trigger_spell_id, true, castItem, triggeredByAura, owner->GetGUID());
            return false;
        }
        case 211592: // Stolen Power
        {
            if (Unit* owner = GetAnyOwner())
                owner->CastSpell(owner, 211529, true);
            return false;
        }
        case 225736: // Recursive Strikes
        {
            basepoints0 = triggerAmount;
            break;
        }
        case 222046: // Maddening Whispers
        {
            if (Aura* aur = victim->GetAura(222050, GetGUID()))
            {
                if (aur->GetStackAmount() == 9)
                {
                    basepoints0 = triggerAmount * 10;
                    CastCustomSpell(victim, 222052, &basepoints0, nullptr, nullptr, true, castItem);
                    aur->Remove();
                    return false;
                }
            }
            break;
        }
        case 202665: // Curse of the Dreadblades
        {
            if (!procSpell)
                return false;

            if (procSpell->Id == 185763 || procSpell->Id == 193315 || procSpell->Id == 196937 || procSpell->Id == 202895 || procSpell->Id == 8676)
                break;

            if (GetPowerCost(POWER_COMBO_POINTS) > 0)
            {
                int32 perc = 0;
                if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(202669))
                    perc += spellInfo->Effects[EFFECT_0]->BasePoints;

                float bp0 = CountPctFromCurHealth(perc);
                CastCustomSpell(this, 202669, &bp0, nullptr, nullptr, true);
            }
            return false;
        }
        case 194909: // Frozen Pulse
        {
            uint8 minUsed = GetMaxPower(POWER_RUNES) - auraSpellInfo->Effects[EFFECT_1]->BasePoints * 2;
            uint8 power = GetPower(POWER_RUNES);
            if ((power - minUsed) > 0)
                return false;
            break;
        }
        case 146199: // Spirit of Chi-Ji
        {
            for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
            {
                if (procSpell->EffectMask < uint32(1 << i))
                    break;

                if (procSpell->Effects[i]->Effect)
                    if (procSpell->Effects[i]->HasRadius())
                        return false;
            }

            break;
        }
        case 146195: // Flurry of Xuen
        case 146197: // Essence of Yu'lon
        {
            if (!victim || victim->IsPlayer())
                return false;

            if (Unit* owner = victim->GetOwner())
                if (owner->IsPlayer())
                    return false;

            if (procSpell)
                for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
                {
                    if (procSpell->EffectMask < uint32(1 << i))
                        break;

                    if (procSpell->Effects[i]->Effect)
                        if (procSpell->Effects[i]->HasRadius())
                            return false;
                }
            break;
        }
        case 145672: // Riposte
        case 145676: // Riposte
        {
            if(Player* player = ToPlayer())
            {
                int32 CRraiting = player->GetUInt32Value(PLAYER_FIELD_COMBAT_RATINGS + CR_PARRY) + player->GetUInt32Value(PLAYER_FIELD_COMBAT_RATINGS + CR_DODGE);
                basepoints0 = int32(CRraiting * triggerAmount / 100);
            }
            break;
        }
        // Arcane Missiles !
        case 79684:
        {
            if (!procSpell)
                return false;

            if (procSpell->Id == 5143 || procSpell->Id == 7268)
                return false;

            break;
        }
        // Selfless Healer
        case 85804:
        {
            if (!procSpell)
                return false;

            if (procSpell->Id != 20271)
                return false;

            break;
        }
        // Twist of Fate
        case 109142:
        {
            if (!victim)
                return false;

            if (!procSpell)
                return false;

            if (victim->GetHealthPct() > 35.0f)
                return false;

            break;
        }
        case 35551: // Combat Potency
        {
            if (!IsPlayer())
                return false;

            if (!roll_chance_f(triggerAmount))
                return false;

            break;
        }
        // Persistent Shield (Scarab Brooch trinket)
        // This spell originally trigger 13567 - Dummy Trigger (vs dummy efect)
        case 26467:
        {
            basepoints0 = int32(CalculatePct(damage, 15));
            target = victim;
            trigger_spell_id = 26470;
            break;
        }
        // Unyielding Knights (item exploit 29108\29109)
        case 38164:
        {
            if (!victim || victim->GetEntry() != 19457)  // Proc only if your target is Grillok
                return false;
            break;
        }
        // Deflection
        case 52420:
        {
            if (!HealthBelowPctDamaged(35, damage))
                return false;
            break;
        }
        // Bonus Healing (Crystal Spire of Karabor mace)
        case 40971:
        {
            // If your target is below $s1% health
            if (!victim || !victim->isAlive() || victim->HealthAbovePct(triggerAmount))
                return false;
            break;
        }
        // Rapid Recuperation
        case 53228:
        case 53232:
        {
            // This effect only from Rapid Fire (ability cast)
            if (!(procSpell->ClassOptions.SpellClassMask[0] & 0x20))
                return false;
            break;
        }
        // Decimation
        case 63156:
        case 63158:
            // Can proc only if target has hp below 25%
            if (!victim || !victim->HealthBelowPct(auraSpellInfo->GetEffect(EFFECT_1, GetSpawnMode())->CalcValue()))
                return false;
            break;
        // Deathbringer Saurfang - Blood Beast's Blood Link
        case 72176:
            basepoints0 = 3;
            break;
        // Professor Putricide - Ooze Spell Tank Protection
        case 71770:
            if (victim)
                victim->CastSpell(victim, trigger_spell_id, true);    // EffectImplicitTarget is self
            return true;
        case 45057: // Evasive Maneuvers (Commendation of Kael`thas trinket)
        case 71634: // Item - Icecrown 25 Normal Tank Trinket 1
        case 71640: // Item - Icecrown 25 Heroic Tank Trinket 1
        case 75475: // Item - Chamber of Aspects 25 Normal Tank Trinket
        case 75481: // Item - Chamber of Aspects 25 Heroic Tank Trinket
        {
            // Procs only if damage takes health below $s1%
            if (!HealthBelowPctDamaged(triggerAmount, damage))
                return false;
            break;
        }
        case 143574: //Swelling corruption (Immerseus HM)
            if (ToCreature())
            {
                CastSpell(victim, trigger_spell_id, true);
                CastSpell(victim, 143581, true);
                RemoveAuraFromStack(143574);
                return true;
            }
            break;
        default:
            break;
    }

    // Custom basepoints/target for exist spell
    // dummy basepoints or other customs
    switch (trigger_spell_id)
    {
        case 194392: // Volley
        {
            CastSpell(victim, trigger_spell_id, TriggerCastFlags(TRIGGERED_FULL_MASK &~TRIGGERED_IGNORE_POWER_AND_REAGENT_COST));
            return true;
        }
        case 144999: // Всплеск стихий
        case 145180: // Empowered Shadows
        {
            basepoints0 = triggerAmount;
            break;
        }
        // Auras which should proc on area aura source (caster in this case):
        // Cast positive spell on enemy target
        case 7099:  // Curse of Mending
        case 39703: // Curse of Mending
        case 29494: // Temptation
        case 20233: // Improved Lay on Hands (cast on target)
        {
            target = victim;
            break;
        }
        case 146310: // Restless Agility
        case 146317: // Restless Spirit
        {
            target = this;
            stack_for_trigger = triggerEntry->GetAuraOptions(GetSpawnMode())->CumulativeAura;
            break;
        }
        // Enlightenment (trigger only from mana cost spells)
        case 35095:
        {
            if (!procSpell || procSpell->Power.PowerType != POWER_MANA || (procSpell->Power.PowerCost == 0 && procSpell->Power.PowerCostPercentage == 0))
                return false;
            break;
        }
        // Shadow's Fate (Shadowmourne questline)
        case 71169:
        {
            if (!victim || !IsPlayer())
                return false;

            Player* player = ToPlayer();
            if (player->GetQuestStatus(24749) == QUEST_STATUS_INCOMPLETE)       // Unholy Infusion
            {
                if (!player->HasAura(71516) || victim->GetEntry() != 36678)    // Shadow Infusion && Professor Putricide
                    return false;
            }
            else if (player->GetQuestStatus(24756) == QUEST_STATUS_INCOMPLETE)  // Blood Infusion
            {
                if (!player->HasAura(72154) || victim->GetEntry() != 37955)    // Thirst Quenched && Blood-Queen Lana'thel
                    return false;
            }
            else if (player->GetQuestStatus(24757) == QUEST_STATUS_INCOMPLETE)  // Frost Infusion
            {
                if (!player->HasAura(72290) || victim->GetEntry() != 36853)    // Frost-Imbued Blade && Sindragosa
                    return false;
            }
            else if (player->GetQuestStatus(24547) != QUEST_STATUS_INCOMPLETE)  // A Feast of Souls
                return false;

            if (!victim->IsCreature())
                return false;
            // critters are not allowed
            if (victim->GetCreatureType() == CREATURE_TYPE_CRITTER)
                return false;
            break;
        }
        //Generate Rage - General Nazgrim[SO]
        case 144278:
        {
            if (victim->ToPlayer() && !victim->HasAura(143494)) //Sundering Blow
            {
                CastSpell(this, 143597, true); //Generate Rage - Energize
                return true;
            }
            break;
        }
    }

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(trigger_spell_id))
        return false;

    // try detect target manually if not set
    if (target == nullptr)
        target = !(procFlags & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry && triggerEntry->IsPositive() ? this : victim;

    if (basepoints0 && triggeredByAura->GetAuraType() == SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE)
        CastCustomSpell(target, trigger_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura);
    else if (basepoints0)
        CastCustomSpell(target, trigger_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura);
    else if (stack_for_trigger)
        AddAura(trigger_spell_id, target, castItem, stack_for_trigger);
    else
        CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(trigger_spell_id, 0, getPreciseTime() + cooldown);

    return true;
}

bool Unit::HandleProcMelleTriggerSpell(Unit* victim, DamageInfo* dmgInfoProc, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, double cooldown)
{
    // Get triggered aura spell info
    SpellInfo const* auraSpellInfo = triggeredByAura->GetSpellInfo();

    // Set trigger spell id, target, custom basepoints
    uint32 trigger_main = auraSpellInfo->GetEffect(triggeredByAura->GetEffIndex(), GetSpawnMode())->MiscValue;
    uint32 trigger_off = auraSpellInfo->GetEffect(triggeredByAura->GetEffIndex(), GetSpawnMode())->TriggerSpell;

    // All ok. Check current trigger spell
    SpellInfo const* triggerMain = sSpellMgr->GetSpellInfo(trigger_main);
    SpellInfo const* triggerOff = sSpellMgr->GetSpellInfo(trigger_off);
    if (triggerMain == nullptr && triggerOff == nullptr)
    {
        // Don't cast unknown spell
        if(SpellProcTriggered(victim, dmgInfoProc, triggeredByAura, procSpell, procFlags, procEx, cooldown))
            return true;
        return false;
    }

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(trigger_main))
        return false;

    Unit* target = victim ? victim : GetTargetUnit();
    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer() ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    // try detect target manually if not set
    if (target == nullptr)
        target = !(procFlags & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerMain && triggerMain->IsPositive() ? this : victim;

    if (triggerMain)
        CastSpell(target, trigger_main, true, castItem, triggeredByAura);

    if (triggerOff)
        CastSpell(target, trigger_off, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
    {
        AddSpellCooldown(trigger_main, 0, getPreciseTime() + cooldown);
        AddSpellCooldown(trigger_off, 0, getPreciseTime() + cooldown);
    }

    return true;
}

bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, DamageInfo* /*dmgInfoProc*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, double cooldown)
{
    int32 scriptId = triggeredByAura->GetMiscValue();

    if (!victim || !victim->isAlive())
        return false;

    uint32 triggered_spell_id = 0;

    // not processed
    if (!triggered_spell_id)
        return false;

    // standard non-dummy case
    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);

    if (!triggerEntry)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleOverrideClassScriptAuraProc: Spell %u triggering for class script id %u", triggered_spell_id, scriptId);
        return false;
    }

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(triggered_spell_id))
        return false;

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer() ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;
    CastSpell(victim, triggered_spell_id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(triggered_spell_id, 0, getPreciseTime() + cooldown);

    return true;
}

void Unit::setPowerType(Powers fieldPower)
{
    if (getPowerType() == fieldPower)
        return;

    //! For expl. Vehicle power POWER_TYPE_VAULT_CRACKING_PROGRESS = 82 has field = 0;
    //! Maybe it's index? but for vehicle send power category.
    SetFieldPowerType(fieldPower);

    if (IsPlayer())
        ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POWER_TYPE);

    Powers new_powertype = fieldPower;
    switch (new_powertype)
    {
        case POWER_MANA:
            break;
        default:
            SetMaxPower(new_powertype, GetCreatePowers(new_powertype));
            SetPower(new_powertype, GetPowerForReset(new_powertype));
            break;
    }
    UpdateMaxPower(new_powertype);
}

FactionTemplateEntry const* Unit::getFactionTemplateEntry() const
{
    FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(getFaction());
    if (!entry)
    {
        static ObjectGuid guid;                             // prevent repeating spam same faction problem

        if (GetGUID() != guid)
        {
            if (Player const* player = ToPlayer())
                TC_LOG_ERROR(LOG_FILTER_UNITS, "Player %s has invalid faction (faction template id) #%u", player->GetName(), getFaction());
            else if (Creature const* creature = ToCreature())
            {
                TC_LOG_ERROR(LOG_FILTER_UNITS, "Creature (template id: %u) has invalid faction (faction template id) #%u", creature->GetCreatureTemplate()->Entry, getFaction());
                guid = GetGUID();
                return sFactionTemplateStore.LookupEntry(35);
            }
            else
                TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit (name=%s, type=%u) has invalid faction (faction template id) #%u", GetName(), uint32(GetTypeId()), getFaction());

            guid = GetGUID();
        }
    }
    return entry;
}

// function based on function Unit::UnitReaction from 13850 client
ReputationRank Unit::GetReactionTo(Unit const* target) const
{
    if (!target)
        return REP_FRIENDLY;

    // always friendly to self
    if (this == target)
        return REP_FRIENDLY;

    if (!IsInWorld() || !target->IsInWorld())
        return REP_FRIENDLY;

    // always friendly to charmer or owner
    if (GetCharmerOrOwnerOrSelf() == target->GetCharmerOrOwnerOrSelf())
        return REP_FRIENDLY;

    if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) || target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
    {
        Player const* selfPlayerOwner = GetAffectingPlayer();
        Player const* targetPlayerOwner = target->GetAffectingPlayer();

        if (selfPlayerOwner && targetPlayerOwner)
        {
            // always friendly to other unit controlled by player, or to the player himself
            if (selfPlayerOwner == targetPlayerOwner)
                return REP_FRIENDLY;

            // duel - always hostile to opponent
            if (selfPlayerOwner->duel && selfPlayerOwner->duel->opponent == targetPlayerOwner->GetGUID() && selfPlayerOwner->duel->state == DUEL_STARTED)
                return REP_HOSTILE;

            // same group - checks dependant only on our faction - skip FFA_PVP for example
            if (selfPlayerOwner->IsInRaidWith(targetPlayerOwner))
                return REP_FRIENDLY; // return true to allow config option AllowTwoSide.Interaction.Group to work
            // however client seems to allow mixed group parties, because in 13850 client it works like:
            // return GetFactionReactionTo(getFactionTemplateEntry(), target);

            if (selfPlayerOwner->IsFFAPvP() && targetPlayerOwner->IsFFAPvP())
                return REP_HOSTILE;
        }

        // check FFA_PVP
        if (IsFFAPvP() && target->IsFFAPvP())
            return REP_HOSTILE;

        if (selfPlayerOwner)
        {
            if (FactionTemplateEntry const* targetFactionTemplateEntry = target->getFactionTemplateEntry())
            {
                if (ReputationRank const* repRank = selfPlayerOwner->GetReputationMgr().GetForcedRankIfAny(targetFactionTemplateEntry))
                    return *repRank;

                if (!selfPlayerOwner->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_IGNORE_REPUTATION))
                {
                    if (FactionEntry const* targetFactionEntry = sFactionStore.LookupEntry(targetFactionTemplateEntry->Faction))
                    {
                        if (targetFactionEntry->CanHaveReputation())
                        {
                            if (targetFactionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD && selfPlayerOwner->HasFlag(PLAYER_FIELD_PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
                                return REP_HOSTILE;

                            // if faction has reputation, hostile state depends only from AtWar state
                            if (selfPlayerOwner->GetReputationMgr().IsAtWar(targetFactionEntry))
                                return REP_HOSTILE;

                            return REP_FRIENDLY;
                        }
                    }
                }
            }
        }
    }

    // do checks dependant only on our faction
    return GetFactionReactionTo(getFactionTemplateEntry(), target);
}

ReputationRank Unit::GetFactionReactionTo(FactionTemplateEntry const* factionTemplateEntry, Unit const* target)
{
    // always neutral when no template entry found
    if (!factionTemplateEntry)
        return REP_NEUTRAL;

    FactionTemplateEntry const* targetFactionTemplateEntry = target->getFactionTemplateEntry();
    if (!targetFactionTemplateEntry)
        return REP_NEUTRAL;

    if (Player const* targetPlayerOwner = target->GetAffectingPlayer())
    {
        if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD && targetPlayerOwner->HasFlag(PLAYER_FIELD_PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
            return REP_HOSTILE;

        if (ReputationRank const* repRank = targetPlayerOwner->GetReputationMgr().GetForcedRankIfAny(factionTemplateEntry))
            return *repRank;

        if (!target->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_IGNORE_REPUTATION))
        {
            if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->Faction))
            {
                if (factionEntry->CanHaveReputation())
                {
                    // CvP case - check reputation, don't allow state higher than neutral when at war
                    ReputationRank repRank = targetPlayerOwner->GetReputationMgr().GetRank(factionEntry);
                    if (targetPlayerOwner->GetReputationMgr().IsAtWar(factionEntry))
                        repRank = std::min(REP_NEUTRAL, repRank);
                    return repRank;
                }
            }
        }
    }

    if (factionTemplateEntry->IsHostileTo(*targetFactionTemplateEntry))
        return REP_HOSTILE;
    if (factionTemplateEntry->IsFriendlyTo(*targetFactionTemplateEntry))
        return REP_FRIENDLY;
    if (targetFactionTemplateEntry->IsFriendlyTo(*factionTemplateEntry))
        return REP_FRIENDLY;
    if (factionTemplateEntry->Flags & FACTION_TEMPLATE_FLAG_HOSTILE_BY_DEFAULT)
        return REP_HOSTILE;

    return REP_NEUTRAL;
}

bool Unit::IsHostileTo(Unit const* unit) const
{
    return GetReactionTo(unit) <= REP_HOSTILE;
}

bool Unit::IsFriendlyTo(Unit const* unit) const
{
    return GetReactionTo(unit) >= REP_FRIENDLY;
}

bool Unit::IsHostileToPlayers() const
{
    FactionTemplateEntry const* my_faction = getFactionTemplateEntry();
    if (!my_faction || !my_faction->Faction)
        return false;

    FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction);
    if (raw_faction && raw_faction->CanHaveReputation())
        return false;

    return my_faction->IsHostileToPlayers();
}

bool Unit::IsNeutralToAll() const
{
    FactionTemplateEntry const* my_faction = getFactionTemplateEntry();
    if (!my_faction || !my_faction->Faction)
        return true;

    FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->Faction);
    if (raw_faction && raw_faction->CanHaveReputation())
        return false;

    return my_faction->IsNeutralToAll();
}

bool Unit::Attack(Unit* victim, bool meleeAttack)
{
    if (!victim || victim == this)
        return false;

    if (auto cre = ToCreature())
        if (cre->GetReactState() == REACT_ATTACK_OFF)
            return false;

    // dead units can neither attack nor be attacked
    if (!isAlive() || !victim->IsInWorld() || !victim->isAlive())
        return false;

    if (victim->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
        return false;

    // player cannot attack in mount state
    if (IsPlayer() && IsMounted() && !HasFlag(UNIT_FIELD_FLAGS_3, UNIT_FLAG3_NOT_CHECK_MOUNT))
        return false;

    // nobody can attack GM in GM-mode
    if (victim->IsPlayer())
    {
        if (victim->ToPlayer()->isGameMaster())
            return false;

        if (auto cre = ToCreature())
            if (cre->IsInEvadeMode())
                return false;
    }
    else
    {
        if (victim->ToCreature()->IsInEvadeMode())
            return false;
    }

    // remove SPELL_AURA_MOD_UNATTACKABLE at attack (in case non-interruptible spells stun aura applied also that not let attack)
    if (HasAuraType(SPELL_AURA_MOD_UNATTACKABLE))
        RemoveAurasByType(SPELL_AURA_MOD_UNATTACKABLE);

    if (m_attacking)
    {
        if (m_attacking == victim)
        {
            //Don't missing target
            if (auto cre = ToCreature())
            {
                if (!HasUnitState(UNIT_STATE_CONTROLLED | UNIT_STATE_CASTING) && !GetGuidValue(UNIT_FIELD_TARGET) && cre->GetReactState() != REACT_PASSIVE)
                    SetTarget(victim->GetGUID());
            }

            // switch to melee attack from ranged/magic
            if (meleeAttack)
            {
                if (!HasUnitState(UNIT_STATE_MELEE_ATTACKING))
                {
                    AddUnitState(UNIT_STATE_MELEE_ATTACKING);
                    SendMeleeAttackStart(victim);
                    return true;
                }
            }
            else if (HasUnitState(UNIT_STATE_MELEE_ATTACKING))
            {
                ClearUnitState(UNIT_STATE_MELEE_ATTACKING);
                SendMeleeAttackStop(victim);
                return true;
            }
            return false;
        }

        // switch target
        InterruptSpell(CURRENT_MELEE_SPELL);
        if (!meleeAttack)
            ClearUnitState(UNIT_STATE_MELEE_ATTACKING);
    }

    if (victim && victim->IsCreature() && !victim->hasAttacker(this))
        victim->ToCreature()->AI()->AttackedBy(this);

    UpdateVictim(victim);

    // Set our target
    SetTarget(victim->GetGUID());

    if (meleeAttack)
        AddUnitState(UNIT_STATE_MELEE_ATTACKING);

    // set position before any AI calls/assistance
    //if (IsCreature())
    //    ToCreature()->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ());

    if (IsCreature() && !ToCreature()->isPet())
    {
        // should not let player enter combat by right clicking target - doesn't helps
        SetInCombatWith(victim);
        if (victim->IsPlayer())
            victim->SetInCombatWith(this);
        AddThreat(victim, 0.0f);

        ToCreature()->SendAIReaction(AI_REACTION_HOSTILE);
        ToCreature()->CallAssistance();
    }

    // delay offhand weapon attack to next attack time
    if (haveOffhandWeapon())
        setAttackTimer(OFF_ATTACK, 1000);

    if (meleeAttack)
        SendMeleeAttackStart(victim);

    // Let the pet know we've started attacking someting. Handles melee attacks only
    // Spells such as auto-shot and others handled in WorldSession::HandleCastSpellOpcode
    if (this->IsPlayer())
    {
        Pet* playerPet = this->ToPlayer()->GetPet();

        if (playerPet && playerPet->isAlive())
            playerPet->AI()->OwnerAttacked(victim);
    }

    return true;
}

bool Unit::AttackStop()
{
    if (!m_attacking)
        return false;

    Unit* victim = m_attacking;

    UpdateVictim(nullptr);

    // Clear our target
    SetTarget(ObjectGuid::Empty);

    ClearUnitState(UNIT_STATE_MELEE_ATTACKING);

    InterruptSpell(CURRENT_MELEE_SPELL);

    // reset only at real combat stop
    if (Creature* creature = ToCreature())
    {
        creature->SetNoCallAssistance(false);

        if (creature->HasSearchedAssistance())
        {
            creature->SetNoSearchAssistance(false);
            UpdateSpeed(MOVE_RUN, false);
        }
    }

    SendMeleeAttackStop(victim);

    return true;
}

void Unit::CombatStop(bool includingCast)
{
    if (includingCast && IsNonMeleeSpellCast(false))
        InterruptNonMeleeSpells(false);

    AttackStop();
    RemoveAllAttackers();
    if (IsPlayer())
        ToPlayer()->SendAttackSwingError(ATTACK_SWING_ERROR_DEAD_TARGET);     // melee and ranged forced attack cancel
    ClearInCombat();
}

void Unit::CombatStopWithPets(bool includingCast)
{
    CombatStop(includingCast);

    for (ControlList::const_iterator itr = m_Controlled.begin(), next; itr != m_Controlled.end(); itr = next)
    {
        next = itr;
        ++next;
        if(Unit* unit = ObjectAccessor::GetUnit(*this, *itr))
            unit->CombatStop(includingCast);
    }
}

void Unit::UpdateVictim(Unit* victim)
{
    if(victim)
    {
        if (m_attacking)
            m_attacking->_removeAttacker(this);

        m_attacking = victim;
        m_attacking->_addAttacker(this);
    }
    else
    {
        m_attacking->_removeAttacker(this);
        m_attacking = nullptr;
    }
}

bool Unit::isAttackingPlayer() const
{
    if (HasUnitState(UNIT_STATE_ATTACK_PLAYER))
        return true;

    for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
        if(Unit* unit = ObjectAccessor::GetUnit(*this, *itr))
            if (unit->isAttackingPlayer())
                return true;

    for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
        if (m_SummonSlot[i])
            if (Creature* summon = GetMap()->GetCreature(m_SummonSlot[i]))
                if (summon->isAttackingPlayer())
                    return true;

    return false;
}

void Unit::RemoveAllAttackers()
{
    while (!m_attackers.empty())
    {
        UnitSet::iterator iter = m_attackers.begin();
        if (!(*iter) || !(*iter)->AttackStop())
        {
            TC_LOG_ERROR(LOG_FILTER_UNITS, "WORLD: Unit has an attacker that isn't attacking it!");
            m_attackers.erase_at(iter);
        }
    }
}

void Unit::ModifyAuraState(AuraStateType flag, bool apply)
{
    if (apply)
    {
        if (!HasFlag(UNIT_FIELD_AURA_STATE, 1<<(flag-1)))
        {
            SetFlag(UNIT_FIELD_AURA_STATE, 1<<(flag-1));
            if (IsPlayer())
            {
                CasterAuraStateSpellList auraSpells = ToPlayer()->GetCasterAuraStateSpellList();
                for (CasterAuraStateSpellList::iterator itr = auraSpells.begin(); itr != auraSpells.end(); ++itr)
                {
                    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(*itr);
                    if (!spellInfo)
                        continue;

                    if (spellInfo->AuraRestrictions.CasterAuraState == uint32(flag))
                        CastSpell(this, spellInfo->Id, true, nullptr);
                }
            }
            else if (Pet* pet = ToCreature()->ToPet())
            {
                for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
                {
                    if (itr->second.state == PETSPELL_REMOVED)
                        continue;
                    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
                    if (!spellInfo || !spellInfo->IsPassive())
                        continue;
                    if (spellInfo->AuraRestrictions.CasterAuraState == uint32(flag))
                        CastSpell(this, itr->first, true, nullptr);
                }
            }
        }
    }
    else
    {
        if (HasFlag(UNIT_FIELD_AURA_STATE, 1<<(flag-1)))
        {
            RemoveFlag(UNIT_FIELD_AURA_STATE, 1<<(flag-1));

            for (Unit::AuraApplicationMap::iterator itr = m_appliedAuras.begin(); itr != m_appliedAuras.end();)
            {
                SpellInfo const* spellProto = (*itr).second->GetBase()->GetSpellInfo();
                if (spellProto->AuraRestrictions.CasterAuraState == uint32(flag))
                    RemoveAura(itr);
                else
                    ++itr;
            }

            if (flag == AURA_STATE_PVP_RAID_PREPARE)
                if (Player* player = ToPlayer())
                    player->SendForceUpdateToClient();
        }
    }
}

uint32 Unit::BuildAuraStateUpdateForTarget(Unit* target) const
{
    uint32 auraStates = GetUInt32Value(UNIT_FIELD_AURA_STATE) &~(PER_CASTER_AURA_STATE_MASK);
    for (AuraStateAurasMap::const_iterator itr = m_auraStateAuras.begin(); itr != m_auraStateAuras.end(); ++itr)
        if ((1<<(itr->first-1)) & PER_CASTER_AURA_STATE_MASK)
            if (itr->second->GetBase()->GetCasterGUID() == target->GetGUID())
                auraStates |= (1<<(itr->first-1));

    return auraStates;
}

bool Unit::HasAuraState(AuraStateType flag, SpellInfo const* spellProto, Unit const* Caster) const
{
    if (Caster)
    {
        if (spellProto)
        {
            if (AuraEffectList const* stateAuras = Caster->GetAuraEffectsByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE))
            for (AuraEffectList::const_iterator j = stateAuras->begin(); j != stateAuras->end(); ++j)
                if ((*j)->IsAffectingSpell(spellProto))
                    return true;
        }
        // Check per caster aura state
        // If aura with aurastate by caster not found return false
        // OO: Sha of Pride: 144363 hack fix.
        if ((1<<(flag-1)) & PER_CASTER_AURA_STATE_MASK && (!spellProto || spellProto->Id != 144363))
        {
            for (AuraStateAurasMap::const_iterator itr = m_auraStateAuras.lower_bound(flag); itr != m_auraStateAuras.upper_bound(flag); ++itr)
                if (itr->second->GetBase()->GetCasterGUID() == Caster->GetGUID())
                    return true;
            return false;
        }
    }

    return HasFlag(UNIT_FIELD_AURA_STATE, 1<<(flag-1));
}

void Unit::SetOwnerGUID(ObjectGuid owner)
{
    if (GetSummonedByGUID() == owner)
        return;

    SetGuidValue(UNIT_FIELD_SUMMONED_BY, owner);
    if (owner.IsEmpty())
        return;

    // Update owner dependent fields
    Player* player = ObjectAccessor::GetPlayer(*this, owner);
    if (!player || !player->HaveAtClient(this)) // if player cannot see this unit yet, he will receive needed data with create object
        return;

    SetFieldNotifyFlag(UF_FLAG_OWNER);

    UpdateData udata(GetMapId());
    WorldPacket packet;
    BuildValuesUpdateBlockForPlayer(&udata, player);
    if (udata.BuildPacket(&packet))
        player->SendDirectMessage(&packet);

    RemoveFieldNotifyFlag(UF_FLAG_OWNER);
}

bool Unit::IsOwnerOrSelf(Unit* owner) const
{
    if(this == owner)
        return true;
    if(GetDemonCreatorGUID() == owner->GetGUID())
        return true;
    if(GetSummonedByGUID() == owner->GetGUID())
        return true;
    if(GetOwnerGUID() == owner->GetGUID())
        return true;
    if(GetCharmerGUID() == owner->GetGUID())
        return true;
    return false;
}

Unit* Unit::GetOwner() const
{
    if (ObjectGuid ownerid = GetOwnerGUID())
        return ObjectAccessor::GetUnit(*this, ownerid);
    return nullptr;
}

Unit* Unit::GetAnyOwner() const
{
    if (GetCharmerGUID())
        return GetCharmer();
    if (ToTempSummon())
        return ToTempSummon()->GetSummoner();
    if (GetOwner())
        return GetOwner();

    return nullptr;
}

Unit* Unit::GetCharmer() const
{
    if (ObjectGuid charmerid = GetCharmerGUID())
        return ObjectAccessor::GetUnit(*this, charmerid);
    return nullptr;
}

Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself() const
{
    ObjectGuid guid = GetCharmerOrOwnerGUID();
    if (guid.IsPlayer())
        return ObjectAccessor::GetPlayer(*this, guid);

    return const_cast<Player*>(ToPlayer());
}

Player* Unit::GetAffectingPlayer() const
{
    if (!GetCharmerOrOwnerGUID())
        return const_cast<Player*>(ToPlayer());

    if (Unit* owner = GetCharmerOrOwner())
        return owner->GetCharmerOrOwnerPlayerOrPlayerItself();
    return nullptr;
}

Minion *Unit::GetFirstMinion() const
{
    if (ObjectGuid pet_guid = GetMinionGUID())
    {
        if (Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, pet_guid))
            if (pet->HasUnitTypeMask(UNIT_MASK_MINION))
                return static_cast<Minion*>(pet);

        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::GetFirstMinion: Minion %u not exist.", pet_guid.GetGUIDLow());
        const_cast<Unit*>(this)->SetMinionGUID(ObjectGuid::Empty);
    }

    return nullptr;
}

Guardian* Unit::GetGuardianPet() const
{
    if (ObjectGuid pet_guid = GetPetGUID())
    {
        if (Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, pet_guid))
            if (pet->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
                return static_cast<Guardian*>(pet);

        TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit::GetGuardianPet: Guardian " UI64FMTD " not exist.", pet_guid.GetCounter());
        const_cast<Unit*>(this)->SetPetGUID(ObjectGuid::Empty);
    }

    return nullptr;
}

Unit* Unit::GetCharm() const
{
    if (ObjectGuid charm_guid = GetCharmGUID())
    {
        if (Unit* pet = ObjectAccessor::GetUnit(*this, charm_guid))
            return pet;

        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::GetCharm: Charmed creature %u not exist.", charm_guid.GetGUIDLow());
        const_cast<Unit*>(this)->SetGuidValue(UNIT_FIELD_CHARM, ObjectGuid::Empty);
    }

    return nullptr;
}

Unit* Unit::GetCharmerOrOwner() const
{
    return !GetCharmerGUID().IsEmpty() ? GetCharmer() : GetOwner();
}

Unit* Unit::GetCharmerOrOwnerOrSelf() const
{
    if (Unit* u = GetCharmerOrOwner())
        return u;
    return const_cast<Unit*>(this);
}

void Unit::SetMinion(Minion *minion, bool apply)
{
    //TC_LOG_DEBUG(LOG_FILTER_PETS, "SetMinion %u for %u, apply %u", minion->GetEntry(), GetEntry(), apply);

    if (apply)
    {
        if (minion->GetSummonedByGUID())
            return;

        if (minion->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
            minion->SetOwnerGUID(GetGUID());

		if ((minion->GetEntry() != 69792 && minion->GetEntry() != 69791) || sWorld->getBoolConfig(CONFIG_PLAYER_CONTROL_GUARDIAN_PETS))
            m_Controlled.insert(minion->GetGUID());

        if (IsPlayer())
        {
            minion->m_ControlledByPlayer = true;
            minion->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
        }

        // Can only have one pet. If a new one is summoned, dismiss the old one.
        if (minion->IsGuardianPet() && !minion->ToCreature()->m_isHati)
        {
            Guardian* oldPet = nullptr;
            for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
            {
                if (*itr == GetPetGUID())
                {
                    Unit* unit = ObjectAccessor::GetUnit(*this, *itr);
                    if (unit)
                        oldPet = static_cast<Guardian*>(unit);
                    break;
                }
            }
            //if (Guardian* oldPet = GetGuardianPet())
            if (oldPet)
            {
                if (oldPet != minion && (oldPet->isPet() || minion->isPet() || oldPet->GetEntry() != minion->GetEntry()))
                {
                    // remove existing minion pet
                    if (oldPet->isPet())
                        oldPet->ToPet()->Remove();
                    else
                        oldPet->UnSummon();
                    SetPetGUID(minion->GetGUID());
                    SetMinionGUID(ObjectGuid::Empty);
                }
            }
            else
            {
                SetPetGUID(minion->GetGUID());
                SetMinionGUID(ObjectGuid::Empty);
            }
        }

        if (minion->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && !minion->ToCreature()->m_isHati)
            SetGuidValue(UNIT_FIELD_SUMMON, minion->GetGUID());

        if (minion->m_Properties && minion->m_Properties->Title == SUMMON_TYPE_MINIPET)
            SetCritterGUID(minion->GetGUID());

        // PvP, FFAPvP
        minion->SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, GetByteValue(UNIT_FIELD_BYTES_2, 1));

        // FIXME: hack, speed must be set only at follow
        if (IsPlayer() && (minion->isPet() || minion->ToCreature()->m_isHati))
            for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
                minion->SetSpeed(UnitMoveType(i), CalculatePct(m_speed_rate[i], 114.0f), true);

        if (IsPlayer())
        {
            // Send infinity cooldown - client does that automatically but after relog cooldown needs to be set again
            SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL));

            if (spellInfo && (spellInfo->HasAttribute(SPELL_ATTR0_DISABLED_WHILE_ACTIVE)))
                ToPlayer()->AddSpellAndCategoryCooldowns(spellInfo, 0, nullptr, true);
        }
    }
    else
    {
        m_Controlled.erase(minion->GetGUID());

        if (minion->GetOwnerGUID() != GetGUID())
        {
            TC_LOG_FATAL(LOG_FILTER_UNITS, "SetMinion: Minion %u is not the minion of owner %u", minion->GetEntry(), GetEntry());
            return;
        }

        if (minion->m_Properties && minion->m_Properties->Title == SUMMON_TYPE_MINIPET)
        {
            if (GetCritterGUID() == minion->GetGUID())
                SetCritterGUID(ObjectGuid::Empty);
        }

        if (minion->IsGuardianPet())
        {
            if (GetPetGUID() == minion->GetGUID())
                SetPetGUID(ObjectGuid::Empty);
        }
        else if (minion->isTotem())
        {
            // All summoned by totem minions must disappear when it is removed.
            if (SpellInfo const* spInfo = sSpellMgr->GetSpellInfo(minion->ToTotem()->GetSpell()))
            {
                for (int i = 0; i < MAX_SPELL_EFFECTS; ++i)
                {
                    if (spInfo->EffectMask < uint32(1 << i))
                        break;

                    if (spInfo->GetEffect(i, GetSpawnMode())->Effect != SPELL_EFFECT_SUMMON)
                        continue;

                    RemoveAllMinionsByFilter(spInfo->GetEffect(i, GetSpawnMode())->MiscValue);
                }
            }

            if (minion->GetEntry() == 15439)
            {
                if(HasAura(117013))
                    RemoveAllMinionsByFilter(61029);
                else
                    RemoveAllMinionsByFilter(15438);
            }
            else if (minion->GetEntry() == 15430)
            {
                if(HasAura(117013))
                    RemoveAllMinionsByFilter(61056);
                else
                    RemoveAllMinionsByFilter(15352);
            }
            else if (minion->GetEntry() == 77934)
            {
                if(HasAura(117013))
                    RemoveAllMinionsByFilter(77942);
                else
                    RemoveAllMinionsByFilter(77936);
            }
        }

        if (IsPlayer())
        {
            SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL));
            // Remove infinity cooldown
            if (spellInfo && (spellInfo->HasAttribute(SPELL_ATTR0_DISABLED_WHILE_ACTIVE)))
                ToPlayer()->SendCooldownEvent(spellInfo);
        }

        // if (minion->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
        {
            if (RemoveGuidValue(UNIT_FIELD_SUMMON, minion->GetGUID()))
            {
                // Check if there is another minion
                for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
                {
                    Unit* unit = ObjectAccessor::GetUnit(*this, *itr);
                    if (!unit)
                        continue;
                    // do not use this check, creature do not have charm guid
                    //if (GetCharmGUID() == (*itr)->GetGUID())
                    if (GetGUID() == unit->GetCharmerGUID() || unit->ToCreature()->m_isHati)
                        continue;

                    //ASSERT(unit->GetOwnerGUID() == GetGUID());
                    if (unit->GetOwnerGUID() != GetGUID())
                    {
                        OutDebugInfo();
                        unit->OutDebugInfo();
                        continue;
                    }
                    if(!unit->IsCreature())
                        continue;

                    if (!unit->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
                        continue;

                    if (AddGuidValue(UNIT_FIELD_SUMMON, unit->GetGUID()))
                    {
                        // show another pet bar if there is no charm bar
                        if (IsPlayer() && !GetCharmGUID())
                        {
                            if (unit->isPet())
                                ToPlayer()->PetSpellInitialize();
                            else
                                ToPlayer()->CharmSpellInitialize();
                        }
                    }
                    break;
                }
            }
        }
    }

    if (m_Controlled.size() < 5)
    {
        uint32 count = 0;
        for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
        {
            float angle = PET_FOLLOW_ANGLE;
            Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr);
            if (!creature)
                continue;
            
            switch (count)
            {
                case 0:
                    angle = PET_FOLLOW_ANGLE;
                    break;
                case 1:
                    angle = PET_FOLLOW_ANGLE * 3;
                    break;
                case 2:
                    angle = PET_FOLLOW_ANGLE * 2;
                    break;
                case 3:
                    angle = PET_FOLLOW_ANGLE * 2 + PET_FOLLOW_ANGLE / 2;
                    break;
                case 4:
                    angle = PET_FOLLOW_ANGLE + PET_FOLLOW_ANGLE / 2;
                    break;
            }
            creature->SetFollowAngle(angle);
            creature->SetFollowDistance(frand(PET_FOLLOW_DIST/ 2 , PET_FOLLOW_DIST *2));
            count++;
        }
    }
    else
    {
        uint32 count = 0;
        for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
        {
            float angle = 2*PET_FOLLOW_ANGLE/3 + (((PET_FOLLOW_ANGLE * 3) / m_Controlled.size()) * count);
            
            if (Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr))
            {
                creature->SetFollowAngle(angle);
                creature->SetFollowDistance(frand(PET_FOLLOW_DIST/ 3 , PET_FOLLOW_DIST *2.5f));
            }
            count++;
        }
    }
}

Creature* Unit::GetMinionByEntry(uint32 entry)
{
    for (Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end();)
    {
        Unit* unit = ObjectAccessor::GetUnit(*this, *itr);
        ++itr;
        if (unit && unit->IsInWorld())
            if (unit->GetEntry() == entry && unit->IsCreature() && unit->ToCreature()->isSummon()) // minion, actually
                return unit->ToCreature();
    }
    return nullptr;
}

void Unit::GetAllMinionsByEntry(std::list<Creature*>& Minions, uint32 entry)
{
    for (Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end();)
    {
        Unit* unit = ObjectAccessor::GetUnit(*this, *itr);
        ++itr;
        if (unit && unit->IsInWorld())
            if (unit->GetEntry() == entry && unit->IsCreature() && unit->ToCreature()->isSummon()) // minion, actually
                Minions.push_back(unit->ToCreature());
    }
}

void Unit::RemoveAllMinionsByFilter(uint32 ID, uint8 filter /*=0*/) //0 - by entry, 1 - by spell 
{
    if(filter)
    {
        for (Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end();)
        {
            Unit* unit = ObjectAccessor::GetUnit(*this, *itr);
            ++itr;

            if (!unit || !unit->IsInWorld() || unit->GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL) != ID)
                continue;

            if (unit->ToCreature() && unit->ToCreature()->isSummon()) // minion, actually
                unit->ToTempSummon()->UnSummon();
            // i think this is safe because i have never heard that a despawned minion will trigger a same minion
        }
    }
    else
    {
        if(!tempSummonList[ID].empty())
            for (auto const& iter : tempSummonList[ID])
                if(Creature* temp = ObjectAccessor::GetCreature(*this, iter))
                    temp->DespawnOrUnsummon(500);
    }
}

void Unit::SetCharm(Unit* charm, bool apply)
{
    if (apply)
    {
        if (IsPlayer())
        {
            if (!AddGuidValue(UNIT_FIELD_CHARM, charm->GetGUID()))
                TC_LOG_FATAL(LOG_FILTER_UNITS, "Player %s is trying to charm unit %u, but it already has a charmed unit " UI64FMTD "", GetName(), charm->GetEntry(), GetCharmGUID().GetCounter());

            charm->m_ControlledByPlayer = true;
            // TODO: maybe we can use this flag to check if controlled by player
            charm->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
        }
        else
            charm->m_ControlledByPlayer = false;

        // PvP, FFAPvP
        charm->SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, GetByteValue(UNIT_FIELD_BYTES_2, 1));

        if (charm->IsPlayer())
            charm->SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_UNK1);

        if (!charm->AddGuidValue(UNIT_FIELD_CHARMED_BY, GetGUID()))
            TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit %u is being charmed, but it already has a charmer " UI64FMTD "", charm->GetEntry(), charm->GetCharmerGUID().GetCounter());

        _isWalkingBeforeCharm = charm->IsWalking();
        if (_isWalkingBeforeCharm)
        {
            charm->SetWalk(false);
            charm->SendMovementFlagUpdate();
        }

        m_Controlled.insert(charm->GetGUID());
    }
    else
    {
        if (IsPlayer())
        {
            if (!RemoveGuidValue(UNIT_FIELD_CHARM, charm->GetGUID()))
                TC_LOG_FATAL(LOG_FILTER_UNITS, "Player %s is trying to uncharm unit %u, but it has another charmed unit " UI64FMTD "", GetName(), charm->GetEntry(), GetCharmGUID().GetCounter());
        }

        if (!charm->RemoveGuidValue(UNIT_FIELD_CHARMED_BY, GetGUID()))
            TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit %u is being uncharmed, but it has another charmer " UI64FMTD "", charm->GetEntry(), charm->GetCharmerGUID().GetCounter());

        if (charm->IsPlayer())
        {
            charm->m_ControlledByPlayer = true;
            charm->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
            charm->RemoveByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_UNK1);
            charm->ToPlayer()->UpdatePvPState();
        }
        else if (Player* player = charm->GetCharmerOrOwnerPlayerOrPlayerItself())
        {
            charm->m_ControlledByPlayer = true;
            charm->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
            charm->SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, player->GetByteValue(UNIT_FIELD_BYTES_2, 1));
        }
        else
        {
            charm->m_ControlledByPlayer = false;
            charm->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
            charm->SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, 0);
        }

        if (charm->IsWalking() != _isWalkingBeforeCharm)
        {
            charm->SetWalk(_isWalkingBeforeCharm);
            charm->SendMovementFlagUpdate(true); // send packet to self, to update movement state on player.
        }

        if (charm->IsPlayer() || !charm->ToCreature()->HasUnitTypeMask(UNIT_MASK_MINION) || charm->GetOwnerGUID() != GetGUID())
            m_Controlled.erase(charm->GetGUID());
    }
}

int32 Unit::DealHeal(Unit* victim, uint32 addhealth, SpellInfo const* spellProto)
{
    int32 gain = 0;

    if (victim->IsAIEnabled)
    {
        victim->GetAI()->HealReceived(this, addhealth);
        if (InstanceScript* instance = GetInstanceScript())
            if (instance->CheckDamageManager())
                instance->UpdateDamageManager(victim->GetGUID(), -int32(addhealth), true);
    }

    if (IsAIEnabled)
        GetAI()->HealDone(victim, addhealth);

    if (addhealth)
        gain = victim->ModifyHealth(int32(addhealth), this);

    Unit* unit = this;

    if (IsCreature() && (ToCreature()->isTotem() || ToCreature()->GetEntry() == 60849))
        unit = GetOwner();

    if (Player* player = unit->ToPlayer())
    {
        if (Battleground* bg = player->GetBattleground())
            bg->UpdatePlayerScore(player, SCORE_HEALING_DONE, gain);

        // use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria)
        if (gain)
            player->UpdateAchievementCriteria(CRITERIA_TYPE_HEALING_DONE, gain, 0, 0, victim);

        if (addhealth)
            player->UpdateAchievementCriteria(CRITERIA_TYPE_HIGHEST_HEAL_CASTED, addhealth);
    }

    if (IsPlayer() && addhealth > 0 && (!spellProto || spellProto->Id != 146347))
    {
        if (victim != this)
        {
            if (float heal = CalculatePct(addhealth, static_cast<float>(GetFloatValue(PLAYER_FIELD_LIFESTEAL))))
            {
                AddDelayedEvent(10, [heal, this]() -> void
                {
                    if (!this)
                        return;
                    CastCustomSpell(this, 146347, &heal, nullptr, nullptr, true);
                });
            }
        }
    }

    if (Player* player = victim->ToPlayer())
    {
        if (gain)
            player->UpdateAchievementCriteria(CRITERIA_TYPE_TOTAL_HEALING_RECEIVED, gain);
        if (addhealth)
            player->UpdateAchievementCriteria(CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED, addhealth);
    }

    return gain;
}

Unit* Unit::GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo)
{
    // Patch 1.2 notes: Spell Reflection no longer reflects abilities
    if (spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REDIRECTED) || spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) || spellInfo->HasAttribute(SPELL_ATTR4_HIDDEN_SPELLBOOK))
        return victim;

    if (AuraEffectList const* magnetAuras = victim->GetAuraEffectsByType(SPELL_AURA_SPELL_MAGNET))
    for (AuraEffectList::const_iterator itr = magnetAuras->begin(); itr != magnetAuras->end(); ++itr)
    {
        if (Unit* magnet = (*itr)->GetBase()->GetCaster())
            if (spellInfo->CheckExplicitTarget(this, magnet) == SPELL_CAST_OK && spellInfo->CheckTarget(this, magnet, false) == SPELL_CAST_OK && _IsValidAttackTarget(magnet, spellInfo))
            {
                // TODO: handle this charge drop by proc in cast phase on explicit target
                //(*itr)->GetBase()->DropCharge(AURA_REMOVE_BY_DROP_CHARGERS);
                return magnet;
            }
    }
    return victim;
}

Unit* Unit::GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo)
{
    if (auto const* interceptAuras = victim->GetAuraEffectsByType(SPELL_AURA_INTERCEPT_MELEE_RANGED_ATTACKS))
    for (auto i = interceptAuras->cbegin(); i != interceptAuras->cend(); ++i)
        if (auto magnet = (*i)->GetBase()->GetCaster())
            if (_IsValidAttackTarget(magnet, spellInfo) && magnet->IsWithinLOSInMap(this) && (!spellInfo || (spellInfo->CheckExplicitTarget(this, magnet) == SPELL_CAST_OK && spellInfo->CheckTarget(this, magnet, false) == SPELL_CAST_OK)))
                {
                    (*i)->GetBase()->DropCharge(AURA_REMOVE_BY_DROP_CHARGERS);
                    return magnet;
                }

    return victim;
}

Unit* Unit::GetFirstControlled() const
{
    // Sequence: charmed, pet, other guardians
    Unit* unit = GetCharm();
    if (!unit)
        if (ObjectGuid guid = GetMinionGUID())
            unit = ObjectAccessor::GetUnit(*this, guid);

    return unit;
}

void Unit::RemoveAllControlled()
{
    // possessed pet and vehicle
    if (IsPlayer())
    {
        ToPlayer()->StopCastingCharm();
        ToPlayer()->UnsummonPetTemporaryIfAny();
    }

    while (!m_Controlled.empty())
    {
        Unit* target = ObjectAccessor::GetUnit(*this, *m_Controlled.begin());
        m_Controlled.erase(m_Controlled.begin());
        if(!target || !target->IsInWorld())
            continue;

        if (target->GetCharmerGUID() == GetGUID())
            target->RemoveCharmAuras();
        else if (target->GetOwnerGUID() == GetGUID() && target->isSummon())
            target->ToTempSummon()->UnSummon();
        // else
            // TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit %u is trying to release unit %u which is neither charmed nor owned by it", GetEntry(), target->GetEntry());
    }

    if (getClass() == CLASS_MONK && (m_SummonSlot[3] || m_SummonSlot[4]))
    {
        for (uint8 i = 3; i < MAX_TOTEM_SLOT; ++i)
            if (m_SummonSlot[i])
                if (Creature* crt = GetMap()->GetCreature(m_SummonSlot[i]))
                    if (TempSummon* tempsum = crt->ToTempSummon())
                        tempsum->UnSummon();
    }

    // Cleaning temporary summons
    if (IsPlayer())
    {
        if (!tempSummonList.empty())
            for (std::map<uint32, GuidList>::iterator itr = tempSummonList.begin(); itr != tempSummonList.end(); ++itr)
                for (GuidList::const_iterator iter = itr->second.begin(); iter != itr->second.end(); ++iter)
                    if (Creature* creature = ObjectAccessor::GetCreature(*this, (*iter)))
                        if (TempSummon* tempSum = creature->ToTempSummon())
                            if (tempSum->m_Properties->Flags & SUMMON_PROP_FLAG_UNK4) //Despawn flag?
                                tempSum->UnSummon(100);
    }

    // if (GetPetGUID())
        // TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit %u is not able to release its pet " UI64FMTD, GetEntry(), GetPetGUID().GetCounter());
    // if (GetMinionGUID())
        // TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit %u is not able to release its minion " UI64FMTD, GetEntry(), GetMinionGUID().GetCounter());
    // if (GetCharmGUID())
        // TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit %u is not able to release its charm " UI64FMTD, GetEntry(), GetCharmGUID().GetCounter());
}

void Unit::TeleportAllControlled()
{
    auto controlled = m_Controlled;
    for (ControlList::const_iterator itr = controlled.begin(); itr != controlled.end(); ++itr)
    {
        Unit* target = ObjectAccessor::GetUnit(*this, *itr);
        if(!target)
        {
            m_Controlled.erase(*itr);
            continue;
        }

        if(target->IsOnVehicle())
            continue;

        target->m_Teleports = true;
        target->CleanupBeforeTeleport();
        target->ResetMap();
    }

    // Cleaning temporary summons
    if (IsPlayer())
    {
        if (!tempSummonList.empty())
            for (std::map<uint32, GuidList>::iterator itr = tempSummonList.begin(); itr != tempSummonList.end(); ++itr)
                for (GuidList::const_iterator iter = itr->second.begin(); iter != itr->second.end(); ++iter)
                    if (Creature* creature = ObjectAccessor::GetCreature(*this, (*iter)))
                    {
                        if (creature->IsOnVehicle())
                            continue;
                        creature->m_Teleports = true;
                        creature->CleanupBeforeTeleport();
                        creature->ResetMap();
                    }
    }
}

void Unit::RestoreAllControlled()
{
    auto controlled = m_Controlled;
    for (ControlList::const_iterator itr = controlled.begin(); itr != controlled.end(); ++itr)
    {
        Unit* target = ObjectAccessor::GetUnit(*this, *itr);
        if(!target)
        {
            m_Controlled.erase(*itr);
            continue;
        }

        if(target->IsOnVehicle())
            continue;

        target->SetMap(GetMap());
        if (Creature* creature = target->ToCreature())
            GetMap()->AddToMap(creature);
        target->m_Teleports = false;
    }

    // Cleaning temporary summons
    if (IsPlayer())
    {
        if (!tempSummonList.empty())
            for (std::map<uint32, GuidList>::iterator itr = tempSummonList.begin(); itr != tempSummonList.end(); ++itr)
                for (GuidList::const_iterator iter = itr->second.begin(); iter != itr->second.end(); ++iter)
                    if (Creature* creature = ObjectAccessor::GetCreature(*this, (*iter)))
                    {
                        if (creature->IsOnVehicle())
                            continue;
                        creature->SetMap(GetMap());
                        GetMap()->AddToMap(creature->ToCreature());
                        creature->m_Teleports = false;
                    }
    }
}

bool Unit::isCharmed() const
{
    return !GetCharmerGUID().IsEmpty();
}

Unit* Unit::GetNextRandomRaidMemberOrPet(float radius)
{
    Player* player = nullptr;
    if (IsPlayer())
        player = ToPlayer();
    // Should we enable this also for charmed units?
    else if (IsCreature() && ToCreature()->isPet())
        player = GetOwner()->ToPlayer();

    if (!player)
        return nullptr;
    Group* group = player->GetGroup();
    // When there is no group check pet presence
    if (!group)
    {
        // We are pet now, return owner
        if (player != this)
            return IsWithinDistInMap(player, radius) ? player : nullptr;
        Unit* pet = GetGuardianPet();
        // No pet, no group, nothing to return
        if (!pet)
            return nullptr;
        // We are owner now, return pet
        return IsWithinDistInMap(pet, radius) ? pet : nullptr;
    }

    std::vector<Unit*> nearMembers;
    // reserve place for players and pets because resizing vector every unit push is unefficient (vector is reallocated then)
    nearMembers.reserve(group->GetMembersCount() * 2);

    for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
        if (Player* Target = itr->getSource())
        {
            // IsHostileTo check duel and controlled by enemy
            if (Target != this && Target->isAlive() && IsWithinDistInMap(Target, radius) && !IsHostileTo(Target))
                nearMembers.push_back(Target);

        // Push player's pet to vector
        if (Unit* pet = Target->GetGuardianPet())
            if (pet != this && pet->isAlive() && IsWithinDistInMap(pet, radius) && !IsHostileTo(pet))
                nearMembers.push_back(pet);
        }

    if (nearMembers.empty())
        return nullptr;

    uint32 randTarget = urand(0, nearMembers.size()-1);
    return nearMembers[randTarget];
}

// only called in Player::SetSeer
// so move it to Player?
void Unit::AddPlayerToVision(Player* player)
{
    if (m_sharedVision.empty())
    {
        setActive(true);
        SetWorldObject(true);
    }
    m_sharedVision.push_back(player);
}

// only called in Player::SetSeer
void Unit::RemovePlayerFromVision(Player* player)
{
    m_sharedVision.remove(player);
    if (m_sharedVision.empty())
    {
        setActive(false);
        SetWorldObject(false);
    }
}

void Unit::RemoveBindSightAuras()
{
    RemoveAurasByType(SPELL_AURA_BIND_SIGHT);
}

void Unit::RemoveCharmAuras()
{
    RemoveAurasByType(SPELL_AURA_MOD_CHARM);
    RemoveAurasByType(SPELL_AURA_MOD_POSSESS);
    RemoveAurasByType(SPELL_AURA_AOE_CHARM);
}

void Unit::UnsummonAllTotems()
{
    for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
    {
        if (!m_SummonSlot[i])
            continue;

        if (Creature* OldTotem = GetMap()->GetCreature(m_SummonSlot[i]))
            if (OldTotem->isSummon())
                OldTotem->ToTempSummon()->UnSummon();
    }
}

void Unit::SendHealSpellLog(Unit* victim, uint32 spellID, uint32 health, uint32 overHeal, uint32 absorbed, bool crit)
{
    WorldPackets::CombatLog::SpellHealLog spellHealLog;
    spellHealLog.TargetGUID = victim->GetGUID();
    spellHealLog.CasterGUID = GetGUID();
    spellHealLog.SpellID = spellID;
    spellHealLog.Health = health;
    spellHealLog.OverHeal = overHeal;
    spellHealLog.Absorbed = absorbed;
    spellHealLog.Crit = crit;
    spellHealLog.LogData.Initialize(victim);

    WorldPackets::Spells::SandboxScalingData sandboxScalingData;
    if (sandboxScalingData.GenerateDataForUnits(this, victim))
        spellHealLog.SandboxScaling = sandboxScalingData;

    SendCombatLogMessage(&spellHealLog);
}

int32 Unit::HealBySpell(Unit* victim, SpellInfo const* spellInfo, uint32 addHealth, bool critical)
{
    uint32 absorb = 0;
    int32 gain = 0;
    // calculate heal absorb and reduce healing
    CalcHealAbsorb(victim, spellInfo, addHealth, absorb);

    if (spellInfo)
    {
        gain = DealHeal(victim, addHealth, spellInfo);
        SendHealSpellLog(victim, spellInfo->Id, addHealth, uint32(addHealth - gain), absorb, critical);
    }

    return gain;
}

void Unit::SendEnergizeSpellLog(Unit* victim, uint32 spellID, int32 damage, int32 overEnergize, Powers powerType)
{
    WorldPackets::CombatLog::SpellEnergizeLog data;
    data.CasterGUID = GetGUID();
    data.TargetGUID = victim->GetGUID();
    data.SpellID = spellID;
    data.Type = powerType;
    data.Amount = damage;
    data.OverEnergize = overEnergize;
    data.LogData.Initialize(victim);
    SendCombatLogMessage(&data);
}

void Unit::EnergizeBySpell(Unit* victim, uint32 spellID, int32 damage, Powers powerType)
{
    int32 gain = victim->ModifyPower(powerType, damage);
    SendEnergizeSpellLog(victim, spellID, damage, damage - gain, powerType);
    victim->getHostileRefManager().threatAssist(this, float(damage) * 0.05f, sSpellMgr->GetSpellInfo(spellID));
}

uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, std::vector<uint32>& ExcludeAuraList, SpellEffIndex effIndex, uint32 stack)
{
    if (!spellProto || !victim || damagetype == DIRECT_DAMAGE)
        return pdamage;

    // Some spells don't benefit from done mods
    if (spellProto->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS) || spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS))
        return pdamage;

    // For totems get damage bonus from owner
    if (IsCreature() && ToCreature()->isTotem())
        if (Unit* owner = GetOwner())
            return owner->SpellDamageBonusDone(victim, spellProto, pdamage, damagetype, ExcludeAuraList, effIndex);

    // Done total percent damage auras
    float DoneTotalMod = 1.0f;
    int32 DoneTotal = 0;
    float tmpDamage = 0.0f;

    // Chaos Bolt - 116858
    // damage is increased by your critical strike chance
    if (IsPlayer() && spellProto && (spellProto->Id == 116858 || spellProto->Id == 215279))
    {
        float crit_chance = GetFloatValue(PLAYER_FIELD_SPELL_CRIT_PERCENTAGE/* + GetFirstSchoolInMask(spellProto->GetSchoolMask())*/);
        int32 modif = victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
        if(modif < -100)
            crit_chance -= 50.0f;
        AddPct(DoneTotalMod, crit_chance);
    }

    // Lava Burst - 51505
    if (IsPlayer() && spellProto && (spellProto->Id == 51505 || spellProto->Id == 77451))
    {
        if (Aura* aura = GetAura(114050)) // Ascendance
        {
            float crit_mod = GetFloatValue(PLAYER_FIELD_SPELL_CRIT_PERCENTAGE);
            int32 modif = victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
            if (modif < -100)
                crit_mod -= 50.0f;
            AddPct(DoneTotalMod, crit_mod);
        }
    }

    // Pet damage?
    if (IsCreature() && !ToCreature()->isPet())
        DoneTotalMod *= ToCreature()->GetSpellDamageMod(ToCreature()->GetCreatureTemplate()->Classification);

    Player* spellModOwner = GetSpellModOwner();
    if (spellModOwner)
        AddPct(DoneTotalMod, spellModOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY) + spellModOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY_BONUS));

    AuraEffectList mModDamagePercentDone;
    GetTotalNotStuckAuraEffectByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, mModDamagePercentDone, ExcludeAuraList);
    if (mModDamagePercentDone.begin() != mModDamagePercentDone.end())
    {
        float best_bonus = 1.0f;
        bool firstCheck = false;

        for (uint8 j = SPELL_SCHOOL_NORMAL; j < MAX_SPELL_SCHOOL; ++j)
        {
            if (spellProto->GetSchoolMask() & (1 << j))
            {
                float temp = 1.0f;
                for (AuraEffectList::iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i)
                {
                    if ((*i)->GetSpellInfo()->AuraRestrictions.CasterAuraState && !HasAuraState(AuraStateType((*i)->GetSpellInfo()->AuraRestrictions.CasterAuraState)))
                        continue;

                    if (ToPlayer() && ToPlayer()->HasItemFitToSpellRequirements((*i)->GetSpellInfo()) && (*i)->GetSpellInfo()->EquippedItemClass != -1 && spellProto->Categories.DefenseType == SPELL_DAMAGE_CLASS_MELEE)
                        AddPct(temp, (*i)->GetAmount());
                    else if ((*i)->GetMiscValue() & (1 << j))
                    {
                        if ((*i)->GetSpellInfo()->EquippedItemClass == -1)
                            AddPct(temp, (*i)->GetAmount());
                        else if ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)
                            AddPct(temp, (*i)->GetAmount());
                    }
                }

                if (best_bonus < temp || !firstCheck)
                {
                    best_bonus = temp;
                    firstCheck = true;
                }
            }
        }
        DoneTotalMod *= best_bonus;
    }

    // Mastery: Combo Strikes
    if (getClass() == CLASS_MONK)
        if (AuraEffect* aurEff = GetAuraEffect(115636, EFFECT_0))
            if (aurEff->GetOldBaseAmount())
                AddPct(DoneTotalMod, aurEff->GetAmount());

    uint32 creatureTypeMask = victim->GetCreatureTypeMask();
    // Add flat bonus from spell damage versus
    DoneTotal += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, creatureTypeMask);
    if (AuraEffectList const* mDamageDoneVersus = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS))
    for (AuraEffectList::const_iterator i = mDamageDoneVersus->begin(); i != mDamageDoneVersus->end(); ++i)
        if (creatureTypeMask & uint32((*i)->GetMiscValue()))
            AddPct(DoneTotalMod, (*i)->GetAmount());

    if (AuraEffectList const* mDamagePctDoneByMech = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE_BY_TARGET_AURA_MECHANIC))
    for (AuraEffectList::const_iterator i = mDamagePctDoneByMech->begin(); i != mDamagePctDoneByMech->end(); ++i)
        if (victim->HasAurasWithMechanic(1 << (*i)->GetMiscValue()))
            AddPct(DoneTotalMod, (*i)->GetAmount());

    // bonus against aurastate
    if (AuraEffectList const* mDamageDoneVersusAurastate = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS_AURASTATE))
    for (AuraEffectList::const_iterator i = mDamageDoneVersusAurastate->begin(); i != mDamageDoneVersusAurastate->end(); ++i)
        if (victim->HasAuraState(AuraStateType((*i)->GetMiscValue())))
            if (HasAura(144421) && GetPower(POWER_ALTERNATE))
            {
                int32 pos = GetPower(POWER_ALTERNATE);
                int32 mod = 0;
                switch (pos)
                {
                case 25:
                    mod = -10;
                    break;
                case 50:
                    mod = -25;
                    break;
                case 75:
                    mod = -50;
                    break;
                case 100:
                    mod = -75;
                    break;
                default:
                    mod = 0;
                    break;
                }
                AddPct(DoneTotalMod, mod);
            }
            else
                AddPct(DoneTotalMod, (*i)->GetAmount());

    // Add SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC percent bonus
    if (spellProto->Categories.Mechanic)
        AddPct(DoneTotalMod, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC, spellProto->Categories.Mechanic));
    else if (spellProto->Effects[effIndex]->Mechanic)
        AddPct(DoneTotalMod, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC, spellProto->Effects[effIndex]->Mechanic));

    // Custom scripted damage
    switch (spellProto->ClassOptions.SpellClassSet)
    {
        case SPELLFAMILY_MAGE:
        {
            // Torment the weak
            if (spellProto->GetSchoolMask() & SPELL_SCHOOL_MASK_ARCANE)
            {
                if (victim->HasAuraWithMechanic((1 << MECHANIC_SNARE) | (1 << MECHANIC_COMBAT_SLOW)))
                {
                    if (AuraEffectList const* mDumyAuras = GetAuraEffectsByType(SPELL_AURA_DUMMY))
                        for (AuraEffectList::const_iterator i = mDumyAuras->begin(); i != mDumyAuras->end(); ++i)
                        {
                            if ((*i)->GetSpellInfo()->GetMisc(m_spawnMode)->MiscData.IconFileDataID == 236226)
                            {
                                AddPct(DoneTotalMod, (*i)->GetAmount());
                                break;
                            }
                        }
                }
            }
            break;
        }
        case SPELLFAMILY_WARLOCK:
        {
            // Shadow Bite (30% increase from each dot)
            if (spellProto->ClassOptions.SpellClassMask[1] & 0x00400000 && isPet())
                if (uint8 count = victim->GetDoTsByCaster(GetOwnerGUID()))
                    AddPct(DoneTotalMod, 30 * count);
            break;
        }
    }

    // Done fixed damage bonus auras
    int32 DoneAdvertisedBenefit = GetSpellPowerDamage(spellProto->GetSchoolMask());
    // Pets just add their bonus damage to their spell damage
    // note that their spell damage is just gain of their own auras
    if (HasUnitTypeMask(UNIT_MASK_GUARDIAN) && spellProto->GetMisc(m_spawnMode)->MiscData.SchoolMask & SPELL_SCHOOL_MASK_MAGIC)
        DoneAdvertisedBenefit += static_cast<Guardian*>(this)->GetBonusDamage();

    SpellEffectInfo const* effectInfo = spellProto->GetEffect(effIndex, m_spawnMode);
    // Check for table values
    float SPDCoeffMod = effectInfo->BonusCoefficient;
    float ApCoeffMod = effectInfo->BonusCoefficientFromAP;

    bool calcSPDBonus = SPDCoeffMod > 0;
    SpellBonusEntry const* bonus = sSpellMgr->GetSpellBonusData(spellProto->Id);
    if (bonus)
    {
        SPDCoeffMod = damagetype == DOT ? bonus->dot_damage : bonus->direct_damage;
        ApCoeffMod = damagetype == DOT ? bonus->ap_dot_bonus : bonus->ap_bonus;

        calcSPDBonus = SPDCoeffMod > 0;

        if (getClass() == CLASS_MONK && bonus->damage_bonus)
        {
            DoneTotal += CalculateMonkSpellDamage(bonus->damage_bonus);
            ApCoeffMod = 0.0f;
            calcSPDBonus = false;
        }
    }

    if (ApCoeffMod > 0)
    {
        //code for bonus AP from dbc
        WeaponAttackType attType;

        if (Player* plr = ToPlayer())
            attType = plr->getClass() == CLASS_HUNTER ? RANGED_ATTACK: BASE_ATTACK;
        else
            attType = (spellProto->IsRangedWeaponSpell() && spellProto->Categories.DefenseType == SPELL_DAMAGE_CLASS_RANGED) ? RANGED_ATTACK : BASE_ATTACK;

        float APbonus = float(victim->GetTotalAuraModifier(attType == BASE_ATTACK ? SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS : SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS));
        APbonus += GetTotalAttackPowerValue(attType);

        if (calcSPDBonus)
            calcSPDBonus = DoneAdvertisedBenefit > APbonus;

        if (!calcSPDBonus)
        {
            DoneTotal += int32(stack * ApCoeffMod * APbonus);

            if (damagetype == DOT)
                TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Unit::SpellDamageBonusDone DOT DoneTotal %i, APbonus %f", DoneTotal, APbonus);
            else
                TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Unit::SpellDamageBonusDone !DOT DoneTotal %i, APbonus %f", DoneTotal, APbonus);
        }
    }
    // Default calculation
    if (calcSPDBonus)
    {
        if (spellModOwner)
        {
            SPDCoeffMod *= 100.0f;
            spellModOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, SPDCoeffMod);
            SPDCoeffMod /= 100.0f;
        }
        DoneTotal += int32(DoneAdvertisedBenefit * SPDCoeffMod * stack);

        if (damagetype == DOT)
            TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Unit::SpellDamageBonusDone DOT DoneTotal %i, SPDCoeffMod %f", DoneTotal, SPDCoeffMod);
        else
            TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Unit::SpellDamageBonusDone !DOT DoneTotal %i, SPDCoeffMod %f", DoneTotal, SPDCoeffMod);
    }

    if (Unit* owner2 = GetAnyOwner())
        AddPct(DoneTotalMod, owner2->GetTotalAuraModifier(SPELL_AURA_PET_DAMAGE_DONE_PCT));

    // 77220 - Mastery: Chaotic Energies
    if (IsPlayer() && spellProto->ClassOptions.SpellClassSet == SPELLFAMILY_WARLOCK && ToPlayer()->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == SPEC_WARLOCK_DESTRUCTION)
    {
        if (AuraEffect const* aurEff = GetAuraEffect(77220, EFFECT_0))
        {
            uint32 _perc = urand(0, aurEff->GetAmount());
            if (spellProto->Id == 116858 && HasAura(242296)) // Item - Warlock T20 Destruction 4P Bonus
                _perc = aurEff->GetAmount();

            AddPct(DoneTotalMod, _perc);
        }
    }

    if (effectInfo->PvPMultiplier && CanPvPScalar())
        DoneTotalMod *= effectInfo->PvPMultiplier;

    tmpDamage = (int32(pdamage) + DoneTotal) * DoneTotalMod;

    // apply spellmod to Done damage (flat and pct)
    if (damagetype != DOT)
    {
        tmpDamage *= SpellPctDone(victim, spellProto, effIndex);

        // apply spellmod to Done damage (flat and pct)
        if (spellModOwner)
            spellModOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage);
    }

    // TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SpellDamageBonusDone spellid %u in effIndex %u tmpDamage %f, pdamage %i DoneTotalMod %f DoneTotal %i", spellProto ? spellProto->Id : 0, effIndex, tmpDamage, pdamage, DoneTotalMod, DoneTotal);

    return uint32(std::max(tmpDamage, 0.0f));
}

bool Unit::CanPvPScalar()
{
    if (Player* player = ToPlayer())
        if (player->HasPvpRulesEnabled())
            return true;

    if (Unit* owner = GetAnyOwner())
        if (Player* player = owner->ToPlayer())
            if (player->HasPvpRulesEnabled())
                return true;

    return false;
}

float Unit::GetProcStatsMultiplier(uint32 spellId) const
{
    float mod = 1.f;

    if (auto const* mTotalAuraList = GetAuraEffectsByType(SPELL_AURA_MOD_ITEM_STATS_AND_PROCK_BY_PCT_WITH_MASK))
    {
        for (auto const& auraEffect : *mTotalAuraList)
        {
            if (auraEffect->GetMiscValue() != 3)
                continue;

            switch (auraEffect->GetMiscValueB())
            {
                case 228:
                {
                    switch (spellId)
                    {
                        case 230011: // Cruel Garrote
                        case 230261: // Flame Wreath
                        case 230017: // Holy Bolt
                        case 230224: // Thunder Ritual
                        case 230090: // Intangible Presence
                        case 230140: // Loose Mana
                        case 230215: // Flame Gale
                        case 230242: // Volatile Energy
                        case 230152: // Legion's Gaze
                        case 230102: // Quite Satisfied
                        case 230103: // Quite Satisfied
                        case 230104: // Quite Satisfied
                        case 230105: // Quite Satisfied
                        case 230121: // Guardian's Familiar
                        {
                            AddPct(mod, auraEffect->GetAmount());
                        }
                    }
                    break;
                }
                case 332:
                {
                    switch (spellId)
                    {
                        case 252088: // Light Speed
                        case 252091: // Master of Shadows
                        case 252202: // Murderous Intent
                        case 252208: // Refractive Shell
                        case 252801: // Shocklight
                        case 252879: // Shadowbind
                        case 252896: // Chaotic Darkness
                        case 252897: // Chaotic Darkness
                        case 252907: // Torment the Weak
                        case 253022: // Dark Sorrows
                        case 253072: // Secure in the Light
                        case 253073: // Secure in the Light
                        case 253098: // Infusion of Light
                        case 253099: // Infusion of Light
                        case 253216: // Light's Embrace
                        {
                            AddPct(mod, auraEffect->GetAmount());
                        }
                    }
                    break;
                }
            }
        }
    }

    return mod;
}

void Unit::SetOverrideAutoattack(uint32 spellId, uint8 attackType)
{
    m_overrideAutoattack[attackType] = spellId;
}

uint32 Unit::GetAutoattackSpellId(uint8 attackType) const
{
    return m_overrideAutoattack[attackType];
}

float Unit::SpellPctDone(Unit* victim, SpellInfo const* spellProto, SpellEffIndex effIndex)
{
    if (!spellProto || !victim)
        return 1.0f;

    if ((spellProto->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) || (spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)))
        return 1.0f;

    if (IsCreature() && isTotem())
        if (Unit* owner = GetOwner())
            return owner->SpellPctDone(victim, spellProto);

    float DoneTotalMod = 1.0f;

    CalculateFromDummy(victim, DoneTotalMod, spellProto, (1<<effIndex));

    if (getClass() == CLASS_PALADIN)
        if (RequiresCurrentSpellsToHolyPower(spellProto))
            DoneTotalMod *= GetModForHolyPowerSpell();

    return DoneTotalMod;
}

void Unit::SpellDotPctDone(Unit* victim, SpellInfo const* spellProto, uint32 effIndex, float& DoneTotalMod, float& DoneTotalAdd, bool onapply)
{
    if (!spellProto || !victim)
        return;

    if ((spellProto->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) || (spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)))
        return;

    if (IsCreature() && isTotem())
        if (Unit* owner = GetOwner())
            return owner->SpellDotPctDone(victim, spellProto, effIndex, DoneTotalMod, DoneTotalAdd, onapply);

    // apply spellmod to Done damage (flat and pct)
    if (Player* modOwner = GetSpellModOwner())
        modOwner->ApplyDotMod(spellProto->Id, SPELLMOD_DOT, DoneTotalMod, DoneTotalAdd, onapply);

    if (onapply)
    {
        CalculateFromDummy(victim, DoneTotalMod, spellProto, (1<<effIndex));

        if (getClass() == CLASS_PALADIN)
            if (RequiresCurrentSpellsToHolyPower(spellProto))
                DoneTotalMod *= GetModForHolyPowerSpell();
    }
}

uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 pdamage)
{
    if (!spellProto)
        return pdamage;

    // Some spells don't benefit from done mods
    if (caster->IsPlayer())
        if (spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS))
            return pdamage;

    int32 TakenTotal = 0;
    float TakenTotalMod = 1.0f;
    SpellSchoolMask schoolMask = spellProto->GetSchoolMask();

    TakenTotalMod *= caster->GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_TAKEN_FROM_CASTER, [=](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->GetCasterGUID() == GetGUID() && (aurEff->GetMiscValue() & schoolMask))
            return true;
        return false;
    });

    Player* spellModOwner = GetSpellModOwner();
    if (spellModOwner)
        pdamage -= CalculatePct(pdamage, (spellModOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY) + spellModOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY_BONUS)) / 2.f);

    // from positive and negative SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN
    // multiplicative bonus, for example Dispersion + Shadowform (0.10*0.85=0.085)

    float bestVal = 1.0f;
    bool firstCheck = false;

    for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
        if (schoolMask & (1 << i))
        {
            float amount = GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, (1 << i));
            if (bestVal < amount || !firstCheck)
            {
                bestVal = amount;
                firstCheck = true;
            }
        }

    TakenTotalMod *= bestVal;

    bestVal = 1.0f;
    firstCheck = false;

    for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
        if (schoolMask & (1 << i))
        {
            float amount = 1.0f;

            amount *= GetTotalAuraMultiplier(SPELL_AURA_INCREASE_SCHOOL_DAMAGE_TAKEN, [=](AuraEffect const* aurEff) -> bool
            {
                if (aurEff->GetCasterGUID() == caster->GetGUID() && (aurEff->GetMiscValue() & (1 << i)))
                    return true;
                return false;
            });

            if (bestVal < amount || !firstCheck)
            {
                bestVal = amount;
                firstCheck = true;
            }
        }

    TakenTotalMod *= bestVal;

    TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_FROM_CASTER, [=](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->GetCasterGUID() == caster->GetGUID() && aurEff->IsAffectingSpell(spellProto))
            return true;
        return false;
    });

    if (uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask())
    {
        TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT, [mechanicMask](AuraEffect const* aurEff) -> bool
        {
            if (mechanicMask & uint32(1 << (aurEff->GetMiscValue())))
                return true;
            return false;
        });
    }

    int32 bestVal1 = 0;
    firstCheck = false;

    for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
        if (schoolMask & (1 << i))
        {
            int32 amount = SpellBaseDamageBonusTaken(SpellSchoolMask(1 << i));
            if (bestVal1 < amount || !firstCheck)
            {
                bestVal1 = amount;
                firstCheck = true;
            }
        }

    TakenTotal += bestVal1;

    float tmpDamage = (float(pdamage) + TakenTotal) * TakenTotalMod;

    // TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SpellDamageBonusTaken spellid %u tmpDamage %f, pdamage %i TakenTotalMod %f TakenTotal %i", spellProto ? spellProto->Id : 0, tmpDamage, pdamage, TakenTotalMod, TakenTotal);

    return uint32(std::max(tmpDamage, 0.0f));
}

int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) const
{
    int32 DoneAdvertisedBenefit = 0;

    if (AuraEffectList const* mDamageDone = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE))
    for (AuraEffectList::const_iterator i = mDamageDone->begin(); i != mDamageDone->end(); ++i)
        if (((*i)->GetMiscValue() & schoolMask) != 0 &&
        (*i)->GetSpellInfo()->EquippedItemClass == -1 &&          // -1 == any item class (not wand then)  
        (*i)->GetSpellInfo()->EquippedItemInventoryTypeMask == 0) //  0 == any inventory type (not wand then)            
            DoneAdvertisedBenefit += (*i)->GetAmount();

    if (IsPlayer())
    {
        // Base value
        DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus();

        // Check if we are ever using mana - PaperDollFrame.lua
        if (GetPowerIndex(POWER_MANA) != MAX_POWERS)
            DoneAdvertisedBenefit += std::max(0, int32(GetStat(STAT_INTELLECT)));  // spellpower from intellect

        // Spell power from SPELL_AURA_MOD_SPELL_POWER_PCT
        DoneAdvertisedBenefit *= GetTotalAuraMultiplier(SPELL_AURA_MOD_SPELL_POWER_PCT);

        // Damage bonus from stats
        if (AuraEffectList const* mDamageDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT))
            for (AuraEffectList::const_iterator i = mDamageDoneOfStatPercent->begin(); i != mDamageDoneOfStatPercent->end(); ++i)
                if ((*i)->GetMiscValue() & schoolMask)
                    DoneAdvertisedBenefit += int32(CalculatePct(GetStat(Stats((*i)->GetMiscValueB())), (*i)->GetAmount()));
    }

    return DoneAdvertisedBenefit;
}

int32 Unit::SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask)
{
    return GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DAMAGE_TAKEN, schoolMask);
}

int32 Unit::GetSpellPowerDamage(SpellSchoolMask schoolMask)
{
    switch (getClass())
    {
        case CLASS_HUNTER:
        case CLASS_ROGUE:
        case CLASS_WARRIOR:
        case CLASS_DEATH_KNIGHT:
        case CLASS_DEMON_HUNTER:
            return 0;
        default:
            break;
    }

    if (!IsPlayer())
        return 0;

    int32 SPD = 0;
    for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
    {
        int32 val = ToPlayer()->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i);
        if (SPD > val || !SPD)
            SPD = val;

        if ((1 << i) & schoolMask && schoolMask != SPELL_SCHOOL_MASK_NORMAL)
            return val;
    }

    return SPD;
}

int32 Unit::GetSpellPowerHealing()
{
    if (Player* plr = ToPlayer())
        return plr->GetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS);

    return 0;
}

bool Unit::isSpellCrit(Unit* victim, SpellInfo const* spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType, float &critChance, Spell* spell) const
{
    Unit* owner = GetAnyOwner();
    //! Mobs can't crit with spells. Players, Pets, Totems can
    if (ToCreature() && !(owner && owner->IsPlayer()))
        return false;

    float crit_chance = 0.0f;
    // Pets have 100% of owner's crit_chance
    if (isAnySummons() && owner)
    {
        if (Player* plr = owner->ToPlayer())
            crit_chance = plr->GetFloatValue(PLAYER_FIELD_SPELL_CRIT_PERCENTAGE);
    }

    switch (spellProto->Categories.DefenseType)
    {
        case SPELL_DAMAGE_CLASS_NONE:
        case SPELL_DAMAGE_CLASS_MAGIC:
        {
            if (schoolMask == SPELL_SCHOOL_MASK_ALL && IsPlayer())
            {
                float maxCrit = 0.0f;
                for (int8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
                    maxCrit = std::max(maxCrit, GetFloatValue(PLAYER_FIELD_SPELL_CRIT_PERCENTAGE + i));
                crit_chance = maxCrit;
            }
            // else if (schoolMask & SPELL_SCHOOL_MASK_NORMAL)
                // crit_chance = 0.0f;
            // For other schools
            else if (IsPlayer())
                crit_chance = GetFloatValue(PLAYER_FIELD_SPELL_CRIT_PERCENTAGE/* + GetFirstSchoolInMask(schoolMask)*/);
            else if(!isAnySummons() || !owner)
                crit_chance = static_cast<float>(m_baseSpellCritChance);
            // taken
            if (victim)
            {
                if (!spellProto->IsPositive())
                {
                    crit_chance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE, schoolMask);
                    crit_chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
                }

                crit_chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [=](AuraEffect const* aurEff) -> bool
                {
                    return (aurEff->GetCasterGUID() == GetGUID() && aurEff->IsAffectingSpell(spellProto));
                });

                // scripted (increase crit chance ... against ... target by x%
                if (AuraEffectList const* mOverrideClassScript = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS))
                for (AuraEffectList::const_iterator i = mOverrideClassScript->begin(); i != mOverrideClassScript->end(); ++i)
                {
                    if (!((*i)->IsAffectingSpell(spellProto)))
                        continue;

                    switch ((*i)->GetMiscValue())
                    {
                        // Shatter
                        case  911:
                        {
                            bool isFrozen = false;

                            if (spellProto->Id == 228598)
                                isFrozen = m_anyDataContainer.Exist("isFrozenTarget");

                            if (!isFrozen && !victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this) && !victim->HasAura(228358, GetGUID()))
                                break;

                            crit_chance *= 1.5; // Multiplies the critical strike chance of all your spells against frozen targets by 1.5
                            crit_chance += 25.0f; // plus an additional 25%

                            if (AuraEffect const* aurEff = GetAuraEffect(231582, EFFECT_0)) // Shatter
                                crit_chance += aurEff->GetAmount();

                            break;
                        }
                        default:
                            break;
                    }
                }
                // Custom crit by class
                switch (spellProto->ClassOptions.SpellClassSet)
                {
                    case SPELLFAMILY_DRUID:
                    {
                        switch (spellProto->Id)
                        {
                            case 8936: // Regrowth
                            case 5185: // Healing Touch
                            {
                                if (spell)
                                {
                                    for (auto itr : spell->GetSpellScripts())
                                    {
                                        if (itr->CallSpecialFunction())
                                            return true;
                                    }
                                }
                                break;
                            }
                        }

						if (victim)
							if (HasAura(213553) && int32(victim->GetHealthPct()) >= 80) // Druid Initiation PvP Talent
								crit_chance += 30;

                        break;
                    }
                    case SPELLFAMILY_PALADIN:
                    {
                        switch (spellProto->Id)
                        {
                            case 25912: ///< Holy Shock (damage)
                            case 25914: ///< Holy Shock (heal)
                                crit_chance *= 2.0f;
                                break;
                            default:
                                break;
                        }
                        break;
                    }
                    case SPELLFAMILY_WARLOCK:
                    {
                        switch (spellProto->Id)
                        {
                            case 116858: // Chaos Bolt
                            case 215279: // Chaos Bolt
                                critChance = 100.0f;
                                return true;
                            default:
                                break;
                        }

						if (victim)
							if (HasAura(213547) && int32(victim->GetHealthPct()) >= 80) // Warlock Initiation PvP Talent
								crit_chance += 30;

                        break;
                    }
					case SPELLFAMILY_SHAMAN:
					{
						if (victim)
							if (HasAura(213556) && int32(victim->GetHealthPct()) >= 80) // Shaman Initiation PvP Talent
								crit_chance += 30;

						break;
					}
					case SPELLFAMILY_MAGE:
					{
						if (victim)
							if (HasAura(213541) && int32(victim->GetHealthPct()) >= 80) // Mage Initiation PvP Talent
								crit_chance += 30;

						break;
					}
					case SPELLFAMILY_PRIEST:
					{
						if (victim)
							if (HasAura(213550) && int32(victim->GetHealthPct()) >= 80) // Priest Initiation PvP Talent
								crit_chance += 30;

						break;
					}
                }
            }
            break;
        }
        case SPELL_DAMAGE_CLASS_MELEE:
        {
            if (victim)
            {
                crit_chance += GetUnitCriticalChance(attackType, victim);
                // Custom crit by class
                switch (spellProto->ClassOptions.SpellClassSet)
                {
                    case SPELLFAMILY_DRUID:
                    {
                        switch (spellProto->Id)
                        {
                            case 5221: // crit chance x2 if prowl
                                if (spell && (spell->GetCastedFromStealth() || HasAura(102543)))
                                    crit_chance *= 2.0f;
                                break;
							case 1822: // Fresh Wounds PVP Honor Talent Feral Spec
								if(victim)
									if (spell)
										if (Aura* aura = GetAura(203224))
											if (aura->GetCustomData() == 1)
											{
												crit_chance += 60;
												aura->SetCustomData(0);
											}
								break;
                            default:
                                break;
                        }

						if (victim)
							if (HasAura(213553) && int32(victim->GetHealthPct()) >= 80) // Druid Initiation PvP Talent
								crit_chance += 30;

                        break;
                    }
                    case SPELLFAMILY_WARRIOR:
                    {
                        switch (spellProto->Id)
                        {
                            case 118000: // Dragon Roar is always a critical hit
                                critChance = 100.0f;
                                return true;
                            default:
                                break;
                        }
                        break;
                    }
                    case SPELLFAMILY_PALADIN:
                    {
                        switch (spellProto->Id)
                        {
                            case 20271: // Judgment
                                if (AuraEffect const* aurEff = GetAuraEffect(218178, EFFECT_0)) // Greater Judgment
                                    if (int32(victim->GetHealthPct()) > aurEff->GetAmount())
                                    {
                                        critChance = 100.0f;
                                        return true;
                                    }
                                break;
                            default:
                                break;
                        }
                        break;
                    }
                    case SPELLFAMILY_DEATHKNIGHT:
                    {
                        switch (spellProto->Id)
                        {
                            case 66196: // Frost Strike
                            case 222026: // Frost Strike
                                if (AuraEffect const* aurEff = GetAuraEffect(204132, EFFECT_1)) // Tundra Stalker (Honor Talent)
                                    if (victim->HasAuraWithMechanic(1 << MECHANIC_ROOT))
                                        critChance += aurEff->GetAmount();
                                break;
                            default:
                                break;
                        }
                        break;
                    }
                }
            }
            break;
        }
        case SPELL_DAMAGE_CLASS_RANGED:
        {
            switch (spellProto->Id)
            {
                case 19434: // Aimed Shot
                case 185358: // Arcane Shot
                case 212621: // Marked Shot
                case 2643: // Multi-Shot
                if (victim)
                    if (AuraEffect const* aurEff = GetAuraEffect(53238, EFFECT_1))
                        if (int32(victim->GetHealthPct()) > aurEff->GetAmount())
                            if (AuraEffect const* aurEffPct = GetAuraEffect(53238, EFFECT_0))
                                crit_chance += aurEffPct->GetAmount();
                    break;
                case 75: // Auto Shot
                case 193455: // Cobra Shot
                if (victim)
                    if (AuraEffect const* aurEff = GetAuraEffect(204308, EFFECT_1))
                        if (int32(victim->GetHealthPct()) > aurEff->GetAmount())
                            if (AuraEffect const* aurEffPct = GetAuraEffect(204308, EFFECT_0))
                                crit_chance += aurEffPct->GetAmount();
                    break;
            }
			if(victim)
				if (HasAura(213539) && int32(victim->GetHealthPct()) >= 80) // Hunter Initiation PvP Talent
					crit_chance += 30;
            if (victim)
                crit_chance += GetUnitCriticalChance(attackType, victim);
            break;
        }
        default:
            return false;
    }

    // not critting spell
    if ((spellProto->HasAttribute(SPELL_ATTR2_CANT_CRIT)))
        return false;

    if (spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS))
        if ((spellProto->HasAttribute(SPELL_ATTR10_STACK_DAMAGE_OR_HEAL)) || GetGUID() == victim->GetGUID())
            return false;

     if (victim && getClass() == CLASS_DRUID && GetShapeshiftForm() == FORM_CAT && HasAura(202022)) // Blood Scent
         if (victim->HasAuraWithMechanic((1 << MECHANIC_BLEED)))
             crit_chance += 10;

    // percent done
    // only players use intelligence for critical chance computations
    if (Player* modOwner = GetSpellModOwner())
        modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance);

    crit_chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_CRITICAL_DAMAGE, [=](AuraEffect const* aurEff) -> bool
    {
        return (aurEff->GetSpellInfo() && int32(victim->GetHealthPct()) > aurEff->GetSpellInfo()->Effects[EFFECT_1]->BasePoints); // why by BP insted of miscB?
    });

    CalculateFromDummy(victim, crit_chance, spellProto, 131071, SPELL_DUMMY_CRIT);

    crit_chance = crit_chance > 0.0f ? crit_chance : 0.0f;
    critChance = crit_chance;

    if (countCrit)
        crit_chance = countCrit;

    if (roll_chance_f(crit_chance))
        return true;
    return false;
}

float Unit::SpellCriticalDamageBonus(SpellInfo const* spellProto, Unit* victim)
{
    // Calculate critical bonus
    float crit_bonus = 2.f;
    crit_bonus *= GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, spellProto->GetSchoolMask());
    crit_bonus -= 1.f;

    if (Player* modOwner = GetSpellModOwner())
        modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);

    if ((IsPlayer() || HasUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER)) && victim &&
        victim != this && (victim->IsPlayer() || victim->HasUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER)))
    {
        crit_bonus = CalculatePct(crit_bonus, 50);
    }

    crit_bonus += 1.f;

    return crit_bonus;
}

uint32 Unit::SpellCriticalHealingBonus(SpellInfo const* spellProto, uint32 damage)
{
    // Calculate critical bonus
    int32 crit_bonus = damage;
    damage = int32(float(damage) * GetTotalAuraMultiplier(SPELL_AURA_MOD_CRITICAL_HEALING_AMOUNT));

    if (Unit* owner = GetOwner())
        crit_bonus *= owner->GetTotalAuraMultiplier(SPELL_AURA_MOD_CRITICAL_HEAl_PCT);
    else
        crit_bonus *= GetTotalAuraMultiplier(SPELL_AURA_MOD_CRITICAL_HEAl_PCT);

    // adds additional heal to crit_bonus (from talents)
    if (Player* modOwner = GetSpellModOwner())
        modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);

    damage += crit_bonus;

    return damage;
}

uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, SpellEffIndex effIndex, uint32 stack)
{
    // For totems get healing bonus from owner (statue isn't totem in fact)
    if (IsCreature() && isTotem())
        if (Unit* owner = GetOwner())
            return owner->SpellHealingBonusDone(victim, spellProto, healamount, damagetype, effIndex, stack);

    // Some spells don't benefit from done mods
    if ((spellProto->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) || (spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)))
        return healamount;

    // No bonus healing for potion spells
    if (spellProto->ClassOptions.SpellClassSet == SPELLFAMILY_POTION)
        return healamount;

    if (spellProto->Id == 19236)
        return healamount;

    // No bonus for Devouring Plague heal
    if (spellProto->Id == 81751)
        return healamount;

    // No bonus for Soul Leech
    if (spellProto->Id == 108366)
        return healamount;

    float DoneTotalMod = 1.0f;
    int32 DoneTotal = 0;

    DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_DONE_PERCENT);

    if (victim)
    {
        AuraEffectList const* mHealingFromHealthPct = GetAuraEffectsByType(SPELL_AURA_MOD_HEALING_DONE_FROM_PCT_HEALTH);
        if (mHealingFromHealthPct && mHealingFromHealthPct->begin() != mHealingFromHealthPct->end())
        {
            float healthPct = std::max(0.0f, 1.0f - float(victim->GetHealth(this)) / victim->GetMaxHealth(this));
            for (AuraEffectList::const_iterator i = mHealingFromHealthPct->begin(); i != mHealingFromHealthPct->end(); ++i)
                if ((*i)->IsAffectingSpell(spellProto))
                    DoneTotalMod *= (100.0f + (*i)->GetAmount() * healthPct) / 100.0f;
        }
    }

    if (Player* modOwner = GetSpellModOwner())
        AddPct(DoneTotalMod, modOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY) + modOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY_BONUS));

    int32 DoneAdvertisedBenefit = SpellBaseHealingBonusDone(spellProto->GetSchoolMask(), false);
    int32 bonusDone = GetSpellPowerDamage(spellProto->GetSchoolMask());

    if (HasUnitTypeMask(UNIT_MASK_GUARDIAN))
        DoneAdvertisedBenefit += static_cast<Guardian*>(this)->GetBonusDamage();

    if (!DoneAdvertisedBenefit || DoneAdvertisedBenefit < bonusDone)
        DoneAdvertisedBenefit = bonusDone;

    // Check for table values
    SpellBonusEntry const* bonus = sSpellMgr->GetSpellBonusData(spellProto->Id);
    float ApCoeffMod = spellProto->GetEffect(effIndex, m_spawnMode)->BonusCoefficientFromAP;
    float dbccoeff = spellProto->GetEffect(effIndex, m_spawnMode)->BonusCoefficient;
    float coeff = 0;
    bool calcBonus = true;

    if (spellProto->Id == 191894) // Custom mod for Mastery: Gust of Mists
        if (AuraEffect const* aurEff = GetAuraEffect(117907, EFFECT_2)) // Mastery: Gust of Mists
            dbccoeff = aurEff->GetAmount() / 1.04f;

    if (getClass() == CLASS_MONK && bonus && bonus->heal_bonus)
    {
        DoneTotal += CalculateMonkSpellDamage(bonus->heal_bonus);
        calcBonus = false;
    }

    SpellEffectInfo const* effectInfo = spellProto->GetEffect(effIndex, m_spawnMode);

    if (calcBonus)
    {
        if (bonus)
        {
            if (damagetype == DOT)
            {
                coeff = bonus->dot_damage;
                if (bonus->ap_dot_bonus > 0)
                    DoneTotal += int32(bonus->ap_dot_bonus * stack * GetTotalAttackPowerValue(
                        (spellProto->IsRangedWeaponSpell() && spellProto->Categories.DefenseType !=SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK));
            }
            else
            {
                coeff = bonus->direct_damage;
                if (bonus->ap_bonus > 0)
                    DoneTotal += int32(bonus->ap_bonus * stack * GetTotalAttackPowerValue(BASE_ATTACK));
            }
        }
        else
        {
            if (dbccoeff/* && spellProto->SchoolMask & SPELL_SCHOOL_MASK_MAGIC*/)
                coeff = dbccoeff; // 77478 use in SCHOOL_MASK_PHYSICAL

            if (ApCoeffMod)
                DoneTotal += int32(ApCoeffMod * stack * GetTotalAttackPowerValue((spellProto->IsRangedWeaponSpell() && spellProto->Categories.DefenseType !=SPELL_DAMAGE_CLASS_MELEE) ? RANGED_ATTACK : BASE_ATTACK));
        }
    }

    // Default calculation
    if (DoneAdvertisedBenefit)
    {
        if (Player* modOwner = GetSpellModOwner())
        {
            coeff *= 100.0f;
            modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff);
            coeff /= 100.0f;
        }

        DoneTotal += int32(DoneAdvertisedBenefit * coeff * stack);
    }

    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
    {
        if (spellProto->EffectMask < uint32(1 << i))
            break;

        switch (effectInfo->ApplyAuraName)
        {
            // Bonus healing does not apply to these spells
            case SPELL_AURA_PERIODIC_LEECH:
            case SPELL_AURA_PERIODIC_HEALTH_FUNNEL:
                DoneTotal = 0;
                break;
        }
        if (effectInfo->Effect == SPELL_EFFECT_HEALTH_LEECH)
            DoneTotal = 0;
    }

    // use float as more appropriate for negative values and percent applying
    float heal = float(int32(healamount) + DoneTotal) * DoneTotalMod;

    if (effectInfo->PvPMultiplier && CanPvPScalar())
        heal *= effectInfo->PvPMultiplier;

    // apply spellmod to Done amount
    if (damagetype != DOT)
    {
        heal *= SpellPctDone(victim, spellProto, effIndex);

        // apply spellmod to Done damage (flat and pct)
        if (Player* modOwner = GetSpellModOwner())
            modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, heal);
    }

    return uint32(std::max(heal, 0.0f));
}

uint32 Unit::SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, SpellEffIndex effIndex, uint32 stack)
{
    float TakenTotalMod = 1.0f;

    // Some spells don't benefit from done mods
    if (spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS))
        return healamount;

    // Healing taken percent
    float ModHealingPct = float(GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_PCT));
    if (ModHealingPct != 1.0f)
        TakenTotalMod *= ModHealingPct;

    // Tenacity increase healing % taken
    if (AuraEffect const* Tenacity = GetAuraEffect(58549, 0))
        AddPct(TakenTotalMod, Tenacity->GetAmount());

    // Healing Done
    int32 TakenTotal = 0;

    // Taken fixed damage bonus auras
    int32 TakenAdvertisedBenefit = SpellBaseHealingBonusTaken(spellProto->GetSchoolMask());

    // Check for table values
    SpellBonusEntry const* bonus = sSpellMgr->GetSpellBonusData(spellProto->Id);
    float dbccoeff = spellProto->GetEffect(effIndex, m_spawnMode)->BonusCoefficient;
    float coeff = 0;
    if (bonus)
        coeff = (damagetype == DOT) ? bonus->dot_damage : bonus->direct_damage;
    else
    {
        coeff = dbccoeff;
        // No bonus healing for SPELL_DAMAGE_CLASS_NONE class spells by default
        if (spellProto->Categories.DefenseType == SPELL_DAMAGE_CLASS_NONE)
        {
            healamount = uint32(std::max((float(healamount) * TakenTotalMod), 0.0f));
            return healamount;
        }
    }

    // Default calculation
    if (TakenAdvertisedBenefit)
    {
        if ((!bonus && !dbccoeff) || coeff < 0)
            coeff = CalculateDefaultCoefficient(spellProto, damagetype) * 1.88f;  // As wowwiki says: C = (Cast Time / 3.5) * 1.88 (for healing spells)

        if (Player* modOwner = GetSpellModOwner())
        {
            coeff *= 100.0f;
            modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_BONUS_MULTIPLIER, coeff);
            coeff /= 100.0f;
        }

        TakenTotal += int32(TakenAdvertisedBenefit * coeff * stack);
    }

    TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_RECEIVED, [caster, spellProto](AuraEffect const* aurEff) -> bool
    {
        if ((caster->GetGUID() == aurEff->GetCasterGUID() && aurEff->IsAffectingSpell(spellProto)) || aurEff->GetBase()->GetId() == 974) // Hack fix for Earth Shield
            return true;
        return false;
    });

    if (damagetype == DOT)
        TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HOT_PCT);

    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
    {
        if (spellProto->EffectMask < uint32(1 << i))
            break;

        switch (spellProto->GetEffect(i, GetSpawnMode())->ApplyAuraName)
        {
            // Bonus healing does not apply to these spells
            case SPELL_AURA_PERIODIC_LEECH:
            case SPELL_AURA_PERIODIC_HEALTH_FUNNEL:
                TakenTotal = 0;
                break;
        }
        if (spellProto->GetEffect(i, GetSpawnMode())->Effect == SPELL_EFFECT_HEALTH_LEECH)
            TakenTotal = 0;
    }

    float heal = float(int32(healamount) + TakenTotal) * TakenTotalMod;
    return uint32(std::max(heal, 0.0f));
}

int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask, bool calcVers/* = true*/)
{
    int32 AdvertisedBenefit = GetTotalAuraModifier(SPELL_AURA_MOD_HEALING_DONE2, [schoolMask](AuraEffect const* aurEff) -> bool
    {
        if (!aurEff->GetMiscValue() || (aurEff->GetMiscValue() & schoolMask) != 0)
            return true;
        return false;
    });

    // Healing bonus of spirit, intellect and strength
    if (IsPlayer())
    {
        // Check if we are ever using mana - PaperDollFrame.lua
        if (GetPowerIndex(POWER_MANA) != MAX_POWERS)
            AdvertisedBenefit += std::max(0, int32(GetStat(STAT_INTELLECT)));  // spellpower from intellect

        AdvertisedBenefit += GetTotalAuraModifier(SPELL_AURA_MOD_HEALING_DONE, [schoolMask](AuraEffect const* aurEff) -> bool
        {
            if (!aurEff->GetMiscValue() || (aurEff->GetMiscValue() & schoolMask) != 0)
                return true;
            return false;
        });

        AdvertisedBenefit *= GetTotalAuraMultiplier(SPELL_AURA_MOD_SPELL_POWER_PCT);

        // Healing bonus from stats
        if (AuraEffectList const* mHealingDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT))
            for (AuraEffectList::const_iterator i = mHealingDoneOfStatPercent->begin(); i != mHealingDoneOfStatPercent->end(); ++i)
                AdvertisedBenefit += int32(CalculatePct(GetStat(Stats((*i)->GetMiscValue())), (*i)->GetAmount()));

        if (calcVers && GetSpellModOwner())
            AdvertisedBenefit += CalculatePct(AdvertisedBenefit, GetSpellModOwner()->GetRatingBonusValue(CR_VERSATILITY_HEALING_DONE) + GetSpellModOwner()->GetTotalAuraModifier(SPELL_AURA_MOD_VERSALITY_PCT));
    }
    return AdvertisedBenefit;
}

int32 Unit::SpellBaseHealingBonusTaken(SpellSchoolMask schoolMask)
{
    return GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_HEALING, schoolMask);
}

bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask)
{
    // If m_immuneToSchool type contain this school type, IMMUNE damage.
    SpellImmuneList const& schoolList = GetSpellImmune(IMMUNITY_SCHOOL);
    for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
        if (itr->type & shoolMask)
            return true;

    // If m_immuneToDamage type contain magic, IMMUNE damage.
    SpellImmuneList const& damageList = GetSpellImmune(IMMUNITY_DAMAGE);
    for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
        if (itr->type & shoolMask)
            return true;

    return false;
}

bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo)
{
    if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY))
        return false;

    uint8 shoolMask = spellInfo->GetSchoolMask();
    uint8 tempMask = shoolMask;

    if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 195710 && spellInfo->Id != 208683)
    {
        // If m_immuneToSchool type contain this school type, IMMUNE damage.
        SpellImmuneList const& schoolList = GetSpellImmune(IMMUNITY_SCHOOL);
        for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
        {
            tempMask &= ~itr->type;
            if (itr->type & shoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->spellId)))
                if (!tempMask)
                    return true;
        }
    }

    // If m_immuneToDamage type contain magic, IMMUNE damage.
    SpellImmuneList const& damageList = GetSpellImmune(IMMUNITY_DAMAGE);
    for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
    {
        tempMask &= ~itr->type;
        if ((itr->type & shoolMask) && !tempMask)
            return true;
    }

    return false;
}

bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo)
{
    if (!spellInfo)
        return false;

    // Single spell immunity.
    SpellImmuneList const& idList = GetSpellImmune(IMMUNITY_ID);
    for (SpellImmuneList::const_iterator itr = idList.begin(); itr != idList.end(); ++itr)
        if (itr->type == spellInfo->Id)
            return true;

    if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_AVOID_MECHANIC))
        return false;

    if (spellInfo->Categories.DispelType)
    {
        SpellImmuneList const& dispelList = GetSpellImmune(IMMUNITY_DISPEL);
        for (SpellImmuneList::const_iterator itr = dispelList.begin(); itr != dispelList.end(); ++itr)
            if (itr->type == spellInfo->Categories.DispelType)
                return true;
    }

    // Spells that don't have effectMechanics.
    if (spellInfo->Categories.Mechanic)
    {
        SpellImmuneList const& mechanicList = GetSpellImmune(IMMUNITY_MECHANIC);
        for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
            if (itr->type == spellInfo->Categories.Mechanic)
                return true;
    }

    bool immuneToAllEffects = true;
    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
    {
        if (spellInfo->EffectMask < uint32(1 << i))
            break;

        // State/effect immunities applied by aura expect full spell immunity
        // Ignore effects with mechanic, they are supposed to be checked separately
        if (!spellInfo->GetEffect(i, GetSpawnMode())->IsEffect())
            continue;
        if (!IsImmunedToSpellEffect(spellInfo, i))
        {
            immuneToAllEffects = false;
            break;
        }
    }

    if (immuneToAllEffects) //Return immune only if the target is immune to all spell effects.
        return true;

    if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 195710 && spellInfo->Id != 208683)
    {
        uint8 mask = spellInfo->GetSchoolMask();
        SpellImmuneList const& schoolList = GetSpellImmune(IMMUNITY_SCHOOL);
        for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
        {
            mask &= ~itr->type;
            SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->spellId);
            if ((itr->type & spellInfo->GetSchoolMask()) && !(immuneSpellInfo && immuneSpellInfo->IsPositive() && spellInfo->IsPositive()) && !spellInfo->CanPierceImmuneAura(immuneSpellInfo))
                if (!mask)
                    return true;
        }
    }

    return false;
}

bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
{
    if (!spellInfo || !spellInfo->GetEffect(index, GetSpawnMode())->IsEffect())
        return false;

    if (spellInfo->HasAttribute(SPELL_ATTR0_CU_IGNORE_AVOID_MECHANIC))
        return false;

    // If m_immuneToEffect type contain this effect type, IMMUNE effect.
    uint32 effect = spellInfo->GetEffect(index, GetSpawnMode())->Effect;
    SpellImmuneList const& effectList = const_cast<Unit*>(this)->GetSpellImmune(IMMUNITY_EFFECT);
    for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr)
        if (itr->type == effect)
            return true;

    if (uint32 mechanic = spellInfo->GetEffect(index, GetSpawnMode())->Mechanic)
    {
        SpellImmuneList const& mechanicList = const_cast<Unit*>(this)->GetSpellImmune(IMMUNITY_MECHANIC);
        for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
            if (itr->type == mechanic)
                return true;
    }

    if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY))
        return false;

    if (uint32 aura = spellInfo->GetEffect(index, GetSpawnMode())->ApplyAuraName)
    {
        SpellImmuneList const& list = const_cast<Unit*>(this)->GetSpellImmune(IMMUNITY_STATE);
        for (SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr)
            if (itr->type == aura)
                if (!(spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)))
                    return true;

            // Check for immune to application of harmful magical effects
        if (AuraEffectList const* immuneAuraApply = GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL))
        for (AuraEffectList::const_iterator iter = immuneAuraApply->begin(); iter != immuneAuraApply->end(); ++iter)
            if (((*iter)->GetMiscValue() & spellInfo->GetSchoolMask()) && !spellInfo->IsPositiveEffect(index))        // Harmful && Magic effects
                return true;

        if (AuraEffectList const* immuneMechanicAuraApply = GetAuraEffectsByType(SPELL_AURA_MECHANIC_IMMUNITY_MASK))
        for (AuraEffectList::const_iterator i = immuneMechanicAuraApply->begin(); i != immuneMechanicAuraApply->end(); ++i)
            if (spellInfo->GetEffect(index, GetSpawnMode())->Mechanic && spellInfo->Categories.Mechanic && ((1 << (spellInfo->GetEffect(index, GetSpawnMode())->Mechanic)) & (*i)->GetMiscValue() ||
                (1 << (spellInfo->Categories.Mechanic)) & (*i)->GetMiscValue()))
                return true;
    }

    //apply immune to disorient spells if player is mounted and flying
    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++)
        if (uint32 mechanic = spellInfo->Effects[i]->Mechanic)
        {
            if (mechanic == MECHANIC_DISORIENTED)
                if ((IsMounted() || HasAura(40120)) && HasUnitMovementFlag(MOVEMENTFLAG_FLYING))
                    return true;
        }
        else if (uint32 mechanic = spellInfo->Categories.Mechanic)
        {
            if (mechanic == MECHANIC_DISORIENTED)
                if ((IsMounted() || HasAura(40120)) && HasUnitMovementFlag(MOVEMENTFLAG_FLYING))
                    return true;
        }

    //immune to daze with Stonehide Leather Barding
    if (uint32 mechanic = spellInfo->Categories.Mechanic)
    {
        if (mechanic == MECHANIC_DAZE)
            if (IsMounted() && HasAura(208705))
                if (GetMapId() == 1220 || GetMapId() == 1669)
                    return true;
    }

    return false;
}

uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto, uint32 effIndex)
{
    if (!victim || pdamage == 0)
        return 0;

    if (spellProto && (spellProto->HasAttribute(SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS)))
        return pdamage;

    uint32 creatureTypeMask = victim->GetCreatureTypeMask();

    // Done fixed damage bonus auras
    int32 DoneFlatBenefit = GetMaxPositiveAuraModifierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE, creatureTypeMask);;
    int32 APbonus = 0;

    if (attType == RANGED_ATTACK)
    {
        APbonus += victim->GetTotalAuraModifier(SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS);
        APbonus += GetMaxPositiveAuraModifierByMiscMask(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS, creatureTypeMask);
    }
    else
    {
        APbonus += victim->GetTotalAuraModifier(SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS);
        APbonus += GetMaxPositiveAuraModifierByMiscMask(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS, creatureTypeMask);
    }

    if (APbonus != 0)                                       // Can be negative
        DoneFlatBenefit += int32(APbonus / 3.5f * GetAPMultiplier(attType, spellProto && spellProto->HasEffect(SPELL_EFFECT_NORMALIZED_WEAPON_DMG)));

    // Done total percent damage auras
    float DoneTotalMod = 1.0f;

    // Some spells don't benefit from pct done mods
    if (spellProto)
    {
        AuraEffectList mModDamagePercentDone;
        std::vector<uint32> ExcludeAuraList;
        GetTotalNotStuckAuraEffectByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, mModDamagePercentDone, ExcludeAuraList);
        for (AuraEffectList::iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i)
        {
            if ((*i)->GetSpellInfo()->AuraRestrictions.CasterAuraState && !HasAuraState(AuraStateType((*i)->GetSpellInfo()->AuraRestrictions.CasterAuraState)))
                continue;

            if ((*i)->GetMiscValue() & spellProto->GetSchoolMask() && !(spellProto->GetSchoolMask() & SPELL_SCHOOL_MASK_NORMAL))
            {
                if ((*i)->GetSpellInfo()->EquippedItemClass == -1)
                    AddPct(DoneTotalMod, (*i)->GetAmount());
                else if ((*i)->GetSpellInfo()->EquippedItemSubClassMask == 0)
                    AddPct(DoneTotalMod, (*i)->GetAmount());
                else if (IsPlayer() && ToPlayer()->HasItemFitToSpellRequirements((*i)->GetSpellInfo()))
                    AddPct(DoneTotalMod, (*i)->GetAmount());
            }
        }

        // Mastery: Combo Strikes
        if (getClass() == CLASS_MONK)
            if (AuraEffect* aurEff = GetAuraEffect(115636, EFFECT_0))
                if (aurEff->GetOldBaseAmount())
                    AddPct(DoneTotalMod, aurEff->GetAmount());
    }

    DoneTotalMod *= GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS, creatureTypeMask);
    DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE_BY_TARGET_AURA_MECHANIC, [=](AuraEffect const* aurEff) -> bool
    {
        if (victim->HasAurasWithMechanic(1 << aurEff->GetMiscValue()))
            return true;
        return false;
    });

    // bonus against aurastate
    if (AuraEffectList const* mDamageDoneVersusAurastate = GetAuraEffectsByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS_AURASTATE))
    for (AuraEffectList::const_iterator i = mDamageDoneVersusAurastate->begin(); i != mDamageDoneVersusAurastate->end(); ++i)
        if (victim->HasAuraState(AuraStateType((*i)->GetMiscValue())))
            if (HasAura(144421) && GetPower(POWER_ALTERNATE))
            {
                int32 pos = GetPower(POWER_ALTERNATE);
                int32 mod = 0;
                switch (pos)
                {
                case 25:
                    mod = -10;
                    break;
                case 50:
                    mod = -25;
                    break;
                case 75:
                    mod = -50;
                    break;
                case 100:
                    mod = -75;
                    break;
                default:
                    mod = 0;
                    break;
                }
                AddPct(DoneTotalMod, mod);
            }
            else
                AddPct(DoneTotalMod, (*i)->GetAmount());

    // Add SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC percent bonus
    if (spellProto)
    {
        if (spellProto->Categories.Mechanic)
            AddPct(DoneTotalMod, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC, spellProto->Categories.Mechanic));
        else
        {
            if (spellProto->Effects[effIndex]->Mechanic)
                AddPct(DoneTotalMod, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC, spellProto->Effects[effIndex]->Mechanic));
        }
    }

    if (!spellProto)
        DoneTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_AUTOATTACK_DAMAGE);

    if (Unit* owner = GetAnyOwner())
        AddPct(DoneTotalMod, owner->GetTotalAuraModifier(SPELL_AURA_PET_DAMAGE_DONE_PCT));

    if (spellProto)
        if (auto scalar = spellProto->GetEffect(effIndex, m_spawnMode)->PvPMultiplier)
            if (CanPvPScalar())
                DoneTotalMod *= scalar;

    float tmpDamage = float(int32(pdamage) + DoneFlatBenefit) * DoneTotalMod;

    // apply spellmod to Done damage
    if (spellProto)
    {
        if (Player* modOwner = GetSpellModOwner())
            modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage);

        CalculateFromDummy(victim, tmpDamage, spellProto, (1 << effIndex));
    }

    // TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "MeleeDamageBonusDone spellid %u in tmpDamage %f, pdamage %i DoneTotalMod %f DoneFlatBenefit %i", spellProto ? spellProto->Id : 0, tmpDamage, pdamage, DoneTotalMod, DoneFlatBenefit);

    // bonus result can be negative
    return uint32(std::max(tmpDamage, 0.0f));
}

uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto)
{
    if (pdamage == 0)
        return 0;

    int32 TakenFlatBenefit = 0;
    float TakenTotalMod = 1.0f;
    SpellSchoolMask schoolMask = spellProto ? spellProto->GetSchoolMask() : SPELL_SCHOOL_MASK_NORMAL;

    TakenTotalMod *= attacker->GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_TAKEN_FROM_CASTER, [=](AuraEffect const* aurEff) -> bool
    {
        if (aurEff->GetCasterGUID() == GetGUID() && (aurEff->GetMiscValue() & schoolMask))
            return true;
        return false;
    });

    TakenFlatBenefit += attacker->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_DAMAGE_TAKEN, GetMeleeDamageSchoolMask());

    if (attType != RANGED_ATTACK)
    {
        if (AuraEffectList const* mMelleDamageTaken = GetAuraEffectsByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN))
        {
            for (AuraEffectList::const_iterator i = mMelleDamageTaken->begin(); i != mMelleDamageTaken->end(); ++i)
                if ((*i)->GetAmount() < 0)
                    TakenFlatBenefit += (*i)->GetAmount();
                else
                    AddPct(TakenTotalMod, (*i)->GetAmount());
        }
    }
    else
    {
        if (AuraEffectList const* mMelleDamageTaken = GetAuraEffectsByType(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN))
        {
            for (AuraEffectList::const_iterator i = mMelleDamageTaken->begin(); i != mMelleDamageTaken->end(); ++i)
                if ((*i)->GetAmount() < 0)
                    TakenFlatBenefit += (*i)->GetAmount();
                else
                    AddPct(TakenTotalMod, (*i)->GetAmount());
        }
    }

    if (Player* spellModOwner = GetSpellModOwner())
        TakenTotalMod -= CalculatePct(1.0, (spellModOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY) + spellModOwner->GetFloatValue(PLAYER_FIELD_VERSATILITY_BONUS)) / 2.f);

    // ..taken
    TakenTotalMod *= GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, GetMeleeDamageSchoolMask());

    TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_INCREASE_SCHOOL_DAMAGE_TAKEN, [=](AuraEffect const* aurEff) -> bool
    {
        return (aurEff->GetCasterGUID() == attacker->GetGUID() && ((aurEff->GetMiscValue() & GetMeleeDamageSchoolMask()) != 0));
    });

    // .. taken pct (special attacks)
    if (spellProto)
    {
        TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_DAMAGE_FROM_CASTER, [=](AuraEffect const* aurEff) -> bool
        {
            return (aurEff->GetCasterGUID() == attacker->GetGUID() && aurEff->IsAffectingSpell(spellProto));
        });

        // Mod damage from spell mechanic
        uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask();

        // Shred, Maul - "Effects which increase Bleed damage also increase Shred damage"
        if (spellProto->ClassOptions.SpellClassSet == SPELLFAMILY_DRUID && spellProto->ClassOptions.SpellClassMask[0] & 0x00008800)
            mechanicMask |= (1<<MECHANIC_BLEED);

        if (mechanicMask)
        {
            TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT, [=](AuraEffect const* aurEff) -> bool
            {
                return (mechanicMask & uint32(1 << (aurEff->GetMiscValue())));
            });
        }
    }
    else
    {
        TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_AUTOATTACK_DAMAGE_TARGET, [=](AuraEffect const* aurEff) -> bool
        {
            return (aurEff->GetCasterGUID() == attacker->GetGUID());
        });
    }

    if (attType != RANGED_ATTACK)
        TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT);
    else
        TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT);

    float tmpDamage = (float(pdamage) + TakenFlatBenefit) * TakenTotalMod;

    // TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "MeleeDamageBonusTaken spellid %u in tmpDamage %f, pdamage %i TakenTotalMod %f TakenFlatBenefit %i", spellProto ? spellProto->Id : 0, tmpDamage, pdamage, TakenTotalMod, TakenFlatBenefit);

    // bonus result can be negative
    return uint32(std::max(tmpDamage, 0.0f));
}

void Unit::ApplyUberImmune(uint32 spellid, bool apply)
{
    if (apply)
        RemoveAurasWithMechanic(IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK, AURA_REMOVE_BY_DEFAULT, spellid);
    for (uint32 mech = MECHANIC_CHARM; mech != MECHANIC_ENRAGED; ++mech)
    {
        if (mech == MECHANIC_DISARM)
            continue;

        if (1 << mech & IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK)
            ApplySpellImmune(spellid, IMMUNITY_MECHANIC, mech, apply);
    }
}

void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply)
{
    if (apply)
    {
        for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(); itr != m_spellImmune[op].end(); ++itr)
        {
            if (itr->spellId == spellId && itr->type == type)
            {
                m_spellImmune[op].erase(*itr);
                itr = m_spellImmune[op].begin();
            }
        }
        SpellImmune Immune;
        Immune.spellId = spellId;
        Immune.type = type;
        m_spellImmune[op].insert(Immune);
    }
    else
    {
        for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(); itr != m_spellImmune[op].end(); ++itr)
        {
            if (itr->spellId == spellId && itr->type == type)
            {
                m_spellImmune[op].erase(*itr);
                break;
            }
        }
    }
}

void Unit::ApplySpellDispelImmunity(const SpellInfo* spellProto, DispelType type, bool apply)
{
    ApplySpellImmune(spellProto->Id, IMMUNITY_DISPEL, type, apply);

    if (apply && spellProto->HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY))
    {
        // Create dispel mask by dispel type
        uint32 dispelMask = SpellInfo::GetDispelMask(type);
        // Dispel all existing auras vs current dispel type
        for (AuraApplicationMap::iterator itr = m_appliedAuras.begin(); itr != m_appliedAuras.end();)
        {
            SpellInfo const* spell = itr->second->GetBase()->GetSpellInfo();
            if (spell->GetDispelMask() & dispelMask)
            {
                // Dispel aura
                RemoveAura(itr);
            }
            else
                ++itr;
        }
    }
}

uint32 Unit::GetSchoolImmunityMask() const
{
    uint32 mask = 0;
    SpellImmuneList const& mechanicList = const_cast<Unit*>(this)->GetSpellImmune(IMMUNITY_SCHOOL);
    for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
        mask |= itr->type;

    return mask;
}

uint32 Unit::GetMechanicImmunityMask() const
{
    uint32 mask = 0;
    SpellImmuneList const& mechanicList = const_cast<Unit*>(this)->GetSpellImmune(IMMUNITY_MECHANIC);
    for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
        mask |= (1 << itr->type);

    return mask;
}

Trinity::AnyDataContainer & Unit::GetAnyDataContainer()
{
    return m_anyDataContainer;
}

float Unit::GetWeaponProcChance() const
{
    // normalized proc chance for weapon attack speed
    // (odd formula...)
    if (isAttackReady(BASE_ATTACK))
        return (GetAttackTime(BASE_ATTACK) * 1.8f / 1000.0f);
    if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
        return (GetAttackTime(OFF_ATTACK) * 1.6f / 1000.0f);
    return 0;
}

float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellInfo* spellProto) const
{
    // proc per minute chance calculation
    if (PPM <= 0)
        return 0.0f;

    // Apply chance modifer aura
    if (spellProto)
        if (Player* modOwner = GetSpellModOwner())
            modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_PROC_PER_MINUTE, PPM);

    return floor((WeaponSpeed * PPM) / 600.0f);   // result is chance in percents (probability = Speed_in_sec * (PPM / 60))
}

void Unit::Mount(uint32 mount, uint32 VehicleId, uint32 creatureEntry)
{
    if (mount)
        SetUInt32Value(UNIT_FIELD_MOUNT_DISPLAY_ID, mount);

    SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT);

    if (Player* player = ToPlayer())
    {
        // mount as a vehicle
        if (VehicleId)
        {
            if (CreateVehicleKit(VehicleId, creatureEntry))
            {
                player->SendOnCancelExpectedVehicleRideAura();
                GetVehicleKit()->InstallAllAccessories(false);
            }
        }

        // don't unsummon pet but SetFlag UNIT_FLAG_STUNNED to disable pet's interface
        if (Pet* pet = player->GetPet())
            pet->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);

        player->UnsummonCurrentBattlePetIfAny(true);
        player->SendMovementSetCollisionHeight(player->GetCollisionHeight(true));
    }

    if (!HasFlag(UNIT_FIELD_FLAGS_3, UNIT_FLAG3_NOT_CHECK_MOUNT))
        RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOUNT);
}

void Unit::Dismount()
{
    if (!IsMounted())
        return;

    SetUInt32Value(UNIT_FIELD_MOUNT_DISPLAY_ID, 0);
    RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT);

    if (Player* thisPlayer = ToPlayer())
        thisPlayer->SendMovementSetCollisionHeight(thisPlayer->GetCollisionHeight(false));

    SendMessageToSet(WorldPackets::Misc::Dismount(GetGUID()).Write(), true);

    // dismount as a vehicle
    if (IsPlayer() && GetVehicleKit())
    {
        SendSetVehicleRecId(0);
        RemoveVehicleKit();
    }

    RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_MOUNTED);

    // only resummon old pet if the player is already added to a map
    // this prevents adding a pet to a not created map which would otherwise cause a crash
    // (it could probably happen when logging in after a previous crash)
    if (Player* player = ToPlayer())
    {
        if (Pet* pPet = player->GetPet())
        {
            if (pPet->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED) && !pPet->HasUnitState(UNIT_STATE_STUNNED))
                pPet->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
        }
        else
        {
            player->ResummonPetTemporaryUnSummonedIfAny();
            player->SummonLastSummonedBattlePet();
        }
    }
}

bool Unit::NeedDismount()
{
    if (Player* player = ToPlayer())
        return !player->HasFlag(PLAYER_FIELD_LOCAL_FLAGS, PLAYER_LOCAL_FLAG_CAN_USE_OBJECTS_MOUNTED);
    return true;
}

void Unit::UpdateMount()
{
    MountCapabilityEntry const* newMount = nullptr;
    AuraEffect* effect = nullptr;

    uint32 MountTypeID = 0;
    if (AuraEffectList const* auras = GetAuraEffectsByType(SPELL_AURA_MOUNTED))
        for (AuraEffectList::const_iterator itr = auras->begin(); itr != auras->end(); ++itr)
        {
            AuraEffect* aura = *itr;
            if (MountEntry const* mountEntry = sDB2Manager.GetMount(aura->GetId()))
                MountTypeID = mountEntry->MountTypeID;
            if (MountTypeID)
            {
                effect = aura;
                break;
            }
        }

    if (MountTypeID)
    {
        DB2Manager::MountTypeXCapabilitySet const* capabilities = sDB2Manager.GetMountCapabilities(MountTypeID);
        if (!capabilities)
            return;

        uint32 zoneId = GetCurrentZoneID();
        uint32 areaId = GetCurrentAreaID();

        if (!zoneId || !areaId) // Not in game, waiting...
            return;

        bool ignoreMountFlags = MountTypeID == 291; // Hack for paladin Divine Steed

        uint32 ridingSkill = 5000;
        if (IsPlayer())
            ridingSkill = ToPlayer()->GetSkillValue(SKILL_RIDING);

        uint32 currentMountFlags = 0;
        AuraEffectList const* auras = GetAuraEffectsByType(SPELL_AURA_MOD_FLYING_RESTRICTIONS);
        if (auras && auras->begin() != auras->end())
        {
            for (AuraEffectList::const_iterator itr = auras->begin(); itr != auras->end(); ++itr)
                currentMountFlags |= (*itr)->GetMiscValue();
        }
        else
        {
            AreaTableEntry const* entry = sAreaTableStore.LookupEntry(areaId);
            if (!entry)
                entry = sAreaTableStore.LookupEntry(zoneId);
            if (entry)
                currentMountFlags = entry->MountFlags;
        }

        for (MountTypeXCapabilityEntry const* mountTypeXCapability : *capabilities)
        {
            MountCapabilityEntry const* mountCapability = sMountCapabilityStore.LookupEntry(mountTypeXCapability->MountCapabilityID);
            if (!mountCapability)
                continue;

            if (ridingSkill < mountCapability->ReqRidingSkill)
                continue;

            uint32 reqFlags = mountCapability->Flags;

            uint32 MountTypeMiscB = 0;
            if (auto const* auras = GetAuraEffectsByType(SPELL_AURA_MOUNTED))
                for (auto const& aura : *auras)
                    MountTypeMiscB = aura->GetMiscValueB();

            if (!ignoreMountFlags)
            {
                if (reqFlags & 1 && !(currentMountFlags & 1))
                    continue;

                if (reqFlags & 2 && !(currentMountFlags & MOUNT_FLAG_SELF_MOUNT))
                    continue;

                if (reqFlags & MOUNT_CAPABILITY_FLAG_FLOAT && !(currentMountFlags & 4))
                    continue;

                if (reqFlags & MOUNT_CAPABILITY_FLAG_UNDERWATER && !(currentMountFlags & 8))
                    continue;
            }

            //if (m_movementInfo.hasPitch) not valid
            //    if (!(reqFlags & MOUNT_CAPABILITY_FLAG_FLOAT))
            //        continue;

            if (HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING))
                if (!(reqFlags & MOUNT_CAPABILITY_FLAG_UNDERWATER) && MountTypeMiscB != 248 && MountTypeMiscB != 229)
                    continue;

            if (mountCapability->ReqMapID != -1 && GetMapId() != uint32(mountCapability->ReqMapID))
                continue;

            if (mountCapability->ReqAreaID && (mountCapability->ReqAreaID != zoneId && mountCapability->ReqAreaID != areaId))
                continue;

            if (mountCapability->ReqSpellAuraID && !HasAura(mountCapability->ReqSpellAuraID))
                continue;

            if (mountCapability->ReqSpellKnownID && (!IsPlayer() || !ToPlayer()->HasSpell(mountCapability->ReqSpellKnownID)))
                continue;

            newMount = mountCapability;
            break;
        }
    }

    if (_mount != newMount)
    {
        uint32 oldSpell = _mount ? _mount->ModSpellAuraID : 0;
        bool oldFlyer = _mount ? (_mount->Flags & 2) : false;
        uint32 newSpell = newMount ? newMount->ModSpellAuraID : 0;
        bool newFlyer = newMount ? (newMount->Flags & 2) : false;

        // This is required for displaying speeds on aura
        if (effect)
            effect->SetAmount(newMount ? newMount->ID : 0);

        if (oldSpell != newSpell)
        {
            if (oldSpell)
                RemoveAurasDueToSpell(oldSpell);

            if (newSpell)
                CastSpell(this, newSpell, true, nullptr, effect);
        }

        if (oldFlyer != newFlyer)
            SetCanFly(newFlyer);

        if (!(oldFlyer ^ newFlyer))
            SetCanFly(newFlyer);

        _mount = newMount;
    }
}

MountCapabilityEntry const* Unit::GetMountCapability(uint32 mountType) const
{
    if (!mountType)
        return nullptr;

    auto capabilities = sDB2Manager.GetMountCapabilities(mountType);
    if (!capabilities)
        return nullptr;

    uint32 areaId = GetCurrentAreaID();
    uint32 ridingSkill = 5000;
    uint32 mountFlags = 0;
    auto isSubmerged = false;
    auto isInWater = false;

    if (IsPlayer())
        ridingSkill = const_cast<Player*>(ToPlayer())->GetSkillValue(SKILL_RIDING);

    auto auras = GetAuraEffectsByType(SPELL_AURA_MOD_FLYING_RESTRICTIONS);
    if (auras && auras->begin() != auras->end())
    {
        for (auto itr = auras->begin(); itr != auras->end(); ++itr)
            mountFlags |= (*itr)->GetMiscValue();
    }
    else if (auto areaTable = sAreaTableStore.LookupEntry(areaId))
        mountFlags = areaTable->MountFlags;

    LiquidData liquid;
    auto liquidStatus = GetMap()->getLiquidStatus(GetPositionX(), GetPositionY(), GetPositionZ(), MAP_ALL_LIQUIDS, &liquid);
    isSubmerged = (liquidStatus & LIQUID_MAP_UNDER_WATER) != 0 || HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
    isInWater = (liquidStatus & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) != 0;

    for (auto mountTypeXCapability : *capabilities)
    {
        auto mountCapability = sMountCapabilityStore.LookupEntry(mountTypeXCapability->MountCapabilityID);
        if (!mountCapability)
            continue;

        if (ridingSkill < mountCapability->ReqRidingSkill)
            continue;

        if (!(mountCapability->Flags & MOUNT_CAPABIILTY_FLAG_IGNORE_RESTRICTIONS))
        {
            if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_GROUND && !(mountFlags & AREA_MOUNT_FLAG_GROUND_ALLOWED))
                continue;
            if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_FLYING && !(mountFlags & AREA_MOUNT_FLAG_FLYING_ALLOWED))
                continue;
            if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_FLOAT && !(mountFlags & AREA_MOUNT_FLAG_FLOAT_ALLOWED))
                continue;
            if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_UNDERWATER && !(mountFlags & AREA_MOUNT_FLAG_UNDERWATER_ALLOWED))
                continue;
        }

        if (!isSubmerged)
        {
            if (!isInWater)
            {
                // player is completely out of water
                if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_GROUND))
                    continue;
            }
            else if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_UNDERWATER))
                continue;
        }
        else if (isInWater)
        {
            if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_UNDERWATER))
                continue;
        }
        else if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_FLOAT))
            continue;

        if (mountCapability->ReqMapID != -1 && int32(GetMapId()) != mountCapability->ReqMapID && GetMap()->GetEntry()->CosmeticParentMapID != mountCapability->ReqMapID && GetMap()->GetEntry()->ParentMapID != mountCapability->ReqMapID)
            continue;

        if (mountCapability->ReqAreaID && !DB2Manager::IsInArea(areaId, mountCapability->ReqAreaID))
            continue;

        if (mountCapability->ReqSpellAuraID && !HasAura(mountCapability->ReqSpellAuraID))
            continue;

        if (mountCapability->ReqSpellKnownID && !const_cast<Unit*>(this)->HasSpell(mountCapability->ReqSpellKnownID))
            continue;

        return mountCapability;
    }

    return nullptr;
}

void Unit::SetInCombatWith(Unit* enemy)
{
    Unit* eOwner = enemy->GetCharmerOrOwnerOrSelf();
    if (eOwner->IsPvP() || eOwner->IsFFAPvP())
    {
        SetInCombatState(enemy, true);
        return;
    }

    // check for duel
    if (eOwner->IsPlayer() && eOwner->ToPlayer()->duel)
    {
        Unit const* myOwner = GetCharmerOrOwnerOrSelf();
        if (eOwner->ToPlayer()->duel->opponent == myOwner->GetGUID())
        {
            SetInCombatState(enemy, true);
            return;
        }
    }

    SetInCombatState(enemy);
}

void Unit::CombatStart(Unit* target, bool initialAggro)
{
    if (initialAggro)
    {
        if (!isInCombat())
            RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_COMBAT);

        if (!target->IsStandState())
            target->SetStandState(UNIT_STAND_STATE_STAND);

        if (!target->isInCombat() && !target->IsPlayer() && !target->ToCreature()->HasReactState(REACT_PASSIVE) && target->ToCreature()->IsAIEnabled)
            target->ToCreature()->AI()->AttackStart(this);

        SetInCombatWith(target);
        target->SetInCombatWith(this);
        
        if (this->IsPlayer())
            sScriptMgr->OnPlayerEnterCombat(this->ToPlayer(), target);
    }

    Unit* who = target->GetCharmerOrOwnerOrSelf();
    if (who->IsPlayer())
        SetContestedPvP(who->ToPlayer());

    Player* me = GetCharmerOrOwnerPlayerOrPlayerItself();
    if (me && who->IsPvP() && (!who->IsPlayer() || !me->duel || me->duel->opponent != who->GetGUID()))
    {
        me->UpdatePvP(true);
        me->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
    }
}

void Unit::SetInCombatState(Unit* enemy, bool PvP)
{
    // only alive units can be in combat
    if (!isAlive())
        return;

    // if (PvP) // not need, if player kill target combat stop automatic
        SetCombatTimer(5000);

		if (Player* player = ToPlayer())
		{
			if (sWorld->getBoolConfig(CONFIG_PLAYER_ALLOW_PVP_TALENTS_ALL_THE_TIME) && player->getLevel() >= 110)
			{
				if (!player->HasPvpRulesEnabled())
					player->EnablePvpRules(false);
			}
			else
			{
				if (PvP)
					player->EnablePvpRules();
			}
		}

    if (isInCombat() || HasUnitState(UNIT_STATE_EVADE))
        return;

    UpdatePowerState();
    SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);

    if (Creature* creature = ToCreature())
    {
        // Set home position at place of engaging combat for escorted creatures
        if ((IsAIEnabled && creature->AI()->IsEscorted()) || GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE || GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
            creature->SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());

        if (enemy)
        {
            if (IsAIEnabled)
            {
                creature->AI()->EnterCombat(enemy);
                RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); // unit has engaged in combat, remove immunity so players can fight back
            }

            if (creature->GetFormation())
                creature->GetFormation()->MemberAttackStart(creature, enemy);

            if (m_zoneScript)
                m_zoneScript->EnterCombatForScript(creature, enemy);
        }

        if (!(creature->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_MOUNTED_COMBAT))
            Dismount();
    }

    for (auto const& guid : m_Controlled)
    {
        if(Unit* unit = ObjectAccessor::GetUnit(*this, guid))
        {
            unit->SetInCombatState(enemy, PvP);
            unit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
        }
    }
}

void Unit::ClearInCombat()
{
    m_CombatTimer = 0;
    UpdatePowerState(false);
    RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);

    // Player's state will be cleared in Player::UpdateContestedPvP
    if (Creature* creature = ToCreature())
    {
        if (creature->GetCreatureTemplate() && creature->GetCreatureTemplate()->unit_flags & UNIT_FLAG_IMMUNE_TO_PC)
            SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); // set immunity state to the one from db on evade

        ClearUnitState(UNIT_STATE_ATTACK_PLAYER);
        if (HasFlag(OBJECT_FIELD_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED))
            SetUInt32Value(OBJECT_FIELD_DYNAMIC_FLAGS, creature->GetCreatureTemplate()->dynamicflags);

        if (creature->isPet() || creature->m_isHati)
        {
            if (Unit* owner = GetOwner())
                for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
                    if (owner->GetSpeedRate(UnitMoveType(i)) > GetSpeedRate(UnitMoveType(i)))
                        SetSpeed(UnitMoveType(i), CalculatePct(owner->GetSpeedRate(UnitMoveType(i)), 114.0f), true);
        }
        else if (!isCharmed())
            return;
    }
    else
    {
        if (Player* plr = ToPlayer())
        {
            plr->UpdatePotionCooldown();

            if (plr->HasPvpRulesEnabled())
            {
                if (!plr->HasPvpStatsScalingEnabled() && !plr->GetUInt32Value(PLAYER_FIELD_DUEL_TEAM) && !plr->IsAreaThatActivatesPvpTalents(plr->GetCurrentAreaID()))
                {
                    if (Aura* aura = GetAura(SPELL_PVP_RULES_ENABLED))
                    {
                        int32 maxDur = aura->GetSpellInfo()->GetMaxDuration();

                        aura->SetMaxDuration(maxDur);
                        aura->SetDuration(maxDur);
                        plr->SetPvpRulesTimer(true);
                    }
                }
            }
        }
    }

    RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);

    bool encounterProgress = false;

    if (InstanceScript* instance = GetInstanceScript())
        if (ToPlayer() && instance->IsEncounterInProgress())
            encounterProgress = true;

    if (!encounterProgress)
        RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LEAVE_COMBAT);
}

bool Unit::isTargetableForAttack(bool checkFakeDeath) const
{
    if (!isAlive())
        return false;

    if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC))
        return false;

    if (IsPlayer() && ToPlayer()->isGameMaster())
        return false;

    return !HasUnitState(UNIT_STATE_UNATTACKABLE) && (!checkFakeDeath || !HasUnitState(UNIT_STATE_DIED));
}

bool Unit::IsValidAttackTarget(Unit const* target) const
{
    return _IsValidAttackTarget(target, nullptr);
}

// function based on function Unit::CanAttack from 13850 client
bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, WorldObject const* obj) const
{
    // can't attack self
    if (!target || this == target)
        return false;

    if (auto player = target->ToPlayer())
        if (player->InBattleground() && (player->HasAura(SPELL_BG_PREPARATION) || player->HasAura(SPELL_ARENA_PREPARATION)))
            return false;

    if (Creature const* creatureTarget = target->ToCreature()) // Prismatic Crystal
        if((creatureTarget->GetEntry() == 76933 && target->IsOwnerOrSelf(const_cast<Unit*>(this))) || (creatureTarget->GetEntry() == 103679 && target->IsOwnerOrSelf(const_cast<Unit*>(this))))
            return true;

    // Sha of anger mind control || Crawler mine (Iron Juggernaut)
    if (target->HasAura(119626) || target->HasAura(144718))
        return true;

    // can't attack unattackable units or GMs
    if (target->HasUnitState(UNIT_STATE_UNATTACKABLE) || (target->IsPlayer() && target->ToPlayer()->isGameMaster()))
        return false;

    // can't attack invisible (ignore stealth for aoe spells) also if the area being looked at is from a spell use the dynamic object created instead of the casting unit.
    if ((!bySpell || !(bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE))) && (obj ? !obj->canSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()) : !canSeeOrDetect(target, bySpell && bySpell->IsAffectingArea())))
        return false;

    // can't attack dead
    if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && !target->isAlive())
       return false;

    // can't attack untargetable
    if ((!bySpell || !(bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE))) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
        return false;

    if (Player const* player = ToPlayer())
    {
        if (player->HasFlag(PLAYER_FIELD_PLAYER_FLAGS, PLAYER_FLAGS_COMMENTATOR_UBER))
            return false;
        if (player->GetGroup() && (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_LFG) || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_LFR)) && player->GetGroup()->isLFGGroup() && IsInRaidWith(target))
            return false;
    }

    // check flags
    if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)
        || (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC))
        || (!target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC)))
        return false;

    if ((!bySpell || !(bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG)))
        && (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC))
        // check if this is a world trigger cast - GOs are using world triggers to cast their spells, so we need to ignore their immunity flag here, this is a temp workaround, needs removal when go cast is implemented properly
        && GetEntry() != WORLD_TRIGGER)
        return false;

    // CvC case - can attack each other only when one of them is hostile
    if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
        return GetReactionTo(target) <= REP_HOSTILE || target->GetReactionTo(this) <= REP_HOSTILE;

    // PvP, PvC, CvP case
    // can't attack friendly targets
    if ( GetReactionTo(target) > REP_NEUTRAL || target->GetReactionTo(this) > REP_NEUTRAL)
        return false;

    // Not all neutral creatures can be attacked
    if (GetReactionTo(target) == REP_NEUTRAL && target->GetReactionTo(this) == REP_NEUTRAL)
    {
        if (!(target->IsPlayer() && IsPlayer()) && !(target->IsCreature() && IsCreature()))
        {
            Player const* player = target->IsPlayer() ? target->ToPlayer() : ToPlayer();
            Unit const* creature = target->IsCreature() ? target : this;

            if (FactionTemplateEntry const* factionTemplate = creature->getFactionTemplateEntry())
                if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->Faction))
                    if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry))
                        if (!(repState->Flags & FACTION_FLAG_AT_WAR))
                            return false;
        }
    }

    Creature const* creatureAttacker = ToCreature();
    if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_TREAT_AS_RAID_UNIT)
        return false;

    Player const* playerAffectingAttacker = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr;
    Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr;

    // check duel - before sanctuary checks
    if (playerAffectingAttacker && playerAffectingTarget)
        if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget->GetGUID() && playerAffectingAttacker->duel->state == DUEL_STARTED)
            return true;

    // PvP case - can't attack when attacker or target are in sanctuary
    // however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp
    if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)
        && ((target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_SANCTUARY) || (GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_SANCTUARY)))
        return false;

    if (!sWorld->IsPvPRealm() && !IsFFAPvP() && !target->IsFFAPvP() && !GetMap()->IsBattlegroundOrArena())
        if (Player const* player = GetAffectingPlayer())
            if (Player const* playerTarget = target->GetAffectingPlayer())
                if ((playerTarget->IsPvP() && !player->IsPvP()) || (!playerTarget->IsPvP() && player->IsPvP()))
                    return false;

    // additional checks - only PvP case
    if (playerAffectingAttacker && playerAffectingTarget)
    {
        if (target->IsPvP())
            return true;

        if (IsFFAPvP() && target->IsFFAPvP())
            return true;

        return (GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_UNK1) || (target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_UNK1);
    }
    return true;
}

bool Unit::IsValidAssistTarget(Unit const* target) const
{
    return _IsValidAssistTarget(target, nullptr);
}

// function based on function Unit::CanAssist from 13850 client
bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const
{
    if(!target)
        return false;

    // can assist to self
    if (!target || this == target)
        return true;

    //Check for pets(need for Wild Mushroom)
    if(target->GetDemonCreatorGUID().GetGUIDLow() == GetGUIDLow() && !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
        return true;

    // can't assist unattackable units or GMs
    if (target->HasUnitState(UNIT_STATE_UNATTACKABLE) || (target->IsPlayer() && target->ToPlayer()->isGameMaster()))
        return false;

    // can't assist own vehicle or passenger
    if (m_vehicle)
        if (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target))
            return false;

    // can't assist invisible
    if ((!bySpell || !(bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE))) && !canSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()))
        return false;

    // can't assist dead
    if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && !target->isAlive())
       return false;

    // can't assist untargetable
    if ((!bySpell || !(bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE))) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
        return false;

    if (!bySpell || !(bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)))
    {
        if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
        {
            if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC))
                return false;
        }
        else
        {
            if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC))
                return false;
        }
    }

    if (Player const* player = ToPlayer())
        if (player->GetGroup() && (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_LFG) || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_LFR)) && player->GetGroup()->isLFGGroup() && IsInRaidWith(target))
            return true;

    // TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "_IsValidAssistTarget Id %i GetReactionTo %i guid %u target %u target %u", bySpell ? bySpell->Id : 0, GetReactionTo(target), GetGUIDLow(), target->GetDemonCreatorGUID().GetGUIDLow(), target->GetGUIDLow());
    // can't assist non-friendly targets
    if (GetReactionTo(target) <= REP_NEUTRAL && target->GetReactionTo(this) <= REP_NEUTRAL && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_TREAT_AS_RAID_UNIT)))
        return false;

    // PvP case
    if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
    {
        Player const* targetPlayerOwner = target->GetAffectingPlayer();
        if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE))
        {
            Player const* selfPlayerOwner = GetAffectingPlayer();
            if (selfPlayerOwner && targetPlayerOwner)
                if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner->duel) // can't assist player which is dueling someone
                    return false;

            // can't assist player in ffa_pvp zone from outside
            if (!bySpell || !(bySpell->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS)))
                if (target->IsFFAPvP() && !IsFFAPvP())
                    return false;

            // can't assist player out of sanctuary from sanctuary if has pvp enabled
            if (target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_PVP)
                if ((GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_SANCTUARY) && !(target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_SANCTUARY))
                    return false;
        }
    }
    // PvC case - player can assist creature only if has specific type flags
    // !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) &&
    else if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && (!bySpell || !(bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))) && !((target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_PVP)))
        if (Creature const* creatureTarget = target->ToCreature())
            return creatureTarget->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_TREAT_AS_RAID_UNIT || creatureTarget->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_CAN_ASSIST;
   
    return true;
}

int64 Unit::ModifyHealth(int64 dVal, Unit* victim /*= NULL*/, uint32 spellId /*= 0*/)
{
    int64 gain = 0;

    if (dVal == 0)
        return 0;

    int64 curHealth = static_cast<int64>(GetHealth(victim));
    int64 val = dVal + curHealth;
    bool canModHP = true;

    if (val <= 0)
    {
        SetHealth(0, spellId);
        gain -= curHealth;
        canModHP = false;
    }

    if (canModHP)
    {
        int64 maxHealth = static_cast<int64>(GetMaxHealth(victim));

        if (val < maxHealth)
        {
            SetHealthScal(val, victim, spellId);
            gain = val - curHealth;
        }
        else if (curHealth != maxHealth)
        {
            SetHealthScal(maxHealth, victim, spellId);
            gain = maxHealth - curHealth;
        }
    }

    if(dVal < 0)
    {
        if (Player* player = GetCharmerOrOwnerPlayerOrPlayerItself())
        {
            WorldPackets::Combat::HealthUpdate packet;
            packet.Guid = GetGUID();
            packet.Health = GetHealth();
            player->SendDirectMessage(packet.Write());
        }
    }

    return gain;
}

int64 Unit::GetHealthGain(int64 dVal)
{
    int64 gain = 0;

    if (dVal == 0)
        return 0;

    int64 curHealth = static_cast<int64>(GetHealth());

    int64 val = dVal + curHealth;
    if (val <= 0)
    {
        return -curHealth;
    }

    int64 maxHealth = static_cast<int64>(GetMaxHealth());

    if (val < maxHealth)
        gain = dVal;
    else if (curHealth != maxHealth)
        gain = maxHealth - curHealth;

    return gain;
}

void Unit::VisualForPower(Powers power, int32 curentVal, int32 modVal, int32 maxPower, bool generate/* = false*/, SpellInfo const* spellInfo/* = nullptr*/)
{
    Player* player = ToPlayer();
    if(!player)
        return;

    int32 oldVal = GetPower(power);
    if(modVal > 0)
    {
        if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(SPELL_AURA_PROC_ON_POWER_PCT))
        for (AuraEffectList::const_iterator i = mTotalAuraList->begin(); i != mTotalAuraList->end(); ++i)
        {
            if ((*i)->GetMiscValue() != power)
                continue;

            uint32 triggered_spell_id = (*i)->GetTriggerSpell() ? (*i)->GetTriggerSpell(): 0;
            if(!sSpellMgr->GetSpellInfo(triggered_spell_id))
                continue;

            uint32 curCount = CountPctFromMaxPower((*i)->GetAmount(), power);
            if (oldVal <= curCount && curentVal >= curCount)
                if ((*i)->GetMiscValueB() == 0)
                    CastSpell(this, triggered_spell_id, TriggerCastFlags(TRIGGERED_FULL_MASK &~ TRIGGERED_IGNORE_CASTER_AURASTATE), nullptr, (*i)); // 19940
        }

        int32 maxCur = curentVal > maxPower ? maxPower : curentVal;

        if (AuraEffectList const* mTotalAuraList2 = GetAuraEffectsByType(SPELL_AURA_PROC_ON_POWER_AMOUNT))
        for (AuraEffectList::const_iterator i = mTotalAuraList2->begin(); i != mTotalAuraList2->end(); ++i)
        {
            if ((*i)->GetMiscValue() != power)
                continue;

            uint32 triggered_spell_id = (*i)->GetTriggerSpell() ? (*i)->GetTriggerSpell(): 0;
            if(!sSpellMgr->GetSpellInfo(triggered_spell_id))
                continue;

            if ((*i)->GetMiscValueB() != 0)
                continue;

            if ((*i)->GetAmount() > oldVal && maxCur >= (*i)->GetAmount())
            {
                CastSpell(this, triggered_spell_id, true, nullptr, (*i));
            }
        }
    }
    else
    {
        if (!generate)
            m_everyPower[power] -= modVal;

        if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(SPELL_AURA_PROC_ON_POWER_PCT))
        for (AuraEffectList::const_iterator i = mTotalAuraList->begin(); i != mTotalAuraList->end(); ++i)
        {
            if ((*i)->GetMiscValue() != power)
                continue;

            uint32 triggered_spell_id = (*i)->GetTriggerSpell() ? (*i)->GetTriggerSpell(): 0;
            if(!sSpellMgr->GetSpellInfo(triggered_spell_id))
                continue;

            uint32 curCount = CountPctFromMaxPower((*i)->GetAmount(), power);
            if (oldVal >= curCount && curentVal <+ curCount)
                if ((*i)->GetMiscValueB() == 1)
                    CastSpell(this, triggered_spell_id, true, nullptr, (*i));
        }

        if (AuraEffectList const* mTotalAuraList2 = GetAuraEffectsByType(SPELL_AURA_PROC_ON_POWER_AMOUNT))
        for (AuraEffectList::const_iterator i = mTotalAuraList2->begin(); i != mTotalAuraList2->end(); ++i)
        {
            if ((*i)->GetMiscValue() != power)
                continue;

            uint32 triggered_spell_id = (*i)->GetTriggerSpell() ? (*i)->GetTriggerSpell(): 0;
            if(!sSpellMgr->GetSpellInfo(triggered_spell_id))
                continue;

            if (oldVal >= (*i)->GetAmount() && curentVal <= (*i)->GetAmount())
                if ((*i)->GetMiscValueB() == 1)
                    CastSpell(this, triggered_spell_id, true, nullptr, (*i));
        }
    }

    uint32 specId = player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID);

    switch(power)
    {
        case POWER_SOUL_SHARDS:
        {
            if (modVal < 0)
            {
                if (AuraEffect* aurEff = GetAuraEffect(215941, EFFECT_0)) // Soul Conduit
                {
                    int8 _count = abs(modVal) / 10.f;
                    for (int i = 0; i < _count; i++)
                        if (roll_chance_i(aurEff->GetAmount()))
                        {
                            player->AddDelayedEvent(100, [player]() -> void
                            {
                                player->CastSpell(player, 215942, true);
                            });
                        }
                }
                if (HasAura(236199)) // Wakener's Loyalty
                {
                    int8 _count = abs(modVal) / 10.f;
                    for (int i = 0; i < _count; i++)
                    {
                        player->AddDelayedEvent(100, [player]() -> void
                        {
                            player->CastSpell(player, 236200, true);
                        });
                    }
                }
            }
            break;
        }
        case POWER_ARCANE_CHARGES:
        {
            if (modVal < 0)
            {
                if (AuraEffect* aurEff = GetAuraEffect(209280, EFFECT_0)) // Mystic Kilt of the Rune Master
                {
                    float bp0 = abs(modVal) * aurEff->GetAmount();
                    CastCustomSpell(this, 209302, &bp0, nullptr, nullptr, true);
                }
                if (AuraEffect* aurEff = GetAuraEffect(248098, EFFECT_0)) // Arcane Barrage Procs Arcane Orb
                {
                    int8 bp = abs(modVal) * aurEff->GetAmount();
                    if (roll_chance_i(bp))
                    {
                        player->AddDelayedEvent(100, [player]() -> void
                        {
                            player->CastSpell(player, 153626, true);
                        });
                    }
                }
                if (AuraEffect* aurEff = GetAuraEffect(251861, EFFECT_0)) // Item - Mage T21 Arcane 2P Bonus
                {
                    float bp = abs(modVal) * aurEff->GetAmount();
                    CastCustomSpell(this, 253262, &bp, nullptr, nullptr, true);
                }
            }
            break;
        }
        case POWER_RUNIC_POWER:
        {
            if (modVal < 0 && !generate)
            {
                if (spellInfo && player->GetSpecializationId() == SPEC_DK_FROST)
                {
                    if (AuraEffect* auraInfo = player->GetAuraEffect(81229, EFFECT_0)) // Runic Empowerment
                    {
                        int32 powerCost = spellInfo->Power.PowerCost ? spellInfo->Power.PowerCost : spellInfo->Power.PowerCostPerSecond;
                        float chance = abs(powerCost * auraInfo->GetAmount() / 100.0f);

                        if (roll_chance_f(chance))
                            player->CastSpell(player, 193486, true);
                    }
                }
                if (AuraEffect* aurEff = GetAuraEffect(242057, EFFECT_0)) // Item - Death Knight T20 Frost 2P Bonus
                {
                    if (m_everyPower[power] >= aurEff->GetAmount() * 30.f)
                    {
                        if (Aura* aura = GetAura(51271))
                        {
                            aura->SetDuration(aura->GetDuration() + 1000);
                        }
                        m_everyPower[power] -= aurEff->GetAmount() * 30.f;
                    }
                }
                if (HasSpell(205723)) // Red Thirst
                {
                    if (m_everyPower[power] > 100)
                    {
                        int32 countMod = int32(m_everyPower[power] / 100);
                        int32 modColdown = countMod * -1000;
                        if (SpellInfo const* _spellInfo = sSpellMgr->GetSpellInfo(205723))
                            modColdown *= _spellInfo->Effects[EFFECT_0]->CalcValue(this);
                        player->ModifySpellCooldown(55233, modColdown);
                        m_everyPower[power] -= (100 * countMod);
                    }
                }
            }
            break;
        }
        case POWER_RUNES:
        {
            if (modVal < 0 && isInCombat())
            {
                if (AuraEffect* aurEff = GetAuraEffect(242063, EFFECT_0)) // Item - Death Knight T20 Frost 4P Bonus
                {
                    if (m_everyPower[power] >= aurEff->GetAmount())
                    {
                        CastSpell(this, 247240, true);
                        m_everyPower[power] -= aurEff->GetAmount();
                    }
                }
            }
            break;
        }
        case POWER_CHI:
        {
            if (modVal < 0)
            {
                if (Aura* aur = GetAura(248643)) // Tigereye Brew (Honor Talent)
                {
                    if (m_everyPower[power] >= 3)
                    {
                        CastSpell(this, 248646, true);
                        CastSpell(this, 248689, true);
                        m_everyPower[power] -= 3;
                    }
                }
            }
            break;
        }
        case POWER_INSANITY:
        {
            if (AuraEffect* aurEff = GetAuraEffect(193173, EFFECT_0)) // Mania
            {
                float speed = GetPowerPct(power) / aurEff->GetAmount();
                if (AuraEffect* aurEffSpeed = GetAuraEffect(195290, EFFECT_0)) // Mania
                {
                    if (speed != aurEffSpeed->GetAmount())
                        aurEffSpeed->ChangeAmount(speed);
                }
                else
                    CastCustomSpell(this, 195290, &speed, nullptr, nullptr, true);
            }

            if (modVal < 0 && (curentVal < 10000 || (curentVal < 6500 && HasAura(193225))))
                RemoveAurasDueToSpell(218413);

            if (!curentVal)
            {
                if (HasAura(193223))
                    CastSpell(this, 195455, true);
                else
                    RemoveAurasDueToSpell(194249);
            }
            break;
        }
        default:
            break;
    }
}

void Unit::UpdatePowerState(bool inCombat)
{
    Player* player = ToPlayer();
    if (!player)
        return;

    if (!inCombat)
    {
        for (ChrClassesXPowerTypesEntry const* powerClassEntry : sChrClassesXPowerTypesStore)
        {
            if (powerClassEntry->ClassID != getClass())
                continue;
            PowerTypeEntry const* powerEntry = sDB2Manager.GetPowerType(powerClassEntry->PowerType);
            if (!powerEntry)
                continue;

            if (powerEntry->RegenInterruptTimeMS)
                player->setPowerCombatTimer(powerClassEntry->PowerType, powerEntry->RegenInterruptTimeMS);
        }
    }
}

Powers Unit::GetPowerTypeBySpecId(uint32 specID)
{
    if (ShapeshiftForm form = GetShapeshiftForm())
    {
        switch (form)
        {
            case FORM_FIERCE_TIGER:
            case FORM_STURDY_OX:
            case FORM_GHOUL:
            case FORM_CAT:
                return POWER_ENERGY;
            case FORM_BEAR:
                return POWER_RAGE;
            case FORM_SPIRITED_CRANE:
            case FORM_WISE_SERPENT:
            case FORM_GHOSTWOLF:
                return POWER_MANA;
            default:
                break;
        }
    }

    switch (specID)
    {
        case SPEC_DRUID_BALANCE:
            return POWER_LUNAR_POWER;
        case SPEC_PRIEST_SHADOW:
            return POWER_INSANITY;
        case SPEC_SHAMAN_ELEMENTAL:
        case SPEC_SHAMAN_ENHANCEMENT:
            return POWER_MAELSTROM;
        case SPEC_MONK_MISTWEAVER:
            return POWER_MANA;
        case SPEC_DEMON_HUNER_VENGEANCE:
            return POWER_PAIN;
    }
    if (ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(getClass()))
        return Powers(cEntry->DisplayPower);
    return getPowerType();
}

void Unit::AddComboPoints(int8 count)
{
    ModifyPower(POWER_COMBO_POINTS, count);
}

void Unit::SetPowerCost(SpellPowerCost power)
{
    for (uint32 i = 0; i <= MAX_POWERS; ++i)
        m_powerCost[i] = power[i];
}

void Unit::ResetPowerCost()
{
    m_powerCost.assign(MAX_POWERS + 1, 0);
}

int32 Unit::GetPowerCost(int8 power)
{
    if (power > MAX_POWERS || power < 0)
        return m_powerCost[MAX_POWERS];
 
    return m_powerCost[power];
}

uint8 Unit::GetComboPoints() const
{
    uint8 points_ = const_cast<Unit*>(this)->GetPowerCost(POWER_COMBO_POINTS);
    uint8 add = 0;
    uint8 cost = 5;
    uint8 mod = 0;

    if(HasAura(138148))
        add += 1;

    mod = 5 - cost;

    TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "GetComboPoints %i", points_);

    return ((points_ + mod) > 6 ? 6 : (points_ + mod)) + add;
}

// returns negative amount on power reduction
int32 Unit::ModifyPower(Powers power, int32 dVal, bool set /*= false*/, SpellInfo const* spellInfo/* = nullptr*/)
{
    uint32 powerIndex = GetPowerIndex(power);
    if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
        return 0;

    int32 gain = 0;

    if (dVal == 0 && power != POWER_ENERGY) // The client will always regen energy if we don't send him the actual value
        return 0;

    if (dVal > 0)
        dVal *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_GENERATE_PCT, power);

    int32 curPower = GetPower(power);

    int32 val = dVal + curPower;
    int32 maxPower = GetMaxPower(power);

    //Visualization for power
    VisualForPower(power, val, dVal, maxPower, false, spellInfo);

    if (val <= 0)
    {
        if (set)
            SetPower(power, 0);
        else
            SetInt32Value(UNIT_FIELD_POWER + powerIndex, 0);

        return -curPower;
    }

    if (val < maxPower)
    {
        if (set)
            SetPower(power, val);
        else
            SetInt32Value(UNIT_FIELD_POWER + powerIndex, val);
        gain = val - curPower;
    }
    else if (curPower != maxPower)
    {
        if (set)
            SetPower(power, maxPower);
        else
            SetInt32Value(UNIT_FIELD_POWER + powerIndex, maxPower);
        gain = maxPower - curPower;
    }

    return gain;
}

// returns negative amount on power reduction
int32 Unit::ModifyPowerPct(Powers power, float pct, bool apply)
{
    float amount = static_cast<float>(GetMaxPower(power));
    ApplyPercentModFloatVar(amount, pct, apply);

    return ModifyPower(power, static_cast<int32>(amount) - GetMaxPower(power));
}

bool Unit::IsAlwaysVisibleFor(WorldObject const* seer) const
{
    if (WorldObject::IsAlwaysVisibleFor(seer))
        return true;

    // Always seen by owner
    if (ObjectGuid guid = GetCharmerOrOwnerGUID())
        if (seer->GetGUID() == guid)
            return true;

    if (Player const* seerPlayer = seer->ToPlayer())
        if (Unit* owner =  GetOwner())
            if (Player* ownerPlayer = owner->ToPlayer())
                if (ownerPlayer->IsGroupVisibleFor(seerPlayer))
                    return true;

    return false;
}

bool Unit::IsAlwaysDetectableFor(WorldObject const* seer) const
{
    if (WorldObject::IsAlwaysDetectableFor(seer))
        return true;

    if (HasAuraTypeWithCaster(SPELL_AURA_MOD_STALKED, seer->GetGUID()))
        return true;

    return false;
}

void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
{
    int32 main_speed_mod  = 0;
    int32 stack_bonus_mod  = 0;
    float stack_bonus     = 1.0f;
    float non_stack_bonus = 1.0f;

    switch (mtype)
    {
        // Only apply debuffs
        case MOVE_FLIGHT_BACK:
        case MOVE_RUN_BACK:
        case MOVE_SWIM_BACK:
            break;
        case MOVE_WALK:
            return;
        case MOVE_RUN:
        {
            if (IsMounted()) // Use on mount auras
            {
                main_speed_mod  = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED);
                stack_bonus_mod += GetTotalAuraModifier(SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS);
                non_stack_bonus += GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK) / 100.0f;
            }
            else
            {
                main_speed_mod  = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SPEED) - GetMaxNegativeAuraModifier(SPELL_AURA_MOD_INCREASE_SPEED);
                stack_bonus_mod += GetTotalAuraModifier(SPELL_AURA_MOD_SPEED_ALWAYS);
                non_stack_bonus += GetMaxPositiveAuraModifier(SPELL_AURA_MOD_SPEED_NOT_STACK) / 100.0f;
            }
            break;
        }
        case MOVE_SWIM:
        {
            main_speed_mod  = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SWIM_SPEED);
            break;
        }
        case MOVE_FLIGHT:
        {
            if (IsCreature() && IsControlledByPlayer()) // not sure if good for pet
            {
                main_speed_mod  = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED);
                stack_bonus_mod += GetTotalAuraModifier(SPELL_AURA_MOD_VEHICLE_SPEED_ALWAYS);

                // for some spells this mod is applied on vehicle owner
                int32 owner_speed_mod = 0;

                if (Unit* owner = GetCharmer())
                    owner_speed_mod = owner->GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED);

                main_speed_mod = std::max(main_speed_mod, owner_speed_mod);
            }
            else if (IsMounted())
            {
                main_speed_mod  = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED);
                stack_bonus_mod += GetTotalAuraModifier(SPELL_AURA_MOD_MOUNTED_FLIGHT_SPEED_ALWAYS);
            }
            else             // Use not mount (shapeshift for example) auras (should stack)
                main_speed_mod  = GetTotalAuraModifier(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) + GetTotalAuraModifier(SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED);

            non_stack_bonus += GetMaxPositiveAuraModifier(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK) / 100.0f;

            // Update speed for vehicle if available
            if (IsPlayer() && GetVehicle())
                GetVehicleBase()->UpdateSpeed(MOVE_FLIGHT, true);
            break;
        }
        default:
            TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::UpdateSpeed: Unsupported move type (%d)", mtype);
            return;
    }

    stack_bonus += float(stack_bonus_mod / 100.0f);
    // now we ready for speed calculation
    float speed = std::max(non_stack_bonus, stack_bonus);
    if (main_speed_mod)
        //AddPct(speed, main_speed_mod);
        speed += float(main_speed_mod / 100.0f);

    if (IsPlayer())
        speed += CalculatePct(speed, ToPlayer()->GetFloatValue(PLAYER_FIELD_SPEED));

    switch (mtype)
    {
        case MOVE_RUN:
        case MOVE_SWIM:
        case MOVE_FLIGHT:
        {
            // Set creature speed rate
            if (IsCreature())
            {
                Unit* pOwner = GetCharmerOrOwner();
                if ((isPet() || isGuardian()) && pOwner) // Must check for owner or crash on "Tame Beast"
                {
                    if (!isInCombat())
                    {
                        float dist = GetDistance(pOwner);
                        float base_rate = 1.14f; // base speed is 114% of owner speed

                        if (dist > 5)
                            base_rate += 1.0f;

                        speed *= pOwner->GetSpeedRate(mtype) * base_rate; // pets derive speed from owner when not in combat
                    }
                    else
                        speed = 1.f;
                }
                else
                    speed *= ToCreature()->GetCreatureTemplate()->speed_run;    // at this point, MOVE_WALK is never reached
            }
            // Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need
            // TODO: possible affect only on MOVE_RUN
            if (int32 normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED))
            {
                if (Creature const* creature = ToCreature())
                {
                    uint32 immuneMask = creature->GetCreatureTemplate()->MechanicImmuneMask;
                    if (immuneMask & (1 << (MECHANIC_SNARE - 1)) || immuneMask & (1 << (MECHANIC_DAZE - 1)))
                        break;
                }

                // Use speed from aura
                float max_speed = normalization / (IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]);
                if (speed > max_speed)
                    speed = max_speed;
            }

            if (mtype == MOVE_RUN)
                if (int32 minSpeedMod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MINIMUM_SPEED_RATE))
                {
                    float minSpeed = minSpeedMod / (IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]);
                    if (speed < minSpeed)
                        speed = minSpeed;
                }

            break;
        }
        default:
            break;
    }

    // for creature case, we check explicit if mob searched for assistance
    if (auto creature = ToCreature())
        if (creature->HasSearchedAssistance())
            speed *= 0.66f;                                 // best guessed value, so this will be 33% reduction. Based off initial speed, mob can then "run", "walk fast" or "walk".

    // Apply strongest slow aura mod to speed
    float slow = 0.f;

    if (auto const* mTotalAuraList = GetAuraEffectsByType(SPELL_AURA_MOD_DECREASE_SPEED))
    {
        for (auto const& auraEffect : *mTotalAuraList)
        {
            if (float amount = abs(auraEffect->GetAmount()))
            {
                if (amount > slow)
                    slow = amount;
            }
        }
    }

    if (slow > 0.f)
        AddPct(speed, -slow);

    if (float minSpeedMod = static_cast<float>(GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MINIMUM_SPEED)))
    {
        float min_speed = minSpeedMod / 100.0f;

        bool bgAuras = HasAura(156621) || HasAura(156618) || HasAura(34976) || HasAura(140876) || HasAura(141210);

        if (bgAuras && HasAuraType(SPELL_AURA_FEATHER_FALL))
            min_speed = 1.f;

        if (speed < min_speed && mtype != MOVE_SWIM)
            speed = min_speed;
    }

    if (float minSpeedMod = static_cast<float>(GetMaxPositiveAuraModifier(SPELL_AURA_MOD_SPEED_NO_CONTROL)))
    {
        float minSpeed = minSpeedMod / 100.0f;

        bool bgAuras = HasAura(156621) || HasAura(156618) || HasAura(34976) || HasAura(140876) || HasAura(141210);

        if (bgAuras && HasAuraType(SPELL_AURA_FEATHER_FALL))
            minSpeed = 1.f;

        if (speed < minSpeed)
            speed = minSpeed;
    }

    if (speed <= 0) //crash client
        speed = 0.01f;

    speed /= GetFloatValue(UNIT_FIELD_MOD_TIME_RATE);

    SetSpeed(mtype, speed, forced);
}

float Unit::GetSpeed(UnitMoveType mtype) const
{
    return m_speed_rate[mtype]*(IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]);
}

void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
{
    if (rate < 0)
        rate = 0.0f;

    // Update speed only on change
    if (m_speed_rate[mtype] == rate)
        return;

    m_speed_rate[mtype] = rate;

    propagateSpeedChange();

    // Spline packets are for creatures and move_update are for players
    static OpcodeServer const moveTypeToOpcode[MAX_MOVE_TYPE][3] =
    {
        {SMSG_MOVE_SPLINE_SET_WALK_SPEED,        SMSG_MOVE_SET_WALK_SPEED,        SMSG_MOVE_UPDATE_WALK_SPEED       },
        {SMSG_MOVE_SPLINE_SET_RUN_SPEED,         SMSG_MOVE_SET_RUN_SPEED,         SMSG_MOVE_UPDATE_RUN_SPEED        },
        {SMSG_MOVE_SPLINE_SET_RUN_BACK_SPEED,    SMSG_MOVE_SET_RUN_BACK_SPEED,    SMSG_MOVE_UPDATE_RUN_BACK_SPEED   },
        {SMSG_MOVE_SPLINE_SET_SWIM_SPEED,        SMSG_MOVE_SET_SWIM_SPEED,        SMSG_MOVE_UPDATE_SWIM_SPEED       },
        {SMSG_MOVE_SPLINE_SET_SWIM_BACK_SPEED,   SMSG_MOVE_SET_SWIM_BACK_SPEED,   SMSG_MOVE_UPDATE_SWIM_BACK_SPEED  },
        {SMSG_MOVE_SPLINE_SET_TURN_RATE,         SMSG_MOVE_SET_TURN_RATE,         SMSG_MOVE_UPDATE_TURN_RATE        },
        {SMSG_MOVE_SPLINE_SET_FLIGHT_SPEED,      SMSG_MOVE_SET_FLIGHT_SPEED,      SMSG_MOVE_UPDATE_FLIGHT_SPEED     },
        {SMSG_MOVE_SPLINE_SET_FLIGHT_BACK_SPEED, SMSG_MOVE_SET_FLIGHT_BACK_SPEED, SMSG_MOVE_UPDATE_FLIGHT_BACK_SPEED},
        {SMSG_MOVE_SPLINE_SET_PITCH_RATE,        SMSG_MOVE_SET_PITCH_RATE,        SMSG_MOVE_UPDATE_PITCH_RATE       },
    };

    if (IsPlayer())
    {
        // register forced speed changes for WorldSession::HandleForceSpeedChangeAck
        // and do it only for real sent packets and use run for run/mounted as client expected
        ++ToPlayer()->m_forced_speed_changes[mtype];

        if (!isInCombat())
        {
            if (Pet* pet = ToPlayer()->GetPet())
                pet->SetSpeed(mtype, CalculatePct(m_speed_rate[mtype], 114.0f), forced);

            if (Unit* hati = GetHati())
                hati->SetSpeed(mtype, CalculatePct(m_speed_rate[mtype], 114.0f), forced);

            if (Creature* battlePet = ToPlayer()->GetSummonedBattlePet())
                battlePet->SetSpeed(mtype, CalculatePct(m_speed_rate[mtype], 114.0f), forced);
        }
    }

    if (Player* playerMover = GetPlayerMover()) // unit controlled by a player.
    {
        // Send notification to self
        WorldPackets::Movement::MoveSetSpeed selfpacket(moveTypeToOpcode[mtype][1]);
        selfpacket.MoverGUID = GetGUID();
        selfpacket.SequenceIndex = m_sequenceIndex++;
        selfpacket.Speed = GetSpeed(mtype);
        playerMover->SendDirectMessage(selfpacket.Write());
        playerMover->GetCheatData()->OrderSent(moveTypeToOpcode[mtype][1]);
    }
    else
    {
        // ???
        WorldPackets::Movement::MoveSplineSetSpeed packet(moveTypeToOpcode[mtype][0]);
        packet.MoverGUID = GetGUID();
        packet.Speed = GetSpeed(mtype);
        SendMessageToSet(packet.Write(), true);
    }
}

void Unit::SendAdjustSplineDuration(float scale)
{
    WorldPackets::Movement::AdjustSplineDuration packet;
    packet.Unit = GetGUID();
    packet.Scale = scale;
    SendMessageToSetInRange(packet.Write(), GetMap()->GetVisibilityRange(), false);
}

void Unit::SendFlightSplineSync(float splineDist)
{
    WorldPackets::Movement::FlightSplineSync packet;
    packet.Guid = GetGUID();
    packet.SplineDist = splineDist;
    SendMessageToSetInRange(packet.Write(), GetMap()->GetVisibilityRange(), false);
}

bool Unit::isDead(bool withFeign) const
{
    bool realDeath = m_deathState == DEAD || m_deathState == CORPSE;
    if (realDeath || !withFeign)
        return realDeath;

    if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH))
        return true;

    // Very many time for check, need other release
    // for (AuraApplicationMap::const_iterator itr = m_appliedAuras.begin(); itr != m_appliedAuras.end(); ++itr)
    // {
        // Aura const* aura = itr->second->GetBase();
        // if (!aura)
            // continue;

        // std::string name = aura->GetSpellInfo()->SpellName;
        // std::string part = "Feign";
        // if (strstr(name.c_str(), part.c_str()))
            // return true;
    // }

    return false;
}

void Unit::setDeathState(DeathState s)
{
    m_deathState = s;

    if (s == ALIVE || s == JUST_RESPAWNED)
        ClearSaveThreatTarget();

    if (s != ALIVE && s != JUST_RESPAWNED)
    {
        CombatStop();
        DeleteThreatList();
        getHostileRefManager().deleteReferences();

        if (IsNonMeleeSpellCast(false))
            InterruptNonMeleeSpells(false);

        ExitVehicle();                                      // Exit vehicle before calling RemoveAllControlled
                                                            // vehicles use special type of charm that is not removed by the next function
                                                            // triggering an assert
        UnsummonAllTotems();
        RemoveAllControlled();
        RemoveAllAurasOnDeath();
    }

    if (s == JUST_DIED)
    {
        ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false);
        ModifyAuraState(AURA_STATE_HEALTHLESS_25_PERCENT, false);
        ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false);
        // remove aurastates allowing special moves
        ClearAllReactives();
        ClearDiminishings();
        if (IsInWorld())
        {
            // Only clear MotionMaster for entities that exists in world
            // Avoids crashes in the following conditions :
            //  * Using 'call pet' on dead pets
            //  * Using 'call stabled pet'
            //  * Logging in with dead pets
            GetMotionMaster()->Clear(false);
            GetMotionMaster()->MoveIdle();
        }
        StopMoving();
        DisableSpline();
        // without this when removing IncreaseMaxHealth aura player may stuck with 1 hp
        // do not why since in IncreaseMaxHealth currenthealth is checked
        SetHealth(0);
        //SetPower(getPowerType(), 0);
        SetUInt32Value(UNIT_FIELD_EMOTE_STATE, 0);

        // players in instance don't have ZoneScript, but they have InstanceScript
        if (ZoneScript* zoneScript = GetZoneScript() ? GetZoneScript() : static_cast<ZoneScript*>(GetInstanceScript()))
            zoneScript->OnUnitDeath(this);
        if (ToCreature() && ToCreature()->m_isHati)
            if (Unit* owner = GetOwner())
                owner->CastSpell(owner, 211117, true);
    }
    else if (s == JUST_RESPAWNED)
        RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); // clear skinnable for creature and player (at battleground)
}

bool Unit::CanHaveThreatList() const
{
    // only creatures can have threat list
    if (!IsCreature())
        return false;

    // only alive units can have threat list
    if (!isAlive() || isDying())
        return false;

    // totems can not have threat list
    if (ToCreature()->isTotem())
        return false;

    // vehicles can not have threat list
    //if (ToCreature()->IsVehicle())
    //    return false;

    // summons can not have a threat list, unless they are controlled by a creature
    if (HasUnitTypeMask(UNIT_MASK_MINION | UNIT_MASK_GUARDIAN | UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Pet*)this)->GetOwnerGUID().IsPlayer())
        return false;

    return true;
}

float Unit::ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask)
{
    if (!HasAuraType(SPELL_AURA_MOD_THREAT) || fThreat < 0)
        return fThreat;

    return fThreat * m_threatModifier[GetFirstSchoolInMask(schoolMask)];
}

void Unit::AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell)
{
    // Only mobs can manage threat lists
    if (CanHaveThreatList())
        m_ThreatManager.addThreat(victim, fThreat, schoolMask, threatSpell);
}

void Unit::DeleteThreatList()
{
    if (CanHaveThreatList() && !m_ThreatManager.isThreatListEmpty())
        SendThreatClear();
    m_ThreatManager.clearReferences();
}

void Unit::DeleteFromThreatList(Unit* victim)
{
    if (CanHaveThreatList() && !m_ThreatManager.isThreatListEmpty())
    {
        // remove unreachable target from our threat list
        // next tick we will select next possible target
        m_HostileRefManager.deleteReference(victim);
        m_ThreatManager.modifyThreatPercent(victim, -101);
       // _removeAttacker(victim);
    }
}

void Unit::TauntApply(Unit* taunter)
{
    if(!IsCreature())
        return;

    if (!taunter || (taunter->IsPlayer() && taunter->ToPlayer()->isGameMaster()))
        return;

    if (!CanHaveThreatList())
        return;

    Creature* creature = ToCreature();

    if (creature->HasReactState(REACT_PASSIVE))
        return;

    Unit* target = getVictim();
    if (target && target == taunter)
        return;

    SetInFront(taunter);
    if (creature->IsAIEnabled)
        creature->AI()->AttackStart(taunter);

    //m_ThreatManager.tauntApply(taunter);
}

void Unit::TauntFadeOut(Unit* taunter)
{
    if(!IsCreature())
        return;

    if (!taunter || (taunter->IsPlayer() && taunter->ToPlayer()->isGameMaster()))
        return;

    if (!CanHaveThreatList())
        return;

    Creature* creature = ToCreature();

    if (creature->HasReactState(REACT_PASSIVE))
        return;

    Unit* target = getVictim();
    if (!target || target != taunter)
        return;

    if (m_ThreatManager.isThreatListEmpty())
    {
        if (creature->IsAIEnabled)
            creature->AI()->EnterEvadeMode();
        return;
    }

    target = creature->SelectVictim();  // might have more taunt auras remaining

    if (target && target != taunter)
    {
        SetInFront(target);
        if (creature->IsAIEnabled)
            creature->AI()->AttackStart(target);
    }
}

Unit* Creature::SelectVictim()
{
    // function provides main threat functionality
    // next-victim-selection algorithm and evade mode are called
    // threat list sorting etc.

    Unit* target = nullptr;
    // First checking if we have some taunt on us
    if (AuraEffectList const* tauntAuras = GetAuraEffectsByType(SPELL_AURA_MOD_TAUNT))
    if (tauntAuras->begin() != tauntAuras->end())
    {
        // Auras are insert, last caster will be on the begin
        AuraEffectList::const_iterator aura = tauntAuras->begin();

        Unit* caster = nullptr;
        if (aura != tauntAuras->end())
            caster = (*aura)->GetCaster();

        // The last taunt aura caster is alive an we are happy to attack him
        if (caster && caster->isAlive())
            return getVictim();
        if (!tauntAuras->empty())
        {
            // We do not have last taunt aura caster but we have more taunt auras,
            // so find first available target
            do
            {
                ++aura;
                caster = (*aura)->GetCaster();
                if (caster && canSeeOrDetect(caster, true) && IsValidAttackTarget(caster) && caster->isInAccessiblePlaceFor(ToCreature()))
                {
                    target = caster;
                    break;
                }
            } while (aura != tauntAuras->end());
        }
        else
            target = getVictim();
    }

    if (CanHaveThreatList())
    {
        if (!target && !m_ThreatManager.isThreatListEmpty())
            // No taunt aura or taunt aura caster is dead standard target selection
            target = m_ThreatManager.getHostilTarget();

        //If target in agrolist check onli friend
        if (target && !IsFriendlyTo(target) && canCreatureAttack(target))
        {
            if (!HasUnitState(UNIT_STATE_CASTING))
                SetInFront(target);
            return target;
        }
    }
    else if (!HasReactState(REACT_PASSIVE))
    {
        // We have player pet probably
        target = getAttackerForHelper();
        if (!target && isSummon())
        {
            if (Unit* owner = ToTempSummon()->GetOwner())
            {
                if (owner->isInCombat())
                    target = owner->getAttackerForHelper();
                if (!target)
                {
                    for (ControlList::const_iterator itr = owner->m_Controlled.begin(); itr != owner->m_Controlled.end(); ++itr)
                    {
                        if(Unit* unit = ObjectAccessor::GetUnit(*this, *itr))
                            if (unit->isInCombat())
                            {
                                target = unit->getAttackerForHelper();
                                if (target)
                                    break;
                            }
                    }
                }
            }
        }
    }
    else
        return nullptr;

    if (target && _IsTargetAcceptable(target) && canCreatureAttack(target))
    {
        SetInFront(target);
        return target;
    }

    // last case when creature must not go to evade mode:
    // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list
    // for example at owner command to pet attack some far away creature
    // Note: creature does not have targeted movement generator but has attacker in this case
    for (UnitSet::iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr)
        if ((*itr) && !canCreatureAttack(*itr) && !(*itr)->IsPlayer() && !(*itr)->ToCreature()->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
            return nullptr;

    // TODO: a vehicle may eat some mob, so mob should not evade
    if (GetVehicle())
        return nullptr;

    // search nearby enemy before enter evade mode
    if (HasReactState(REACT_AGGRESSIVE))
    {
        target = SelectNearestTargetInAttackDistance(m_CombatDistance ? m_CombatDistance : ATTACK_DISTANCE);

        if (target && _IsTargetAcceptable(target) && canCreatureAttack(target))
            return target;
    }

    if (AuraEffectList const* iAuras = GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY))
    if (iAuras->begin() != iAuras->end())
    {
        for (Unit::AuraEffectList::const_iterator itr = iAuras->begin(); itr != iAuras->end(); ++itr)
        {
            if ((*itr)->GetBase()->IsPermanent())
            {
                AI()->EnterEvadeMode();
                break;
            }
        }
        return nullptr;
    }

    // enter in evade mode in other case
    AI()->EnterEvadeMode();

    return nullptr;
}

bool Unit::GetThreatTarget(ObjectGuid const& targetGuid)
{
    for (GuidSet::const_iterator itr = m_savethreatlist.begin(); itr != m_savethreatlist.end(); ++itr)
        if ((*itr) == targetGuid)
            return true;

    return false;
}

Unit::VisibleAuraContainer const& Unit::GetVisibleAuras() const
{
    return m_visibleAuras;
}

bool Unit::HasVisibleAura(AuraApplication* aurApp) const
{
    return m_visibleAuras.count(aurApp) > 0;
}

void Unit::SetVisibleAura(AuraApplication* aurApp)
{
    m_visibleAuras.insert(aurApp);
    m_visibleAurasToUpdate.insert(aurApp);
    UpdateAuraForGroup();
}

void Unit::RemoveVisibleAura(AuraApplication* aurApp)
{
    m_visibleAuras.erase(aurApp);
    m_visibleAurasToUpdate.erase(aurApp);
    UpdateAuraForGroup();
}

float Unit::ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const
{
    if (Player* modOwner = GetSpellModOwner())
    {
        modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_ALL_EFFECTS, value);
        switch (effect_index)
        {
            case 0:
                modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT1, value);
                break;
            case 1:
                modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT2, value);
                break;
            case 2:
                modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT3, value);
                break;
            case 3:
                modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT4, value);
                break;
            case 4:
                modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_EFFECT5, value);
                break;
        }
    }
    return value;
}

// function uses real base points (typically value - 1)
float Unit::CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effect_index, float const* basePoints, Item* m_castitem, bool lockBasePoints, float* variance /*= nullptr*/, int32 comboPoints) const
{
    if (variance)
        *variance = 0.0f;

    return spellProto->GetEffect(effect_index, GetSpawnMode())->CalcValue(this, basePoints, target, m_castitem, lockBasePoints, variance, comboPoints);
}

int32 Unit::CalcSpellDuration(SpellInfo const* spellProto, int8 comboPoints)
{
    uint8 holyPower = 0;

    int32 minduration = spellProto->GetDuration(m_spawnMode);
    int32 maxduration = spellProto->GetMaxDuration(m_spawnMode);

    int32 duration = 0;

    if (spellProto->Power.PowerType == POWER_HOLY_POWER)
        holyPower = GetModForHolyPowerSpell();

    if (comboPoints && minduration != -1 && minduration != maxduration)
        duration = minduration + int32((maxduration - minduration) * comboPoints / 5);
    else if (holyPower && !(spellProto->HasAttribute(SPELL_ATTR8_HEALING_SPELL)))
        duration = maxduration * holyPower;
    else
        duration = minduration;

    return duration;
}

int32 Unit::ModSpellDuration(SpellInfo const* spellProto, Unit const* target, int32 duration, bool positive, uint32 effectMask, Unit* caster)
{
    // don't mod permanent auras duration
    if (duration < 0)
        return duration;

    // some auras are not affected by duration modifiers
    if (spellProto->HasAttribute(SPELL_ATTR7_IGNORE_DURATION_MODS))
        return duration;

    if (caster)
    {
        // Skull Bash
        // need find other interrupt spells, whose interrupt duration is affected by spellmods and
        // implement proper code
        if (spellProto->Id == 93985)
            if (Player* modOwner = caster->GetSpellModOwner())
                modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DURATION, duration);
    }

    // cut duration only of negative effects
    if (!positive)
    {
        int32 mechanic = spellProto->GetSpellMechanicMaskByEffectMask(effectMask);

        int32 durationMod;
        int32 durationMod_always = 0;
        int32 durationMod_not_stack = 0;

        for (uint8 i = 1; i <= MECHANIC_ENRAGED; ++i)
        {
            if (!(mechanic & 1<<i))
                continue;
            // Find total mod value (negative bonus)
            int32 new_durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD, i);
            // Find max mod (negative bonus)
            int32 new_durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, i);
            // Check if mods applied before were weaker
            if (new_durationMod_always < durationMod_always)
                durationMod_always = new_durationMod_always;
            if (new_durationMod_not_stack < durationMod_not_stack)
                durationMod_not_stack = new_durationMod_not_stack;
        }

        // Select strongest negative mod
        if (durationMod_always > durationMod_not_stack)
            durationMod = durationMod_not_stack;
        else
            durationMod = durationMod_always;

        if (durationMod != 0)
            AddPct(duration, durationMod);

        // there are only negative mods currently
        durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL, spellProto->Categories.DispelType);

        durationMod = 0;
        if (durationMod_always > durationMod_not_stack)
            durationMod += durationMod_not_stack;
        else
            durationMod += durationMod_always;

        if (durationMod != 0)
            AddPct(duration, durationMod);
    }
    else
    {
        // else positive mods here, there are no currently
        // when there will be, change GetTotalAuraModifierByMiscValue to GetTotalPositiveAuraModifierByMiscValue

        // Mixology - duration boost
        if (target->IsPlayer())
            if (sSpellMgr->IsSpellMemberOfSpellGroup(spellProto->Id, SPELL_GROUP_ELIXIR_BATTLE) || sSpellMgr->IsSpellMemberOfSpellGroup(spellProto->Id, SPELL_GROUP_ELIXIR_GUARDIAN))
                if (target->HasAura(53042) && const_cast<Unit*>(target)->HasSpell(spellProto->GetEffect(0, GetSpawnMode())->TriggerSpell))
                    duration *= 2;
    }

    return std::max(duration, 0);
}

void Unit::ModSpellCastTime(SpellInfo const* spellProto, int32 & castTime, Spell* spell)
{
    if (!spellProto || castTime < 0)
        return;
    // called from caster
    if (Player* modOwner = GetSpellModOwner())
        modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CASTING_TIME, castTime, spell);

    if (!(spellProto->GetMisc(m_spawnMode)->MiscData.Attributes[0] & (SPELL_ATTR0_ABILITY|SPELL_ATTR0_TRADESPELL)) && ((IsPlayer() && spellProto->ClassOptions.SpellClassSet) || IsCreature()))
        castTime = int32(float(castTime) * GetFloatValue(UNIT_FIELD_MOD_CASTING_SPEED) * GetFloatValue(UNIT_FIELD_MOD_TIME_RATE));
    else if (spellProto->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !(spellProto->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)))
        castTime = int32(float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK]);
    else if (spellProto->GetSpellVisual() == 3881 && HasAura(67556)) // cooking with Chef Hat.
        castTime = 500;

    switch (getClass())
    {
        case CLASS_DEATH_KNIGHT:
            if (AuraEffect const* aurEff = GetAuraEffect(77616, 0))
                if (aurEff->GetAmount() == spellProto->Id)
                    castTime = 0;
            break;
        case CLASS_SHAMAN:
            if (spellProto->Id == 188070)
                if (GetPower(POWER_MAELSTROM) >= 20)
                    castTime = 0;
            break;
        default:
            break;
    }

    switch (spellProto->Id)
    {
        case 2366: //herbalism
        case 2368:
        case 3570:
        case 11993:
        case 28695:
        case 50300:
        case 74519:
        case 110413:
        case 158745:
        case 195114:
            if (HasAura(190970) || HasAura(20552) || HasAura(172107) || HasAura(126342) || HasAura(185562)
                || HasAura(191212))
                castTime = 500;
            break;
        case 80451: //archaeology
            if (HasAura(190974) || HasAura(92682) || HasAura(126342) || HasAura(185562) || HasAura(191212))
                castTime = 500;
            break;
        case 8613: //skinning
        case 8617:
        case 8618:
        case 10768:
        case 32678:
        case 50305:
        case 74522:
        case 102216:
        case 158756:
        case 195125:
        case 205243:
            if (HasAura(190973) || HasAura(68978) || HasAura(126342) || HasAura(185562) || HasAura(191212))
                castTime = 510;
            break;
        case 2575: //mining
        case 2576:
        case 3564:
        case 10248:
        case 29354:
        case 50310:
        case 74517:
        case 102161:
        case 158754:
        case 195122:
            if (HasAura(190971) || HasAura(68978) || HasAura(172100) || HasAura(254767) || HasAura(176061)
                || HasAura(126342) || HasAura(185562) || HasAura(191212))
                castTime = 1600;
            else if (HasAura(255655))
                castTime = 1700;
            break;
        default:
            break;
    }

    CalculateCastTimeFromDummy(castTime, spellProto);
}

DiminishingLevels Unit::GetDiminishing(DiminishingGroup group)
{
    for (Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
    {
        if (i->DRGroup != group)
            continue;

        if (!i->hitCount)
            return DIMINISHING_LEVEL_1;

        if (!i->hitTime)
            return DIMINISHING_LEVEL_1;

        // If last spell was casted more than 15 seconds ago - reset the count.
        if (i->stack == 0 && getMSTimeDiff(i->hitTime, getMSTime()) > DiminishingDuration())
        {
            i->hitCount = DIMINISHING_LEVEL_1;
            return DIMINISHING_LEVEL_1;
        }
        // or else increase the count.
        return DiminishingLevels(i->hitCount);
    }
    return DIMINISHING_LEVEL_1;
}

uint32 Unit::DiminishingDuration() const
{
    uint32 MSTime = getMSTime();
    if (MSTime > 5000)
    {
        uint32 checktime = MSTime / 5000;
        checktime *= 5000;

        return MSTime - checktime + 15000;
    }
    return MSTime + 15000;
}

void Unit::IncrDiminishing(DiminishingGroup group)
{
    // Checking for existing in the table
    for (Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
    {
        if (i->DRGroup != group)
            continue;
        if (int32(i->hitCount) < GetDiminishingReturnsMaxLevel(group))
            i->hitCount += 1;
        return;
    }
    m_Diminishing.push_back(DiminishingReturn(group, getMSTime(), DIMINISHING_LEVEL_2));
}

float Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration, Unit* caster, DiminishingLevels Level, int32 limitduration)
{
    if (duration == -1 || group == DIMINISHING_NONE)
        return 1.0f;

    // test pet/charm masters instead pets/charmeds
    Unit const* targetOwner = GetCharmerOrOwner();
    Unit const* casterOwner = caster->GetCharmerOrOwner();

    // Duration of crowd control abilities on pvp target is limited by 10 sec. (2.2.0)
    if (limitduration > 0 && duration > limitduration)
    {
        Unit const* target = targetOwner ? targetOwner : this;
        Unit const* source = casterOwner ? casterOwner : caster;

        if ((target->IsPlayer()
            || ((Creature*)target)->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_ALL_DIMINISH)
            && source->IsPlayer())
            duration = limitduration;
    }

    float mod = 1.0f;

    if (group == DIMINISHING_TAUNT)
    {
        if (IsCreature() && (ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_TAUNT_DIMINISH))
        {
            DiminishingLevels diminish = Level;
            switch (diminish)
            {
                case DIMINISHING_LEVEL_1: break;
                case DIMINISHING_LEVEL_2: mod = 0.65f; break;
                case DIMINISHING_LEVEL_3: mod = 0.4225f; break;
                case DIMINISHING_LEVEL_4: mod = 0.274625f; break;
                case DIMINISHING_LEVEL_TAUNT_IMMUNE: mod = 0.0f; break;
                default: break;
            }
        }
    }
    // Some diminishings applies to mobs too (for example, Stun)
    else if ((GetDiminishingReturnsGroupType(group) == DRTYPE_PLAYER
        && ((targetOwner ? (targetOwner->IsPlayer()) : (IsPlayer()))
        || (IsCreature() && ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_ALL_DIMINISH)))
        || GetDiminishingReturnsGroupType(group) == DRTYPE_ALL)
    {
        DiminishingLevels diminish = Level;
        switch (diminish)
        {
            case DIMINISHING_LEVEL_1: break;
            case DIMINISHING_LEVEL_2: mod = 0.5f; break;
            case DIMINISHING_LEVEL_3: mod = 0.25f; break;
            case DIMINISHING_LEVEL_IMMUNE: mod = 0.0f; break;
            default: break;
        }
    }

    duration = int32(duration * mod);
    return mod;
}

void Unit::ApplyDiminishingAura(DiminishingGroup group, bool apply)
{
    // Checking for existing in the table
    for (Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
    {
        if (i->DRGroup != group)
            continue;

        if (apply)
            i->stack += 1;
        else if (i->stack)
        {
            i->stack -= 1;
            // Remember time after last aura from group removed
            if (i->stack == 0)
                i->hitTime = getMSTime();
        }
        break;
    }
}

float Unit::GetSpellMaxRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const
{
    SpellRangeEntry const* RangeEntry = spellInfo->GetMisc(m_spawnMode)->RangeEntry;
    if (!RangeEntry)
        return 0;

    if (RangeEntry->RangeMax[1] == RangeEntry->RangeMax[0])
        return spellInfo->GetMaxRange(false, const_cast<Unit*>(this));

    if (RangeEntry->ID != 202) // Range 1 != 2 only in 202 range index, this is prevent crashed core
        return spellInfo->GetMaxRange(false, const_cast<Unit*>(this));

    if (!target)
        return spellInfo->GetMisc(m_spawnMode)->RangeEntry->RangeMax[1];

    return spellInfo->GetMaxRange(!IsHostileTo(target), const_cast<Unit*>(this));
}

float Unit::GetSpellMinRangeForTarget(Unit const* target, SpellInfo const* spellInfo) const
{
    SpellRangeEntry const* RangeEntry = spellInfo->GetMisc(m_spawnMode)->RangeEntry;
    if (!RangeEntry)
        return 0;

    if (RangeEntry->RangeMin[1] == RangeEntry->RangeMin[0])
        return spellInfo->GetMinRange();

    if (RangeEntry->ID != 202) // Range 1 != 2 only in 202 range index, this is prevent crashed core
        return spellInfo->GetMinRange();

    bool isHostile = false;
    if (!IsInWorld() || !target || !target->IsInWorld() || target == this || !IsHostileTo(target))
        isHostile = true;

    return spellInfo->GetMinRange(isHostile);
}

Unit* Unit::GetUnit(WorldObject& object, ObjectGuid guid)
{
    return ObjectAccessor::GetUnit(object, guid);
}

Player* Unit::GetPlayer(WorldObject& object, ObjectGuid guid)
{
    return ObjectAccessor::GetPlayer(object, guid);
}

Creature* Unit::GetCreature(WorldObject& object, ObjectGuid guid)
{
    return object.GetMap()->GetCreature(guid);
}

GameObject* Unit::GetGameObjectOnMap(WorldObject& object, ObjectGuid guid)
{
    return object.GetMap()->GetGameObject(guid);
}

uint32 Unit::GetCreatureType() const
{
    if (IsPlayer())
    {
        ShapeshiftForm form = GetShapeshiftForm();
        SpellShapeshiftFormEntry const* ssEntry = sSpellShapeshiftFormStore.LookupEntry(form);
        if (ssEntry && ssEntry->CreatureType > 0)
            return ssEntry->CreatureType;
        return CREATURE_TYPE_HUMANOID;
    }
    return ToCreature()->GetCreatureTemplate()->Type;
}

uint32 Unit::GetCreatureTypeMask() const
{
    uint32 creatureType = GetCreatureType();
    return (creatureType >= 1) ? (1 << (creatureType - 1)) : 0;
}

bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply)
{
    if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "ERROR in HandleStatModifier(): non-existing UnitMods or wrong UnitModifierType!");
        return false;
    }

    switch (modifierType)
    {
        case BASE_PCT_EXCLUDE_CREATE:
        case BASE_VALUE:
        case TOTAL_VALUE:
            m_auraModifiersGroup[unitMod][modifierType] += apply ? amount : -amount;
            break;
        case BASE_PCT:
        case TOTAL_PCT:
            ApplyPercentModFloatVar(m_auraModifiersGroup[unitMod][modifierType], amount, apply);
            break;
        default:
            break;
    }

    if (!CanModifyStats())
        return false;

    switch (unitMod)
    {
        case UNIT_MOD_STAT_STRENGTH:
        case UNIT_MOD_STAT_AGILITY:
        case UNIT_MOD_STAT_STAMINA:
        case UNIT_MOD_STAT_INTELLECT:
        {
            if (Player* plr = ToPlayer())
            {
                plr->SendUpdateStat(1 << unitMod);
            }
            else
            {
                UpdateStats(GetStatByAuraGroup(unitMod));
            }
            break;
        }
        case UNIT_MOD_ARMOR:
        {
            if (Player* plr = ToPlayer())
            {
                plr->SendUpdateStat(USM_ARMOR);
            }
            else
            {
                UpdateArmor();
            }
            break;
        }
        case UNIT_MOD_HEALTH:              UpdateMaxHealth();       break;

        case UNIT_MOD_RESISTANCE_HOLY:
        case UNIT_MOD_RESISTANCE_FIRE:
        case UNIT_MOD_RESISTANCE_NATURE:
        case UNIT_MOD_RESISTANCE_FROST:
        case UNIT_MOD_RESISTANCE_SHADOW:
        case UNIT_MOD_RESISTANCE_ARCANE:   UpdateResistances(GetSpellSchoolByAuraGroup(unitMod));      break;

        case UNIT_MOD_ATTACK_POWER:
        {
            if (Player* plr = ToPlayer())
            {
                plr->SendUpdateStat(USM_MELEE_AP);
            }
            else
            {
                UpdateAttackPowerAndDamage();
            }
            break;
        }
        case UNIT_MOD_ATTACK_POWER_RANGED:
        {
            if (Player* plr = ToPlayer())
            {
                plr->SendUpdateStat(USM_RANGE_AP);
            }
            else
            {
                UpdateAttackPowerAndDamage(true);
            }
            break;
        }
        case UNIT_MOD_DAMAGE_MAINHAND:     UpdateDamagePhysical(BASE_ATTACK);    break;
        case UNIT_MOD_DAMAGE_OFFHAND:      UpdateDamagePhysical(OFF_ATTACK);     break;
        case UNIT_MOD_DAMAGE_RANGED:       UpdateDamagePhysical(RANGED_ATTACK);  break;

        default:
            break;
    }

    return true;
}

float Unit::GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const
{
    if (unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "attempt to access non-existing modifier value from UnitMods!");
        return 0.0f;
    }

    if (modifierType == TOTAL_PCT && m_auraModifiersGroup[unitMod][modifierType] <= 0.0f)
        return 0.0f;

    return m_auraModifiersGroup[unitMod][modifierType];
}

float Unit::GetTotalAuraModValue(UnitMods unitMod) const
{
    if (unitMod >= UNIT_MOD_END)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "attempt to access non-existing UnitMods in GetTotalAuraModValue()!");
        return 0.0f;
    }

    if (m_auraModifiersGroup[unitMod][TOTAL_PCT] <= 0.0f)
        return 0.0f;

    float value = m_auraModifiersGroup[unitMod][BASE_VALUE] * m_auraModifiersGroup[unitMod][BASE_PCT_EXCLUDE_CREATE];
    value *= m_auraModifiersGroup[unitMod][BASE_PCT];
    value += m_auraModifiersGroup[unitMod][TOTAL_VALUE];
    value *= m_auraModifiersGroup[unitMod][TOTAL_PCT];

    return value;
}

SpellSchools Unit::GetSpellSchoolByAuraGroup(UnitMods unitMod) const
{
    SpellSchools school = SPELL_SCHOOL_NORMAL;

    switch (unitMod)
    {
        case UNIT_MOD_RESISTANCE_HOLY:     school = SPELL_SCHOOL_HOLY;          break;
        case UNIT_MOD_RESISTANCE_FIRE:     school = SPELL_SCHOOL_FIRE;          break;
        case UNIT_MOD_RESISTANCE_NATURE:   school = SPELL_SCHOOL_NATURE;        break;
        case UNIT_MOD_RESISTANCE_FROST:    school = SPELL_SCHOOL_FROST;         break;
        case UNIT_MOD_RESISTANCE_SHADOW:   school = SPELL_SCHOOL_SHADOW;        break;
        case UNIT_MOD_RESISTANCE_ARCANE:   school = SPELL_SCHOOL_ARCANE;        break;

        default:
            break;
    }

    return school;
}

Stats Unit::GetStatByAuraGroup(UnitMods unitMod) const
{
    Stats stat = STAT_STRENGTH;

    switch (unitMod)
    {
        case UNIT_MOD_STAT_STRENGTH:    stat = STAT_STRENGTH;      break;
        case UNIT_MOD_STAT_AGILITY:     stat = STAT_AGILITY;       break;
        case UNIT_MOD_STAT_STAMINA:     stat = STAT_STAMINA;       break;
        case UNIT_MOD_STAT_INTELLECT:   stat = STAT_INTELLECT;     break;

        default:
            break;
    }

    return stat;
}

Powers Unit::GetPowerTypeByAuraGroup(UnitMods unitMod) const
{
    return Powers(unitMod - UNIT_MOD_MANA);
}

float Unit::GetTotalAttackPowerValue(WeaponAttackType attType) const
{
    if (attType == RANGED_ATTACK)
    {
        int32 ap = GetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER) + GetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MOD_POS) - GetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MOD_NEG);
        if (ap < 0)
            return 0.0f;
        return ap * (1.0f + GetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER));
    }
    int32 ap = GetInt32Value(UNIT_FIELD_ATTACK_POWER) + GetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_POS) - GetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_NEG);
    if (ap < 0)
        return 0.0f;
    return ap * (1.0f + GetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER));
}

float Unit::GetWeaponDamageRange(WeaponAttackType attType, WeaponDamageRange type) const
{
    if (attType == OFF_ATTACK && !haveOffhandWeapon())
        return 0.0f;

    return m_weaponDamage[attType][type];
}

void Unit::SetHealth(uint64 val, uint32 spellId)
{
    if (getDeathState() == JUST_DIED)
        val = 0;
    else if (IsPlayer() && getDeathState() == DEAD)
        val = 1;
    else
    {
        uint64 maxHealth = GetMaxHealth();
        if (maxHealth < val)
            val = maxHealth;
    }

    uint64 oldHealth = GetHealth();

    SetUInt64Value(UNIT_FIELD_HEALTH, val);

    if (val != oldHealth && isAlive() && !spellId) // For use this option need alive, if not maybe crashed server when target die
    {
        if (AuraEffectList const* mTotalAuraList = GetAuraEffectsByType(SPELL_AURA_PROC_ON_HP_BELOW))
        {
            for (AuraEffectList::const_iterator i = mTotalAuraList->begin(); i != mTotalAuraList->end(); ++i)
            {
                if (AuraEffect* eff = (*i))
                {
                    Aura* aura = eff->GetBase();
                    if (!aura)
                        continue;

                    if (aura->IsRemoved()) // Don`t remove this, prevent freeze
                        continue;

                    int32 miscVal = eff->GetMiscValue();
                    int64 reqCount = CountPctFromMaxHealth(eff->GetAmount());

                    if (miscVal ? (oldHealth >= reqCount && val < reqCount) : (oldHealth < reqCount && val >= reqCount))
                    {
                        if (miscVal)
                        {
                            switch (eff->GetId())
                            {
                                case 214622: if (HasAura(214648)) continue; break; // Warlord's Fortitude, prevent proc
                                case 210532: if (HasAura(205069)) continue; break; // Seraphim's Blessing (Honor Talent)
                                case 187185: if (HasAura(187187)) continue; break; // Light of Argus: Faded
                                case 187198: if (HasAura(187200)) continue; break; // Rising Dawn
                                case 189938: if (HasAura(189936)) continue; break; // Souleater's Bond
                            }
                        }

                        if (uint32 triggered_spell_id = eff->GetTriggerSpell())
                            CastSpell(this, triggered_spell_id, true, nullptr, eff);
                    }
                }
            }
        }
    }

    if (Player* player = ToPlayer())
    {
        if (player->HaveSpectators())
        {
            SpectatorAddonMsg msg;
            msg.SetPlayer(player->GetGUID());
            msg.SetCurrentHP(val);
            msg.SetMaxHP(GetMaxHealth());
            if (Pet* pet = player->GetPet())
            {
                msg.SetPet(pet->GetCreatureTemplate()->Family);
                msg.SetPetHP(pet->GetHealthPct());
            }
            else
            {
                msg.SetPet(0);
                msg.SetPetHP(0);
            }
            player->SendSpectatorAddonMsgToBG(msg);
        }

        player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_HP);
    }
    else if (Creature* creature = ToCreature())
    {
        if (Pet* pet = creature->ToPet())
        {
            if (pet->isControlled())
            {
                Unit* owner = GetOwner();
                if (owner && (owner->IsPlayer()))
                    pet->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_HP);
            }
        }
    }
}

void Unit::SetMaxHealth(uint64 val)
{
    if (!val)
        val = 1;

    uint64 health = GetHealth();
    SetUInt64Value(UNIT_FIELD_MAX_HEALTH, val);

    if (IsPlayer())
    {
        if (ToPlayer()->HaveSpectators())
        {
            SpectatorAddonMsg msg;
            msg.SetPlayer(ToPlayer()->GetGUID());
            msg.SetMaxHP(val);
            ToPlayer()->SendSpectatorAddonMsgToBG(msg);
        }

        ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_HP);
    }
    else if (Creature* creature = ToCreature())
    {
        if (Pet* pet = creature->ToPet())
        {
            if (pet->isControlled())
            {
                Unit* owner = GetOwner();
                if (owner && (owner->IsPlayer()) && owner->ToPlayer()->GetGroup())
                    pet->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_HP);
            }
        }
    }

    if (val < health)
        SetHealth(val);
}

uint32 Unit::GetPowerIndex(uint32 powerType) const
{
    uint32 classId = getClass();
    if (ToPet() && ToPet()->getPetType() == HUNTER_PET)
        classId = CLASS_HUNTER;

    return sDB2Manager.GetPowerIndexByClass(powerType, classId);
}

uint32 Unit::GetAttackTime(WeaponAttackType att) const
{
    float f_BaseAttackTime = GetFloatValue(UNIT_FIELD_ATTACK_ROUND_BASE_TIME + att) / m_modAttackSpeedPct[att];
    return static_cast<uint32>(f_BaseAttackTime);
}

void Unit::SetAttackTime(WeaponAttackType att, uint32 val)
{
    float amount = val * m_modAttackSpeedPct[att];
    SetFloatValue(UNIT_FIELD_ATTACK_ROUND_BASE_TIME + att, amount);
}

int32 Unit::GetPower(Powers power) const
{
    if (power == POWER_HEALTH)
        return GetHealth();

    uint32 powerIndex = GetPowerIndex(power);
    if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
        return 0;

    return GetInt32Value(UNIT_FIELD_POWER + powerIndex);
}

int32 Unit::GetMaxPower(Powers power) const
{
    if (power == POWER_HEALTH)
        return GetMaxHealth();

    uint32 powerIndex = GetPowerIndex(power);
    if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
        return 0;

    return GetInt32Value(UNIT_FIELD_MAX_POWER + powerIndex);
}

//! base max power for initiation
int32 Unit::GetCreatePowers(Powers power) const
{
    PowerTypeEntry const* powerEntry = sDB2Manager.GetPowerType(power);
    if (!powerEntry)
        return 0;

    if (!powerEntry->MaxBasePower)
        return GetCreateMana();

    return powerEntry->MaxBasePower;
}

int32 Unit::GetPowerForReset(Powers power, uint16 powerDisplayID /*= 0*/) const
{
    switch (powerDisplayID)
    {
        case POWER_TYPE_VAULT_CRACKING_PROGRESS:
            return 0;
        default:
            break;
    }

    PowerTypeEntry const* powerEntry = sDB2Manager.GetPowerType(power);
    if (!powerEntry)
        return 0;

    return powerEntry->DefaultPower;
}

void Unit::InitialPowers(bool maxpower)
{
    int32 classId = getClass();
    ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(classId);
    if(!classEntry)
        return;

    WorldPackets::Combat::PowerUpdate packet;
    packet.Guid = GetGUID();

    for (ChrClassesXPowerTypesEntry const* powerEntry : sChrClassesXPowerTypesStore)
    {
        if (powerEntry->ClassID != classId)
            continue;

        Powers power = Powers(powerEntry->PowerType);
        int32 curval = GetPowerForReset(power);
        int32 createval = GetCreatePowers(power);
        int32 powerIndex = GetPowerIndex(power);

        if (power != POWER_ALTERNATE)
        {
            if(maxpower)
            {
                SetInt32Value(UNIT_FIELD_MAX_POWER + powerIndex, createval);
                SetInt32Value(UNIT_FIELD_POWER + powerIndex, curval);
            }
            else
                SetInt32Value(UNIT_FIELD_POWER + powerIndex, curval);
        }

        if (power != POWER_MANA && (classId == CLASS_WARLOCK || maxpower)) //warlock not send power > 0
            continue;

        packet.Powers.emplace_back(curval, power);
    }

    SendMessageToSet(packet.Write(), IsPlayer() ? true : false);
}

void Unit::ResetPowers(float perc, bool duel)
{
    int32 classId = getClass();
    ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(classId);
    if(!classEntry)
        return;

    for (ChrClassesXPowerTypesEntry const* powerClassEntry : sChrClassesXPowerTypesStore)
    {
        if (powerClassEntry->ClassID != classId)
            continue;

        Powers power = Powers(powerClassEntry->PowerType);
        int32 curval = GetPowerForReset(power);

        switch (power)
        {
            case POWER_MANA:
                if (duel)
                    SetPower(power, GetMaxPower(POWER_MANA));
                break;
            case POWER_ALTERNATE:
                break;
            case POWER_RUNES:
                SetPower(power, curval);
                break;
            default:
            {
                if (perc)
                    SetPower(power, uint32(CalculatePct(curval, perc)));
                else
                    SetPower(power, curval);
            }
            break;
        }
    }
}

void Unit::SetPower(Powers power, int32 val, bool send)
{
    uint32 powerIndex = GetPowerIndex(power);
    if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
        return;

    int32 maxPower = GetMaxPower(power);
    if (maxPower < val)
        val = maxPower;

    //Visualization for power
    VisualForPower(power, val, 0, maxPower);

    if (auto creature = ToCreature())
        if (creature->IsAIEnabled)
            creature->AI()->SetPower(power, val);

    SetInt32Value(UNIT_FIELD_POWER + powerIndex, val);

    if (IsInWorld() && send)
    {
        WorldPackets::Combat::PowerUpdate packet;
        packet.Guid = GetGUID();
        packet.Powers.emplace_back(val, power);
        SendMessageToSet(packet.Write(), IsPlayer() ? true : false);
    }
    
    if (Player* player = ToPlayer())
    {
        if (player->HaveSpectators())
        {
            uint8 mod = 1;
            if (power == POWER_RAGE || power == POWER_RUNIC_POWER)
                mod = 10;

            SpectatorAddonMsg msg;
            msg.SetPlayer(GetGUID());
            msg.SetCurrentPower(int32(val / mod));
            msg.SetMaxPower(int32(GetMaxPower(power) / mod));
            msg.SetPowerType(power);
            player->SendSpectatorAddonMsgToBG(msg);
        }

        player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER);
    }
}

void Unit::SetMaxPower(Powers power, int32 val)
{
    uint32 powerIndex = GetPowerIndex(power);
    if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
        return;

    SetInt32Value(UNIT_FIELD_MAX_POWER + powerIndex, val);

    if (IsPlayer())
    {
        if (ToPlayer()->HaveSpectators())
        {
            uint8 mod = 1;
            if (power == POWER_RAGE || power == POWER_RUNIC_POWER)
                mod = 10;

            SpectatorAddonMsg msg;
            msg.SetPlayer(GetGUID());
            msg.SetMaxPower(int32(val / mod));
            msg.SetPowerType(power);
            ToPlayer()->SendSpectatorAddonMsgToBG(msg);
        }

        ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER);
    }
}

inline float GetGameTableColumnForCombatRating(GtCombatRatingsEntry const* row, uint32 rating)
{
    switch (rating)
    {
        case CR_AMPLIFY:
            return row->Amplify;
        case CR_DEFENSE_SKILL:
            return row->DefenseSkill;
        case CR_DODGE:
            return row->Dodge;
        case CR_PARRY:
            return row->Parry;
        case CR_BLOCK:
            return row->Block;
        case CR_HIT_MELEE:
            return row->HitMelee;
        case CR_HIT_RANGED:
            return row->HitRanged;
        case CR_HIT_SPELL:
            return row->HitSpell;
        case CR_CRIT_MELEE:
            return row->CritMelee;
        case CR_CRIT_RANGED:
            return row->CritRanged;
        case CR_CRIT_SPELL:
            return row->CritSpell;
        case CR_MULTISTRIKE:
            return row->MultiStrike;
        case CR_READINESS:
            return row->Readiness;
        case CR_SPEED:
            return row->Speed;
        case CR_RESILIENCE_CRIT_TAKEN:
            return row->ResilienceCritTaken;
        case CR_RESILIENCE_PLAYER_DAMAGE:
            return row->ResiliencePlayerDamage;
        case CR_LIFESTEAL:
            return row->Lifesteal;
        case CR_HASTE_MELEE:
            return row->HasteMelee;
        case CR_HASTE_RANGED:
            return row->HasteRanged;
        case CR_HASTE_SPELL:
            return row->HasteSpell;
        case CR_AVOIDANCE:
            return row->Avoidance;
        case CR_STURDINESS:
            return row->Sturdiness;
        case CR_UNUSED_7:
            return row->Unused7;
        case CR_EXPERTISE:
            return row->Expertise;
        case CR_ARMOR_PENETRATION:
            return row->ArmorPenetration;
        case CR_MASTERY:
            return row->Mastery;
        case CR_PVP_POWER:
            return row->PvPPower;
        case CR_CLEAVE:
            return row->Cleave;
        case CR_VERSATILITY_DAMAGE_DONE:
            return row->VersatilityDamageDone;
        case CR_VERSATILITY_HEALING_DONE:
            return row->VersatilityHealingDone;
        case CR_VERSATILITY_DAMAGE_TAKEN:
            return row->VersatilityDamageTaken;
        case CR_UNUSED_12:
            return row->Unused12;
        default:
            break;
    }

    return 1.0f;
}

float Unit::GetRatingMultiplier(CombatRating cr) const
{
    GtCombatRatingsEntry const* Rating = sCombatRatingsGameTable.GetRow(GetEffectiveLevel());
    if (!Rating)
        return 1.0f;

    if (float value = GetGameTableColumnForCombatRating(Rating, cr))
        return 1.0f / value;

    return 1.0f;
}

void Unit::AddToWorld()
{
    if (!IsInWorld())
    {
        WorldObject::AddToWorld();
    }
}

void Unit::RemoveFromWorld()
{
    // cleanup
    ASSERT(GetGUID());

    if (IsInWorld())
    {
        m_duringRemoveFromWorld = true;
        if (IsVehicle())
            RemoveVehicleKit(true);

        RemoveCharmAuras();
        RemoveBindSightAuras();
        RemoveNotOwnSingleTargetAuras();

        RemoveAllGameObjects();
        RemoveAllDynObjects();
        RemoveAllAreaObjects();

        ExitVehicle();  // Remove applied auras with SPELL_AURA_CONTROL_VEHICLE
        UnsummonAllTotems();
        RemoveAllControlled();

        if(!ToCreature())
            RemoveAreaAurasDueToLeaveWorld();

        if (GetCharmerGUID())
        {
            TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit %u has charmer guid when removed from world", GetEntry());
            // ASSERT(false);
        }

        if (Unit* owner = GetOwner())
            owner->m_Controlled.erase(GetGUID());

        WorldObject::RemoveFromWorld();
        m_duringRemoveFromWorld = false;
    }
}

void Unit::CleanupBeforeRemoveFromMap(bool finalCleanup)
{
    // if (finalCleanup)
        m_cleanupDone = true;

    // This needs to be before RemoveFromWorld to make GetCaster() return a valid pointer on aura removal
    InterruptNonMeleeSpells(true);
    RemoveAllAuras();

    if (IsInWorld())
        RemoveFromWorld();

    //! ==-- DOUBLE DRAGON --==
    RemoveAllAuras();   //remove auras witch was added while we where removing from world.

    if (!finalCleanup)
        for (AuraEffectListMap::iterator iter = m_modMapAuras.begin(); iter != m_modMapAuras.end(); ++iter)
            iter->second->clear();

    ASSERT(m_appliedAuras.empty());
    ASSERT(m_ownedAuras.empty());
    ASSERT(GetGUID());

    // A unit may be in removelist and not in world, but it is still in grid
    // and may have some references during delete
    RemoveAllGameObjects();

    m_Events.KillAllEvents(false);                      // non-delatable (currently casted spells) will not deleted now but it will deleted at call in Map::RemoveAllObjectsInRemoveList
    CombatStop();
    DeleteThreatList();
    ClearSaveThreatTarget();
    getHostileRefManager().setOnlineOfflineState(false);
    GetMotionMaster()->Clear(false);                    // remove different non-standard movement generators.
    m_cleanupDone = false;
}

void Unit::CleanupBeforeTeleport()
{
    m_cleanupDone = true;

    RemoveAurasAllNotOwned(GetVehicleBase() ? GetVehicleBase()->GetGUID() : GetGUID());

    if (IsInWorld())
    {
        m_duringRemoveFromWorld = true;

        RemoveAllGameObjects();
        RemoveAllDynObjects();
        RemoveAllAreaObjects();

        UnsummonAllTotems();

        WorldObject::RemoveFromWorld();
        m_duringRemoveFromWorld = false;
    }

    RemoveAurasAllNotOwned(GetVehicleBase() ? GetVehicleBase()->GetGUID() : GetGUID());

    ASSERT(GetGUID());

    RemoveAllGameObjects();

    m_Events.KillAllEvents(false);
    CombatStop();
    DeleteThreatList();
    ClearSaveThreatTarget();
    getHostileRefManager().setOnlineOfflineState(false);

    // Remove from map
    UpdateObjectVisibility(true);
    if (Creature* creature = ToCreature())
    {
        if (creature->IsInGrid())
            creature->RemoveFromGrid();
    }
    else if (Player* player = ToPlayer())
    {
        if (player->IsInGrid())
            player->RemoveFromGrid();
    }

    m_cleanupDone = false;
}

void Unit::CleanupsBeforeDelete(bool finalCleanup)
{
    if (finalCleanup)
        m_Teleports = false;

    CleanupBeforeRemoveFromMap(finalCleanup);

    WorldObject::CleanupsBeforeDelete(finalCleanup);
}

Unit* Unit::GetMover() const
 {
     if (Player const* player = ToPlayer())
         return player->m_mover;

     return nullptr;
 }
 
 Player* Unit::GetPlayerMover() const
 {
     if (Unit* mover = GetMover())
         return mover->ToPlayer();

     return nullptr;
 }

void Unit::UpdateCharmAI()
{
    if (IsPlayer())
        return;

    if (i_disabledAI) // disabled AI must be primary AI
    {
        if (!isCharmed())
        {
            delete i_AI;
            i_AI = i_disabledAI;
            i_disabledAI = nullptr;
        }
    }
    else
    {
        if (isCharmed())
        {
            i_disabledAI = i_AI;
            if (isPossessed() || IsVehicle())
                i_AI = new PossessedAI(ToCreature());
            else
                i_AI = new PetAI(ToCreature());
        }
    }
}

bool Unit::isPossessed() const
{
    return HasUnitState(UNIT_STATE_POSSESSED);
}

bool Unit::isPossessedByPlayer() const
{
    return HasUnitState(UNIT_STATE_POSSESSED) && GetCharmerGUID().IsPlayer();
}

bool Unit::isPossessing() const
{
    if (Unit* u = GetCharm())
        return u->isPossessed();
    return false;
}

bool Unit::isPossessing(Unit* u) const
{
    return u->isPossessed() && GetCharmGUID() == u->GetGUID();
}

CharmInfo* Unit::InitCharmInfo()
{
    if (!m_charmInfo)
        m_charmInfo = new CharmInfo(this);

    return m_charmInfo;
}

void Unit::DeleteCharmInfo()
{
    if (!m_charmInfo)
        return;

    m_charmInfo->RestoreState();
    delete m_charmInfo;
    m_charmInfo = nullptr;
}

bool Unit::isFrozen() const
{
    return HasAuraState(AURA_STATE_FROZEN);
}

struct ProcTriggeredData
{
    ProcTriggeredData(Aura* _aura) : aura(_aura)
    {
        effMask = 0;
        spellProcEvent = nullptr;
        isProcOneEff = false;
    }
    SpellProcEventEntry const* spellProcEvent;
    Aura* aura;
    uint32 effMask;
    bool isProcOneEff;
};

typedef std::list< ProcTriggeredData > ProcTriggeredList;

// List of auras that CAN be trigger but may not exist in spell_proc_event
// in most case need for drop charges
// in some types of aura need do additional check
// for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic
bool InitTriggerAuraData()
{
    for (uint16 i = 0; i < TOTAL_AURAS; ++i)
    {
        isTriggerAura[i] = false;
        isNonTriggerAura[i] = false;
        isAlwaysTriggeredAura[i] = false;
        isDamageCapAura[i] = false;
    }
    isTriggerAura[SPELL_AURA_DUMMY] = true;
    isTriggerAura[SPELL_AURA_PERIODIC_DUMMY] = true;
    isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true;
    isTriggerAura[SPELL_AURA_MOD_THREAT] = true;
    isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura does not have charges but needs to be removed on trigger
    isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true;
    isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true;
    isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true;
    isTriggerAura[SPELL_AURA_MOD_STEALTH] = true;
    isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura does not have charges but needs to be removed on trigger
    isTriggerAura[SPELL_AURA_MOD_FEAR_2] = true;
    isTriggerAura[SPELL_AURA_MOD_ROOT] = true;
    isTriggerAura[SPELL_AURA_MOD_ROOTED] = true;
    isTriggerAura[SPELL_AURA_TRANSFORM] = true;
    isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true;
    isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true;
    isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true;
    isTriggerAura[SPELL_AURA_PROC_MELEE_TRIGGER_SPELL] = true;
    isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true;
    isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK] = true;
    isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED] = true;
    isTriggerAura[SPELL_AURA_SCHOOL_ABSORB] = true; // Savage Defense untested
    isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true;
    isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true;
    isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true;
    isTriggerAura[SPELL_AURA_MECHANIC_IMMUNITY] = true;
    isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true;
    isTriggerAura[SPELL_AURA_SPELL_MAGNET] = true;
    isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER] = true;
    isTriggerAura[SPELL_AURA_INTERCEPT_MELEE_RANGED_ATTACKS] = true;
    isTriggerAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true;
    isTriggerAura[SPELL_AURA_MOD_MECHANIC_RESISTANCE] = true;
    isTriggerAura[SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS] = true;
    isTriggerAura[SPELL_AURA_MOD_MELEE_HASTE] = true;
    isTriggerAura[SPELL_AURA_MOD_MELEE_ATTACK_SPEED] = true;
    isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE] = true;
    isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true;
    isTriggerAura[SPELL_AURA_MOD_DAMAGE_FROM_CASTER] = true;
    isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true;
    isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true;
    isTriggerAura[SPELL_AURA_CAST_WHILE_WALKING] = true;
    isTriggerAura[SPELL_AURA_ADD_PCT_MODIFIER] = true;
    isTriggerAura[SPELL_AURA_ADD_FLAT_MODIFIER] = true;
    isTriggerAura[SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS] = true;
    isTriggerAura[SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2] = true;
    isTriggerAura[SPELL_AURA_MOD_HEALING_RECEIVED] = true;
    isTriggerAura[SPELL_AURA_IGNORE_CD] = true;
    isTriggerAura[SPELL_AURA_INCREASE_SCHOOL_DAMAGE_TAKEN] = true;
    isTriggerAura[SPELL_AURA_MOD_DODGE_PERCENT] = true;
    isTriggerAura[SPELL_AURA_MELEE_SLOW] = true;
    isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_DONE] = true;
    isTriggerAura[SPELL_AURA_MOD_HEALING_DONE_PERCENT] = true;
    isTriggerAura[SPELL_AURA_ENABLE_PVP_STAT_SCALING] = true;
    isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_COPY] = true;
    isTriggerAura[SPELL_AURA_MOD_MAX_MANA] = true;
    isTriggerAura[SPELL_AURA_MOD_ABSORBTION_PERCENT] = true;

    isNonTriggerAura[SPELL_AURA_MOD_POWER_REGEN] = true;
    isNonTriggerAura[SPELL_AURA_REDUCE_PUSHBACK] = true;

    isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true;
    isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR] = true;
    isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR_2] = true;
    isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true;
    isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true;
    isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true;
    isAlwaysTriggeredAura[SPELL_AURA_SPELL_MAGNET] = true;
    isAlwaysTriggeredAura[SPELL_AURA_SCHOOL_ABSORB] = true;
    isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true;
    isAlwaysTriggeredAura[SPELL_AURA_CAST_WHILE_WALKING] = true;
    isAlwaysTriggeredAura[SPELL_AURA_WATER_WALK] = true;
    isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOTED] = true;

    isDamageCapAura[SPELL_AURA_MOD_CONFUSE] = true;
    isDamageCapAura[SPELL_AURA_MOD_FEAR] = true;
    isDamageCapAura[SPELL_AURA_MOD_FEAR_2] = true;
    isDamageCapAura[SPELL_AURA_MOD_ROOT] = true;
    isDamageCapAura[SPELL_AURA_MOD_STUN] = true;
    isDamageCapAura[SPELL_AURA_TRANSFORM] = true;
    isDamageCapAura[SPELL_AURA_MOD_ROOTED] = true;

    return true;
}

uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition)
{
    uint32 procEx = PROC_EX_NONE;
    // Check victim state
    if (missCondition != SPELL_MISS_NONE)
        switch (missCondition)
        {
            case SPELL_MISS_MISS:    procEx|=PROC_EX_MISS;   break;
            case SPELL_MISS_RESIST:  procEx|=PROC_EX_RESIST; break;
            case SPELL_MISS_DODGE:   procEx|=PROC_EX_DODGE;  break;
            case SPELL_MISS_PARRY:   procEx|=PROC_EX_PARRY;  break;
            case SPELL_MISS_BLOCK:   procEx|=PROC_EX_BLOCK;  break;
            case SPELL_MISS_EVADE:   procEx|=PROC_EX_EVADE;  break;
            case SPELL_MISS_IMMUNE:  procEx|=PROC_EX_IMMUNE; break;
            case SPELL_MISS_IMMUNE2: procEx|=PROC_EX_IMMUNE; break;
            case SPELL_MISS_DEFLECT: procEx|=PROC_EX_DEFLECT;break;
            case SPELL_MISS_ABSORB:  procEx|=PROC_EX_ABSORB; break;
            case SPELL_MISS_REFLECT: procEx|=PROC_EX_REFLECT;break;
            default:
                break;
        }
    else
    {
        // On block
        if (damageInfo->blocked)
            procEx|=PROC_EX_BLOCK;
        // On absorb
        if (damageInfo->absorb)
            procEx|=PROC_EX_ABSORB;
        // On crit
        if (damageInfo->HitInfo & SPELL_HIT_TYPE_CRIT)
            procEx|=PROC_EX_CRITICAL_HIT;
        else
            procEx|=PROC_EX_NORMAL_HIT;
    }
    return procEx;
}

void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpell, DamageInfo* dmgInfoProc, SpellInfo const* procAura, Uint32Set* AppliedProcMods, Spell* spell)
{
    // Player is loaded now - do not allow passive spell casts to proc
    if (IsPlayer() && ToPlayer()->GetSession()->PlayerLoading())
        return;
    // For melee/ranged based attack need update skills and set some Aura states if victim present
    if (procFlag & MELEE_BASED_TRIGGER_MASK && target)
    {
        // If exist crit/parry/dodge/block need update aura state (for victim and attacker)
        if (procExtra & (PROC_EX_CRITICAL_HIT|PROC_EX_PARRY|PROC_EX_DODGE|PROC_EX_BLOCK))
        {
            // for victim
            if (isVictim)
            {
                // if victim and dodge attack
                if (procExtra & PROC_EX_DODGE)
                {
                    // Update AURA_STATE on dodge
                    if (getClass() != CLASS_ROGUE) // skip Rogue Riposte
                    {
                        ModifyAuraState(AURA_STATE_DEFENSE, true);
                        StartReactiveTimer(REACTIVE_DEFENSE);
                    }
                }
                // if victim and parry attack
                if (procExtra & PROC_EX_PARRY)
                {
                    // For Hunters only Counterattack (skip Mongoose bite)
                    if (getClass() == CLASS_HUNTER)
                    {
                        ModifyAuraState(AURA_STATE_HUNTER_PARRY, true);
                        StartReactiveTimer(REACTIVE_HUNTER_PARRY);
                    }
                    else
                    {
                        ModifyAuraState(AURA_STATE_DEFENSE, true);
                        StartReactiveTimer(REACTIVE_DEFENSE);
                    }
                }
                // if and victim block attack
                if (procExtra & PROC_EX_BLOCK)
                {
                    ModifyAuraState(AURA_STATE_DEFENSE, true);
                    StartReactiveTimer(REACTIVE_DEFENSE);
                }
            }
        }
    }

    Unit* actor = isVictim ? target : this;
    Unit* actionTarget = !isVictim ? target : this;

    bool isAutoattackProcSpell = false;

    if (!isVictim)
    {
        if (Player* plr = ToPlayer())
        {
            if (procSpell && procSpell->Id == plr->GetAutoattackSpellId(attType))
                isAutoattackProcSpell = true;
        }
    }

    HealInfo healInfo = HealInfo(actor, actionTarget, dmgInfoProc->GetDamage(), procSpell, procSpell ? SpellSchoolMask(procSpell->GetMisc(m_spawnMode)->MiscData.SchoolMask) : SPELL_SCHOOL_MASK_NORMAL);
    ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, spell, dmgInfoProc, &healInfo);

    std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
    ProcTriggeredList procTriggered;
    // Fill procTriggered list
    for (AuraApplicationMap::const_iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr)
    {
        AuraApplicationPtr auraApp = itr->second;
        // Do not allow auras to proc from effect triggered by itself
        if (procAura && procAura->Id == itr->first || !auraApp || !auraApp->GetBase()->GetSpellInfo()->GetAuraOptions(GetSpawnMode())->IsProcAura)
            continue;
        ProcTriggeredData triggerData(auraApp->GetBase());

        if (triggerData.aura->HasAuraAttribute(AURA_ATTR_IS_NOT_ACTIVE))
            continue;

        // Defensive procs are active on absorbs (so absorption effects are not a hindrance)
        bool active = dmgInfoProc->GetDamage() || dmgInfoProc->GetAddPower() || (procExtra & PROC_EX_ON_CAST) || (procExtra & PROC_EX_BLOCK && isVictim) || (procExtra & PROC_EX_ABSORB);

        SpellInfo const* spellProto = auraApp->GetBase()->GetSpellInfo();
        if(triggerData.aura->HasAuraAttribute(AURA_ATTR_IS_USING_CHARGES))
        {
            if(((procFlag & SPELL_PROC_FROM_CAST_MASK )|| isAutoattackProcSpell) && !(procExtra & PROC_EX_ON_CAST))
                continue;
        }
        else if (((procFlag & SPELL_PROC_FROM_CAST_MASK) || isAutoattackProcSpell) && (procExtra & PROC_EX_ON_CAST) && !(spellProto->AttributesCu[0] & SPELL_ATTR0_CU_PROC_ONLY_ON_CAST))
            continue;
        else if (((procFlag & SPELL_PROC_FROM_CAST_MASK) || isAutoattackProcSpell) && !(procExtra & PROC_EX_ON_CAST) && (spellProto->AttributesCu[0] & SPELL_ATTR0_CU_PROC_ONLY_ON_CAST))
            continue;

        // only auras that has triggered spell should proc from fully absorbed damage
        if ((procExtra & PROC_EX_ABSORB && isVictim) || ((procFlag & PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG) && spellProto->Categories.DefenseType == SPELL_DAMAGE_CLASS_MAGIC))
            if (dmgInfoProc->GetDamage() || spellProto->HasTriggerSpell)
                active = true;

        if (procSpell && !(procSpell->HasAuraInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE)))
            // time for hardcode! Some spells can proc on absorb
            if (spellProto->Id == 33757 || spellProto->HasAura(SPELL_AURA_MOD_STEALTH) || spellProto->HasAura(SPELL_AURA_MOD_INVISIBILITY))
                active = true;

        bool stealthAuraProc = false;

        if (isVictim)
        {
            if (spellProto->HasAttribute(SPELL_ATTR0_CU_IS_STEALTH_AURA))
            {
                if (procSpell && procSpell->IsPositive())
                    continue;

                if (!dmgInfoProc->GetDamage() && !(procExtra & PROC_EX_ABSORB) && procSpell && !procSpell->HasAura(SPELL_AURA_MOD_STUN) && !procSpell->HasAura(SPELL_AURA_MOD_CONFUSE) &&
                    !procSpell->HasAura(SPELL_AURA_MOD_FEAR) && !procSpell->HasAura(SPELL_AURA_MOD_FEAR_2))
                    continue;

                stealthAuraProc = true;
                active = true;
            }
            else if ((procSpell && procSpell->Categories.Mechanic == MECHANIC_DISARM) || (spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)))
                active = true;
        }
        // do checks using conditions table
        ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_PROC, spellProto->Id);
        ConditionSourceInfo condInfo = ConditionSourceInfo(eventInfo.GetActor(), eventInfo.GetActionTarget());
        if (!sConditionMgr->IsObjectMeetToConditions(condInfo, conditions))
            continue;

        // Triggered spells not triggering additional spells
        bool triggered = !(spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) ? (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) : false;

        for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
        {
            if (spellProto->EffectMask < uint32(1 << i))
                break;

            if (auraApp->HasEffect(i))
            {
                if (!IsTriggeredAtSpellProcEvent(target, spellProto, procSpell, procFlag, procExtra, attType, isVictim, active, triggerData.spellProcEvent, i))
                    continue;
                if (procSpell && procSpell->IsNotProcSpell(isVictim) && !stealthAuraProc && (triggerData.spellProcEvent == nullptr || !(triggerData.spellProcEvent->procEx & PROC_EX_NOT_ACTIVE_SPELL)))
                    continue;
                AuraEffect* aurEff = auraApp->GetBase()->GetEffect(i);
                // Skip this auras
                if (isNonTriggerAura[aurEff->GetAuraType()])
                    continue;
                if (isDamageCapAura[aurEff->GetAuraType()])
                    triggerData.isProcOneEff = true;
                // If not trigger by default and spellProcEvent == NULL - skip
                if (!isTriggerAura[aurEff->GetAuraType()] && (triggerData.spellProcEvent == nullptr || !(triggerData.spellProcEvent->effectMask & (1<<i))))
                {
                    if (auraApp->GetBase()->CallScriptEffectProcHandlers(aurEff, &*auraApp, eventInfo))
                        continue;
                    continue;
                }
                if (!SpellProcCheck(target, spellProto, procSpell, i, aurEff))
                    continue;
                if (!RollProcEffectResult(aurEff, triggerData.spellProcEvent, procFlag, procExtra, i))
                    continue;
                // Some spells must always trigger
                if (!triggered || isAlwaysTriggeredAura[aurEff->GetAuraType()])
                    triggerData.effMask |= 1<<i;
            }
        }

        if (triggerData.effMask)
        {
            // If set trigger always but only one time
            if(triggerData.spellProcEvent && (triggerData.spellProcEvent->procEx & PROC_EX_EX_ONE_TIME_TRIGGER))
            {
                bool foundProc = false;
                for (ProcTriggeredList::const_iterator i = procTriggered.begin(); i != procTriggered.end(); ++i)
                    if(i->aura->GetId() == triggerData.aura->GetId())
                    {
                        foundProc = true;
                        break;
                    }
                if(!foundProc)
                    procTriggered.push_front(triggerData);
            }
            else
                procTriggered.push_front(triggerData);
        }
    }

    #ifdef WIN32
    TC_LOG_DEBUG(LOG_FILTER_PROC, "ProcDamageAndSpell: procSpell %u procTriggered %u procFlag %u procExtra %u isVictim %u", procSpell ? procSpell->Id : 0, procTriggered.size(), procFlag, procExtra, isVictim);
    #endif

    // Nothing found
    if (procTriggered.empty())
        return;

    std::lock_guard<std::recursive_mutex> guard(i_proc_lock);

    // Note: must SetCantProc(false) before return
    if (procExtra & (PROC_EX_INTERNAL_TRIGGERED | PROC_EX_INTERNAL_CANT_PROC))
        SetCantProc(true);

    // Handle effects proceed this time
    for (ProcTriggeredList::const_iterator i = procTriggered.begin(); i != procTriggered.end(); ++i)
    {
        // look for aura in auras list, it may be removed while proc event processing
        if (i->aura->IsRemoved())
            continue;

        bool isModifier = false;
        bool isReflect = false;

        bool useCharges  = i->aura->HasAuraAttribute(AURA_ATTR_IS_USING_CHARGES);
        // no more charges to use, prevent proc
        if (useCharges && !i->aura->GetCharges())
            continue;

        bool procSuccess = RollProcResult(target, i->aura, attType, isVictim, i->spellProcEvent, procFlag, procExtra, dmgInfoProc, procSpell, i->aura->GetCastItemGUID());
        if (!procSuccess)
            continue;

        bool takeCharges = false;
        SpellInfo const* spellInfo = i->aura->GetSpellInfo();
        uint32 Id = i->aura->GetId();

        if(spellInfo->GetAuraOptions(GetSpawnMode())->ProcTypeMask & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_POS_NEG))
            useCharges = true;

        AuraApplication const* aurApp = i->aura->GetApplicationOfTarget(GetGUID());

        // For players set spell cooldown if need
        double cooldown = spellInfo->GetAuraOptions(GetSpawnMode())->ProcCategoryRecovery / 1000.0f;
        if (IsPlayer())
        {
            if (i->spellProcEvent && i->spellProcEvent->cooldown)
                cooldown = i->spellProcEvent->cooldown;
        }

        // This bool is needed till separate aura effect procs are still here
        bool handled = false;
        if (HandleAuraProc(target, dmgInfoProc, i->aura, procSpell, procFlag, procExtra, cooldown, &handled))
        {
            TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), Id);
            takeCharges = true;
        }

        bool removeWithDelay = false;

        if (!handled)
        {
            for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
            {
                if (!(i->effMask & (1<<effIndex)))
                    continue;

                AuraEffect* triggeredByAura = i->aura->GetEffect(effIndex);
                if(!triggeredByAura)
                    continue;

                //if aura has cap to damage, do not proc other auras
                if(i->isProcOneEff && !isDamageCapAura[triggeredByAura->GetAuraType()])
                    continue;

                bool prevented = i->aura->CallScriptEffectProcHandlers(triggeredByAura, aurApp, eventInfo);
                if (prevented)
                {
                    takeCharges = false;
                    continue;
                }

                // Proc chain chack. Not handle proc from current effect in future prock from it.
                if (m_triggeredEffect.count(triggeredByAura))
                    continue;

                m_triggeredEffect.insert(triggeredByAura);

                switch (triggeredByAura->GetAuraType())
                {
                    case SPELL_AURA_PROC_MELEE_TRIGGER_SPELL:
                    {
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_PROC_MELEE_TRIGGER_SPELL: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
                        // Don`t drop charge or add cooldown for not started trigger
                        if (HandleProcMelleTriggerSpell(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        break;
                    }
                    case SPELL_AURA_PROC_TRIGGER_SPELL:
                    case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
                    {
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_PROC_TRIGGER_SPELL: casting spell id %u (triggered by %s dummy aura of spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));
                        // Don`t drop charge or add cooldown for not started trigger
                        if (HandleProcTriggerSpell(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        break;
                    }
                    case SPELL_AURA_PROC_TRIGGER_DAMAGE:
                    {
                        // target has to be valid
                        if (!target)
                            break;

                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_PROC_TRIGGER_DAMAGE: doing %u damage from spell id %u (triggered by %s aura of spell %u)", triggeredByAura->GetAmount(), spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
                        SpellNonMeleeDamage damageInfo(this, target, spellInfo->Id, spellInfo->GetSpellXSpellVisualId(this, target), spellInfo->GetMisc(m_spawnMode)->MiscData.SchoolMask);
                        std::vector<uint32> ExcludeAuraList;
                        uint32 newDamage = SpellDamageBonusDone(target, spellInfo, triggeredByAura->GetAmount(), SPELL_DIRECT_DAMAGE, ExcludeAuraList, static_cast<SpellEffIndex>(effIndex));
                        newDamage = target->SpellDamageBonusTaken(this, spellInfo, newDamage);
                        CalculateSpellDamageTaken(&damageInfo, newDamage, spellInfo, (1 << effIndex));
                        DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb, spellInfo);
                        SendSpellNonMeleeDamageLog(&damageInfo);
                        DealSpellDamage(&damageInfo, true);
                        takeCharges = true;
                        break;
                    }
                    case SPELL_AURA_MANA_SHIELD:
                    case SPELL_AURA_DUMMY:
                    case SPELL_AURA_PERIODIC_DUMMY:
                    case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
                    case SPELL_AURA_MOD_HEALING_DONE_PERCENT:
                    case SPELL_AURA_ENABLE_PVP_STAT_SCALING:
                    {
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_DUMMY: casting spell id %u (triggered by %s dummy aura of spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));
                        // chargeable mods are breaking on hit

                        if(SpellProcTriggered(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        else if (HandleDummyAuraProc(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown, spell))
                            takeCharges = true;

                        if (useCharges && takeCharges)
                        {
                            if (procExtra & PROC_EX_ON_CAST)
                            {
                                if (spellInfo->HasAttribute(SPELL_ATTR1_CU_IS_USING_STACKS))
                                {
                                    if (i->aura->GetStackAmount() < 2)
                                        removeWithDelay = true;
                                }
                                else if (i->aura->GetCharges() < 2)
                                    removeWithDelay = true;
                            }
                        }
                        break;
                    }
                    case SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS:
                    case SPELL_AURA_IGNORE_CD:
                    case SPELL_AURA_MOD_HEALING_RECEIVED:
                    {
                        if (!triggeredByAura->IsAffectingSpell(procSpell) && !triggeredByAura->IsAffectingSpell(procAura))
                            break;

                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS: casting spell id %u (triggered by %s dummy aura of spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));

                        SpellProcTriggered(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown);
                        takeCharges = true;
                        break;
                    }
                    case SPELL_AURA_INCREASE_SCHOOL_DAMAGE_TAKEN:
                    case SPELL_AURA_PERIODIC_HEAL:
                    case SPELL_AURA_MOD_DODGE_PERCENT:
                    case SPELL_AURA_MELEE_SLOW:
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_PERIODIC_HEAL: casting spell id %u (triggered by %s dummy aura of spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));
                        if(SpellProcTriggered(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2:
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2: casting spell id %u (triggered by %s dummy aura of spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));
                        if (procSpell && (procSpell->Id == triggeredByAura->GetAmount()))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_OBS_MOD_POWER:
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_OBS_MOD_POWER: casting spell id %u (triggered by %s aura of spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));
                        if (HandleObsModEnergyAuraProc(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: casting spell id %u (triggered by %s aura of spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));
                        if (HandleModDamagePctTakenAuraProc(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_MOD_MELEE_HASTE:
                    case SPELL_AURA_MOD_MELEE_ATTACK_SPEED:
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_MOD_MELEE_HASTE: casting spell id %u (triggered by %s haste aura of spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));
                        if (HandleHasteAuraProc(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_PROC_TRIGGER_SPELL_COPY:
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting copy spell %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
                        if (HandleProcTriggerSpellCopy(target, dmgInfoProc, triggeredByAura, procSpell, cooldown))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: casting spell id %u (triggered by %s aura of spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));
                        if (HandleOverrideClassScriptAuraProc(target, dmgInfoProc, triggeredByAura, procSpell, cooldown))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE:
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());

                        if (HandleProcTriggerSpell(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK:
                    case SPELL_AURA_MOD_CASTING_SPEED:
                        // Skip melee hits or instant cast spells
                        if (procSpell && (procSpell->CalcCastTime() != 0 || procSpell->IsChanneled()))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_REFLECT_SPELLS:
                        isReflect = true;
                        takeCharges = true;
                        break;
                    case SPELL_AURA_REFLECT_SPELLS_SCHOOL:
                        // Skip Melee hits and spells ws wrong school
                        if (procSpell && (triggeredByAura->GetMiscValue() & procSpell->GetMisc(m_spawnMode)->MiscData.SchoolMask))         // School check
                            takeCharges = true;
                        break;
                    case SPELL_AURA_SPELL_MAGNET:
                    {
                        Unit* unitOwner = triggeredByAura->GetBase()->GetUnitOwner();
                        // Skip Melee hits and targets with magnet aura
                        if (procSpell && (unitOwner == this))         // Magnet
                        {
                            if (unitOwner->GetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL) == GroundingTotem)
                                break;

                            takeCharges = true;
                            // Off-like kill totem for non damage spells
                            if(!dmgInfoProc->GetDamage())
                                CastSpell(this, 45254, true);
                        }
                        break;
                    }
                    case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT:
                    case SPELL_AURA_MOD_POWER_COST_SCHOOL:
                        // Skip melee hits and spells ws wrong school or zero cost
                        if (procSpell &&
                            (procSpell->Power.PowerCost != 0 || procSpell->Power.PowerCostPercentage != 0) && // Cost check
                            (triggeredByAura->GetMiscValue() & procSpell->GetMisc(m_spawnMode)->MiscData.SchoolMask))          // School check
                            takeCharges = true;
                        break;
                    case SPELL_AURA_MECHANIC_IMMUNITY:
                        // Compare mechanic
                        if (procSpell && (procSpell->GetAllEffectsMechanicMask() & uint32(1<<(triggeredByAura->GetMiscValue()))))
                            takeCharges = true;
                        else if (triggeredByAura->GetId() == 213610)
                            takeCharges = true;
                        break;
                    case SPELL_AURA_MOD_MECHANIC_RESISTANCE:
                        // Compare mechanic
                        if (procSpell && (procSpell->GetAllEffectsMechanicMask() & uint32(1<<(triggeredByAura->GetMiscValue()))))
                            takeCharges = true;
                        break;
                    case SPELL_AURA_MOD_DAMAGE_FROM_CASTER:
                        // Compare casters
                        if (triggeredByAura->GetCasterGUID() == target->GetGUID())
                        {
                            takeCharges = true;
                            if (triggeredByAura->GetId() == 197277) // Judgment
                            {
                                if (Unit* caster = triggeredByAura->GetCaster())
                                {
                                    if (AuraEffect* aurEff = caster->GetAuraEffect(238134, EFFECT_0)) // Judge Unworthy
                                    {
                                        if (roll_chance_i(aurEff->GetAmount()))
                                        {
                                            std::list<Unit*> targetList;
                                            GetAttackableUnitListInRange(targetList, 8.f);

                                            targetList.remove(this);

                                            if (!targetList.empty())
                                            {
                                                std::list<Unit*> saveList = targetList;
                                                for (std::list<Unit*>::const_iterator itr = targetList.begin(); itr != targetList.end(); ++itr)
                                                {
                                                    if (!(*itr)->IsWithinLOSInMap(caster) || caster->IsFriendlyTo(*itr) || (*itr)->HasAura(197277))
                                                        saveList.remove(*itr);
                                                }

                                                if (!saveList.empty())
                                                {
                                                    int32 dur = triggeredByAura->GetBase()->GetDuration();
                                                    std::list<Unit*>::const_iterator itr = saveList.begin();
                                                    caster->AddAura(197277, (*itr), nullptr, NULL, dur, dur);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s spell crit chance aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
                        if (procSpell && HandleSpellCritChanceAuraProc(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        break;
                    // CC Auras which use their amount amount to drop
                    // Are there any more auras which need this?
                    case SPELL_AURA_MOD_CONFUSE:
                    case SPELL_AURA_MOD_FEAR:
                    case SPELL_AURA_MOD_FEAR_2:
                    case SPELL_AURA_MOD_STUN:
                    case SPELL_AURA_MOD_ROOT:
                    case SPELL_AURA_MOD_ROOTED:
                    case SPELL_AURA_TRANSFORM:
                    {
                        if (triggeredByAura->GetId() == 114052) // Ascendance
                        {
                            const_cast<ProcTriggeredData*>(&*i)->isProcOneEff = false;
                            break;
                        }
                        if (triggeredByAura->GetId() == 3355) // Freezing Trap
                        {
                            if (triggeredByAura->GetAmount() > 0)
                            {
                                if (Unit* caster = triggeredByAura->GetCaster())
                                {
                                    Aura* aura = triggeredByAura->GetBase();
                                    AuraEffect const* aurEff = caster->GetAuraEffect(234955, EFFECT_1); // Waylay
                                    if (aurEff && aura && (aura->GetMaxDuration() - aura->GetDuration()) < aurEff->GetAmount())
                                        break;
                                }
                            }
                        }
                        if (procSpell && (triggeredByAura->GetId() == 339 || triggeredByAura->GetId() == 235963)) // Entangling Roots
                        {
                            if (Unit* caster = triggeredByAura->GetCaster())
                            {
                                if (caster->HasAura(206852) || caster->HasAura(233755)) // Deep Roots (Honor Talent)
                                    break;

                                if (caster == target && caster->HasAura(210638)) // Hardened Roots
                                    if (procSpell->GetAllEffectsMechanicMask() & (1 << MECHANIC_BLEED))
                                        break;
                            }
                        }

                        if (procExtra & PROC_EX_INTERNAL_HOT)
                            break;

                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SPELL_AURA_MOD_CONFUSE: casting mending (triggered by %s dummy aura of spell %u), procSpell %u",
                            (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));

                        // chargeable mods are breaking on hit
                        if (useCharges && int32(dmgInfoProc->GetDamage() + dmgInfoProc->GetAbsorb()))
                            takeCharges = true;
                        else
                        {
                            // Spell own direct damage at apply wont break the CC
                            if (procSpell && (procSpell->Id == triggeredByAura->GetId()))
                            {
                                Aura* aura = triggeredByAura->GetBase();
                                // called from spellcast, should not have ticked yet
                                if (aura->GetDuration() == aura->GetMaxDuration())
                                    break;
                            }
                            int32 damageLeft = triggeredByAura->GetAmount();
                            // No damage left
                            if (damageLeft < int32(dmgInfoProc->GetDamage() + dmgInfoProc->GetAbsorb()))
                            {
                                std::list<uint32> auras;
                                auras.push_back(i->aura->GetId());
                                SendDispelLog(target ? target->GetGUID() : ObjectGuid::Empty, procSpell ? procSpell->Id : 0, auras, true, false);

                                i->aura->Remove();
                            }
                            else
                                triggeredByAura->SetAmount(damageLeft - dmgInfoProc->GetDamage() - dmgInfoProc->GetAbsorb());
                        }
                        break;
                    }
                    case SPELL_AURA_MOD_STEALTH:
                    {
                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: SPELL_AURA_MOD_STEALTH casting spell id %u (triggered by %s spell spell %u), procSpell %u", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0));
                        takeCharges = true;
                        break;
                    }
                    case SPELL_AURA_CAST_WHILE_WALKING:
                    case SPELL_AURA_CAST_WHILE_WALKING2:
                    {
                        if (!triggeredByAura->IsAffectingSpell(procSpell) && !triggeredByAura->IsAffectingSpell(procAura))
                            break;
                        if (dmgInfoProc->GetStartCast() && i->aura->GetApplyMSTime() > dmgInfoProc->GetStartCast()) // Prevent proc if aura apply after cast spell
                            break;
                        if (HandleCastWhileWalkingAuraProc(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                        {
                            takeCharges = true;
                            isModifier = true;
                        }
                        break;
                    }
                    case SPELL_AURA_ADD_FLAT_MODIFIER:
                    case SPELL_AURA_ADD_PCT_MODIFIER:
                    {
                        if (useCharges && !triggeredByAura->IsAffectingSpell(procSpell) && !triggeredByAura->IsAffectingSpell(procAura))
                            break;

                        //take charges only if spell moded by this spell
                        if (procExtra & PROC_EX_ON_CAST)
                        {
                            if ((1 << triggeredByAura->GetMiscValue()) & 0x00204420) // SPELLMOD_COST SPELLMOD_GLOBAL_COOLDOWN SPELLMOD_RANGE SPELLMOD_CASTING_TIME
                            {
                                if (AppliedProcMods)
                                {
                                    Uint32Set::iterator iterMod = AppliedProcMods->find(triggeredByAura->GetId());
                                    if (iterMod == AppliedProcMods->end())
                                        break;
                                }
                            }
                            if (dmgInfoProc->GetStartCast() && i->aura->GetApplyMSTime() > dmgInfoProc->GetStartCast()) // Prevent proc if aura apply after cast spell
                                break;
                        }

                        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: SPELL_AURA_ADD_PCT(FLAT)_MODIFIER casting spell id %u (triggered by %s spell spell %u), procSpell %u GetCharges %u",
                        spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId(), (procSpell ? procSpell->Id : 0), i->aura->GetCharges());

                        if (HandleSpellModAuraProc(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                        {
                            if (useCharges && (procExtra & PROC_EX_ON_CAST))
                            {
                                if (spellInfo->HasAttribute(SPELL_ATTR1_CU_IS_USING_STACKS))
                                {
                                    if (i->aura->GetStackAmount() < 2)
                                        removeWithDelay = true;
                                }
                                else if (i->aura->GetCharges() < 2)
                                    removeWithDelay = true;
                            }

                            takeCharges = true;
                        }
                        break;
                    }
                    case SPELL_AURA_ABILITY_IGNORE_AURASTATE:
                        if (HandleIgnoreAurastateAuraProc(target, dmgInfoProc, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
                            takeCharges = true;
                        break;
                    default:
                        // nothing do, just charges counter
                        // Don't drop charge for Earth Shield because of second effect
                        if (triggeredByAura->GetId() == 974)
                            break;

                        takeCharges = true;
                        break;
                } // switch (triggeredByAura->GetAuraType())
                m_triggeredEffect.erase(triggeredByAura);
            } // for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
        } // if (!handled)

        // Remove charge (aura can be removed by triggers)
        if (useCharges && takeCharges)
        {
            if (removeWithDelay)
            {
                uint32 spellId = i->aura->GetId();

                AddDelayedEvent(50, [this, spellId]() -> void
                {
                    RemoveAurasDueToSpell(spellId);
                });
            }
            else if (!i->aura->IsPassive())
            {
                i->aura->DropCharge();
            }
            
            if(isReflect) // reflect take only one aura
                return;
            if(isModifier && (procExtra & PROC_EX_ON_CAST)) // same proc use charge on cast can take only one aura
                return;
        }
    }

    // Cleanup proc requirements
    if (procExtra & (PROC_EX_INTERNAL_TRIGGERED | PROC_EX_INTERNAL_CANT_PROC))
        SetCantProc(false);
}

void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo)
{
    // use provided list of auras which can proc
    if (procAuras)
    {
        for (std::list<AuraApplication*>::iterator itr = procAuras->begin(); itr!= procAuras->end(); ++itr)
        {
            ASSERT((*itr)->GetTarget() == this);
            if (!(*itr)->GetRemoveMode())
                if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo))
                {
                    (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo);
                    aurasTriggeringProc.push_back(*itr);
                }
        }
    }
    // or generate one on our own
    else
    {
        for (AuraApplicationMap::iterator itr = m_appliedAuras.begin(); itr!= m_appliedAuras.end(); ++itr)
        {
            if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second.get(), eventInfo))
            {
                itr->second->GetBase()->PrepareProcToTrigger(itr->second.get(), eventInfo);
                aurasTriggeringProc.push_back(itr->second.get());
            }
        }
    }
}

void Unit::TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo)
{
    DamageInfo dmgInfo = DamageInfo(damageInfo);
    TriggerAurasProcOnEvent(nullptr, nullptr, damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, 0, 0, damageInfo.procEx, nullptr, &dmgInfo, nullptr);
}

void Unit::TriggerAurasProcOnEvent(std::list<AuraApplication*>* myProcAuras, std::list<AuraApplication*>* targetProcAuras, Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo)
{
    // prepare data for self trigger
    ProcEventInfo myProcEventInfo = ProcEventInfo(this, actionTarget, actionTarget, typeMaskActor, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo);
    std::list<AuraApplication*> myAurasTriggeringProc;
    GetProcAurasTriggeredOnEvent(myAurasTriggeringProc, myProcAuras, myProcEventInfo);

    // prepare data for target trigger
    ProcEventInfo targetProcEventInfo = ProcEventInfo(this, actionTarget, this, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo);
    std::list<AuraApplication*> targetAurasTriggeringProc;
    if (typeMaskActionTarget)
        GetProcAurasTriggeredOnEvent(targetAurasTriggeringProc, targetProcAuras, targetProcEventInfo);

    TriggerAurasProcOnEvent(myProcEventInfo, myAurasTriggeringProc);

    if (typeMaskActionTarget)
        TriggerAurasProcOnEvent(targetProcEventInfo, targetAurasTriggeringProc);
}

void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, std::list<AuraApplication*>& aurasTriggeringProc)
{
    for (std::list<AuraApplication*>::iterator itr = aurasTriggeringProc.begin(); itr != aurasTriggeringProc.end(); ++itr)
    {
        if (!(*itr)->GetRemoveMode())
            (*itr)->GetBase()->TriggerProcOnEvent(*itr, eventInfo);
    }
}

SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const
{
    return SPELL_SCHOOL_MASK_NORMAL;
}

Player* Unit::GetSpellModOwner() const
{
    if (IsPlayer())
        return const_cast<Player*>(ToPlayer());
    if (Creature const* creature = ToCreature())
    {
        if (creature->isPet() || creature->isTotem())
        {
            Unit* owner = GetOwner();
            if (owner && owner->IsPlayer())
                return static_cast<Player*>(owner);
        }
        else if (creature->isSummon())
        {
            if(isSummon())
                if(Unit* owner = ToTempSummon()->GetSummoner())
                    if (owner && owner->IsPlayer())
                        return static_cast<Player*>(owner);
        }
    }
    return nullptr;
}

void Unit::SendPetActionFeedback(uint32 spellID, uint8 msg)
{
    Unit* owner = GetOwner();
    if (!owner || !owner->IsPlayer())
        return;

    owner->ToPlayer()->SendDirectMessage(WorldPackets::PetPackets::ActionFeedback(spellID, msg).Write());
}

void Unit::SendPetTalk(uint32 actionSound)
{
    Unit* owner = GetOwner();
    if (!owner || !owner->IsPlayer())
        return;

    owner->ToPlayer()->SendDirectMessage(WorldPackets::PetPackets::Sound(GetGUID(), actionSound).Write());
}

void Unit::SendPetAIReaction(ObjectGuid guid)
{
    Unit* owner = GetOwner();
    if (!owner || !owner->IsPlayer())
        return;

    WorldPackets::Combat::AIReaction packet;
    packet.UnitGUID = guid;
    packet.Reaction = AI_REACTION_HOSTILE;
    owner->ToPlayer()->SendDirectMessage(packet.Write());
}

void Unit::StopMoving()
{
    ClearUnitState(UNIT_STATE_MOVING);

    // not need send any packets if not in world or not moving
    if (!IsInWorld() || movespline->Finalized())
        return;

    // Update position using old spline
    UpdateSplinePosition(true);
    Movement::MoveSplineInit(*this).Stop();
}

void Unit::SendMovementFlagUpdate(bool self /*= false*/)
{
    if (IsPlayer())
    {
        WorldPackets::Movement::MoveUpdate playerMovement;
        playerMovement.movementInfo = &m_movementInfo;
        playerMovement.movementInfo->RemoteTimeValid = true;
        SendMessageToSet(playerMovement.Write(), self);
        return;
    }
    //creature will give update at next movement.
    //if creature not moving we can send update movement by stop.
    if (!isMoving())
        Movement::MoveSplineInit(*this).Stop();
}

bool Unit::IsSitState() const
{
    uint8 s = getStandState();
    return
        s == UNIT_STAND_STATE_SIT_CHAIR        || s == UNIT_STAND_STATE_SIT_LOW_CHAIR  ||
        s == UNIT_STAND_STATE_SIT_MEDIUM_CHAIR || s == UNIT_STAND_STATE_SIT_HIGH_CHAIR ||
        s == UNIT_STAND_STATE_SIT;
}

bool Unit::IsStandState() const
{
    uint8 s = getStandState();
    return !IsSitState() && s != UNIT_STAND_STATE_SLEEP && s != UNIT_STAND_STATE_KNEEL;
}

void Unit::SetStandState(uint8 state, uint32 animKitID /* = 0*/)
{
    setStandStateValue(state);

    if (IsStandState())
       RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_SEATED);

    if (IsPlayer())
    {
        WorldPackets::Misc::StandStateUpdate update;
        update.AnimKitID = animKitID;
        update.State = static_cast<UnitStandStateType>(state);
        ToPlayer()->SendDirectMessage(update.Write());
    }
}

bool Unit::IsPolymorphed() const
{
    uint32 transformId = getTransForm();
    if (!transformId)
        return false;

    if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(transformId))
        return spellInfo->GetSpellSpecific() == SPELL_SPECIFIC_MAGE_POLYMORPH;

    return false;
}

float Unit::GetModelSize(uint32 displayId /*= 0*/) const
{
    if (auto displayInfo = sCreatureDisplayInfoStore.LookupEntry(displayId ? displayId : GetDisplayId()))
        return displayInfo->CreatureModelScale;

    return 1.0f;
}

void Unit::SetDisplayId(uint32 modelId, bool resize /* = false */)
{
    if (resize)
    {
        RemoveAurasDueToSpell(127315);
    }

    if (IsPlayer() && modelId == GetCustomDisplayId())  // если идет арена, проверяем на облики
    { 
       if (ToPlayer()->GetBattleground() && GetMapId() != 1101)
       {
            if (GetModelForForm(GetShapeshiftForm()))
                SetDisplayId(GetModelForForm(GetShapeshiftForm()));
            else
                SetDisplayId(GetNativeDisplayId());
            return;
       }
    }
    
       //for custom morph if reshift - need set scale normally
    if (IsPlayer() && GetCustomDisplayId() && !(modelId == GetCustomDisplayId()))
       SetObjectScale(1.0f);
    
       // this need for custom morph (donate)
    if (resize && (IsPlayer() || GetEntry() == 230008))
        SetObjectScale(1.0f / GetModelSize(modelId));
    
    SetUInt32Value(UNIT_FIELD_DISPLAY_ID, modelId);

    if (ToCreature())
    {
        if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId))
        {
            SetFloatValue(UNIT_FIELD_BOUNDING_RADIUS, minfo->bounding_radius);
            SetFloatValue(UNIT_FIELD_COMBAT_REACH, minfo->combat_reach);
        }
    }

    if (IsCreature() && ToCreature()->isPet())
    {
        Pet* pet = ToPet();
        if (!pet->isControlled())
            return;
        Unit* owner = GetOwner();
        if (owner && (owner->IsPlayer()) && owner->ToPlayer()->GetGroup())
            pet->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MODEL_ID);
    }
}

MirrorImageData Unit::GetMirrorImageInfo() const
{
    return m_mirrorImageData;
}

void Unit::InitMirrorImageData(Unit* target)
{
    m_mirrorImageData.DisplayID = target->GetDisplayId();

    if (Player* targetPlayer = target->ToPlayer())
    {
        m_mirrorImageData.isPlayer = true;
        m_mirrorImageData.DisplayID = targetPlayer->GetDisplayId();
        m_mirrorImageData.RaceID = targetPlayer->getRace();
        m_mirrorImageData.Gender = targetPlayer->getGender();
        m_mirrorImageData.ClassID = targetPlayer->getClass();
        m_mirrorImageData.SkinColor = targetPlayer->GetSkinValue();
        m_mirrorImageData.FaceVariation = targetPlayer->GetFaceValue();
        m_mirrorImageData.HairVariation = targetPlayer->GetHairStyleValue();
        m_mirrorImageData.HairColor = targetPlayer->GetHairColorValue();
        m_mirrorImageData.BeardVariation = targetPlayer->GetFacialStyleValue();

        for (uint32 i = 0; i < PLAYER_CUSTOM_DISPLAY_SIZE; ++i)
            m_mirrorImageData.CustomDisplay[i] = targetPlayer->GetByteValue(PLAYER_FIELD_BYTES_2, PLAYER_BYTES_2_OFFSET_CUSTOM_DISPLAY_OPTION + i);
        if (ObjectGuid guild = targetPlayer->GetGuildGUID())
            m_mirrorImageData.GuildGUID = guild;

        m_mirrorImageData.ItemDisplayID.reserve(11);

        static EquipmentSlots const itemSlots[] =
        {
            EQUIPMENT_SLOT_HEAD,
            EQUIPMENT_SLOT_SHOULDERS,
            EQUIPMENT_SLOT_BODY,
            EQUIPMENT_SLOT_CHEST,
            EQUIPMENT_SLOT_WAIST,
            EQUIPMENT_SLOT_LEGS,
            EQUIPMENT_SLOT_FEET,
            EQUIPMENT_SLOT_WRISTS,
            EQUIPMENT_SLOT_HANDS,
            EQUIPMENT_SLOT_TABARD,
            EQUIPMENT_SLOT_BACK
        };

        for (auto const& slot : itemSlots)
        {
            uint32 itemDisplayId = 0;
            if ((slot == EQUIPMENT_SLOT_HEAD && targetPlayer->HasFlag(PLAYER_FIELD_PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM)) ||
                (slot == EQUIPMENT_SLOT_BACK && targetPlayer->HasFlag(PLAYER_FIELD_PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK)))
                itemDisplayId = 0;
            else if (Item const* item = targetPlayer->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
                itemDisplayId = item->GetDisplayId(targetPlayer);

            m_mirrorImageData.ItemDisplayID.push_back(itemDisplayId);
        }
    }
}

void Unit::ClearMirrorImageData()
{
    m_mirrorImageData.isPlayer = false;
    m_mirrorImageData.DisplayID = 0;
    m_mirrorImageData.RaceID = 0;
    m_mirrorImageData.Gender = 0;
    m_mirrorImageData.ClassID = 0;
    m_mirrorImageData.SkinColor = 0;
    m_mirrorImageData.FaceVariation = 0;
    m_mirrorImageData.HairVariation = 0;
    m_mirrorImageData.HairColor = 0;
    m_mirrorImageData.BeardVariation = 0;

    m_mirrorImageData.ItemDisplayID.clear();
    m_mirrorImageData.CustomDisplay[0] = 0;
    m_mirrorImageData.CustomDisplay[1] = 0;
    m_mirrorImageData.CustomDisplay[2] = 0;
}

void Unit::RestoreDisplayId()
{
    AuraEffect* handledAura = nullptr;

    std::list<AuraType> auratypelist;
    auratypelist.push_back(SPELL_AURA_TRANSFORM);
    auratypelist.push_back(SPELL_AURA_CLONE_CASTER);

    // try to receive model from transform auras
    Unit::AuraEffectList transforms;
    const_cast<Unit*>(this)->GetAuraEffectsByListType(&auratypelist, transforms);

    if (transforms.begin() != transforms.end())
    {
        // iterate over already applied transform auras - from newest to oldest
        for (Unit::AuraEffectList::iterator i = transforms.begin(); i != transforms.end(); ++i)
        {
            if (AuraApplication const* aurApp = (*i)->GetBase()->GetApplicationOfTarget(GetGUID()))
            {
                if (!handledAura)
                    handledAura = (*i);
                // prefer negative auras
                if (!aurApp->IsPositive())
                {
                    handledAura = (*i);
                    break;
                }
            }
        }
    }

    // transform aura was found
    if (handledAura)
    {
        SetDisplayId(GetNativeDisplayId());
        handledAura->HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true);
    }
    // we've found shapeshift
    else if (uint32 modelId = GetModelForForm(GetShapeshiftForm()))
        SetDisplayId(modelId);
    // no auras found - set modelid to default
    else if (GetCustomDisplayId())
       SetDisplayId(GetCustomDisplayId(), true);
    else
        SetDisplayId(GetNativeDisplayId());
}

void Unit::ClearAllReactives()
{
    for (uint8 i = 0; i < MAX_REACTIVE; ++i)
        m_reactiveTimer[i] = 0;

    if (HasAuraState(AURA_STATE_DEFENSE))
        ModifyAuraState(AURA_STATE_DEFENSE, false);
    if (getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY))
        ModifyAuraState(AURA_STATE_HUNTER_PARRY, false);
}

void Unit::UpdateReactives(uint32 p_time)
{
    for (uint8 i = 0; i < MAX_REACTIVE; ++i)
    {
        ReactiveType reactive = ReactiveType(i);

        if (!m_reactiveTimer[reactive])
            continue;

        if (m_reactiveTimer[reactive] <= p_time)
        {
            m_reactiveTimer[reactive] = 0;

            switch (reactive)
            {
                case REACTIVE_DEFENSE:
                    if (HasAuraState(AURA_STATE_DEFENSE))
                        ModifyAuraState(AURA_STATE_DEFENSE, false);
                    break;
                case REACTIVE_HUNTER_PARRY:
                    if (getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY))
                        ModifyAuraState(AURA_STATE_HUNTER_PARRY, false);
                    break;
                default:
                    break;
            }
        }
        else
        {
            m_reactiveTimer[reactive] -= p_time;
        }
    }
}

Unit* Unit::SelectNearbyTarget(Unit* exclude, float dist) const
{
    std::list<Unit*> targets;
    Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, this, dist);
    Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(this, targets, u_check);
    Trinity::VisitNearbyObject(this, dist, searcher);

    // remove current target
    if (getVictim())
        targets.remove(getVictim());

    if (exclude)
        targets.remove(exclude);

    // remove not LoS targets
    for (std::list<Unit*>::iterator tIter = targets.begin(); tIter != targets.end();)
    {
        if (!IsWithinLOSInMap(*tIter) || (*tIter)->isTotem() || (*tIter)->isSpiritService() || (*tIter)->GetCreatureType() == CREATURE_TYPE_CRITTER)
            targets.erase(tIter++);
        else
            ++tIter;
    }

    // no appropriate targets
    if (targets.empty())
        return nullptr;

    // select random
    return Trinity::Containers::SelectRandomContainerElement(targets);
}

Unit* Unit::SelectNearbyAlly(Unit* exclude /*= nullptr*/, float dist /*= NOMINAL_MELEE_RANGE*/, bool checkValidAssist /*= false*/) const
{
    std::list<Unit*> targets;
    Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(this, this, dist);
    Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(this, targets, u_check);
    Trinity::VisitNearbyObject(this, dist, searcher);

    if (exclude)
        targets.remove(exclude);

    // remove not LoS targets
    for (std::list<Unit*>::iterator tIter = targets.begin(); tIter != targets.end();)
    {
        if (!IsWithinLOSInMap(*tIter) || (*tIter)->isTotem() || (*tIter)->isSpiritService() || (*tIter)->GetCreatureType() == CREATURE_TYPE_CRITTER)
            targets.erase(tIter++);
        else if (checkValidAssist && !IsValidAssistTarget(*tIter))
            targets.erase(tIter++);
        else
            ++tIter;
    }

    // no appropriate targets
    if (targets.empty())
        return nullptr;

    // select random
    return Trinity::Containers::SelectRandomContainerElement(targets);
}

Unit* Unit::GetNearbyVictim(Unit* exclude, float dist, bool IsInFront, bool IsNeutral) const
{
    Unit* Nearby    = nullptr;
    float nearbydist = NULL;
    std::list<Unit*> targetList;
    GetAttackableUnitListInRange(targetList, dist);

    if (exclude) targetList.remove(exclude);

    if (!targetList.empty())
    {
        for (std::list<Unit*>::const_iterator itr = targetList.begin(); itr != targetList.end(); ++itr)
        {
            if (!(*itr)->IsWithinLOSInMap(this) || IsFriendlyTo(*itr))
                continue;

            if (IsInFront)
                if (!isInFront(*itr))
                    continue;

            if (IsNeutral)
                if ((*itr)->IsNeutralToAll())
                    continue;

            Position pos;

            if (Nearby)
            {
                (*itr)->GetPosition(&pos);
                float dist2 = GetDistance(pos);

                if (nearbydist > dist2)
                {
                    nearbydist = dist2;
                    Nearby = (*itr);
                }
                else continue;
            }
            else
            {
                Nearby = (*itr);
                Nearby->GetPosition(&pos);
                nearbydist = GetDistance(pos);
            }
        }
    }
    return Nearby;
}

void Unit::CalcAttackTimePercentMod(WeaponAttackType att, float val)
{
    val /= GetTotalAuraMultiplier(SPELL_AURA_MOD_MELEE_AND_RANGED_ATTACK_SPEED_PCT);
    val /= GetTotalAuraMultiplier(SPELL_AURA_MOD_MELEE_ATTACK_SPEED);
    val *= GetFloatValue(UNIT_FIELD_MOD_TIME_RATE);

    uint32 attackTime = BASE_ATTACK_TIME;

    if (Player* plr = ToPlayer())
    {
        auto const& shapeshift = sSpellShapeshiftFormStore.LookupEntry(GetShapeshiftForm());
        if (shapeshift && shapeshift->CombatRoundTime)
        {
            attackTime = shapeshift->CombatRoundTime;
        }
        else if (Item* weapon = plr->GetItemByPos(INVENTORY_SLOT_BAG_0, att == OFF_ATTACK ? EQUIPMENT_SLOT_OFFHAND : EQUIPMENT_SLOT_MAINHAND))
        {
            if (ItemTemplate const* iTemp = weapon->GetTemplate())
                attackTime = iTemp->GetDelay();
        }
    }
    else if (!isAnySummons())
    {
        attackTime = GetAttackTime(att);
    }

    float remainingTimePct = static_cast<float>(m_attackTimer[att]) / (attackTime * m_modAttackSpeedPct[att]);

    m_modAttackSpeedPct[att] = val;

    SetAttackTime(att, attackTime);

    if (val && isPet())
        UpdateDamagePhysical(att);

    m_attackTimer[att] = uint32(GetAttackTime(att) * m_modAttackSpeedPct[att] * remainingTimePct);
}

uint32 Unit::GetCastingTimeForBonus(SpellInfo const* spellProto, DamageEffectType damagetype, uint32 CastingTime) const
{
    // Not apply this to creature casted spells with casttime == 0
    if (CastingTime == 0 && IsCreature() && !ToCreature()->isPet())
        return 3500;

    if (CastingTime > 7000) CastingTime = 7000;
    if (CastingTime < 1500) CastingTime = 1500;

    if (damagetype == DOT && !spellProto->IsChanneled())
        CastingTime = 3500;

    int32 overTime    = 0;
    uint8 effects     = 0;
    bool DirectDamage = false;
    bool AreaEffect   = false;

    for (uint32 i = 0; i < MAX_SPELL_EFFECTS; i++)
    {
        if (spellProto->EffectMask < uint32(1 << i))
            break;

        switch (spellProto->GetEffect(i, GetSpawnMode())->Effect)
        {
            case SPELL_EFFECT_SCHOOL_DAMAGE:
            case SPELL_EFFECT_POWER_DRAIN:
            case SPELL_EFFECT_HEALTH_LEECH:
            case SPELL_EFFECT_ENVIRONMENTAL_DAMAGE:
            case SPELL_EFFECT_POWER_BURN:
            case SPELL_EFFECT_HEAL:
                DirectDamage = true;
                break;
            case SPELL_EFFECT_APPLY_AURA:
                switch (spellProto->GetEffect(i, GetSpawnMode())->ApplyAuraName)
                {
                    case SPELL_AURA_PERIODIC_DAMAGE:
                    case SPELL_AURA_PERIODIC_HEAL:
                    case SPELL_AURA_PERIODIC_LEECH:
                        if (spellProto->GetDuration(m_spawnMode))
                            overTime = spellProto->GetDuration(m_spawnMode);
                        break;
                    default:
                        // -5% per additional effect
                        ++effects;
                        break;
                }
            default:
                break;
        }

        if (spellProto->GetEffect(i, GetSpawnMode())->IsTargetingArea())
            AreaEffect = true;
    }

    // Combined Spells with Both Over Time and Direct Damage
    if (overTime > 0 && CastingTime > 0 && DirectDamage)
    {
        // mainly for DoTs which are 3500 here otherwise
        uint32 OriginalCastTime = spellProto->CalcCastTime();
        if (OriginalCastTime > 7000) OriginalCastTime = 7000;
        if (OriginalCastTime < 1500) OriginalCastTime = 1500;
        // Portion to Over Time
        float PtOT = (overTime / 15000.0f) / ((overTime / 15000.0f) + (OriginalCastTime / 3500.0f));

        if (damagetype == DOT)
            CastingTime = uint32(CastingTime * PtOT);
        else if (PtOT < 1.0f)
            CastingTime  = uint32(CastingTime * (1 - PtOT));
        else
            CastingTime = 0;
    }

    // Area Effect Spells receive only half of bonus
    if (AreaEffect)
        CastingTime /= 2;

    // 50% for damage and healing spells for leech spells from damage bonus and 0% from healing
    for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
    {
        if (spellProto->EffectMask < uint32(1 << j))
            break;

        auto const& info = spellProto->GetEffect(j, GetSpawnMode());
        if (info->Effect == SPELL_EFFECT_HEALTH_LEECH || (info->Effect == SPELL_EFFECT_APPLY_AURA && info->ApplyAuraName == SPELL_AURA_PERIODIC_LEECH))
        {
            CastingTime /= 2;
            break;
        }
    }

    // -5% of total per any additional effect
    for (uint8 i = 0; i < effects; ++i)
        CastingTime *= 0.95f;

    return CastingTime;
}

void Unit::UpdateAuraForGroup()
{
    if (Player* player = ToPlayer())
        player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_AURAS);
    else if (IsCreature() && ToCreature()->isPet())
    {
        auto pet = ToPet();
        if (pet->isControlled())
        {
            auto owner = GetOwner();
            if (owner && (owner->IsPlayer()) && owner->ToPlayer()->GetGroup())
                pet->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_AURAS);
        }
    }
}

bool Unit::VisibleAuraSlotCompare::operator()(AuraApplication* left, AuraApplication* right) const
{
    return left->GetSlot() < right->GetSlot();
}

float Unit::CalculateDefaultCoefficient(SpellInfo const *spellInfo, DamageEffectType damagetype) const
{
    // Damage over Time spells bonus calculation
    float DotFactor = 1.0f;
    if (damagetype == DOT)
    {

        int32 DotDuration = spellInfo->GetDuration(m_spawnMode);
        if (!spellInfo->IsChanneled() && DotDuration > 0)
            DotFactor = DotDuration / 15000.0f;

        if (uint32 DotTicks = spellInfo->GetMaxTicks())
            DotFactor /= DotTicks;
    }

    int32 CastingTime = spellInfo->IsChanneled() ? spellInfo->GetDuration(m_spawnMode) : spellInfo->CalcCastTime();
    // Distribute Damage over multiple effects, reduce by AoE
    CastingTime = GetCastingTimeForBonus(spellInfo, damagetype, CastingTime);

    // As wowwiki says: C = (Cast Time / 3.5)
    return (CastingTime / 3500.0f) * DotFactor;
}

float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized)
{
    if (Player* player = ToPlayer())
    {
        if (IsInFeralForm())
            return 1.8f;

        Item* weapon = player->GetWeaponForAttack(attType, true);
        if (!weapon)
            return 2.0f;

        if (!normalized)
            return weapon->GetTemplate()->GetDelay() / 1000.0f;

        switch (weapon->GetTemplate()->GetSubClass())
        {
            case ITEM_SUBCLASS_WEAPON_AXE2:
            case ITEM_SUBCLASS_WEAPON_MACE2:
            case ITEM_SUBCLASS_WEAPON_POLEARM:
            case ITEM_SUBCLASS_WEAPON_SWORD2:
            case ITEM_SUBCLASS_WEAPON_STAFF:
            case ITEM_SUBCLASS_WEAPON_FISHING_POLE:
                return 3.6f;
            case ITEM_SUBCLASS_WEAPON_AXE:
            case ITEM_SUBCLASS_WEAPON_MACE:
            case ITEM_SUBCLASS_WEAPON_SWORD:
            case ITEM_SUBCLASS_WEAPON_WARGLAIVES:
            case ITEM_SUBCLASS_WEAPON_EXOTIC:
            case ITEM_SUBCLASS_WEAPON_EXOTIC2:
            case ITEM_SUBCLASS_WEAPON_FIST_WEAPON:
                return 2.4f;
            case ITEM_SUBCLASS_WEAPON_DAGGER:
                return 1.8f;
            case ITEM_SUBCLASS_WEAPON_BOW:
            case ITEM_SUBCLASS_WEAPON_GUN:
            case ITEM_SUBCLASS_WEAPON_CROSSBOW:
                return 2.8f;
            default:
                return weapon->GetTemplate()->GetDelay() / 1000.0f;
        }
    }

    return float(GetAttackTime(attType)) / 1000.0f;
}

void Unit::SetContestedPvP(Player* attackedPlayer /*= nullptr*/, bool forceByAura /*= false*/)
{
    Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
    if (!player || (!forceByAura || (attackedPlayer && (attackedPlayer == player || (player->duel && player->duel->opponent == attackedPlayer->GetGUID())))))
        return;

    int32 duration = std::chrono::duration_cast<Milliseconds>(Seconds(30)).count();
    if (forceByAura)
        duration = GetTotalAuraDurationByType(SPELL_AURA_CONTESTED_PVP, true);
    
    player->SetContestedPvPTimer(duration);

    if (forceByAura)
        SetFlag(PLAYER_FIELD_PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
    else
    {
        if (!player->HasUnitState(UNIT_STATE_ATTACK_PLAYER))
        {
            player->AddUnitState(UNIT_STATE_ATTACK_PLAYER);
            player->SetFlag(PLAYER_FIELD_PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
        }

        if (!HasUnitState(UNIT_STATE_ATTACK_PLAYER))
            AddUnitState(UNIT_STATE_ATTACK_PLAYER);
    }

    UpdateObjectVisibility();
}

OutdoorPvP* Unit::GetOutdoorPvP() const
{
    if (OutdoorPvP* pvp = sOutdoorPvPMgr->GetOutdoorPvPToZoneId(GetCurrentZoneID()))
        return pvp;
    if (OutdoorPvP* pvp = sOutdoorPvPMgr->GetOutdoorPvPToZoneId(GetCurrentAreaID()))
        return pvp;
    return nullptr;
}

Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spellID)
{
    Player* player = ToPlayer();
    if (!player)
        return nullptr;

    player->m_currentSummonedSlot = player->getSlotForNewPet();

    if (player->m_currentSummonedSlot == PET_SLOT_FULL_LIST)
        return nullptr;

    Pet* pet = new Pet(player, HUNTER_PET);

    if (!pet->CreateBaseAtCreature(creatureTarget))
    {
        delete pet;
        return nullptr;
    }

    InitTamedPet(pet, spellID);
    pet->SetSlot(player->m_currentSummonedSlot);
    player->AddPetInfo(pet);

    return pet;
}

Pet* Unit::CreateTamedPetFrom(uint32 creatureEntry, uint32 spellID)
{
    Player* player = ToPlayer();
    if (!player)
        return nullptr;

    player->m_currentSummonedSlot = player->getSlotForNewPet();

    if (player->m_currentSummonedSlot == PET_SLOT_FULL_LIST)
        return nullptr;

    CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureEntry);
    if (!creatureInfo)
        return nullptr;

    Pet* pet = new Pet(player, HUNTER_PET);

    if (!pet->CreateBaseAtCreatureInfo(creatureInfo, this) || !InitTamedPet(pet, spellID))
    {
        delete pet;
        return nullptr;
    }

    pet->SetSlot(player->m_currentSummonedSlot);
    player->AddPetInfo(pet);

    return pet;
}

bool Unit::InitTamedPet(Pet* pet, uint32 spellID)
{
    pet->SetCreatorGUID(GetGUID());
    pet->setFaction(getFaction());
    pet->SetUInt32Value(UNIT_FIELD_CREATED_BY_SPELL, spellID);

    if (IsPlayer())
    {
        pet->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
        pet->AddUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER);
    }

    if (!pet->InitStatsForLevel(GetEffectiveLevel(), false))
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Pet::InitStatsForLevel() failed for creature (Entry: %u)!", pet->GetEntry());
        return false;
    }

    pet->GetCharmInfo()->SetPetNumber(sObjectMgr->GeneratePetNumber(), true);
    // this enables pet details window (Shift+P)
    pet->InitPetCreateSpells();
    pet->SetFullHealth();
    return true;
}

bool Unit::SpellProcTriggered(Unit* victim, DamageInfo* dmgInfoProc, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, double cooldown)
{
    uint32 damage = dmgInfoProc->GetDamage();
    int32 addpowertype = dmgInfoProc->GetAddPType();
    SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo();
    uint32 effIndex = triggeredByAura->GetEffIndex();
    float triggerAmount = triggeredByAura->GetAmount();
    int32 cooldown_spell_id = 0;
    uint32 triggered_spell_id = triggeredByAura->GetTriggerSpell() ? triggeredByAura->GetTriggerSpell(): 0;

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer() ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    Unit* target = victim;
    float basepoints0 = 0.f;
    float basepoints1 = 0.f;
    float basepoints2 = 0.f;
    ObjectGuid originalCaster;
    Unit* procSpellCaster = dmgInfoProc->GetAttacker();
    ObjectGuid procSpellCasterGUID = procSpellCaster ? procSpellCaster->GetGUID() : ObjectGuid::Empty;
    int32 schoolMask = procSpell ? SpellSchoolMask(procSpell->GetMisc(m_spawnMode)->MiscData.SchoolMask) : SPELL_SCHOOL_MASK_NORMAL;

    if (std::vector<SpellTriggered> const* spellTrigger = sSpellMgr->GetSpellTriggered(dummySpell->Id))
    {
        bool check = false;
        std::list<int32> groupList;
        for (std::vector<SpellTriggered>::const_iterator itr = spellTrigger->begin(); itr != spellTrigger->end(); ++itr)
        {
            if(itr->dummyId) //take amount from other spell
            {
                if (AuraEffect const* aurEff = GetAuraEffect(abs(itr->dummyId), itr->dummyEffect))
                    triggerAmount = aurEff->GetAmount();
                else if(SpellInfo const* dummySpellInfo = sSpellMgr->GetSpellInfo(abs(itr->dummyId)))
                    triggerAmount = dummySpellInfo->Effects[itr->dummyEffect]->BasePoints;
            }
            if(itr->spell_cooldown)
                cooldown_spell_id = abs(itr->spell_cooldown);
            else
                cooldown_spell_id = abs(itr->spell_trigger);

            int32 spell_trigger = itr->spell_trigger;
            if (!itr->randList.empty())
                spell_trigger = Trinity::Containers::SelectRandomContainerElement(itr->randList);

            #ifdef WIN32
            TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SpellTriggered target %u, caster %u, spell_trigger %i, chance %i, triggerAmount %i, damage %i, GetAbsorb %i, GetResist %i, GetBlock %i",
            itr->target, itr->caster, itr->spell_trigger, itr->chance, triggerAmount, damage, dmgInfoProc->GetAbsorb(), dmgInfoProc->GetResist(), dmgInfoProc->GetBlock());
            TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, " group %i, effIndex %i, effectmask %i, option %i, (1<<effIndex) %i, procFlag %i addpowertype %i addptype %i procEx %i cooldown %f cooldown_spell_id %i",
            itr->group, effIndex, itr->effectmask, itr->option, (1<<effIndex), procFlag, addpowertype, itr->addptype, procEx, cooldown, cooldown_spell_id);
            #endif

            if (G3D::fuzzyGt(cooldown, 0.0))
            {
                if (itr->option == SPELL_TRIGGER_COOLDOWN || itr->option == SPELL_TRIGGER_MODIFY_COOLDOWN)
                {
                    if (!HasSpellCooldown(spell_trigger))
                        continue;

                    cooldown_spell_id = dummySpell->Id;

                    if (HasSpellCooldown(cooldown_spell_id))
                        return false;
                }
                else if (HasSpellCooldown(cooldown_spell_id))
                    return false;
            }

            if (!(itr->effectmask & (1<<effIndex)))
                continue;

            if (itr->procFlags > 0 && !(itr->procFlags & procFlag))
                continue;
            if (itr->procFlags < 0 && (abs(itr->procFlags) & procFlag))
                continue;
            if (itr->procEx > 0 && !(itr->procEx & procEx))
                continue;
            if (itr->procEx < 0 && (abs(itr->procEx) & procEx))
                continue;
            if (itr->schoolMask > 0 && !(itr->schoolMask & schoolMask))
                continue;
            if (itr->schoolMask < 0 && (abs(itr->schoolMask) & schoolMask))
                continue;
            if(itr->addptype != -1 && itr->addptype != addpowertype)
                continue;
            if(itr->addptype == -1 && addpowertype != -1)
                continue;
            if(itr->chance != 0)
            {
                if(itr->chance > 100) // chance get from amount
                {
                    if(!roll_chance_i(triggerAmount))
                        continue;
                }
                else if(itr->chance == -1) // chance get from amount / 10
                {
                    int32 rollchance = urand(0, 1000);
                    if (rollchance > triggerAmount)
                        continue;
                }
                else if(itr->chance == -2) // chance get from amount / 100
                {
                    int32 rollchance = urand(0, 10000);
                    if (rollchance > triggerAmount)
                        continue;
                }
                else if(itr->chance == -3) // chance get from target health
                {
                    if (!target)
                        continue;
                    if (dummySpell->GetAuraOptions(GetSpawnMode())->ProcChance < int32(target->GetHealthPct()))
                        continue;
                    int32 rollchance = 100 - int32((target->GetHealthPct() / 100) * dummySpell->GetAuraOptions(GetSpawnMode())->ProcChance);
                    if(!roll_chance_i(rollchance))
                        continue;
                }
                else if(!roll_chance_i(itr->chance))
                    continue;
            }
            if(itr->check_spell_id > 0)
            {
                if(!procSpell)
                    continue;
                if(procSpell->Id != itr->check_spell_id)
                    continue;
            }
            else if(itr->check_spell_id < 0)
            {
                if(!procSpell)
                    continue;
                if(procSpell->Id == abs(itr->check_spell_id))
                    continue;
            }

            if(itr->group != 0 && !groupList.empty())
            {
                bool groupFind = false;
                for (std::list<int32>::const_iterator group_itr = groupList.begin(); group_itr != groupList.end(); ++group_itr)
                    if((*group_itr) == itr->group)
                        groupFind = true;
                if(groupFind)
                    continue;
            }

            if(itr->target == 8 && target == this) //not trigger spell for self
                continue;
            if(itr->target == 1 || itr->target == 6 || !target) //get target self
                target = this;
            if(itr->target == 3 && ToPlayer()) //get target owner
                if (Pet* pet = ToPlayer()->GetPet())
                    target = static_cast<Unit*>(pet);
            if(itr->target == 4 && target->ToPlayer()) //get target pet
                if (Pet* pet = target->ToPlayer()->GetPet())
                    target = static_cast<Unit*>(pet);
            if(itr->target == 5) //get target owner
                if (Unit* owner = GetAnyOwner())
                    target = owner;
            if(itr->target == 7) //get target self
                target = triggeredByAura->GetCaster();
            if(itr->target == 10) //get target owner or self
            {
                if (Player* _player = ToPlayer())
                {
                    if (Pet* pet = _player->GetPet())
                        target = static_cast<Unit*>(pet);
                }
                else if (Unit* owner = GetAnyOwner())
                    target = owner;
            }
            if(itr->target == 11 && itr->slot) // Get summon from list
            {
                Unit* summon = nullptr;
                GuidList* summonList = GetSummonList(itr->slot);
                for (GuidList::const_iterator iter = summonList->begin(); iter != summonList->end(); ++iter)
                    if(summon = ObjectAccessor::GetUnit(*this, (*iter)))
                        break;
                if (!summon)
                    continue;
                target = summon;
            }


            Unit* _caster = this;
            Unit* _targetAura = this;
            Unit* _targetAura2 = this;

            if(itr->caster == 1) //get caster aura
                _caster = triggeredByAura->GetCaster();
            if(itr->caster == 3 && ToPlayer()) //get caster owner
                if (Pet* pet = ToPlayer()->GetPet())
                    _caster = static_cast<Unit*>(pet);
            if(itr->caster == 4) //get caster owner
                if (Unit* owner = GetAnyOwner())
                    _caster = owner;
            if(itr->caster == 5) //get caster is target
                _caster = victim;
            if(itr->caster == 10) //get target owner or self
            {
                if (Player* _player = ToPlayer())
                {
                    if (Pet* pet = _player->GetPet())
                        _caster = static_cast<Unit*>(pet);
                }
                else if (Unit* owner = GetAnyOwner())
                    _caster = owner;
            }
            if(itr->caster == 11 && itr->slot) // Get summon from list
            {
                Unit* summon = nullptr;
                GuidList* summonList = GetSummonList(itr->slot);
                for (GuidList::const_iterator iter = summonList->begin(); iter != summonList->end(); ++iter)
                    if(summon = ObjectAccessor::GetUnit(*this, (*iter)))
                        break;
                if (!summon)
                    continue;
                _caster = summon;
            }

            if(!_caster)
                _caster = this;

            if(itr->target == 9) //get target select
                if (Player* _player = _caster->ToPlayer())
                    if (Unit* _select = _player->GetSelectedUnit())
                        target = _select;

            if(itr->targetaura == 1) //get caster aura
                _targetAura = triggeredByAura->GetCaster();
            if(itr->targetaura == 2) //get target aura
                _targetAura = victim;
            if(itr->targetaura == 3) //get target select
                if (Player* _player = ToPlayer())
                    if (Unit* _select = _player->GetSelectedUnit())
                        _targetAura = _select;

            if(itr->targetaura2 == 1) //get caster aura
                _targetAura2 = triggeredByAura->GetCaster();
            if(itr->targetaura2 == 2) //get target aura
                _targetAura2 = victim;
            if(itr->targetaura2 == 3) //get target select
                if (Player* _player = ToPlayer())
                    if (Unit* _select = _player->GetSelectedUnit())
                        _targetAura2 = _select;

            if (_targetAura)
            {
                if (itr->CreatureType > 0 && !(itr->CreatureType & (1<<_targetAura->GetCreatureType())))
                    continue;
                if (itr->CreatureType < 0 && (itr->CreatureType & (1<<_targetAura->GetCreatureType())))
                    continue;
            }

            if (!_targetAura)
                continue;

            if(itr->aura2 > 0 && !_targetAura2->HasAura(itr->aura2))
            {
                check = true;
                continue;
            }
            if(itr->aura2 < 0 && _targetAura2->HasAura(abs(itr->aura2)))
            {
                check = true;
                continue;
            }

            float bp0 = itr->bp0;
            float bp1 = itr->bp1;
            float bp2 = itr->bp2;
            switch (itr->option)
            {
                case SPELL_TRIGGER_BP: //0
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    if(spell_trigger > 0)
                        basepoints0 = triggerAmount;
                    else
                        basepoints0 = -(triggerAmount);

                    triggered_spell_id = abs(spell_trigger);
                    if(basepoints0)
                        _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    else
                        _caster->CastSpell(target, triggered_spell_id, true);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                        {
                            if(basepoints0)
                                _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &bp1, &bp2, true);
                            else
                                _caster->CastSpell(pet, triggered_spell_id, true);
                        }
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_BP_CUSTOM: //1
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &bp0, &bp1, &bp2, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &bp1, &bp2, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_MANA_COST: //2
                {
                    if(!procSpell)
                        continue;
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    SpellPowerCost cost;
                    procSpell->CalcPowerCost(_caster, procSpell->GetSchoolMask(), cost);
                    basepoints0 = CalculatePct(cost[POWER_MANA], bp0);

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &bp1, &bp2, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &bp1, &bp2, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_DAM_HEALTH: //3
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    float percent = 0.0f;
                    if (Player* plr = ToPlayer())
                    {
                        if (dummySpell->HasAttribute(SPELL_ATTR8_MASTERY_SPECIALIZATION))
                            percent = plr->GetFloatValue(PLAYER_FIELD_MASTERY) * dummySpell->GetEffect(effIndex, m_spawnMode)->BonusCoefficient;
                    }

                    if (!percent)
                        percent = triggerAmount;

                    if(bp0)
                        percent += bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;

                    basepoints0 = CalculatePct(int32(dmgInfoProc->GetDamage() + dmgInfoProc->GetAbsorb()), percent);

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_COOLDOWN: //4
                {
                    if(Player* player = target->ToPlayer())
                    {
                        uint32 spellid = abs(spell_trigger);
                        if(itr->bp0 == 0.0f)
                            player->RemoveSpellCooldown(spellid, true);
                        else
                        {
                            if(itr->aura && !procSpell)
                                continue;
                            if(itr->aura && procSpell && itr->aura != procSpell->Id)
                                continue;

                            int32 delay = itr->bp0;
                            player->ModifySpellCooldown(spellid, delay);
                        }
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_UPDATE_DUR: //5
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    if (spell_trigger > 0)
                    {
                        if (Aura* aura = target->GetAura(abs(spell_trigger), _caster->GetGUID()))
                            aura->RefreshTimers();
                    }
                    else
                    {
                        if (procSpell->Id == -(spell_trigger))
                            if (procSpellCasterGUID == triggeredByAura->GetCasterGUID())
                                triggeredByAura->GetBase()->RefreshTimers();
                    }

                    check = true;
                    break;
                }
                case SPELL_TRIGGER_GET_DUR_AURA: //6
                {
                    if(Aura* aura = target->GetAura(itr->aura, GetGUID()))
                        basepoints0 = int32(aura->GetDuration() / 1000);
                    if(basepoints0)
                    {
                        triggered_spell_id = abs(spell_trigger);
                        _caster->CastCustomSpell(this, triggered_spell_id, &basepoints0, &bp1, &bp2, true, castItem, triggeredByAura, originalCaster);
                        if(itr->target == 6)
                        {
                            if (Guardian* pet = GetGuardianPet())
                                _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &bp1, &bp2, true);
                        }
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_UPDATE_DUR_TO_MAX: //8
                {
                    if(Aura* aura = target->GetAura(abs(spell_trigger), GetGUID()))
                        aura->SetDuration(aura->GetSpellInfo()->GetMaxDuration(), true);
                    check = true;
                }
                break;
                case SPELL_TRIGGER_PERC_FROM_DAMGE: //9
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    float amount = bp0;

                    if (!bp0)
                        amount = triggerAmount / 100.0f;

                    basepoints0 = CalculatePct(damage, amount);

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &bp1, &bp2, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &bp1, &bp2, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_PERC_MAX_MANA: //10
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    int32 percent = triggerAmount;
                    if(bp0)
                        percent += bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;
                    basepoints0 = CalculatePct(target->GetMaxPower(POWER_MANA), percent);

                    triggered_spell_id = abs(spell_trigger);
                    //target->EnergizeBySpell(target, triggered_spell_id, basepoints0, POWER_MANA);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura, originalCaster);
                    check = true;
                }
                break;
                case SPELL_TRIGGER_PERC_BASE_MANA: //11
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    int32 percent = triggerAmount;
                    if(bp0)
                        percent += bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;
                    basepoints0 = CalculatePct(target->GetCreateMana(), percent);

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura, originalCaster);
                    //target->EnergizeBySpell(target, triggered_spell_id, basepoints0, POWER_MANA);
                    check = true;
                }
                break;
                case SPELL_TRIGGER_PERC_CUR_MANA: //12
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    basepoints0 = int32((itr->bp0 / 100.0f) * target->GetPower(POWER_MANA));

                    triggered_spell_id = abs(spell_trigger);
                    target->EnergizeBySpell(target, triggered_spell_id, basepoints0, POWER_MANA);
                    check = true;
                }
                break;
                case SPELL_TRIGGER_CHECK_PROCK: //13
                {
                    if(!procSpell)
                    {
                        check = true;
                        continue;
                    }
                    triggered_spell_id = abs(spell_trigger);

                    if(itr->aura == procSpell->Id)
                        _caster->CastSpell(target, triggered_spell_id, true);

                    check = true;
                }
                break;
                case SPELL_TRIGGER_CHECK_DAMAGE: //16
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    triggered_spell_id = abs(spell_trigger);

                    if(int32(damage) >= triggerAmount)
                    {
                        _caster->CastSpell(target, triggered_spell_id, itr->bp0 ? false : true);
                        triggeredByAura->GetBase()->Remove(AURA_REMOVE_BY_DEFAULT);
                        check = true;
                        continue;
                    }

                    triggeredByAura->SetAmount(triggerAmount - damage);

                    check = true;
                }
                break;
                case SPELL_TRIGGER_CAST_OR_REMOVE: // 20
                {
                    triggered_spell_id = abs(spell_trigger);

                    if (itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if (itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    if (spell_trigger > 0)
                        _caster->CastSpell(target, triggered_spell_id, true, castItem);
                    else
                        _caster->RemoveAurasDueToSpell(triggered_spell_id);

                    check = true;
                    break;
                }
                case SPELL_TRIGGER_UPDATE_DUR_TO_IGNORE_MAX: //21
                {
                    if(Aura* aura = target->GetAura(abs(spell_trigger), GetGUID()))
                    {
                        int32 _duration = int32(aura->GetDuration() + int32(triggerAmount * 1000));
                        if(bp0 && bp0 < _duration)
                            _duration = int32(bp0);

                        aura->SetDuration(_duration, true);
                        if (_duration > aura->GetMaxDuration())
                            aura->SetMaxDuration(_duration);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_ADD_DURATION: //22
                {
                    int32 _duration = bp0 ? bp0 : triggerAmount;
                    triggered_spell_id = abs(spell_trigger);
                    if(Aura* aura = target->GetAura(triggered_spell_id, GetGUID()))
                    {
                        _duration += aura->GetDuration();
                        if (_duration < aura->GetMaxDuration())
                            aura->SetDuration(_duration, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_MODIFY_COOLDOWN: //23
                {
                    if(Player* player = target->ToPlayer())
                    {
                        triggered_spell_id = abs(spell_trigger);
                        if(itr->aura && !procSpell)
                            continue;
                        if(itr->aura && procSpell && itr->aura != procSpell->Id)
                            continue;

                        int32 ChangeCooldown = triggerAmount * (target->GetComboPoints() ? target->GetComboPoints() : 1);

                        if(ChangeCooldown < 100)
                            ChangeCooldown *= IN_MILLISECONDS;

                        if(bp0)
                            ChangeCooldown += bp0;
                        if(bp1)
                            ChangeCooldown /= bp1;
                        if(bp2)
                            ChangeCooldown *= bp2;

                        player->ModifySpellCooldown(triggered_spell_id, -ChangeCooldown);
                    }
                    check = true;
                    break;
                }
                case SPELL_TRIGGER_ADD_DURATION_OR_CAST_DURATION: //25
                {
                    triggered_spell_id = abs(spell_trigger);

                    basepoints0 = triggerAmount > 100 ? triggerAmount : triggerAmount * 1000;

                    if (bp0)
                        basepoints0 += bp0;
                    if (bp1)
                        basepoints0 /= bp1;
                    if (bp2)
                        basepoints0 *= bp2;

                    if (Aura* aura = _caster->GetAura(triggered_spell_id))
                    {
                        int32 _duration = int32(aura->GetDuration() + basepoints0);
                        aura->SetDuration(_duration, true);
                    }
                    else
                        _caster->CastSpellDuration(_caster, triggered_spell_id, true, basepoints0);
                    check = true;
                }
                break;
                case SPELL_TRIGGER_REMOVE_CD_RUNE: //26
                {
                    if(Player* _player = _caster->ToPlayer())
                    {
                        int32 runesRestor = 0;
                        for (int i = 0; i < _player->GetMaxPower(POWER_RUNES) ; i++)
                        {
                            if (_player->GetRuneCooldown(i) == _player->GetRuneBaseCooldown() && runesRestor < 1)
                            {
                                runesRestor++;
                                _player->SetRuneCooldown(i, 0);
                                _player->AddRunePower(i);
                            }
                        }
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_BP_SPELLID: //27
                {
                    if(!procSpell)
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    triggered_spell_id = abs(spell_trigger);
                    if(itr->bp0)
                        basepoints0 = procSpell->Id;
                    if(itr->bp1)
                        basepoints1 = procSpell->Id;
                    if(itr->bp2)
                        basepoints2 = procSpell->Id;
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints1, &basepoints2, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints1, &basepoints2, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_BP_SPD_AP: //28
                {
                    if(!procSpell)
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    WeaponAttackType attType = BASE_ATTACK;
                    if (procSpell->Categories.DefenseType == SPELL_DAMAGE_CLASS_RANGED)
                        attType = RANGED_ATTACK;

                    int32 SPD = GetSpellPowerDamage(SPELL_SCHOOL_MASK_ALL);
                    int32 SPDH = GetSpellPowerHealing();
                    int32 AP = GetTotalAttackPowerValue(attType);

                    triggered_spell_id = abs(spell_trigger);
                    switch (int32(itr->bp0))
                    {
                        case 1:
                            basepoints0 = SPD;
                            break;
                        case 2:
                            basepoints0 = SPDH;
                            break;
                        case 3:
                            basepoints0 = AP;
                            break;
                    }
                    switch (int32(itr->bp1))
                    {
                        case 1:
                            basepoints1 = SPD;
                            break;
                        case 2:
                            basepoints1 = SPDH;
                            break;
                        case 3:
                            basepoints1 = AP;
                            break;
                    }
                    switch (int32(itr->bp2))
                    {
                        case 1:
                            basepoints2 = SPD;
                            break;
                        case 2:
                            basepoints2 = SPDH;
                            break;
                        case 3:
                            basepoints2 = AP;
                            break;
                    }

                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints1, &basepoints2, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints1, &basepoints2, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_COMBOPOINT_BP: //29
                {
                    if (!GetComboPoints())
                    {
                        check = true;
                        continue;
                    }
                    if (itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if (itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    basepoints0 = triggerAmount * GetComboPoints();
                    if (bp0)
                        basepoints0 += bp0;
                    if (bp1)
                        basepoints0 /= bp1;
                    if (bp2)
                        basepoints0 *= bp2;

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    if (itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_DAM_PERC_FROM_MAX_HP: //30
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    basepoints0 = int32(float(dmgInfoProc->GetDamage() * 100.0f) / target->GetMaxHealth(_caster));
                    if(bp0)
                        basepoints0 *= bp0;

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_SUMM_DAMAGE_PROC: //31
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    int32 limited = 0;
                    int32 summ_damage = triggerAmount + dmgInfoProc->GetDamage();
                    triggered_spell_id = abs(spell_trigger);
                    if (itr->bp0)
                        limited = int32(GetSpellPowerDamage(SPELL_SCHOOL_MASK_ALL) * itr->bp0);
                    else if (itr->bp1)
                        limited = int32(GetSpellPowerHealing() * itr->bp1);
                    else if (itr->bp2)
                    {
                        WeaponAttackType attType = BASE_ATTACK;
                        if (procSpell && procSpell->Categories.DefenseType == SPELL_DAMAGE_CLASS_RANGED)
                            attType = RANGED_ATTACK;
                        limited = int32(GetTotalAttackPowerValue(attType) * itr->bp2);
                    }

                    if(summ_damage < limited)
                    {
                        triggeredByAura->SetAmount(summ_damage);
                        check = true;
                        continue;
                    }
                    triggeredByAura->SetAmount(0);
                    _caster->CastSpell(target, triggered_spell_id, true);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastSpell(pet, triggered_spell_id, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_ADDPOWER_PCT: //32
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    int32 percent = triggerAmount;
                    if(bp0)
                        percent += bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;

                    if(!dmgInfoProc->GetAddPower())
                    {
                        check = true;
                        continue;
                    }

                    basepoints0 = CalculatePct(dmgInfoProc->GetAddPower(), percent);

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_ADD_ABSORB_PCT: //33
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    int32 percent = triggerAmount;
                    if(bp0)
                        percent += bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;

                    if(!dmgInfoProc->GetAbsorb())
                    {
                        check = true;
                        continue;
                    }

                    basepoints0 = CalculatePct(dmgInfoProc->GetAbsorb(), percent);

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_ADD_BLOCK_PCT: //34
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    int32 percent = triggerAmount;
                    if(bp0)
                        percent += bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;

                    if(!dmgInfoProc->GetBlock())
                    {
                        check = true;
                        continue;
                    }

                    basepoints0 = CalculatePct(dmgInfoProc->GetBlock(), percent);

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_NEED_COMBOPOINTS: //35
                {
                    if(!procSpell || !GetComboPoints())
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastSpell(target, triggered_spell_id, true);
                    check = true;
                }
                break;
                case SPELL_TRIGGER_ADD_STACK: //17
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    triggered_spell_id = abs(spell_trigger);

                    if(Aura* aura = _caster->GetAura(triggered_spell_id))
                        if(aura->GetStackAmount() < (triggerAmount + bp1))
                            aura->ModStackAmount(bp0);

                    check = true;
                }
                break;
                case SPELL_TRIGGER_ADD_CHARGES: //18
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    triggered_spell_id = abs(spell_trigger);

                    if(Aura* aura = _caster->GetAura(triggered_spell_id))
                        if(aura->GetCharges() < (triggerAmount + bp1))
                            aura->ModCharges(bp0);

                    check = true;
                }
                break;
                case SPELL_TRIGGER_ADD_CHARGES_STACK: //19
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    triggered_spell_id = abs(spell_trigger);

                    if(Aura* aura = _caster->GetAura(triggered_spell_id))
                        if(aura->GetCharges() < (triggerAmount + bp1))
                        {
                            aura->ModCharges(bp0);
                            aura->ModStackAmount(bp0);
                        }

                    check = true;
                }
                break;
                case SPELL_TRIGGER_HOLYPOWER_BONUS: //36
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    int32 percent = triggerAmount;
                    if(bp0)
                        percent += bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;

                    basepoints0 = CalculatePct(int32(dmgInfoProc->GetDamage() + dmgInfoProc->GetAbsorb()), percent) / GetModForHolyPowerSpell();

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_CAST_AFTER_MAX_STACK: // 37
                {
                    if (target->HasAura(spell_trigger))
                    {
                        check = true;
                        continue;
                    }
                    if (itr->bp0 > 0 && !_targetAura->HasAura(itr->bp0))
                    {
                        check = true;
                        continue;
                    }
                    if (itr->bp0 < 0 && _targetAura->HasAura(abs(itr->bp0)))
                    {
                        check = true;
                        continue;
                    }

                    triggered_spell_id = itr->aura;

                    if (Aura* aura = _targetAura->GetAura(triggered_spell_id))
                    {
                        if ((aura->GetStackAmount() + 1) > (aura->GetSpellInfo()->GetAuraOptions(GetSpawnMode())->CumulativeAura - 1))
                        {
                            RemoveAurasDueToSpell(triggered_spell_id);
                            triggered_spell_id = spell_trigger;
                        }
                    }
                    _caster->CastSpell(target, triggered_spell_id, true, castItem);
                    break;
                }
                case SPELL_TRIGGER_DAM_MAXHEALTH: // 38
                {
                    triggered_spell_id = abs(spell_trigger);
                    int32 percHP = bp0 ? bp0 : triggerAmount;
                    basepoints0 = target->CountPctFromMaxHealth(percHP);
                    if(bp1)
                        basepoints0 /= bp1;
                    if(bp2)
                        basepoints0 *= bp2;

                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    check = true;
                    break;
                }
                case SPELL_TRIGGER_STACK_AMOUNT: //39
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    int32 percent = 100;
                    if(bp0)
                        percent = bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;

                    basepoints0 = CalculatePct(dmgInfoProc->GetDamage() + dmgInfoProc->GetAbsorb(), percent);

                    triggeredByAura->SetAmount(triggerAmount + basepoints0);
                    check = true;
                }
                break;
                case SPELL_TRIGGER_BP_DURATION: //40
                {
                    triggered_spell_id = abs(spell_trigger);
                    basepoints0 = triggeredByAura->GetBase()->GetDuration();
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    check = true;
                }
                break;
                case SPELL_TRIGGER_ADD_STACK_AND_CAST: //41
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }

                    triggered_spell_id = abs(spell_trigger);
                    basepoints0 = triggeredByAura->GetBase()->GetStackAmount() + bp1;

                    if(basepoints0 > 0)
                        _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);

                    if(spell_trigger < 0)
                        triggeredByAura->GetBase()->Remove();
                    else
                        triggeredByAura->GetBase()->ModStackAmount(bp1);

                    check = true;
                }
                break;
                case SPELL_TRIGGER_ADD_SPELL_CHARGES: //42
                {
                    triggered_spell_id = abs(spell_trigger);

                    if (Player* _player = _caster->ToPlayer())
                        _player->ModSpellCharge(triggered_spell_id, bp0);

                    check = true;
                }
                break;
                case SPELL_TRIGGER_DAM_PERC_AT_TARGET: //43
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    float percent = 0.0f;
                    if (Player* plr = ToPlayer())
                    {
                        if (dummySpell->HasAttribute(SPELL_ATTR8_MASTERY_SPECIALIZATION))
                            percent = plr->GetFloatValue(PLAYER_FIELD_MASTERY) * dummySpell->GetEffect(effIndex, m_spawnMode)->BonusCoefficient;
                    }

                    if (!percent)
                        percent = triggerAmount;

                    if(bp0)
                        percent += bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;

                    basepoints0 = CalculatePct(int32(dmgInfoProc->GetDamage() + dmgInfoProc->GetAbsorb()), percent);

                    triggered_spell_id = abs(spell_trigger);
                    if (ObjectGuid areaTriggerGuid = triggeredByAura->GetBase()->GetSpellAreaTrigger())
                    {
                        if(AreaTrigger* areaTrigger = ObjectAccessor::GetAreaTrigger(*this, areaTriggerGuid))
                        {
                            areaTrigger->CalculateRadius();
                            GuidList* affectedList = areaTrigger->GetAffectedPlayers();
                            for (GuidList::const_iterator iter = affectedList->begin(); iter != affectedList->end(); ++iter)
                            {
                                if(Unit* _unit = ObjectAccessor::GetUnit(*this, *iter))
                                    _caster->CastCustomSpell(_unit, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                            }
                        }
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_FROM_ENERGY: //44
                {
                    if (!procSpell || procSpell->Power.PowerType == POWER_NULL)
                        continue;

                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    float percent = 0.0f;
                    if (Player* plr = ToPlayer())
                    {
                        if (dummySpell->HasAttribute(SPELL_ATTR8_MASTERY_SPECIALIZATION))
                            percent = plr->GetFloatValue(PLAYER_FIELD_MASTERY) * dummySpell->GetEffect(effIndex, m_spawnMode)->BonusCoefficient;
                    }

                    if (!percent)
                        percent = triggerAmount;

                    if(bp0)
                        percent += bp0;
                    if(bp1)
                        percent /= bp1;
                    if(bp2)
                        percent *= bp2;

                    basepoints0 = CalculatePct(float(procSpell->Power.PowerCost), percent);

                    triggered_spell_id = abs(spell_trigger);
                    _caster->CastCustomSpell(target, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true, castItem, triggeredByAura, originalCaster);
                    if(itr->target == 6)
                    {
                        if (Guardian* pet = GetGuardianPet())
                            _caster->CastCustomSpell(pet, triggered_spell_id, &basepoints0, &basepoints0, &basepoints0, true);
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_CAST_DELAY: //45
                {
                    if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                    {
                        check = true;
                        continue;
                    }
                    if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                    {
                        check = true;
                        continue;
                    }
                    int32 delay = 250;
                    if(bp0)
                        delay = bp0;

                    float percent = 0.0f;
                    if(bp1)
                        percent = triggerAmount;
                    if(bp2)
                        percent = bp2;

                    CustomSpellValues values;
                    if (percent)
                    {
                        basepoints0 = CalculatePct(int32(dmgInfoProc->GetDamage() + dmgInfoProc->GetAbsorb()), percent);
                        values.AddSpellMod(SPELLVALUE_BASE_POINT0, basepoints0);
                        values.AddSpellMod(SPELLVALUE_BASE_POINT1, basepoints0);
                        values.AddSpellMod(SPELLVALUE_BASE_POINT2, basepoints0);
                    }
                    ObjectGuid targetGUID = target->GetGUID();
                    ObjectGuid originalCasterGUID = triggeredByAura->GetCasterGUID();
                    triggered_spell_id = abs(spell_trigger);
                    SpellPowerCost powerCost = m_powerCost;
                    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(triggered_spell_id);
                    if (!spellInfo)
                        break;

                    _caster->AddDelayedEvent(delay, [_caster, targetGUID, spellInfo, originalCasterGUID, powerCost, values]() -> void
                    {
                        if (!_caster)
                            return;

                        Unit* target = ObjectAccessor::GetUnit(*_caster, targetGUID);
                        if (!target)
                            return;

                        SpellCastTargets _targets;
                        _targets.SetCaster(_caster);
                        _targets.SetUnitTarget(target);

                        TriggerCastData triggerData;
                        triggerData.triggerFlags = TRIGGERED_FULL_MASK;
                        triggerData.originalCaster = originalCasterGUID;
                        triggerData.SubType = SPELL_CAST_TYPE_MISSILE;
                        triggerData.powerCost = powerCost;

                        _caster->CastSpell(_targets, spellInfo, &values, triggerData);
                    });
                    check = true;
                }
                break;
                case SPELL_TRIGGER_MOFIFY_CHARGES_COLLDOWN: //46
                {
                    if (Player* plr = target->ToPlayer())
                    {
                        triggered_spell_id = abs(spell_trigger);
                        if (itr->aura && !procSpell)
                            continue;
                        if (itr->aura && procSpell && itr->aura != procSpell->Id)
                            continue;
 
                        int32 chargecd = triggerAmount * (target->GetComboPoints() ? target->GetComboPoints() : 1);
 
                        if (chargecd < 100)
                            chargecd *= IN_MILLISECONDS;
 
                        if (bp0)
                            chargecd += bp0;
                        if (bp1)
                            chargecd /= bp1;
                        if (bp2)
                            chargecd *= bp2;
 
                        plr->ModSpellChargeCooldown(triggered_spell_id, chargecd);
                    }
                    check = true;
                    break;
                }
                case SPELL_TRIGGER_TO_AT_SRC: //47
                {
                    triggered_spell_id = abs(spell_trigger);

                    std::list<AreaTrigger*> list;
                    _caster->GetAreaObjectList(list, triggeredByAura->GetId());
                    if(!list.empty())
                    {
                        for (std::list<AreaTrigger*>::iterator itr2 = list.begin(); itr2 != list.end(); ++itr2)
                        {
                            if(AreaTrigger* areaObj = (*itr2))
                            {
                                _caster->CastSpell(areaObj->GetPositionX(), areaObj->GetPositionY(), areaObj->GetPositionZ(), triggered_spell_id, true, castItem, triggeredByAura, originalCaster);
                                if (bp0)
                                    areaObj->Despawn();
                            }
                        }
                    }
                    check = true;
                }
                break;
                case SPELL_TRIGGER_CAST_DURACTION: //48
                {
                    triggered_spell_id = abs(spell_trigger);

                    basepoints0 = triggerAmount > 100 ? triggerAmount : triggerAmount * 1000;

                    if (bp0)
                        basepoints0 += bp0;
                    if (bp1)
                        basepoints0 /= bp1;
                    if (bp2)
                        basepoints0 *= bp2;

                    _caster->CastSpellDuration(target, triggered_spell_id, true, basepoints0);
                    check = true;
                }
                break;
            }
            if(itr->group != 0 && check)
                groupList.push_back(itr->group);
        }

        if (G3D::fuzzyGt(cooldown, 0.0))
            AddSpellCooldown(cooldown_spell_id, 0, getPreciseTime() + cooldown);
        if(check)
            return true;
    }
    return false;
}

bool Unit::SpellProcCheck(Unit* victim, SpellInfo const* spellProto, SpellInfo const* procSpell, uint8 effect, AuraEffect* triggeredByAura)
{
    bool procCheck = false;
    ObjectGuid casterGUID = GetGUID();
    bool procCheckActiveted = false;
    bool procCheckSecond = false;
    bool procCheckSecondActiveted = false;
    int32 spellProcId = procSpell ? procSpell->Id : -1;
    uint32 procPowerType = procSpell ? procSpell->Power.PowerType : 0;
    uint32 procDmgClass = procSpell ? procSpell->Categories.DefenseType : 0;
    uint32 Attributes = procSpell ? procSpell->GetMisc(m_spawnMode)->MiscData.Attributes[0] : 0;
    uint32 AllEffectsMechanicMask = procSpell ? procSpell->GetAllEffectsMechanicMask() : 0;
    uint32 SpellTypeMask = procSpell ? procSpell->GetSpellTypeMask() : 1;
    int32 duration = procSpell ? procSpell->GetDuration(m_spawnMode) : 0;
    int32 specCheckid = ToPlayer() ? ToPlayer()->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) : 0;
    int32 deathstateMask = victim ? (1 << victim->getDeathState()) : 0;

    TC_LOG_DEBUG(LOG_FILTER_PROC, "SpellProcCheck: spellProto->Id %i, effect %i, spellProcId %i, procPowerType %i, procDmgClass %i, AllEffectsMechanicMask %i, specCheckid %i, SpellTypeMask %i duration %i",
    spellProto->Id, effect, spellProcId, procPowerType, procDmgClass, AllEffectsMechanicMask, specCheckid, SpellTypeMask, duration);

    if (std::vector<SpellPrcoCheck> const* spellCheck = sSpellMgr->GetSpellPrcoCheck(spellProto->Id))
    {
        for (std::vector<SpellPrcoCheck>::const_iterator itr = spellCheck->begin(); itr != spellCheck->end(); ++itr)
        {
            if (!(itr->effectmask & (1<<effect)))
                continue;
            Unit* _checkTarget = this;

            if(itr->target == 1 && victim)
                _checkTarget = victim;
            if(itr->target == 3 && ToPlayer()) //get target owner pet
                if (Pet* pet = ToPlayer()->GetPet())
                    _checkTarget = static_cast<Unit*>(pet);
            if(itr->target == 4 && victim && victim->ToPlayer()) //get target pet
                if (Pet* pet = victim->ToPlayer()->GetPet())
                    _checkTarget = static_cast<Unit*>(pet);
            if(itr->target == 7) //get target self
                _checkTarget = triggeredByAura->GetCaster();

            if (!_checkTarget)
                _checkTarget = this;

            //TC_LOG_DEBUG(LOG_FILTER_PROC, "SpellProcCheck: spellProto->Id %i, spelltypeMask %i, HasMask %i",
            //spellProto->Id, itr->spelltypeMask, (SpellTypeMask & abs(itr->spelltypeMask)));

            //if this spell exist not proc
            if (itr->checkspell < 0)
            {
                procCheckActiveted = true;
                if (-(itr->checkspell) == spellProcId)
                {
                    if(itr->hastalent != 0 || itr->specId != 0 || itr->spellAttr0 != 0 || itr->targetTypeMask != 0 || itr->perchp != 0 || itr->fromlevel != 0 || itr->mechanicMask != 0 || itr->combopoints != 0)
                    {
                        if(itr->hastalent > 0 && _checkTarget->HasAura(itr->hastalent, casterGUID))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->hastalent < 0 && !_checkTarget->HasAura(abs(itr->hastalent), casterGUID))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->specId > 0 && itr->specId != specCheckid)
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->specId < 0 && itr->specId == specCheckid)
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->spellAttr0 > 0 && !(Attributes & itr->spellAttr0))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->spellAttr0 < 0 && (Attributes & abs(itr->spellAttr0)))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->targetTypeMask != 0 && !(itr->targetTypeMask & (1 << _checkTarget->GetTypeId())))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->mechanicMask != 0 && !(AllEffectsMechanicMask & itr->mechanicMask))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->combopoints != 0 && !GetComboPoints())
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->spelltypeMask > 0 && !(SpellTypeMask & itr->spelltypeMask))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->spelltypeMask < 0 && (SpellTypeMask & abs(itr->spelltypeMask)))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->deathstateMask != 0 && !(deathstateMask & itr->deathstateMask))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->fromlevel > 0 && _checkTarget->getLevel() >= itr->fromlevel)
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->fromlevel < 0 && _checkTarget->getLevel() < abs(itr->fromlevel))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->perchp > 0 && _checkTarget->GetHealthPct() >= itr->perchp)
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->perchp < 0 && _checkTarget->GetHealthPct() < abs(itr->perchp))
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->hasDuration > 0 && duration < 0)
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->hasDuration < 0 && duration > 0)
                        {
                            procCheck = true;
                            break;
                        }
                        if(itr->chance != 0 && !roll_chance_i(itr->chance) && procCheck)
                        {
                            procCheck = false;
                            continue;
                        }
                    }
                    else if(itr->chance != 0 && roll_chance_i(itr->chance))
                        procCheck = false;
                    else
                        procCheck = true;
                    break;
                }
            }
            //if this spell not exist not proc
            else if (itr->checkspell == spellProcId)
            {
                procCheckActiveted = true;
                if(itr->hastalent > 0 && !_checkTarget->HasAura(itr->hastalent, casterGUID))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->hastalent < 0 && _checkTarget->HasAura(abs(itr->hastalent), casterGUID))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->chance != 0 && !roll_chance_i(itr->chance))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->specId > 0 && itr->specId != specCheckid)
                {
                    procCheck = true;
                    continue;
                }
                if(itr->specId < 0 && itr->specId == specCheckid)
                {
                    procCheck = true;
                    continue;
                }
                if(itr->spellAttr0 > 0 && !(Attributes & itr->spellAttr0))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->spellAttr0 < 0 && (Attributes & abs(itr->spellAttr0)))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->targetTypeMask != 0 && !(itr->targetTypeMask & (1 << _checkTarget->GetTypeId())))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->mechanicMask != 0 && !(AllEffectsMechanicMask & itr->mechanicMask))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->combopoints != 0 && !GetComboPoints())
                {
                    procCheck = true;
                    continue;
                }
                if(itr->spelltypeMask > 0 && !(SpellTypeMask & itr->spelltypeMask))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->spelltypeMask < 0 && (SpellTypeMask & abs(itr->spelltypeMask)))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->deathstateMask != 0 && !(deathstateMask & itr->deathstateMask))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->fromlevel > 0 && _checkTarget->getLevel() < itr->fromlevel)
                {
                    procCheck = true;
                    continue;
                }
                if(itr->fromlevel < 0 && _checkTarget->getLevel() > abs(itr->fromlevel))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->perchp > 0 && _checkTarget->GetHealthPct() < itr->perchp)
                {
                    procCheck = true;
                    continue;
                }
                if(itr->perchp < 0 && _checkTarget->GetHealthPct() > abs(itr->perchp))
                {
                    procCheck = true;
                    continue;
                }
                if(itr->hasDuration > 0 && duration < 0)
                {
                    procCheck = true;
                    continue;
                }
                if(itr->hasDuration < 0 && duration > 0)
                {
                    procCheck = true;
                    continue;
                }
                procCheck = false;
                break;
            }
            //other check
            else if (itr->checkspell == 0)
            {
                procCheckSecondActiveted = true;
                if(itr->hastalent != 0)
                {
                    if(itr->hastalent > 0 && !_checkTarget->HasAura(itr->hastalent, casterGUID))
                    {
                        procCheckSecond = true;
                        continue;
                    }
                    if(itr->hastalent < 0 && _checkTarget->HasAura(abs(itr->hastalent), casterGUID))
                    {
                        procCheckSecond = true;
                        continue;
                    }
                }

                if(itr->specId > 0 && itr->specId != specCheckid)
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->specId < 0 && itr->specId == specCheckid)
                {
                    procCheckSecond = true;
                    continue;
                }
                if(procSpell && itr->spellAttr0 > 0 && !(procSpell->GetMisc(m_spawnMode)->MiscData.Attributes[0] & itr->spellAttr0))
                {
                    procCheckSecond = true;
                    continue;
                }
                if(procSpell && itr->spellAttr0 < 0 && (procSpell->GetMisc(m_spawnMode)->MiscData.Attributes[0] & abs(itr->spellAttr0)))
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->targetTypeMask != 0 && !(itr->targetTypeMask & (1 << _checkTarget->GetTypeId())))
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->mechanicMask != 0 && !(AllEffectsMechanicMask & itr->mechanicMask))
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->combopoints != 0 && !GetComboPoints())
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->spelltypeMask > 0 && !(SpellTypeMask & itr->spelltypeMask))
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->spelltypeMask < 0 && (SpellTypeMask & abs(itr->spelltypeMask)))
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->deathstateMask != 0 && !(deathstateMask & itr->deathstateMask))
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->fromlevel > 0 && _checkTarget->getLevel() < itr->fromlevel)
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->fromlevel < 0 && _checkTarget->getLevel() >= abs(itr->fromlevel))
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->perchp > 0 && _checkTarget->GetHealthPct() < itr->perchp)
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->perchp < 0 && _checkTarget->GetHealthPct() >= abs(itr->perchp))
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->hasDuration > 0 && duration < 0)
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->hasDuration < 0 && duration > 0)
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->powertype != -1 && itr->dmgclass != -1)
                {
                    if(itr->powertype != procPowerType || itr->dmgclass != procDmgClass)
                    {
                        procCheckSecond = true;
                        continue;
                    }
                }
                else if(itr->dmgclass != -1 && itr->dmgclass != procDmgClass)
                {
                    procCheckSecond = true;
                    continue;
                }
                else if(itr->powertype != -1 && itr->powertype != procPowerType)
                {
                    procCheckSecond = true;
                    continue;
                }
                if(itr->chance != 0 && !roll_chance_i(itr->chance))
                {
                    procCheckSecond = true;
                    break;
                }
                procCheckSecond = false;
                break;
            }
            else
                procCheck = true;
        }
    }

    //TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SpellProcCheck: spellProto->Id %i, effect %i, spellProcId %i procCheck %i procCheckSecondActiveted %i procCheckSecond %i procCheckActiveted %i",
    //spellProto->Id, effect, spellProcId, procCheck, procCheckSecondActiveted, procCheckSecond, procCheckActiveted);

    //if check true false proc
    if(procCheck && !procCheckSecondActiveted)
    {
        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SpellProcCheck: spellProto->Id %i, effect %i, spellProcId %i procCheck", spellProto->Id, effect, spellProcId);
        return false;
    }
    if(procCheckSecond && !procCheckActiveted)
    {
        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SpellProcCheck: spellProto->Id %i, effect %i, spellProcId %i procCheckSecond", spellProto->Id, effect, spellProcId);
        return false;
    }
    if(procCheck && procCheckSecond)
    {
        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SpellProcCheck: spellProto->Id %i, effect %i, spellProcId %i procCheckSecond && procCheck", spellProto->Id, effect, spellProcId, procCheckSecond);
        return false;
    }

    return true;
}

void Unit::CalculateFromDummy(Unit* victim, float &amount, SpellInfo const* spellProto, uint32 mask, SpellAuraDummyType type) const
{
    TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Unit::CalculateFromDummy start GetId %i, amount %f, mask %i type %u", spellProto->Id, amount, mask, type);

    if (std::vector<SpellAuraDummy> const* spellAuraDummy = sSpellMgr->GetSpellAuraDummy(spellProto->Id))
    {
        for (std::vector<SpellAuraDummy>::const_iterator itr = spellAuraDummy->begin(); itr != spellAuraDummy->end(); ++itr)
        {
            TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Unit::CalculateFromDummy spellDummyId %i, effectmask %i, option %i, aura %i, type %i", itr->spellDummyId, itr->effectmask, itr->option, itr->aura, itr->type);

            if(itr->type != type) // default 0, damage 1, crit 2, time 3, proc 4, duration 5
                continue;

            if (!(itr->effectmask & mask))
                continue;

            Unit* _caster = const_cast<Unit*>(this);
            Unit* _targetAura = const_cast<Unit*>(this);
            Unit* _target = victim;
            bool check = false;

            if(itr->caster == 1 && victim) //get caster as target
                _caster = victim;

            if(itr->targetaura == 2 && victim) //get target aura
                _targetAura = victim;

            if(itr->caster == 3) //get caster owner
                if (Unit* owner = GetAnyOwner())
                    _caster = owner;

            if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                continue;
            if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                continue;

            if (itr->hastalent)
                if (_caster->HasAuraLinkedSpell(_caster, _target, itr->hastype, itr->hastalent, itr->hasparam))
                    continue;

            if (itr->hastalent2)
                if (_caster->HasAuraLinkedSpell(_caster, _target, itr->hastype2, itr->hastalent2, itr->hasparam2))
                    continue;

            Aura* _aura = _caster->GetAura(abs(itr->spellDummyId), _targetAura->GetGUID());

            switch (itr->option)
            {
                case SPELL_DUMMY_CRIT_RESET: //5
                {
                    if(itr->spellDummyId > 0 && !_aura)
                    {
                        amount = 0.0f;
                        check = true;
                    }
                    if(itr->spellDummyId < 0 && _aura)
                    {
                        amount = 0.0f;
                        check = true;
                    }
                    break;
                }
                case SPELL_DUMMY_CRIT_ADD_PERC: //6
                {
                    if(itr->spellDummyId > 0 && _aura)
                    {
                        if(AuraEffect const* dummyEff = _aura->GetEffect(itr->effectDummy))
                        {
                            float bp = itr->custombp;
                            if(!bp)
                                bp = dummyEff->GetAmount();
                            amount += CalculatePct(amount, bp);
                            check = true;
                        }
                    }
                    if(itr->spellDummyId < 0 && _aura)
                    {
                        if(AuraEffect const* dummyEff = _aura->GetEffect(itr->effectDummy))
                        {
                            float bp = itr->custombp;
                            if(!bp)
                                bp = dummyEff->GetAmount();
                            amount -= CalculatePct(amount, bp);
                            check = true;
                        }
                    }
                    break;
                }
                case SPELL_DUMMY_CRIT_ADD_VALUE: //7
                {
                    if(itr->spellDummyId > 0 && _aura)
                    {
                        if(AuraEffect const* dummyEff = _aura->GetEffect(itr->effectDummy))
                        {
                            float bp = itr->custombp;
                            if(!bp)
                                bp = dummyEff->GetAmount();
                            amount += bp;
                            check = true;
                        }
                    }
                    if(itr->spellDummyId < 0 && _aura)
                    {
                        if(AuraEffect const* dummyEff = _aura->GetEffect(itr->effectDummy))
                        {
                            float bp = itr->custombp;
                            if(!bp)
                                bp = dummyEff->GetAmount();
                            amount -= bp;
                            check = true;
                        }
                    }
                    break;
                }
                case SPELL_DUMMY_DAMAGE_ADD_PERC: //9
                {
                    if(itr->spellDummyId > 0 && _aura)
                    {
                        if(AuraEffect const* dummyEff = _aura->GetEffect(itr->effectDummy))
                        {
                            float bp = itr->custombp;
                            if(!bp)
                                bp = dummyEff->GetAmount();
                            amount += CalculatePct(amount, bp);
                            check = true;
                        }
                    }
                    if(itr->spellDummyId < 0 && _aura)
                    {
                        if(AuraEffect const* dummyEff = _aura->GetEffect(itr->effectDummy))
                        {
                            float bp = itr->custombp;
                            if(!bp)
                                bp = dummyEff->GetAmount();
                            amount -= CalculatePct(amount, bp);
                            check = true;
                        }
                    }
                    break;
                }
                case SPELL_DUMMY_DAMAGE_ADD_VALUE: //10
                {
                    if(itr->spellDummyId > 0 && _aura)
                    {
                        if(AuraEffect const* dummyEff = _aura->GetEffect(itr->effectDummy))
                        {
                            float bp = itr->custombp;
                            if(!bp)
                                bp = dummyEff->GetAmount();
                            amount += bp;
                            check = true;
                        }
                    }
                    if(itr->spellDummyId < 0 && _aura)
                    {
                        if(AuraEffect const* dummyEff = _aura->GetEffect(itr->effectDummy))
                        {
                            float bp = itr->custombp;
                            if(!bp)
                                bp = dummyEff->GetAmount();
                            amount -= bp;
                            check = true;
                        }
                    }
                    break;
                }
            }
            if(check && itr->removeAura)
                _caster->RemoveAurasDueToSpell(itr->removeAura);
        }
    }
    TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Unit::CalculateFromDummy end GetId %i, amount %f", spellProto->Id, amount);
}

void Unit::CalculateCastTimeFromDummy(int32& castTime, SpellInfo const* spellProto)
{
    TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "CalculateCastTimeFromDummy start GetId %i, castTime %i", spellProto->Id, castTime);

    if (std::vector<SpellAuraDummy> const* spellAuraDummy = sSpellMgr->GetSpellAuraDummy(spellProto->Id))
    {
        for (std::vector<SpellAuraDummy>::const_iterator itr = spellAuraDummy->begin(); itr != spellAuraDummy->end(); ++itr)
        {
            if(itr->type != SPELL_DUMMY_TIME) // type = 3
                continue;

            Unit* _caster = this;
            Unit* _targetAura = this;
            bool check = false;

            if(itr->caster == 2 && _caster->ToPlayer()) //get target pet
            {
                if (Pet* pet = _caster->ToPlayer()->GetPet())
                    _caster = static_cast<Unit*>(pet);
            }
            if(itr->caster == 3) //get target owner
            {
                if (Unit* owner = _caster->GetOwner())
                    _caster = owner;
            }

            if(itr->targetaura == 1 && _caster->ToPlayer()) //get target pet
            {
                if (Pet* pet = _caster->ToPlayer()->GetPet())
                    _targetAura = static_cast<Unit*>(pet);
            }
            if(itr->targetaura == 2) //get target owner
            {
                if (Unit* owner = _caster->GetOwner())
                    _targetAura = owner;
            }

            if(itr->aura > 0 && !_targetAura->HasAura(itr->aura))
                continue;
            if(itr->aura < 0 && _targetAura->HasAura(abs(itr->aura)))
                continue;

            if (itr->hastalent)
                if (_caster->HasAuraLinkedSpell(_caster, _caster, itr->hastype, itr->hastalent, itr->hasparam))
                    continue;

            if (itr->hastalent2)
                if (_caster->HasAuraLinkedSpell(_caster, _caster, itr->hastype2, itr->hastalent2, itr->hasparam2))
                    continue;

            Aura* _aura = _caster->GetAura(abs(itr->spellDummyId), _targetAura->GetGUID());

            switch (itr->option)
            {
                case SPELL_DUMMY_CASTTIME_ADD_PERC: //13
                {
                    if(itr->spellDummyId > 0 && _aura)
                    {
                        if(SpellInfo const* dummyInfo = sSpellMgr->GetSpellInfo(itr->spellDummyId))
                        {
                            int32 bp = itr->custombp;
                            if(!bp)
                                bp = dummyInfo->Effects[itr->effectDummy]->BasePoints;

                            castTime += CalculatePct(castTime, bp);
                            check = true;
                        }
                    }
                    if(itr->spellDummyId < 0 && _aura)
                    {
                        if(SpellInfo const* dummyInfo = sSpellMgr->GetSpellInfo(abs(itr->spellDummyId)))
                        {
                            int32 bp = itr->custombp;
                            if(!bp)
                                bp = dummyInfo->Effects[itr->effectDummy]->BasePoints;
                            castTime -= CalculatePct(castTime, bp);
                            check = true;
                        }
                    }
                    break;
                }
                case SPELL_DUMMY_CASTTIME_ADD_VALUE: //14
                {
                    if(itr->spellDummyId > 0 && _aura)
                    {
                        if(SpellInfo const* dummyInfo = sSpellMgr->GetSpellInfo(itr->spellDummyId))
                        {
                            int32 bp = itr->custombp;
                            if(!bp)
                                bp = dummyInfo->Effects[itr->effectDummy]->BasePoints;
                            castTime += bp;
                            check = true;
                        }
                    }
                    if(itr->spellDummyId < 0 && _aura)
                    {
                        if(SpellInfo const* dummyInfo = sSpellMgr->GetSpellInfo(abs(itr->spellDummyId)))
                        {
                            int32 bp = itr->custombp;
                            if(!bp)
                                bp = dummyInfo->Effects[itr->effectDummy]->BasePoints;
                            castTime -= bp;
                            check = true;
                        }
                    }
                    break;
                }
            }
            if(check && itr->removeAura)
                _caster->RemoveAurasDueToSpell(itr->removeAura);
        }
    }
    TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "CalculateCastTimeFromDummy end GetId %i, castTime %i", spellProto->Id, castTime);
}

bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, SpellInfo const* spellProto, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent, uint8 effect)
{
    if(!spellProto)
        return false;

    // let the aura be handled by new proc system if it has new entry
    //if (sSpellMgr->GetSpellProcEntry(spellProto->Id))
        //return false;

    bool findSpellProcEvent = false;
    bool hasSpellProcEventInDB = false;
    // Get proc Event Entry
    //spellProcEvent = sSpellMgr->GetSpellProcEvent(spellProto->Id);
    if (std::vector<SpellProcEventEntry> const* spellproc = sSpellMgr->GetSpellProcEvent(spellProto->Id))
    {
        for (std::vector<SpellProcEventEntry>::const_iterator itr = spellproc->begin(); itr != spellproc->end(); ++itr)
        {
            hasSpellProcEventInDB = true;

            if (itr->effectMask & (1 << effect))
            {
                findSpellProcEvent = true;
                spellProcEvent = &(*itr);
                break;
            }
        }
    }

    if (hasSpellProcEventInDB && !findSpellProcEvent)
        return false;

    // Get EventProcFlag
    uint32 EventProcFlag;
    if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags
        EventProcFlag = spellProcEvent->procFlags;
    else
        EventProcFlag = spellProto->GetAuraOptions(GetSpawnMode())->ProcTypeMask;       // else get from spell proto

    // Continue if no trigger exist
    if (!EventProcFlag)
        return false;

    // Additional checks for triggered spells (ignore trap casts)
    if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION))
    {
        if (!(spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)))
            return false;
        if(spellProto->GetAuraOptions(GetSpawnMode())->ProcTypeMask & (PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG|PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS))// for triggered spell set to active
            active = true;
    }

    //Spell can`t proc from HOT
    if (procFlag & PROC_FLAG_TAKEN_PERIODIC && EventProcFlag & PROC_FLAG_TAKEN_PERIODIC)
    {
        switch(spellProto->Id)
        {
            case 41635: // Prayer of Mending
                if (procExtra & PROC_EX_INTERNAL_HOT)
                    return false;
                break;
        }
    }

    // Aura added by spell can`t trigger from self (prevent drop charges/do triggers)
    // But except periodic and kill triggers (can triggered from self)
    if (procSpell && procSpell->Id == spellProto->Id && !(spellProto->GetAuraOptions(GetSpawnMode())->ProcTypeMask & (PROC_FLAG_KILL)))
        return false;

    if (spellProto->HasAttribute(SPELL_ATTR4_UNK19))
        if (procFlag & PROC_FLAG_TAKEN_PERIODIC && EventProcFlag & PROC_FLAG_TAKEN_PERIODIC)
            if (procExtra & (PROC_EX_INTERNAL_HOT | PROC_EX_ABSORB))
                return false;

    // Check spellProcEvent data requirements
    if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active))
    {
        // TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "IsTriggeredAtSpellProcEvent: false procSpell %i, EventProcFlag %i, active %i, procExtra %i, isVictim %i procFlag %u Id %u", procSpell ? procSpell->Id : 0, EventProcFlag, active, procExtra, isVictim, procFlag, spellProto->Id);
        return false;
    }

    // In most cases req get honor or XP from kill
    if (EventProcFlag & PROC_FLAG_KILL && IsPlayer())
    {
        bool allow = false;

        if (victim)
            allow = ToPlayer()->isHonorOrXPTarget(victim);

        // Shadow Word: Death - can trigger from every kill
        //if (aura->GetId() == 32409)
            //allow = true;
        if (!allow)
            return false;
    }

    // Check if current equipment allows aura to proc
    if (!isVictim && IsPlayer())
    {
        Player* player = ToPlayer();
        if (spellProto->EquippedItemClass == ITEM_CLASS_WEAPON)
        {
            Item* item = nullptr;
            if (attType == BASE_ATTACK || attType == RANGED_ATTACK)
                item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
            else if (attType == OFF_ATTACK)
                item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);

            if (player->IsInFeralForm())
                return false;

            if (!item || item->CantBeUse() || item->GetTemplate()->GetClass() != ITEM_CLASS_WEAPON)
                return false;

            if (spellProto->EquippedItemSubClassMask && !((1<<item->GetTemplate()->GetSubClass()) & spellProto->EquippedItemSubClassMask))
                return false;
        }
        else if (spellProto->EquippedItemClass == ITEM_CLASS_ARMOR)
        {
            // Check if player is wearing shield
            Item* item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
            if (!item || item->CantBeUse() || item->GetTemplate()->GetClass() != ITEM_CLASS_ARMOR)
                return false;

            if (spellProto->EquippedItemSubClassMask && !((1<<item->GetTemplate()->GetSubClass()) & spellProto->EquippedItemSubClassMask))
                return false;
        }
    }

    return true;
}

bool Unit::RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent, uint32 procFlag, uint32 procExtra, DamageInfo* dmgInfoProc, SpellInfo const* procSpell, ObjectGuid castItemGUID)
{
    SpellInfo const* spellInfo = aura->GetSpellInfo();
    float chance = float(spellInfo->GetAuraOptions(GetSpawnMode())->ProcChance);

    if (spellProcEvent && spellProcEvent->customChance)
        chance = spellProcEvent->customChance;

    if (spellProcEvent && spellProcEvent->ppmRate != 0)
    {
        if (!isVictim)
            chance = GetPPMProcChance(GetAttackTime(attType), spellProcEvent->ppmRate, spellInfo);
        else if (victim)
            chance = victim->GetPPMProcChance(victim->GetAttackTime(attType), spellProcEvent->ppmRate, spellInfo);
    }

    if (spellInfo->GetAuraOptions(m_spawnMode)->ProcBasePPM > 0.f)
    {
        if (Player* plr = ToPlayer())
        {
            float spellRPPM = spellInfo->CalcProcPPM(this, aura->GetCasterLevel());
            double cooldown = plr->GetRPPMSpellCooldownDelay(spellInfo->Id, castItemGUID); //base cap
            bool procked = plr->GetRPPMProcChance(cooldown, spellRPPM, spellInfo, castItemGUID);

            if (procked)
            {
                plr->SetLastSuccessfulProc(spellInfo->Id, getPreciseTime(), castItemGUID);
                plr->AddRPPMSpellCooldown(spellInfo->Id, castItemGUID, getPreciseTime() + cooldown);
            }

            return procked;
        }
    }

    // Fucking custom blizz spell
    switch (spellInfo->Id)
    {
        case 203179: // Opportunity Strikes
        {
            chance = CalculatePct(spellInfo->GetAuraOptions(GetSpawnMode())->ProcChance, 100 - victim->GetHealthPct());
            break;
        }
        case 137384: // Combo Breaker
            chance = 0.f;
            if (AuraEffect const* aurEff = aura->GetEffect(EFFECT_0))
                chance = aurEff->GetAmount();
            if (AuraEffect const* aurEff = GetAuraEffect(195267, EFFECT_0)) // Strength of Xuen
                chance += aurEff->GetAmount();
            break;
        case 190447: // Brain Freeze
            chance = 0.f;
            if (AuraEffect const* aurEff = aura->GetEffect(EFFECT_0))
                chance = aurEff->GetAmount();
            break;
        case 208722: // Echoes of the Great Sundering
            chance = 0.f;
            if (AuraEffect const* aurEff = aura->GetEffect(EFFECT_0))
                chance = CalculatePct(aurEff->GetAmount(), GetPowerCost(POWER_MAELSTROM));
            break;
        case 191336: // Hunting Companion
            chance = 0.f;
            if(Unit* owner = GetAnyOwner())
            {
                if (AuraEffect const* aurEff = owner->GetAuraEffect(191334, EFFECT_0)) // Mastery: Hunting Companion
                    chance = float(aurEff->GetAmount());
                if (AuraEffect const* aurEff = owner->GetAuraEffect(186289, EFFECT_1)) // Aspect of the Eagle
                    chance += aurEff->GetAmount();
                if (procSpell && procSpell->Id == 206933 && owner->HasAura(237327))
                {
                    if (owner->HasAura(211340)) // Item - Hunter T19 Survival 2P Bonus
                        chance *= 3.f;
                    else
                        chance *= 2.f;
                }
            }
            break;
        case 16974: // Hack
            chance = spellInfo->Effects[EFFECT_2]->CalcValue(this) * GetComboPoints();
            break;
        case 209420: // Greenskin's Waterlogged Wristcuffs (Hack fix)
            chance = spellInfo->Effects[EFFECT_0]->CalcValue(this) * GetComboPoints();
            break;
        case 192657: // Bag of Tricks
            chance = 0.f;
            if (AuraEffect const* aurEff = aura->GetEffect(EFFECT_0))
            {
                chance = aurEff->GetAmount() / 10.f * GetComboPoints();
            }
            break;
        case 191494: // Scourge the Unbeliever
            if (AuraEffect const* aurEff = aura->GetEffect(EFFECT_0))
                chance = aurEff->GetAmount();
            break;
        case 79684: // For tallent Words of Power
        {
            if (AuraEffect const* aurEff = aura->GetEffect(EFFECT_0))
                chance = aurEff->GetAmount();

            if (AuraEffect const* aurEff1 = GetAuraEffect(205035, EFFECT_0))
                if (AuraEffect const* aurEff2 = GetAuraEffect(205035, EFFECT_1))
                    chance += (GetPowerPct(POWER_MANA) / aurEff2->GetAmount()) * aurEff1->GetAmount();

            if (procSpell && procSpell->Id == 30451)
                chance *= 2.f;
            break;
        }
        case 59057: // Rime
        {
            chance = 0.f;
            if (AuraEffect const* aurEff = aura->GetEffect(EFFECT_1))
                chance = aurEff->GetAmount();

            if (procSpell && procSpell->Id == 207230)
                chance /= 2.f;

            if (AuraEffect const* aurEff2 = GetAuraEffect(211042, EFFECT_0)) // Item - Death Knight T19 Frost 2P Bonus. Bravo blizz for custom rework
                if (procSpell && procSpell->Id == 49020)
                    chance += aurEff2->GetAmount();
            break;
        }
    }

    if (Player* modOwner = GetSpellModOwner())
        modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance);

    return roll_chance_f(chance);
}

bool Unit::RollProcEffectResult(AuraEffect* aurEff, SpellProcEventEntry const* spellProcEvent, uint32 procFlag, uint32 procExtra, uint8 effect)
{
    SpellInfo const* spellInfo = aurEff->GetSpellInfo();
    float chance = float(spellInfo->GetAuraOptions(GetSpawnMode())->ProcChance);

    if(spellInfo->Effects[effect]->PointsPerResource != 0.0f)
        chance = spellInfo->Effects[effect]->PointsPerResource * GetComboPoints();
    else
        return true;

    return roll_chance_f(chance);
}

bool Unit::HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura)
{
    // aura can be deleted at casts
    SpellInfo const* spellProto = triggeredByAura->GetSpellInfo();
    float heal = triggeredByAura->GetAmount();
    ObjectGuid caster_guid = triggeredByAura->GetCasterGUID();

    // Currently only Prayer of Mending
    if (!(spellProto->ClassOptions.SpellClassSet == SPELLFAMILY_PRIEST && spellProto->ClassOptions.SpellClassMask[1] & 0x20))
    {
        TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "Unit::HandleAuraRaidProcFromChargeWithValue, received not handled spell: %u", spellProto->Id);
        return false;
    }

    // jumps
    int32 jumps = triggeredByAura->GetBase()->GetCharges()-1;

    // current aura expire
    triggeredByAura->GetBase()->SetCharges(1);             // will removed at next charges decrease

    // next target selection
    if (jumps > 0)
    {
        if (Unit* caster = triggeredByAura->GetCaster())
        {
            float radius = triggeredByAura->GetSpellInfo()->GetEffect(triggeredByAura->GetEffIndex(), GetSpawnMode())->CalcRadius(caster);

            if (Unit* target = GetNextRandomRaidMemberOrPet(radius))
            {
                CastCustomSpell(target, spellProto->Id, &heal, nullptr, nullptr, true, nullptr, triggeredByAura, caster_guid);
                Aura* aura = target->GetAura(spellProto->Id, caster->GetGUID());
                if (aura != nullptr)
                    aura->SetCharges(jumps);
            }
        }
    }

    // heal
    CastCustomSpell(this, 33110, &heal, nullptr, nullptr, true, nullptr, nullptr, caster_guid);
    return true;

}

void Unit::SendDurabilityLoss(Player* receiver, uint32 percent)
{
    WorldPackets::Misc::DurabilityDamageDeath durabilityDamage;
    durabilityDamage.Percent = percent;
    receiver->SendDirectMessage(durabilityDamage.Write());
}

void Unit::SetAnimTier(uint32 tier)
{
    WorldPackets::Update::SetAnimTimer packet;
    packet.Tier = tier;
    packet.Unit = GetGUID();
    SendMessageToSet(packet.Write(), true);
}

void Unit::PlayOneShotAnimKit(uint16 animKitID)
{
    if (animKitID && !sAnimKitStore.LookupEntry(animKitID))
        return;

    WorldPackets::Misc::PlayOneShotAnimKit data;
    data.Unit = GetGUID();
    data.AnimKitID = animKitID;
    SendMessageToSet(data.Write(), true);
}

void Unit::SetAnimKitId(uint16 animKitID)
{
    if (_aiAnimKitId == animKitID)
        return;

    if (animKitID && !sAnimKitStore.LookupEntry(animKitID))
        return;

    _aiAnimKitId = animKitID;

    WorldPackets::Misc::SetAIAnimKit data;
    data.Unit = GetGUID();
    data.AnimKitID = animKitID;
    SendMessageToSet(data.Write(), true);
}

void Unit::SetMovementAnimKitId(uint16 animKitID)
{
    if (_movementAnimKitId == animKitID)
        return;

    if (animKitID && !sAnimKitStore.LookupEntry(animKitID))
        return;

    _movementAnimKitId = animKitID;

    WorldPackets::Misc::SetMovementAnimKit data;
    data.Unit = GetGUID();
    data.AnimKitID = animKitID;
    SendMessageToSet(data.Write(), true);
}

void Unit::SetMeleeAnimKitId(uint16 animKitID)
{
    if (_meleeAnimKitId == animKitID)
        return;

    if (animKitID && !sAnimKitStore.LookupEntry(animKitID))
        return;

    _meleeAnimKitId = animKitID;

    WorldPackets::Misc::SetMeleeAnimKit data;
    data.Unit = GetGUID();
    data.AnimKitID = animKitID;
    SendMessageToSet(data.Write(), true);
}

void Unit::Kill(Unit* victim, bool durabilityLoss, SpellInfo const* spellProto)
{
    // Prevent killing unit twice (and giving reward from kill twice)
    if (!victim || victim->isDead(false) || victim->m_IsInKillingProcess)
        return;

    volatile uint32 entryorguid = victim->IsPlayer() ? victim->GetGUIDLow() : victim->GetEntry();
    victim->m_IsInKillingProcess = true;

    // find player: owner of controlled `this` or `this` itself maybe
    Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
    Creature* creature = victim->ToCreature();

    bool isRewardAllowed = true;
    if (creature)
    {
        isRewardAllowed = creature->IsDamageEnoughForLootingAndReward();
        if (!isRewardAllowed)
            creature->SetLootRecipient(nullptr);
    }

    if (isRewardAllowed && creature && creature->GetLootRecipient())
        player = creature->GetLootRecipient();

    // Reward player, his pets, and group/raid members
    // call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
    if (isRewardAllowed && player && player != victim)
    {
        WorldPackets::Party::PartyKillLog partyKillLog;
        partyKillLog.Player = player->GetGUID();
        partyKillLog.Victim = victim->GetGUID();

        Player* looter = player;

        if (Group* group = player->GetGroup())
        {
            group->BroadcastPacket(partyKillLog.Write(), group->GetMemberGroup(player->GetGUID()) != 0);

            if (creature)
            {
                group->UpdateLooterGuid(creature, true);
                if (group->GetLooterGuid())
                {
                    looter = ObjectAccessor::FindPlayer(group->GetLooterGuid());
                    if (looter)
                    {
                        creature->SetLootRecipient(looter);   // update creature loot recipient to the allowed looter.
                        group->SendLooter(creature, looter);
                    }
                    else
                        group->SendLooter(creature, nullptr);
                }
                else
                    group->SendLooter(creature, nullptr);

                group->UpdateLooterGuid(creature);
            }
        }
        else
        {
            player->SendDirectMessage(partyKillLog.Write());

            if (creature)
            {
                WorldPackets::Loot::LootList lootList;
                lootList.Owner = creature->GetGUID();
                lootList.LootObj = creature->loot.GetGUID();
                player->SendMessageToSet(lootList.Write(), true);
            }
        }

        if (creature)
        {
            if(creature->isWorldBoss())
            {
                if (uint16 encounterId = sObjectMgr->GetDungeonEncounterByCreature(creature->GetEntry()))
                {
                    if (creature->InInstance())
                        creature->GetMap()->SendToPlayers(WorldPackets::Instance::BossKillCredit(encounterId).Write());
                    if (!creature->GetSaveThreatList()->empty()) // If empty list is cheater???
                    {
                        for (auto const& _guid : *creature->GetSaveThreatList())
                        {
                            if (Player* playerInfo = ObjectAccessor::GetPlayer(*this, _guid))
                            {
                                playerInfo->UpdateAchievementCriteria(CRITERIA_TYPE_COMPLETE_DUNGEON_ENCOUNTER, encounterId, 0, 0, victim, this);
                                playerInfo->UpdateAchievementCriteria(CRITERIA_TYPE_DUNGEON_ENCOUNTER_COUNTER, encounterId, 0, 0, victim, this);
                            }
                        }
                    }
                    else if (Group* group = player->GetGroup())
                    {
                        for (GroupReference* iter = group->GetFirstMember(); iter != nullptr; iter = iter->next())
                        {
                            Player* playerInfo = iter->getSource();
                            if (!playerInfo || !playerInfo->CanContact())
                                continue;

                            if (!playerInfo->IsAtGroupRewardDistance(creature))
                                continue;

                            playerInfo->UpdateAchievementCriteria(CRITERIA_TYPE_COMPLETE_DUNGEON_ENCOUNTER, encounterId, 0, 0, victim, this);
                            playerInfo->UpdateAchievementCriteria(CRITERIA_TYPE_DUNGEON_ENCOUNTER_COUNTER, encounterId, 0, 0, victim, this);
                        }
                    }
                    else
                    {
                        player->UpdateAchievementCriteria(CRITERIA_TYPE_COMPLETE_DUNGEON_ENCOUNTER, encounterId, 0, 0, victim, this);
                        player->UpdateAchievementCriteria(CRITERIA_TYPE_DUNGEON_ENCOUNTER_COUNTER, encounterId, 0, 0, victim, this);
                    }
                }
            }

            if (!looter)
                looter = player;

            bool isNonChallenge = GetMap() && !GetMap()->isChallenge();
            bool isLootDisable = creature->IsAIEnabled && creature->GetAI()->IsDisableGenerateLoot();

            if (isNonChallenge && !isLootDisable) // Don`t loot
            {
                if (creature->IsPersonal() || GetMap() && (GetMap()->GetLootDifficulty() == DIFFICULTY_TIMEWALKING || GetMap()->GetLootDifficulty() == DIFFICULTY_TIMEWALKING_RAID))
                    GeneratePersonalLoot(creature, player);
                else
                    GenerateLoot(creature, looter);
            }

            if (creature->CanShared() && isNonChallenge)
            {
                for (auto const& _guid : *creature->GetSaveThreatList())
                {
                    if (Player* playerForReward = ObjectAccessor::GetPlayer(*creature, _guid))
                    {
                        if (player != playerForReward && (!player->GetGroup() || player->GetGroup() != playerForReward->GetGroup()) && creature->isTappedBy(playerForReward))
                            playerForReward->RewardPlayerAndGroupAtKill(victim, false);
                    }
                }
            }

            // Crashed server
            if (victim->GetCreatureType() != CREATURE_TYPE_CRITTER)
            {
                std::vector<ObjectGuid> tempList;
                for (auto const& _guid : *creature->GetSaveThreatList())
                {
                    if (GetGUID() == _guid)
                        continue;

                    tempList.push_back(_guid);
                }

                for (auto itr : tempList)
                {
                    if (Player* playerForReward = ObjectAccessor::GetPlayer(*this, itr))
                    {
                        DamageInfo dmgInfoProc = DamageInfo(playerForReward, victim, 0, nullptr, SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE, 0);
                        playerForReward->ProcDamageAndSpellFor(false, victim, PROC_FLAG_KILL, PROC_EX_NONE, BASE_ATTACK, nullptr, &dmgInfoProc, nullptr);
                    }
                }
            }
            player->RewardPlayerAndGroupAtKill(victim, false);
        }
        else
            player->RewardPlayerAndGroupAtKill(victim, false);
    }

    // Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim
    if (isPet() || isTotem())
        if (Unit* owner = GetOwner())
        {
            DamageInfo dmgInfoProc = DamageInfo(owner, victim, 0, spellProto ? spellProto : nullptr, spellProto ? SpellSchoolMask(spellProto->GetMisc(m_spawnMode)->MiscData.SchoolMask) : SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE, 0);
            owner->ProcDamageAndSpell(victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_EX_NONE, &dmgInfoProc);
        }

    if (victim->GetCreatureType() != CREATURE_TYPE_CRITTER)
    {
        DamageInfo dmgInfoProc = DamageInfo(this, victim, 0, spellProto ? spellProto : nullptr, spellProto ? SpellSchoolMask(spellProto->GetMisc(m_spawnMode)->MiscData.SchoolMask) : SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE, 0);
        ProcDamageAndSpell(victim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, &dmgInfoProc, BASE_ATTACK, spellProto ? spellProto : nullptr, nullptr);
    }

    // Proc auras on death - must be before aura/combat remove
    DamageInfo dmgInfoProc = DamageInfo(this, victim, 0, spellProto ? spellProto : nullptr, spellProto ? SpellSchoolMask(spellProto->GetMisc(m_spawnMode)->MiscData.SchoolMask) : SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE, 0);
    victim->ProcDamageAndSpell(nullptr, PROC_FLAG_DEATH, PROC_FLAG_NONE, PROC_EX_NONE, &dmgInfoProc, BASE_ATTACK, spellProto ? spellProto : nullptr);

    // update get killing blow achievements, must be done before setDeathState to be able to require auras on target
    // and before Spirit of Redemption as it also removes auras
    if (player)
        player->UpdateAchievementCriteria(CRITERIA_TYPE_GET_KILLING_BLOWS, 1, 0, 0, victim);

    // if talent known but not triggered (check priest class for speedup check)
    bool spiritOfRedemption = false;
    if (victim->IsPlayer() && victim->getClass() == CLASS_PRIEST)
    {
        if(AuraEffect const* aurEff = victim->GetAuraEffect(20711, 0))
        {
            //@TODO 735
            // Remove all expected to remove at death auras (most important negative case like DoT or periodic triggers)
            if (victim->HasAura(211317) && !victim->HasAura(211319)) // Archbishop Benedictus' Restitution
                victim->CastSpell(victim, 211336, true);
            else
            {
                victim->RemoveAllAurasOnDeath();
            }

            // FORM_SPIRITOFREDEMPTION and related auras
            victim->CastSpell(victim, 27827, true, nullptr, aurEff);
            spiritOfRedemption = true;
        }
    }

    if (!spiritOfRedemption)
    {
        TC_LOG_DEBUG(LOG_FILTER_UNITS, "SET JUST_DIED");
        victim->setDeathState(JUST_DIED);
    }

    if (Creature* crt = victim->ToCreature())
        if (CreatureAI* ai = crt->AI())
            ai->ComonOnHome();

    // Inform pets (if any) when player kills target)
    // MUST come after victim->setDeathState(JUST_DIED); or pet next target
    // selection will get stuck on same target and break pet react state
    if (player)
    {
        Pet* pet = player->GetPet();
        if (pet && pet->isAlive() && pet->isControlled())
            pet->AI()->KilledUnit(victim);
    }

    // 10% durability loss on death
    // clean InHateListOf
    if (Player* plrVictim = victim->ToPlayer())
    {
        // remember victim PvP death for corpse type and corpse reclaim delay
        // at original death (not at SpiritOfRedemtionTalent timeout)
        plrVictim->SetPvPDeath(player != nullptr);

        // only if not player and not controlled by player pet. And not at BG
        if ((durabilityLoss && !player && !victim->ToPlayer()->InBattleground()) || (player && sWorld->getBoolConfig(CONFIG_DURABILITY_LOSS_IN_PVP)))
        {
            double baseLoss = sWorld->getRate(RATE_DURABILITY_LOSS_ON_DEATH);
            TC_LOG_DEBUG(LOG_FILTER_UNITS, "We are dead, losing %u percent durability", baseLoss);
            // Durability loss is calculated more accurately again for each item in Player::DurabilityLoss
            plrVictim->DurabilityLossAll(baseLoss, false, true);
            // durability lost message
            SendDurabilityLoss(plrVictim, baseLoss);
        }
        // Call KilledUnit for creatures
        if (IsCreature() && IsAIEnabled)
            ToCreature()->AI()->KilledUnit(victim);

        // last damage from non duel opponent or opponent controlled creature
        if (plrVictim->duel)
        {
            auto dueler = ObjectAccessor::FindPlayer(plrVictim->duel->opponent);

            plrVictim->CombatStopWithPets(true);
            dueler->CombatStopWithPets(true);
            plrVictim->AddDelayedEvent(1000, [plrVictim]() -> void
            {
                plrVictim->DuelComplete(DUEL_INTERRUPTED);
            });
        }

        if (InstanceScript* script = plrVictim->GetInstanceScript())
        {
            script->OnPlayerDies(plrVictim);
            script->OnPlayerDiesForScript(plrVictim);
        }
    }
    else                                                // creature died
    {
        TC_LOG_DEBUG(LOG_FILTER_UNITS, "DealDamageNotPlayer");

        if (!creature->isPet())
        {
            std::list<HostileReference*>& threatlist = creature->getThreatManager().getThreatList();
            for (std::list<HostileReference*>::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
            {
                if (Unit* unit = Unit::GetUnit(*creature, (*itr)->getUnitGuid()))
                    if (unit->IsPlayer())
                        if (unit->m_attackers.empty())
                            unit->SetCombatTimer(0);
            }
            creature->DeleteThreatList();
            CreatureTemplate const* cInfo = creature->GetCreatureTemplate();
            if (cInfo && (cInfo->lootid || cInfo->maxgold > 0))
                creature->SetFlag(OBJECT_FIELD_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
        }

        // Call KilledUnit for creatures, this needs to be called after the lootable flag is set
        if (IsCreature() && IsAIEnabled)
            ToCreature()->AI()->KilledUnit(victim);

        // Call creature just died function
        if (creature->IsAIEnabled)
        {
            creature->KillAllDelayedCombats();
            creature->AI()->JustDied(this);
        }

        if (TempSummon* summon = creature->ToTempSummon())
            if (Unit* summoner = summon->GetSummoner())
                if (summoner->ToCreature() && summoner->IsAIEnabled)
                    summoner->ToCreature()->AI()->SummonedCreatureDies(creature, this);

        // Call instance script.
        if (InstanceScript* script = creature->GetInstanceScript())
        {
            script->CreatureDies(creature, this);
            script->CreatureDiesForScript(creature, this);
        }

        // Dungeon specific stuff, only applies to players killing creatures
        if (creature->InInstance())
        {
            Map* instanceMap = creature->GetMap();
            Player* creditedPlayer = GetCharmerOrOwnerPlayerOrPlayerItself();
            // TODO: do instance binding anyway if the charmer/owner is offline

            if (instanceMap->IsDungeon() && creditedPlayer)
            {
                if (instanceMap->IsRaidOrHeroicDungeon())
                {
                    if (creature->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
                        static_cast<InstanceMap*>(instanceMap)->PermBindAllPlayers(creditedPlayer);
                }
            }
        }
    }

    if (m_attackers.empty())
        SetCombatTimer(0);

    // outdoor pvp things, do these after setting the death state, else the player activity notify won't work... doh...
    // handle player kill only if not suicide (spirit of redemption for example)
    if (this != victim)
    {
        if (player)
        {
            if (OutdoorPvP* pvp = player->GetOutdoorPvP())
                pvp->HandleKill(player, victim);

            if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(player->GetCurrentZoneID()))
                bf->HandleKill(player, victim);
        }

        if (OutdoorPvP* pvp = GetOutdoorPvP())
            pvp->HandleKill(this, victim);
    }

    //if (victim->IsPlayer())
    //    if (OutdoorPvP* pvp = victim->ToPlayer()->GetOutdoorPvP())
    //        pvp->HandlePlayerActivityChangedpVictim->ToPlayer();

    // battleground things (do this at the end, so the death state flag will be properly set to handle in the bg->handlekill)
    if (player && player->InBattleground())
    {
        if (Battleground* bg = player->GetBattleground())
        {
            if (victim->IsPlayer())
                bg->HandleKillPlayer(victim->ToPlayer(), player);
            else
                bg->HandleKillUnit(victim->ToCreature(), player);
        }
    }

    if (this != victim && victim->IsPlayer())
        if (auto outdoorPvP = victim->ToPlayer()->GetOutdoorPvP())
            outdoorPvP->HandlePlayerKilled(victim->ToPlayer());

    // achievement stuff
    if (victim->IsPlayer())
    {
        if (IsCreature())
            victim->ToPlayer()->UpdateAchievementCriteria(CRITERIA_TYPE_KILLED_BY_CREATURE, GetEntry());
        else if (IsPlayer() && victim != this)
            victim->ToPlayer()->UpdateAchievementCriteria(CRITERIA_TYPE_KILLED_BY_PLAYER, 1, ToPlayer()->GetTeam());
    }

    // Hook for OnPVPKill Event
    if (Player* killerPlr = ToPlayer())
    {
        if (Player* killedPlr = victim->ToPlayer())
            sScriptMgr->OnPVPKill(killerPlr, killedPlr);
        else if (Creature* killedCre = victim->ToCreature())
            sScriptMgr->OnCreatureKill(killerPlr, killedCre);
    }
    else if (Creature* killerCre = ToCreature())
    {
        if (Player* killed = victim->ToPlayer())
            sScriptMgr->OnPlayerKilledByCreature(killerCre, killed);
    }

    victim->m_IsInKillingProcess = false;
}

void Unit::SetControlled(bool apply, UnitState state)
{
    if (apply)
    {
        if (HasUnitState(state))
            return;

        AddUnitState(state);

        switch (state)
        {
            case UNIT_STATE_STUNNED:
                SetStunned(true);
                break;
            case UNIT_STATE_ROOT:
                if (!HasUnitState(UNIT_STATE_STUNNED))
                    SetRooted(true);
                break;
            case UNIT_STATE_CONFUSED:
                if (!HasUnitState(UNIT_STATE_STUNNED))
                {
                    ClearUnitState(UNIT_STATE_MELEE_ATTACKING);
                    SendMeleeAttackStop();
                    // SendCancelAutoRepeat ?
                    SetConfused(true);
                    CastStop();
                }
                break;
            case UNIT_STATE_FLEEING:
                if (!HasUnitState(UNIT_STATE_STUNNED | UNIT_STATE_CONFUSED))
                {
                    ClearUnitState(UNIT_STATE_MELEE_ATTACKING);
                    SendMeleeAttackStop();
                    // SendCancelAutoRepeat ?
                    SetFeared(true);
                    CastStop();
                }
                break;
            default:
                break;
        }
    }
    else
    {
        switch (state)
        {
            case UNIT_STATE_STUNNED:
                {
                    if (HasAuraType(SPELL_AURA_MOD_STUN))    return;
                    SetStunned(false);
                }
                break;
            case UNIT_STATE_ROOT:
                {
                    if (HasAuraType(SPELL_AURA_MOD_ROOT) || HasAuraType(SPELL_AURA_MOD_ROOTED) || GetVehicle())    return;
                    SetRooted(false);
                }
                break;
            case UNIT_STATE_CONFUSED:
                {
                    if (HasAuraType(SPELL_AURA_MOD_CONFUSE)) return;
                    SetConfused(false);
                }
                break;
            case UNIT_STATE_FLEEING:
                {
                    if (HasAuraType(SPELL_AURA_MOD_FEAR))    return;
                    SetFeared(false);
                }
                break;
            default: return;
        }

        ClearUnitState(state);

        if (HasUnitState(UNIT_STATE_STUNNED))
            SetStunned(true);
        else
        {
            if (HasUnitState(UNIT_STATE_ROOT))
                SetRooted(true);

            if (HasUnitState(UNIT_STATE_CONFUSED))
                SetConfused(true);
            else if (HasUnitState(UNIT_STATE_FLEEING))
                SetFeared(true);
        }
    }
}

void Unit::SetStunned(bool apply)
{
    if (apply)
    {
        SetTarget(ObjectGuid::Empty);
        SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);

        // MOVEMENTFLAG_ROOT cannot be used in conjunction with MOVEMENTFLAG_MASK_MOVING (tested 3.3.5a)
        // this will freeze clients. That's why we remove MOVEMENTFLAG_MASK_MOVING before
        // setting MOVEMENTFLAG_ROOT
        RemoveUnitMovementFlag(MOVEMENTFLAG_MASK_MOVING);
        AddUnitMovementFlag(MOVEMENTFLAG_ROOT);

        // Creature specific
        if (!IsPlayer())
            ToCreature()->StopMoving();
        else
            SetStandState(UNIT_STAND_STATE_STAND);

        SetRooted(true);
        CastStop(0, true);
    }
    else
    {
        if (isAlive() && getVictim())
            SetTarget(getVictim()->GetGUID());

        // don't remove UNIT_FLAG_STUNNED for pet when owner is mounted (disabled pet's interface)
        Unit* owner = GetOwner();
        if (!owner || !owner->IsPlayer() || (owner->IsPlayer() && !owner->ToPlayer()->IsMounted()))
            RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);

        if (!HasUnitState(UNIT_STATE_ROOT))         // prevent moving if it also has root effect
        {
            SetRooted(false);
            RemoveUnitMovementFlag(MOVEMENTFLAG_ROOT);
        }
    }
}

void Unit::SetRooted(bool apply)
{
    m_movementInfo.ChangePosition(GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
    m_movementInfo.UpdateTime(getMSTime());

    if (apply)
    {
        // MOVEMENTFLAG_ROOT cannot be used in conjunction with MOVEMENTFLAG_MASK_MOVING (tested 3.3.5a)
        // this will freeze clients. That's why we remove MOVEMENTFLAG_MASK_MOVING before
        // setting MOVEMENTFLAG_ROOT
        RemoveUnitMovementFlag(MOVEMENTFLAG_MASK_MOVING);
        AddUnitMovementFlag(MOVEMENTFLAG_ROOT);
        StopMoving();
    }
    else
        RemoveUnitMovementFlag(MOVEMENTFLAG_ROOT);

    static OpcodeServer const rootOpcodeTable[2][2] = {{SMSG_MOVE_SPLINE_UNROOT, SMSG_MOVE_UNROOT}, {SMSG_MOVE_SPLINE_ROOT, SMSG_MOVE_ROOT}};

    if (Player* playerMover = GetPlayerMover()) // unit controlled by a player.
    {
        WorldPackets::Movement::MoveSetFlag packet(rootOpcodeTable[apply][1]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
        playerMover->GetCheatData()->OrderSent(rootOpcodeTable[apply][1]);
    }
    else
    {
        WorldPackets::Movement::MoveSplineSetFlag packet(rootOpcodeTable[apply][0]);
        packet.MoverGUID = GetGUID();
        SendMessageToSet(packet.Write(), true);
    }
}

void Unit::SetFeared(bool apply)
{
    if (apply)
    {
        SetTarget(ObjectGuid::Empty);

        uint32 mechanic_mask = (1 << MECHANIC_FEAR) | (1 << MECHANIC_HORROR);

        Unit* caster = nullptr;
        Unit::AuraList fearAuras;
        GetAuraEffectsByMechanic(mechanic_mask, fearAuras);
        if (!fearAuras.empty())
            caster = ObjectAccessor::GetUnit(*this, (*fearAuras.begin())->GetCasterGUID());
        if (!caster)
            caster = getAttackerForHelper();
        GetMotionMaster()->MoveFleeing(caster, fearAuras.empty() ? sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_FLEE_DELAY) : 0);             // caster == NULL processed in MoveFleeing
    }
    else
    {
        if (isAlive())
        {
            if (GetMotionMaster()->GetCurrentMovementGeneratorType() == FLEEING_MOTION_TYPE || GetMotionMaster()->GetCurrentMovementGeneratorType() == TIMED_FLEEING_MOTION_TYPE)
                GetMotionMaster()->MovementExpired();
            if (getVictim())
                SetTarget(getVictim()->GetGUID());
        }
    }

    if (Player* player = ToPlayer())
        if (!player->HasUnitState(UNIT_STATE_POSSESSED))
            ToPlayer()->SetClientControl(this, !apply);
}

void Unit::SetConfused(bool apply)
{
    if (apply)
    {
        SetTarget(ObjectGuid::Empty);
        GetMotionMaster()->MoveConfused();
    }
    else
    {
        if (isAlive())
        {
            if (GetMotionMaster()->GetCurrentMovementGeneratorType() == CONFUSED_MOTION_TYPE)
                GetMotionMaster()->MovementExpired();
            if (getVictim())
                SetTarget(getVictim()->GetGUID());
        }
    }

    if (Player* player = ToPlayer())
    {
        if (player->HasUnitState(UNIT_STATE_POSSESSED))
            return;

        player->SendMovementSetCollisionHeight(player->GetCollisionHeight(apply));
        player->SetClientControl(this, !apply);
    }
}

bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* aurApp)
{
    if (!charmer)
        return false;

    // dismount players when charmed
    if (IsPlayer())
        RemoveAurasByType(SPELL_AURA_MOUNTED);

    if (charmer->IsPlayer())
        charmer->RemoveAurasByType(SPELL_AURA_MOUNTED);

    //ASSERT(type != CHARM_TYPE_POSSESS || charmer->IsPlayer());
    if(!(type != CHARM_TYPE_POSSESS || charmer->IsPlayer()))
        return false;
    //ASSERT((type == CHARM_TYPE_VEHICLE) == IsVehicle());
    if(!((type == CHARM_TYPE_VEHICLE) == IsVehicle()))
        return false;

    TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "SetCharmedBy: charmer %u (GUID %u), charmed %u (GUID %u), type %u.", charmer->GetEntry(), charmer->GetGUID().GetGUIDLow(), GetEntry(), GetGUID().GetGUIDLow(), uint32(type));

    if (this == charmer)
    {
        TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit::SetCharmedBy: Unit %u (GUID %u) is trying to charm itself!", GetEntry(), GetGUID().GetGUIDLow());
        return false;
    }

    //if (HasUnitState(UNIT_STATE_UNATTACKABLE))
    //    return false;

    if (IsPlayer() && ToPlayer()->GetTransport())
    {
        TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit::SetCharmedBy: Player on transport is trying to charm %u (GUID %u)", GetEntry(), GetGUIDLow());
        return false;
    }

    // Already charmed
    if (GetCharmerGUID())
    {
        TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit::SetCharmedBy: %u (GUID %u) has already been charmed but %u (GUID %u) is trying to charm it!", GetEntry(), GetGUIDLow(), charmer->GetEntry(), charmer->GetGUIDLow());
        return false;
    }

    CastStop();
    CombatStop(); // TODO: CombatStop(true) may cause crash (interrupt spells)
    DeleteThreatList();
    ClearSaveThreatTarget();

    // Charmer stop charming
    if (charmer->IsPlayer())
    {
        charmer->ToPlayer()->StopCastingCharm();
        charmer->ToPlayer()->StopCastingBindSight();
    }

    // Charmed stop charming
    if (IsPlayer())
    {
        ToPlayer()->StopCastingCharm();
        ToPlayer()->StopCastingBindSight();
    }

    // StopCastingCharm may remove a possessed pet?
    if (!IsInWorld())
    {
        TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit::SetCharmedBy: %u (GUID %u) is not in world but %u (GUID %u) is trying to charm it!", GetEntry(), GetGUIDLow(), charmer->GetEntry(), charmer->GetGUIDLow());
        return false;
    }

    // charm is set by aura, and aura effect remove handler was called during apply handler execution
    // prevent undefined behaviour
    if (aurApp && aurApp->GetRemoveMode())
        return false;

    // Set charmed
    Map* map = GetMap();
    if (!IsVehicle() || (IsVehicle() && map && !map->IsBattleground()))
        setFaction(charmer->getFaction());

    charmer->SetCharm(this, true);

    if (m_zoneScript)
        m_zoneScript->OnUnitCharmed(this, charmer);

    if (IsCreature())
    {
        ToCreature()->AI()->OnCharmed(true);
        GetMotionMaster()->MoveIdle();
    }
    else
    {
        Player* player = ToPlayer();
        if (player->isAFK())
            player->ToggleAFK();
        player->SetClientControl(this, false);
    }

    // charm is set by aura, and aura effect remove handler was called during apply handler execution
    // prevent undefined behaviour
    if (aurApp && aurApp->GetRemoveMode())
        return false;

    // Pets already have a properly initialized CharmInfo, don't overwrite it.
    if (type != CHARM_TYPE_VEHICLE && !GetCharmInfo())
    {
        InitCharmInfo();
        if (type == CHARM_TYPE_POSSESS)
            GetCharmInfo()->InitPossessCreateSpells();
        else
            GetCharmInfo()->InitCharmCreateSpells();
    }

    if (charmer->IsPlayer())
    {
        switch (type)
        {
            case CHARM_TYPE_VEHICLE:
                SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
                charmer->ToPlayer()->SetClientControl(this, true);
                charmer->ToPlayer()->SetMover(this);
                charmer->ToPlayer()->SetViewpoint(this, true);
                charmer->ToPlayer()->VehicleSpellInitialize();
                break;
            case CHARM_TYPE_POSSESS:
                AddUnitState(UNIT_STATE_POSSESSED);
                SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
                charmer->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
                charmer->ToPlayer()->SetClientControl(this, true);
                charmer->ToPlayer()->SetMover(this);
                charmer->ToPlayer()->SetViewpoint(this, true);
                charmer->ToPlayer()->PossessSpellInitialize();
                break;
            case CHARM_TYPE_CHARM:
                if (IsCreature() && charmer->getClass() == CLASS_WARLOCK)
                {
                    CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate();
                    if (cinfo && cinfo->Type == CREATURE_TYPE_DEMON)
                    {
                        // to prevent client crash
                        SetClass(CLASS_MAGE);

                        // just to enable stat window
                        if (GetCharmInfo())
                            GetCharmInfo()->SetPetNumber(sObjectMgr->GeneratePetNumber(), true);

                        // if charmed two demons the same session, the 2nd gets the 1st one's name
                        SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, uint32(time(nullptr))); // cast can't be helped
                    }
                }
                charmer->ToPlayer()->CharmSpellInitialize();
                break;
            default:
            case CHARM_TYPE_CONVERT:
                break;
        }
    }else if (type == CHARM_TYPE_CONVERT)
    {
        if (ToPlayer())
        {
            ToPlayer()->SetClientControl(charmer, true);
            ToPlayer()->SetMover(charmer);
        }
    }

    return true;
}

void Unit::RemoveCharmedBy(Unit* charmer)
{
    if (!isCharmed())
        return;

    if (!charmer)
        charmer = GetCharmer();
    if (charmer != GetCharmer()) // one aura overrides another?
    {
//        TC_LOG_FATAL(LOG_FILTER_UNITS, "Unit::RemoveCharmedBy: this: " UI64FMTD " true charmer: " UI64FMTD " false charmer: " UI64FMTD,
//            GetGUID().GetCounter(), GetCharmerGUID().GetCounter(), charmer->GetGUID().GetCounter());
//        ASSERT(false);
        return;
    }

    CharmType type;
    if (HasUnitState(UNIT_STATE_POSSESSED))
        type = CHARM_TYPE_POSSESS;
    else if (charmer && charmer->IsOnVehicle(this))
        type = CHARM_TYPE_VEHICLE;
    else
        type = CHARM_TYPE_CHARM;

    CastStop();
    CombatStop(); // TODO: CombatStop(true) may cause crash (interrupt spells)
    AttackStop();
    getHostileRefManager().deleteReferences();
    DeleteThreatList();
    ClearSaveThreatTarget();
    Map* map = GetMap();
    if (!IsVehicle() || (IsVehicle() && map && !map->IsBattleground()))
        RestoreFaction();
    GetMotionMaster()->Clear(true);
    GetMotionMaster()->InitDefault();

    if (m_zoneScript)
        m_zoneScript->OnUnitRemoveCharmed(this, charmer);

    if (Creature* creature = ToCreature())
    {
        // Creature will restore its old AI on next update
        if (creature->AI())
            creature->AI()->OnCharmed(false);

        // Vehicle should not attack its passenger after he exists the seat
        if (type != CHARM_TYPE_VEHICLE && charmer)
            LastCharmerGUID = charmer->GetGUID();
    }
    else
        ToPlayer()->SetClientControl(this, true);

    // If charmer still exists
    if (!charmer)
        return;

    ASSERT(type != CHARM_TYPE_POSSESS || charmer->IsPlayer());
    ASSERT(type != CHARM_TYPE_VEHICLE || (IsCreature() && IsVehicle()));

    charmer->SetCharm(this, false);

    if (charmer->IsPlayer())
    {
        switch (type)
        {
            case CHARM_TYPE_VEHICLE:
                charmer->ToPlayer()->SetClientControl(charmer, true);
                charmer->ToPlayer()->SetViewpoint(this, false);
                charmer->ToPlayer()->SetClientControl(this, false);
                if (IsPlayer())
                    ToPlayer()->SetMover(this);
                RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
                break;
            case CHARM_TYPE_POSSESS:
                charmer->ToPlayer()->SetClientControl(charmer, true);
                charmer->ToPlayer()->SetViewpoint(this, false);
                charmer->ToPlayer()->SetClientControl(this, false);
                if (IsPlayer())
                    ToPlayer()->SetMover(this);
                charmer->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL);
                ClearUnitState(UNIT_STATE_POSSESSED);
                RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
                break;
            case CHARM_TYPE_CHARM:
                if (IsCreature() && charmer->getClass() == CLASS_WARLOCK)
                {
                    CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate();
                    if (cinfo && cinfo->Type == CREATURE_TYPE_DEMON)
                    {
                        SetClass(cinfo->unit_class);
                        if (GetCharmInfo())
                            GetCharmInfo()->SetPetNumber(0, true);
                        else
                            TC_LOG_ERROR(LOG_FILTER_UNITS, "Aura::HandleModCharm: target=" UI64FMTD " with typeid=%d has a charm aura but no charm info!", GetGUID().GetCounter(), GetTypeId());
                    }
                }
                break;
            default:
            case CHARM_TYPE_CONVERT:
                break;
        }
    }else if (type == CHARM_TYPE_CONVERT)
    {
        if (ToPlayer())
        {
            ToPlayer()->SetClientControl(charmer, false);
            ToPlayer()->SetMover(this);
        }
    }

    // a guardian should always have charminfo
    if (charmer->IsPlayer() && this != charmer->GetFirstControlled())
        charmer->ToPlayer()->SendRemoveControlBar();
    else if (IsPlayer() || (IsCreature() && !ToCreature()->isGuardian()))
        DeleteCharmInfo();
}

void Unit::RestoreFaction()
{
    if (IsPlayer())
    {
        if (HasAuraType(SPELL_AURA_MOD_FACTION))
        {
            if (AuraEffectList const* auraEffList = GetAuraEffectsByType(SPELL_AURA_MOD_FACTION))
            for (AuraEffectList::const_iterator itr = auraEffList->begin(); itr != auraEffList->end(); ++itr)
            {
                ToPlayer()->setFactionForRace((*itr)->GetMiscValue());
                break;
            }
        }
        else
            ToPlayer()->setFactionForRace(getRace());
    }
    else
    {
        if (HasUnitTypeMask(UNIT_MASK_MINION))
        {
            if (Unit* owner = GetOwner())
            {
                setFaction(owner->getFaction());
                return;
            }
        }

        if (CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate())  // normal creature
            setFaction(cinfo->faction);
    }
}

bool Unit::CreateVehicleKit(uint32 id, uint32 creatureEntry, uint32 RecAura, bool loading /*= false*/)
{
    VehicleEntry const* vehInfo = sVehicleStore.LookupEntry(id);
    if (!vehInfo)
        return false;

    m_vehicleKit = new Vehicle(this, vehInfo, creatureEntry, RecAura);
    m_updateFlag |= UPDATEFLAG_VEHICLE;
    m_unitTypeMask |= UNIT_MASK_VEHICLE;

    if (!loading)
        SendSetVehicleRecId(id);

    return true;
}

void Unit::RemoveVehicleKit(bool onRemoveFromWorld /*= false*/)
{
    if (!m_vehicleKit || m_Teleports)
        return;

    if (!onRemoveFromWorld)
        SendSetVehicleRecId(0);

    m_vehicleKit->Uninstall(true);
    delete m_vehicleKit;

    m_vehicleKit = nullptr;

    m_updateFlag &= ~UPDATEFLAG_VEHICLE;
    m_unitTypeMask &= ~UNIT_MASK_VEHICLE;
    RemoveFlag(UNIT_FIELD_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK | UNIT_NPC_FLAG_PLAYER_VEHICLE);
}

Unit* Unit::GetVehicleBase() const
{
    return m_vehicle ? m_vehicle->GetBase() : nullptr;
}

Creature* Unit::GetVehicleCreatureBase() const
{
    if (Unit* veh = GetVehicleBase())
        if (Creature* c = veh->ToCreature())
            return c;

    return nullptr;
}

ObjectGuid Unit::GetTransGUID() const
{
    if (!IsInWorld())
        return ObjectGuid::Empty;

    return m_movementInfo.transport.Guid;
}

TransportBase* Unit::GetDirectTransport() const
{
    if (Vehicle* veh = GetVehicle())
        return veh;
    return GetTransport();
}

bool Unit::IsInPartyWith(Unit const* unit) const
{
    if (this == unit)
        return true;

    const Unit* u1 = GetCharmerOrOwnerOrSelf();
    const Unit* u2 = unit->GetCharmerOrOwnerOrSelf();
    if (u1 == u2)
        return true;

    if (u1->IsPlayer() && u2->IsPlayer())
        return u1->ToPlayer()->IsInSameGroupWith(u2->ToPlayer());
    if ((u2->IsPlayer() && u1->IsCreature() && u1->ToCreature()->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_TREAT_AS_RAID_UNIT) ||
        (u1->IsPlayer() && u2->IsCreature() && u2->ToCreature()->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_TREAT_AS_RAID_UNIT))
        return true;
    return false;
}

bool Unit::IsInRaidWith(Unit const* unit) const
{
    if (this == unit)
        return true;

    const Unit* u1 = GetCharmerOrOwnerOrSelf();
    const Unit* u2 = unit->GetCharmerOrOwnerOrSelf();
    if (u1 == u2)
        return true;

    if (u1->IsPlayer() && u2->IsPlayer())
        return u1->ToPlayer()->IsInSameRaidWith(u2->ToPlayer());
    if ((u2->IsPlayer() && u1->IsCreature() && u1->ToCreature()->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_TREAT_AS_RAID_UNIT) ||
        (u1->IsPlayer() && u2->IsCreature() && u2->ToCreature()->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_TREAT_AS_RAID_UNIT))
        return true;
    return false;
}

void Unit::GetPartyMembers(std::list<Unit*> &TagUnitMap)
{
    Unit* owner = GetCharmerOrOwnerOrSelf();
    Group* group = nullptr;
    if (owner->IsPlayer())
        group = owner->ToPlayer()->GetGroup();

    if (group)
    {
        uint8 subgroup = owner->ToPlayer()->GetSubGroup();

        for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
        {
            Player* Target = itr->getSource();

            // IsHostileTo check duel and controlled by enemy
            if (Target && Target->GetSubGroup() == subgroup && !IsHostileTo(Target))
            {
                if (Target->isAlive() && IsInMap(Target))
                    TagUnitMap.push_back(Target);

                if (Guardian* pet = Target->GetGuardianPet())
                    if (pet->isAlive() && IsInMap(Target))
                        TagUnitMap.push_back(pet);
            }
        }
    }
    else
    {
        if (owner->isAlive() && (owner == this || IsInMap(owner)))
            TagUnitMap.push_back(owner);
        if (Guardian* pet = owner->GetGuardianPet())
            if (pet->isAlive() && (pet == this || IsInMap(pet)))
                TagUnitMap.push_back(pet);
    }
}


void Unit::GetRaidMembers(std::list<Unit*>& tagUnitMap)
{
    auto const& owner = GetCharmerOrOwnerOrSelf();
    Group* group = nullptr;
    if (owner->IsPlayer())
        group = owner->ToPlayer()->GetGroup();

    if (group)
    {
        for (auto itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
        {
            auto const& target = itr->getSource();
            if (target && !IsHostileTo(target))
            {
                if (target->isAlive() && IsInMap(target))
                    tagUnitMap.push_back(target);

                if (auto const& pet = target->GetGuardianPet())
                    if (pet->isAlive() && IsInMap(target))
                        tagUnitMap.push_back(pet);
            }
        }
    }
    else
    {
        if (owner->isAlive() && (owner == this || IsInMap(owner)))
            tagUnitMap.push_back(owner);

        if (auto const& pet = owner->GetGuardianPet())
            if (pet->isAlive() && (pet == this || IsInMap(pet)))
                tagUnitMap.push_back(pet);
    }
}

bool Unit::IsContestedGuard() const
{
    if (FactionTemplateEntry const* entry = getFactionTemplateEntry())
        return entry->IsContestedGuardFaction();
    return false;
}

bool Unit::IsPvP() const
{
    return HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_PVP);
}

bool Unit::IsFFAPvP() const
{
    return HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP);
}

void Unit::SetPvP(bool state)
{
    if (state)
        SetByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_PVP);
    else
        RemoveByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_PVP);

    if (IsPlayer())
        ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_STATUS);
}

Aura* Unit::ToggleAura(uint32 spellId, Unit* target)
{
    if (!target)
        return nullptr;

    if (target->HasAura(spellId))
    {
        target->RemoveAurasDueToSpell(spellId);
        return nullptr;
    }
    return target->AddAura(spellId, target);
}

Aura* Unit::AddAura(uint32 spellId, Unit* target, Item* castItem, uint16 stackAmount, int32 Duration/* = 0*/, int32 MaxDuration/* = 0*/)
{
    if (!target)
        return nullptr;

    SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
    if (!spellInfo)
        return nullptr;

    if (!target->isAlive() && !spellInfo->IsPassive() && !(spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD) && !(spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD))))
        return nullptr;

    return AddAura(spellInfo, MAX_EFFECT_MASK, target, castItem, stackAmount, Duration, MaxDuration);
}

Aura* Unit::AddAura(SpellInfo const* spellInfo, uint32 effMask, Unit* target, Item* castItem, uint16 stackAmount, int32 Duration/* = 0*/, int32 MaxDuration/* = 0*/)
{
    if (!spellInfo)
        return nullptr;

    if (target->IsImmunedToSpell(spellInfo))
        return nullptr;

    for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
    {
        if (spellInfo->EffectMask < uint32(1 << i))
            break;

        if (!(effMask & (1<<i)))
            continue;
        if (target->IsImmunedToSpellEffect(spellInfo, i))
            effMask &= ~(1<<i);
    }

    if (Aura* aura = Aura::TryRefreshStackOrCreate(spellInfo, effMask, target, this, nullptr, castItem, ObjectGuid::Empty, nullptr, stackAmount))
    {
        int32 handleDuration = 0;

        if (MaxDuration != 0)
        {
            aura->SetMaxDuration(MaxDuration);
        }
        else
        {
            bool calcDiminishingReturns = true;

            if (Map* map = GetMap())
            {
                if (map->IsDungeon())
                    calcDiminishingReturns = false;
            }

            handleDuration = aura->GetMaxDuration();

            if (calcDiminishingReturns)
            {
                DiminishingGroup m_diminishGroup = GetDiminishingReturnsGroupForSpell(spellInfo, false);
                int32 limitduration = GetDiminishingReturnsLimitDuration(m_diminishGroup, spellInfo);
                ((UnitAura*)aura)->SetDiminishGroup(m_diminishGroup);

                if (limitduration && limitduration < handleDuration)
                    handleDuration = limitduration;
            }

            bool positive = spellInfo->IsPositive();
            if (AuraApplication* aurApp = aura->GetApplicationOfTarget(GetGUID()))
                positive = aurApp->IsPositive();

            handleDuration = ModSpellDuration(spellInfo, target, handleDuration, positive, effMask);
            aura->SetMaxDuration(handleDuration);
        }

        if (Duration != 0)
        {
            aura->SetDuration(Duration);
        }
        else
        {
            aura->SetDuration(handleDuration);
        }

        aura->ApplyForTargets();
        return aura;
    }
    return nullptr;
}

void Unit::SetAuraStack(uint32 spellId, Unit* target, uint32 stack)
{
    Aura* aura = target->GetAura(spellId, GetGUID());
    if (!aura)
        aura = AddAura(spellId, target);
    if (aura && stack)
        aura->ModStackAmount(stack);
}

void Unit::SendPlaySpellVisualKit(uint32 id, uint32 kitType, uint32 duration /*= 0*/)
{
    WorldPackets::Spells::PlaySpellVisualKit packet;
    packet.Unit = GetGUID();
    packet.KitRecID = id;
    packet.KitType = kitType;
    packet.Duration = duration;
    SendMessageToSet(packet.Write(), true);
}

void Unit::ApplyResilience(Unit const* victim, float *damage) const
{
    // player mounted on multi-passenger mount is also classified as vehicle
    if (IsVehicle() && !IsPlayer() || (victim->IsVehicle() && !victim->IsPlayer()))
        return;

    // Resilience works only for players or pets against other players or pets
    if (!IsPlayer() && (GetOwner() && !GetOwner()->IsPlayer()))
        return;

    // Don't consider resilience if not in PvP - player or pet
    if (!GetCharmerOrOwnerPlayerOrPlayerItself())
        return;

    if (victim->GetOwnerGUID() && victim->GetOwnerGUID() == GetGUID())
        return;

    Unit const* target = nullptr;
    if (victim->IsPlayer())
        target = victim;
    else if (victim->IsCreature() && victim->GetOwner() && victim->GetOwner()->IsPlayer())
        target = victim->GetOwner();

    if (!target)
        return;

    *damage -= target->GetDamageReduction(*damage);
}

// Melee based spells can be miss, parry or dodge on this step
// Crit or block - determined on damage calculation phase! (and can be both in some time)
float Unit::MeleeSpellMissChance(const Unit* victim, WeaponAttackType attType, uint32 spellId) const
{
    //calculate miss chance
    float missChance = victim->GetUnitMissChance(this, spellId);

    if (attType == RANGED_ATTACK)
        missChance -= victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE);
    else
        missChance -= victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE);

    if (!spellId && haveOffhandWeapon() && !IsInFeralForm())
        missChance += 17.0f;

    missChance -= GetTotalAuraModifier(SPELL_AURA_IGNORE_DUAL_WIELD_HIT_PENALTY);

    if (IsCreature() && ToCreature()->GetCreatureTemplate()->TypeFlags[0] & CREATURE_TYPEFLAGS_BOSS)
        missChance = 0.0f;

    // Calculate hit chance
    float hitChance = 100.0f;

    // Spellmod from SPELLMOD_RESIST_MISS_CHANCE
    if (spellId)
    {
        if (Player* modOwner = GetSpellModOwner())
            modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, hitChance);
    }

    missChance += hitChance - 100.0f;

    if (attType == RANGED_ATTACK)
        missChance -= m_modRangedHitChance;
    else
        missChance -= m_modMeleeHitChance;

    RoundToInterval(missChance, 0.0f, 100.0f);

    return missChance;
}

void Unit::SetPhaseMask(uint32 newPhaseMask, bool update)
{
    if (newPhaseMask == GetPhaseMask())
        return;

    if (IsInWorld())
    {
        // modify hostile references for new phasemask, some special cases deal with hostile references themselves
        if (IsCreature() || (!ToPlayer()->isGameMaster() && !ToPlayer()->GetSession()->PlayerLogout()))
        {
            HostileRefManager& refManager = getHostileRefManager();
            HostileReference* ref = refManager.getFirst();

            while (ref)
            {
                if (Unit* unit = ref->getSource()->getOwner())
                    if (Creature* creature = unit->ToCreature())
                        refManager.setOnlineOfflineState(creature, creature->InSamePhase(newPhaseMask));

                ref = ref->next();
            }

            // modify threat lists for new phasemask
            if (!IsPlayer())
            {
                std::list<HostileReference*> threatList = getThreatManager().getThreatList();
                std::list<HostileReference*> offlineThreatList = getThreatManager().getOfflineThreatList();

                // merge expects sorted lists
                threatList.sort();
                offlineThreatList.sort();
                threatList.merge(offlineThreatList);

                for (std::list<HostileReference*>::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
                    if (Unit* unit = (*itr)->getTarget())
                        unit->getHostileRefManager().setOnlineOfflineState(ToCreature(), unit->InSamePhase(newPhaseMask));
            }
        }
    }

    WorldObject::SetPhaseMask(newPhaseMask, false);

    if (!IsInWorld())
        return;

    for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
        if(Unit* unit = ObjectAccessor::GetUnit(*this, *itr))
            if (unit->IsCreature())
                unit->SetPhaseMask(newPhaseMask, true);

    for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
        if (m_SummonSlot[i])
            if (Creature* summon = GetMap()->GetCreature(m_SummonSlot[i]))
                summon->SetPhaseMask(newPhaseMask, true);

    RemoveNotOwnSingleTargetAuras(newPhaseMask);            // we can lost access to caster or target

    // Update visibility after phasing pets and summons so they wont despawn
    if (update)
        UpdateObjectVisibility();
}

void Unit::SetPhaseId(std::set<uint32> const& newPhase, bool update)
{
    //- if differend size - we should do it.
    if (newPhase.size() == GetPhases().size())
    {
        bool _exit = true;
        for (auto phaseID : newPhase)
            if (!HasPhaseId(phaseID))
            {
                _exit = false;
                break;
            }
        if (_exit)
            return;
    }

    WorldObject::SetPhaseId(newPhase, false);

    if (IsInWorld())
    {
        // modify hostile references for new phasemask, some special cases deal with hostile references themselves
        if (IsCreature() || (!ToPlayer()->isGameMaster() && !ToPlayer()->GetSession()->PlayerLogout()))
        {
            HostileRefManager& refManager = getHostileRefManager();
            HostileReference* ref = refManager.getFirst();

            while (ref)
            {
                if (Unit* unit = ref->getSource()->getOwner())
                    if (Creature* creature = unit->ToCreature())
                        refManager.setOnlineOfflineState(creature, creature->InSamePhase(this));

                ref = ref->next();
            }

            // modify threat lists for new phasemask
            if (!IsPlayer())
            {
                std::list<HostileReference*> threatList = getThreatManager().getThreatList();
                std::list<HostileReference*> offlineThreatList = getThreatManager().getOfflineThreatList();

                // merge expects sorted lists
                threatList.sort();
                offlineThreatList.sort();
                threatList.merge(offlineThreatList);

                for (std::list<HostileReference*>::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
                    if (Unit* unit = (*itr)->getTarget())
                        unit->getHostileRefManager().setOnlineOfflineState(ToCreature(), unit->InSamePhase(this));
            }
        }
    }

    if (!IsInWorld())
    {
        OnRelocated();
        return;
    }

    for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
        if(Unit* unit = ObjectAccessor::GetUnit(*this, *itr))
            if (unit->IsCreature())
                unit->SetPhaseId(newPhase, true);

    for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
        if (m_SummonSlot[i])
            if (Creature* summon = GetMap()->GetCreature(m_SummonSlot[i]))
                summon->SetPhaseId(newPhase, true);

    RebuildTerrainSwaps();

    // Update visibility after phasing pets and summons so they wont despawn
    if (update)
        UpdateObjectVisibility();
}

class AINotifyTask final : public BasicEvent
{
public:
    explicit AINotifyTask(Unit* me) : m_owner(me)
    {
        m_owner->setAINotifyScheduled(true);
    }

    ~AINotifyTask()
    {
        m_owner->setAINotifyScheduled(false);
    }

    bool Execute(uint64, uint32) final
    {
        Trinity::AIRelocationNotifier notifier(*m_owner);
        Trinity::VisitNearbyObject(m_owner, m_owner->GetVisibilityRange(), notifier);
        return true;
    }

    static void Schedule(Unit* me)
    {
        if (!me->isAINotifyScheduled())
        {
            EventProcessor &events = me->m_Events;
            events.AddEvent(new AINotifyTask(me), events.CalculateTime(sWorld->GetVisibilityAINotifyDelay()));
        }
    }

private:
    Unit* m_owner;
};

class VisibilityUpdateTask final : public BasicEvent
{
public:
    VisibilityUpdateTask(Unit* me) : m_owner(me) { }

    bool Execute(uint64, uint32) final
    {
        uint32 _ss = getMSTime();
        UpdateVisibility(m_owner);

        if (m_owner->IsPlayer() && m_owner->GetMap())
            m_owner->GetMap()->loadGridsInRange(*m_owner, m_owner->GetMap()->IsScenario() ? MAX_VISIBILITY_DISTANCE : m_owner->GetGridActivationRange());


        uint32 _mss = GetMSTimeDiffToNow(_ss);
        if (_mss > 200)
            sLog->outDiff("VisibilityUpdateTask player %u wait %ums", m_owner->GetGUIDLow(), _mss);

        m_owner->m_VisibilityUpdateScheduled = false;
        return true;
    }

    static void UpdateVisibility(Unit* me)
    {
        SharedVisionList const &shList = me->GetSharedVisionList();
        if (!shList.empty())
        {
            for (SharedVisionList::const_iterator it = shList.begin(); it != shList.end();)
                (*it++)->UpdateVisibilityForPlayer();
        }

        if (Player* player = me->ToPlayer())
            player->UpdateVisibilityForPlayer();

        me->WorldObject::UpdateObjectVisibility(true);
    }

private:
    Unit* m_owner;
};

void Unit::OnRelocated()
{
    if (IsPlayer())
    {
        if (!m_VisibilityUpdateScheduled)
        {
            m_VisibilityUpdateScheduled = true;
            m_Events.AddEvent(new VisibilityUpdateTask(this), m_Events.CalculateTime(sWorld->GetVisibilityAINotifyDelay()));
        }
    }
    else if (!m_lastVisibilityUpdPos.IsInDist(this, sWorld->GetVisibilityRelocationLowerLimitC()))
    {
        m_lastVisibilityUpdPos = *this;
        m_Events.AddEvent(new VisibilityUpdateTask(this), m_Events.CalculateTime(1));
    }

    AINotifyTask::Schedule(this);
}

void Unit::UpdateObjectVisibility(bool forced)
{
    if (forced)
        VisibilityUpdateTask::UpdateVisibility(this);
    else
        m_Events.AddEvent(new VisibilityUpdateTask(this), m_Events.CalculateTime(sWorld->GetVisibilityAINotifyDelay()));

    AINotifyTask::Schedule(this);
}

void Unit::SendMoveKnockBack(Player* player, float speedXY, float speedZ, float vcos, float vsin)
{
    AddUnitState(UNIT_STATE_JUMPING);

    WorldPackets::Movement::MoveKnockBack knockBack;
    knockBack.MoverGUID = GetGUID();
    knockBack.SequenceIndex = m_sequenceIndex++;
    knockBack.Direction = Position(vcos, vsin);
    knockBack.Speeds.HorzSpeed = speedXY;
    knockBack.Speeds.VertSpeed = speedZ;
    player->SendDirectMessage(knockBack.Write());
    player->GetCheatData()->KnockBack(speedXY, speedZ, vcos, vsin);
}

void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ, Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/)
{
    Player* player = nullptr;
    if (IsPlayer())
        player = ToPlayer();
    else if (Unit* charmer = GetCharmer())
    {
        player = charmer->ToPlayer();
        if (player && player->m_mover != this)
            player = nullptr;
    }

    if (!player)
        GetMotionMaster()->MoveKnockbackFrom(x, y, speedXY, speedZ);
    else
    {
        float vcos, vsin;
        GetSinCos(x, y, vsin, vcos);
        SendMoveKnockBack(player, speedXY, -speedZ, vcos, vsin);
    }
}

float Unit::GetCombatRatingReduction(CombatRating cr) const
{
    if (Player const* player = ToPlayer())
        return player->GetRatingBonusValue(cr);
    // Player's pet get resilience from owner
    if (isPet() && GetOwner())
        if (Player* owner = GetOwner()->ToPlayer())
            return owner->GetRatingBonusValue(cr);
    return 0.0f;
}

uint32 Unit::GetCombatRatingDamageReduction(CombatRating cr, float cap, uint32 damage) const
{
    float percent = std::min(GetCombatRatingReduction(cr), cap);
    float dmgPct = 0.f;

    if (Player* pl = GetCharmerOrOwnerPlayerOrPlayerItself())
    {
        float auraPercent = pl->GetFloatValue(PLAYER_FIELD_MOD_RESILIENCE_PERCENT);
        dmgPct = std::min(50.f, auraPercent + percent);
    }

    return CalculatePct(damage, dmgPct);
}

uint32 Unit::GetModelForForm(ShapeshiftForm form)
{
    if (Player* player = ToPlayer())
    {
        if (Item* artifact = player->GetArtifactWeapon())
            if (ArtifactAppearanceEntry const* artifactAppearance = sArtifactAppearanceStore.LookupEntry(artifact->GetModifier(ITEM_MODIFIER_ARTIFACT_APPEARANCE_ID)))
                if (ShapeshiftForm(artifactAppearance->OverrideShapeshiftFormID) == form)
                    return artifactAppearance->OverrideShapeshiftDisplayID;
    }

    switch (form)
    {
        case FORM_CAT:
        {
            // Hack for Druid of the Flame, Fandral's Flamescythe
            if (HasAura(99245) || HasAura(138927))
                return 38150;

            // check Incarnation
            bool epic = HasAura(102543);

            // Based on Hair color
            if (getRace() == RACE_NIGHTELF)
            {
                // Glyph of the Feral Chameleon
                if (HasAura(210333))
                {
                    uint32 models[] = { 29405, 29406, 29407, 29408, 892 };
                    uint32 epicModels[] = { 43764, 43763, 43762, 43765, 43761 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                uint8 hairColor = ToPlayer()->GetHairColorValue();
                switch (hairColor)
                {
                    case 7: // Violet
                    case 8:
                        return epic ? 43764 : 29405;
                    case 3: // Light Blue
                        return epic ? 43763 : 29406;
                    case 0: // Green
                    case 1: // Light Green
                    case 2: // Dark Green
                        return epic ? 43762 : 29407;
                    case 4: // White
                        return epic ? 43765 : 29408;
                    default: // original - Dark Blue
                        return epic ? 43761 :892;
                }
            }
            if (getRace() == RACE_TROLL)
            {
                // Glyph of the Feral Chameleon
                if (HasAura(210333))
                {
                    uint32 models[] = { 33668, 33667, 33666, 33665, 33669 };
                    uint32 epicModels[] = { 43776, 43778, 43773, 43775, 43777 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                uint8 hairColor = ToPlayer()->GetHairColorValue();
                switch (hairColor)
                {
                    case 0: // Red
                    case 1:
                        return epic ? 43776 : 33668;
                    case 2: // Yellow
                    case 3:
                        return epic ? 43778 : 33667;
                    case 4: // Blue
                    case 5:
                    case 6:
                        return epic ? 43773 : 33666;
                    case 7: // Purple
                    case 10:
                        return epic ? 43775 : 33665;
                    default: // original - white
                        return epic ? 43777 : 33669;
                }
            }
            if (getRace() == RACE_WORGEN)
            {
                // Glyph of the Feral Chameleon
                if (HasAura(210333))
                {
                    uint32 models[] = { 33662, 33661, 33664, 33663, 33660 };
                    uint32 epicModels[] = { 43781, 43780, 43784, 43785, 43782 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                // Based on Skin color
                uint8 skinColor = ToPlayer()->GetSkinValue();
                // Male
                if (getGender() == GENDER_MALE)
                {
                    switch (skinColor)
                    {
                        case 1: // Brown
                            return epic ? 43781 :33662;
                        case 2: // Black
                        case 7:
                            return epic ? 43780 : 33661;
                        case 4: // yellow
                            return epic ? 43784 : 33664;
                        case 3: // White
                        case 5:
                            return epic ? 43785 : 33663;
                        default: // original - Gray
                            return epic ? 43782 : 33660;
                    }
                }
                // Female
                switch (skinColor)
                {
                    case 5: // Brown
                    case 6:
                        return epic ? 43781 : 33662;
                    case 7: // Black
                    case 8:
                        return epic ? 43780 : 33661;
                    case 3: // yellow
                    case 4:
                        return epic ? 43784 : 33664;
                    case 2: // White
                        return epic ? 43785 : 33663;
                    default: // original - Gray
                        return epic ? 43782 : 33660;
                }
            }
            // Based on Skin color
            if (getRace() == RACE_TAUREN)
            {
                // Glyph of the Feral Chameleon
                if (HasAura(210333))
                {
                    uint32 models[] = { 29409, 29410, 29411, 29412, 8571 };
                    uint32 epicModels[] = { 43769, 43770, 43768, 43766, 43767 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                uint8 skinColor = ToPlayer()->GetSkinValue();

                // Male
                if (getGender() == GENDER_MALE)
                {
                    switch (skinColor)
                    {
                        case 12: // White
                        case 13:
                        case 14:
                        case 18: // Completly White
                            return epic ? 43769 : 29409;
                        case 9: // Light Brown
                        case 10:
                        case 11:
                            return epic ? 43770 : 29410;
                        case 6: // Brown
                        case 7:
                        case 8:
                            return epic ? 43768 : 29411;
                        case 0: // Dark
                        case 1:
                        case 2:
                        case 3: // Dark Grey
                        case 4:
                        case 5:
                            return epic ? 43766 : 29412;
                        default: // original - Grey
                            return epic ? 43767 : 8571;
                    }
                }
                // Female
                switch (skinColor)
                {
                    case 10: // White
                        return epic ? 43769 : 29409;
                    case 6: // Light Brown
                    case 7:
                        return epic ? 43770 : 29410;
                    case 4: // Brown
                    case 5:
                        return epic ? 43768 : 29411;
                    case 0: // Dark
                    case 1:
                    case 2:
                    case 3:
                        return epic ? 43766 : 29412;
                    default: // original - Grey
                        return epic ? 43767 : 8571;
                }
            }

            // Based on Skin color
            if (getRace() == RACE_HIGHMOUNTAIN_TAUREN)
            {
                // Glyph of the Feral Chameleon
                if (HasAura(210333))
                {
                    uint32 models[] = { 80597, 80598, 80599, 80600, 80596 };
                    uint32 epicModels[] = { 43769, 43770, 43768, 43766, 43767 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                uint8 skinColor = ToPlayer()->GetSkinValue();

                switch (skinColor)
                {
                    case 0:
                        return 80598; // dark brown
                    case 1:
                        return 80597; // brown
                    case 2:
                        return 80599; // light brown with white horns
                    case 3:
                        return 80596; // dark
                    case 4:
                    default:
                        return 80600; // light brown with brown horns
                }
            }

            if (Player::TeamForRace(getRace()) == ALLIANCE)
                return 892;
            return 8571;
        }
        case FORM_BEAR:
        {
            // check Incarnation
            bool epic = HasAura(102558);

            // Based on Hair color
            if (getRace() == RACE_NIGHTELF)
            {
                // Glyph of the Ursol Chameleon
                if (HasAura(107059))
                {
                    uint32 models[] = { 29413, 29414, 29416, 29417, 2281 };
                    uint32 epicModels[] = { 43759, 43756, 43760, 43757, 43758 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                uint8 hairColor = ToPlayer()->GetHairColorValue();
                switch (hairColor)
                {
                    case 0: // Green
                    case 1: // Light Green
                    case 2: // Dark Green
                        return epic ? 43759 : 29413;
                    case 6: // Dark Blue
                        return epic ? 43756 : 29414;
                    case 4: // White
                        return epic ? 43760 : 29416;
                    case 3: // Light Blue
                        return epic ? 43757 : 29415;
                    default: // original - Violet
                        return epic ? 43758 : 2281;
                }
            }
            if (getRace() == RACE_TROLL)
            {
                // Glyph of the Ursol Chameleon
                if (HasAura(107059))
                {
                    uint32 models[] = { 33657, 33659, 33656, 33658, 33655 };
                    uint32 epicModels[] = { 43748, 43750, 43747, 43749, 43746 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                uint8 hairColor = ToPlayer()->GetHairColorValue();
                switch (hairColor)
                {
                    case 0: // Red
                    case 1:
                        return epic ? 43748 : 33657;
                    case 2: // Yellow
                    case 3:
                        return epic ? 43750 : 33659;
                    case 7: // Purple
                    case 10:
                        return epic ? 43747 : 33656;
                    case 8: // White
                    case 9:
                    case 11:
                    case 12:
                        return epic ? 43749 : 33658;
                    default: // original - Blue
                        return epic ? 43746 : 33655;
                }
            }
            if (getRace() == RACE_WORGEN)
            {
                // Glyph of the Ursol Chameleon
                if (HasAura(107059))
                {
                    uint32 models[] = { 33652, 33651, 33653, 33654, 33650 };
                    uint32 epicModels[] = { 43752, 43751, 43754, 43755, 43753 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                // Based on Skin color
                uint8 skinColor = ToPlayer()->GetSkinValue();
                // Male
                if (getGender() == GENDER_MALE)
                {
                    switch (skinColor)
                    {
                        case 1: // Brown
                            return epic ? 43752 : 33652;
                        case 2: // Black
                        case 7:
                            return epic ? 43751 : 33651;
                        case 4: // Yellow
                            return epic ? 43754 : 33653;
                        case 3: // White
                        case 5:
                            return epic ? 43755 : 33654;
                        default: // original - Gray
                            return epic ? 43753 : 33650;
                    }
                }
                // Female
                switch (skinColor)
                {
                    case 5: // Brown
                    case 6:
                        return epic ? 43752 : 33652;
                    case 7: // Black
                    case 8:
                        return epic ? 43751 : 33651;
                    case 3: // yellow
                    case 4:
                        return epic ? 43755 : 33654;
                    case 2: // White
                        return epic ? 43754 : 33653;
                    default: // original - Gray
                        return epic ? 43753 : 33650;
                }
            }
            // Based on Skin color
            if (getRace() == RACE_TAUREN)
            {
                // Glyph of the Ursol Chameleon
                if (HasAura(107059))
                {
                    uint32 models[] = { 29418, 29419, 29420, 29421, 2289 };
                    uint32 epicModels[] = { 43741, 43743, 43745, 43744, 43742 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                uint8 skinColor = ToPlayer()->GetSkinValue();
                // Male
                if (getGender() == GENDER_MALE)
                {
                    switch (skinColor)
                    {
                        case 0: // Dark (Black)
                        case 1:
                        case 2:
                            return epic ? 43741 : 29418;
                        case 3: // White
                        case 4:
                        case 5:
                        case 12:
                        case 13:
                        case 14:
                            return epic ? 43743 : 29419;
                        case 9: // Light Brown/Grey
                        case 10:
                        case 11:
                        case 15:
                        case 16:
                        case 17:
                            return epic ? 43745 : 29420;
                        case 18: // Completly White
                            return epic ? 43744 : 29421;
                        default: // original - Brown
                            return epic ? 43742 : 2289;
                    }
                }
                // Female
                switch (skinColor)
                {
                    case 0: // Dark (Black)
                    case 1:
                        return epic ? 43741 : 29418;
                    case 2: // White
                    case 3:
                        return epic ? 43743 : 29419;
                    case 6: // Light Brown/Grey
                    case 7:
                    case 8:
                    case 9:
                        return epic ? 43745 : 29420;
                    case 10: // Completly White
                        return epic ? 43744 : 29421;
                    default: // original - Brown
                        return epic ? 43742 : 2289;
                }
            }

            // Based on Skin color
            if (getRace() == RACE_HIGHMOUNTAIN_TAUREN)
            {
                // Glyph of the Ursol Chameleon
                if (HasAura(107059))
                {
                    uint32 models[] = { 80595, 80594, 80593, 80592, 80591 };
                    uint32 epicModels[] = { 43741, 43743, 43745, 43744, 43742 };
                    return epic ? epicModels[urand(0, 4)] : models[urand(0, 4)];
                }

                uint8 skinColor = ToPlayer()->GetSkinValue();

                switch (skinColor)
                {
                    case 0:
                        return 80592; // red
                    case 1:
                        return 80595; // brown
                    case 2:
                        return 80593; // grey
                    case 3:
                        return 80591; // dark
                    case 4:
                    default:
                        return 80594; // white
                }
            }

            if (Player::TeamForRace(getRace()) == ALLIANCE)
                return 2281;
            return 2289;
        }
        case FORM_FLIGHT:
            if (Player::TeamForRace(getRace()) == ALLIANCE)
                return 20857;
            return 20872;
        case FORM_FLIGHT_EPIC:
        {
            bool isMount = HasAura(231437);
            bool sentinel = HasAura(219062); // Glyph of the Sentinel
            if (Player::TeamForRace(getRace()) == HORDE)
            {
                if (getRace() == RACE_TROLL)
                    return sentinel ? 64331 : (isMount ? 74306 : 37730);
                else if (getRace() == RACE_TAUREN)
                    return sentinel ? 64329 : (isMount ? 74304 : 21244);
                else if (getRace() == RACE_HIGHMOUNTAIN_TAUREN)
                    return sentinel ? 64329 : (isMount ? 74304 : 81439);
            }
            else if (Player::TeamForRace(getRace()) == ALLIANCE)
            {
                if (getRace() == RACE_NIGHTELF)
                    return sentinel ? 64328 : (isMount ? 74305 : 21243);
                else if (getRace() == RACE_WORGEN)
                    return sentinel ? 64330 : (isMount ? 74307 : 37729);
            }
        }
        case FORM_TRAVEL:
            // Glyph of the Cheetah
            if (HasAura(131113))
                return 918;

            // Glyph of the Doe
            else if (HasAura(224122))
                return 70448;

            if (Player::TeamForRace(getRace()) == ALLIANCE)
                return 40816;

            if (getRace() == RACE_HIGHMOUNTAIN_TAUREN)
            {
                if (getGender() == GENDER_MALE)
                    return 81113;
                else
                    return 81440;
            }
            return 45339;
        case FORM_MOONKIN:
        case FORM_MOONKIN2:
        {
            // Glyph of the Stars
            if (HasAura(114301))
                return 0;

            // check Incarnation
            bool epic = HasAura(102560);

            if (getRace() == RACE_TROLL)
                return epic ? 43789 : 37174;
            if (getRace() == RACE_TAUREN || getRace() == RACE_HIGHMOUNTAIN_TAUREN)
                return epic ? 43786 : 15375;
            if (getRace() == RACE_NIGHTELF)
                return epic ? 43790 : 15374;
            if (getRace() == RACE_WORGEN)
                return epic ? 43787 : 37173;
            break;
        }
        case FORM_GHOSTWOLF:
            // Glyph of the Spectral Wolf
            if (HasAura(58135))
                return 55290;
            // Glyph of the Spectral Raptor
            else if (HasAura(212631))
                return 74353;
            break;
        case FORM_AQUA:
            // Glyph of the Orca
            if (HasAura(114333))
                return 40815;
            break;
        case FORM_SPIRITOFREDEMPTION:
            // Glyph of the Val'kyr
            if (HasAura(126094))
                return 67161;
            break;
        case FORM_METAMORPHOSIS:
        case FORM_METAMORPHOSIS2:
            return 25277;
        default:
            break;
    }

    uint32 modelid = 0;
    SpellShapeshiftFormEntry const* formEntry = sSpellShapeshiftFormStore.LookupEntry(form);
    if (formEntry && formEntry->CreatureDisplayID[0])
    {
        // Take the alliance modelid as default
        if (!IsPlayer())
            return formEntry->CreatureDisplayID[0];
        if (Player::TeamForRace(getRace()) == ALLIANCE)
            modelid = formEntry->CreatureDisplayID[0];
        else
            modelid = formEntry->CreatureDisplayID[1];

        // If the player is horde but there are no values for the horde modelid - take the alliance modelid
        if (!modelid && Player::TeamForRace(getRace()) == HORDE)
            modelid = formEntry->CreatureDisplayID[0];
    }

    return modelid;
}

uint32 Unit::GetModelForTotem(uint32 spellId) const
{
    uint8 elementIndex = 0;
    uint8 sRaceIndex = 0;

    switch (spellId)
    {
        case EarthbindTotem: case ResonanceTotem:   case EarthgrabTotem:
        case VoodooTotem:    case EarthenWallTotem: case AncestralProtectionTotem:
        {
            elementIndex = Earth;
            break;
        }
        case SkyfuryTotem: case EmberTotem: case LiquidMagmaTotem:
        {
            elementIndex = Fire;
            break;
        }
        case CloudburstTotem: case HealingStreamTotem: case StormTotem:
        case SpiritLinkTotem: case HealingTideTotem:
        {
            elementIndex = Water;
            break;
        }
        case WindRushTotem: case CounterstrikeTotem: case TailwindTotem:
        case WindfuryTotem: case CapacitorTotem:     case GroundingTotem:
        {
            elementIndex = Air;
            break;
        }
        default:
            return 0;
    }

    switch (getRace())
    {
        case RACE_PANDAREN_ALLIANCE:
        case RACE_PANDAREN_HORDE:
        case RACE_PANDAREN_NEUTRAL:    sRaceIndex = PandarenShaman;/*-------*/break;
        case RACE_ORC:/*-------------*/sRaceIndex = OrcShaman;/*------------*/break;
        case RACE_DWARF:/*-----------*/sRaceIndex = DwarfShaman;/*----------*/break;
        case RACE_TAUREN:/*----------*/sRaceIndex = TaurenShaman;/*---------*/break;
        case RACE_TROLL:/*-----------*/sRaceIndex = TrollShaman;/*----------*/break;
        case RACE_GOBLIN:/*----------*/sRaceIndex = GoblinShaman;/*---------*/break;
        case RACE_DRAENEI:/*---------*/sRaceIndex = DraeneiShaman;/*--------*/break;
        case RACE_HIGHMOUNTAIN_TAUREN: sRaceIndex = HighmountainTaurenShaman; break;
        default:
            return 0;
    }

    return TotemModelId[elementIndex][sRaceIndex];
}

void Unit::JumpTo(float speedXY, float speedZ, float angle)
{
    if (IsCreature())
        GetMotionMaster()->MoveJumpTo(angle, speedXY, speedZ);
    else
        SendMoveKnockBack(ToPlayer(), speedXY, -speedZ, std::cos(angle + GetOrientation()), std::sin(angle + GetOrientation()));
}

void Unit::JumpTo(WorldObject* obj, float speedZ)
{
    float x, y, z;
    obj->GetContactPoint(this, x, y, z);
    float speedXY = GetExactDist2d(x, y) * 10.0f / speedZ;
    GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ);
}

bool Unit::HandleSpellClick(Unit* clicker, int8 seatId)
{
    bool res = false;
    uint32 spellClickEntry = GetVehicleKit() ? GetVehicleKit()->GetCreatureEntry() : GetEntry();
    SpellClickInfoMapBounds clickPair = sObjectMgr->GetSpellClickInfoMapBounds(spellClickEntry);
    for (SpellClickInfoContainer::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr)
    {
        //! First check simple relations from clicker to clickee
        if (!itr->second.IsFitToRequirements(clicker, this))
            continue;

        //! Check database conditions
        ConditionList conds = sConditionMgr->GetConditionsForSpellClickEvent(spellClickEntry, itr->second.spellId);
        ConditionSourceInfo info = ConditionSourceInfo(clicker, this);
        if (!sConditionMgr->IsObjectMeetToConditions(info, conds))
            continue;

        Unit* caster = (itr->second.castFlags & NPC_CLICK_CAST_CASTER_CLICKER) ? clicker : this;
        Unit* target = (itr->second.castFlags & NPC_CLICK_CAST_TARGET_CLICKER) ? clicker : this;
        ObjectGuid origCasterGUID = (itr->second.castFlags & NPC_CLICK_CAST_ORIG_CASTER_OWNER) ? GetOwnerGUID() : clicker->GetGUID();

        SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(itr->second.spellId);
        // if (!spellEntry) should be checked at npc_spellclick load

        if (seatId > -1)
        {
            uint8 i = 0;
            bool valid = false;
            while (i < MAX_SPELL_EFFECTS && !valid)
            {
                if (spellEntry->EffectMask < uint32(1 << i))
                    break;

                if (spellEntry->GetEffect(i, GetSpawnMode())->ApplyAuraName == SPELL_AURA_CONTROL_VEHICLE)
                {
                    valid = true;
                    break;
                }
                ++i;
            }

            if (!valid)
            {
                TC_LOG_ERROR(LOG_FILTER_SQL, "Spell %u specified in npc_spellclick_spells is not a valid vehicle enter aura!", itr->second.spellId);
                continue;
            }

            if (IsInMap(caster))
                caster->CastCustomSpell(itr->second.spellId, SpellValueMod(SPELLVALUE_BASE_POINT0+i), seatId+1, target, false, nullptr, nullptr, origCasterGUID);
            else    // This can happen during Player::_LoadAuras
            {
                float bp0 = seatId + 1.0f;
                if (Aura* aura = Aura::TryRefreshStackOrCreate(spellEntry, MAX_EFFECT_MASK, this, clicker, &bp0, nullptr, origCasterGUID))
                    aura->ApplyForTargets();
            }
            res = true;
        }
        else
        {
            if (IsInMap(caster))
                caster->CastSpell(target, spellEntry, false, nullptr, nullptr, origCasterGUID);
            else if (Aura* aura = Aura::TryRefreshStackOrCreate(spellEntry, MAX_EFFECT_MASK, this, clicker, nullptr, nullptr, origCasterGUID))
                aura->ApplyForTargets();
            res = true;
        }
    }

    Creature* creature = ToCreature();
    if (creature && creature->IsAIEnabled)
        creature->AI()->OnSpellClick(clicker);

    if (Player* player = clicker->ToPlayer())
        if (OutdoorPvP* pvp = player->GetOutdoorPvP())
            pvp->HandleSpellClick(player, this);

    if (!res)
        return false;

    return true;
}

void Unit::EnterVehicle(Unit* base, int8 seatId, bool fullTriggered /*= false*/)
{
    CastCustomSpell(VEHICLE_SPELL_RIDE_HARDCODED, SPELLVALUE_BASE_POINT0, seatId + 1, base, fullTriggered);
}

void Unit::_EnterVehicle(Vehicle* vehicle, int8 seatId, AuraApplication const* aurApp)
{
    // Must be called only from aura handler
    if (!isAlive() || GetVehicleKit() == vehicle || vehicle->GetBase()->IsOnVehicle(this))
        return;

    if (m_vehicle)
    {
        if (m_vehicle == vehicle)
        {
            if (seatId >= 0 && seatId != GetTransSeat())
            {
                TC_LOG_DEBUG(LOG_FILTER_VEHICLES, "EnterVehicle: %u leave vehicle %u seat %d and enter %d.", GetEntry(), m_vehicle->GetBase()->GetEntry(), GetTransSeat(), seatId);
                ChangeSeat(seatId);
            }
            return;
        }
        TC_LOG_DEBUG(LOG_FILTER_VEHICLES, "EnterVehicle: %u exit %u and enter %u.", GetEntry(), m_vehicle->GetBase()->GetEntry(), vehicle->GetBase()->GetEntry());
        ExitVehicle();
    }

    if (aurApp && aurApp->GetRemoveMode())
        return;

    Player* player = ToPlayer();
    if (player)
    {
        if (vehicle->GetBase()->IsPlayer() && player->isInCombat())
        {
            vehicle->GetBase()->RemoveAura(const_cast<AuraApplication*>(aurApp));
            return;
        }
    }

    ASSERT(!m_vehicle);

    vehicle->AddPassenger(this, seatId);
}

bool Unit::CanSwim() const
{
    // Mirror client behavior, if this method returns false then client will not use swimming animation and for players will apply gravity as if there was no water
    if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CANNOT_SWIM))
        return false;

    if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) // is player
        return true;

    if (HasFlag(UNIT_FIELD_FLAGS_2, 0x1000000))
        return false;

    return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_RENAME | UNIT_FLAG_UNK_15);
}

void Unit::ChangeSeat(int8 seatId, bool next)
{
    if (!m_vehicle)
        return;

    // Don't change if current and new seat are identical
    if (seatId == GetTransSeat())
        return;

    SeatMap::const_iterator seat = (seatId < 0 ? m_vehicle->GetNextEmptySeat(GetTransSeat(), next) : m_vehicle->Seats.find(seatId));
    // The second part of the check will only return true if seatId >= 0. @Vehicle::GetNextEmptySeat makes sure of that.
    if (seat == m_vehicle->Seats.end() || seat->second.Passenger.Guid)
        return;

    // Todo: the functions below could be consolidated and refactored to take
    // SeatMap::const_iterator as parameter, to save redundant map lookups.
    m_vehicle->RemovePassenger(this);

    // Set m_vehicle to NULL before adding passenger as adding new passengers is handled asynchronously
    // and someone may call ExitVehicle again before passenger is added to new seat
    Vehicle* veh = m_vehicle;
    m_vehicle = nullptr;
    if (!veh->AddPassenger(this, seatId))
        ASSERT(false);
}

void Unit::ExitVehicle(Position const* exitPosition)
{
    //! This function can be called at upper level code to initialize an exit from the passenger's side.
    if (!m_vehicle || !GetVehicleBase())
        return;

    Map* mapInfo = GetMap();
    if (!mapInfo || mapInfo && !mapInfo->IsMapUnload())
        GetVehicleBase()->RemoveAurasByType(SPELL_AURA_CONTROL_VEHICLE, GetGUID());

    if (m_vehicle)
        if (m_vehicle->ArePassengersSpawnedByAI())
            _ExitVehicle(exitPosition);

    //! To do:
    //! We need to allow SPELL_AURA_CONTROL_VEHICLE unapply handlers in spellscripts
    //! to specify exit coordinates and either store those per passenger, or we need to
    //! init spline movement based on those coordinates in unapply handlers, and
    //! relocate exiting passengers based on Unit::moveSpline data. Either way,
    //! Coming Soon(TM)
}

void Unit::_ExitVehicle(Position const* exitPosition)
{
    /// It's possible m_vehicle is NULL, when this function is called indirectly from @VehicleJoinEvent::Abort.
    /// In that case it was not possible to add the passenger to the vehicle. The vehicle aura has already been removed
    /// from the target in the aforementioned function and we don't need to do anything else at this point.
    if (!m_vehicle || !m_vehicle->GetBase())
        return;

    Unit* veh = m_vehicle->GetBase();
    if (!veh || !veh->IsInWorld())
        return;

    m_vehicle->RemovePassenger(this);
    Player* player = ToPlayer();

    // If player is on mounted duel and exits the mount should immediately lose the duel
    if (player && player->duel && player->duel->isMounted)
        player->DuelComplete(DUEL_FLED);

    // This should be done before dismiss, because there may be some aura removal
    m_vehicle = nullptr;

    SetDisableGravity(false, true);
    SetControlled(false, UNIT_STATE_ROOT);      // SMSG_MOVE_FORCE_UNROOT, ~MOVEMENTFLAG_ROOT

    Position pos;
    if (!exitPosition)                          // Exit position not specified
        veh->GetPosition(&pos);                 // This should use passenger's current position, leaving it as it is now
                                                // because we calculate positions incorrect (sometimes under map)
    else
        pos = *exitPosition;

    if (veh->IsCreature() && veh->IsAIEnabled)
    {
        if (auto creature = veh->ToCreature())
            creature->AI()->CalcExitVehiclePos(pos);
    }
    // Privent unomal relocation out of map while doing some spline at exit from vehicle
    DisableSpline();

    if (player)
    {
        player->ResummonPetTemporaryUnSummonedIfAny();
        player->SummonLastSummonedBattlePet();
    }

    AddUnitState(UNIT_STATE_MOVE);

    if (HasUnitMovementFlag(MOVEMENTFLAG_ROOT))
        SetRooted(false);

    float x, y, z, o;
    pos.GetPosition(x, y, z, o);
    // UpdateGroundPositionZ(pos.GetPositionX(), pos.GetPositionY(), z);

    Transport* transport = veh->GetTransport();
    if (transport)
    {
        transport->CalculatePassengerOffset(x, y, z, &o);
        m_movementInfo.transport.Pos.Relocate(x, y, z, o);

        // This object must be added to transport before adding to map for the client to properly display it
        transport->AddPassenger(this);
    }

    Movement::MoveSplineInit init(*this);
    init.MoveTo(x, y, z);
    init.SetFacing(GetOrientation());
    if (!transport)
        init.SetTransportExit();
    else
    {
        init.SetTransportEnter();
        init.DisableTransportPathTransformations(); // It makes no sense to target global orientation
    }
    init.Launch();

    //GetMotionMaster()->MoveFall();            // Enable this once passenger positions are calculater properly (see above)

    if (veh->HasUnitTypeMask(UNIT_MASK_MINION) && veh->IsCreature())
        if (static_cast<Minion*>(veh)->GetOwner() == this)
            veh->ToCreature()->DespawnOrUnsummon(200);

    if (HasUnitTypeMask(UNIT_MASK_ACCESSORY))
    {
        bool extraDespawn = false;

        if (auto creature = veh->ToCreature())
            extraDespawn = creature->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_VEH_INSTANT_DESPAWN_PASSENGERS;

        // Vehicle just died, we die too
        if (veh->getDeathState() == JUST_DIED && !extraDespawn)
            setDeathState(JUST_DIED);
        // If for other reason we as minion are exiting the vehicle (ejected, master dismounted) - unsummon
        else
            ToTempSummon()->UnSummon(2000); // Approximation
    }

    if (player)
        player->ZoneTeleport(m_zoneId);
}

void Unit::NearTeleportTo(float x, float y, float z, float orientation, bool casting /*= false*/, bool stopMove /*= true*/)
{
    DisableSpline(stopMove);
    if (IsPlayer())
        ToPlayer()->TeleportTo(GetMapId(), x, y, z, orientation, TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (casting ? TELE_TO_SPELL : 0) | (stopMove ? 0 : TELE_TO_NOT_DISABLE_MOVE));
    else
    {
        Position pos = {x, y, z, orientation};
        SendTeleportPacket(pos);
        UpdatePosition(x, y, z, orientation, true);
        UpdateObjectVisibility();
    }
}

bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool teleport, bool stop/* = false*/)
{
    // prevent crash when a bad coord is sent by the client
    if (!Trinity::IsValidMapCoord(x, y, z, orientation))
    {
        TC_LOG_DEBUG(LOG_FILTER_UNITS, "Unit::UpdatePosition(%f, %f, %f) .. bad coordinates!", x, y, z);
        return false;
    }

    bool turn = G3D::fuzzyGt(M_PI - fabs(fabs(GetOrientation() - orientation) - M_PI), 0.0f);
    bool relocated = (teleport || GetPositionX() != x || GetPositionY() != y || GetPositionZ() != z);

    if (turn && !stop)
        RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);

    //if (IsPlayer())
        //TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "UpdatePosition loc(%f %f %f) relocated %i, GetPosition(%f %f %f)", x, y, z, relocated, GetPositionX(), GetPositionY(), GetPositionZ());

    if (relocated)
    {
        if (!stop)
            RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE);

        // move and update visible state if need
        if (GetMap())
        {
            if (IsPlayer())
                GetMap()->PlayerRelocation(ToPlayer(), x, y, z, orientation);
            else
                GetMap()->CreatureRelocation(ToCreature(), x, y, z, orientation);

            // code block for underwater state update
            if (!m_lastUnderWatterPos.IsInDist(this, World::Relocation_UpdateUnderwateLimit))
            {
                m_lastUnderWatterPos = *this;
                UpdateUnderwaterState(GetMap(), x, y, z);
            }
        }
        else
        {
            AddDelayedEvent(100, [this, x, y, z, orientation]() -> void
            {
                if (!GetMap())
                    return;

                if (IsPlayer())
                    GetMap()->PlayerRelocation(ToPlayer(), x, y, z, orientation);
                else
                    GetMap()->CreatureRelocation(ToCreature(), x, y, z, orientation);

                // code block for underwater state update
                if (!m_lastUnderWatterPos.IsInDist(this, World::Relocation_UpdateUnderwateLimit))
                {
                    m_lastUnderWatterPos = *this;
                    UpdateUnderwaterState(GetMap(), x, y, z);
                }
            });
        }

        // Enable check for zone.
        if (!m_zoneUpdateAllow && !m_lastZoneUpdPos.IsInDist(this, World::Relocation_UpdateUnderwateLimit))
        {
            m_zoneUpdateAllow = true;
            m_lastZoneUpdPos = *this;
        }
    }
    else if (turn)
        UpdateOrientation(orientation);

    return (relocated || turn);
}

//! Only server-side orientation update, does not broadcast to client
void Unit::UpdateOrientation(float orientation)
{
    SetOrientation(orientation);
    if (IsVehicle())
        GetVehicleKit()->RelocatePassengers();
}

//! Only server-side height update, does not broadcast to client
void Unit::UpdateHeight(float newZ)
{
    SetPositionH(newZ);
    if (IsVehicle())
        GetVehicleKit()->RelocatePassengers();
}

void Unit::SendThreatUpdate()
{
    if (!getThreatManager().isThreatListEmpty())
    {
        WorldPackets::Combat::ThreatUpdate packet;
        packet.UnitGUID = GetGUID();
        std::list<HostileReference*> const &tlist = getThreatManager().getThreatList();
        packet.ThreatList.reserve(tlist.size());
        for (std::list<HostileReference*>::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr)
        {
            WorldPackets::Combat::ThreatInfo info;
            info.UnitGUID = (*itr)->getUnitGuid();
            info.Threat = int64((*itr)->getThreat());
            packet.ThreatList.push_back(info);
        }
        SendMessageToSet(packet.Write(), false);
    }
}

void Unit::SendHighestThreatUpdate(HostileReference* pHostileReference)
{
    if (!getThreatManager().isThreatListEmpty())
    {
        WorldPackets::Combat::HighestThreatUpdate packet;
        packet.UnitGUID = GetGUID();
        packet.HighestThreatGUID = pHostileReference->getUnitGuid();
        std::list<HostileReference*> const &tlist = getThreatManager().getThreatList();
        packet.ThreatList.reserve(tlist.size());
        for (std::list<HostileReference*>::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr)
        {
            WorldPackets::Combat::ThreatInfo info;
            info.UnitGUID = (*itr)->getUnitGuid();
            info.Threat = int64((*itr)->getThreat());
            packet.ThreatList.push_back(info);
        }
        SendMessageToSet(packet.Write(), false);
    }
}

void Unit::SendThreatClear()
{
    if (!IsInWorld())
        return;

    WorldPackets::Combat::ThreatClear clear;
    clear.UnitGUID = GetGUID();
    SendMessageToSet(clear.Write(), false);
}

void Unit::SendThreatRemove(HostileReference* pHostileReference)
{
    if (!IsInWorld())
        return;

    WorldPackets::Combat::ThreatRemove packet;
    packet.UnitGUID = GetGUID();
    packet.AboutGUID = pHostileReference->getUnitGuid();
    SendMessageToSet(packet.Write(), false);
}

// baseRage means damage taken when attacker = false
void Unit::RewardRage(float baseRage, bool attacker)
{
    float addRage = 0.0f;

    if (attacker)
    {
        addRage = baseRage;
        AddPct(addRage, GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT));
    }

    m_addPower[POWER_RAGE] += int32(addRage * 10);
}

void Unit::StopAttackFaction(uint32 faction_id)
{
    if (Unit* victim = getVictim())
    {
        if (victim->getFactionTemplateEntry()->Faction == faction_id)
        {
            AttackStop();
            if (IsNonMeleeSpellCast(false))
                InterruptNonMeleeSpells(false);

            // melee and ranged forced attack cancel
            if (IsPlayer())
                ToPlayer()->SendAttackSwingError(ATTACK_SWING_ERROR_DEAD_TARGET);
        }
    }

    UnitSet* attackers = getAttackers();
    for (UnitSet::iterator itr = attackers->begin(); itr != attackers->end(); ++itr)
    {
        if ((*itr)->getFactionTemplateEntry()->Faction == faction_id)
            (*itr)->AttackStop();
    }

    getHostileRefManager().deleteReferencesForFaction(faction_id);

    for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
        if(Unit* unit = ObjectAccessor::GetUnit(*this, *itr))
            unit->StopAttackFaction(faction_id);
}

void Unit::OutDebugInfo() const
{
    return; // Not use

    TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::OutDebugInfo");
    TC_LOG_INFO(LOG_FILTER_UNITS, "GUID " UI64FMTD ", entry %u, type %u, name %s", GetGUID().GetCounter(), GetEntry(), (uint32)GetTypeId(), GetName());
    TC_LOG_INFO(LOG_FILTER_UNITS, "OwnerGUID " UI64FMTD ", MinionGUID " UI64FMTD ", CharmerGUID " UI64FMTD ", CharmedGUID " UI64FMTD, GetOwnerGUID().GetCounter(), GetMinionGUID().GetCounter(), GetCharmerGUID().GetCounter(), GetCharmGUID().GetCounter());
    TC_LOG_INFO(LOG_FILTER_UNITS, "In world %u, unit type mask %u", (uint32)(IsInWorld() ? 1 : 0), m_unitTypeMask);
    if (IsInWorld())
        TC_LOG_INFO(LOG_FILTER_UNITS, "Mapid %u", GetMapId());

    std::ostringstream o;
    o << "Summon Slot: ";
    for (uint32 i = 0; i < MAX_SUMMON_SLOT; ++i)
        o << m_SummonSlot[i] << ", ";

    TC_LOG_INFO(LOG_FILTER_UNITS, "%s", o.str().c_str());
    o.str("");

    o << "Controlled List: ";
    for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
        o << *itr << ", ";
    TC_LOG_INFO(LOG_FILTER_UNITS, "%s", o.str().c_str());
    o.str("");

    o << "Aura List: ";
    for (AuraApplicationMap::const_iterator itr = m_appliedAuras.begin(); itr != m_appliedAuras.end(); ++itr)
        o << itr->first << ", ";
    TC_LOG_INFO(LOG_FILTER_UNITS, "%s", o.str().c_str());
    o.str("");

    if (IsVehicle())
    {
        o << "Passenger List: ";
        for (SeatMap::iterator itr = GetVehicleKit()->Seats.begin(); itr != GetVehicleKit()->Seats.end(); ++itr)
            if (Unit* passenger = ObjectAccessor::GetUnit(*GetVehicleBase(), itr->second.Passenger.Guid))
                o << passenger->GetGUID() << ", ";
        TC_LOG_INFO(LOG_FILTER_UNITS, "%s", o.str().c_str());
    }

    if (GetVehicle())
        TC_LOG_INFO(LOG_FILTER_UNITS, "On vehicle %u.", GetVehicleBase()->GetEntry());
}

float Unit::GetRemainingPeriodicAmount(ObjectGuid caster, uint32 spellId, AuraType auraType, uint8 effectIndex, float oldAmount) const
{
    float amount = oldAmount;
    if (AuraEffectList const* periodicAuras = GetAuraEffectsByType(auraType))
    for (AuraEffectList::const_iterator i = periodicAuras->begin(); i != periodicAuras->end(); ++i)
    {
        if ((*i)->GetCasterGUID() != caster || (*i)->GetId() != spellId || (*i)->GetEffIndex() != effectIndex || !(*i)->GetTotalTicks() || !(*i)->GetBase())
            continue;

        int32 duration = (*i)->GetBase()->GetDuration();
        int32 amplitude = (*i)->GetPeriod();
        if (!duration || !amplitude)
            continue;

        uint32 auraTotalTick = (*i)->GetTotalTicks();
        int32 perTimer = amplitude - (*i)->GetPeriodicTimer();
        int32 realDuration = duration;
        uint32 ReallTicksleft = 0;

        duration -= perTimer;
        ReallTicksleft++;

        ReallTicksleft += duration / amplitude;

        if ((*i)->GetBase()->HasAuraAttribute(AURA_ATTR_REAPPLIED_AURA))
            ReallTicksleft++;

        uint32 totalTick = auraTotalTick + 1;

        float amountLeft = (*i)->GetAmount() * ReallTicksleft;
        oldAmount *= auraTotalTick;
        amountLeft += oldAmount;

        if (totalTick > 0 && amountLeft != 0)
            amount = amountLeft / totalTick;
        break;
    }

    return amount;
}

void Unit::SendBreakTarget(Unit* victim)
{
    if (!victim)
        return;

    WorldPackets::Combat::BreakTarget breakTarget;
    breakTarget.UnitGUID = victim->GetGUID();
    SendMessageToSet(breakTarget.Write(), true);
}

bool Unit::IsVisionObscured(Unit* victim)
{
    Aura* victimAura = nullptr;
    Aura* myAura = nullptr;
    Unit* victimCaster = nullptr;
    Unit* myCaster = nullptr;

    if (AuraEffectList const* vAuras = victim->GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING))
    for (AuraEffectList::const_iterator i = vAuras->begin(); i != vAuras->end();)
    {
        victimAura = (*i)->GetBase();
        victimCaster = victimAura->GetCaster();
        break;
    }
    if (AuraEffectList const* myAuras = GetAuraEffectsByType(SPELL_AURA_INTERFERE_TARGETTING))
    for (AuraEffectList::const_iterator i = myAuras->begin(); i != myAuras->end();)
    {
        myAura = (*i)->GetBase();
        myCaster = myAura->GetCaster();
        break;
    }

    if ((myAura != nullptr && myCaster == nullptr) || (victimAura != nullptr && victimCaster == nullptr))
        return false; // Failed auras, will result in crash

    // E.G. Victim is in smoke bomb, and I'm not
    // Spells fail unless I'm friendly to the caster of victim's smoke bomb
    if (victimAura != nullptr && myAura == nullptr)
    {
        if (IsFriendlyTo(victimCaster))
            return false;
        return true;
    }
    // Victim is not in smoke bomb, while I am
    // Spells fail if my smoke bomb aura's caster is my enemy
    if (myAura != nullptr && victimAura == nullptr)
    {
        if (IsFriendlyTo(myCaster))
            return false;
        return true;
    }
    return false;
}

bool Unit::CanFreeMove() const
{
    return !HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) && GetOwnerGUID().IsEmpty();
}

void Unit::SetSplineTimer(uint32 interval)
{
    m_movesplineTimer.Reset(interval);
}

uint32 Unit::GetResistance(SpellSchoolMask mask) const
{
    int32 resist = -1;
    for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
        if (mask & (1 << i) && (resist < 0 || resist > int32(GetResistance(SpellSchools(i)))))
            resist = int32(GetResistance(SpellSchools(i)));

    // resist value will never be negative here
    return uint32(resist);
}

void Unit::SetInFront(Unit const* target)
{
    if (!HasUnitState(UNIT_STATE_CANNOT_TURN))
        SetOrientation(GetAngle(target));
}

void Unit::SetFacingTo(float ori)
{
    SetOrientation(ori); //Update for server

    Movement::MoveSplineInit init(*this);
    init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZ());
    if (GetTransport())
        init.DisableTransportPathTransformations(); // It makes no sense to target global orientation
    init.SetFacing(ori);
    init.Launch();
}

void Unit::SetFacingTo(Unit const* target)
{
    SetInFront(target); //Update for server

    Movement::MoveSplineInit init(*this);
    init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZ());
    init.SetFacing(target);
    init.Launch();
}

void Unit::SetFacingToObject(WorldObject* object)
{
    // never face when already moving
    if (!IsStopped())
        return;

    // TODO: figure out under what conditions creature will move towards object instead of facing it where it currently is.
    SetFacingTo(GetAngle(object));
}

bool Unit::SetWalk(bool enable)
{
    if (enable == IsWalking())
        return false;

    if (enable)
        AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
    else
        RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);

    static OpcodeServer const walkModeTable[2] = {SMSG_MOVE_SPLINE_SET_RUN_MODE, SMSG_MOVE_SPLINE_SET_WALK_MODE};

    WorldPackets::Movement::MoveSplineSetFlag packet(walkModeTable[enable]);
    packet.MoverGUID = GetGUID();
    SendMessageToSet(packet.Write(), true);
    return true;
}

bool Unit::SetDisableGravity(bool disable, bool isPlayer)
{
    if (disable == IsLevitating())
        return false;

    if (!IsPlayer() || isPlayer)
    {
        if (disable)
        {
            AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
            RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_SPLINE_ELEVATION);
            SetFall(false, isPlayer);
        }
        else
        {
            RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
            if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY))
                SetFall(true, isPlayer);
        }
    }

    static OpcodeServer const gravityOpcodeTable[2][2] = {{SMSG_MOVE_SPLINE_ENABLE_GRAVITY, SMSG_MOVE_ENABLE_GRAVITY}, {SMSG_MOVE_SPLINE_DISABLE_GRAVITY, SMSG_MOVE_DISABLE_GRAVITY}};

    if (Player* playerMover = GetPlayerMover())
    {
        WorldPackets::Movement::MoveSetFlag packet(gravityOpcodeTable[disable][1]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
        playerMover->GetCheatData()->OrderSent(gravityOpcodeTable[disable][1]);
    }
    else
    {
        WorldPackets::Movement::MoveSplineSetFlag packet(gravityOpcodeTable[disable][0]);
        packet.MoverGUID = GetGUID();
        SendMessageToSet(packet.Write(), true);
    }

    return true;
}

bool Unit::SetFall(bool enable, bool isPlayer)
{
    if (enable && IsLevitating()) // If disable gravity not use Feather Fall
        return false;

    if (enable == HasUnitMovementFlag(MOVEMENTFLAG_FALLING))
        return false;

    if (!IsPlayer() || isPlayer)
    {
        if (enable)
        {
            AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
            m_movementInfo.fall.SetFallTime(0);
        }
        else
            RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR);
    }

    return true;
}

bool Unit::SetSwim(bool enable)
{
    if (enable == HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING))
        return false;

    if (!IsPlayer())
    {
        if (enable)
            AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
        else
            RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
    }

    static OpcodeServer const swimOpcodeTable[2] = {SMSG_MOVE_SPLINE_STOP_SWIM, SMSG_MOVE_SPLINE_START_SWIM};

    WorldPackets::Movement::MoveSplineSetFlag packet(swimOpcodeTable[enable]);
    packet.MoverGUID = GetGUID();
    SendMessageToSet(packet.Write(), true);

    return true;
}

bool Unit::SetCanFly(bool enable)
{
    //if (enable == HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY))
        //return false;

    if (!IsPlayer())
    {
        if (enable)
        {
            AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
            RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_SPLINE_ELEVATION);
            SetFall(false);
        }
        else
        {
            RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_MASK_MOVING_FLY);
            if (!IsLevitating())
                SetFall(true);
        }
    }

    static OpcodeServer const flyOpcodeTable[2][2] = {{SMSG_MOVE_SPLINE_UNSET_FLYING, SMSG_MOVE_UNSET_CAN_FLY}, {SMSG_MOVE_SPLINE_SET_FLYING, SMSG_MOVE_SET_CAN_FLY}};

    if (!enable && IsPlayer())
        ToPlayer()->SetFallInformation(0, GetPositionZ());

    if (Player* playerMover = GetPlayerMover())
    {
        WorldPackets::Movement::MoveSetFlag packet(flyOpcodeTable[enable][1]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
        playerMover->GetCheatData()->OrderSent(flyOpcodeTable[enable][1]);
    }
    else
    {
        WorldPackets::Movement::MoveSplineSetFlag packet(flyOpcodeTable[enable][0]);
        packet.MoverGUID = GetGUID();
        SendMessageToSet(packet.Write(), true);
    }

    return true;
}

bool Unit::SetWaterWalking(bool enable)
{
    if (enable == HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING))
        return false;

    if (!IsPlayer())
    {
        if (enable)
            AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
        else
            RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
    }

    static OpcodeServer const waterWalkingOpcodeTable[2][2] = {{SMSG_MOVE_SPLINE_SET_LAND_WALK, SMSG_MOVE_SET_LAND_WALK}, {SMSG_MOVE_SPLINE_SET_WATER_WALK, SMSG_MOVE_SET_WATER_WALK}};

    if (Player* playerMover = GetPlayerMover())
    {
        WorldPackets::Movement::MoveSetFlag packet(waterWalkingOpcodeTable[enable][1]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
        playerMover->GetCheatData()->OrderSent(waterWalkingOpcodeTable[enable][1]);
    }
    else
    {
        WorldPackets::Movement::MoveSplineSetFlag packet(waterWalkingOpcodeTable[enable][0]);
        packet.MoverGUID = GetGUID();
        SendMessageToSet(packet.Write(), true);
    }

    return true;
}

bool Unit::SetFeatherFall(bool enable)
{
    if (enable && IsLevitating()) // If disable gravity not use Feather Fall
        return false;

    if (enable == HasUnitMovementFlag(MOVEMENTFLAG_FEATHER_FALL))
        return false;

    if (!IsPlayer())
    {
        if (enable)
            AddUnitMovementFlag(MOVEMENTFLAG_FEATHER_FALL);
        else
            RemoveUnitMovementFlag(MOVEMENTFLAG_FEATHER_FALL);
    }

    static OpcodeServer const featherFallOpcodeTable[2][2] = {{SMSG_MOVE_SPLINE_SET_NORMAL_FALL, SMSG_MOVE_SET_NORMAL_FALL}, {SMSG_MOVE_SPLINE_SET_FEATHER_FALL, SMSG_MOVE_SET_FEATHER_FALL}};

    if (Player* playerMover = GetPlayerMover())
    {
        WorldPackets::Movement::MoveSetFlag packet(featherFallOpcodeTable[enable][1]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
        playerMover->GetCheatData()->OrderSent(featherFallOpcodeTable[enable][1]);
    }
    else
    {
        WorldPackets::Movement::MoveSplineSetFlag packet(featherFallOpcodeTable[enable][0]);
        packet.MoverGUID = GetGUID();
        SendMessageToSet(packet.Write(), true);
    }

    return true;
}

bool Unit::SetHover(bool enable)
{
    if (enable == HasUnitMovementFlag(MOVEMENTFLAG_HOVER))
        return false;

    //! Unconfirmed for players:
    if (!IsPlayer())
    {
        if (enable)
        {
            if (!IsLevitating())
                m_updateFlag |= UPDATEFLAG_PLAY_HOVER_ANIM;
            // SetMiscStandFlags(UNIT_BYTE1_FLAG_HOVER);
            SetPositionH(GetFloatValue(UNIT_FIELD_HOVER_HEIGHT));
        }
        else
        {
            if (!IsLevitating())
                m_updateFlag &= ~UPDATEFLAG_PLAY_HOVER_ANIM;
            // RemoveMiscStandFlags(UNIT_BYTE1_FLAG_HOVER);
            SetPositionH(0.0f);
        }

        if (!IsLevitating())
        {
            WorldPackets::Misc::SetPlayHoverAnim playHoverAnim;
            playHoverAnim.UnitGUID = GetGUID();
            playHoverAnim.PlayHoverAnim = enable;
            SendMessageToSet(playHoverAnim.Write(), true);
        }
    }

    if (enable)
    {
        //! No need to check height on ascent
        if (!IsPlayer())
            AddUnitMovementFlag(MOVEMENTFLAG_HOVER);
        UpdateHeight(GetFloatValue(UNIT_FIELD_HOVER_HEIGHT));
    }
    else
    {
        if (!IsPlayer())
            RemoveUnitMovementFlag(MOVEMENTFLAG_HOVER);
        UpdateHeight(0.0f);
    }

    static OpcodeServer const hoverOpcodeTable[2][2] = {{SMSG_MOVE_SPLINE_UNSET_HOVER, SMSG_MOVE_UNSET_HOVERING}, {SMSG_MOVE_SPLINE_SET_HOVER, SMSG_MOVE_SET_HOVERING}};

    if (Player* playerMover = GetPlayerMover())
    {
        WorldPackets::Movement::MoveSetFlag packet(hoverOpcodeTable[enable][1]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
    }
    else
    {
        WorldPackets::Movement::MoveSplineSetFlag packet(hoverOpcodeTable[enable][0]);
        packet.MoverGUID = GetGUID();
        SendMessageToSet(packet.Write(), true);
    }

    return true;
}

bool Unit::SetCollision(bool disable)
{
    if (disable == HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_COLLISION))
        return false;

    if (disable)
        AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_COLLISION);
    else
        RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_COLLISION);

    static OpcodeServer const collisionOpcodeTable[2][2] = {{SMSG_MOVE_SPLINE_ENABLE_COLLISION, SMSG_MOVE_ENABLE_COLLISION}, {SMSG_MOVE_SPLINE_DISABLE_COLLISION, SMSG_MOVE_DISABLE_COLLISION}};

    if (Player* playerMover = GetPlayerMover())
    {
        WorldPackets::Movement::MoveSetFlag packet(collisionOpcodeTable[disable][1]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
    }
    else
    {
        WorldPackets::Movement::MoveSplineSetFlag packet(collisionOpcodeTable[disable][0]);
        packet.MoverGUID = GetGUID();
        SendMessageToSet(packet.Write(), true);
    }

    return true;
}

void Unit::FocusTarget(Spell const* focusSpell, ObjectGuid target)
{
    // already focused
    if (_focusSpell)
        return;

    _focusSpell = focusSpell;

    SetGuidValue(UNIT_FIELD_TARGET, target);

    if (focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST))
        AddUnitState(UNIT_STATE_ROTATING);
}
void Unit::ReleaseFocus(Spell const* focusSpell)
{
    // focused to something else
    if (focusSpell != _focusSpell)
        return;

    _focusSpell = nullptr;

    if (Unit* victim = getVictim())
        SetGuidValue(UNIT_FIELD_TARGET, victim->GetGUID());
    else
        SetGuidValue(UNIT_FIELD_TARGET, ObjectGuid::Empty);

    if (focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST))
        ClearUnitState(UNIT_STATE_ROTATING);
}

Unit* Unit::GetTargetUnit() const
{
    if (m_curTargetGUID)
        return ObjectAccessor::GetUnit(*this, m_curTargetGUID);
    return nullptr;
}

Unit* Unit::GetLastTargetUnit() const
{
    if (m_lastTargetGUID)
        return ObjectAccessor::GetUnit(*this, m_lastTargetGUID);
    return nullptr;
}

bool Unit::IsSplineEnabled() const
{
    return !movespline->Finalized() && (movespline->Duration() > 1);
}

bool Unit::IsSplineFinished() const
{
    return movespline->Finalized();
}

void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const
{
    if (!target)
        return;

    uint32 valCount = m_valuesCount;
    uint32* flags = UnitUpdateFieldFlags;
    uint32 visibleFlag = UF_FLAG_PUBLIC;

    if (target == this)
        visibleFlag |= UF_FLAG_PRIVATE;
    else if (IsPlayer())
        valCount = PLAYER_FIELD_END_NOT_SELF;

    std::size_t blockCount = UpdateMask::GetBlockCount(valCount);

    Player* plr = GetCharmerOrOwnerPlayerOrPlayerItself();
    if (GetOwnerGUID() == target->GetGUID())
        visibleFlag |= UF_FLAG_OWNER;

    if (HasFlag(OBJECT_FIELD_DYNAMIC_FLAGS, UNIT_DYNFLAG_SPECIALINFO))
        if (HasAuraTypeWithCaster(SPELL_AURA_EMPATHY, target->GetGUID()))
            visibleFlag |= UF_FLAG_SPECIAL_INFO;

    if (plr && plr->IsInSameRaidWith(target))
        visibleFlag |= UF_FLAG_PARTY_MEMBER;

    Creature const* creature = ToCreature();

    *data << uint8(blockCount);
    std::size_t maskPos = data->wpos();
    data->resize(data->size() + blockCount * sizeof(UpdateMask::BlockType));

    for (uint16 index = 0; index < valCount; ++index)
    {
        if (_fieldNotifyFlags & flags[index] ||
            ((flags[index] & visibleFlag) & UF_FLAG_SPECIAL_INFO) ||
            ((updateType == UPDATETYPE_VALUES ? _changesMask[index] : m_uint32Values[index]) && (flags[index] & visibleFlag)) ||
            (index == UNIT_FIELD_AURA_STATE && HasFlag(UNIT_FIELD_AURA_STATE, PER_CASTER_AURA_STATE_MASK)) ||
            (index == OBJECT_FIELD_DYNAMIC_FLAGS && target->needUpdateDynamicFlags))
        {
            UpdateMask::SetUpdateBit(data->contents() + maskPos, index);

            if (index == UNIT_FIELD_NPC_FLAGS)
            {
                uint32 appendValue = m_uint32Values[index];

                if (creature)
                    if (!target->canSeeSpellClickOn(creature))
                        appendValue &= ~UNIT_NPC_FLAG_SPELLCLICK;

                if (appendValue & UNIT_NPC_FLAG_TRAINER)
                    if (!creature->isCanTrainingOf(target, false))
                        appendValue &= ~(UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_TRAINER_CLASS | UNIT_NPC_FLAG_TRAINER_PROFESSION);

                *data << uint32(appendValue);
            }
            else if (index == UNIT_FIELD_NPC_FLAGS2)
            {
                uint32 appendValue = m_uint32Values[index];

                if (appendValue & UNIT_NPC_FLAG2_GARRISON_ARCHITECT)
                    if (Garrison* garr = target->GetGarrisonPtr())
                        if (!garr->GetCountOfBluePrints())
                            appendValue &= ~UNIT_NPC_FLAG2_GARRISON_ARCHITECT;

                if (appendValue & UNIT_NPC_FLAG2_GARRISON_MISSION_NPC)
                    if (Garrison* garr = target->GetGarrisonPtr())
                        if (!garr->GetCountOFollowers())
                            appendValue &= ~UNIT_NPC_FLAG2_GARRISON_MISSION_NPC;

                if (appendValue & UNIT_NPC_FLAG2_SHIPMENT_CRAFTER)
                    if (Garrison* garr = target->GetGarrisonPtr())
                        if (!garr->GetCountOFollowers())
                            appendValue &= ~UNIT_NPC_FLAG2_SHIPMENT_CRAFTER;

                *data << uint32(appendValue);
            }
            else if (index == UNIT_FIELD_AURA_STATE)
            {
                // Check per caster aura states to not enable using a spell in client if specified aura is not by target
                *data << BuildAuraStateUpdateForTarget(target);
            }
            // FIXME: Some values at server stored in float format but must be sent to client in uint32 format
            else if (index >= UNIT_FIELD_ATTACK_ROUND_BASE_TIME && index <= UNIT_FIELD_RANGED_ATTACK_ROUND_BASE_TIME)
            {
                // convert from float to uint32 and send
                *data << uint32(m_floatValues[index] < 0 ? 0 : m_floatValues[index]);
            }
            // there are some float values which may be negative or can't get negative due to other checks
            else if ((index >= UNIT_FIELD_STAT_NEG_BUFF && index < UNIT_FIELD_STAT_NEG_BUFF + MAX_STATS) ||
                (index >= UNIT_FIELD_RESISTANCE_BUFF_MODS_POSITIVE  && index < (UNIT_FIELD_RESISTANCE_BUFF_MODS_POSITIVE + MAX_SPELL_SCHOOL)) ||
                (index >= UNIT_FIELD_RESISTANCE_BUFF_MODS_NEGATIVE  && index < (UNIT_FIELD_RESISTANCE_BUFF_MODS_NEGATIVE + MAX_SPELL_SCHOOL)) ||
                (index >= UNIT_FIELD_STAT_POS_BUFF && index < UNIT_FIELD_STAT_POS_BUFF + MAX_STATS))
            {
                *data << uint32(m_floatValues[index]);
            }
            // Gamemasters should be always able to select units - remove not selectable flag
            else if (index == UNIT_FIELD_FLAGS)
            {
                uint32 appendValue = m_uint32Values[UNIT_FIELD_FLAGS];
                if (target->isGameMaster())
                    appendValue &= ~UNIT_FLAG_NOT_SELECTABLE;

                *data << uint32(appendValue);
            }
            // use modelid_a if not gm, _h if gm for CREATURE_FLAG_EXTRA_TRIGGER creatures
            else if (index == UNIT_FIELD_DISPLAY_ID)
            {
                uint32 displayId = m_uint32Values[index];
                if (creature)
                {
                    CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(m_uint32Values[index]);
                    CreatureTemplate const* cinfo = creature->GetCreatureTemplate();

                    // this also applies for transform auras
                    if (SpellInfo const* transform = sSpellMgr->GetSpellInfo(getTransForm()))
                        for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
                        {
                            if (transform->EffectMask < uint32(1 << i))
                                break;

                            if (transform->Effects[i]->IsAura(SPELL_AURA_TRANSFORM))
                                if (CreatureTemplate const* transformInfo = sObjectMgr->GetCreatureTemplate(transform->Effects[i]->MiscValue))
                                {
                                    cinfo = transformInfo;
                                    break;
                                }
                        }

                    if (modelInfo && modelInfo->hostileId && IsHostileTo(target))
                        displayId = modelInfo->hostileId;
                    else if (cinfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER)
                        if (target->isGameMaster())
                            displayId = cinfo->GetFirstVisibleModel();
                }

                *data << uint32(displayId);
            }
            // hide lootable animation for unallowed players
            else if (index == OBJECT_FIELD_DYNAMIC_FLAGS)
            {
                uint32 dynamicFlags = m_uint32Values[index] & ~UNIT_DYNFLAG_TAPPED;

                if (creature)
                {
                    if (creature->hasLootRecipient() && !creature->isTappedBy(target))
                        dynamicFlags |= UNIT_DYNFLAG_TAPPED;

                    if (!target->isAllowedToLoot(creature))
                        dynamicFlags &= ~UNIT_DYNFLAG_LOOTABLE;
                }

                // TOS: The Desolate Host
                if (dynamicFlags & UNIT_DYNFLAG_NOT_SELECTABLE_MODEL)
                {
                    if (target == this
                        || HasAura(235734) && target->HasAura(235734) || HasAura(235732) && target->HasAura(235732)
                        || target->HasAura(235734) && HasAura(235113) || target->HasAura(235732) && HasAura(235620))
                    {
                        dynamicFlags &= ~UNIT_DYNFLAG_NOT_SELECTABLE_MODEL;
                    }
                }

                // unit UNIT_DYNFLAG_TRACK_UNIT should only be sent to caster of SPELL_AURA_MOD_STALKED auras
                if (dynamicFlags & UNIT_DYNFLAG_TRACK_UNIT)
                    if (!HasAuraTypeWithCaster(SPELL_AURA_MOD_STALKED, target->GetGUID()))
                        dynamicFlags &= ~UNIT_DYNFLAG_TRACK_UNIT;

                *data << dynamicFlags;
            }
            // FG: pretend that OTHER players in own group are friendly ("blue")
            else if (index == UNIT_FIELD_BYTES_2 || index == UNIT_FIELD_FACTION_TEMPLATE)
            {
                if (IsControlledByPlayer() && target != this && (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_LFG) || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_LFR)) && IsInRaidWith(target))
                {
                    FactionTemplateEntry const* ft1 = getFactionTemplateEntry();
                    FactionTemplateEntry const* ft2 = target->getFactionTemplateEntry();
                    if (ft1 && ft2 && !ft1->IsFriendlyTo(*ft2))
                    {
                        if (index == UNIT_FIELD_BYTES_2)
                            // Allow targetting opposite faction in party when enabled in config
                            *data << (m_uint32Values[UNIT_FIELD_BYTES_2] & ((UNIT_BYTE2_FLAG_SANCTUARY /*| UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5*/) << 8)); // this flag is at uint8 offset 1 !!
                        else
                            // pretend that all other HOSTILE players have own faction, to allow follow, heal, rezz (trade wont work)
                            *data << uint32(target->getFaction());
                    }
                    else
                        *data << m_uint32Values[index];
                }
                else
                    *data << m_uint32Values[index];
            }
            else if (index == PLAYER_FIELD_BYTES_6)
            {
                if (target != this && target->IsPlayer() && target->IsSpectator())
                    *data << (m_uint32Values[PLAYER_FIELD_BYTES_6] &~((PLAYER_BYTES_6_OFFSET_ARENA_FACTION) << 8));
                else
                    *data << m_uint32Values[index];
            }
            else
            {
                // send in current format (float as float, uint32 as uint32)
                *data << m_uint32Values[index];
            }
        }
    }
}

bool Unit::SetCanDoubleJump(bool enable)
{
    if (enable == HasExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_DOUBLE_JUMP))
        return false;

    if (enable)
        AddExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_DOUBLE_JUMP);
    else
        RemoveExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_DOUBLE_JUMP);

    static OpcodeServer const doubleJumpOpcodeTable[2] =
    {
        SMSG_MOVE_DISABLE_DOUBLE_JUMP,
        SMSG_MOVE_ENABLE_DOUBLE_JUMP
    };

    if (Player* playerMover = GetPlayerMover())
    {
        WorldPackets::Movement::MoveSetFlag packet(doubleJumpOpcodeTable[enable]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
    }

    return true;
}

void Unit::SendSetVehicleRecId(uint32 vehicleID)
{
    if (Player* player = ToPlayer())
    {
        WorldPackets::Vehicle::MoveSetVehicleRecID moveSetVehicleRec;
        moveSetVehicleRec.MoverGUID = GetGUID();
        moveSetVehicleRec.SequenceIndex = m_sequenceIndex++;
        moveSetVehicleRec.VehicleRecID = vehicleID;
        player->SendDirectMessage(moveSetVehicleRec.Write());
    }
    else
    {
        WorldPackets::Vehicle::SetVehicleRecID setVehicleRec;
        setVehicleRec.VehicleGUID = GetGUID();
        setVehicleRec.VehicleRecID = vehicleID;
        SendMessageToSet(setVehicleRec.Write(), false);
    }
}

bool Unit::SetCanTransitionBetweenSwimAndFly(bool enable)
{
    if (!IsPlayer())
        return false;

    if (enable && HasExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_SWIM_TO_FLY_TRANS))
        return false;

    if (enable)
        AddExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_SWIM_TO_FLY_TRANS);
    else
        RemoveExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_SWIM_TO_FLY_TRANS);

    static OpcodeServer const swimToFlyTransOpcodeTable[2] = {SMSG_MOVE_DISABLE_TRANSITION_BETWEEN_SWIM_AND_FLY, SMSG_MOVE_ENABLE_TRANSITION_BETWEEN_SWIM_AND_FLY};
    if (Player* playerMover = GetPlayerMover())
    {
        WorldPackets::Movement::MoveSetFlag packet(swimToFlyTransOpcodeTable[enable]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
    }

    return true;
}

bool Unit::SetCanTurnWhileFalling(bool enable)
{
    if (enable == HasExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_TURN_WHILE_FALLING))
        return false;

    if (enable)
        AddExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_TURN_WHILE_FALLING);
    else
        RemoveExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_TURN_WHILE_FALLING);

    static OpcodeServer const canTurnWhileFallingOpcodeTable[2] = { SMSG_MOVE_UNSET_CAN_TURN_WHILE_FALLING, SMSG_MOVE_SET_CAN_TURN_WHILE_FALLING };
    if (Player* playerMover = GetPlayerMover())
    {
        WorldPackets::Movement::MoveSetFlag packet(canTurnWhileFallingOpcodeTable[enable]);
        packet.MoverGUID = GetGUID();
        packet.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(packet.Write());
    }

    return true;
}

void Unit::SendTeleportPacket(Position &destPos)
{
    WorldPackets::Movement::MoveUpdateTeleport packet;
    packet.movementInfo = &m_movementInfo;
    packet.movementInfo->RemoteTimeValid = true;
    Unit* broadcastSource = this;

    if (Player* playerMover = GetPlayerMover())
    {
        float x, y, z, o;
        destPos.GetPosition(x, y, z, o);
        if (TransportBase* transportBase = GetDirectTransport())
            transportBase->CalculatePassengerOffset(x, y, z, &o);

        WorldPackets::Movement::MoveTeleport selfPacket;
        selfPacket.MoverGUID = GetGUID();
        if (ObjectGuid transGuid = GetTransGUID())
            selfPacket.TransportGUID = transGuid;
        selfPacket.Pos = Position(x, y, z);
        selfPacket.Facing = o;
        selfPacket.SequenceIndex = m_sequenceIndex++;
        playerMover->SendDirectMessage(selfPacket.Write());

        broadcastSource = playerMover;
    }
    //else
    //{
        packet.movementInfo->Guid = GetGUID(); 
        packet.movementInfo->Pos.Relocate(destPos);
        packet.movementInfo->MoveIndex = getMSTime();
    //}

    broadcastSource->SendMessageToSet(packet.Write(), false);
}

bool Unit::HandleCastWhileWalkingAuraProc(Unit* victim, DamageInfo* /*dmgInfoProc*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, double cooldown)
{
    SpellInfo const* triggeredByAuraSpell = triggeredByAura->GetSpellInfo();

    uint32 triggered_spell_id = 0;
    Unit* target = victim;
    float basepoints0 = 0;

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer()
        ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    // processed charge only counting case
    if (!triggered_spell_id)
        return true;

    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);

    if (!triggerEntry)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleSpellCritChanceAuraProc: Spell %u has non-existing triggered spell %u", triggeredByAuraSpell->Id, triggered_spell_id);
        return false;
    }

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(triggered_spell_id))
        return false;

    if (basepoints0)
        CastCustomSpell(target, triggered_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura);
    else
        CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(triggered_spell_id, 0, getPreciseTime() + cooldown);

    return true;
}

bool Unit::HandleSpellModAuraProc(Unit* victim, DamageInfo* dmgInfoProc, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, double cooldown)
{
    SpellInfo const* triggeredByAuraSpell = triggeredByAura->GetSpellInfo();

    uint32 triggered_spell_id = 0;
    Unit* target = victim;
    float basepoints0 = 0;

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer()
        ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    if (triggeredByAuraSpell->Id == 116680)
    {
        if (procSpell->ClassOptions.SpellClassMask.HasFlag(0, 0, 0, 2))
        {
            if (HasAura(216509))
                return false;
        }
        if (procSpell->ClassOptions.SpellClassMask.HasFlag(33554432))
        {
            if (HasAura(216992))
                return false;
        }
     }

    // processed charge only counting case
    if (!triggered_spell_id)
    {
        SpellProcTriggered(victim, dmgInfoProc, triggeredByAura, procSpell, procFlags, procEx, cooldown);
        return true;
    }

    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);
    if (!triggerEntry)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleSpellCritChanceAuraProc: Spell %u has non-existing triggered spell %u", triggeredByAuraSpell->Id, triggered_spell_id);
        return false;
    }

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(triggered_spell_id))
        return false;

    if (basepoints0)
        CastCustomSpell(target, triggered_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura);
    else
        CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(triggered_spell_id, 0, getPreciseTime() + cooldown);

    return true;
}

bool Unit::HandleIgnoreAurastateAuraProc(Unit* victim, DamageInfo* /*dmgInfoProc*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 /*procEx*/, double cooldown)
{
    SpellInfo const* triggeredByAuraSpell = triggeredByAura->GetSpellInfo();

    uint32 triggered_spell_id = 0;
    Unit* target = victim;
    float basepoints0 = 0;

    Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && IsPlayer()
        ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : nullptr;

    // processed charge only counting case
    if (!triggered_spell_id)
        return true;

    SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id);

    if (!triggerEntry)
    {
        TC_LOG_ERROR(LOG_FILTER_UNITS, "Unit::HandleSpellCritChanceAuraProc: Spell %u has non-existing triggered spell %u", triggeredByAuraSpell->Id, triggered_spell_id);
        return false;
    }

    if (G3D::fuzzyGt(cooldown, 0.0) && HasSpellCooldown(triggered_spell_id))
        return false;

    if (basepoints0)
        CastCustomSpell(target, triggered_spell_id, &basepoints0, nullptr, nullptr, true, castItem, triggeredByAura);
    else
        CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura);

    if (G3D::fuzzyGt(cooldown, 0.0))
        AddSpellCooldown(triggered_spell_id, 0, getPreciseTime() + cooldown);

    return true;
}

void Unit::SendDispelFailed(ObjectGuid const& targetGuid, uint32 spellId, std::list<uint32>& spellList)
{
    WorldPackets::Spells::DispelFailed dispelFailed;
    dispelFailed.CasterGUID = GetGUID();
    dispelFailed.VictimGUID = targetGuid;
    dispelFailed.SpellID = spellId;
    for (auto const& v : spellList)
        dispelFailed.FailedSpellIDs.push_back(v);
    SendMessageToSet(dispelFailed.Write(), true);
}

void Unit::SendDispelLog(ObjectGuid const& unitTargetGuid, uint32 spellId, std::list<uint32>& spellList, bool broke, bool stolen)
{
    WorldPackets::Spells::SpellDispellLog dispellLog;
    dispellLog.TargetGUID = unitTargetGuid;
    dispellLog.CasterGUID = GetGUID();
    dispellLog.SpellID = spellId;
    dispellLog.IsSteal = stolen;
    dispellLog.IsBreak = broke;
    for (auto const& v : spellList)
    {
        WorldPackets::Spells::DispellData data;
        data.SpellID = v;
        data.IsHarmful = !stolen;
        dispellLog.Dispell.push_back(data);
    }

    SendMessageToSet(dispellLog.Write(), true);
}

uint32 Unit::GetNpcDamageTakenInPastSecs(uint32 secs) const
{
    auto const offset = std::min<size_t>(secs, npcDamageTaken_.size());
    return std::accumulate(npcDamageTaken_.begin(), npcDamageTaken_.begin() + offset, 0u);
}

uint32 Unit::GetPlayerDamageTakenInPastSecs(uint32 secs) const
{
    auto const offset = std::min<size_t>(secs, playerDamageTaken_.size());
    return std::accumulate(playerDamageTaken_.begin(), playerDamageTaken_.begin() + offset, 0u);
}

uint32 Unit::GetDamageTakenInPastSecs(uint32 secs, bool fromNpc, bool fromPlayer) const
{
    static_assert(std::is_same<decltype(npcDamageTaken_), decltype(playerDamageTaken_)>::value, "npcDamageTaken_ and playerDamageTaken_ must have same type");

    auto const offset = std::min<size_t>(secs, npcDamageTaken_.size());

    uint32 damage = 0;
    for (size_t i = 0; i < offset; ++i)
    {
        if (fromNpc)
            damage += npcDamageTaken_[i];
        if (fromPlayer)
            damage += playerDamageTaken_[i];
    }

    return damage;
}

int32 Unit::GetHighestExclusiveSameEffectSpellGroupValue(AuraEffect const* aurEff, AuraType auraType, bool checkMiscValue /*= false*/, int32 miscValue /*= 0*/) const
{
    int32 val = 0;
    SpellSpellGroupMapBounds spellGroup = sSpellMgr->GetSpellSpellGroupMapBounds(aurEff->GetSpellInfo()->GetFirstRankSpell()->Id);
    for (SpellSpellGroupMap::const_iterator itr = spellGroup.first; itr != spellGroup.second; ++itr)
    {
        if (sSpellMgr->GetSpellGroupStackRule(itr->second) == SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT)
        {
            if (AuraEffectList const* auraEffList = GetAuraEffectsByType(auraType))
            for (AuraEffectList::const_iterator auraItr = auraEffList->begin(); auraItr != auraEffList->end(); ++auraItr)
            {
                if (aurEff != (*auraItr) && (!checkMiscValue || (*auraItr)->GetMiscValue() == miscValue) && sSpellMgr->IsSpellMemberOfSpellGroup((*auraItr)->GetSpellInfo()->Id, itr->second))
                    if (abs(val) < abs((*auraItr)->GetAmount())) // absolute value only
                        val = (*auraItr)->GetAmount();
            }
        }
    }

    return val;
}

bool Unit::CheckAndIncreaseCastCounter()
{
    uint32 maxCasts = sWorld->getIntConfig(CONFIG_MAX_SPELL_CASTS_IN_CHAIN);

    if (maxCasts && m_castCounter >= maxCasts)
        return false;

    ++m_castCounter;
    return true;
}

bool Unit::RequiresCurrentSpellsToHolyPower(SpellInfo const* spellProto)
{
    if (!spellProto)
        return false;

    if (spellProto->Power.PowerType == POWER_HOLY_POWER)
        return true;
    for (uint8 i = 0; i < CURRENT_MAX_SPELL; ++i)
    {
        Spell* spell = m_currentSpells[i];

        if (!spell)
            continue;

        SpellInfo const* currentSpellInfo = spell->GetSpellInfo();
        if (currentSpellInfo && currentSpellInfo->Power.PowerType == POWER_HOLY_POWER)
            return true;
    }
    return false;
}

uint8 Unit::HandleHolyPowerCost(uint8 cost, SpellPowerEntry const* power)
{
    if (!power->ManaCost)
        return 0;

    uint8 m_holyPowerAdditional = power->OptionalCost;
    if (!cost)
    {
        if (m_holyPowerAdditional)
            m_modForHolyPowerSpell = (m_holyPowerAdditional / power->ManaCost) + 1;
        else
            m_modForHolyPowerSpell = 1;
        return 0;
    }

    if (!GetPower(POWER_HOLY_POWER))
        return 0;

    uint8 m_holyPower = power->ManaCost;
    if (m_holyPowerAdditional)
    {
        m_modForHolyPowerSpell = GetPower(POWER_HOLY_POWER) / power->ManaCost;
        m_holyPower += std::min(uint8(GetPower(POWER_HOLY_POWER) - power->ManaCost), m_holyPowerAdditional);
    }
    else
        m_modForHolyPowerSpell = 1;

    return m_holyPower;
}

bool Unit::IsInDisallowedMountForm() const
{
    if (SpellInfo const* transformSpellInfo = sSpellMgr->GetSpellInfo(getTransForm()))
        if (transformSpellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_MOUNTED))
            return false;
            
    if (GetDisplayId() == GetCustomDisplayId()) // donate morph allow use mount
        return false;

    switch (GetDisplayId()) // hackfix for some morph allow use mount
    {
    case 35107: case 69858: case 68670 :case 68671: case 42851: case 42853: case 42854: case 7550: case 58943: return false; default: break;
    }

    if (ShapeshiftForm form = GetShapeshiftForm())
    {
        SpellShapeshiftFormEntry const* shapeshift = sSpellShapeshiftFormStore.LookupEntry(form);
        if (!shapeshift)
            return true;

        if (form == FORM_MOONKIN || form == FORM_MOONKIN2) // On mookin form allow mount
            return false;

        if (!(shapeshift->Flags & SHAPESHIFT_FORM_IS_NOT_A_SHAPESHIFT))
            return true;
    }

    if (GetDisplayId() == GetNativeDisplayId())
        return false;

    CreatureDisplayInfoEntry const* display = sCreatureDisplayInfoStore.LookupEntry(GetDisplayId());
    if (!display)
        return true;

    CreatureDisplayInfoExtraEntry const* displayExtra = sCreatureDisplayInfoExtraStore.LookupEntry(display->ExtendedDisplayInfoID);
    if (!displayExtra)
        return true;

    CreatureModelDataEntry const* model = sCreatureModelDataStore.LookupEntry(display->ModelID);
    ChrRacesEntry const* race = sChrRacesStore.LookupEntry(displayExtra->DisplayRaceID);

    if (model && !(model->Flags & 0x80))
        if (race && !(race->Flags & 0x4))
            return true;

    return false;
}

void DelayCastEvent::Execute(Unit *caster)
{
    Unit* target = caster;

    if (TargetGUID && target->GetGUID() != TargetGUID)
        target = ObjectAccessor::GetUnit(*caster, TargetGUID);

    if (CasterGUID && caster->GetGUID() != CasterGUID)
        caster = ObjectAccessor::GetUnit(*caster, CasterGUID);

    if (!target || !caster)
        return;

    caster->CastSpell(target, Spell, triggered);
};

bool DelayCastEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
    if (!m_owner.IsInWorld())
        return true;

    Unit* caster = &m_owner;
    Unit* target = &m_owner;

    if (TargetGUID && target->GetGUID() != TargetGUID)
        target = ObjectAccessor::GetUnit(m_owner, TargetGUID);

    if (CasterGUID && caster->GetGUID() != CasterGUID)
        caster = ObjectAccessor::GetUnit(*caster, CasterGUID);

    if (target && caster)
        caster->CastSpell(target, Spell, triggered);

    return true;
};

void Unit::SendSpellCreateVisual(SpellInfo const* spellInfo, Position const* position, Unit* target, uint32 type, uint32 visualId)
{
    bool exist = false;
    bool HasPosition = false;
    Position TargetPosition{0.0f,0.0f,0.0f,0.0f};

    WorldPackets::Spells::PlaySpellVisual visual;
    visual.Source = GetGUID();
    if (target)
        visual.Target = target->GetGUID();
    visual.TravelSpeed = spellInfo->GetMisc(m_spawnMode)->MiscData.Speed;

    if (auto const* spellVisual = sSpellMgr->GetPlaySpellVisualData(spellInfo->Id))
    {
        float chance = 100.0f / spellVisual->size();
        for (auto const& x : *spellVisual)
        {
            if (x.type != type)
                continue;

            if (x.type == SPELL_VISUAL_TYPE_CUSTOM && x.SpellVisualID != visualId)
                continue;

            visual.SpellVisualID = x.SpellVisualID;
            visual.MissReason = x.MissReason;
            visual.ReflectStatus = x.ReflectStatus;
            if (x.TravelSpeed)
                visual.TravelSpeed = x.TravelSpeed;
            visual.SpeedAsTime = x.SpeedAsTime;
            HasPosition = x.HasPosition;
            exist = true;
            if (!type && roll_chance_f(chance))
                break;
        }
    }

    if (!exist)
        return;

    if (visual.TravelSpeed && HasPosition)
    {
        if (target && target != this)
            TargetPosition = target->GetPosition();
        else
            TargetPosition = *position;
    }
    if (visual.TravelSpeed < 0.0f)
        visual.TravelSpeed = 0.0f;

    visual.TargetPosition = TargetPosition;

    SendMessageToSet(visual.Write(), true);
}

void Unit::CancelSpellVisualKit(int32 spellVisualKitID)
{
    WorldPackets::Spells::CancelSpellVisualKit packet;
    packet.Source = GetGUID();
    packet.SpellVisualKitID = spellVisualKitID;
    SendMessageToSetInRange(packet.Write(), GetMap()->GetVisibilityRange(), true);
}

void Unit::SendSpellPlayOrphanVisual(SpellInfo const* spellInfo, bool apply, Position const* position, Unit* target)
{
    if (!spellInfo)
        return;

    auto const* orphan = sSpellMgr->GetSpellVisualPlayOrphan(spellInfo->Id);
    if (!orphan)
        return;

    for (auto const& playOrphan : *orphan)
    {
        if (apply)
        {
            WorldPackets::Spells::PlayOrphanSpellVisual orphanVisual;
            if (target && !playOrphan.type)
                orphanVisual.Target = target->GetGUID();
            orphanVisual.SourceLocation = static_cast<Position>(*this);
            orphanVisual.SourceRotation = playOrphan.SourceOrientation;
            if (target && playOrphan.type)
                orphanVisual.TargetLocation = static_cast<Position>(*target);
            else
                orphanVisual.TargetLocation = *position;
            orphanVisual.SpellVisualID = playOrphan.SpellVisualID;
            orphanVisual.TravelSpeed = playOrphan.TravelSpeed;
            orphanVisual.UnkFloat = playOrphan.UnkFloat;
            orphanVisual.SpeedAsTime = playOrphan.SpeedAsTime;
            SendMessageToSet(orphanVisual.Write(), true);
        }
        else
        {
            SendMessageToSet(WorldPackets::Spells::CancelOrphanSpellVisual(playOrphan.SpellVisualID).Write(), true);

            WorldPackets::Spells::CancelSpellVisual calcelVisual;
            calcelVisual.Source = GetGUID();
            calcelVisual.SpellVisualID = playOrphan.SpellVisualID;
            SendMessageToSet(calcelVisual.Write(), true);
        }
    }
}

void Unit::PlaySpellVisual(Position target, int32 spellVisualID, float travelSpeed, ObjectGuid targetGuid, bool speedAsTime)
{
    WorldPackets::Spells::PlaySpellVisual visual;
    visual.Source = GetGUID();
    visual.Target = targetGuid;
    visual.TravelSpeed = travelSpeed;
    
    visual.SpellVisualID = spellVisualID;
    visual.MissReason = 0;
    visual.ReflectStatus = 0;
    visual.SpeedAsTime = speedAsTime;
    
    visual.TargetPosition = target;
    
    SendMessageToSet(visual.Write(), true);
}

void Unit::PlayOrphanSpellVisual(Position source, Position orientation, Position target, int32 spellVisualID, float travelSpeed, ObjectGuid targetGuid, bool speedAsTime)
{
    WorldPackets::Spells::PlayOrphanSpellVisual orphanVisual;
    orphanVisual.Target = targetGuid;
    orphanVisual.SourceLocation = source;
    orphanVisual.SourceRotation = orientation;
    orphanVisual.TargetLocation = target;
    orphanVisual.SpellVisualID = spellVisualID;
    orphanVisual.TravelSpeed = travelSpeed;
    orphanVisual.UnkFloat = 0.0f;
    orphanVisual.SpeedAsTime = speedAsTime;
    SendMessageToSet(orphanVisual.Write(), true);
}

void Unit::CancelOrphanSpellVisual(int32 spellVisualID)
{
    SendMessageToSetInRange(WorldPackets::Spells::CancelOrphanSpellVisual(spellVisualID).Write(), GetMap()->GetVisibilityRange(), false);
}

void Unit::SendSpellVisualKit(SpellInfo const* spellInfo)
{
    if (auto const* visualkit = sSpellMgr->GetSpellVisualKit(spellInfo->Id))
        for (auto const& visual : *visualkit)
            SendPlaySpellVisualKit(visual.KitRecID, visual.KitType, visual.Duration);
}

void Unit::SendSpellCooldown(int32 spellID, int32 spelCooldown, int32 cooldown /*= 0*/, SpellCooldownFlags flags /*= SpellCooldownFlags::NONE*/)
{
    Player* player = ToPlayer();
    if (!player)
        if (Unit* owner = GetAnyOwner())
            player = owner->ToPlayer();

    if (!player)
        return;

    if (!cooldown)
    {
        if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spelCooldown))
        {
            cooldown = spellInfo->Cooldowns.RecoveryTime;
            if (!cooldown)
                cooldown = spellInfo->Cooldowns.StartRecoveryTime;
            if (!cooldown)
                cooldown = spellInfo->Cooldowns.CategoryRecoveryTime;
        }
    }

    if (!cooldown)
        if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID))
            cooldown = spellInfo->Cooldowns.RecoveryTime;

    if (!cooldown)
        return;

    WorldPackets::Spells::SpellCooldown cooldowns;
    cooldowns.Caster = GetGUID();
    cooldowns.Flags = static_cast<uint8>(flags);
    cooldowns.SpellCooldowns.emplace_back(spellID, cooldown);
    player->SendDirectMessage(cooldowns.Write());
}

void Unit::SetDynamicPassiveSpells(uint32 spellId, uint32 slot)
{
    //from sniff 1-3 enable spell, 0-2 disable
    SetDynamicValue(UNIT_DYNAMIC_FIELD_PASSIVE_SPELLS, slot, spellId);
}

void Unit::SetDynamicWorldEffects(uint32 effect, uint32 slot)
{
    SetDynamicValue(UNIT_DYNAMIC_FIELD_WORLD_EFFECTS, slot, effect);
}

uint32 Unit::GetDynamicPassiveSpells(uint32 slot)
{
    //from sniff 1-3 enable spell, 0-2 disable
    return GetDynamicValue(UNIT_DYNAMIC_FIELD_PASSIVE_SPELLS, slot);
}

void Unit::SendMissileCancel(uint32 spellId, bool reverse /*= true*/)
{
    if (!IsPlayer())
        return;

    WorldPackets::Spells::MissileCancel missicleCancel;
    missicleCancel.OwnerGUID = GetGUID();
    missicleCancel.SpellID = spellId;
    missicleCancel.Reverse = reverse;
    ToPlayer()->SendDirectMessage(missicleCancel.Write());
}

void Unit::SendLossOfControl(Unit* caster, uint32 spellId, uint32 duraction, uint32 rmDuraction, Mechanics mechanic, SpellSchoolMask schoolMask, LossOfControlType type, bool apply)
{
    if (!IsPlayer() || !caster)
        return;

    if (apply)
    {
        WorldPackets::Spells::AddLossOfControl addLoosOfControl;
        addLoosOfControl.Target = GetGUID();
        addLoosOfControl.Mechanic = mechanic;
        addLoosOfControl.Type = type;
        addLoosOfControl.SpellID = spellId;
        addLoosOfControl.Caster = caster->GetGUID();
        addLoosOfControl.Duration = 0/*duraction*/;
        addLoosOfControl.DurationRemaining = rmDuraction;
        addLoosOfControl.LockoutSchoolMask = schoolMask;
        ToPlayer()->SendDirectMessage(addLoosOfControl.Write());
    }
    else
    {
        WorldPackets::Spells::RemoveLossOfControl removeLoosOfControl;
        removeLoosOfControl.Type = type;
        removeLoosOfControl.SpellID = spellId;
        removeLoosOfControl.Caster = caster->GetGUID();
        ToPlayer()->SendDirectMessage(removeLoosOfControl.Write());
    }
}

void Unit::SendDisplayToast(uint32 entry, ToastType type, bool isBonusRoll, uint32 count, DisplayToastMethod method, uint32 questID, Item* item /*= nullptr*/)
{
    if (!IsPlayer())
        return;

    WorldPackets::Loot::LootDisplayToast data;
    data.QuestID = questID;
    data.Quantity = count;
    data.DisplayToastMethod = static_cast<uint8>(method);
    data.Type = static_cast<uint8>(type);
    data.BonusRoll = isBonusRoll;

    switch (type)
    {
        case ToastType::CURRENCY:
            data.CurrencyID = entry;
            break;
        case ToastType::ITEM:
            if (!item)
                return;
            data.Loot.Initialize(item);
            data.SpecID = 0;
            data.ItemQuantity = 0;
            break;
        default:
            break;
    }

    ToPlayer()->SendDirectMessage(data.Write());
}

void Unit::GeneratePersonalLoot(Creature* creature, Player* anyLooter)
{
    if (!anyLooter)
        return;

    uint32 lootid = creature->GetCreatureTemplate()->lootid;

    uint8 LootMethod = anyLooter->GetGroup() ? anyLooter->GetGroup()->GetLootMethod() : PERSONAL_LOOT;
    bool isPersonalGroup = LootMethod == PERSONAL_LOOT;
    Map* map = creature->GetMap();
    uint32 spellForRep = 0;
    uint32 spellForBonusLoot = 0;
    uint32 cooldownid = creature->GetEntry();
    uint32 cooldowntype = TYPE_CREATURE;
    uint32 goEntry = 0;
    GameObject* go = nullptr;

    //Get boss personal loot template
    if (PersonalLootData const* plData = sObjectMgr->GetPersonalLootData(creature->GetEntry(), TYPE_CREATURE))
    {
        spellForRep = plData->lootspellId;
        spellForBonusLoot = plData->bonusspellId;
        goEntry = plData->goEntry;
        if (plData->cooldownid)
        {
            cooldownid = plData->cooldownid;
            lootid = plData->cooldownid;
            cooldowntype = plData->cooldowntype;
        }

        if (plData->cooldowntype == TYPE_ZONE)
        {
            cooldownid = anyLooter->GetCurrentZoneID();
            cooldowntype = plData->cooldowntype;
        }

        if(goEntry)
        {
            cooldownid = goEntry;
            lootid = goEntry;
            cooldowntype = TYPE_GO;
        }
    }

    if (!lootid)
        return;

    if (InstanceScript* script = creature->GetInstanceScript())
        go = script->GetGameObjectByEntry(goEntry);

    if (goEntry && !go)
        return;

    // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GeneratePersonalLoot spellForRep %i spellForBonusLoot %i lootid %i isPersonalGroup %u GetLootMethod %i", spellForRep, spellForBonusLoot, lootid, isPersonalGroup, anyLooter->GetGroup() ? anyLooter->GetGroup()->GetLootMethod() : -1);

    //Generate loot for instance
    if (creature->InInstance())
    {
        //Loot for LFR is personal
        if(map->IsLfr())
        {
            Map::PlayerList const& playerList = map->GetPlayers();
            if (playerList.isEmpty())
                return;

            GuidList looterList;
            for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
                if (Player* player = itr->getSource())
                    if (!player->IsPlayerLootCooldown(cooldownid, cooldowntype, creature->GetMap()->GetDifficultyID()))
                        looterList.push_back(player->GetGUID());

            uint8 lootCount = looterList.size() > 5 ? uint8(looterList.size() / 5) : 1;
            Trinity::Containers::RandomResizeList(looterList, lootCount);

            for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
            {
                if (Player* player = itr->getSource())
                {
                    if (!player->CanContact())
                        continue;

                    if (spellForBonusLoot) //Bonus roll
                        if (!player->IsPlayerLootCooldown(spellForBonusLoot, TYPE_SPELL, creature->GetMap()->GetDifficultyID())) //Bonus loot
                            creature->CastSpell(player, spellForBonusLoot, false);

                    if (player->IsPlayerLootCooldown(cooldownid, cooldowntype, creature->GetMap()->GetDifficultyID()))
                        continue;

                    if (spellForRep) //Gain reputation
                        player->CastSpell(player, spellForRep, false);

                    Loot* loot = &player->personalLoot[creature->GetGUID()];
                    loot->clear();
                    loot->personal = true;
                    loot->objType = 1;
                    auto result = std::find(looterList.begin(), looterList.end(), player->GetGUID());
                    if (result != looterList.end())
                        loot->chance = 100;
                    else
                        loot->chance = 0;

                    // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GeneratePersonalLoot GetEntry %i GetGUID %i player %i", creature->GetEntry(), loot->GetGUID(), player->GetGUID());

                    if (lootid)
                    {
                        switch(cooldowntype)
                        {
                            case TYPE_GO:
                                loot->FillLoot(lootid, LootTemplates_Gameobject, player, true, false, creature);
                                break;
                            case TYPE_CREATURE:
                            case TYPE_ZONE:
                                loot->FillLoot(lootid, LootTemplates_Creature, player, true, false, creature);
                                break;
                        }
                    }

                    loot->AutoStoreItems();
                    player->RemoveLoot(creature->GetGUID());

                    player->AddPlayerLootCooldown(cooldownid, creature->GetGUIDLow(), cooldowntype, true, creature->GetMap()->GetDifficultyID());
                }
            }
            return;
        }
            //Other difficulty
        // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GeneratePersonalLoot Other difficulty is raid loot");
        Map::PlayerList const& playerList = map->GetPlayers();
        if (playerList.isEmpty())
            return;

        GuidList looterList;
        for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
            if (Player* player = itr->getSource())
                if (!player->IsPlayerLootCooldown(cooldownid, cooldowntype, creature->GetMap()->GetDifficultyID()))
                    looterList.push_back(player->GetGUID());

        uint8 lootCount = looterList.size() > 5 ? uint8(looterList.size() / 5) : 1;
        Trinity::Containers::RandomResizeList(looterList, lootCount);

        for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
        {
            if (Player* player = itr->getSource())
            {
                if (!player->CanContact())
                    continue;

                if(spellForBonusLoot) //Bonus roll
                    if (!player->IsPlayerLootCooldown(spellForBonusLoot, TYPE_SPELL, creature->GetMap()->GetDifficultyID())) //Bonus loot
                        creature->CastSpell(player, spellForBonusLoot, false);

                if (isPersonalGroup)
                {
                    Loot* lootPers = &player->personalLoot[go ? go->GetGUID() : creature->GetGUID()];
                    lootPers->clear();
                    lootPers->objType = 1;
                    lootPers->personal = true;
                    lootPers->isOnlyQuest = isPersonalGroup;
                    auto result = std::find(looterList.begin(), looterList.end(), player->GetGUID());
                    if (result != looterList.end())
                        lootPers->chance = 100;
                    else
                        lootPers->chance = 0;

                    switch(cooldowntype)
                    {
                        case TYPE_GO:
                            lootPers->FillLoot(lootid, LootTemplates_Gameobject, player, true, false, creature);
                            break;
                        case TYPE_CREATURE:
                        case TYPE_ZONE:
                        {
                            lootPers->FillLoot(lootid, LootTemplates_Creature, player, true, false, creature);

                            uint32 mingold = creature->GetCreatureTemplate()->mingold;
                            uint32 maxgold = creature->GetCreatureTemplate()->maxgold;
                            creature->CalculateMoney(mingold, maxgold);
                            lootPers->generateMoneyLoot(mingold, maxgold, player->GetMap()->IsDungeon());

                            if (!lootPers->isLooted())
                                creature->AddToLootList(player->GetGUID());

                            player->AddPlayerLootCooldown(cooldownid, creature->GetGUIDLow(), cooldowntype, true, creature->GetMap()->GetDifficultyID());
                            break;
                        }
                    }
                }
            }
        }

        Loot* loot = &(go ? go->loot : creature->loot);
        if (creature->lootForPickPocketed)
            creature->lootForPickPocketed = false;

        loot->clear();
        loot->objType = 1;
        loot->isOnlyQuest = isPersonalGroup;
        switch(cooldowntype)
        {
            case TYPE_GO:
                loot->FillLoot(lootid, LootTemplates_Gameobject, anyLooter, false, false, creature);
                break;
            case TYPE_CREATURE:
            case TYPE_ZONE:
                loot->FillLoot(lootid, LootTemplates_Creature, anyLooter, false, false, creature);
                break;
        }
        // Don`t can take gold
        // loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold);
        return;
    }
    if(creature->isWorldBoss())
    {
        // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GeneratePersonalLoot isWorldBoss");

        if(spellForBonusLoot) //Bonus roll
            creature->CastSpell(creature, spellForBonusLoot, false);
        if(spellForRep) //Gain reputation
            creature->CastSpell(creature, spellForRep, false);
    }
    uint32 questId = creature->GetTrackingQuestID();

    GuidSet* onlyGoldList = creature->GetSaveThreatList();
    GuidList looterList;
    for (GuidSet::const_iterator itr = onlyGoldList->begin(); itr != onlyGoldList->end(); ++itr)
    {
        if (Player* looter = ObjectAccessor::GetPlayer(*creature, (*itr)))
        {
            if(questId && looter->IsQuestRewarded(questId))
                continue;

            if (looter->IsPlayerLootCooldown(cooldownid, cooldowntype, creature->GetMap()->GetDifficultyID()) || creature->GetCurrentZoneID() != looter->GetCurrentZoneID())
                continue;

            looterList.push_back(*itr);
        }
    }

    if (onlyGoldList->empty()) // If creature killed without add to thread add killer
        onlyGoldList->insert(anyLooter->GetGUID());

    uint8 lootCount = onlyGoldList->size() > 5 ? uint8(onlyGoldList->size() / 5) : 1;
    Trinity::Containers::RandomResizeList(looterList, lootCount);

    for (GuidSet::const_iterator itr = onlyGoldList->begin(); itr != onlyGoldList->end(); ++itr)
    {
        if (Player* looter = ObjectAccessor::GetPlayer(*creature, (*itr)))
        {
            if (looter->IsPlayerLootCooldown(cooldownid, cooldowntype, creature->GetMap()->GetDifficultyID()) || creature->GetCurrentZoneID() != looter->GetCurrentZoneID())
                continue;

            Loot* loot = &looter->personalLoot[creature->GetGUID()];
            loot->clear();
            loot->personal = true;
            loot->isRareNext = questId && looter->IsQuestRewarded(questId);
            loot->objType = 1;
            auto result = std::find(looterList.begin(), looterList.end(), (*itr));
            if (result != looterList.end())
                loot->chance = 100;
            else
                loot->chance = 0;

            if (lootid)
            {
                switch(cooldowntype)
                {
                    case TYPE_GO:
                        loot->FillLoot(lootid, LootTemplates_Gameobject, looter, true, false, creature);
                        break;
                    case TYPE_CREATURE:
                    case TYPE_ZONE:
                        loot->FillLoot(lootid, LootTemplates_Creature, looter, true, false, creature);
                        break;
                }
            }

            if(creature->isWorldBoss())
            {
                // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GeneratePersonalLoot IsAutoLoot lootGUID %s", loot->GetGUID().ToString().c_str());
                loot->AutoStoreItems();
                looter->RemoveLoot(creature->GetGUID());
            }
            else if (!loot->isLooted())
                creature->AddToLootList(looter->GetGUID());

            if (creature->isWorldBoss())
                looter->AddPlayerLootCooldown(cooldownid, creature->GetGUIDLow(), cooldowntype, true, creature->GetMap()->GetDifficultyID());

            // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GeneratePersonalLoot lootGUID %s", loot->GetGUID().ToString().c_str());
        }
    }

    // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GeneratePersonalLoot anyLooter %s creature %s", anyLooter->GetGUID().ToString().c_str(), creature->GetGUID().ToString().c_str());
    Loot* loot = &creature->loot;
    if (creature->lootForPickPocketed)
        creature->lootForPickPocketed = false;

    loot->clear();
    loot->objType = 1;
    loot->isOnlyQuest = true;
    loot->FillLoot(lootid, LootTemplates_Creature, anyLooter, false, false, creature);
}

void Unit::GenerateLoot(Creature* creature, Player* anyLooter)
{
    uint32 lootid = creature->GetCreatureTemplate()->lootid;
    if (!lootid)
        return;

    Group* group = anyLooter->GetGroup();
    bool isPersonalGroup = true;
    uint8 LootMethod = group ? group->GetLootMethod() : PERSONAL_LOOT;

    if (creature->isWorldBoss() || LootMethod != PERSONAL_LOOT)
        isPersonalGroup = false;

    Map* map = creature->GetMap();

    if (isPersonalGroup)
    {
        if (creature->InInstance())
        {
            Map::PlayerList const& playerList = map->GetPlayers();
            if (playerList.isEmpty())
                return;

            for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
            {
                if (Player* player = itr->getSource())
                {
                    if (!player->CanContact())
                        continue;

                    Loot* lootPers = &player->personalLoot[creature->GetGUID()];
                    lootPers->clear();
                    lootPers->personal = true;
                    lootPers->isOnlyQuest = true;
                    lootPers->objType = 1;

                    // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GenerateLoot GetEntry %i GetGUID %i player %i", creature->GetEntry(), lootPers->GetGUID(), player->GetGUID());

                    if (lootid)
                        lootPers->FillLoot(lootid, LootTemplates_Creature, player, true, false, creature);

                    uint32 mingold = creature->GetCreatureTemplate()->mingold;
                    uint32 maxgold = creature->GetCreatureTemplate()->maxgold;
                    creature->CalculateMoney(mingold, maxgold);
                    lootPers->generateMoneyLoot(mingold, maxgold, player->GetMap()->IsDungeon());

                    if (!lootPers->isLooted())
                        creature->AddToLootList(player->GetGUID());
                    if (lootPers->isLooted())
                        player->RemoveLoot(creature->GetGUID());
                }
            }
        }
        else
        {
            GuidSet* savethreatlist = creature->GetSaveThreatList();
            if (savethreatlist->empty()) // If creature killed without add to thread add killer
                savethreatlist->insert(anyLooter->GetGUID());

            for (GuidSet::const_iterator itr = savethreatlist->begin(); itr != savethreatlist->end(); ++itr)
            {
                if (Player* looter = ObjectAccessor::GetPlayer(*creature, (*itr)))
                {
                    if (!creature->isTappedBy(looter))
                        continue;

                    Loot* lootPers = &looter->personalLoot[creature->GetGUID()];
                    lootPers->clear();
                    lootPers->personal = true;
                    lootPers->isOnlyQuest = true;
                    lootPers->objType = 1;

                    if (lootid)
                        lootPers->FillLoot(lootid, LootTemplates_Creature, looter, true, false, creature);

                    uint32 mingold = creature->GetCreatureTemplate()->mingold;
                    uint32 maxgold = creature->GetCreatureTemplate()->maxgold;
                    creature->CalculateMoney(mingold, maxgold);
                    lootPers->generateMoneyLoot(mingold, maxgold, looter->GetMap()->IsDungeon());

                    if (!lootPers->isLooted())
                        creature->AddToLootList(looter->GetGUID());
                    if (lootPers->isLooted())
                        looter->RemoveLoot(creature->GetGUID());

                    if (group = looter->GetGroup())
                    {
                        for (GroupReference* iter = group->GetFirstMember(); iter != nullptr; iter = iter->next())
                        {
                            Player* memberLooter = iter->getSource();
                            if (!memberLooter || !memberLooter->CanContact())
                                continue;

                            if (!memberLooter->IsAtGroupRewardDistance(creature))
                                continue;

                            Loot* lootMember = &memberLooter->personalLoot[creature->GetGUID()];
                            if (!lootMember->isLooted())
                                continue;

                            lootMember->clear();
                            lootMember->personal = true;
                            lootMember->isOnlyQuest = true;
                            lootMember->objType = 1;

                            if (lootid)
                                lootMember->FillLoot(lootid, LootTemplates_Creature, memberLooter, true, false, creature);

                            mingold = creature->GetCreatureTemplate()->mingold;
                            maxgold = creature->GetCreatureTemplate()->maxgold;
                            creature->CalculateMoney(mingold, maxgold);
                            lootMember->generateMoneyLoot(mingold, maxgold, memberLooter->GetMap()->IsDungeon());

                            if (!lootMember->isLooted())
                                creature->AddToLootList(memberLooter->GetGUID());
                            if (lootMember->isLooted())
                                memberLooter->RemoveLoot(creature->GetGUID());

                            // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GenerateLoot memberLooter %s", memberLooter->GetGUID().ToString().c_str());
                        }
                    }
                    // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GenerateLoot looter %s", looter->GetGUID().ToString().c_str());
                }
            }
        }
    }
    else
        creature->AddToLootList(anyLooter->GetGUID());

    // TC_LOG_DEBUG(LOG_FILTER_LOOT, "Unit::GenerateLoot anyLooter %s creature %s isPersonalGroup %u", anyLooter->GetGUID().ToString().c_str(), creature->GetGUID().ToString().c_str(), isPersonalGroup);
    Loot* loot = &creature->loot;
    if (creature->lootForPickPocketed)
        creature->lootForPickPocketed = false;

    loot->clear();
    loot->objType = 1;
    loot->isOnlyQuest = isPersonalGroup;
    loot->FillLoot(lootid, LootTemplates_Creature, anyLooter, false, false, creature);
}

void Unit::TargetsWhoHasMyAuras(std::list<Unit*>& targetList, std::vector<uint32>& auraList)
{
    targetList.clear();
    bool findAura = false;
    
    for (auto& j : m_whoHasMyAuras)
    {
        for (auto& v : j.second)
        {
            if (findAura)
                break;

            for (auto& h : auraList)
            {
                if (v == h)
                {
                    findAura = true;
                    break;
                }
            }
        }
        
        if (findAura)
        {
            findAura = false;

            if (Unit* target = ObjectAccessor::GetUnit(*this, j.first))
                targetList.push_back(target);
        }
    }
}

void Unit::TargetsWhoHasMyAuras(std::list<ObjectGuid>& targetList, std::vector<uint32>& auraList)
{
    targetList.clear();
    bool findAura = false;

    for (auto& j : m_whoHasMyAuras)
    {
        for (auto& v : j.second)
        {
            if (findAura)
                break;

            for (auto& h : auraList)
            {
                if (v == h)
                {
                    findAura = true;
                    break;
                }
            }
        }

        if (findAura)
        {
            findAura = false;
            targetList.push_back(j.first);
        }
    }
}

uint32 Unit::TargetsWhoHasMyAuras(std::vector<uint32>& auraList)
{
    uint32 count = 0;
    bool findAura = false;

    for (auto& j : m_whoHasMyAuras)
    {
        findAura = false;

        for (auto& v : j.second)
        {
            if (findAura)
            {
                findAura = false;
                break;
            }

            for (auto& h : auraList)
            {
                if (v == h)
                {
                    findAura = true;
                    count++;
                    break;
                }
            }
        }
    }

    return count;
}

bool Unit::TargetHasMyAura(ObjectGuid const & targetGUID, uint32 auraId)
{
    auto itr = m_whoHasMyAuras.find(targetGUID);

    if (itr != m_whoHasMyAuras.end())
    {
        for (auto& j : itr->second)
        {
            if (j == auraId)
                return true;
        }
    }
    return false;
}

void Unit::RemoveIdInWHMAList(ObjectGuid const& guid, uint32 auraId)
{
    auto itr = m_whoHasMyAuras.find(guid);

    if (itr == m_whoHasMyAuras.end())
        return;

    uint32 index = 0;

    for (auto& j : itr->second)
    {
        if (j == auraId)
        {
            std::lock_guard<std::recursive_mutex> guard(who_aura_lock);

            m_whoHasMyAuras[guid].erase(m_whoHasMyAuras[guid].begin() + index);

            if (m_whoHasMyAuras[guid].empty())
                m_whoHasMyAuras.erase(guid);

            return;
        }
        index++;
    }
}

bool Unit::HasMyAura(uint32 spellId)
{
    if (!IsPlayer())
        return false;

    {
        std::lock_guard<std::recursive_mutex> _lock(my_aura_lock);
        AuraMyMap::const_iterator itr = m_my_Auras.find(spellId);
        if (itr != m_my_Auras.end())
            return true;
    }

    return false;
}

// Use only for one target aura if multipli aura maybe return NULL when remove first aura
// For use in multi aura need rework
Unit::AuraList* Unit::GetMyAura(uint32 spellId)
{
    if (!IsPlayer())
        return nullptr;

    {
        std::lock_guard<std::recursive_mutex> _lock(my_aura_lock);
        AuraMyMap::iterator itr = m_my_Auras.find(spellId);
        if (itr != m_my_Auras.end())
            return &itr->second;
        return nullptr;
    }
}

uint32 Unit::GetCountMyAura(uint32 spellId)
{
    if (!IsPlayer())
        return 0;

    {
        std::lock_guard<std::recursive_mutex> _lock(my_aura_lock);
        AuraMyMap::iterator itr = m_my_Auras.find(spellId);
        if (itr != m_my_Auras.end())
            return itr->second.size();
    }

    return 0;
}

void Unit::AddMyCastAuras(Aura* aura)
{
    if (!IsPlayer())
        return;

    {
        std::lock_guard<std::recursive_mutex> _lock(my_aura_lock);
        m_my_Auras[aura->GetId()].push_back(aura);
    }
}

void Unit::RemoveMyCastAuras(uint32 auraId, Aura* aura)
{
    if (!IsPlayer())
        return;

    {
        std::lock_guard<std::recursive_mutex> _lock(my_aura_lock);
        AuraMyMap::iterator itr = m_my_Auras.find(auraId);
        if (itr != m_my_Auras.end())
        {
            itr->second.remove(aura);
            if (itr->second.empty())
                m_my_Auras.erase(itr);
        }
    }
}

void Unit::RemoveMyAurasDueToSpell(uint32 spellId)
{
    if (!IsPlayer())
        return;

    AuraList _auraList;
    {
        std::lock_guard<std::recursive_mutex> _lock(my_aura_lock);
        AuraMyMap::iterator itr = m_my_Auras.find(spellId);
        if (itr != m_my_Auras.end())
            _auraList = itr->second;
        else
            return;
    }
    for (AuraList::iterator itr = _auraList.begin(); itr != _auraList.end(); ++itr)
        if (Aura* _aura = *itr)
            if (!_aura->IsRemoved())
                _aura->Remove();
}

void Unit::RemovePetAndOwnerAura(uint32 spellId, Unit* /*owner*/)
{
    for (Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
    {
        if(Unit* unit = ObjectAccessor::GetUnit(*this, *itr))
            unit->RemoveAurasDueToSpell(spellId);
    }
    RemoveAurasDueToSpell(spellId);
}

struct CombatLogSender
{
    WorldObject const* _source;
    WorldPackets::CombatLog::CombatLogServerPacket const* _message;
    float const _distSq;

    CombatLogSender(WorldObject const* src, WorldPackets::CombatLog::CombatLogServerPacket* msg, float dist) : _source(src), _message(msg), _distSq(dist * dist)
    {
        msg->Write();
    }

    bool IsInRangeHelper(WorldObject const* object) const;
    void Visit(PlayerMapType &m);
    void Visit(CreatureMapType &m);
    void Visit(DynamicObjectMapType &m);

    template <typename NotInterested>
    void Visit(NotInterested &) { }

    void SendPacket(Player* player)
    {
        if (!player->HaveAtClient(_source))
            return;

        if (player->IsAdvancedCombatLoggingEnabled())
            player->SendDirectMessage(_message->GetFullLogPacket());
        else
            player->SendDirectMessage(_message->GetBasicLogPacket());
    }
};

bool CombatLogSender::IsInRangeHelper(WorldObject const* object) const
{
    //if (!object->IsInPhase(_source))
    //    return false;

    return object->GetExactDist2dSq(_source) <= _distSq;
}

void CombatLogSender::Visit(PlayerMapType& m)
{
    for (auto& target : m)
    {
        if (!IsInRangeHelper(target))
            continue;

        if (target->HasSharedVision())
        {
            auto i = target->GetSharedVisionList().begin();
            for (; i != target->GetSharedVisionList().end(); ++i)
                if ((*i)->m_seer == target)
                    SendPacket(*i);
        }

        if (target->m_seer == target || target->GetVehicle())
            SendPacket(target);
    }
}

void CombatLogSender::Visit(CreatureMapType& m)
{
    for (auto& target : m)
    {
        if (!IsInRangeHelper(target))
            continue;

        if (target->HasSharedVision())
        {
            auto i = target->GetSharedVisionList().begin();
            for (; i != target->GetSharedVisionList().end(); ++i)
                if ((*i)->m_seer == target)
                    SendPacket(*i);
        }
    }
}

void CombatLogSender::Visit(DynamicObjectMapType& m)
{
    for (auto& target : m)
    {
        if (!IsInRangeHelper(target))
            continue;

        if (auto caster = target->GetCaster())
        {
            auto player = caster->ToPlayer();
            if (player && player->m_seer == target)
                SendPacket(player);
        }
    }
}

void Unit::SendCombatLogMessage(WorldPackets::CombatLog::CombatLogServerPacket* combatLog) const
{
    CombatLogSender notifier(this, combatLog, GetVisibilityRange());
    Trinity::VisitNearbyWorldObject(this, GetVisibilityRange(), notifier);
}

Unit* Unit::GetUnitForLinkedSpell(Unit* caster, Unit* target, uint8 type, Unit* explTarget)
{
    switch (type)
    {
        case LINK_UNIT_TYPE_PET: //1
            return static_cast<Unit*>(ToPlayer() ? ToPlayer()->GetPet() : nullptr);
        case LINK_UNIT_TYPE_OWNER: //2
            return GetAnyOwner();
        case LINK_UNIT_TYPE_CASTER: //3
            return caster;
        case LINK_UNIT_TYPE_SELECTED: //4
            return ToPlayer() ? ToPlayer()->GetSelectedUnit() : nullptr;
        case LINK_UNIT_TYPE_TARGET: //5
            return target;
        case LINK_UNIT_TYPE_VICTIM: //6
            return getVictim();
        case LINK_UNIT_TYPE_ATTACKER: //7
            {
                if (Unit* owner = caster->GetAnyOwner())
                    return owner->getAttackerForHelper();
                return caster->getAttackerForHelper();
            }
        case LINK_UNIT_TYPE_NEARBY: //8
            return SelectNearbyTarget(target);
        case LINK_UNIT_TYPE_NEARBY_ALLY: //9
            return SelectNearbyAlly(target);
        case LINK_UNIT_TYPE_ORIGINALCASTER: //10
            return this;
        case LINK_UNIT_TYPE_EXPL_TARGET: //11
            return explTarget;
        case LINK_UNIT_TYPE_LAST_TARGET: // 12
        {
            if (Unit* owner = caster->GetAnyOwner())
                return owner->GetLastTargetUnit();
            return caster->GetLastTargetUnit();
        }
        case LINK_UNIT_TYPE_UNIT_TARGET: //13
            return GetTargetUnit();
        case LINK_UNIT_TYPE_TOP_AGGRO: //14
        {
            if (auto creature = caster->ToCreature())
                if (auto target = creature->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 100.0f, true))
                    return target;
            return getVictim();
        }
    }

    return nullptr;
}

bool Unit::HasAuraLinkedSpell(Unit* caster, Unit* target, uint8 type, int32 hastalent, int32 param)
{
    switch (type)
    {
        case LINK_HAS_AURA_ON_CASTER: // 0
        {
            if(!caster)
                return true;
            if(hastalent > 0)
                return !caster->HasAura(hastalent);
            if(hastalent < 0)
                return caster->HasAura(abs(hastalent));
            break;
        }
        case LINK_HAS_AURA_ON_TARGET: // 1
        {
            if(!target)
                return true;
            if(hastalent > 0)
                return !target->HasAura(hastalent);
            if(hastalent < 0)
                return target->HasAura(abs(hastalent));
            break;
        }
        case LINK_HAS_SPELL_ON_CASTER: // 2
        {
            if(!caster)
                return true;
            if(hastalent > 0)
                return !caster->HasSpell(hastalent);
            if(hastalent < 0)
                return caster->HasSpell(abs(hastalent));
            break;
        }
        case LINK_HAS_AURA_ON_OWNER: // 3
        {
            if(!caster || !caster->GetOwner())
                return true;
            if(hastalent > 0)
                return !caster->GetOwner()->HasAura(hastalent);
            if(hastalent < 0)
                return caster->GetOwner()->HasAura(abs(hastalent));
            break;
        }
        case LINK_HAS_AURATYPE: // 4
            return target ? !target->HasAuraTypeWithCaster(AuraType(hastalent), caster ? caster->GetGUID() : ObjectGuid::Empty) : true;
        case LINK_HAS_MY_AURA_ON_CASTER: // 5
        {
            if(!caster)
                return false;
            if(hastalent > 0)
                return !caster->HasAura(hastalent, caster->GetGUID());
            if(hastalent < 0)
                return caster->HasAura(abs(hastalent), caster->GetGUID());
            break;
        }
        case LINK_HAS_MY_AURA_ON_TARGET: // 6
        {
            if(hastalent > 0)
                return target ? !target->HasAura(hastalent, caster ? caster->GetGUID() : ObjectGuid::Empty) : true;
            if(hastalent < 0)
                return target ? target->HasAura(abs(hastalent), caster ? caster->GetGUID() : ObjectGuid::Empty) : true;
            break;
        }
        case LINK_HAS_AURA_STATE: // 7
        {
            if(hastalent > 0)
                return target ? !target->HasAuraState(AuraStateType(hastalent)) : true;
            if(hastalent < 0)
                return target ? target->HasAuraState(AuraStateType(abs(hastalent))) : true;
            break;
        }
        case LINK_HAS_SPECID: // 8
        {
            if(!caster)
                return true;
            Player* _player = caster->ToPlayer();
            if (!_player)
                return true;
            if(hastalent > 0)
                return _player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) != hastalent;
            if(hastalent < 0)
                return _player->GetUInt32Value(PLAYER_FIELD_CURRENT_SPEC_ID) == abs(hastalent);
            break;
        }
        case LINK_HAS_OBJECT_TYPE: // 9
        {
            if(!target)
                return true;
            if(hastalent > 0)
                return target->GetTypeId() != hastalent;
            if(hastalent < 0)
                return target->GetTypeId() == abs(hastalent);
            break;
        }
        case LINK_HAS_FRIEND: // 10
        {
            if(!target || !caster)
                return true;
            if(hastalent > 0)
                return !target->IsFriendlyTo(caster);
            if(hastalent < 0)
                return target->IsFriendlyTo(caster);
            break;
        }
        case LINK_HAS_ATTACKABLE: // 11
        {
            if(!target || !caster)
                return true;
            if(hastalent > 0)
                return !caster->IsValidAttackTarget(target);
            if(hastalent < 0)
                return caster->IsValidAttackTarget(target);
            break;
        }
        case LINK_HAS_DISTANCE: // 12
        {
            if(!target || !caster)
                return true;
            if(hastalent > 0)
                return int32(caster->GetDistance(target)) < param;
            if(hastalent < 0)
                return int32(caster->GetDistance(target)) > param;
            break;
        }
        case LINK_HAS_AURA_STACK_ON_CASTER: // 13
        {
            if(!caster)
                return true;
            if(hastalent > 0)
            {
                if (Aura* aura = caster->GetAura(hastalent))
                    return aura->GetStackAmount() < param;
                return true;
            }
            if(hastalent < 0)
            {
                if (Aura* aura = caster->GetAura(abs(hastalent)))
                    return aura->GetStackAmount() > param;
                return true;
            }
            break;
        }
        case LINK_HAS_AURA_STACK_ON_TARGET: // 14
        {
            if(!target)
                return true;
            if(hastalent > 0)
            {
                if (Aura* aura = target->GetAura(hastalent))
                    return aura->GetStackAmount() < param;
                return true;
            }
            if(hastalent < 0)
            {
                if (Aura* aura = target->GetAura(abs(hastalent)))
                    return aura->GetStackAmount() > param;
                return true;
            }
            break;
        }
        case LINK_HAS_CASTER_FULL_HP: // 15
        {
            if(!caster)
                return true;
            if(hastalent > 0)
                return !caster->IsFullHealth();
            if(hastalent < 0)
                return caster->IsFullHealth();
            break;
        }
        case LINK_HAS_TARGET_FULL_HP: // 16
        {
            if(!target)
                return true;
            if(hastalent > 0)
                return !target->IsFullHealth();
            if(hastalent < 0)
                return target->IsFullHealth();
            break;
        }
        case LINK_IN_COMBAT: // 17
        {
            if(hastalent > 0)
                return !isInCombat();
            if(hastalent < 0)
                return isInCombat();
            break;
        }
        case LINK_HAS_CASTER_CHECK_HP: // 18
        {
            if(!caster)
                return true;
            if(hastalent > 0)
                return !caster->HealthAbovePct(param);
            if(hastalent < 0)
                return !caster->HealthBelowPct(param);
            break;
        }
        case LINK_HAS_TARGET_CHECK_HP: // 19
        {
            if(!target)
                return true;
            if(hastalent > 0)
                return !target->HealthAbovePct(param);
            if(hastalent < 0)
                return !target->HealthBelowPct(param);
            break;
        }
        case LINK_HAS_CASTER_CLASS: // 20
        {
            if(!caster)
                return true;
            if(hastalent > 0)
                return caster->getClass() != param;
            if(hastalent < 0)
                return caster->getClass() == param;
            break;
        }
        case LINK_HAS_TARGET_CLASS: // 21
        {
            if(!target)
                return true;
            if(hastalent > 0)
                return target->getClass() != param;
            if(hastalent < 0)
                return target->getClass() == param;
            break;
        }
        case LINK_HAS_SPELL_COOLDOWN: // 22
        {
            if(!caster)
                return true;
            if(hastalent > 0)
                return !caster->HasSpellCooldown(param);
            if(hastalent < 0)
                return caster->HasSpellCooldown(param);
            break;
        }
        case LINK_HAS_CASTER_AURA_MECHANIC: // 23
        {
            if(!caster)
                return true;
            if(hastalent > 0)
                return !caster->HasAurasWithMechanic(hastalent);
            if(hastalent < 0)
                return caster->HasAurasWithMechanic(abs(hastalent));
            break;
        }
        case LINK_HAS_TARGET_AURA_MECHANIC: // 24
        {
            if(!target)
                return true;
            if(hastalent > 0)
                return !target->HasAurasWithMechanic(hastalent);
            if(hastalent < 0)
                return target->HasAurasWithMechanic(abs(hastalent));
            break;
        }
        case LINK_HAS_CASTER_IS_MOVING: // 25
        {
            if(!caster)
                return true;
            if(hastalent > 0)
                return !caster->isMoving();
            if(hastalent < 0)
                return caster->isMoving();
            break;
        }
        case LINK_HAS_TARGET_IS_MOVING: // 26
        {
            if(!target)
                return true;
            if(hastalent > 0)
                return !target->isMoving();
            if(hastalent < 0)
                return target->isMoving();
            break;
        }
    }
    return true;
}

SpellInfo const* Unit::GetCastSpellInfo(SpellInfo const* spellInfo) const
{
    std::list<AuraType> auratypelist;
    auratypelist.push_back(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS);
    auratypelist.push_back(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_2);
    Unit::AuraEffectList swaps;
    const_cast<Unit*>(this)->GetAuraEffectsByListType(&auratypelist, swaps);

    for (AuraEffect const* auraEffect : swaps)
    {
        if ((!auraEffect->GetSpellClassMask() && uint32(auraEffect->GetMiscValue()) == spellInfo->Id) ||
            (auraEffect->GetSpellClassMask() && auraEffect->IsAffectingSpell(spellInfo)))
                if (SpellInfo const* newInfo = sSpellMgr->GetSpellInfo(auraEffect->GetAmount()))
                    return newInfo;
    }

    return spellInfo;
}

int32 Unit::CalculateMonkSpellDamage(float coeff)
{
    Player* pPlayer = ToPlayer();
    if (!pPlayer)
        return 0.0f;

    Item* mainItem = pPlayer->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
    Item* offItem = pPlayer->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);

    float MHmin = 0;
    float MHmax = 0;
    float OHmin = 0;
    float OHmax = 0;
    float baseCoef = 0.857143f;

    int32 AP = GetTotalAttackPowerValue(BASE_ATTACK) / 3.5;
    bool dualwield = mainItem && offItem;

    // Main Hand
    if (mainItem)
        if (ItemTemplate const* tempMain = mainItem->GetTemplate())
        {
            tempMain->GetDamage(mainItem->GetItemLevel(GetEffectiveLevel()), MHmin, MHmax);

            MHmin /= tempMain->GetDelay() / 1000.0f;
            MHmax /= tempMain->GetDelay() / 1000.0f;

            if (tempMain->GetInventoryType() == INVTYPE_2HWEAPON)
                baseCoef = 1.0f;
        }

    // Off Hand
    if (offItem)
        if (ItemTemplate const* temp = offItem->GetTemplate())
        {
            temp->GetDamage(offItem->GetItemLevel(GetEffectiveLevel()), OHmin, OHmax);

            if (OHmin && OHmax && temp->GetDelay())
            {
                OHmin /= 2;
                OHmax /= 2;

                OHmin /= temp->GetDelay() / 1000.0f;
                OHmax /= temp->GetDelay() / 1000.0f;
            }
            else
                dualwield = false;
        }

    // DualWield coefficient
    if (dualwield)
    {
        MHmin += OHmin;
        MHmax += OHmax;
    }

    if (HasAuraType(SPELL_AURA_MOD_DISARM))
    {
        MHmin = 0;
        MHmax = 0;
    }

    MHmin *= baseCoef;
    MHmin += AP - 1;

    if (MHmin <= 0) MHmin = 1.0f;

    MHmax *= baseCoef;
    MHmax += AP + 1;

    MHmin *= coeff;
    MHmax *= coeff;

    return irand(int32(MHmin), int32(MHmax));
}

uint32 Unit::GetVirtualItemId(uint32 slot) const
{
    if (slot >= MAX_EQUIPMENT_ITEMS)
        return 0;

    return GetUInt32Value(UNIT_FIELD_VIRTUAL_ITEMS + slot * 2);
}

uint16 Unit::GetVirtualItemAppearanceMod(uint32 slot) const
{
    if (slot >= MAX_EQUIPMENT_ITEMS)
        return 0;

    return GetUInt16Value(UNIT_FIELD_VIRTUAL_ITEMS + slot * 2 + 1, 0);
}

void Unit::SetVirtualItem(uint32 slot, uint32 itemID, uint16 appearanceModID /*= 0*/, uint16 itemVisual /*= 0*/)
{
    if (slot >= MAX_EQUIPMENT_ITEMS)
        return;

    SetUInt32Value(UNIT_FIELD_VIRTUAL_ITEMS + slot * 2, itemID);
    SetUInt16Value(UNIT_FIELD_VIRTUAL_ITEMS + slot * 2 + 1, 0, appearanceModID);
    SetUInt16Value(UNIT_FIELD_VIRTUAL_ITEMS + slot * 2 + 1, 1, itemVisual);
}

void Unit::SetLevel(uint8 lvl)
{
    SetUInt32Value(UNIT_FIELD_LEVEL, lvl);

    if (IsPlayer())
        ToPlayer()->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_LEVEL);

    if (IsPlayer())
        sWorld->UpdateCharacterInfoLevel(ToPlayer()->GetGUIDLow(), lvl);
}

void Unit::SetEffectiveLevel(uint8 lvl)
{
    SetUInt32Value(UNIT_FIELD_EFFECTIVE_LEVEL, lvl);
}

void Unit::SetMaxItemLevel(uint16 ilvl)
{
    SetUInt32Value(UNIT_FIELD_MAX_ITEM_LEVEL, ilvl);
}

void Unit::SetMinItemLevel(uint16 ilvl)
{
    SetUInt32Value(UNIT_FIELD_MIN_ITEM_LEVEL, ilvl);
}

uint8 Unit::getLevel() const
{
    return uint8(GetUInt32Value(UNIT_FIELD_LEVEL));
}

uint8 Unit::GetEffectiveLevel() const
{
    return uint8(GetUInt32Value(UNIT_FIELD_EFFECTIVE_LEVEL) ? GetUInt32Value(UNIT_FIELD_EFFECTIVE_LEVEL) : GetUInt32Value(UNIT_FIELD_LEVEL));
}

uint8 Unit::getLevelForTarget(WorldObject const* target) const
{
    Unit const* unit = target ? target->ToUnit() : nullptr;
    Creature const* creature = ToCreature();
    if (!unit || !creature)
        return GetEffectiveLevel();

    int32 level = GetEffectiveLevel();
    int32 levelTarget = unit->GetEffectiveLevel();
    int32 levelMin = creature->ScaleLevelMin;
    int32 levelMax = creature->ScaleLevelMax;

    // if (creature->isWorldBoss())
        // level = unit->GetEffectiveLevel() + sWorld->getIntConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF);
    // else 
    if (levelMin && levelMax)
    {
        if (levelMin <= levelTarget && levelTarget <= levelMax)
        {
            level = levelTarget;
            if (creature->isWorldBoss())
                level += 1;
        }
        else if(levelMin >= levelTarget)
        {
            level = levelMin;
            if (creature->isWorldBoss())
                level += 1;
        }
        else if(levelMax <= levelTarget)
            level = levelMax;
    }

    if (CreatureTemplate const* cInfo = creature->GetCreatureTemplate())
        level += cInfo->ScaleLevelDelta;

    if (level < 1)
        return 1;
    if (level > 123)
        return 123;
    return uint8(level);
}

uint64 Unit::GetHealth(Unit* victim) const
{
    Creature const* creature = ToCreature();
    if (!victim || !creature)
        return GetHealth();

    if (!creature->ScaleLevelMin || !creature->ScaleLevelMax)
        return GetHealth();

    uint8 level = getLevelForTarget(victim);
    CreatureLevelStat const* scaleStat = const_cast<Creature*>(creature)->GetScaleLevelStat(level);
    if (!scaleStat)
        return GetHealth();

    float health = CalculatePct(static_cast<float>(scaleStat->healthMax), static_cast<float>(GetHealthPct()));
    if (health > 0.0f && health <= 1.0f)
        return 1;

    return uint64(CalculatePct(scaleStat->healthMax, GetHealthPct()) * GetModifierValue(UNIT_MOD_HEALTH, TOTAL_PCT));
}

uint64 Unit::GetMaxHealth(Unit* victim) const
{
    Creature const* creature = ToCreature();
    if (!victim || !creature)
        return GetMaxHealth();

    if (!creature->ScaleLevelMin || !creature->ScaleLevelMax)
        return GetMaxHealth();

    uint8 level = getLevelForTarget(victim);
    CreatureLevelStat const* scaleStat = const_cast<Creature*>(creature)->GetScaleLevelStat(level);
    if (!scaleStat)
        return GetMaxHealth();

    return uint64(scaleStat->healthMax * GetModifierValue(UNIT_MOD_HEALTH, TOTAL_PCT));
}

uint32 Unit::GetArmor(Unit* victim) const
{
    Creature const* creature = ToCreature();
    if (!victim || !creature)
        return GetArmor();

    if (!creature->ScaleLevelMin || !creature->ScaleLevelMax)
        return GetArmor();

    uint8 level = getLevelForTarget(victim);
    CreatureLevelStat const* scaleStat = const_cast<Creature*>(creature)->GetScaleLevelStat(level);
    if (!scaleStat)
        return GetArmor();

    return uint32(scaleStat->BaseArmor * GetModifierValue(UNIT_MOD_ARMOR, TOTAL_PCT));
}

void Unit::SetHealthScal(uint64 val, Unit* victim, uint32 spellId)
{
    Creature const* creature = ToCreature();
    if (!victim || !creature)
    {
        SetHealth(val, spellId);
        return;
    }

    if (!creature->ScaleLevelMin || !creature->ScaleLevelMax)
    {
        SetHealth(val, spellId);
        return;
    }

    CreatureLevelStat const* scaleStat = const_cast<Creature*>(creature)->GetScaleLevelStat(getLevelForTarget(victim));
    if (!scaleStat)
    {
        SetHealth(val, spellId);
        return;
    }

    uint64 healthMax = scaleStat->healthMax * GetModifierValue(UNIT_MOD_HEALTH, TOTAL_PCT);

    SetHealth(CalculatePct(GetMaxHealth(), 100.f * val / healthMax), spellId);
}

float Unit::getScaleForTarget(int32 delta) const
{
    float scale = 1.0f;
    switch (delta)
    {
        case 1: scale = 1.0625f; break;
        case 2: scale = 1.1250f; break;
        case 3: scale = 1.1875f; break;
        case 4: scale = 1.125f; break;
        case 5: scale = 4.0f; break;
        case 6: scale = 4.5f; break;
        case 7: scale = 5.0f; break;
        case 8: scale = 5.5f; break;
        case 9: scale = 6.0f; break;
        default:
            if (delta >= 10)
                scale = 16.500f;
            break;
    }

    return scale;
}

uint32 Unit::GetDamageFromLevelScale(Unit* target, uint32 damage)
{
    if (target)
    {
        int32 plrlvl = 0;
        int32 targlvl = 0;
        bool tapgetPlayer = target->IsPlayer() || target->HasUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER);
        bool casterPlayer = IsPlayer() || HasUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER);

        if (tapgetPlayer)
        {
            plrlvl = target->GetEffectiveLevel();
            targlvl = getLevelForTarget(target);
        }
        else if (casterPlayer)
        {
            plrlvl = GetEffectiveLevel();
            targlvl = target->getLevelForTarget(this);
        }
        int32 lvlCalc = plrlvl - targlvl;

        uint8 plrExp = Trinity::GetExpansionForLevel(plrlvl);
        Creature const* creature = ToCreature();

        if (creature && tapgetPlayer)
        {
            CreatureTemplate const* cInfo = creature->GetCreatureTemplate();
            if (cInfo->RequiredExpansion < plrExp)
            {
                if (lvlCalc > 10)
                    damage /= 10;
            }
            else if (cInfo->ScaleLevelMax && (cInfo->ScaleLevelMax > plrlvl))
                damage *= getScaleForTarget(lvlCalc);
        }
        else if (casterPlayer && target->IsCreature() && !target->HasUnitTypeMask(UNIT_MASK_CREATED_BY_PLAYER))
        {
            if (target->ToCreature()->GetCreatureTemplate()->RequiredExpansion < plrExp)
                damage *= getScaleForTarget(lvlCalc);
        }
    }

    return damage;
}

void Unit::_addAttacker(Unit* pAttacker)                  // must be called only from Unit::Attack(Unit*)
{
    m_attackers.insert(pAttacker);
}

void Unit::_removeAttacker(Unit* pAttacker)               // must be called only from Unit::AttackStop()
{
    m_attackers.erase(UnitHashGen(pAttacker));
}

Unit* Unit::getAttackerForHelper() const                 // If someone wants to help, who to give them
{
    if (getVictim() != nullptr)
        return getVictim();

    if (!const_cast<Unit*>(this)->getAttackers()->empty())
        return *(const_cast<Unit*>(this)->getAttackers()->begin());

    return nullptr;
}

bool Unit::hasAttacker(Unit * pAttacker)
{
    for (UnitSet::iterator  itr = m_attackers.begin(); itr != m_attackers.end(); ++itr)
        if ((*itr) == pAttacker)
            return true;

    return false;
}

/**
* Summons group of creatures. Should be called only by instances of Creature and GameObject classes.
*
* @param group Id of group to summon.
* @param list  List to store pointers to summoned creatures.
*/

void Unit::SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list /*= NULL*/)
{
    ASSERT((IsGameObject() || IsCreature()) && "Only GOs and creatures can summon npc groups!");

    //TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "WorldObject:SummonCreatureGroup");

    std::vector<TempSummonData> const* data = sObjectMgr->GetSummonGroup(GetEntry(), IsGameObject() ? SUMMONER_TYPE_GAMEOBJECT : SUMMONER_TYPE_CREATURE, group);
    if (!data)
        return;

    TempSummonGroupKey groupKey = TempSummonGroupKey(GetEntry(), IsGameObject() ? SUMMONER_TYPE_GAMEOBJECT : SUMMONER_TYPE_CREATURE, group);

    //If group exist derespawn
    if(!tempSummonGroupList[groupKey].empty())
    {
        for (auto const& iter : tempSummonGroupList[groupKey])
            if(Creature* temp = ObjectAccessor::GetCreature(*this, iter))
                temp->DespawnOrUnsummon();

        tempSummonGroupList[groupKey].clear();
    }

    for (std::vector<TempSummonData>::const_iterator itr = data->begin(); itr != data->end(); ++itr)
    {
        switch (itr->actionType)
        {
            case SUMMON_ACTION_TYPE_DEFAULT:
            {
                if (TempSummon* summon = SummonCreature(itr->entry, itr->pos, itr->sumType, itr->time))
                {
                    if (list)
                        list->push_back(summon);

                    tempSummonGroupList[groupKey].push_back(summon->GetGUID());
                }
                break;
            }
            case SUMMON_ACTION_TYPE_ROUND_HOME_POS:
            {
                float stepbyangle = 2*M_PI / itr->count;
                for (uint8 i = 0; i < itr->count; ++i)
                {
                    float x = 0.0f, y = 0.0f;
                    Position posHome{GetPositionX(), GetPositionY(), GetPositionZ()};
                    if(ToCreature())
                        posHome = ToCreature()->GetHomePosition();

                    x = posHome.GetPositionX() + (GetObjectSize() + itr->distance) * std::cos(stepbyangle*i);
                    y = posHome.GetPositionY() + (GetObjectSize() + itr->distance) * std::sin(stepbyangle*i);
                    Trinity::NormalizeMapCoord(x);
                    Trinity::NormalizeMapCoord(y);
                    posHome.Relocate(x, y, GetPositionZ());

                    if (TempSummon* summon = SummonCreature(itr->entry, posHome, itr->sumType, itr->time))
                    {
                        if (list)
                            list->push_back(summon);

                        tempSummonGroupList[groupKey].push_back(summon->GetGUID());
                    }
                }
                break;
            }
            case SUMMON_ACTION_TYPE_ROUND_SUMMONER:
            {
                float stepbyangle = 2*M_PI / itr->count;
                for (uint8 i = 0; i < itr->count; ++i)
                {
                    float x = 0.0f, y = 0.0f;
                    GetNearPoint2D(x, y, itr->distance, stepbyangle*i);
                    Position pos {x, y, GetPositionZ()};

                    if (TempSummon* summon = SummonCreature(itr->entry, pos, itr->sumType, itr->time))
                    {
                        if (list)
                            list->push_back(summon);

                        tempSummonGroupList[groupKey].push_back(summon->GetGUID());
                    }
                }
                break;
            }
        }
    }
}

void Unit::SummonCreatureGroupDespawn(uint8 group, std::list<TempSummon*>* list /*= NULL*/)
{
    if(list)
    {
        for (std::list<TempSummon*>::const_iterator iter = list->begin(); iter != list->end(); ++iter)
            if(TempSummon *const temp = *iter)
                temp->DespawnOrUnsummon();
        return;
    }

    //TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "WorldObject:SummonCreatureGroupDespawn");

    std::vector<TempSummonData> const* data = sObjectMgr->GetSummonGroup(GetEntry(), IsGameObject() ? SUMMONER_TYPE_GAMEOBJECT : SUMMONER_TYPE_CREATURE, group);
    if (!data)
        return;

    TempSummonGroupKey groupKey = TempSummonGroupKey(GetEntry(), IsGameObject() ? SUMMONER_TYPE_GAMEOBJECT : SUMMONER_TYPE_CREATURE, group);
    if (tempSummonGroupList[groupKey].empty())
        return;

    for (auto const& iter : tempSummonGroupList[groupKey])
        if(Creature* temp = ObjectAccessor::GetCreature(*this, iter))
            temp->DespawnOrUnsummon();

    tempSummonGroupList[groupKey].clear();
}

bool Unit::isAnySummons() const
{
    if (IsPlayer())
        return false;

    return (m_unitTypeMask & (UNIT_MASK_GUARDIAN | UNIT_MASK_PET | UNIT_MASK_HUNTER_PET | UNIT_MASK_TOTEM | UNIT_MASK_VEHICLE)) != 0;
}

bool Unit::CanVehicleAI() const
{
    switch (GetEntry())
    {
        case 98035: // Dreadstalkers use defaul AI
        case 107258: // Brood of Nithogg use defaul AI
            return false;
    }
    return true;
}

void Unit::CreateConversation(uint32 id)
{
    Conversation* conversation = new Conversation;
    if (!conversation->CreateConversation(sObjectMgr->GetGenerator<HighGuid::Conversation>()->Generate(), id, this, nullptr, *this))
        delete conversation;
}

void Unit::CreateAreaTrigger(uint32 id, float x, float y, float z, float orientation, uint32 customEntry)
{
    Position pos = {x, y, z, orientation};
    AreaTrigger* areaTrigger = new AreaTrigger;
    if (!areaTrigger->CreateAreaTrigger(sObjectMgr->GetGenerator<HighGuid::AreaTrigger>()->Generate(), id, this, nullptr, pos, pos, nullptr, ObjectGuid::Empty, customEntry))
        delete areaTrigger;
}

void Unit::Clear()
{
    // WorldObject::Clear();

    // set current spells as deletable
    for (uint8 i = 0; i < CURRENT_MAX_SPELL; ++i)
        if (m_currentSpells[i])
        {
            m_currentSpells[i]->SetReferencedFromCurrent(false);
            m_currentSpells[i] = nullptr;
        }

    _DeleteRemovedAuras();

    while (!m_sharedVision.empty())
    {
        if (auto player = m_sharedVision.front())
        {
            m_sharedVision.pop_front();

            if (player->IsSpectator() && player->GetSpectateFrom())
                player->SetViewpoint(player->GetSpectateFrom(), false);
        }
    }

    delete m_charmInfo;
    delete movespline;

    m_powerCost.clear();

    m_Controlled.clear();
    tempSummonGroupList.clear();
    tempSummonList.clear();
    m_whoHasMyAuras.clear();
    m_attackers.clear();
    m_triggeredEffect.clear();
    m_scAuras.clear();
    m_gbAuras.clear();
    m_interruptableAuras.clear();
    m_auraStateAuras.clear();
    m_my_Auras.clear();
    m_visibleAuras.clear();
    m_sharedVision.clear();
    m_appliedAuras.clear();
    m_ownedAuras.clear();
    m_removedAuras.clear();
    m_gameObj.clear();
    m_dynObj.clear();
    m_AreaObj.clear();
    m_ConversationObj.clear();
    m_savethreatlist.clear();
    m_playerTotalDamage.clear();
}

uint32 Unit::GetSize()
{
    uint32 size = 0;

    size += m_sharedVision.size() * sizeof(SharedVisionList);
    size += sizeof(CharmInfo);
    size += sizeof(Movement::MoveSpline);
    size += m_powerCost.size() * sizeof(SpellPowerCost);
    size += m_powerCostSave.size() * sizeof(SpellPowerCost);
    size += m_Controlled.size() * sizeof(ControlList);
    size += tempSummonGroupList.size() * sizeof(TempSummonGroupMap);
    size += tempSummonList.size() * sizeof(TempSummonMap);
    size += m_whoHasMyAuras.size() * sizeof(std::map<ObjectGuid, std::vector<uint32>>);
    size += m_attackers.size() * sizeof(UnitSet);
    size += m_triggeredEffect.size() * sizeof(AuraEffectSet);
    size += m_scAuras.size() * sizeof(AuraList);
    size += m_gbAuras.size() * sizeof(AuraList);
    size += m_interruptableAuras.size() * sizeof(AuraApplicationList);
    size += m_auraStateAuras.size() * sizeof(AuraStateAurasMap);
    size += m_my_Auras.size() * sizeof(AuraMyMap);
    size += m_visibleAuras.size() * sizeof(VisibleAuraContainer);
    size += m_appliedAuras.size() * sizeof(AuraApplicationMap);
    size += m_ownedAuras.size() * sizeof(AuraMap);
    size += m_removedAuras.size() * sizeof(AuraList);
    size += m_gameObj.size() * sizeof(GameObjectList);
    size += m_dynObj.size() * sizeof(DynObjectList);
    size += m_AreaObj.size() * sizeof(AreaObjectList);
    size += m_ConversationObj.size() * sizeof(ConversationObjectList);
    size += m_savethreatlist.size() * sizeof(GuidList);
    size += m_playerTotalDamage.size() * sizeof(std::map<ObjectGuid, uint32>);

    if (Player* player = ToPlayer())
    {
        ChatHandler(player).PSendSysMessage("Unit m_SummonSlot size %u", sizeof(ObjectGuid) * MAX_SUMMON_SLOT);
        ChatHandler(player).PSendSysMessage("Unit m_ObjectSlot size %u", sizeof(ObjectGuid) * MAX_GAMEOBJECT_SLOT);
        ChatHandler(player).PSendSysMessage("Unit LiquidData size %u", sizeof(LiquidData));
        ChatHandler(player).PSendSysMessage("Unit ZLiquidStatus size %u", sizeof(ZLiquidStatus));
        ChatHandler(player).PSendSysMessage("Unit m_everyPower size %u", sizeof(int32) * MAX_POWERS);
        ChatHandler(player).PSendSysMessage("Unit m_addPower size %u", sizeof(int32) * MAX_POWERS);
        ChatHandler(player).PSendSysMessage("Unit m_powerRegenTimer size %u", sizeof(int32) * MAX_POWERS);
        ChatHandler(player).PSendSysMessage("Unit m_powerCombatTimer size %u", sizeof(int32) * MAX_POWERS);
        ChatHandler(player).PSendSysMessage("Unit m_powerFraction size %u", sizeof(float) * MAX_POWERS_PER_CLASS);
        ChatHandler(player).PSendSysMessage("Unit m_threatModifier size %u", sizeof(float) * MAX_SPELL_SCHOOL);
        ChatHandler(player).PSendSysMessage("Unit m_powerCost size %u", sizeof(SpellPowerCost));
        ChatHandler(player).PSendSysMessage("Unit EventProcessor size %u", sizeof(EventProcessor));
        ChatHandler(player).PSendSysMessage("Unit m_spellImmune size %u", sizeof(SpellImmuneList) * MAX_SPELL_IMMUNITY);
        ChatHandler(player).PSendSysMessage("Unit TempSummonGroupMap size %u", sizeof(TempSummonGroupMap));
        ChatHandler(player).PSendSysMessage("Unit TempSummonMap size %u", sizeof(TempSummonMap));
        ChatHandler(player).PSendSysMessage("Unit m_baseRatingValue size %u", sizeof(int16) * MAX_COMBAT_RATING);
        ChatHandler(player).PSendSysMessage("Unit m_attackTimer size %u", sizeof(uint32) * MAX_ATTACK);
        ChatHandler(player).PSendSysMessage("Unit m_createStats size %u", sizeof(float) * MAX_STATS);
        ChatHandler(player).PSendSysMessage("Unit AuraEffectList size %u", sizeof(AuraEffectList*) * TOTAL_AURAS);
        ChatHandler(player).PSendSysMessage("Unit m_auraTypeCount size %u", sizeof(uint8) * TOTAL_AURAS);
        ChatHandler(player).PSendSysMessage("Unit m_auraModifiersGroup size %u", sizeof(float) * MODIFIER_TYPE_END * UNIT_MOD_END);
        ChatHandler(player).PSendSysMessage("Unit ThreatManager size %u", sizeof(ThreatManager));
        ChatHandler(player).PSendSysMessage("Unit TimeTrackerSmall size %u", sizeof(TimeTrackerSmall));
        ChatHandler(player).PSendSysMessage("Unit Diminishing size %u", sizeof(Diminishing));
        ChatHandler(player).PSendSysMessage("Unit FollowerRefManager size %u", sizeof(FollowerRefManager));
        ChatHandler(player).PSendSysMessage("Unit playerDamageTaken_ size %u", sizeof(std::array<uint32, DAMAGE_TRACKING_PERIOD>));
        ChatHandler(player).PSendSysMessage("Unit FunctionProcessor size %u", sizeof(FunctionProcessor));
    }
    return size;
}

void Unit::RemoveMultiSingleTargetAuras(uint32 newPhase)
{
    AuraList& gbAuras = GetMultiSingleTargetAuras();
    for (AuraList::iterator iter = gbAuras.begin(); iter != gbAuras.end();)
    {
        Aura* aura = *iter;
        if (aura)
        {
            WorldObject* _owner = aura->GetOwner();
            if (_owner && _owner != this && !_owner->InSamePhase(newPhase))
            {
                if (Unit* _unit = _owner->ToUnit())
                {
                    uint32 auraID = aura->GetId();
                    _unit->AddDelayedEvent(100, [_unit, auraID]() -> void
                    {
                        if (_unit)
                            _unit->RemoveAurasDueToSpell(auraID);
                    });
                }
                gbAuras.remove(aura);
                iter = gbAuras.begin();
            }
            else
                ++iter;
        }
        else
            ++iter;
    }
}

void Unit::AddSpellTargets(uint32 spellId, ObjectGuid target)
{
    m_spell_targets[spellId].insert(target);
}

void Unit::RemoveSpellTargets(uint32 spellId, ObjectGuid target)
{
    GuidSetInMap::iterator itr = m_spell_targets.find(spellId);
    if (itr != m_spell_targets.end())
        itr->second.erase(target);
}

void Unit::ClearSpellTargets(uint32 spellId)
{
    GuidSetInMap::iterator itr = m_spell_targets.find(spellId);
    if (itr != m_spell_targets.end())
        itr->second.clear();
}

bool Unit::ExistSpellTarget(uint32 spellId, ObjectGuid target)
{
    GuidSetInMap::iterator itr = m_spell_targets.find(spellId);
    if (itr == m_spell_targets.end())
        return false;

    GuidSet::iterator iter = itr->second.find(target);
    if (iter == itr->second.end())
        return false;

    return true;
}

BrawlersGuild* Unit::GetBrawlerGuild()
{
    if (Map* map = GetMap())
        return map->m_brawlerGuild;

    return nullptr;
}

Unit* Unit::GetHati()
{
    for (ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
    {
        if (Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr))
        {
            switch (creature->GetEntry())
            {
                case 100324:
                case 106548:
                case 106549:
                case 106550:
                case 106551:
                    if (creature->isAlive())
                        return creature->ToUnit();
                    break;
                default:
                    break;
            }
        }
    }
    return nullptr;
}

GlobalCooldownMgr& Unit::GetGlobalCooldownMgr()
{
    return m_GlobalCooldownMgr;
}

bool Unit::IsValidDesolateHostTarget(Unit* target, SpellInfo const* spellInfo) const
{
    if (GetMapId() != 1676)
        return true;

    if (spellInfo)
    {
        if (spellInfo->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE))
            return true;
    }

    bool valid = true;

    if (IsPlayer() && target->IsPlayer())
    {
        if (HasAura(235621) && !target->HasAura(235621) || !HasAura(235621) && target->HasAura(235621))
            valid = false;
    }
    else if (IsPlayer() && target->IsCreature())
    {
        switch (target->GetEntry())
        {
            // Real World
            case 118460: case 118715: case 118728: case 119940: case 119941:
            {
                if (HasAura(235621))
                    valid = false;
                break;
            }
            // Spirit World
            case 118462: case 118729: case 118730: case 119938: case 119939: 
            {
                if (!HasAura(235621))
                    valid = false;
                break;
            }
            default:
                break;
        }
    }
    return valid;
}

bool Unit::IsUnitMeetCondition(uint32 visualId, SpellInfo const* spellInfo/*= null*/) const
{
    switch (visualId)
    {
        case 121258:
            if (GetEntry() != 123906)
                return false;
            break;
        case 126679:
            if (GetEntry() != 123921)
                return false;
            break;
        case 126685:
            if (GetEntry() != 123929)
                return false;
            break;
        default:
            break;
    }
    return true;
}

