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

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "ulduar.h"
#include "ScriptedEscortAI.h"
#include "SpellAuraEffects.h"
#include "PassiveAI.h"
#include "Player.h"

enum ThorimSpells
{
    // THORIM
    SPELL_LIGHTNING_DESTRUCTION             = 62393,
    SPELL_SHEATH_OF_LIGHTNING               = 62276,
    SPELL_STORMHAMMER                       = 62042,
    SPELL_BERSERK_FRIENDS                   = 62560,
    SPELL_CHAIN_LIGHTNING_10                = 62131,
    SPELL_CHAIN_LIGHTNING_25                = 64390,
    SPELL_UNBALANCING_STRIKE                = 62130,
    SPELL_BERSERK                           = 26662,

    SPELL_CHARGE_ORB                        = 62016,
    SPELL_LIGHTNING_PILLAR_P1               = 63238,

    SPELL_LIGHTNING_ORB_VISUAL              = 62186,
    SPELL_LIGHTNING_CHARGE_DAMAGE           = 62466,
    SPELL_LIGHTNING_CHARGE_BUFF             = 62279,
    SPELL_LIGHTNING_PILLAR_P2               = 62976,
    SPELL_LIGHTNING_ORB_CHARGER             = 62278,
    
    // SIF
    SPELL_TOUCH_OF_DOMINION                 = 62507,
    SPELL_SIF_TRANSFORM                     = 64778,
    SPELL_SIF_CHANNEL_HOLOGRAM              = 64324,
    SPELL_FROSTBOLT                         = 62601,
    SPELL_FROSTBOLT_VALLEY                  = 62604,
    SPELL_BLIZZARD_10                       = 62577,
    SPELL_BLIZZARD_25                       = 62603,
    SPELL_FROST_NOVA                        = 62605,

    // DARK RUNE ACOLYTE
    SPELL_GREATER_HEAL_10                   = 62334,
    SPELL_GREATER_HEAL_25                   = 62442,
    SPELL_HOLY_SMITE_10                     = 62335,
    SPELL_HOLY_SMITE_25                     = 62443,
    SPELL_RENEW_10                          = 62333,
    SPELL_RENEW_25                          = 62441,

    // CAPTURED MERCENARY SOLDIER
    SPELL_BARBED_SHOT                       = 62318,
    SPELL_WING_CLIP                         = 40652,
    SPELL_SHOOT                             = 16496,

    // CAPTURED MERCENARY CAPTAIN
    SPELL_DEVASTATE                         = 62317,
    SPELL_HEROIC_STRIKE                     = 62444,

    // JORMUNGAR BEHEMOTH
    SPELL_ACID_BREATH_10                    = 62315,
    SPELL_ACID_BREATH_25                    = 62415,
    SPELL_SWEEP_10                          = 62316,
    SPELL_SWEEP_25                          = 62417,

    // IRON RING GUARD
    SPELL_IMPALE_10                         = 62331,
    SPELL_IMPALE_25                         = 62418,
    SPELL_WHIRLING_TRIP                     = 64151,

    // IRON HONOR GUARD
    SPELL_SHIELD_SMASH_10                   = 62332,
    SPELL_SHIELD_SMASH_25                   = 62420,
    SPELL_CLEAVE                            = 42724,
    SPELL_HAMSTRING                         = 48639,

    // DARK RUNE WARBRINGER
    SPELL_AURA_OF_CELERITY                  = 62320,
    SPELL_RUNIC_STRIKE                      = 62322,

    // DARK RUNE EVOKER
    SPELL_RUNIC_LIGHTNING_10                = 62327,
    SPELL_RUNIC_LIGHTNING_25                = 62445,
    SPELL_RUNIC_MENDING_10                  = 62328,
    SPELL_RUNIC_MENDING_25                  = 62446,
    SPELL_RUNIC_SHIELD_10                   = 62321,
    SPELL_RUNIC_SHIELD_25                   = 62529,

    // DARK RUNE CHAMPION
    SPELL_CHARGE                            = 32323,
    SPELL_MORTAL_STRIKE                     = 35054,
    SPELL_WHIRLWIND                         = 15578,

    // DARK RUNE COMMONER
    SPELL_LOW_BLOW                          = 62326,
    SPELL_PUMMEL                            = 38313,

    // RUNIC COLOSSUS
    SPELL_COLOSSUS_CHARGE_10                = 62613,
    SPELL_COLOSSUS_CHARGE_25                = 62614,
    SPELL_RUNIC_BARRIER                     = 62338,
    SPELL_SMASH                             = 62339,
    SPELL_RUNIC_SMASH_LEFT                  = 62057,
    SPELL_RUNIC_SMASH_RIGHT                 = 62058,
    SPELL_RUNIC_SMASH_DAMAGE                = 62465,

    // ANCIENT RUNE GIANT
    SPELL_RUNE_DETONATION                   = 62526,
    SPELL_RUNIC_FORTIFICATION               = 62942,
    SPELL_STOMP_10                          = 62411,
    SPELL_STOMP_25                          = 62413,

    // TRAPS
    SPELL_LIGHTNING_FIELD                   = 64972,
    SPELL_PARALYTIC_FIELD_FIRST             = 62241,
    SPELL_PARALYTIC_FIELD_SECOND            = 63540,
};

#define SPELL_GREATER_HEAL          RAID_MODE(SPELL_GREATER_HEAL_10, SPELL_GREATER_HEAL_25)
#define SPELL_HOLY_SMITE            RAID_MODE(SPELL_HOLY_SMITE_10, SPELL_HOLY_SMITE_25)
#define SPELL_RENEW                 RAID_MODE(SPELL_RENEW_10, SPELL_RENEW_25)
#define SPELL_ACID_BREATH           RAID_MODE(SPELL_ACID_BREATH_10, SPELL_ACID_BREATH_25)
#define SPELL_SWEEP                 RAID_MODE(SPELL_SWEEP_10, SPELL_SWEEP_25)
#define SPELL_IMPALE                RAID_MODE(SPELL_IMPALE_10, SPELL_IMPALE_25)
#define SPELL_COLOSSUS_CHARGE       RAID_MODE(SPELL_COLOSSUS_CHARGE_10, SPELL_COLOSSUS_CHARGE_25)
#define SPELL_STOMP                 RAID_MODE(SPELL_STOMP_10, SPELL_STOMP_25)
#define SPELL_SHIELD_SMASH          RAID_MODE(SPELL_SHIELD_SMASH_10, SPELL_SHIELD_SMASH_25)
#define SPELL_RUNIC_LIGHTNING       RAID_MODE(SPELL_RUNIC_LIGHTNING_10, SPELL_RUNIC_LIGHTNING_25)
#define SPELL_RUNIC_MENDING         RAID_MODE(SPELL_RUNIC_MENDING_10, SPELL_RUNIC_MENDING_25)
#define SPELL_RUNIC_SHIELD          RAID_MODE(SPELL_RUNIC_SHIELD_10, SPELL_RUNIC_SHIELD_25)
#define SPELL_CHAIN_LIGHTNING       RAID_MODE(SPELL_CHAIN_LIGHTNING_10, SPELL_CHAIN_LIGHTNING_25)

enum ThormNPCandGOs
{
    // ARENA INIT
    NPC_DARK_RUNE_ACOLYTE_I                 = 32886,
    NPC_CAPTURED_MERCENARY_SOLDIER_ALLY     = 32885,
    NPC_CAPTURED_MERCENARY_SOLDIER_HORDE    = 32883,
    NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY     = 32908,
    NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE    = 32907,
    NPC_JORMUNGAR_BEHEMOT                   = 32882,

    // ARENA PHASE
    NPC_DARK_RUNE_WARBRINGER                = 32877,
    NPC_DARK_RUNE_EVOKER                    = 32878,
    NPC_DARK_RUNE_CHAMPION                  = 32876,
    NPC_DARK_RUNE_COMMONER                  = 32904,

    // GAUNTLET
    NPC_IRON_RING_GUARD                     = 32874,
    NPC_RUNIC_COLOSSUS                      = 32872,
    NPC_ANCIENT_RUNE_GIANT                  = 32873,
    NPC_DARK_RUNE_ACOLYTE_G                 = 33110,
    NPC_IRON_HONOR_GUARD                    = 32875,

    // TRIGGERS
    NPC_LIGHTNING_ORB                       = 33138,
    NPC_THUNDER_ORB                         = 33378,
    NPC_PILLAR                              = 32892,
    NPC_SIF_BLIZZARD                        = 32879,

    NPC_SIF                                 = 33196,
};

enum ThorimEvents
{
    EVENT_THORIM_START_PHASE1               = 1,
    EVENT_THORIM_STORMHAMMER                = 2,
    EVENT_THORIM_CHARGE_ORB                 = 3,
    EVENT_THORIM_LIGHTNING_ORB              = 4,
    EVENT_THORIM_NOT_REACH_IN_TIME          = 5,
    EVENT_THORIM_FILL_ARENA                 = 6,
    EVENT_THORIM_UNBALANCING_STRIKE         = 7,
    EVENT_THORIM_LIGHTNING_CHARGE           = 8,
    EVENT_THORIM_CHAIN_LIGHTNING            = 9,
    EVENT_THORIM_BERSERK                    = 10,
    EVENT_THORIM_AGGRO                      = 11,
    EVENT_THORIM_AGGRO2                     = 12,
    EVENT_THORIM_OUTRO1                     = 13,
    EVENT_THORIM_OUTRO2                     = 14,
    EVENT_THORIM_OUTRO3                     = 15,

    EVENT_DR_ACOLYTE_GH                     = 20,
    EVENT_DR_ACOLYTE_HS                     = 21,
    EVENT_DR_ACOLYTE_R                      = 22,

    EVENT_CM_SOLDIER_BS                     = 30,
    EVENT_CM_SOLDIER_S                      = 31,
    EVENT_CM_SOLDIER_WC                     = 32,

    EVENT_CM_CAPTAIN_D                      = 40,
    EVENT_CM_CAPTAIN_HC                     = 41,

    EVENT_JB_ACID_BREATH                    = 50,
    EVENT_JB_SWEEP                          = 51,

    EVENT_IR_GUARD_IMPALE                   = 60,
    EVENT_IR_GUARD_WHIRL                    = 61,

    EVENT_RC_RUNIC_BARRIER                  = 70,
    EVENT_RC_SMASH                          = 71,
    EVENT_RC_RUNIC_SMASH                    = 72,
    EVENT_RC_RUNIC_SMASH_TRIGGER            = 73,
    EVENT_RC_CHARGE                         = 74,

    EVENT_ARG_RD                            = 80,
    EVENT_ARG_RF                            = 81,
    EVENT_ARG_STOMP                         = 82,
    EVENT_ARG_SPAWN                         = 83,

    EVENT_IH_GUARD_CLEAVE                   = 90,
    EVENT_IH_GUARD_HAMSTRING                = 91,
    EVENT_IH_GUARD_SHIELD_SMASH             = 92,

    EVENT_SIF_START_TALK                    = 100,
    EVENT_SIF_JOIN_TALK                     = 101,
    EVENT_SIF_FINISH_DOMINION               = 102,
    EVENT_SIF_FROSTBOLT_VALLEY              = 103,
    EVENT_SIF_BLIZZARD                      = 104,
    EVENT_SIF_FROST_NOVA_START              = 105,
    EVENT_SIF_FROST_NOVA_CAST               = 106,

    EVENT_DR_WARBRINGER_RS                  = 110,

    EVENT_DR_EVOKER_RL                      = 120,
    EVENT_DR_EVOKER_RM                      = 121,
    EVENT_DR_EVOKER_RS                      = 122,

    EVENT_DR_CHAMPION_WH                    = 130,
    EVENT_DR_CHAMPION_CH                    = 131,
    EVENT_DR_CHAMPION_MS                    = 132,

    EVENT_DR_COMMONER_PM                    = 140,
    EVENT_DR_COMMONER_LB                    = 141,
};

const Position ArenaNPCs[] =
{
    {2178.5f,  -300.2f,  441.97f, 2.5f},
    {2188.12f, -295.1f,  443.75,  2.5f},
    {2180.9f,  -286.8f,  433.3f,  2.49f},
    {2193.2f,  -280.6f,  443.14f, 2.79f},
    {2191.8f,  -270.2f,  438.3f,  3.0f},
    {2186.84f, -238.5f,  439.7f,  3.4f},
    {2166.3f,  -213.0f,  440.0f,  4.1f},
    {2100.5f,  -213.5f,  441.66f, 5.4f},
    {2091.0f,  -231.26f, 435.17f, 5.5f},
    {2083.2f,  -239.2f,  438.77f, 5.85f},
    {2081.54f, -253.27f, 434.67f, 6.19f},
    {2077.65f, -272.73f, 439.12f, 0.15f},
    {2084.36f, -282.12f, 435.87f, 0.24f},
    {2087.46f, -298.71f, 440.5f,  0.59f}
};

enum ThorimSounds
{
    SOUND_AGGRO1                = 15733,
    SOUND_AGGRO2                = 15734,
    SOUND_SPECIAL1              = 15735,
    SOUND_SPECIAL2              = 15736,
    SOUND_SPECIAL3              = 15737,
    SOUND_JUMPDOWN              = 15738,
    SOUND_SLAY1                 = 15739,
    SOUND_SLAY2                 = 15740,
    SOUND_BERSERK               = 15741,
    SOUND_AWIPE                 = 15742,
    SOUND_DEFEATED              = 15743,
    SOUND_NORM1                 = 15744,
    SOUND_NORM2                 = 15745,
    SOUND_NORM3                 = 15746,
    SOUND_HARD1                 = 15747,
    SOUND_HARD2                 = 15748,
    SOUND_HARD3                 = 15749,

    SOUND_SIF_START             = 15668,
    SOUND_SIF_DESPAWN           = 15669,
    SOUND_SIF_EVENT             = 15670,
};

enum Misc
{
    ACTION_START_TRASH_DIED     = 1,
    ACTION_ALLOW_HIT            = 2,
    ACTION_SIF_JOIN_FIGHT       = 3,
    ACTION_SIF_START_TALK       = 4,
    ACTION_SIF_START_DOMINION   = 5,
    ACTION_SIF_TRANSFORM        = 6,
    ACTION_IRON_HONOR_DIED      = 7,

    EVENT_PHASE_START           = 1,
    EVENT_PHASE_RING            = 2,
    EVENT_PHASE_OUTRO           = 3,

    DATA_HIT_BY_LIGHTNING       = 1,
    DATA_LOSE_YOUR_ILLUSION     = 2,
};

const Position Middle = {2134.68f, -263.13f, 419.44f};

const uint32 RollTable[3] = { 32877, 32878, 32876 };


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

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

    struct boss_thorimAI : public ScriptedAI
    {
        boss_thorimAI(Creature* pCreature) : ScriptedAI(pCreature), summons(me)
        {
            m_pInstance = pCreature->GetInstanceScript();
            if (_encounterFinished = (!me->IsAlive()))
                if (m_pInstance)
                    m_pInstance->SetData(TYPE_THORIM, DONE);
        }

        bool _isArenaEmpty;
        bool _encounterFinished;
        bool _spawnCommoners;
        bool _hardMode;
        bool _isHitAllowed;
        bool _isAlly;
        uint8 _trashCounter;        

        InstanceScript* m_pInstance;
        EventMap events;
        SummonList summons;

        bool _hitByLightning;

        void DisableThorim(bool apply)
        {
            if (apply)
            {
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_PACIFIED);
                me->DisableRotate(true);
                me->AddUnitState(UNIT_STATE_ROOT);
            }
            else
            {
                me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_PACIFIED);
                me->DisableRotate(false);
                me->ClearUnitState(UNIT_STATE_ROOT);
                me->resetAttackTimer(BASE_ATTACK);
            }
        }

        GameObject* GetThorimObject(uint32 entry)
        {
            if (m_pInstance)
                return ObjectAccessor::GetGameObject(*me, m_pInstance->GetData64(entry));
            return NULL;
        }

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

        void SpawnAllNPCs()
        {
            // Jormungar Behemoth 32882
            me->SummonCreature(NPC_JORMUNGAR_BEHEMOT, 2149.68f, -263.477f, 419.679f, 3.12102f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);

            // Captured Mercenary Soldier 32885
            me->SummonCreature(_isAlly ? NPC_CAPTURED_MERCENARY_SOLDIER_ALLY : NPC_CAPTURED_MERCENARY_SOLDIER_HORDE, 2127.24f, -251.309f, 419.793f, 5.89921f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);
            me->SummonCreature(_isAlly ? NPC_CAPTURED_MERCENARY_SOLDIER_ALLY : NPC_CAPTURED_MERCENARY_SOLDIER_HORDE, 2120.1f, -258.99f, 419.764f, 6.24828f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);
            me->SummonCreature(_isAlly ? NPC_CAPTURED_MERCENARY_SOLDIER_ALLY : NPC_CAPTURED_MERCENARY_SOLDIER_HORDE, 2123.32f, -254.771f, 419.789f, 6.17846f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);
            
            // Captured Mercenary Captain 32908
            me->SummonCreature(_isAlly ? NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY : NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE, 2131.31f, -259.182f, 419.974f, 5.91667f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);
            
            // Dark Rune Acolyte (arena) 32886
            me->SummonCreature(NPC_DARK_RUNE_ACOLYTE_I, 2129.09f, -277.142f, 419.756f, 1.22173f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);

            // Iron Ring Guard 32874
            me->SummonCreature(NPC_IRON_RING_GUARD, 2217.69f, -337.394f, 412.177f, 1.23918f);
            me->SummonCreature(NPC_IRON_RING_GUARD, 2218.38f, -297.505f, 412.176f, 1.02974f);
            me->SummonCreature(NPC_IRON_RING_GUARD, 2235.26f, -338.345f, 412.134f, 1.58979f);
            me->SummonCreature(NPC_IRON_RING_GUARD, 2235.07f, -297.985f, 412.134f, 1.61336f);

            // Dark Rune Acolyte (gauntlet) 33110
            me->SummonCreature(NPC_DARK_RUNE_ACOLYTE_G, 2198.29f, -436.92f, 419.985f, 0.261799f);
            me->SummonCreature(NPC_DARK_RUNE_ACOLYTE_G, 2227.58f, -308.303f, 412.134f, 1.59372f);
            me->SummonCreature(NPC_DARK_RUNE_ACOLYTE_G, 2227.47f, -345.375f, 412.134f, 1.56622f);

            // Iron Honor Guard 32875
            me->SummonCreature(NPC_IRON_HONOR_GUARD, 2198.05f, -428.769f, 419.985f, 6.05629f);
            me->SummonCreature(NPC_IRON_HONOR_GUARD, 2220.31f, -436.22f, 412.26f, 1.06465f);

            // Runic Colossus 32872
            me->SummonCreature(NPC_RUNIC_COLOSSUS, 2227.5f, -396.179f, 412.176f, 1.79769f);
            
            // Ancient Rune Giant 32873
            me->SummonCreature(NPC_ANCIENT_RUNE_GIANT, 2134.57f, -440.318f, 438.331f, 0.226893f);

            // Sif 33196
            me->SummonCreature(NPC_SIF, 2147.86f, -301.2f, 438.246f, 2.488f);
        }

        void CloseDoors()
        {
            GameObject* go;
            if (go = GetThorimObject(DATA_THORIM_LEVER))
            {
                go->SetUInt32Value(GAMEOBJECT_FLAGS, 48);
                go->SetGoState(GO_STATE_READY);
            }
            if (go = GetThorimObject(DATA_THORIM_FIRST_DOORS))
                go->SetGoState(GO_STATE_READY);

            if (go = GetThorimObject(DATA_THORIM_SECOND_DOORS))
                go->SetGoState(GO_STATE_READY);

            if (go = GetThorimObject(DATA_THORIM_FENCE))
                go->SetGoState(GO_STATE_ACTIVE);
        }

        void EnterEvadeMode()
        {
            DisableThorim(false);
            CreatureAI::EnterEvadeMode();
        }

        void Reset()
        {
            if (m_pInstance && !_encounterFinished)
                m_pInstance->SetData(TYPE_THORIM, NOT_STARTED);

            events.Reset();
            events.SetPhase(0);
            summons.DespawnAll();

            _trashCounter = 0;
            _isAlly = true;
            _isHitAllowed = false;
            _spawnCommoners = false;
            _hardMode = false;
            _isArenaEmpty = false;
            _hitByLightning = false;

            if (Player *t = SelectTargetFromPlayerList(1000))
                if (t->GetTeamId() == TEAM_HORDE)
                    _isAlly = false;

            SpawnAllNPCs();

            CloseDoors();
            DisableThorim(false);
        }

        uint32 GetData(uint32 param) const
        {
            if (param == DATA_HIT_BY_LIGHTNING)
                return !_hitByLightning;
            if (param == DATA_LOSE_YOUR_ILLUSION)
                return _hardMode;

            return 0;
        }

        void DoAction(int32 param)
        {
            if (param == ACTION_START_TRASH_DIED)
            {
                _trashCounter++;
                // activate levar
                if (_trashCounter >= 6)
                {
                    if (GameObject* go = GetThorimObject(DATA_THORIM_LEVER))
                        go->RemoveFlag(GAMEOBJECT_FLAGS, 48);

                    events.SetPhase(EVENT_PHASE_START);
                    events.ScheduleEvent(EVENT_THORIM_START_PHASE1, 20000);
                    _trashCounter = 0;
                }
                else if (_trashCounter == 5)
                    events.ScheduleEvent(EVENT_THORIM_AGGRO, 0);
            }
            else if (param == ACTION_ALLOW_HIT)
                _isHitAllowed = true;
        }

        void KilledUnit(Unit*)
        {
            if (urand(0,2))
                return;

            if (urand(0,1))
            {
                me->MonsterYell("Can't you at least put up a fight!?", LANG_UNIVERSAL, 0);
                me->PlayDirectSound(SOUND_SLAY1);
            }
            else
            {
                me->MonsterYell("Pathetic!", LANG_UNIVERSAL, 0);
                me->PlayDirectSound(SOUND_SLAY2);
            }
        }

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

        void EnterCombat(Unit*)
        {
            if (m_pInstance && !_encounterFinished)
                m_pInstance->SetData(TYPE_THORIM, IN_PROGRESS);
            me->setActive(true);
            DisableThorim(true);
            me->CastSpell(me, SPELL_SHEATH_OF_LIGHTNING, true);
            //me->CastSpell(me, SPELL_TOUCH_OF_DOMINION, true);
        }

        void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask)
        {
            if (who && _isHitAllowed && who->GetPositionZ() > 430 && who->GetTypeId() == TYPEID_PLAYER)
            {
                _isHitAllowed = false;
                DisableThorim(false);

                events.SetPhase(EVENT_PHASE_RING);
                events.ScheduleEvent(EVENT_THORIM_UNBALANCING_STRIKE, 8000, 0, EVENT_PHASE_RING);
                events.ScheduleEvent(EVENT_THORIM_LIGHTNING_CHARGE, 12500, 0, EVENT_PHASE_RING);
                events.ScheduleEvent(EVENT_THORIM_CHAIN_LIGHTNING, 13000, 0, EVENT_PHASE_RING);
                events.ScheduleEvent(EVENT_THORIM_BERSERK, 300000, 0, EVENT_PHASE_RING);

                me->GetMotionMaster()->MoveChase(me->GetVictim());
                me->GetMotionMaster()->MoveJump(Middle.GetPositionX(), Middle.GetPositionY(), Middle.GetPositionZ(), 20, 20);
                me->RemoveAura(SPELL_SHEATH_OF_LIGHTNING);

                me->MonsterYell("Impertinent whelps! You dare challenge me atop my pedestal! I will crush you myself!", LANG_UNIVERSAL, 0);
                me->PlayDirectSound(SOUND_JUMPDOWN);

                // Hard Mode
                if (!me->HasAura(62565 /*TOUCH OF DOMINION TRIGGER*/))
                {
                    if (m_pInstance)
                        m_pInstance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, 64980 /*SIFFED ACHIEVEMENT*/);
                    
                    _hardMode = true;
                    EntryCheckPredicate pred(NPC_SIF);
                    summons.DoAction(ACTION_SIF_JOIN_FIGHT, pred);
                }

                DoResetThreat();
                if (Player* player = GetArenaPlayer())
                    me->AddThreat(player, 1000.0f);
            }

            if (damage >= me->GetHealth())
            {
                damage = 0;
                if (!_encounterFinished)
                {
                    _encounterFinished = true;
                    me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
                    me->setFaction(35);
                    me->SetHealth(me->GetMaxHealth());
                    me->CombatStop();
                    me->RemoveAllAuras();
                    events.Reset();
                    DisableThorim(true);

                    me->MonsterYell("Stay your arms! I yield!", LANG_UNIVERSAL, 0);
                    me->PlayDirectSound(SOUND_DEFEATED);
                    
                    events.SetPhase(EVENT_PHASE_OUTRO);
                    events.ScheduleEvent(EVENT_THORIM_OUTRO1, 2000, 0, EVENT_PHASE_OUTRO);

                    GameObject* go = NULL;
                    if (go = GetThorimObject(DATA_THORIM_FENCE))
                        go->SetGoState(GO_STATE_ACTIVE);

                    uint32 chestId = me->GetMap()->Is25ManRaid() ? GO_THORIM_CHEST_HERO : GO_THORIM_CHEST;
                    if (_hardMode)
                        chestId += 1; // hard mode offset

                    if (go = me->SummonGameObject(chestId, 2134.73f, -286.32f, 419.51f, 0.0f, 0, 0, 0, 0, 0))
                        go->SetUInt32Value(GAMEOBJECT_FLAGS, 0);

                    // Defeat credit
                    if (m_pInstance)
                    {
                        me->CastSpell(me, 64985, true); // credit
                        m_pInstance->SetData(TYPE_THORIM, DONE);
                    }
                }
            }
        }

        void SpawnArenaNPCs()
        {
            Creature* cr;
            uint8 rnd;
            if (_spawnCommoners || urand(0,2))
                _spawnCommoners = !_spawnCommoners;

            for (uint8 i = 0; i < (_spawnCommoners ? 7 : 2); ++i)
            {
                rnd = urand(0, 13);
                if (cr = me->SummonCreature((_spawnCommoners ? NPC_DARK_RUNE_COMMONER : RollTable[urand(0,2)]), ArenaNPCs[rnd], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000))
                    cr->GetMotionMaster()->MoveJump(
                        Middle.GetPositionX()+urand(19,24)*cos(Middle.GetAngle(cr)),
                        Middle.GetPositionY()+urand(19,24)*sin(Middle.GetAngle(cr)),
                        Middle.GetPositionZ(), 20, 20);
            }
        }

        void SpellHit(Unit* caster, const SpellInfo* spellInfo)
        {
            if (spellInfo->Id == SPELL_LIGHTNING_ORB_CHARGER)
            {
                me->SetOrientation(me->GetAngle(caster));
                me->CastSpell(caster, SPELL_LIGHTNING_CHARGE_DAMAGE, true);
                me->CastSpell(me, SPELL_LIGHTNING_CHARGE_BUFF, true);
                events.RescheduleEvent(EVENT_THORIM_LIGHTNING_CHARGE, 10000, 0, EVENT_PHASE_RING);
            }
        }

        void SpellHitTarget(Unit* target, const SpellInfo* spellInfo)
        {
            if (spellInfo->Id == SPELL_LIGHTNING_CHARGE_DAMAGE && target->GetTypeId() == TYPEID_PLAYER)
                _hitByLightning = true;
        }

        void PlaySpecial()
        {
            if (urand(0,9))
                return;

            switch (urand(0,2))
            {
                case 0:
                    me->MonsterYell("Behold the power of the storms and despair!", LANG_UNIVERSAL, 0);
                    me->PlayDirectSound(SOUND_SPECIAL1);
                    break;
                case 1:
                    me->MonsterYell("Do not hold back! Destroy them!", LANG_UNIVERSAL, 0);
                    me->PlayDirectSound(SOUND_SPECIAL2);
                    break;
                case 2:
                    me->MonsterYell("Have you begun to regret your intrusion? ", LANG_UNIVERSAL, 0);
                    me->PlayDirectSound(SOUND_SPECIAL3);
                    break;
            }
        }

        Player* GetArenaPlayer()
        {
            Map::PlayerList const& pList = me->GetMap()->GetPlayers();
            for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr)
                if (Player *p = itr->GetSource())
                    if (p->GetPositionX() > 2085 && p->GetPositionX() < 2185 && p->GetPositionY() < -214 && p->GetPositionY() > -305 && p->IsAlive() && p->GetPositionZ() < 425)
                        return p;
            return NULL;
        }

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

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

            switch (events.GetEvent())
            {
                case EVENT_THORIM_AGGRO:
                    me->MonsterYell("Interlopers! You mortals who dare to interfere with my sport will pay... Wait--you...", LANG_UNIVERSAL, 0);
                    me->PlayDirectSound(SOUND_AGGRO1);
                    events.ScheduleEvent(EVENT_THORIM_AGGRO2, 9000);
                    events.PopEvent();

                    if (GameObject* go = GetThorimObject(DATA_THORIM_FENCE))
                        go->SetGoState(GO_STATE_READY);

                    break;
                case EVENT_THORIM_AGGRO2:
                {
                    me->MonsterYell("I remember you... In the mountains... But you... what is this? Where am--", LANG_UNIVERSAL, 0);
                    me->PlayDirectSound(SOUND_AGGRO2);
                    events.PopEvent();

                    EntryCheckPredicate pred(NPC_SIF);
                    summons.DoAction(ACTION_SIF_START_TALK, pred);
                    break;
                }
                case EVENT_THORIM_START_PHASE1:
                {
                    events.PopEvent();
                    events.ScheduleEvent(EVENT_THORIM_STORMHAMMER, 8000, 0, EVENT_PHASE_START);
                    events.ScheduleEvent(EVENT_THORIM_CHARGE_ORB, 14000, 0, EVENT_PHASE_START);
                    events.ScheduleEvent(EVENT_THORIM_FILL_ARENA, 0, 0, EVENT_PHASE_START);
                    events.ScheduleEvent(EVENT_THORIM_LIGHTNING_ORB, 5000, 0, EVENT_PHASE_START); // checked every 5 secs if there are players on arena
                    events.ScheduleEvent(EVENT_THORIM_NOT_REACH_IN_TIME, 300000, 0, EVENT_PHASE_START);

                    EntryCheckPredicate pred(NPC_SIF);
                    summons.DoAction(ACTION_SIF_START_DOMINION, pred);
                    break;
                }
                case EVENT_THORIM_STORMHAMMER:
                    me->CastCustomSpell(SPELL_STORMHAMMER, SPELLVALUE_MAX_TARGETS, 1, me->GetVictim(), false);
                    events.RepeatEvent(16000);
                    PlaySpecial();
                    break;
                case EVENT_THORIM_CHARGE_ORB:
                    me->CastCustomSpell(SPELL_CHARGE_ORB, SPELLVALUE_MAX_TARGETS, 1, me, false);
                    events.RepeatEvent(16000);
                    PlaySpecial();
                    break;
                case EVENT_THORIM_LIGHTNING_ORB:
                {
                    if (GetArenaPlayer())
                    {
                        // Player found, repeat and return
                        events.RepeatEvent(5000);
                        return;
                    }

                    // No players found
                    me->MonsterYell("Failures! Weaklings!", LANG_UNIVERSAL, 0);
                    me->PlayDirectSound(SOUND_AWIPE);
                    me->SummonCreature(NPC_LIGHTNING_ORB, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());

                    _isArenaEmpty = true;
                    events.PopEvent();
                    events.CancelEvent(EVENT_THORIM_NOT_REACH_IN_TIME);
                    break;
                }
                case EVENT_THORIM_NOT_REACH_IN_TIME:
                    _isArenaEmpty = true;
                    events.PopEvent();
                    events.CancelEvent(EVENT_THORIM_LIGHTNING_ORB);
                    me->CastSpell(me, SPELL_BERSERK_FRIENDS, true);
                    me->SummonCreature(NPC_LIGHTNING_ORB, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
                    break;
                case EVENT_THORIM_FILL_ARENA:
                    SpawnArenaNPCs();
                    events.RepeatEvent(10000);
                    PlaySpecial();
                    break;
                case EVENT_THORIM_UNBALANCING_STRIKE:
                    me->CastSpell(me->GetVictim(), SPELL_UNBALANCING_STRIKE, false);
                    events.RepeatEvent(20000);
                    break;
                case EVENT_THORIM_LIGHTNING_CHARGE:
                    me->CastSpell(me, SPELL_LIGHTNING_PILLAR_P2, true);
                    events.PopEvent();
                    break;
                case EVENT_THORIM_CHAIN_LIGHTNING:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->CastSpell(target, SPELL_CHAIN_LIGHTNING, false);
                    events.RepeatEvent(15000);
                    break;
                case EVENT_THORIM_BERSERK:
                    me->CastSpell(me, SPELL_BERSERK, true);
                    events.PopEvent();
                    me->MonsterYell("My patience has reached its limit!", LANG_UNIVERSAL, 0);
                    me->PlayDirectSound(SOUND_BERSERK);
                    break;
                case EVENT_THORIM_OUTRO1:
                    events.PopEvent();
                    if (_hardMode)
                    {
                        me->MonsterYell("You! Fiend! You are not my beloved! Be gone!", LANG_UNIVERSAL, 0);
                        me->PlayDirectSound(SOUND_HARD1);
                        events.ScheduleEvent(EVENT_THORIM_OUTRO2, 5000, 0, 3);
                        EntryCheckPredicate pred(NPC_SIF);
                        summons.DoAction(ACTION_SIF_TRANSFORM, pred);
                    }
                    else
                    {
                        me->MonsterYell("I feel as though I am awakening from a nightmare, but the shadows in this place yet linger.", LANG_UNIVERSAL, 0);
                        me->PlayDirectSound(SOUND_NORM1);
                        events.ScheduleEvent(EVENT_THORIM_OUTRO2, 9000, 0, 3);
                    }
                    break;
                case EVENT_THORIM_OUTRO2:
                    events.PopEvent();
                    if (_hardMode)
                    {
                        me->MonsterYell("Behold the hand behind all the evil that has befallen Ulduar! Left my kingdom in ruins, corrupted my brother and slain my wife!", LANG_UNIVERSAL, 0);
                        me->PlayDirectSound(SOUND_HARD2);
                        events.ScheduleEvent(EVENT_THORIM_OUTRO3, 12000, 0, 3);
                    }
                    else
                    {
                        me->MonsterYell("Sif... was Sif here? Impossible--she died by my brother's hand. A dark nightmare indeed....", LANG_UNIVERSAL, 0);
                        me->PlayDirectSound(SOUND_NORM2);
                        events.ScheduleEvent(EVENT_THORIM_OUTRO3, 10000, 0, 3);
                    }
                    break;
                case EVENT_THORIM_OUTRO3:
                    events.PopEvent();
                    if (_hardMode)
                    {
                        me->MonsterYell("And now it falls to you, champions, to avenge us all! The task before you is great, but I will lend you my aid as I am able. You must prevail!", LANG_UNIVERSAL, 0);
                        me->PlayDirectSound(SOUND_HARD3);
                    }
                    else
                    {
                        me->MonsterYell("I need time to reflect.... I will aid your cause if you should require it. I owe you at least that much. Farewell.", LANG_UNIVERSAL, 0);
                        me->PlayDirectSound(SOUND_NORM3);
                    }

                    // Defeat credit
                    if (m_pInstance)
                        m_pInstance->SetData(TYPE_THORIM, DONE);

                    me->DespawnOrUnsummon(8000);
                    break;
            }

            if (!_encounterFinished)
                DoMeleeAttackIfReady();
        }
    };
};

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

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

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

        void MoveInLineOfSight(Unit*) {}
        void AttackStart(Unit*) {}

        bool _allowCast;
        EventMap events;

        void Reset()
        {
            events.Reset();
            me->SetReactState(REACT_PASSIVE);
            _allowCast = false;
        }

        void DoAction(int32 param)
        {
            if (param == ACTION_SIF_START_TALK)
                events.ScheduleEvent(EVENT_SIF_START_TALK, 9000);
            else if (param == ACTION_SIF_START_DOMINION)
            {
                if (me->GetInstanceScript())
                    if (Creature* cr = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetData64(TYPE_THORIM)))
                        me->CastSpell(cr, SPELL_TOUCH_OF_DOMINION, false);

                events.ScheduleEvent(EVENT_SIF_FINISH_DOMINION, 150000);
            }
            else if (param == ACTION_SIF_JOIN_FIGHT)
            {
                me->InterruptNonMeleeSpells(false);
                events.ScheduleEvent(EVENT_SIF_JOIN_TALK, 9000);
                events.CancelEvent(EVENT_SIF_START_TALK);
                events.CancelEvent(EVENT_SIF_FINISH_DOMINION);
            }
            else if (param == ACTION_SIF_TRANSFORM)
            {
                me->CastSpell(me, SPELL_SIF_TRANSFORM, true);
                me->DespawnOrUnsummon(5000);
                events.Reset();
                _allowCast = false;
            }
        }

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

            switch (events.GetEvent())
            {
                case EVENT_SIF_FINISH_DOMINION:
                    events.PopEvent();
                    me->PlayDirectSound(SOUND_SIF_DESPAWN);
                    me->MonsterYell("This pathetic morons are harmless. Relive my station, dispose of them!", LANG_UNIVERSAL, 0);
                    me->DespawnOrUnsummon(5000);
                    break;
                case EVENT_SIF_START_TALK:
                    events.PopEvent();
                    me->MonsterYell("Thorim, my lord, why else would these invaders have come into your sanctum but to slay you? They must be stopped!", LANG_UNIVERSAL, 0);
                    me->PlayDirectSound(SOUND_SIF_START);
                    break;
                case EVENT_SIF_JOIN_TALK:
                    me->PlayDirectSound(SOUND_SIF_EVENT);
                    me->MonsterYell("Impossible! Lord Thorim, I will bring your foes a frigid death!", LANG_UNIVERSAL, 0);
                    events.PopEvent();
                    events.ScheduleEvent(EVENT_SIF_FROST_NOVA_START, 1000);
                    events.ScheduleEvent(EVENT_SIF_FROSTBOLT_VALLEY, 11000);
                    events.ScheduleEvent(EVENT_SIF_BLIZZARD, 15000);
                    break;
                case EVENT_SIF_FROSTBOLT_VALLEY:
                    me->CastSpell(me, SPELL_FROSTBOLT_VALLEY, false);
                    events.RepeatEvent(13000);
                    return;
                case EVENT_SIF_BLIZZARD:
                    me->SummonCreature(NPC_SIF_BLIZZARD, 2108.7f, -280.04f, 419.42f, 0, TEMPSUMMON_TIMED_DESPAWN, 30000);
                    events.RepeatEvent(30000);
                    return;
                case EVENT_SIF_FROST_NOVA_START:
                    me->NearTeleportTo(2108+urand(0, 42), -238-irand(0,46), 420.02f, me->GetAngle(&Middle));
                    events.RepeatEvent(20000);
                    events.DelayEvents(5001);
                    events.ScheduleEvent(EVENT_SIF_FROST_NOVA_CAST, 2500);
                    _allowCast = false;
                    return;
                case EVENT_SIF_FROST_NOVA_CAST:
                    _allowCast = true;
                    me->CastSpell(me, SPELL_FROST_NOVA, false);
                    events.PopEvent();
                    return;
            }

            // has casting check before event select (return in events)
            if (_allowCast)
                if (Player* target = SelectTargetFromPlayerList(70))
                {
                    me->CastSpell(target, SPELL_FROSTBOLT, false);
                    me->StopMoving();
                }
        }
    };
};

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

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

    struct boss_thorim_lightning_orbAI : public npc_escortAI
    {
        boss_thorim_lightning_orbAI(Creature* pCreature) : npc_escortAI(pCreature)
        { 
            InitWaypoint();
            Reset();
            Start(false, true, 0);
        }

        uint32 Timer;

        void EnterEvadeMode() {}
        void MoveInLineOfSight(Unit*) {}
        void AttackStart(Unit*) {}

        void InitWaypoint()
        {
            AddWaypoint(1, 2135, -304, 438.24f, 0);
            AddWaypoint(2, 2132, -441, 438.24f, 0);
            AddWaypoint(3, 2167, -442, 438.24f, 0);
            AddWaypoint(4, 2227, -432, 412.18f, 0);
            AddWaypoint(5, 2227, -263, 412.17f, 0);
            AddWaypoint(6, 2179, -262, 414.7f, 0);
            AddWaypoint(7, 2169, -261, 419.3f, 0);
            AddWaypoint(8, 2110, -251, 419.42f, 0);
        }

        void Reset()
        {
            me->CastSpell(me, SPELL_LIGHTNING_DESTRUCTION, true);
        }

        void WaypointReached(uint32 point)
        {
        }
    };
};

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

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

    struct boss_thorim_trapAI : public NullCreatureAI
    {
        boss_thorim_trapAI(Creature* pCreature) : NullCreatureAI(pCreature) { }

        uint32 _checkTimer;

        void Reset() { _checkTimer = 1; }
        void UpdateAI(uint32 diff)
        {
            if (_checkTimer)
            {
                _checkTimer += diff;
                if ((_checkTimer >= 1000 && _checkTimer < 10000) || _checkTimer >= 60000)
                {
                    if (me->SelectNearbyTarget(NULL, 12.0f))
                    {
                        me->CastSpell(me, SPELL_LIGHTNING_FIELD, true);
                        me->CastSpell(me, (me->GetEntry() == 33054 /*NPC_THORIM_TRAP_BUNNY*/ ? SPELL_PARALYTIC_FIELD_FIRST : SPELL_PARALYTIC_FIELD_SECOND), true);
                        _checkTimer = 10000;
                        return;
                    }
                    _checkTimer = 1;
                }
            }
        }
    };
};

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

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

    struct boss_thorim_sif_blizzardAI : public npc_escortAI
    {
        boss_thorim_sif_blizzardAI(Creature* pCreature) : npc_escortAI(pCreature)
        { 
            InitWaypoint();
            Reset();
            Start(false, true, 0);
            SetDespawnAtEnd(false);
        }

        void MoveInLineOfSight(Unit *who) {}
        void EnterCombat(Unit *who) {}
        void AttackStart(Unit *who) {}

        void InitWaypoint()
        {
            AddWaypoint(1, 2104.6f, -268.5f, 419.4f, 0);
            AddWaypoint(2, 2104.3f, -256.3f, 419.4f, 0);
            AddWaypoint(3, 2109.3f, -246.4f, 419.4f, 0);
            AddWaypoint(4, 2117.9f, -238.6f, 419.4f, 0);
            AddWaypoint(5, 2128.8f, -232.1f, 419.4f, 0);
            AddWaypoint(6, 2151.9f, -237.5f, 419.4f, 0);
            AddWaypoint(7, 2164.9f, -256.3f, 419.4f, 0);
            AddWaypoint(8, 2161.5f, -280.0f, 419.4f, 0);
        }

        void Reset()
        {
            me->SetSpeed(MOVE_RUN, 1);
            me->SetSpeed(MOVE_WALK, 1);
            me->CastSpell(me, RAID_MODE(SPELL_BLIZZARD_10, SPELL_BLIZZARD_25), true);
        }

        void WaypointReached(uint32 point)
        {
        }
    };
};

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

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

    struct boss_thorim_pillarAI : public NullCreatureAI
    {
        boss_thorim_pillarAI(Creature* pCreature) : NullCreatureAI(pCreature) { }

        uint32 _resetTimer;

        void Reset() 
        { 
            _resetTimer = 0;
            me->SetControlled(true, UNIT_STATE_STUNNED);
            me->SetDisableGravity(true);
        }

        void SpellHit(Unit*, const SpellInfo* spellInfo)
        {
            if (spellInfo->Id == SPELL_CHARGE_ORB)
                me->CastSpell(me, SPELL_LIGHTNING_PILLAR_P1, true);
            else if (spellInfo->Id == SPELL_LIGHTNING_PILLAR_P2)
            {
                if (Creature *cr = me->FindNearestCreature(NPC_THUNDER_ORB, 100))
                    cr->CastSpell(cr, SPELL_LIGHTNING_ORB_VISUAL, true);
            }
        }

        void UpdateAI(uint32 diff)
        {
            _resetTimer += diff;
            if (_resetTimer >= 10000)
                Reset(); // _resetTimer set to 0
        }
    };
};

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

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

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

        EventMap events;
        bool _isCaster;
        bool _playerAttack;

        void Reset()
        {
            events.Reset();
            _isCaster = (me->GetEntry() == NPC_DARK_RUNE_ACOLYTE_I);
            _playerAttack = false;
            if (me->GetEntry() != NPC_JORMUNGAR_BEHEMOT)
                if (Creature* cr = me->FindNearestCreature(NPC_JORMUNGAR_BEHEMOT, 30.0f))
                    AttackStart(cr);
        }

        void DamageTaken(Unit* who, uint32&, DamageEffectType, SpellSchoolMask)
        {
            if (!_playerAttack && who && (who->GetTypeId() == TYPEID_PLAYER || IS_PLAYER_GUID(who->GetOwnerGUID())))
            {
                if (me->GetInstanceScript())
                    if (Creature* thorim = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetData64(TYPE_THORIM)))
                    {
                        if (!thorim->IsInCombat())
                        {
                            thorim->SetInCombatWithZone();
                            thorim->AI()->AttackStart(who);
                        }
                    }
                _playerAttack = true;
                me->getThreatManager().resetAllAggro();
                me->CallForHelp(40.0f);
                AttackStart(who);
            }

            if (!_playerAttack && me->HealthBelowPct(60))
                me->SetHealth(me->GetMaxHealth());
        }

        void JustDied(Unit*)
        {
            if (me->GetInstanceScript())
                if (Creature* thorim = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetData64(TYPE_THORIM)))
                    thorim->AI()->DoAction(ACTION_START_TRASH_DIED);
        }

        void EnterCombat(Unit* who)
        {
            if (me->GetEntry() == NPC_DARK_RUNE_ACOLYTE_I)
            {
                events.ScheduleEvent(EVENT_DR_ACOLYTE_GH, 10000);
                events.ScheduleEvent(EVENT_DR_ACOLYTE_HS, 5000);
                events.ScheduleEvent(EVENT_DR_ACOLYTE_R, 7000);
            }
            else if (me->GetEntry() == NPC_CAPTURED_MERCENARY_SOLDIER_ALLY || me->GetEntry() == NPC_CAPTURED_MERCENARY_SOLDIER_HORDE)
            {
                events.ScheduleEvent(EVENT_CM_SOLDIER_BS, 9000);
                events.ScheduleEvent(EVENT_CM_SOLDIER_WC, 5000);
                events.ScheduleEvent(EVENT_CM_SOLDIER_S, 0);
            }
            else if (me->GetEntry() == NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY || me->GetEntry() == NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE)
            {
                events.ScheduleEvent(EVENT_CM_CAPTAIN_D, 9000);
                events.ScheduleEvent(EVENT_CM_CAPTAIN_HC, 5000);
            }
            else if (me->GetEntry() == NPC_JORMUNGAR_BEHEMOT)
            {
                events.ScheduleEvent(EVENT_JB_ACID_BREATH, 12000);
                events.ScheduleEvent(EVENT_JB_SWEEP, 5000);
            }

            me->CallForHelp(10);
        }

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

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

            switch (events.GetEvent())
            {
                case EVENT_DR_ACOLYTE_GH:
                    if (HealthBelowPct(60))
                        me->CastSpell(me, SPELL_GREATER_HEAL, false);
                    else if (Unit* target = DoSelectLowestHpFriendly(60.0f, 20))
                        me->CastSpell(target, SPELL_GREATER_HEAL, false);
                    events.RepeatEvent(10000);
                    break;
                case EVENT_DR_ACOLYTE_HS:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM,0))
                        me->CastSpell(target, SPELL_HOLY_SMITE, false);
                    events.RepeatEvent(1600);
                    break;
                case EVENT_DR_ACOLYTE_R:
                    if (HealthBelowPct(75) && !me->HasAura(SPELL_RENEW))
                        me->CastSpell(me, SPELL_GREATER_HEAL, false);
                    else if (Unit* target = DoSelectLowestHpFriendly(60.0f, 10))
                        me->CastSpell(target, SPELL_RENEW, false);
                    events.RepeatEvent(7000);
                    break;
                case EVENT_CM_SOLDIER_BS:
                    me->CastSpell(me->GetVictim(), SPELL_BARBED_SHOT, false);
                    events.RepeatEvent(9000);
                    break;
                case EVENT_CM_SOLDIER_WC:
                    me->CastSpell(me->GetVictim(), SPELL_WING_CLIP, false);
                    events.RepeatEvent(5000);
                    break;
                case EVENT_CM_SOLDIER_S:
                    if (me->GetDistance(me->GetVictim()) > 8)
                        me->CastSpell(me->GetVictim(), SPELL_SHOOT, false);
                    
                    events.RepeatEvent(1500);
                    break;
                case EVENT_CM_CAPTAIN_D:
                    me->CastSpell(me->GetVictim(), SPELL_DEVASTATE, false);
                    events.RepeatEvent(9000);
                    break;
                case EVENT_CM_CAPTAIN_HC:
                    me->CastSpell(me->GetVictim(), SPELL_HEROIC_STRIKE, false);
                    events.RepeatEvent(5000);
                    break;
                case EVENT_JB_ACID_BREATH:
                    me->CastSpell(me->GetVictim(), SPELL_ACID_BREATH, false);
                    events.RepeatEvent(12000);
                    break;
                case EVENT_JB_SWEEP:
                    me->CastSpell(me->GetVictim(), SPELL_SWEEP, false);
                    events.RepeatEvent(5000);
                    break;
            }

            if (!_isCaster || (me->GetPower(POWER_MANA)*100 / me->GetMaxPower(POWER_MANA) < 10))
                DoMeleeAttackIfReady();
        }
    };
};

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

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

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

        EventMap events;
        bool _isCaster;

        void Reset()
        {
            events.Reset();
            _isCaster = (me->GetEntry() == NPC_DARK_RUNE_ACOLYTE_G);
        }

        void EnterCombat(Unit* who)
        {
            if (me->GetEntry() == NPC_IRON_RING_GUARD)
            {
                events.ScheduleEvent(EVENT_IR_GUARD_IMPALE, 12000);
                events.ScheduleEvent(EVENT_IR_GUARD_WHIRL, 5000);
            }
            else if (me->GetEntry() == NPC_DARK_RUNE_ACOLYTE_I)
            {
                events.ScheduleEvent(EVENT_DR_ACOLYTE_GH, 10000);
                events.ScheduleEvent(EVENT_DR_ACOLYTE_HS, 5000);
                events.ScheduleEvent(EVENT_DR_ACOLYTE_R, 7000);
            }
            else if (me->GetEntry() == NPC_IRON_HONOR_GUARD)
            {
                events.ScheduleEvent(EVENT_IH_GUARD_CLEAVE, 6000);
                events.ScheduleEvent(EVENT_IH_GUARD_HAMSTRING, 9000);
                events.ScheduleEvent(EVENT_IH_GUARD_SHIELD_SMASH, 15000);
                
                if (Creature* runeGiant = me->FindNearestCreature(NPC_ANCIENT_RUNE_GIANT, 200.0f))
                    runeGiant->AI()->DoAction(ACTION_IRON_HONOR_DIED);
            }

            me->CallForHelp(25);
        }

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

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

            switch (events.GetEvent())
            {
                case EVENT_IR_GUARD_IMPALE:
                    me->CastSpell(me->GetVictim(), SPELL_IMPALE, false);
                    events.RepeatEvent(12000);
                    break;
                case EVENT_IR_GUARD_WHIRL:
                    me->CastSpell(me->GetVictim(), SPELL_WHIRLING_TRIP, false);
                    events.RepeatEvent(5000);
                    break;
                case EVENT_DR_ACOLYTE_GH:
                    if (HealthBelowPct(60))
                        me->CastSpell(me, SPELL_GREATER_HEAL, false);
                    else if (Unit* target = DoSelectLowestHpFriendly(60.0f, 20))
                        me->CastSpell(target, SPELL_GREATER_HEAL, false);
                    events.RepeatEvent(10000);
                    break;
                case EVENT_DR_ACOLYTE_HS:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM,0))
                        me->CastSpell(target, SPELL_HOLY_SMITE, false);
                    events.RepeatEvent(1600);
                    break;
                case EVENT_DR_ACOLYTE_R:
                    if (HealthBelowPct(75) && !me->HasAura(SPELL_RENEW))
                        me->CastSpell(me, SPELL_GREATER_HEAL, false);
                    else if (Unit* target = DoSelectLowestHpFriendly(60.0f, 10))
                        me->CastSpell(target, SPELL_RENEW, false);
                    events.RepeatEvent(7000);
                    break;
                case EVENT_IH_GUARD_CLEAVE:
                    me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false);
                    events.RepeatEvent(6000);
                    break;
                case EVENT_IH_GUARD_HAMSTRING:
                    me->CastSpell(me->GetVictim(), SPELL_HAMSTRING, false);
                    events.RepeatEvent(9000);
                    break;
                case EVENT_IH_GUARD_SHIELD_SMASH:
                    me->CastSpell(me->GetVictim(), SPELL_SHIELD_SMASH, false);
                    events.RepeatEvent(15000);
                    break;
            }

            if (!_isCaster || (me->GetPower(POWER_MANA)*100 / me->GetMaxPower(POWER_MANA) < 10))
                DoMeleeAttackIfReady();
        }
    };
};

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

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

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

        EventMap events;
        bool _leftHand;
        bool _checkTarget;
        float _nextTriggerPos;
        uint64 _triggerLeftGUID[2], _triggerRightGUID[2];

        void Reset()
        {
            _nextTriggerPos = 0.0f;
            _leftHand = false;
            _checkTarget = false;
            events.Reset();
            events.ScheduleEvent(EVENT_RC_RUNIC_SMASH, 0);
            Creature* c;

            if (c = me->SummonCreature(33140, 2221, -385, me->GetPositionZ()))
                _triggerRightGUID[0] = c->GetGUID();
            if (c = me->SummonCreature(33140, 2210, -385, me->GetPositionZ()))
                _triggerRightGUID[1] = c->GetGUID();

            if (c = me->SummonCreature(33141, 2235, -385, me->GetPositionZ()))
                _triggerLeftGUID[0] = c->GetGUID();
            if (c = me->SummonCreature(33141, 2246, -385, me->GetPositionZ()))
                _triggerLeftGUID[1] = c->GetGUID();
        }

        void JustDied(Unit*)
        {
            if (me->GetInstanceScript())
                if (GameObject *go = ObjectAccessor::GetGameObject(*me, me->GetInstanceScript()->GetData64(DATA_THORIM_FIRST_DOORS)))
                    go->SetGoState(GO_STATE_ACTIVE);
        }

        void EnterCombat(Unit*)
        {
            events.CancelEvent(EVENT_RC_RUNIC_SMASH);
            events.ScheduleEvent(EVENT_RC_RUNIC_BARRIER, 10000);
            events.ScheduleEvent(EVENT_RC_SMASH, 18000);
            events.ScheduleEvent(EVENT_RC_CHARGE, 15000);

            me->InterruptNonMeleeSpells(false);
            _checkTarget = true;
        }

        void SpellHit(Unit*, const SpellInfo* spellInfo)
        {
            if (spellInfo->Id == SPELL_RUNIC_SMASH_LEFT || spellInfo->Id == SPELL_RUNIC_SMASH_RIGHT)
            {
                _leftHand = spellInfo->Id == SPELL_RUNIC_SMASH_LEFT;
                events.RescheduleEvent(EVENT_RC_RUNIC_SMASH_TRIGGER, 1000);
            }
        }

        void RunRunicSmash(bool cast)
        {
            if (Creature* cr = ObjectAccessor::GetCreature(*me, _leftHand ? _triggerLeftGUID[0] : _triggerRightGUID[0]) )
            {
                if (cast)
                    cr->CastSpell(cr, SPELL_RUNIC_SMASH_DAMAGE, true);
                cr->SetPosition(_leftHand ? 2235.0f : 2221.0f, _nextTriggerPos, cr->GetPositionZ(), 0.0f);
                cr->StopMovingOnCurrentPos();
            }
            if( Creature* cr = ObjectAccessor::GetCreature(*me, _leftHand ? _triggerLeftGUID[1] : _triggerRightGUID[1]) )
            {
                if (cast)
                    cr->CastSpell(cr, SPELL_RUNIC_SMASH_DAMAGE, true);
                cr->SetPosition(_leftHand ? 2246.0f : 2210.0f, _nextTriggerPos, cr->GetPositionZ(), 0.0f);
                cr->StopMovingOnCurrentPos();
            }
        }

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

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

            switch (events.GetEvent())
            {
                case EVENT_RC_RUNIC_SMASH_TRIGGER:
                    _nextTriggerPos += 16.0f;
                    if (_nextTriggerPos > -260.0f)
                        events.PopEvent();
                    else 
                        events.RescheduleEvent(EVENT_RC_RUNIC_SMASH_TRIGGER, 500);

                    RunRunicSmash(true);
                    break;
                case EVENT_RC_RUNIC_SMASH:
                    if (urand(0,1))
                        me->CastSpell(me, SPELL_RUNIC_SMASH_LEFT, false);
                    else
                        me->CastSpell(me, SPELL_RUNIC_SMASH_RIGHT, false);

                    _nextTriggerPos = -385.0f;
                    RunRunicSmash(false);
                    events.RepeatEvent(11000);
                    break;
                case EVENT_RC_RUNIC_BARRIER:
                    me->CastSpell(me, SPELL_RUNIC_BARRIER, false);
                    me->MonsterTextEmote("Runic Colossus surrounds itself with a crackling Runic Barrier!", 0, true);
                    events.RepeatEvent(20000);
                    break;
                case EVENT_RC_SMASH:
                    me->CastSpell(me->GetVictim(), SPELL_SMASH, false);
                    events.RepeatEvent(10000);
                    break;
                case EVENT_RC_CHARGE:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->CastSpell(target, SPELL_CHARGE, false);
                    events.RepeatEvent(15000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

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

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

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

        EventMap events;
        bool _isInCombat;

        void Reset()
        {
            _isInCombat = false;
            events.Reset();
        }

        void EnterCombat(Unit*)
        {
            _isInCombat = true;
            events.CancelEvent(EVENT_ARG_SPAWN);
            events.ScheduleEvent(EVENT_ARG_RD, 12000);
            events.ScheduleEvent(EVENT_ARG_STOMP, 8000);

            me->CastSpell(me, SPELL_RUNIC_FORTIFICATION, false);
            me->MonsterTextEmote("Ancient Rune Giant fortifies nearby allies with runic might", 0, true);
        }

        void JustDied(Unit*)
        {
            if (InstanceScript* pInstance = me->GetInstanceScript())
            {
                if (GameObject* go = ObjectAccessor::GetGameObject(*me, pInstance->GetData64(DATA_THORIM_SECOND_DOORS)))
                    go->SetGoState(GO_STATE_ACTIVE);

                if (Creature* thorim = ObjectAccessor::GetCreature(*me, pInstance->GetData64(TYPE_THORIM)))
                    thorim->AI()->DoAction(ACTION_ALLOW_HIT);
            }
        }

        void DoAction(int32 param)
        {
            if (param == ACTION_IRON_HONOR_DIED)
                events.RescheduleEvent(EVENT_ARG_SPAWN, 20000);
        }

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

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

            switch (events.GetEvent())
            {
                case EVENT_ARG_RD:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->CastSpell(target, SPELL_RUNE_DETONATION, false);
                    events.RepeatEvent(12000);
                    break;
                case EVENT_ARG_STOMP:
                    me->CastSpell(me->GetVictim(), SPELL_STOMP, false);
                    events.RepeatEvent(8000);
                    break;
                case EVENT_ARG_SPAWN:
                    if (Creature *cr = me->SummonCreature(NPC_IRON_HONOR_GUARD, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 20000))
                        if (Unit* target = SelectTargetFromPlayerList(150.0f))
                            cr->AI()->AttackStart(target);
                    events.RepeatEvent(10000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};

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

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

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

        EventMap events;
        bool _isCaster;

        void Reset()
        {
            _isCaster = (me->GetEntry() == NPC_DARK_RUNE_EVOKER);
            events.Reset();
            if (me->GetEntry() == NPC_DARK_RUNE_WARBRINGER)
                me->CastSpell(me, SPELL_AURA_OF_CELERITY, true);
        }

        void EnterCombat(Unit*)
        {
            if (me->GetEntry() == NPC_DARK_RUNE_WARBRINGER)
            {
                events.ScheduleEvent(EVENT_DR_WARBRINGER_RS, 8000);
            }
            else if (me->GetEntry() == NPC_DARK_RUNE_EVOKER)
            {
                events.ScheduleEvent(EVENT_DR_EVOKER_RL, 2500);
                events.ScheduleEvent(EVENT_DR_EVOKER_RM, 4000);
                events.ScheduleEvent(EVENT_DR_EVOKER_RS, 10000);
            }
            else if (me->GetEntry() == NPC_DARK_RUNE_CHAMPION)
            {
                events.ScheduleEvent(EVENT_DR_CHAMPION_WH, 6000);
                events.ScheduleEvent(EVENT_DR_CHAMPION_CH, 12000);
                events.ScheduleEvent(EVENT_DR_CHAMPION_MS, 8000);
            }
            else if (me->GetEntry() == NPC_DARK_RUNE_COMMONER)
            {
                events.ScheduleEvent(EVENT_DR_COMMONER_LB, 5000);
                events.ScheduleEvent(EVENT_DR_COMMONER_PM, 6000);
            }
        }

        bool CanAIAttack(const Unit* target) const
        {
            return target->GetPositionX() < 2180 && target->GetPositionZ() < 425;
        }

        bool SelectT()
        {
            Player* target = NULL;
            Map::PlayerList const& pList = me->GetMap()->GetPlayers();
            uint8 num = urand(0, pList.getSize()-1);
            uint8 count = 0;
            for (Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr, ++count)
            {
                if (itr->GetSource()->GetPositionX() > 2180 || !itr->GetSource()->IsAlive() || itr->GetSource()->GetPositionZ() > 425)
                    continue;

                if (count <= num || !target)
                    target = itr->GetSource();
                else
                    break;
            }

            if (target)
            {
                AttackStart(target);
                me->AddThreat(target, 500.0f);
                if (me->GetEntry() == NPC_DARK_RUNE_EVOKER && urand(0,1))
                    me->CastSpell(me, SPELL_RUNIC_SHIELD, false);
                else if (me->GetEntry() == NPC_DARK_RUNE_CHAMPION && !urand(0,2))
                    me->CastSpell(target, SPELL_CHARGE, false);
                return true;
            }
            return false;
        }

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

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

            switch (events.GetEvent())
            {
                case EVENT_DR_WARBRINGER_RS:
                    me->CastSpell(me->GetVictim(), SPELL_RUNIC_STRIKE, false);
                    events.RepeatEvent(8000);
                    break;
                case EVENT_DR_EVOKER_RL:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->CastSpell(target, SPELL_RUNIC_LIGHTNING, false);
                    events.RepeatEvent(2500);
                    break;
                case EVENT_DR_EVOKER_RM:
                    if (Unit* target = DoSelectLowestHpFriendly(40.0f, 15))
                        me->CastSpell(target, SPELL_RUNIC_MENDING, false);
                    else
                        me->CastSpell(me, SPELL_RUNIC_MENDING, false);
                    events.RepeatEvent(4000);
                    break;
                case EVENT_DR_EVOKER_RS:
                    me->CastSpell(me, SPELL_RUNIC_SHIELD, false);
                    events.RepeatEvent(10000);
                    break;
                case EVENT_DR_CHAMPION_CH:
                    if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                        me->CastSpell(target, SPELL_CHARGE, false);
                    events.RepeatEvent(12000);
                    break;
                case EVENT_DR_CHAMPION_WH:
                    if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED))
                        me->CastSpell(me, SPELL_WHIRLWIND, false);
                    events.RepeatEvent(6000);
                    break;
                case EVENT_DR_CHAMPION_MS:
                    me->CastSpell(me->GetVictim(), SPELL_MORTAL_STRIKE, false);
                    events.RepeatEvent(8000);
                    break;
                case EVENT_DR_COMMONER_LB:
                    me->CastSpell(me->GetVictim(), SPELL_LOW_BLOW, false);
                    events.RepeatEvent(5000);
                    break;
                case EVENT_DR_COMMONER_PM:
                    me->CastSpell(me->GetVictim(), SPELL_PUMMEL, false);
                    events.RepeatEvent(6000);
                    break;
            }

            if (!_isCaster || (me->GetPower(POWER_MANA)*100 / me->GetMaxPower(POWER_MANA) < 10))
                DoMeleeAttackIfReady();
        }
    };
};

class go_thorim_lever : public GameObjectScript
{ 
public: 
    go_thorim_lever() : GameObjectScript("go_thorim_lever") { } 

    bool OnGossipHello(Player* pPlayer, GameObject* pGo)
    {
        if (GameObject *g = pPlayer->FindNearestGameObject(GO_ARENA_LEVER_GATE, 50))
            g->UseDoorOrButton();

        pGo->UseDoorOrButton();
        return true;
    }
};

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

        class spell_thorim_lightning_pillar_P2_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_thorim_lightning_pillar_P2_AuraScript);

            void OnPeriodic(AuraEffect const* aurEff)
            {
                PreventDefaultAction();
                if (Unit* caster = GetCaster())
                    GetUnitOwner()->CastSpell(caster, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true);
            }

            void Register()
            {
                 OnEffectPeriodic += AuraEffectPeriodicFn(spell_thorim_lightning_pillar_P2_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
            }
        };

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

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

        class spell_thorim_trash_impale_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_thorim_trash_impale_AuraScript);

            void OnPeriodic(AuraEffect const* aurEff)
            {
                // deals damage until target is healed above 90%
                if (GetUnitOwner()->HealthAbovePct(90))
                    SetDuration(0);
            }

            void Register()
            {
                 OnEffectPeriodic += AuraEffectPeriodicFn(spell_thorim_trash_impale_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
            }
        };

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

class achievement_thorim_stand_in_the_lightning : public AchievementCriteriaScript
{
    public:
        achievement_thorim_stand_in_the_lightning() : AchievementCriteriaScript("achievement_thorim_stand_in_the_lightning") {}

        bool OnCheck(Player* player, Unit*)
        {
            if (InstanceScript* instance = player->GetInstanceScript())
                if (Creature* cr = ObjectAccessor::GetCreature(*player, instance->GetData64(TYPE_THORIM)))
                    return cr->AI()->GetData(DATA_HIT_BY_LIGHTNING);
                        
            return false;
        }
};

class achievement_thorim_lose_your_illusion : public AchievementCriteriaScript
{
    public:
        achievement_thorim_lose_your_illusion() : AchievementCriteriaScript("achievement_thorim_lose_your_illusion") {}

        bool OnCheck(Player* player, Unit*)
        {
            if (InstanceScript* instance = player->GetInstanceScript())
                if (Creature* cr = ObjectAccessor::GetCreature(*player, instance->GetData64(TYPE_THORIM)))
                    return cr->AI()->GetData(DATA_LOSE_YOUR_ILLUSION);
                        
            return false;
        }
};

void AddSC_boss_thorim()
{
    // Main encounter
    new boss_thorim();
    new boss_thorim_sif();
    new boss_thorim_lightning_orb();
    new boss_thorim_trap();
    new boss_thorim_pillar();
    new boss_thorim_sif_blizzard();

    // Trash
    new boss_thorim_start_npcs();
    new boss_thorim_gauntlet_npcs();
    new boss_thorim_arena_npcs();

    // Mini bosses
    new boss_thorim_runic_colossus();
    new boss_thorim_ancient_rune_giant();
    
    // GOs
    new go_thorim_lever();

    // Spells
    new spell_thorim_lightning_pillar_P2();
    new spell_thorim_trash_impale();

    // Achievements
    new achievement_thorim_stand_in_the_lightning();
    new achievement_thorim_lose_your_illusion();
}
