/*
REWRITTEN FROM SCRATCH BY PUSSYWIZARD, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "trial_of_the_crusader.h"
#include "SpellScript.h"
#include "PassiveAI.h"
#include "Player.h"

enum AnubTexts
{
    SAY_INTRO               = 0,
    SAY_AGGRO               = 1,
    EMOTE_SUBMERGE          = 2,
    EMOTE_BURROWER          = 3,
    SAY_EMERGE              = 4,
    SAY_LEECHING_SWARM      = 5,
    EMOTE_LEECHING_SWARM    = 6,
    SAY_KILL_PLAYER         = 7,
    SAY_DEATH               = 8,

    EMOTE_SPIKE             = 0,
};

enum AnubNPCs
{
    NPC_FROST_SPHERE                            = 34606,
    NPC_BURROW                                  = 34862,
    NPC_BURROWER                                = 34607,
    NPC_SCARAB                                  = 34605,
    NPC_SPIKE                                   = 34660,
};

const Position AnubLocs[]=
{
    // scarab's beginning pos
    {722.65f, 135.41f, 142.16f, M_PI},

    // churning ground spawns
    {694.886353f, 102.484665f, 142.119614f, 0},
    {731.987244f, 83.3824690f, 142.119614f, 0},
    {694.500671f, 185.363968f, 142.117905f, 0},
    {740.184509f, 193.443390f, 142.117584f, 0},

    // sphere spawns
    { 786.6439f, 108.2498f, 155.6701f, 0 },
    { 806.8429f, 150.5902f, 155.6701f, 0 },
    { 759.1386f, 163.9654f, 155.6701f, 0 },
    { 744.3701f, 119.5211f, 155.6701f, 0 },
    { 710.0211f, 120.8152f, 155.6701f, 0 },
    { 706.6383f, 161.5266f, 155.6701f, 0 },
};

class HideNpcEvent : public BasicEvent
{
public:
    HideNpcEvent(Creature& owner) : _owner(owner) { }

    bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/)
    {
        _owner.SetVisible(false);
        return true;
    }

private:
    Creature& _owner;
};

enum AnubSpells
{
    // Anub'arak
    SPELL_SUBMERGE                              = 65981,
    SPELL_EMERGE                                = 65982,
    SPELL_BERSERK                               = 26662,

    SPELL_FREEZING_SLASH                        = 66012,
    SPELL_PENETRATING_COLD                      = 66013,
    SPELL_SUMMON_SCARAB                         = 66339,
    SPELL_SUMMON_BURROWER                       = 66332,
    SPELL_LEECHING_SWARM                        = 66118,

    // Anub'arak Pursue
    SPELL_MARK                                  = 67574,
    SPELL_SUMMON_SPIKE                          = 66169,
    SPELL_SPIKE_SPEED1                          = 65920,
    SPELL_SPIKE_TRAIL                           = 65921,
    SPELL_SPIKE_SPEED2                          = 65922,
    SPELL_SPIKE_SPEED3                          = 65923,
    SPELL_SPIKE_FAIL                            = 66181,
    SPELL_SPIKE_TELE                            = 66170,
    SPELL_IMPALE                                = 65919,

    // Scarab
    SPELL_DETERMINATION                         = 66092,
    SPELL_ACID_MANDIBLE                         = 65774,

    // Burrow
    SPELL_CHURNING_GROUND                       = 66969,

    // Frost Sphere
    SPELL_FROST_SPHERE                          = 67539,
    SPELL_PERMAFROST                            = 66193,
    SPELL_PERMAFROST_VISUAL                     = 65882,

    // Burrower
    SPELL_SPIDER_FRENZY                         = 66128,
    SPELL_EXPOSE_WEAKNESS                       = 67720,
    SPELL_SHADOW_STRIKE                         = 66134,
    SPELL_SUBMERGE_EFFECT                       = 53421,
    SPELL_EMERGE_EFFECT                         = 66947,
};

enum AnubEvents
{
    EVENT_RESPAWN_SPHERE = 1,
    EVENT_ENRAGE,
    EVENT_SPELL_FREEZING_SLASH,
    EVENT_SPELL_PENETRATING_COLD,
    EVENT_SUMMON_NERUBIAN,
    EVENT_SUBMERGE,
    EVENT_EMERGE,
    EVENT_EMERGE_2,
    EVENT_SPELL_SUMMON_SPIKE,
    EVENT_SPELL_SHADOW_STRIKE,
    EVENT_SUMMON_SCARAB,
};

#define SUBMERGE_INTERVAL   80000
#define EMERGE_INTERVAL     60000

class boss_anubarak_trial : public CreatureScript
{
public:
    boss_anubarak_trial() : CreatureScript("boss_anubarak_trial") {}

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new boss_anubarak_trialAI(pCreature);
    };

    struct boss_anubarak_trialAI : public ScriptedAI
    {
        boss_anubarak_trialAI(Creature* pCreature) : ScriptedAI(pCreature), summons(me)
        {
            pInstance = (InstanceScript*)pCreature->GetInstanceScript();
            events.Reset();
            bIntro = false;
            bPhase3 = false;
            me->ApplySpellImmune(0, IMMUNITY_ID, RAID_MODE(66193,67855,67856,67857), true);
            me->m_SightDistance = 90.0f; // for MoveInLineOfSight distance
        }

        InstanceScript* pInstance;
        SummonList summons;
        EventMap events;
        bool bIntro;
        bool bPhase3;
        uint64 SphereGUID[6];
        uint64 BurrowGUID[4];

        void Reset()
        {
            me->SetStandState(UNIT_STAND_STATE_SUBMERGED);
            me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
            summons.DespawnAll();
            for( uint8 i=0; i<10; ++i )
            {
                float angle = rand_norm()*2*M_PI;
                float dist = rand_norm()*40.0f;
                if( Creature* c = me->SummonCreature(NPC_SCARAB, AnubLocs[0].GetPositionX()+cos(angle)*dist, AnubLocs[0].GetPositionY()+sin(angle)*dist, AnubLocs[0].GetPositionZ(), 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000) )
                {
                    c->setFaction(31);
                    c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
                    c->GetMotionMaster()->MoveRandom(15.0f);
                }
            }
        }

        void DoAction(int32 param)
        {
            switch( param )
            {
                case -1:
                    summons.DespawnAll();
                    break;
            }
        }

        void EnterCombat(Unit* /*who*/)
        {
            me->setActive(true);
            events.Reset();
            events.RescheduleEvent(EVENT_ENRAGE, 600000);
            events.RescheduleEvent(EVENT_SPELL_FREEZING_SLASH, urand(7000,15000));
            events.RescheduleEvent(EVENT_SPELL_PENETRATING_COLD, urand(15000,20000));
            events.RescheduleEvent(EVENT_SUMMON_NERUBIAN, urand(5000,8000));
            events.RescheduleEvent(EVENT_SUBMERGE, SUBMERGE_INTERVAL);
            if( !IsHeroic() )
                events.RescheduleEvent(EVENT_RESPAWN_SPHERE, 4000);

            for( std::list<uint64>::iterator itr = summons.begin(); itr != summons.end(); ++itr )
                if( Creature* c = pInstance->instance->GetCreature(*itr) )
                {
                    c->GetMotionMaster()->MoveIdle();
                    c->StopMoving();
                    c->CastSpell(c, SPELL_SUBMERGE, false);
                    c->AI()->DoAction(1);
                }
            summons.clear();
            for( uint8 i=0; i<4; ++i )
                if( Creature* c = me->SummonCreature(NPC_BURROW, AnubLocs[i+1]) )
                    BurrowGUID[i] = c->GetGUID();
            for( uint8 i=0; i<6; ++i )
                if( Creature* c = me->SummonCreature(NPC_FROST_SPHERE, AnubLocs[i+5]) )
                    SphereGUID[i] = c->GetGUID();

            Talk(SAY_AGGRO);
            DoZoneInCombat();
            if( pInstance )
                pInstance->SetData(TYPE_ANUBARAK, IN_PROGRESS);
        }

        void JustReachedHome()
        {
            me->setActive(false);
        }

        void UpdateAI(uint32 diff)
        {
            if( !UpdateVictim() )
                return;

            events.Update(diff);

            if( me->HasUnitState(UNIT_STATE_CASTING) )
                return;

            if( !bPhase3 && HealthBelowPct(30) && !me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) && !me->HasAura(SPELL_SUBMERGE) && !me->HasAura(SPELL_EMERGE) )
            {
                bPhase3 = true;
                events.CancelEvent(EVENT_SUBMERGE);
                events.CancelEvent(EVENT_EMERGE);
                events.CancelEvent(EVENT_EMERGE_2);
                if( !IsHeroic() )
                    events.CancelEvent(EVENT_SUMMON_NERUBIAN);
                me->CastSpell((Unit*)NULL, SPELL_LEECHING_SWARM, false);
                Talk(EMOTE_LEECHING_SWARM);
                Talk(SAY_LEECHING_SWARM);
                return;
            }

            switch( events.GetEvent() )
            {
                case 0:
                    break;
                case EVENT_ENRAGE:
                    {
                        me->CastSpell(me, SPELL_BERSERK, true);
                        events.PopEvent();
                    }
                    break;
                case EVENT_RESPAWN_SPHERE:
                    {
                        uint8 StartAt = urand(0,5);
                        uint8 i = StartAt;
                        do
                        {
                            if( Creature* c = ObjectAccessor::GetCreature(*me, SphereGUID[i]) )
                                if( !c->HasAura(SPELL_FROST_SPHERE) )
                                {
                                    if( Creature* c = me->SummonCreature(NPC_FROST_SPHERE, AnubLocs[i+5]) )
                                        SphereGUID[i] = c->GetGUID();
                                    break;
                                }
                            i = (i+1)%6;
                        }
                        while( i != StartAt );
                        events.RepeatEvent(4000);
                    }
                    break;
                case EVENT_SPELL_FREEZING_SLASH:
                    {
                        if( me->GetVictim() )
                            me->CastSpell(me->GetVictim(), SPELL_FREEZING_SLASH, false);
                        events.RepeatEvent(urand(15000,20000));
                    }
                    break;
                case EVENT_SPELL_PENETRATING_COLD:
                    {
                        me->CastCustomSpell(SPELL_PENETRATING_COLD, SPELLVALUE_MAX_TARGETS, RAID_MODE(2,5,2,5));
                        events.RepeatEvent(18000);
                    }
                    break;
                case EVENT_SUMMON_NERUBIAN:
                    {
                        me->CastCustomSpell(SPELL_SUMMON_BURROWER, SPELLVALUE_MAX_TARGETS, RAID_MODE(1,2,2,4));
                        events.RepeatEvent(45000);
                    }
                    break;
                case EVENT_SUBMERGE:
                    {
                        me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                        bool berserk = me->HasAura(SPELL_BERSERK);
                        me->RemoveAllAuras();
                        if (berserk)
                            me->CastSpell(me, SPELL_BERSERK, true);
                        Talk(EMOTE_SUBMERGE);
                        Talk(EMOTE_BURROWER);
                        me->CastSpell(me, SPELL_SUBMERGE, false);
                        events.CancelEvent(EVENT_SUMMON_NERUBIAN);
                        events.CancelEvent(EVENT_SPELL_FREEZING_SLASH);
                        events.CancelEvent(EVENT_SPELL_PENETRATING_COLD);
                        events.RescheduleEvent(EVENT_EMERGE, EMERGE_INTERVAL);
                        events.RescheduleEvent(EVENT_SPELL_SUMMON_SPIKE, 2500);
                        events.RescheduleEvent(EVENT_SUMMON_SCARAB, 3000);
                        events.PopEvent();
                    }
                    break;
                case EVENT_SUMMON_SCARAB:
                    {
                        uint8 i = urand(0,3);
                        if( Creature* c = ObjectAccessor::GetCreature(*me, BurrowGUID[i]) )
                            me->CastSpell(c, SPELL_SUMMON_SCARAB, true);
                        events.RepeatEvent(4000);
                    }
                    break;
                case EVENT_EMERGE:
                    {
                        me->CastSpell(me, SPELL_SPIKE_TELE, true);
                        summons.DespawnEntry(NPC_SPIKE);
                        events.CancelEvent(EVENT_SUMMON_SCARAB);
                        events.RescheduleEvent(EVENT_EMERGE_2, 2000);
                        events.PopEvent();
                    }
                    break;
                case EVENT_EMERGE_2:
                    {
                        Talk(SAY_EMERGE);
                        me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                        me->setAttackTimer(BASE_ATTACK, 3000);
                        me->RemoveAura(SPELL_SUBMERGE);
                        me->CastSpell(me, SPELL_EMERGE, false);
                        events.RescheduleEvent(EVENT_SUMMON_NERUBIAN, urand(5000,8000));
                        events.RescheduleEvent(EVENT_SPELL_FREEZING_SLASH, urand(7000,15000));
                        events.RescheduleEvent(EVENT_SPELL_PENETRATING_COLD, urand(15000,20000));
                        events.RescheduleEvent(EVENT_SUBMERGE, SUBMERGE_INTERVAL);
                        events.PopEvent();
                    }
                    break;
                case EVENT_SPELL_SUMMON_SPIKE:
                    me->CastSpell(me, SPELL_SUMMON_SPIKE, true);
                    events.PopEvent();
                    break;
            }

            DoMeleeAttackIfReady();
        }

        void JustSummoned(Creature* summon)
        {
            if( !summon )
                return;

            summons.Summon(summon);
        }

        void JustDied(Unit* /*pKiller*/)
        {
            events.Reset();
            summons.DespawnAll();
            Talk(SAY_DEATH);
            if( pInstance )
                pInstance->SetData(TYPE_ANUBARAK, DONE);


            Player* plr = NULL;
            if( !pInstance->instance->GetPlayers().isEmpty() )
                plr = pInstance->instance->GetPlayers().begin()->GetSource();

            if( !plr )
                return;

            // remove loot for the other faction (items are invisible for players, done in conditions), so corpse can be skinned
            for( std::vector<LootItem>::iterator itr = me->loot.items.begin(); itr != me->loot.items.end(); ++itr )
                if( ItemTemplate const *iProto = sObjectMgr->GetItemTemplate((*itr).itemid) )
                    if( ((iProto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && plr->GetTeamId() != TEAM_HORDE) || ((iProto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && plr->GetTeamId() != TEAM_ALLIANCE) )
                    {
                        (*itr).count = 0;
                        (*itr).is_looted = true;
                        --me->loot.unlootedCount;
                    }
        }
        
        void KilledUnit(Unit* who)
        {
            if( who->GetTypeId() == TYPEID_PLAYER )
                Talk(SAY_KILL_PLAYER);
        }

        void EnterEvadeMode()
        {
            events.Reset();
            summons.DespawnAll();
            me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
            if( pInstance )
                pInstance->SetData(TYPE_FAILED, 1);
        }
        
        void MoveInLineOfSight(Unit* who)
        {
            if (who->GetTypeId() != TYPEID_PLAYER || me->GetExactDistSq(who) > 6400.0f) // 80yd*80yd
                return;

            if (me->getStandState() != UNIT_STAND_STATE_STAND)
                me->SetStandState(UNIT_STAND_STATE_STAND);

            if (!bIntro)
            {
                me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
                if( !me->IsInCombat() )
                    Talk(SAY_INTRO);
                bIntro = true;
            }
            ScriptedAI::MoveInLineOfSight(who);
        }

        bool CanAIAttack(const Unit* target) const
        {
            return target->GetEntry() != NPC_FROST_SPHERE;
        }
    };
};

class npc_swarm_scarab : public CreatureScript
{
public:
    npc_swarm_scarab() : CreatureScript("npc_swarm_scarab") {}

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_swarm_scarabAI(pCreature);
    };

    struct npc_swarm_scarabAI : public ScriptedAI
    {
        npc_swarm_scarabAI(Creature* pCreature) : ScriptedAI(pCreature) {}

        int32 determinationTimer;
        int32 despawnTimer;

        void DoAction(int32 param)
        {
            if( param == 1 )
                despawnTimer = 2000;
        }

        void Reset()
        {
            me->SetCorpseDelay(10*60);
            me->CastSpell(me, SPELL_ACID_MANDIBLE, true);
            determinationTimer = urand(10000,50000);
            despawnTimer = 0;
            if( me->getFaction() == 16 ) // hostile - it's phase 2
                if( Unit* target = me->SelectNearestTarget(250.0f) )
                {
                    AttackStart(target);
                    DoZoneInCombat();
                    if( Unit* t = SelectTarget(SELECT_TARGET_RANDOM, 0, 250.0f, true) )
                    {
                        me->AddThreat(t, 20000.0f);
                        AttackStart(t);
                    }
                }
        }

        void EnterCombat(Unit* /*who*/)
        {
            DoZoneInCombat();
        }

        void UpdateAI(uint32 diff)
        {
            if( despawnTimer )
            {
                if( despawnTimer <= (int32)diff )
                {
                    despawnTimer = 0;
                    me->DisappearAndDie();
                }
                else
                    despawnTimer -= diff;

                return;
            }

            if( !UpdateVictim() )
                return;

            if( determinationTimer <= (int32)diff )
            {
                me->CastSpell(me, SPELL_DETERMINATION, false);
                determinationTimer = urand(20000,60000);
            }
            else
                determinationTimer -= diff;

            DoMeleeAttackIfReady();
        }

        void JustDied(Unit* killer)
        {
            me->CastSpell(me, RAID_MODE(SPELL_TRAITOR_KING_10, SPELL_TRAITOR_KING_25, SPELL_TRAITOR_KING_10, SPELL_TRAITOR_KING_25), true);
            me->m_Events.AddEvent(new HideNpcEvent(*me), me->m_Events.CalculateTime(5000));
        }

        bool CanAIAttack(const Unit* target) const
        {
            return target->GetEntry() != NPC_FROST_SPHERE && !me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
        }
    };
};

class npc_frost_sphere : public CreatureScript
{
public:
    npc_frost_sphere() : CreatureScript("npc_frost_sphere") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_frost_sphereAI(pCreature);
    };

    struct npc_frost_sphereAI : public NullCreatureAI
    {
        npc_frost_sphereAI(Creature* pCreature) : NullCreatureAI(pCreature)
        {
            uint32 ID = 0;
            if (me->GetMap())
                switch (me->GetMap()->GetDifficulty())
                {
                    case RAID_DIFFICULTY_10MAN_NORMAL: ID = 66118; break;
                    case RAID_DIFFICULTY_25MAN_NORMAL: ID = 67630; break;
                    case RAID_DIFFICULTY_10MAN_HEROIC: ID = 68646; break;
                    case RAID_DIFFICULTY_25MAN_HEROIC: ID = 68647; break;
                }
            if (ID)
                me->ApplySpellImmune(0, IMMUNITY_ID, ID, true);

            permafrostTimer = 0;
            me->CastSpell(me, SPELL_FROST_SPHERE, true);
            me->GetMotionMaster()->MoveRandom(20.0f);
            me->SetCorpseDelay(15*60*1000);
        }

        uint32 permafrostTimer;

        void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask)
        {
            if( me->GetHealth() <= damage )
            {
                damage = 0;
                if( !me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) )
                {
                    me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                    me->GetMotionMaster()->MoveIdle();
                    me->GetMotionMaster()->MoveCharge(me->GetPositionX(), me->GetPositionY(), 143.0f, 20.0f);
                    permafrostTimer = 1500;
                }
            }
        }

        void SpellHit(Unit* caster, const SpellInfo* spell)
        {
            if( spell->Id == SPELL_SPIKE_FAIL )
            {
                me->RemoveAllAuras();
                me->DespawnOrUnsummon(1500);
            }
        }

        void UpdateAI(uint32 diff)
        {
            if( permafrostTimer )
            {
                if( permafrostTimer <= diff )
                {
                    permafrostTimer = 0;
                    me->RemoveAurasDueToSpell(SPELL_FROST_SPHERE);
                    me->SetDisplayId(11686);
                    me->SetObjectScale(2.0f);
                    me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), 142.7f, me->GetOrientation(), false);
                    me->SetFacingTo(me->GetOrientation());
                    me->CastSpell(me, SPELL_PERMAFROST_VISUAL, true);
                    me->CastSpell(me, SPELL_PERMAFROST, true);
                    me->SetCanFly(false);
                }
                else
                    permafrostTimer -= diff;
            }
        }
    };
};

class npc_nerubian_burrower : public CreatureScript
{
public:
    npc_nerubian_burrower() : CreatureScript("npc_nerubian_burrower") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_nerubian_burrowerAI(pCreature);
    };

    struct npc_nerubian_burrowerAI : public ScriptedAI
    {
        npc_nerubian_burrowerAI(Creature* pCreature) : ScriptedAI(pCreature)
        {
            // I am summoned by another npc (SPELL_EFFECT_FORCE_CAST), inform Anub'arak
            if (InstanceScript* pInstance = me->GetInstanceScript())
                if (uint64 guid = pInstance->GetData64(TYPE_ANUBARAK))
                    if (Creature* anub = pInstance->instance->GetCreature(guid))
                        CAST_AI(boss_anubarak_trial::boss_anubarak_trialAI, anub->AI())->JustSummoned(me);
        }

        EventMap events;

        void Reset()
        {
            me->SetCorpseDelay(10*60);
            me->CastSpell(me, SPELL_EXPOSE_WEAKNESS, true);
            me->CastSpell(me, SPELL_SPIDER_FRENZY, true);
            events.Reset();
            events.RescheduleEvent(EVENT_SUBMERGE, 30000);
            if( IsHeroic() )
                events.RescheduleEvent(EVENT_SPELL_SHADOW_STRIKE, urand(30000,45000));
            if( Unit* target = me->SelectNearestTarget(250.0f) )
            {
                AttackStart(target);
                DoZoneInCombat();
            }
        }

        void SpellHitTarget(Unit* target, const SpellInfo* spell)
        {
            if( !target || !spell )
                return;

            if( spell->Id == SPELL_SHADOW_STRIKE )
            {
                float o = target->GetOrientation();
                if( o >= M_PI )
                    o -= M_PI;
                else
                    o += M_PI;
                me->NearTeleportTo(target->GetPositionX()+cos(o)*5.0f, target->GetPositionY()+sin(o)*5.0f, target->GetPositionZ()+0.6f, target->GetOrientation());
                AttackStart(target);
                me->GetMotionMaster()->MoveChase(target);
                events.DelayEvents(3000);
            }
        }

        void UpdateAI(uint32 diff)
        {
            if( !UpdateVictim() )
                return;

            events.Update(diff);

            if( me->HasUnitState(UNIT_STATE_CASTING) )
                return;

            switch( events.GetEvent() )
            {
                case 0:
                    break;
                case EVENT_SPELL_SHADOW_STRIKE:
                    if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 250.0f, true) )
                        me->CastSpell(target, SPELL_SHADOW_STRIKE, false);
                    events.RepeatEvent(urand(30000,45000));
                    break;
                case EVENT_SUBMERGE:
                    if( HealthBelowPct(80) && !me->HasAura(RAID_MODE(66193,67855,67856,67857)) ) // not having permafrost - allow submerge
                    {
                        me->GetMotionMaster()->MoveIdle();
                        me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                        me->RemoveAllAuras();
                        me->CastSpell(me, SPELL_EXPOSE_WEAKNESS, true);
                        me->CastSpell(me, SPELL_SPIDER_FRENZY, true);
                        me->CastSpell(me, SPELL_SUBMERGE, false);
                        events.PopEvent();
                        events.DelayEvents(15000);
                        events.RescheduleEvent(EVENT_EMERGE, 10000);
                    }
                    else
                        events.RepeatEvent(3000);
                    break;
                case EVENT_EMERGE:
                    me->SetHealth(me->GetMaxHealth());
                    me->GetMotionMaster()->MoveChase(me->GetVictim());
                    me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                    me->CastSpell(me, SPELL_EMERGE, false);
                    me->RemoveAura(SPELL_SUBMERGE);
                    events.PopEvent();
                    events.RescheduleEvent(EVENT_SUBMERGE, 30000);
                    break;
            }

            DoMeleeAttackIfReady();
        }

        void JustDied(Unit* killer)
        {
            me->m_Events.AddEvent(new HideNpcEvent(*me), me->m_Events.CalculateTime(5000));
        }

        bool CanAIAttack(const Unit* target) const
        {
            return target->GetEntry() != NPC_FROST_SPHERE;
        }
    };
};

class npc_anubarak_spike : public CreatureScript
{
public:
    npc_anubarak_spike() : CreatureScript("npc_anubarak_spike") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new npc_anubarak_spikeAI(pCreature);
    };

    struct npc_anubarak_spikeAI : public ScriptedAI
    {
        npc_anubarak_spikeAI(Creature* pCreature) : ScriptedAI(pCreature)
        {
            me->SetReactState(REACT_PASSIVE);
            me->SetCorpseDelay(0);
        }

        EventMap events;
        uint64 TargetGUID;

        void DoAction(int32 param)
        {
            if( param == -1 )
            {
                if( Unit* target = ObjectAccessor::GetPlayer(*me, TargetGUID) )
                    target->RemoveAura(SPELL_MARK);
                TargetGUID = 0;
                me->RemoveAllAuras();
                me->GetMotionMaster()->MoveIdle();
                events.Reset();
                events.RescheduleEvent(3, 4000);
            }
        }

        void SelectNewTarget(bool next)
        {
            if (TargetGUID)
                if( Unit* target = ObjectAccessor::GetPlayer(*me, TargetGUID) )
                    target->RemoveAura(SPELL_MARK);
            TargetGUID = 0;
            if (!next)
            {
                events.Reset();
                me->RemoveAllAuras();
            }
            DoZoneInCombat();
            DoResetThreat();
            if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 250.0f, true) )
            {
                if (!next)
                {
                    me->CastSpell(me, SPELL_SPIKE_SPEED1, true);
                    me->CastSpell(me, SPELL_SPIKE_TRAIL, true);
                    events.RescheduleEvent(1, 7000);
                }
                TargetGUID = target->GetGUID();
                me->CastSpell(target, SPELL_MARK, true);
                Talk(EMOTE_SPIKE, target);
                AttackStart(target);
                me->GetMotionMaster()->MoveChase(target);
            }
        }

        void Reset()
        {
            SelectNewTarget(false);
        }

        void UpdateAI(uint32 diff)
        {
            if( TargetGUID )
            {
                Unit* target = ObjectAccessor::GetPlayer(*me, TargetGUID);
                if( !target || !target->HasAura(SPELL_MARK) || !me->IsValidAttackTarget(target) || me->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE || !me->HasUnitState(UNIT_STATE_CHASE_MOVE) )
                {
                    SelectNewTarget(true);
                    return;
                }
            }

            events.Update(diff);

            switch( events.GetEvent() )
            {
                case 0:
                    break;
                case 1:
                    me->CastSpell(me, SPELL_SPIKE_SPEED2, true);
                    events.PopEvent();
                    events.RescheduleEvent(2, 7000);
                    break;
                case 2:
                    me->CastSpell(me, SPELL_SPIKE_SPEED3, true);
                    events.PopEvent();
                    break;
                case 3:
                    Reset();
                    break;
            }
        }

        void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask)
        {
            damage = 0;
        }
    };
};

class spell_pursuing_spikes : public SpellScriptLoader
{
    public:
        spell_pursuing_spikes() : SpellScriptLoader("spell_pursuing_spikes") { }

        class spell_pursuing_spikesAuraScript : public AuraScript
        {
            PrepareAuraScript(spell_pursuing_spikesAuraScript)

            void HandleEffectPeriodic(AuraEffect const * aurEff)
            {
                if( Unit* target = GetTarget() )
                {
                    if( Creature* c = target->FindNearestCreature(NPC_FROST_SPHERE, 8.0f, true) )
                    {
                        target->UpdatePosition(*c, false);
                        target->CastCustomSpell(SPELL_SPIKE_FAIL, SPELLVALUE_MAX_TARGETS, 1);
                        if( target->GetTypeId() == TYPEID_UNIT )
                            target->ToCreature()->AI()->DoAction(-1);
                        Remove();
                        return;
                    }
                    target->CastSpell((Unit*)NULL, SPELL_IMPALE, true);
                }
            }

            void Register()
            {
                OnEffectPeriodic += AuraEffectPeriodicFn(spell_pursuing_spikesAuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
            }
        };

        AuraScript *GetAuraScript() const
        {
            return new spell_pursuing_spikesAuraScript();
        }
};

// 66118 Leeching Swarm
enum eLeechingSwarmSpells
{
    SPELL_LEECHING_SWARM_DMG    = 66240,
    SPELL_LEECHING_SWARM_HEAL   = 66125,
};

class spell_gen_leeching_swarm : public SpellScriptLoader
{
    public:
    spell_gen_leeching_swarm() : SpellScriptLoader("spell_gen_leeching_swarm") { }

    class spell_gen_leeching_swarm_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_gen_leeching_swarm_AuraScript);

            bool Validate(SpellInfo const* /*spellEntry*/)
            {
                if (!sSpellMgr->GetSpellInfo(SPELL_LEECHING_SWARM_DMG))
                    return false;
                if (!sSpellMgr->GetSpellInfo(SPELL_LEECHING_SWARM_HEAL))
                    return false;
                return true;
            }

        void HandleEffectPeriodic(AuraEffect const* aurEff)
        {
            if (Unit* caster = GetCaster())
            {
                int32 lifeLeeched = GetTarget()->CountPctFromCurHealth(aurEff->GetAmount());
                if (lifeLeeched < 250)
                    lifeLeeched = 250;
                // Damage
                caster->CastCustomSpell(GetTarget(), SPELL_LEECHING_SWARM_DMG, &lifeLeeched, 0, 0, true);
                // Heal is handled in damage spell. It has to heal the same amount, but some of the dmg can be resisted.
            }
        }

        void Register()
        {
            OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_leeching_swarm_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
        }
    };

    AuraScript* GetAuraScript() const
    {
        return new spell_gen_leeching_swarm_AuraScript();
    }
};

class spell_gen_leeching_swarm_dmg : public SpellScriptLoader
{
    public:
    spell_gen_leeching_swarm_dmg() : SpellScriptLoader("spell_gen_leeching_swarm_dmg") {}

    class spell_gen_leeching_swarm_dmg_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_gen_leeching_swarm_dmg_SpellScript);

        void HandleAfterHit()
        {
            if (Unit* caster = GetCaster())
                if (GetHitDamage() > 0)
                {
                    int32 damage = GetHitDamage();
                    caster->CastCustomSpell(caster, SPELL_LEECHING_SWARM_HEAL, &damage, 0, 0, true);
                }
        }


        void Register()
        {
            AfterHit += SpellHitFn(spell_gen_leeching_swarm_dmg_SpellScript::HandleAfterHit);
        }
    };

    SpellScript* GetSpellScript() const
    {
        return new spell_gen_leeching_swarm_dmg_SpellScript();
    }
};

void AddSC_boss_anubarak_trial()
{
    new boss_anubarak_trial();
    new npc_swarm_scarab();
    new npc_frost_sphere();
    new npc_nerubian_burrower();
    new npc_anubarak_spike();
    new spell_pursuing_spikes();
    new spell_gen_leeching_swarm();
    new spell_gen_leeching_swarm_dmg();
}
