/*
 * Originally written by Xinef - Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-AGPL
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "magisters_terrace.h"

enum Yells
{
    SAY_AGGRO               = 0,
    SAY_HELPER_DIED         = 1,
    SAY_PLAYER_KILLED       = 5,
    SAY_DEATH               = 10,
};

enum Spells
{
    SPELL_MEDALION_OF_IMMUNITY  = 46227,
    SPELL_DISPEL_MAGIC          = 27609,
    SPELL_FLASH_HEAL            = 17843,
    SPELL_SHADOW_WORD_PAIN_N    = 14032,
    SPELL_SHADOW_WORD_PAIN_H    = 15654,
    SPELL_POWER_WORD_SHIELD_N   = 44291,
    SPELL_POWER_WORD_SHIELD_H   = 46193,
    SPELL_RENEW_N               = 44174,
    SPELL_RENEW_H               = 46192,
};

enum Misc
{
    MAX_ACTIVE_HELPERS      = 4,
    MAX_HELPERS_COUNT       = 8
};

const Position helpersLocations[MAX_ACTIVE_HELPERS]=
{
    {123.77f, 17.6007f, -19.921f, 4.98f},
    {131.731f, 15.0827f, -19.921f, 4.98f},
    {121.563f, 15.6213f, -19.921f, 4.98f},
    {129.988f, 17.2355f, -19.921f, 4.98f},
};

const uint32 helpersEntries[MAX_HELPERS_COUNT] =
{
    24557,                                                  //Kagani Nightstrike
    24558,                                                  //Elris Duskhallow
    24554,                                                  //Eramas Brightblaze
    24561,                                                  //Yazzaj
    24559,                                                  //Warlord Salaris
    24555,                                                  //Garaxxas
    24553,                                                  //Apoko
    24556,                                                  //Zelfan
};

enum Events
{
    EVENT_SPELL_FLASH_HEAL      = 1,
    EVENT_SPELL_RENEW           = 2,
    EVENT_SPELL_PW_SHIELD       = 3,
    EVENT_SPELL_SW_PAIN         = 4,
    EVENT_SPELL_DISPEL          = 5,
    EVENT_CHECK_DIST            = 6,
    EVENT_SPELL_IMMUNITY        = 7,
};

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

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_priestess_delrissaAI(creature);
    }

    struct boss_priestess_delrissaAI : public ScriptedAI
    {
        boss_priestess_delrissaAI(Creature* creature) : ScriptedAI(creature), summons(me)
        {
            instance = creature->GetInstanceScript();
        }

        InstanceScript* instance;
        EventMap events;
        SummonList summons;

        uint8 PlayersKilled;
        uint8 HelpersKilled;

        void Reset()
        {
            PlayersKilled = SAY_PLAYER_KILLED;
            HelpersKilled = SAY_HELPER_DIED;
            instance->SetData(DATA_DELRISSA_EVENT, NOT_STARTED);
            summons.Respawn();

            me->SetLootMode(0);
        }

        void InitializeAI()
        {
            ScriptedAI::InitializeAI();
            std::list<uint32> helpersList;
            for (uint8 i = 0; i < MAX_HELPERS_COUNT; ++i)
                helpersList.push_back(helpersEntries[i]);
            Trinity::Containers::RandomResizeList(helpersList, MAX_ACTIVE_HELPERS);

            uint8 j = 0;
            for (std::list<uint32>::const_iterator itr = helpersList.begin(); itr != helpersList.end(); ++itr, ++j)
                me->SummonCreature(*itr, helpersLocations[j], TEMPSUMMON_MANUAL_DESPAWN, 0);
        }

        void JustSummoned(Creature* summon)
        {
            summons.Summon(summon);
        }

        void SummonedCreatureDies(Creature* summon, Unit*)
        {
            if (me->IsAlive() && HelpersKilled < SAY_PLAYER_KILLED)
            {
                if (HelpersKilled == MAX_ACTIVE_HELPERS)
                    me->SetLootMode(1);
                Talk(HelpersKilled);
            }
            else if (HelpersKilled == MAX_ACTIVE_HELPERS)
            {
                me->loot.clear();
                me->loot.FillLoot(me->GetCreatureTemplate()->lootid, LootTemplates_Creature, me->GetLootRecipient(), false, false, 1);
                instance->SetData(DATA_DELRISSA_EVENT, DONE);
                me->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
            }
            ++HelpersKilled;
        }

        void EnterCombat(Unit* who)
        {
            Talk(SAY_AGGRO);
            summons.DoZoneInCombat();
            instance->SetData(DATA_DELRISSA_EVENT, IN_PROGRESS);

            events.ScheduleEvent(EVENT_SPELL_FLASH_HEAL, 15000);
            events.ScheduleEvent(EVENT_SPELL_RENEW, 10000);
            events.ScheduleEvent(EVENT_SPELL_PW_SHIELD, 2000);
            events.ScheduleEvent(EVENT_SPELL_SW_PAIN, 5000);
            events.ScheduleEvent(EVENT_SPELL_DISPEL, 7500);
            events.ScheduleEvent(EVENT_CHECK_DIST, 5000);
            if (IsHeroic())
                events.ScheduleEvent(EVENT_SPELL_IMMUNITY, 4000);
        }

        void KilledUnit(Unit* victim)
        {
            if (victim->GetTypeId() != TYPEID_PLAYER)
                return;

            if (PlayersKilled < SAY_DEATH)
                Talk(PlayersKilled++);
        }

        void JustDied(Unit* /*killer*/)
        {
            Talk(SAY_DEATH);

            if (HelpersKilled == MAX_ACTIVE_HELPERS+1)
                instance->SetData(DATA_DELRISSA_EVENT, DONE);
        }

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

            events.Update(diff);
            if (me->HasUnitState(UNIT_STATE_CASTING))
                return;

            switch (events.ExecuteEvent())
            {
                case EVENT_CHECK_DIST:
                    if (me->GetDistance(me->GetHomePosition()) > 75.0f)
                    {
                        EnterEvadeMode();
                        return;
                    }
                    events.ScheduleEvent(EVENT_CHECK_DIST, 5000);
                    break;
                case EVENT_SPELL_FLASH_HEAL:
                    if (Unit* target = DoSelectLowestHpFriendly(40.0f, 1000))
                        me->CastSpell(target, SPELL_FLASH_HEAL, false);
                    events.ScheduleEvent(EVENT_SPELL_FLASH_HEAL, 15000);
                    break;
                case EVENT_SPELL_RENEW:
                    if (Unit* target = DoSelectLowestHpFriendly(40.0f, 1000))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_RENEW_N, SPELL_RENEW_H), false);
                    events.ScheduleEvent(EVENT_SPELL_RENEW, 7000);
                    break;
                case EVENT_SPELL_PW_SHIELD:
                {
                    std::list<Creature*> cList = DoFindFriendlyMissingBuff(40.0f, DUNGEON_MODE(SPELL_POWER_WORD_SHIELD_N, SPELL_POWER_WORD_SHIELD_H));
                    if (Unit* target = Trinity::Containers::SelectRandomContainerElement(cList))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_POWER_WORD_SHIELD_N, SPELL_POWER_WORD_SHIELD_H), false);
                    events.ScheduleEvent(EVENT_SPELL_PW_SHIELD, 10000);
                    break;
                }
                case EVENT_SPELL_DISPEL:
                {
                    Unit* target = NULL;
                    switch (urand(0, 2))
                    {
                        case 0: target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30, true); break;
                        case 1: target = me; break;
                        case 2: target = ObjectAccessor::GetCreature(*me, Trinity::Containers::SelectRandomContainerElement(summons)); break;
                    }

                    if (target)
                        me->CastSpell(target, SPELL_DISPEL_MAGIC, false);
                    events.ScheduleEvent(EVENT_SPELL_DISPEL, 12000);
                    break;
                }
                case EVENT_SPELL_IMMUNITY:
                    if (me->HasUnitState(UNIT_STATE_LOST_CONTROL))
                    {
                        me->CastSpell(me, SPELL_MEDALION_OF_IMMUNITY, false);
                        events.ScheduleEvent(EVENT_SPELL_IMMUNITY, 60000);
                    }
                    else
                        events.ScheduleEvent(EVENT_SPELL_IMMUNITY, 1000);
                    break;
                case EVENT_SPELL_SW_PAIN:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_SHADOW_WORD_PAIN_N, SPELL_SHADOW_WORD_PAIN_H), false);
                    events.ScheduleEvent(EVENT_SPELL_SW_PAIN, 10000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

enum helpersShared
{
    SPELL_HEALING_POTION                = 15503,

    EVENT_SPELL_HELPER_HEALING_POTION   = 20,
    EVENT_SPELL_HELPER_IMMUNITY         = 21,
    EVENT_HELPER_RESET_THREAT           = 22,

    AI_TYPE_MELEE                       = 1,
    AI_TYPE_RANGED                      = 2
};

struct boss_priestess_lackey_commonAI : public ScriptedAI
{
    boss_priestess_lackey_commonAI(Creature* creature, uint32 type) : ScriptedAI(creature), summons(me)
    {
        instance = creature->GetInstanceScript();
        aiType = type;
    }

    InstanceScript* instance;
    EventMap events;
    SummonList summons;
    uint32 actualEventId;
    uint8 aiType;

    float GetThreatMod(float dist, float armor, uint32 health, uint32 /*maxhealth*/, Unit* target)
    {
        float unimportant_dist = (aiType == AI_TYPE_MELEE ? 5.0f : 25.0f);
        if (dist > unimportant_dist) dist -= unimportant_dist; else dist = 0.0f;
        const float dist_factor = (aiType == AI_TYPE_MELEE ? 15.0f : 25.0f);
        float mod_dist = dist_factor/(dist_factor + dist); // 0.2 .. 1.0
        float mod_health = health > 20000 ? 2.0f : (40000-health)/10000.0f; // 2.0 .. 4.0
        float mod_armor = aiType == AI_TYPE_MELEE ? Unit::CalcArmorReducedDamage(me, target, 10000, NULL)/10000.0f : 1.0f;
        return mod_dist * mod_health * mod_armor;
    }

    void RecalculateThreat()
    {
        ThreatContainer::StorageType const& tList = me->getThreatManager().getThreatList();
        for( ThreatContainer::StorageType::const_iterator itr = tList.begin(); itr != tList.end(); ++itr )
        {
            Unit* pUnit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
            if( pUnit && pUnit->GetTypeId() == TYPEID_PLAYER && me->getThreatManager().getThreat(pUnit) )
            {
                float threatMod = GetThreatMod(me->GetDistance2d(pUnit), (float)pUnit->GetArmor(), pUnit->GetHealth(), pUnit->GetMaxHealth(), pUnit);
                me->getThreatManager().modifyThreatPercent(pUnit, -100);
                if (HostileReference* ref = me->getThreatManager().getOnlineContainer().getReferenceByTarget(pUnit))
                    ref->addThreat(10000000.0f * threatMod);
            }
        }
    }

    void Reset()
    {
        events.Reset();
        summons.DespawnAll();
        actualEventId = 0;
    }
    
    void JustSummoned(Creature* summon)
    {
        summons.Summon(summon);
    }

    void EnterEvadeMode()
    {
        if (Creature* delrissa = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_DELRISSA)))
            if (!delrissa->IsAlive())
            {
                delrissa->Respawn();
                return;
            }
        ScriptedAI::EnterEvadeMode();
    }

    void EnterCombat(Unit* who)
    {
        if (Creature* delrissa = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_DELRISSA)))
            if (delrissa->IsAlive() && !delrissa->IsInCombat())
                delrissa->AI()->AttackStart(who);

        events.ScheduleEvent(EVENT_SPELL_HELPER_HEALING_POTION, 1000);
        events.ScheduleEvent(EVENT_HELPER_RESET_THREAT, urand(8000, 10000));
        if (IsHeroic())
            events.ScheduleEvent(EVENT_SPELL_HELPER_IMMUNITY, 2000, 1);

        RecalculateThreat();
    }

    void JustDied(Unit* /*killer*/)
    {
        summons.DespawnAll();
    }

    void KilledUnit(Unit* victim)
    {
        if (Creature* delrissa = ObjectAccessor::GetCreature(*me, instance->GetData64(NPC_DELRISSA)))
            delrissa->AI()->KilledUnit(victim);
    }

    void AttackStart(Unit* victim)
    {
        if (victim && me->Attack(victim, true))
            me->GetMotionMaster()->MoveChase(victim, aiType == AI_TYPE_MELEE ? 0.0f : 20.0f);
    }

    void UpdateAI(uint32 diff)
    {
        actualEventId = 0;
        events.Update(diff);
        if (me->HasUnitState(UNIT_STATE_CASTING))
            return;

        actualEventId = events.ExecuteEvent();
        switch (actualEventId)
        {
            case EVENT_SPELL_HELPER_IMMUNITY:
                if (me->HasUnitState(UNIT_STATE_LOST_CONTROL))
                {
                    me->CastSpell(me, SPELL_MEDALION_OF_IMMUNITY, false);
                    events.ScheduleEvent(EVENT_SPELL_HELPER_IMMUNITY, 60000);
                }
                else
                    events.ScheduleEvent(EVENT_SPELL_HELPER_IMMUNITY, 1000);
                break;
            case EVENT_SPELL_HELPER_HEALING_POTION:
                if (me->HealthBelowPct(25))
                {
                    me->CastSpell(me, SPELL_HEALING_POTION, false);
                    break;
                }
                events.ScheduleEvent(EVENT_SPELL_HELPER_HEALING_POTION, 1000);
                break;
            case EVENT_HELPER_RESET_THREAT:
                RecalculateThreat();
                events.ScheduleEvent(EVENT_HELPER_RESET_THREAT, urand(8000, 10000));
                break;

        }
    }
};

enum RogueEnum
{
    SPELL_KIDNEY_SHOT       = 27615,
    SPELL_VANISH            = 44290,
    SPELL_GOUGE             = 12540,
    SPELL_KICK              = 27613,
    SPELL_BACKSTAB_N        = 15657,
    SPELL_BACKSTAB_H        = 15582,
    SPELL_EVISCERATE_N      = 27611,
    SPELL_EVISCERATE_H      = 46189,

    EVENT_SPELL_GOUGE       = 1,
    EVENT_SPELL_KICK        = 2,
    EVENT_SPELL_VANISH      = 3,
    EVENT_SPELL_EVISCERATE  = 4,
    EVENT_SPELL_BACKSTAB    = 5,
};

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

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_kagani_nightstrikeAI(creature);
    }

    struct boss_kagani_nightstrikeAI : public boss_priestess_lackey_commonAI
    {
        boss_kagani_nightstrikeAI(Creature* creature) : boss_priestess_lackey_commonAI(creature, AI_TYPE_MELEE) { }

        void EnterCombat(Unit* who)
        {
            boss_priestess_lackey_commonAI::EnterCombat(who);

            events.ScheduleEvent(EVENT_SPELL_GOUGE, 5500);
            events.ScheduleEvent(EVENT_SPELL_KICK, 9000);
            events.ScheduleEvent(EVENT_SPELL_VANISH, 200);
            events.ScheduleEvent(EVENT_SPELL_EVISCERATE, 6000);
            events.ScheduleEvent(EVENT_SPELL_BACKSTAB, 4000);
        }

        void MovementInform(uint32 type, uint32 point)
        {
            if (type == CHASE_MOTION_TYPE && me->HasAura(SPELL_VANISH) && me->GetVictim())
                me->CastSpell(me->GetVictim(), SPELL_KIDNEY_SHOT, false);
        }

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

            boss_priestess_lackey_commonAI::UpdateAI(diff);

            switch (actualEventId)
            {
                case EVENT_SPELL_VANISH:
                    me->CastSpell(me, SPELL_VANISH, false);
                    DoResetThreat();
                    if (Unit* unit = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->AddThreat(unit, 1000.0f);

                    events.ScheduleEvent(EVENT_SPELL_VANISH, 30000);
                    break;
                case EVENT_SPELL_GOUGE:
                    me->CastSpell(me->GetVictim(), SPELL_GOUGE, false);
                    events.ScheduleEvent(EVENT_SPELL_GOUGE, 15000);
                    break;
                case EVENT_SPELL_KICK:
                    if (me->GetVictim()->HasUnitState(UNIT_STATE_CASTING))
                    {
                        me->CastSpell(me->GetVictim(), SPELL_KICK, false);
                        events.ScheduleEvent(EVENT_SPELL_KICK, 15000);
                    }
                    else
                        events.ScheduleEvent(EVENT_SPELL_KICK, 1000);
                    break;
                case EVENT_SPELL_EVISCERATE:
                    me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_EVISCERATE_N, SPELL_EVISCERATE_H), false);
                    events.ScheduleEvent(EVENT_SPELL_EVISCERATE, 10000);
                    break;
                case EVENT_SPELL_BACKSTAB:
                    if (!me->GetVictim()->HasInArc(static_cast<float>(M_PI), me))
                    {
                        me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_BACKSTAB_N, SPELL_BACKSTAB_H), false);
                        events.ScheduleEvent(EVENT_SPELL_BACKSTAB, 5000);
                    }
                    else
                        events.ScheduleEvent(EVENT_SPELL_BACKSTAB, 1000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

enum WarlockEnum
{
    SPELL_IMMOLATE_N            = 44267,
    SPELL_IMMOLATE_H            = 46191,
    SPELL_SHADOW_BOLT_N         = 12471,
    SPELL_SHADOW_BOLT_H         = 15232,
    SPELL_CURSE_OF_AGONY_N      = 14875,
    SPELL_CURSE_OF_AGONY_H      = 46190,
    SPELL_SEED_OF_CORRUPTION    = 44141,
    SPELL_FEAR                  = 38595,
    SPELL_SUMMON_IMP            = 44163,

    EVENT_SPELL_IMMOLATE        = 1,
    EVENT_SPELL_SHADOW_BOLT     = 2,
    EVENT_SPELL_SEED_OF_CORRUPTION= 3,
    EVENT_SPELL_CURSE_OF_AGONY  = 4,
    EVENT_SPELL_FEAR            = 5,
};

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

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_ellris_duskhallowAI(creature);
    }

    struct boss_ellris_duskhallowAI : public boss_priestess_lackey_commonAI
    {

        boss_ellris_duskhallowAI(Creature* creature) : boss_priestess_lackey_commonAI(creature, AI_TYPE_RANGED) { }

        void EnterCombat(Unit* who)
        {
            me->CastSpell(me, SPELL_SUMMON_IMP, false);
            boss_priestess_lackey_commonAI::EnterCombat(who);

            events.ScheduleEvent(EVENT_SPELL_IMMOLATE, 3000);
            events.ScheduleEvent(EVENT_SPELL_SHADOW_BOLT, 1000);
            events.ScheduleEvent(EVENT_SPELL_SEED_OF_CORRUPTION, 10000);
            events.ScheduleEvent(EVENT_SPELL_CURSE_OF_AGONY, 6000);
            events.ScheduleEvent(EVENT_SPELL_FEAR, 15000);
        }

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

            boss_priestess_lackey_commonAI::UpdateAI(diff);

            switch (actualEventId)
            {
                case EVENT_SPELL_IMMOLATE:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_IMMOLATE_N, SPELL_IMMOLATE_H), false);
                    events.ScheduleEvent(EVENT_SPELL_IMMOLATE, 12000);
                    break;
                case EVENT_SPELL_SHADOW_BOLT:
                    me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_SHADOW_BOLT_N, SPELL_SHADOW_BOLT_H), false);
                    events.ScheduleEvent(EVENT_SPELL_SHADOW_BOLT, 5000);
                    break;
                case EVENT_SPELL_SEED_OF_CORRUPTION:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
                        me->CastSpell(target, SPELL_SEED_OF_CORRUPTION, false);
                    events.ScheduleEvent(EVENT_SPELL_SEED_OF_CORRUPTION, 18000);
                    break;
                case EVENT_SPELL_CURSE_OF_AGONY:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_CURSE_OF_AGONY_N, SPELL_CURSE_OF_AGONY_H), false);
                    events.ScheduleEvent(EVENT_SPELL_CURSE_OF_AGONY, 13000);
                    break;
                case EVENT_SPELL_FEAR:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f))
                        me->CastSpell(target, SPELL_FEAR, false);
                    events.ScheduleEvent(EVENT_SPELL_FEAR, 15000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

enum MonkEnum
{
    SPELL_KNOCKDOWN_N       = 11428,
    SPELL_KNOCKDOWN_H       = 46183,
    SPELL_SNAP_KICK         = 46182,
    SPELL_FISTS_OF_ARCANE   = 44120,

    EVENT_SPELL_KNOCKDOWN   = 1,
    EVENT_SPELL_SNAP_KICK   = 2,
    EVENT_SPELL_FISTS       = 3,
};

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

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_eramas_brightblazeAI(creature);
    }

    struct boss_eramas_brightblazeAI : public boss_priestess_lackey_commonAI
    {
        boss_eramas_brightblazeAI(Creature* creature) : boss_priestess_lackey_commonAI(creature, AI_TYPE_MELEE) { }

        void EnterCombat(Unit* who)
        {
            boss_priestess_lackey_commonAI::EnterCombat(who);

            events.ScheduleEvent(EVENT_SPELL_KNOCKDOWN, 6000);
            events.ScheduleEvent(EVENT_SPELL_SNAP_KICK, 3000);
            events.ScheduleEvent(EVENT_SPELL_FISTS, 0);
        }

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

            boss_priestess_lackey_commonAI::UpdateAI(diff);

            switch (actualEventId)
            {
                case EVENT_SPELL_KNOCKDOWN:
                    me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_KNOCKDOWN_N, SPELL_KNOCKDOWN_H), false);
                    events.ScheduleEvent(EVENT_SPELL_KNOCKDOWN, 10000);
                    break;
                case EVENT_SPELL_SNAP_KICK:
                    me->CastSpell(me->GetVictim(), SPELL_SNAP_KICK, false);
                    events.ScheduleEvent(EVENT_SPELL_SNAP_KICK, 10000);
                    break;
                case EVENT_SPELL_FISTS:
                    me->CastSpell(me->GetVictim(), SPELL_FISTS_OF_ARCANE, false);
                    events.ScheduleEvent(EVENT_SPELL_FISTS, 10000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

enum MageEnum
{
    SPELL_POLYMORPH         = 13323,
    SPELL_ICE_BLOCK         = 27619,
    SPELL_BLIZZARD_N        = 44178,
    SPELL_BLIZZARD_H        = 46195,
    SPELL_ICE_LANCE_N       = 44176,
    SPELL_ICE_LANCE_H       = 46194,
    SPELL_CONE_OF_COLD_N    = 38384,
    SPELL_CONE_OF_COLD_H    = 12611,
    SPELL_FROSTBOLT_N       = 15043,
    SPELL_FROSTBOLT_H       = 15530,
    SPELL_BLINK             = 14514,

    EVENT_SPELL_POLYMORPH   = 1,
    EVENT_SPELL_ICE_BLOCK   = 2,
    EVENT_SPELL_BLIZZARD    = 3,
    EVENT_SPELL_ICE_LANCE   = 4,
    EVENT_SPELL_COC         = 5,
    EVENT_SPELL_FROSTBOLT   = 6,
    EVENT_SPELL_BLINK       = 7
};

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

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_yazzaiAI(creature);
    }

    struct boss_yazzaiAI : public boss_priestess_lackey_commonAI
    {
        boss_yazzaiAI(Creature* creature) : boss_priestess_lackey_commonAI(creature, AI_TYPE_RANGED) { }

        void EnterCombat(Unit* who)
        {
            boss_priestess_lackey_commonAI::EnterCombat(who);

            events.ScheduleEvent(EVENT_SPELL_POLYMORPH, 1000);
            events.ScheduleEvent(EVENT_SPELL_ICE_BLOCK, 1000);
            events.ScheduleEvent(EVENT_SPELL_BLIZZARD, 8000);
            events.ScheduleEvent(EVENT_SPELL_ICE_LANCE, 12000);
            events.ScheduleEvent(EVENT_SPELL_COC, 10000);
            events.ScheduleEvent(EVENT_SPELL_FROSTBOLT, 3000);
            events.ScheduleEvent(EVENT_SPELL_BLINK, 5000);
        }

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

            boss_priestess_lackey_commonAI::UpdateAI(diff);

            switch (actualEventId)
            {
                case EVENT_SPELL_POLYMORPH:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->CastSpell(target, SPELL_POLYMORPH, false);
                    events.ScheduleEvent(EVENT_SPELL_POLYMORPH, 20000);
                    break;
                case EVENT_SPELL_ICE_BLOCK:
                    if (HealthBelowPct(35))
                    {
                        me->CastSpell(me, SPELL_ICE_BLOCK, false);
                        return;
                    }
                    events.ScheduleEvent(EVENT_SPELL_ICE_BLOCK, 1000);
                    break;
                case EVENT_SPELL_BLIZZARD:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_BLIZZARD_N, SPELL_BLIZZARD_H), false);
                    events.ScheduleEvent(EVENT_SPELL_BLIZZARD, 20000);
                    break;
                case EVENT_SPELL_ICE_LANCE:
                    me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_ICE_LANCE_N, SPELL_ICE_LANCE_H), false);
                    events.ScheduleEvent(EVENT_SPELL_ICE_LANCE, 12000);
                    break;
                case EVENT_SPELL_COC:
                    me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_CONE_OF_COLD_N, SPELL_CONE_OF_COLD_H), false);
                    events.ScheduleEvent(EVENT_SPELL_COC, 10000);
                    break;
                case EVENT_SPELL_FROSTBOLT:
                    me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_FROSTBOLT_N, SPELL_FROSTBOLT_H), false);
                    events.ScheduleEvent(EVENT_SPELL_FROSTBOLT, 8000);
                    break;
                case EVENT_SPELL_BLINK:
                {
                    bool InMeleeRange = false;
                    ThreatContainer::StorageType const &t_list = me->getThreatManager().getThreatList();
                    for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
                        if (Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
                            if (target->IsWithinMeleeRange(me))
                            {
                                InMeleeRange = true;
                                break;
                            }

                    if (InMeleeRange)
                        me->CastSpell(me, SPELL_BLINK, false);
                    events.ScheduleEvent(EVENT_SPELL_BLINK, 15000);
                    break;
                }
            }

            DoMeleeAttackIfReady();
        }
    };
};

enum WarriorEnum
{
    SPELL_INTERCEPT             = 27577,
    SPELL_DISARM                = 27581,
    SPELL_PIERCING_HOWL         = 23600,
    SPELL_FRIGHTENING_SHOUT     = 19134,
    SPELL_HAMSTRING             = 27584,
    SPELL_BATTLE_SHOUT          = 27578,
    SPELL_MORTAL_STRIKE         = 44268,

    EVENT_SPELL_DISARM          = 1,
    EVENT_SPELL_PIERCING_HOWL   = 2,
    EVENT_SPELL_SHOUT           = 3,
    EVENT_SPELL_HAMSTRING       = 4,
    EVENT_SPELL_MORTAL_STRIKE   = 5,
    EVENT_SPELL_INTERCEPT       = 6,
};

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

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_warlord_salarisAI(creature);
    }

    struct boss_warlord_salarisAI : public boss_priestess_lackey_commonAI
    {
        boss_warlord_salarisAI(Creature* creature) : boss_priestess_lackey_commonAI(creature, AI_TYPE_MELEE) { }

        void EnterCombat(Unit* who)
        {
            boss_priestess_lackey_commonAI::EnterCombat(who);
            me->CastSpell(me, SPELL_BATTLE_SHOUT, false);

            events.ScheduleEvent(EVENT_SPELL_DISARM, 6000);
            events.ScheduleEvent(EVENT_SPELL_PIERCING_HOWL, 10000);
            events.ScheduleEvent(EVENT_SPELL_SHOUT, 18000);
            events.ScheduleEvent(EVENT_SPELL_HAMSTRING, 4000);
            events.ScheduleEvent(EVENT_SPELL_MORTAL_STRIKE, 8000);
            events.ScheduleEvent(EVENT_SPELL_INTERCEPT, 1000);
        }

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

            boss_priestess_lackey_commonAI::UpdateAI(diff);

            switch (actualEventId)
            {
                case EVENT_SPELL_INTERCEPT:
                    me->CastSpell(me->GetVictim(), SPELL_INTERCEPT, false);
                    events.ScheduleEvent(EVENT_SPELL_INTERCEPT, 10000);
                    break;
                case EVENT_SPELL_DISARM:
                    me->CastSpell(me->GetVictim(), SPELL_DISARM, false);
                    events.ScheduleEvent(EVENT_SPELL_DISARM, 16000);
                    break;
                case EVENT_SPELL_HAMSTRING:
                    me->CastSpell(me->GetVictim(), SPELL_HAMSTRING, false);
                    events.ScheduleEvent(EVENT_SPELL_HAMSTRING, 15000);
                    break;
                case EVENT_SPELL_MORTAL_STRIKE:
                    me->CastSpell(me->GetVictim(), SPELL_MORTAL_STRIKE, false);
                    events.ScheduleEvent(EVENT_SPELL_MORTAL_STRIKE, 10000);
                    break;
                case EVENT_SPELL_PIERCING_HOWL:
                    me->CastSpell(me, SPELL_PIERCING_HOWL, false);
                    events.ScheduleEvent(EVENT_SPELL_PIERCING_HOWL, 15000);
                    break;
                case EVENT_SPELL_SHOUT:
                    me->CastSpell(me, SPELL_FRIGHTENING_SHOUT, false);
                    events.ScheduleEvent(EVENT_SPELL_SHOUT, 18000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

enum HunterEnum
{
    SPELL_AIMED_SHOT            = 44271,
    SPELL_SHOOT_N               = 15620,
    SPELL_SHOOT_H               = 22907,
    SPELL_CONCUSSIVE_SHOT       = 27634,
    SPELL_MULTI_SHOT            = 31942,
    SPELL_WING_CLIP             = 44286,
    SPELL_FREEZING_TRAP         = 44136,

    NPC_SLIVER                  = 24552,

    EVENT_SPELL_AIMED_SHOT      = 1,
    EVENT_SPELL_SHOOT           = 2,
    EVENT_SPELL_CONCUSSIVE_SHOT = 3,
    EVENT_SPELL_MULTI_SHOT      = 4,
    EVENT_SPELL_WING_CLIP       = 5
};

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

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_garaxxasAI(creature);
    }

    struct boss_garaxxasAI : public boss_priestess_lackey_commonAI
    {
        boss_garaxxasAI(Creature* creature) : boss_priestess_lackey_commonAI(creature, AI_TYPE_RANGED) {}

        void Reset()
        {
            boss_priestess_lackey_commonAI::Reset();
            me->SummonCreature(NPC_SLIVER, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0);
        }

        void EnterCombat(Unit* who)
        {
            boss_priestess_lackey_commonAI::EnterCombat(who);
            me->CastSpell(me, SPELL_FREEZING_TRAP, true);

            events.ScheduleEvent(EVENT_SPELL_AIMED_SHOT, 8000);
            events.ScheduleEvent(EVENT_SPELL_SHOOT, 0);
            events.ScheduleEvent(EVENT_SPELL_CONCUSSIVE_SHOT, 6000);
            events.ScheduleEvent(EVENT_SPELL_MULTI_SHOT, 10000);
            events.ScheduleEvent(EVENT_SPELL_WING_CLIP, 4000);
        }

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

            boss_priestess_lackey_commonAI::UpdateAI(diff);

            switch (actualEventId)
            {
                case EVENT_SPELL_WING_CLIP:
                    me->CastSpell(me->GetVictim(), SPELL_WING_CLIP, false);
                    events.ScheduleEvent(EVENT_SPELL_WING_CLIP, 4000);
                    break;
                case EVENT_SPELL_AIMED_SHOT:
                    me->CastSpell(me->GetVictim(), SPELL_AIMED_SHOT, false);
                    events.ScheduleEvent(EVENT_SPELL_AIMED_SHOT, 15000);
                    break;
                case EVENT_SPELL_CONCUSSIVE_SHOT:
                    me->CastSpell(me->GetVictim(), SPELL_CONCUSSIVE_SHOT, false);
                    events.ScheduleEvent(EVENT_SPELL_CONCUSSIVE_SHOT, 15000);
                    break;
                case EVENT_SPELL_MULTI_SHOT:
                    me->CastSpell(me->GetVictim(), SPELL_MULTI_SHOT, false);
                    events.ScheduleEvent(EVENT_SPELL_MULTI_SHOT, 10000);
                    break;
                case EVENT_SPELL_SHOOT:
                    me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_SHOOT_N, SPELL_SHOOT_H), false);
                    events.ScheduleEvent(EVENT_SPELL_SHOOT, 2500);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

enum ShamanEnum
{
    SPELL_WINDFURY_TOTEM        = 27621,
    SPELL_FIRE_NOVA_TOTEM       = 44257,
    SPELL_EARTHBIND_TOTEM       = 15786,
    SPELL_WAR_STOMP             = 46026,
    SPELL_PURGE                 = 27626,
    SPELL_LESSER_HEALING_WAVE_N = 44256,
    SPELL_LESSER_HEALING_WAVE_H = 46181,
    SPELL_FROST_SHOCK_N         = 21401,
    SPELL_FROST_SHOCK_H         = 46180,

    EVENT_SPELL_TOTEM1          = 1,
    EVENT_SPELL_TOTEM2          = 2,
    EVENT_SPELL_TOTEM3          = 3,
    EVENT_SPELL_WAR_STOMP       = 4,
    EVENT_SPELL_PURGE           = 5,
    EVENT_SPELL_HEALING_WAVE    = 6,
    EVENT_SPELL_FROST_SHOCK     = 7
};

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

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_apokoAI(creature);
    }

    struct boss_apokoAI : public boss_priestess_lackey_commonAI
    {
        boss_apokoAI(Creature* creature) : boss_priestess_lackey_commonAI(creature, AI_TYPE_MELEE) { }

        uint32 Totem_Timer;
        uint8  Totem_Amount;
        uint32 War_Stomp_Timer;
        uint32 Purge_Timer;
        uint32 Healing_Wave_Timer;
        uint32 Frost_Shock_Timer;

        void EnterCombat(Unit* who)
        {
            boss_priestess_lackey_commonAI::EnterCombat(who);

            events.ScheduleEvent(EVENT_SPELL_TOTEM1, 2000);
            events.ScheduleEvent(EVENT_SPELL_TOTEM2, 4000);
            events.ScheduleEvent(EVENT_SPELL_TOTEM3, 6000);
            events.ScheduleEvent(EVENT_SPELL_WAR_STOMP, 10000);
            events.ScheduleEvent(EVENT_SPELL_PURGE, 14000);
            events.ScheduleEvent(EVENT_SPELL_HEALING_WAVE, 12000);
            events.ScheduleEvent(EVENT_SPELL_FROST_SHOCK, 8000);
        }

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

            boss_priestess_lackey_commonAI::UpdateAI(diff);

            switch (actualEventId)
            {
                case EVENT_SPELL_TOTEM1:
                    me->CastSpell(me, SPELL_WINDFURY_TOTEM, false);
                    events.ScheduleEvent(EVENT_SPELL_TOTEM1, 20000);
                    break;
                case EVENT_SPELL_TOTEM2:
                    me->CastSpell(me, SPELL_FIRE_NOVA_TOTEM, false);
                    events.ScheduleEvent(EVENT_SPELL_TOTEM2, 20000);
                    break;
                case EVENT_SPELL_TOTEM3:
                    me->CastSpell(me, SPELL_EARTHBIND_TOTEM, false);
                    events.ScheduleEvent(EVENT_SPELL_TOTEM3, 20000);
                    break;
                case EVENT_SPELL_WAR_STOMP:
                    me->CastSpell(me, SPELL_WAR_STOMP, false);
                    events.ScheduleEvent(EVENT_SPELL_WAR_STOMP, 120000);
                    break;
                case EVENT_SPELL_PURGE:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.0f))
                        me->CastSpell(target, SPELL_PURGE, false);
                    events.ScheduleEvent(EVENT_SPELL_PURGE, 15000);
                    break;
                case EVENT_SPELL_FROST_SHOCK:
                    me->CastSpell(me, DUNGEON_MODE(SPELL_FROST_SHOCK_N, SPELL_FROST_SHOCK_H), false);
                    events.ScheduleEvent(EVENT_SPELL_FROST_SHOCK, 12000);
                    break;
                case EVENT_SPELL_HEALING_WAVE:
                    if (Unit* target = DoSelectLowestHpFriendly(40.0f, 1000))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_LESSER_HEALING_WAVE_N, SPELL_LESSER_HEALING_WAVE_H), false);
                    events.ScheduleEvent(EVENT_SPELL_HEALING_WAVE, 12000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

enum EngineerEnum
{
    SPELL_GOBLIN_DRAGON_GUN_N   = 44272,
    SPELL_GOBLIN_DRAGON_GUN_H   = 46186,
    SPELL_ROCKET_LAUNCH_N       = 44137,
    SPELL_ROCKET_LAUNCH_H       = 46187,
    SPELL_FEL_IRON_BOMB_N       = 46024,
    SPELL_FEL_IRON_BOMB_H       = 46184,
    SPELL_RECOMBOBULATE         = 44274,
    SPELL_HIGH_EXPLOSIVE_SHEEP  = 44276,

    EVENT_SPELL_DRAGON_GUN      = 1,
    EVENT_SPELL_ROCKET_LAUNCH   = 2,
    EVENT_SPELL_RECOMBOBULATE   = 3,
    EVENT_SPELL_EXPLOSIVE_SHEEP = 4,
    EVENT_SPELL_IRON_BOMB       = 5
};

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

    CreatureAI* GetAI(Creature* creature) const
    {
        return new boss_zelfanAI(creature);
    }

    struct boss_zelfanAI : public boss_priestess_lackey_commonAI
    {
        boss_zelfanAI(Creature* creature) : boss_priestess_lackey_commonAI(creature, AI_TYPE_RANGED) { }

        void EnterCombat(Unit* who)
        {
            boss_priestess_lackey_commonAI::EnterCombat(who);

            events.ScheduleEvent(EVENT_SPELL_DRAGON_GUN, 20000);
            events.ScheduleEvent(EVENT_SPELL_ROCKET_LAUNCH, 7000);
            events.ScheduleEvent(EVENT_SPELL_RECOMBOBULATE, 14000);
            events.ScheduleEvent(EVENT_SPELL_EXPLOSIVE_SHEEP, 10000);
            events.ScheduleEvent(EVENT_SPELL_IRON_BOMB, 5000);
        }

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

            boss_priestess_lackey_commonAI::UpdateAI(diff);

            switch (actualEventId)
            {
                case EVENT_SPELL_DRAGON_GUN:
                    me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_GOBLIN_DRAGON_GUN_N, SPELL_GOBLIN_DRAGON_GUN_H), false);
                    events.ScheduleEvent(EVENT_SPELL_DRAGON_GUN, 20000);
                    break;
                case EVENT_SPELL_ROCKET_LAUNCH:
                    me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_ROCKET_LAUNCH_N, SPELL_ROCKET_LAUNCH_H), false);
                    events.ScheduleEvent(EVENT_SPELL_ROCKET_LAUNCH, 20000);
                    break;
                case EVENT_SPELL_IRON_BOMB:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 15.0f))
                        me->CastSpell(target, DUNGEON_MODE(SPELL_FEL_IRON_BOMB_N, SPELL_FEL_IRON_BOMB_H), false);
                    events.ScheduleEvent(EVENT_SPELL_IRON_BOMB, 20000);
                    break;
                case EVENT_SPELL_RECOMBOBULATE:
                {
                    std::list<Creature*> cList = DoFindFriendlyMissingBuff(30.0f, SPELL_RECOMBOBULATE);
                    for (std::list<Creature*>::const_iterator itr = cList.begin(); itr != cList.end(); ++itr)
                        if ((*itr)->IsPolymorphed())
                        {
                            me->CastSpell(*itr, SPELL_RECOMBOBULATE, false);
                            break;
                        }
                    events.ScheduleEvent(EVENT_SPELL_RECOMBOBULATE, 10000);
                    break;
                }
                case EVENT_SPELL_EXPLOSIVE_SHEEP:
                    me->CastSpell(me, SPELL_HIGH_EXPLOSIVE_SHEEP, false);
                    events.ScheduleEvent(EVENT_SPELL_EXPLOSIVE_SHEEP, 60000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

void AddSC_boss_priestess_delrissa()
{
    new boss_priestess_delrissa();
    new boss_kagani_nightstrike();
    new boss_ellris_duskhallow();
    new boss_eramas_brightblaze();
    new boss_yazzai();
    new boss_warlord_salaris();
    new boss_garaxxas();
    new boss_apoko();
    new boss_zelfan();
}
