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

/* ScriptData
SDName: Undercity
SD%Complete: 95
SDComment: Quest support: 6628, 9180(post-event).
SDCategory: Undercity
EndScriptData */

/* ContentData
npc_lady_sylvanas_windrunner
npc_highborne_lamenter
npc_parqual_fintallas
EndContentData */

#include "ObjectAccessor.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "SpellScript.h"

/*######
## npc_lady_sylvanas_windrunner
######*/

enum Sylvanas
{
    QUEST_JOURNEY_TO_UNDERCITY      = 9180,

    EMOTE_LAMENT_END                = 0,
    SAY_LAMENT_END                  = 1,
    EMOTE_LAMENT                    = 2,

    // Ambassador Sunsorrow
    SAY_SUNSORROW_WHISPER           = 0,

    SOUND_CREDIT                    = 10896,

    NPC_HIGHBORNE_LAMENTER          = 21628,
    NPC_HIGHBORNE_BUNNY             = 21641,
    NPC_AMBASSADOR_SUNSORROW        = 16287,

    SPELL_HIGHBORNE_AURA            = 37090,
    SPELL_SYLVANAS_CAST             = 36568,
    //SPELL_RIBBON_OF_SOULS         = 34432, the real one to use might be 37099
    SPELL_RIBBON_OF_SOULS           = 37099,

    // Combat spells
    SPELL_BLACK_ARROW               = 59712,
    SPELL_FADE                      = 20672,
    SPELL_FADE_BLINK                = 29211,
    SPELL_MULTI_SHOT                = 59713,
    SPELL_SHOT                      = 59710,
    SPELL_SUMMON_SKELETON           = 59711,

    // Events
    EVENT_FADE                      = 1,
    EVENT_SUMMON_SKELETON           = 2,
    EVENT_BLACK_ARROW               = 3,
    EVENT_SHOOT                     = 4,
    EVENT_MULTI_SHOT                = 5,
    EVENT_LAMENT_OF_THE_HIGHBORN    = 6,
    EVENT_SUNSORROW_WHISPER         = 7,

    GUID_EVENT_INVOKER              = 1,
};

float HighborneLoc[4][3] =
{
    {1285.41f, 312.47f, 0.51f},
    {1286.96f, 310.40f, 1.00f},
    {1289.66f, 309.66f, 1.52f},
    {1292.51f, 310.50f, 1.99f},
};

#define HIGHBORNE_LOC_Y             -61.00f
#define HIGHBORNE_LOC_Y_NEW         -55.50f

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

    bool OnQuestReward(Player* player, Creature* creature, const Quest* _Quest, uint32 /*slot*/) override
    {
        if (_Quest->GetQuestId() == QUEST_JOURNEY_TO_UNDERCITY)
            creature->AI()->SetGUID(player->GetGUID(), GUID_EVENT_INVOKER);

        return true;
    }

    struct npc_lady_sylvanas_windrunnerAI : public ScriptedAI
    {
        npc_lady_sylvanas_windrunnerAI(Creature* creature) : ScriptedAI(creature)
        {
        }

        void Reset() override
        {
            LamentEvent = false;
            playerGUID.Clear();
            _events.Reset();
        }

        void EnterCombat(Unit* /*who*/) override
        {
            _events.ScheduleEvent(EVENT_FADE, 30000);
            _events.ScheduleEvent(EVENT_SUMMON_SKELETON, 20000);
            _events.ScheduleEvent(EVENT_BLACK_ARROW, 15000);
            _events.ScheduleEvent(EVENT_SHOOT, 8000);
            _events.ScheduleEvent(EVENT_MULTI_SHOT, 10000);
        }

        void SetGUID(ObjectGuid guid, int32 type) override
        {
            if (type == GUID_EVENT_INVOKER)
            {
                Talk(EMOTE_LAMENT);
                DoPlayMusic(SOUND_CREDIT, true);
                DoCast(me, SPELL_SYLVANAS_CAST, false);
                playerGUID = guid;
                LamentEvent = true;

                for (uint8 i = 0; i < 4; ++i)
                    me->SummonCreature(NPC_HIGHBORNE_LAMENTER, HighborneLoc[i][0], HighborneLoc[i][1], HIGHBORNE_LOC_Y, HighborneLoc[i][2], TEMPSUMMON_TIMED_DESPAWN, 160000);

                _events.ScheduleEvent(EVENT_LAMENT_OF_THE_HIGHBORN, 2000);
                _events.ScheduleEvent(EVENT_SUNSORROW_WHISPER, 10000);
            }
        }

        void JustSummoned(Creature* summoned) override
        {
            if (summoned->GetEntry() == NPC_HIGHBORNE_BUNNY)
            {
                summoned->SetDisableGravity(true);
                float speed = summoned->GetDistance(summoned->GetPositionX(), summoned->GetPositionY(), me->GetPositionZ() + 15.0f) / (1000.0f * 0.001f);
                summoned->MonsterMoveWithSpeed(summoned->GetPositionX(), summoned->GetPositionY(), me->GetPositionZ() + 15.0f, speed);
                summoned->CastSpell(summoned, SPELL_RIBBON_OF_SOULS, false);
            }
        }

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

            _events.Update(diff);

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

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_FADE:
                        DoCast(me, SPELL_FADE);
                        // add a blink to simulate a stealthed movement and reappearing elsewhere
                        DoCast(me, SPELL_FADE_BLINK);
                        // if the victim is out of melee range she cast multi shot
                        if (Unit* victim = me->GetVictim())
                            if (me->GetDistance(victim) > 10.0f)
                                DoCast(victim, SPELL_MULTI_SHOT);
                        _events.ScheduleEvent(EVENT_FADE, urand(30000, 35000));
                        break;
                    case EVENT_SUMMON_SKELETON:
                        DoCast(me, SPELL_SUMMON_SKELETON);
                        _events.ScheduleEvent(EVENT_SUMMON_SKELETON, urand(20000, 30000));
                        break;
                    case EVENT_BLACK_ARROW:
                        if (Unit* victim = me->GetVictim())
                            DoCast(victim, SPELL_BLACK_ARROW);
                        _events.ScheduleEvent(EVENT_BLACK_ARROW, urand(15000, 20000));
                        break;
                    case EVENT_SHOOT:
                        if (Unit* victim = me->GetVictim())
                            DoCast(victim, SPELL_SHOT);
                        _events.ScheduleEvent(EVENT_SHOOT, urand(8000, 10000));
                        break;
                    case EVENT_MULTI_SHOT:
                        if (Unit* victim = me->GetVictim())
                            DoCast(victim, SPELL_MULTI_SHOT);
                        _events.ScheduleEvent(EVENT_MULTI_SHOT, urand(10000, 13000));
                        break;
                    case EVENT_LAMENT_OF_THE_HIGHBORN:
                        if (!me->HasAura(SPELL_SYLVANAS_CAST))
                        {
                            Talk(SAY_LAMENT_END);
                            Talk(EMOTE_LAMENT_END);
                            LamentEvent = false;
                            me->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL);
                            Reset();
                        }
                        else
                        {
                            DoSummon(NPC_HIGHBORNE_BUNNY, me, 10.0f, 3000, TEMPSUMMON_TIMED_DESPAWN);
                            _events.ScheduleEvent(EVENT_LAMENT_OF_THE_HIGHBORN, 2000);
                        }
                        break;
                    case EVENT_SUNSORROW_WHISPER:
                        if (Creature* ambassador = me->FindNearestCreature(NPC_AMBASSADOR_SUNSORROW, 20.0f))
                            if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
                                ambassador->AI()->Talk(SAY_SUNSORROW_WHISPER, player);
                        break;
                    default:
                        break;
                }
            }

            DoMeleeAttackIfReady();
        }

    private:
        EventMap _events;
        bool LamentEvent;
        ObjectGuid playerGUID;
    };

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

/*######
## npc_highborne_lamenter
######*/

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

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

    struct npc_highborne_lamenterAI : public ScriptedAI
    {
        npc_highborne_lamenterAI(Creature* creature) : ScriptedAI(creature) { }

        uint32 EventMoveTimer;
        uint32 EventCastTimer;
        bool EventMove;
        bool EventCast;

        void Reset() override
        {
            EventMoveTimer = 10000;
            EventCastTimer = 17500;
            EventMove = true;
            EventCast = true;
        }

        void EnterCombat(Unit* /*who*/) override { }

        void UpdateAI(uint32 diff) override
        {
            if (EventMove)
            {
                if (EventMoveTimer <= diff)
                {
                    me->SetDisableGravity(true);
                    me->MonsterMoveWithSpeed(me->GetPositionX(), me->GetPositionY(), HIGHBORNE_LOC_Y_NEW, me->GetDistance(me->GetPositionX(), me->GetPositionY(), HIGHBORNE_LOC_Y_NEW) / (5000 * 0.001f));
                    me->SetPosition(me->GetPositionX(), me->GetPositionY(), HIGHBORNE_LOC_Y_NEW, me->GetOrientation());
                    EventMove = false;
                }
                else EventMoveTimer -= diff;
            }
            if (EventCast)
            {
                if (EventCastTimer <= diff)
                {
                    DoCast(me, SPELL_HIGHBORNE_AURA);
                    EventCast = false;
                }
                else EventCastTimer -= diff;
            }
        }
    };
};

/*######
## npc_parqual_fintallas
######*/

enum ParqualFintallas
{
    SPELL_MARK_OF_SHAME         = 6767
};

#define GOSSIP_HPF1             "Gul'dan"
#define GOSSIP_HPF2             "Kel'Thuzad"
#define GOSSIP_HPF3             "Ner'zhul"

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

    bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
    {
        ClearGossipMenuFor(player);
        if (action == GOSSIP_ACTION_INFO_DEF + 1)
        {
            CloseGossipMenuFor(player);
            creature->CastSpell(player, SPELL_MARK_OF_SHAME, false);
        }
        if (action == GOSSIP_ACTION_INFO_DEF + 2)
        {
            CloseGossipMenuFor(player);
            player->AreaExploredOrEventHappens(6628);
        }
        return true;
    }

    bool OnGossipHello(Player* player, Creature* creature) override
    {
        if (creature->IsQuestGiver())
            player->PrepareQuestMenu(creature->GetGUID());

        if (player->GetQuestStatus(6628) == QUEST_STATUS_INCOMPLETE && !player->HasAura(SPELL_MARK_OF_SHAME))
        {
            AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HPF1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
            AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HPF2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
            AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HPF3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
            SendGossipMenuFor(player, 5822, creature->GetGUID());
        }
        else
            SendGossipMenuFor(player, 5821, creature->GetGUID());

        return true;
    }
};

enum Factions
{
    FACTION_HOSTILE = 14,
    FACTION_FRIENDLY_TO_ALL = 35
};

/*######
## ALLIANCE
#######*/

enum WrynnMisc
{
    // Creatures
    NPC_WRYNN = 32401,

    // Texts
    WRYNN_SAY_PREP_1 = 0,
    WRYNN_SAY_PREP_2 = 1,
    WRYNN_SAY_PREP_3 = 2,
    WRYNN_SAY_PREP_4 = 3,
    WRYNN_SAY_PREP_5 = 4,
    WRYNN_SAY_PREP_6 = 5,
    WRYNN_SAY_SEWERS_1 = 6,
    WRYNN_SAY_SEWERS_2 = 7,
    WRYNN_SAY_SEWERS_3 = 8,
    WRYNN_SAY_SEWERS_4 = 9,
    WRYNN_SAY_APO_1 = 10,
    WRYNN_SAY_APO_2 = 11,
    WRYNN_SAY_APO_3 = 12,
    WRYNN_SAY_APO_4 = 13,
    WRYNN_SAY_APO_5 = 14,
    WRYNN_SAY_APO_6 = 15,
    WRYNN_SAY_APO_7 = 16,
    WRYNN_SAY_APO_8 = 17,
    WRYNN_SAY_APO_9 = 18,
    WRYNN_SAY_APO_10 = 19,
    WRYNN_SAY_APO_11 = 20,
    WRYNN_SAY_APO_12 = 21,
    WRYNN_SAY_THRONE_1 = 22,
    WRYNN_SAY_THRONE_2 = 23,
    WRYNN_SAY_THRONE_3 = 24,
    WRYNN_SAY_THRONE_4 = 25,
    WRYNN_SAY_THRONE_5 = 26,
    WRYNN_SAY_THRONE_6 = 27,
    WRYNN_SAY_THRONE_7 = 28,
    WRYNN_SAY_THRONE_8 = 29,
    WRYNN_SAY_THRONE_9 = 30,

    // Spells
    SPELL_WHIRLWIND = 41056,
    SPELL_WRYNN_BUFF = 60964,
    SPELL_HEROIC_LEAP = 57793,

    // Events
    EVENT_WHIRLWIND = 1,
    EVENT_HEROIC_LEAP = 2,
    EVENT_AGGRO_JAINA = 3,
    EVENT_WRYNN_BUFF = 4
};

enum JainaMisc
{
    // Creatures
    NPC_JAINA = 32402,

    // Texts
    JAINA_SAY_SEWERS_1 = 0,
    JAINA_SAY_APO_1 = 1,
    JAINA_SAY_THRONE_1 = 2,
    JAINA_SAY_THRONE_2 = 3,
    JAINA_SAY_THRONE_3 = 4,

    // Spells
    SPELL_FIREBALL = 20692,
    SPELL_BLIZZARD = 20680,
    SPELL_ELEMENTALS = 20681,
    SPELL_DEEPFREEZE = 61224,
    SPELL_JAINA_BUFF = 61011,

    // Events
    EVENT_FIREBALL = 1,
    EVENT_BLIZZARD = 2,
    EVENT_ELEMENTAL = 3
};

/*######
## HORDE
#######*/

enum ThrallMisc
{
    // Creatures
    NPC_THRALL = 32518,

    // Gossips
    GOSSIP_THRALL = 60193,

    // Texts
    THRALL_SAY_THRONE_A_1 = 0,
    THRALL_SAY_THRONE_A_2 = 1,

    THRALL_SAY_PREP_1 = 2,
    THRALL_SAY_PREP_2 = 3,
    THRALL_SAY_PREP_3 = 4,
    THRALL_SAY_PREP_4 = 5,
    THRALL_SAY_PREP_5 = 6,
    THRALL_SAY_PREP_6 = 7,
    THRALL_SAY_PREP_7 = 8,
    THRALL_SAY_PREP_8 = 9,
    THRALL_SAY_COURTYARD_1 = 10,
    THRALL_SAY_COURTYARD_2 = 11,
    THRALL_SAY_COURTYARD_3 = 12,
    THRALL_SAY_COURTYARD_4 = 13,
    THRALL_SAY_COURTYARD_5 = 14,
    THRALL_SAY_ELEVATOR_1 = 15,
    THRALL_SAY_ELEVATOR_2 = 16,
    THRALL_SAY_ELEVATOR_3 = 17,
    THRALL_SAY_SANCTUM_1 = 18,
    THRALL_SAY_SANCTUM_2 = 19,
    THRALL_SAY_SANCTUM_3 = 20,
    THRALL_SAY_SANCTUM_4 = 21,
    THRALL_SAY_SANCTUM_5 = 22,
    THRALL_SAY_SANCTUM_6 = 23,
    THRALL_SAY_SANCTUM_7 = 24,
    THRALL_SAY_THRONE_1 = 25,
    THRALL_SAY_THRONE_2 = 26,
    THRALL_SAY_THRONE_3 = 27,
    THRALL_SAY_THRONE_4 = 28,
    THRALL_SAY_THRONE_5 = 29,
    THRALL_SAY_THRONE_6 = 30,
    THRALL_SAY_THRONE_7 = 31,
    THRALL_SAY_THRONE_8 = 32,
    THRALL_SAY_THRONE_9 = 33,
    THRALL_SAY_THRONE_10 = 34,
    THRALL_SAY_THRONE_11 = 35,

    // Sounds
    SOUND_THRALL_1 = 16212,
    SOUND_THRALL_2 = 16214,

    // Spells
    SPELL_THRALL_BUFF = 64670,
    SPELL_TIDAL_WAVE = 59635,
    SPELL_TIDAL_WAVE_SUMMON = 59627,
    SPELL_TIDAY_FURY = 59631,
    SPELL_TIDAY_FURY_EFFECT = 59629,
    SPELL_CALL_OF_AIR = 59898,
    SPELL_CYCLONE_FALL = 59892,
    SPELL_CALL_OF_EARTH = 60207,
    SPELL_PORTAL_COLLAPSE = 60285,
    SPELL_TELEPORT_SPAWN_VISUAL = 60427,
    SPELL_SUMMONED_DEMON = 7741,
    SPELL_DEEP_FREEZE = 60511,
    SPELL_GREATER_MASS_TELEPORT = 60516,
    SPELL_WATER_REVENANT_ENTRANCE = 55760,
    SPELL_TELEPORT_OG = 60699,

    // Combatspells
    SPELL_CHAIN_LIGHTNING = 59517,
    SPELL_LAVA_BURST = 59519,
    SPELL_THUNDER = 59507,
    SPELL_HEROIC_VANGUARD = 59506,

    // Events
    EVENT_CHAIN_LIGHTNING = 1,
    EVENT_LAVA_BURST = 2,
    EVENT_THUNDER = 3,
    EVENT_AGGRO_SYLVANAS = 4,
    EVENT_THRALL_BUFF = 5,

    // Creatures
    NPC_WARSONG_BATTLEGUARD = 31739,
    NPC_VORTEX = 31782,
    NPC_TIDAL_WAVE = 31765,
    NPC_WHIRLWIND = 31688,
    NPC_CAVE_DUMMY = 32200,
    NPC_SLINGER_TRIGGER = 31577,
    NPC_OVERLORD_SAURFANG = 32315,
    NPC_DISTANT_VOICE = 32277,
    NPC_PLAGUE_TRIGGER = 31576,
    NPC_BLIGHT_ABBERATION = 31844,

    // Gameobjects
    GO_BLOCKED_PASSAGE = 194935,
    GO_HORDE_BANNER = 194004,
    GO_PORTAL_ORGRIMMAR = 193427,

    // Mounts
    MODEL_WHITE_WULF = 14575
};

enum SlyvanasMisc
{
    // Creatures
    NPC_SYLVANAS = 32365,

    // Texts
    SYLVANAS_SAY_COURTYARD_1 = 0,
    SYLVANAS_SAY_ELEVATOR_1 = 1,
    SYLVANAS_SAY_SANCTUM_1 = 2,
    SYLVANAS_SAY_SANCTUM_2 = 3,
    SYLVANAS_SAY_SANCTUM_3 = 4,
    SYLVANAS_SAY_SANCTUM_4 = 5,
    SYLVANAS_SAY_SANCTUM_5 = 6,
    SYLVANAS_SAY_THRONE_1 = 7,

    // Spells
    SPELL_SYLVANAS_BUFF = 59756,
    SPELL_SHRIEK_OF_HIGHBORN = 59514,
    SPELL_LEAP_TO_PLATFORM = 56347,

    // Events
    EVENT_SHRIEK_OF_HIGHBORN = 1,
    EVENT_SYLVANAS_BUFF = 6,

    // Mounts
    MODEL_SKELETON_MOUNT = 10721
};

enum SaurfangMisc
{
    // Texts
    SAY_SAURFANG_ARRIVAL_1 = 0,
    SAY_SAURFANG_ARRIVAL_2 = 1,
    SAY_SAURFANG_ARRIVAL_3 = 2
};

/*######
## ENEMY
#######*/

enum BlightWormMisc
{
    // Creatures
    NPC_BLIGHTWORM = 32483,

    // Spells
    SPELL_INGEST = 61123,
    SPELL_INGEST_TRIGGER = 61124,
    SPELL_BLIGHT_BREATH = 61125,

    // Events
    EVENT_INFEST = 1,
    EVENT_BLIGHT_BREATH = 2
};

enum PutressMisc
{
    // Creatures
    NPC_PUTRESS = 31530,

    NPC_EXPERIMENT = 32519,
    NPC_GENERATOR = 36212,

    // Spells
    SPELL_BLIGHT_EMPOWERMENT = 59449,
    SPELL_BLIGHT_OVERLOAD = 61181,
    SPELL_BLIGHT_BARREL = 59460,
    SPELL_UNHOLY_FRENZY = 60300,
    SPELL_PUTRESS_CASTING_STATE = 59447,

    // Texts
    PUTRESS_SAY_1 = 0,
    PUTRESS_SAY_2 = 1,
    PUTRESS_SAY_3 = 2,
    PUTRESS_SAY_4 = 3,
    PUTRESS_SAY_5 = 4,
    PUTRESS_SAY_6 = 5,
    PUTRESS_SAY_7 = 6,
    PUTRESS_SAY_8 = 7,

    // Sounds
    SOUND_PUTRESS = 16920,
};

enum KhanokMisc
{
    // Creatures
    NPC_KHANOK = 32511
};

enum ValimathrasMisc
{
    // Creatures
    NPC_VARIMATHRAS = 31565,
    NPC_VARIMATHRAS_PORTAL = 31811,

    // Texts
    SAY_VALIMATHRAS_INTRO_0 = 0,
    SAY_VALIMATHRAS_INTRO_1 = 1,
    SAY_VALIMATHRAS_INTRO_2 = 2,
    SAY_VALIMATHRAS_INNER_SANKTUM_0 = 3,
    SAY_VALIMATHRAS_INNER_SANKTUM_1 = 4,
    SAY_CLOSE_DOOR = 5,
    SAY_THRONE_1 = 6,
    SAY_THRONE_2 = 7,
    SAY_THRONE_3 = 8,
    SAY_THRONE_4 = 9,
    SAY_THRONE_5 = 10,
    SAY_THRONE_6 = 11,
    SAY_VALIMATHRAS_ATTACK = 12,

    // Spells
    SPELL_VALIMATHRAS_PORTAL = 68424,
    SPELL_CARION_SWARM = 59434,
    SPELL_DRAIN_LIFE = 17238,
    SPELL_MIGHT_OF_VARIMATHRAS = 59424,
    SPELL_SHADOW_BOLT_VOLLEY = 20741,
    SPELL_AURA_OF_VARIMATHRAS = 60289,
    SPELL_LEGION_PORTAL = 59680,
    SPELL_OPENING_LEGION_PORTALS = 60224,
};

enum TrashMisc
{
    // Creatures
    NPC_DREADLORD = 32391,
    NPC_GUARDIAN = 32390,
    NPC_CHEMIST = 32395,
    NPC_BETRAYER = 32394,
    NPC_FELBEAST = 32392,
    NPC_DOCTOR = 32397,
    NPC_COLLABORATOR = 32396,
    NPC_SW_SOLDIER = 32387,
    NPC_HORDE_SOLDIER = 32510,
    NPC_HORDE_GUARD = 31739,

    // Horde
    NPC_TREACHEROUS_GUARDIAN_H = 31532,
    NPC_DREADLORD_H = 31531,
    NPC_FELBEAST_H = 31528,
    NPC_MARAUDER_H = 31527,
    NPC_BETRAYER_H = 31529,
    NPC_CHEMIST_H = 31482,
    NPC_COLLABORATOR_H = 31524,
    NPC_DOCTOR_H = 31516,
    NPC_DOOMGUARD_PILLARGER = 32159,
    NPC_BLIGHT_SLINGER = 31526,
    NPC_BLIGHT_SPREADER = 31831,
    NPC_FELGUARD_MORADEUR = 32393,
    NPC_LEGION_OVERLORD = 32271,
    NPC_LEGION_INVADER = 32269,
    NPC_LEGION_DREADWHISPER = 32270,

    // Texts
    SAY_BURN_UC = 0,
    SAY_PUTRESS_ANGER = 1,
    SAY_FOR_THE_HORDE = 2
};

enum QuestMisc
{
    QUEST_BATTLE_A = 13377,
    QUEST_BATTLE_H = 13267,

    SPELL_PHASING_HORDE = 59062,

    NPC_VOICE = 32277,

    VOICE_SAY_THRONE = 0,

    WAVE_MAXCOUNT = 12,
    GENERATOR_MAXCOUNT = 5,
    ALLIANCE_FORCE_MAXCOUNT = 6,
    HORDE_FORCE_MAXCOUNT = 6,
    WAVE_COURTYARD_FIGHT = 9,

    ZONE_TIRISFAL = 85,
    ZONE_UNDERCITY = 1497
};

enum Worldstates
{
    // Alliance
    WORLD_STATE_MANHUNT_COUNTDOWN_A = 3958,
    WORLD_STATE_MANHUNT_STARTS_A = 3966,
    WORLD_STATE_SEWERS_FIGHT_A = 3962,
    WORLD_STATE_SEWERS_DONE_A = 3964,
    WORLD_STATE_APOTHECARIUM_FIGHT_A = 3972,
    WORLD_STATE_APOTHECARIUM_DONE_A = 3971,
    WORLD_STATE_FAIL_A = 3963,

    // Horde
    WORLD_STATE_BATTLE_COUNTDOWN_H = 3876,
    WORLD_STATE_BATTLE_START_H = 3875,
    WORLD_STATE_COURTYARD_FIGHT_H = 3885,
    WORLD_STATE_COURTYARD_DONE_H = 3886,
    WORLD_STATE_INNER_SANKTUM_FIGHT_H = 3887,
    WORLD_STATE_INNER_SANKTUM_DONE_H = 3888,
    WORLD_STATE_APOTHECARIUM_FIGHT_H = 3891, // unused
    WORLD_STATE_APOTHECARIUM_DONE_H = 3892, // unused
    WORLD_STATE_ROYAL_QUARTER_FIGHT_H = 3889,
    WORLD_STATE_ROYAL_QUARTER_DONE_H = 3890,
    WORLD_STATE_FAIL_H = 3878
};

struct Location
{
    float x, y, z, o;
};

static Location AllianceSpawn[] =
{
    { 1603.97f, 718.02f, 65.10f, 0  }, // guardian // sewers
    { 1604.78f, 657.22f, 40.80f, 0  }, // wave 1
    { 1632.13f, 649.19f, 30.67f, 0  }, // wave 2
    { 1683.66f, 590.37f, -8.59f, 0  }, // wave 3
    { 1665.51f, 543.32f, -13.23f, 0 }, // wave 4
    { 1684.25f, 542.06f, -11.99f, 0 },
    { 1680.86f, 596.73f, -6.37f, 0  },
    { 1676.41f, 558.28f, -18.46f, 0 }, // Blightworm
    { 1685.16f, 620.41f, 5.74f, 0   }, // soldiers
    { 0.0f, 0.0f, 0.0f, 0           }, // trash wave
    { 1500.03f, 409.59f, -62.18f, 0 }, // guardians
    { 1444.25f, 453.86f, -70.48f, 0 }, // dreadlords
    { 1432.43f, 403.20f, -85.26f, 0 }, // putress
    { 1456.51f, 417.55f, -84.95f, 0 }, // experiment
    { 1415.38f, 377.54f, -84.95f, 0 }, // experiment
    { 1422.69f, 446.36f, -76.22f, 0 }, // experiment
    { 1386.19f, 412.01f, -77.17f, 0 }, // experiment
    { 1300.75f, 347.39f, -65.02f, 0 }, // thrall
    { 1296.79f, 348.37f, -65.02f, 0 }, // sylvanas
    { 1293.46f, 351.19f, -65.02f, 0 }, // horde soldier 32510
    { 1293.79f, 347.75f, -65.02f, 0 },
    { 1296.24f, 345.34f, -65.02f, 0 },
    { 1300.41f, 344.47f, -65.02f, 0 },
    { 1303.96f, 345.26f, -65.02f, 0 },
    { 1305.43f, 348.06f, -65.02f, 0 },
    { 1306.92f, 390.59f, -64.33f, 4.472f }, // aliance soldiers
    { 1311.03f, 390.10f, -64.19f, 4.472f },
    { 1315.31f, 388.98f, -64.18f, 4.472f },
    { 1316.38f, 392.82f, -63.32f, 4.472f },
    { 1311.93f, 394.38f, -63.25f, 4.472f },
    { 1307.92f, 395.53f, -63.24f, 4.472f },
};

static Location AllianceWP[] =
{
    { 1737.06f, 734.176f, 48.8f, 0      }, // Jaina sewers UNUSED
    { 1682.92f, 730.89f, 76.84f, 0      }, // UNUSED
    { 1662.18f, 540.67f, -11.64f, 0.60f }, // soldiers
    { 1676.45f, 544.81f, -16.45f, 2.23f },
    { 1687.14f, 555.37f, -16.62f, 2.35f },
    { 1666.22f, 477.69f, -11.89f, 2.14f },
    { 1594.92f, 422.44f, -46.38f, 0     }, // jaina balcony
    { 1423.19f, 412.73f, -84.60f, 0     }, // jaina putress
    { 1311.93f, 394.38f, -63.25f, 0     }, // jaina throne room wait
    { 1300.75f, 347.39f, -65.02f, 0     }, // jaina throne room
};

static Location HordeSpawn[] =
{
    { 1581.94f, 383.22f, -62.22f, 0 } // Khanok
};

static Location ThrallSpawn[] =
{
    // Vortex
    { 1880.0001f, 237.8242f, 59.472f, 3.060f  },
    // NPC_DOCTOR_H
    { 1808.29f, 264.223f, 65.3997f, 5.41411f  },
    { 1792.05f, 282.213f, 70.3996f, 5.46674f  },
    { 1798.03f, 197.815f, 70.3997f, 0.550926f },
    // NPC_CHEMIST_H
    { 1806.59f, 266.874f, 65.3997f, 5.528f    },
    { 1808.18f, 211.038f, 65.3996f, 0.799897f },
    { 1803.01f, 213.037f, 65.3996f, 0.496734f },
    { 1803.01f, 213.037f, 65.3996f, 0.496734f },
    { 1809.58f, 197.105f, 70.3999f, 0.593338f },
    { 1816.2f, 196.655f, 70.3999f, 0.820318f  },
    { 1791.75f, 197.267f, 70.3999f, 0.584698f },
    { 1814.45f, 279.218f, 70.3998f, 5.52878f  },
    { 1793.98f, 280.346f, 70.3996f, 5.50522f  },
    // NPC_TREACHEROUS_GUARDIAN_H
    { 1806.31f, 213.05f, 65.3998f, 0.52893f   },
    { 1813.54f, 197.01f, 70.3999f, 0.807745f  },
    { 1789.15f, 197.6f, 70.3999f, 0.367921f   },
    { 1804.79f, 263.79f, 65.3998f, 5.49265f   },
    { 1818.12f, 280.6f, 70.3997f, 5.59475f    },
    { 1790.19f, 279.868f, 70.3997f, 5.77539f  },
    // NPC_BLIGHT_SLINGER
    { 1827.64f, 196.716f, 70.3996f, 1.61907f  },
    { 1778.47f, 195.17f, 70.3996f, 0.989177f  },
    { 1826.86f, 280.42f, 70.3997f, 4.69234f   },
    { 1779.52f, 280.479f, 70.3996f, 5.37171f  },
    // NPC_VARIMATHRAS
    { 1810.926f, 236.826f, 62.753f, 0.148f    },
    // NPC_VARIMATHRAS_PORTAL
    { 1805.194f, 235.725f, 65.173f, 0.334f    },
    // NPC_TREACHEROUS_GUARDIAN_H
    { 1753.151f, 238.632f, 61.372f, 0.049f    },
    // NPC_DOCTOR_H
    { 1785.647f, 212.416f, 59.686f, 1.663f    },
    // NPC_CHEMIST_H
    { 1784.802f, 265.842f, 59.458f, 4.722f    },
    // NPC_BLIGHT_ABBERATION
    { 1805.753f, 285.499f, 70.399f, 4.691f    },
    // NPC_WARSONG_BATTLEGUARD
    { 1835.734f, 261.468f, 59.901f, 3.720f    },
    { 1821.705f, 256.014f, 60.016f, 3.512f    },
    { 1837.801f, 216.490f, 60.105f, 2.381f    },
    { 1826.564f, 227.173f, 60.198f, 2.381f    },
    { 1804.952f, 199.451f, 70.399f, 1.478f    },
    { 1805.011f, 211.147f, 65.399f, 1.607f    },
    { 1805.447f, 277.378f, 70.400f, 4.627f    },
    { 1805.389f, 265.287f, 65.399f, 4.718f    },
    // GO_HORDE_BANNER
    { 1750.697f, 232.644f, 64.748f, 6.151f    },
    { 1750.706f, 245.729f, 65.585f, 0.009f    },
    { 1737.508f, 239.323f, 62.641f, 0.054f    },
    { 1634.253f, 226.927f, 62.592f, 0.983f    },
    { 1628.978f, 231.074f, 62.592f, 0.411f    },
    { 1589.801f, 236.328f, 60.149f, 0.157f    },
    { 1589.135f, 243.569f, 60.149f, 5.887f    },
    // NPC_WARSONG_BATTLEGUARD
    { 1590.156f, 243.612f, 60.151f, 5.159f    },
    { 1631.783f, 249.797f, 62.591f, 5.523f    },
    // NPC_CAVE_BUNNY
    { 1543.961548f, 240.997314f,  52.765247f, 4.885232f  },
    { 1543.961548f, 240.997314f,  45.870247f, 4.885232f  },
    { 1543.961548f, 240.997314f,  32.318245f, 4.885232f  },
    { 1543.961548f, 240.997314f,  18.325245f, 4.885232f  },
    { 1543.961548f, 240.997314f,   4.570244f, 4.885232f   },
    { 1543.961548f, 240.997314f,  -7.679757f, 4.885232f  },
    { 1543.961548f, 240.997314f, -20.867758f, 4.885232f },
    { 1543.961548f, 240.997314f, -33.817757f, 4.885232f },
    { 1543.961548f, 240.997314f, -41.360523f, 4.885232f },
    // Jumppoint Thrall
    { 1542.196f, 241.254f, -41.360f, 3.276f   },
    // Jumppoint Sylvanas
    { 1543.511f, 236.552f, -41.360f, 3.050f   },
    // NPC_WARSONG_BATTLEGUARD
    { 1527.904f, 206.368f, -43.058f, 1.179f   },
    { 1534.110f, 216.822f, -43.058f, 4.569f   },
    // Undercity Top Trashpackspawn
    { 1585.577f, 240.465f, -52.150f, 3.193f   },
    // Undercity Buttom Trashpackspawn Left
    { 1528.291f, 269.948f, -62.178f, 0.672f   },
    // Undercity Buttom Trashpackspawn Right
    { 1618.600f, 302.468f, -62.177f, 0.466f   },
    // NPC_BLIGHT_ABBERATION Jump Location
    { 1805.845f, 251.430f, 60.587f, 4.722f    },
    // Valimathras Inner Sanctum Spawn
    { 1596.689f, 422.276f, -46.387f, 4.720f   },
    // Valimathras Inner Sanctum Portal Spawn
    { 1596.665f, 425.150f, -43.357f, 4.704f   },
    // NPC_KHANOK - Inner Sunktum Spawn Left
    { 1544.917f, 367.955f, -62.182f, 0.243f   },
    // NPC_KHANOK - Inner Sunktum Spawn Right
    { 1643.633f, 368.598f, -62.156f, 2.873f   },
    // NPC_KHANOK - Inner Sunktum Spawn Top
    { 1591.325f, 397.874f, -4.130f, 6.191f    },
    // NPC_KHANOK - Inner Sunktum Middle
    { 1573.400f, 398.450f, -65.862f, 5.618f   },
    // NPC_WARSONG_BATTLEGUARD - NPC_KHANOK WinSpawn
    { 1590.502f, 375.876f, -62.177f, 3.237f   },
    // Valimathras Room Preparation
    // Stones
    { 1437.063f, 403.759f, -57.818f, 5.517f       },
    { 1442.303f, 392.866f, -58.111f, 5.450552f    },
    { 1447.207f, 397.880f, -58.102f, 5.403430f    },
    // Valimathras
    { 1290.323f, 315.996f, -57.320f, 1.322530f    },
    // Valimathras Portals
    { 1326.634f, 58.580f, -60.661907f, 4.008590f  },
    { 1341.374f, 310.516f, -60.661415f, 2.838340f },
    { 1304.504f, 276.763f, -60.661442f, 1.833025f },
    { 1258.071f, 289.268f, -60.661636f, 0.741321f },
    { 1245.688f, 336.614f, -60.661243f, 5.983858f },
    { 1280.382f, 371.556f, -60.661404f, 4.931426f },
    // Valimathras Trashspawn
    { 1325.059f, 332.652f, -65.027f, 2.186f       },
    { 1270.474f, 350.982f, -65.027f, 0.034f       },
};

#define GOSSIP_WRYNN      "Reporting for duty, your majesty! Let the assault begin!"
#define GOSSIP_THRALL     "I am ready, Warchief."

/*######
## ALLIANCE
#######*/

/*######
## npc_varian_wrynn
######*/

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

    bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
    {
        ClearGossipMenuFor(player);

        switch (action)
        {
            case GOSSIP_ACTION_INFO_DEF + 1:
                CloseGossipMenuFor(player);

                if (auto ai = CAST_AI(npc_varian_wrynn::npc_varian_wrynnAI, creature->AI()))
                {
                    ai->Start(true, true, player->GetGUID());
                    if (Creature* jaina = GetClosestCreatureWithEntry(creature, NPC_JAINA, 50.0f))
                        ai->jainaGUID = jaina->GetGUID();
                    else
                        ai->jainaGUID.Clear();
                    ai->SetDespawnAtEnd(false);
                    ai->SetDespawnAtFar(false);
                }

                break;
        }

        return true;
    }

    bool OnGossipHello(Player* player, Creature* creature) override
    {
        if (creature->IsQuestGiver())
            player->PrepareQuestMenu(creature->GetGUID());

        if (player->GetQuestStatus(QUEST_BATTLE_A) == QUEST_STATUS_INCOMPLETE)
            AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_WRYNN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);

        SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());

        return true;
    }

    struct npc_varian_wrynnAI : public npc_escortAI
    {
        npc_varian_wrynnAI(Creature* creature) : npc_escortAI(creature)
        {
            allianceGuardsGUID.clear();
        }

        bool bStepping;
        bool summoned;

        uint32 step;
        uint32 phaseTimer;

        uint32 whirlwindTimer;

        ObjectGuid jainaGUID;
        ObjectGuid putressGUID;
        ObjectGuid blightWormGUID;
        ObjectGuid khanokGUID;
        ObjectGuid thrallGUID;
        ObjectGuid sylvanasGUID;

        ObjectGuid generatorGUID[GENERATOR_MAXCOUNT];
        ObjectGuid allianceForcesGUID[ALLIANCE_FORCE_MAXCOUNT];
        ObjectGuid hordeForcesGUID[HORDE_FORCE_MAXCOUNT];
        GuidVector allianceGuardsGUID;

        EventMap _events;

        void EnterEvadeMode() override
        {
            me->DeleteThreatList();
            me->CombatStop(true);
            me->SetLootRecipient(nullptr);

            if (HasEscortState(STATE_ESCORT_ESCORTING))
            {
                AddEscortState(STATE_ESCORT_RETURNING);
                ReturnToLastPoint();
            }
            else
            {
                me->GetMotionMaster()->MoveTargetedHome();
                Reset();
            }
        }

        void Reset() override
        {
            if (!HasEscortState(STATE_ESCORT_ESCORTING))
            {
                me->SetCorpseDelay(1);
                me->SetRespawnTime(1);
                me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);

                bStepping = false;
                step = 0;
                phaseTimer = 0;
                jainaGUID.Clear();
                _events.ScheduleEvent(EVENT_WHIRLWIND, 5 * IN_MILLISECONDS);
                _events.ScheduleEvent(EVENT_HEROIC_LEAP, 10 * IN_MILLISECONDS);
                _events.ScheduleEvent(EVENT_AGGRO_JAINA, 2 * IN_MILLISECONDS);
                _events.ScheduleEvent(EVENT_WRYNN_BUFF, 2 * IN_MILLISECONDS);
                me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);

                if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                {
                    putress->DespawnOrUnsummon();
                    putressGUID.Clear();
                }

                if (Creature* blightWorm = ObjectAccessor::GetCreature(*me, blightWormGUID))
                {
                    blightWorm->DespawnOrUnsummon();
                    blightWormGUID.Clear();
                }

                if (Creature* khanok = ObjectAccessor::GetCreature(*me, khanokGUID))
                {
                    khanok->DespawnOrUnsummon();
                    khanokGUID.Clear();
                }

                if (Creature* thrall = ObjectAccessor::GetCreature(*me, thrallGUID))
                {
                    thrall->DespawnOrUnsummon();
                    thrallGUID.Clear();
                }

                if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID))
                {
                    sylvanas->DespawnOrUnsummon();
                    sylvanasGUID.Clear();
                }

                for (uint8 i = 0; i < GENERATOR_MAXCOUNT; ++i)
                {
                    if (Creature* temp = ObjectAccessor::GetCreature(*me, generatorGUID[i]))
                    {
                        generatorGUID[i].Clear();
                        temp->DespawnOrUnsummon();
                    }
                }

                for (uint8 i = 0; i < ALLIANCE_FORCE_MAXCOUNT; ++i)
                {
                    if (Creature* temp = ObjectAccessor::GetCreature(*me, allianceForcesGUID[i]))
                    {
                        allianceForcesGUID[i].Clear();
                        temp->DespawnOrUnsummon();
                    }
                }

                for (ObjectGuid const& guid : allianceGuardsGUID)
                    if (Creature* temp = ObjectAccessor::GetCreature(*me, guid))
                        temp->DespawnOrUnsummon();

                allianceGuardsGUID.clear();

                for (uint8 i = 0; i < HORDE_FORCE_MAXCOUNT; ++i)
                {
                    if (Creature* temp = ObjectAccessor::GetCreature(*me, hordeForcesGUID[i]))
                    {
                        hordeForcesGUID[i].Clear();
                        temp->DespawnOrUnsummon();
                    }
                }
            }
        }

        void JustSummoned(Creature* summonedCreature) override
        {
            switch (summonedCreature->GetEntry())
            {
                case NPC_GENERATOR:
                    summonedCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                    summonedCreature->ApplySpellImmune(0, IMMUNITY_ID, SPELL_WRYNN_BUFF, true);
                    summonedCreature->ApplySpellImmune(0, IMMUNITY_ID, SPELL_THRALL_BUFF, true);
                    summonedCreature->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);
                    break;
                default:
                    break;
            }
        }

        void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
        {
            switch (summon->GetEntry())
            {
                case NPC_BLIGHTWORM:
                    UpdateWorldState(me->GetMap(), WORLD_STATE_SEWERS_FIGHT_A, 0);
                    UpdateWorldState(me->GetMap(), WORLD_STATE_SEWERS_DONE_A, 1);
                    bStepping = true;
                    break;
                case NPC_PUTRESS:
                    UpdateWorldState(me->GetMap(), WORLD_STATE_APOTHECARIUM_FIGHT_A, 0);
                    UpdateWorldState(me->GetMap(), WORLD_STATE_APOTHECARIUM_DONE_A, 1);
                    bStepping = true;
                    break;
                default:
                    break;
            }
        }

        void UpdateWorldState(Map* map, uint32 id, uint32 state)
        {
            Map::PlayerList const& players = map->GetPlayers();

            if (!players.isEmpty())
            {
                for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
                {
                    if (Player* player = itr->GetSource())
                        player->SendUpdateWorldState(id, state);
                }
            }
        }

        void SetHoldState(bool bOnHold)
        {
            SetEscortPaused(bOnHold);
        }

        void JumpToNextStep(uint32 uiTimer)
        {
            phaseTimer = uiTimer;
            ++step;
        }

        void SpawnWave(uint32 waveId)
        {
            switch (waveId)
            {
                case 0:
                    me->SummonCreature(NPC_GUARDIAN, AllianceSpawn[0].x, AllianceSpawn[0].y, AllianceSpawn[0].z, TEMPSUMMON_DEAD_DESPAWN);
                    break;
                case 1:
                    for (uint8 i = 0; i < WAVE_MAXCOUNT; ++i)
                    {
                        switch (urand(0, 12))
                        {
                            case 0:
                                me->SummonCreature(NPC_DREADLORD, AllianceSpawn[1].x + rand32() % 5, AllianceSpawn[1].y + rand32() % 5, AllianceSpawn[1].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                            case 2:
                            case 3:
                                me->SummonCreature(NPC_DOCTOR, AllianceSpawn[1].x + rand32() % 5, AllianceSpawn[1].y + rand32() % 5, AllianceSpawn[1].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 4:
                            case 5:
                            case 6:
                                me->SummonCreature(NPC_FELBEAST, AllianceSpawn[1].x + rand32() % 5, AllianceSpawn[1].y + rand32() % 5, AllianceSpawn[1].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 7:
                            case 8:
                            case 9:
                                me->SummonCreature(NPC_BETRAYER, AllianceSpawn[1].x + rand32() % 5, AllianceSpawn[1].y + rand32() % 5, AllianceSpawn[1].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 10:
                            case 11:
                            case 12:
                                me->SummonCreature(NPC_COLLABORATOR, AllianceSpawn[1].x + rand32() % 5, AllianceSpawn[1].y + rand32() % 5, AllianceSpawn[1].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                case 2:
                    for (uint8 i = 0; i < WAVE_MAXCOUNT; ++i)
                    {
                        switch (urand(0, 3))
                        {
                            case 0:
                                me->SummonCreature(NPC_COLLABORATOR, AllianceSpawn[2].x - rand32() % 5, AllianceSpawn[2].y - rand32() % 5, AllianceSpawn[2].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                                me->SummonCreature(NPC_DOCTOR, AllianceSpawn[2].x - rand32() % 5, AllianceSpawn[2].y - rand32() % 5, AllianceSpawn[2].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 2:
                                me->SummonCreature(NPC_FELBEAST, AllianceSpawn[2].x - rand32() % 5, AllianceSpawn[2].y - rand32() % 5, AllianceSpawn[2].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 3:
                                me->SummonCreature(NPC_BETRAYER, AllianceSpawn[2].x - rand32() % 5, AllianceSpawn[2].y - rand32() % 5, AllianceSpawn[2].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                case 3:
                    for (uint8 i = 0; i < WAVE_MAXCOUNT; ++i)
                    {
                        switch (urand(0, 4))
                        {
                            case 0:
                                me->SummonCreature(NPC_GUARDIAN, AllianceSpawn[3].x - rand32() % 5, AllianceSpawn[3].y - rand32() % 5, AllianceSpawn[3].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                            case 2:
                                me->SummonCreature(NPC_CHEMIST, AllianceSpawn[3].x - rand32() % 5, AllianceSpawn[3].y - rand32() % 5, AllianceSpawn[3].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 3:
                            case 4:
                                me->SummonCreature(NPC_DOCTOR, AllianceSpawn[3].x - rand32() % 5, AllianceSpawn[3].y - rand32() % 5, AllianceSpawn[3].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                case 4:
                    for (uint8 i = 0; i < WAVE_MAXCOUNT; ++i)
                    {
                        switch (urand(0, 5))
                        {
                            case 0:
                                if (Unit* temp = me->SummonCreature(NPC_DOCTOR, AllianceSpawn[4].x - rand32() % 5, AllianceSpawn[4].y - rand32() % 5, AllianceSpawn[4].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), false);
                                break;
                            case 1:
                                if (Unit* temp = me->SummonCreature(NPC_CHEMIST, AllianceSpawn[4].x - rand32() % 5, AllianceSpawn[4].y - rand32() % 5, AllianceSpawn[4].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), false);
                                break;
                            case 2:
                                if (Unit* temp = me->SummonCreature(NPC_BETRAYER, AllianceSpawn[4].x - rand32() % 5, AllianceSpawn[4].y - rand32() % 5, AllianceSpawn[4].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), false);
                                break;
                            case 3:
                                if (Unit* temp = me->SummonCreature(NPC_DOCTOR, AllianceSpawn[5].x - rand32() % 5, AllianceSpawn[5].y - rand32() % 5, AllianceSpawn[5].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), false);
                                break;
                            case 4:
                                if (Unit* temp = me->SummonCreature(NPC_CHEMIST, AllianceSpawn[5].x - rand32() % 5, AllianceSpawn[5].y - rand32() % 5, AllianceSpawn[5].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), false);
                                break;
                            case 5:
                                if (Unit* temp = me->SummonCreature(NPC_BETRAYER, AllianceSpawn[5].x - rand32() % 5, AllianceSpawn[5].y - rand32() % 5, AllianceSpawn[5].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), false);
                                break;
                        }
                    }
                    break;
                case 5:
                    for (uint8 i = 0; i < WAVE_MAXCOUNT; ++i)
                        if (Unit* temp = me->SummonCreature(NPC_GUARDIAN, AllianceSpawn[6].x - rand32() % 5, AllianceSpawn[6].y - rand32() % 5, AllianceSpawn[6].z, TEMPSUMMON_DEAD_DESPAWN))
                            temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), false);
                    break;
                case 6:
                    if (Unit* temp = me->SummonCreature(NPC_BLIGHTWORM, AllianceSpawn[7].x, AllianceSpawn[7].y, AllianceSpawn[7].z, TEMPSUMMON_MANUAL_DESPAWN))
                    {
                        blightWormGUID = temp->GetGUID();
                        temp->AddThreat(me, 100.0f);
                        me->AddThreat(temp, 100.0f);
                        if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                        {
                            temp->AddThreat(jaina, 100.0f);
                            jaina->AddThreat(temp, 100.0f);
                        }
                    }
                    if (Unit* temp = me->SummonCreature(NPC_KHANOK, HordeSpawn[0].x, HordeSpawn[0].y, HordeSpawn[0].z, TEMPSUMMON_MANUAL_DESPAWN))
                    {
                        khanokGUID = temp->GetGUID();
                        if (Creature* khanok = ObjectAccessor::GetCreature(*me, khanokGUID))
                            khanok->setDeathState(JUST_DIED);
                    }
                    if (Unit* temp = me->SummonCreature(NPC_PUTRESS, AllianceSpawn[12].x, AllianceSpawn[12].y, AllianceSpawn[12].z, TEMPSUMMON_MANUAL_DESPAWN))
                    {
                        putressGUID = temp->GetGUID();
                        temp->CastSpell(temp, SPELL_PUTRESS_CASTING_STATE);
                    }
                    for (uint8 i = 0; i < GENERATOR_MAXCOUNT; ++i)
                    {
                        switch (i)
                        {
                            case 0:
                                if (Unit* temp = me->SummonCreature(NPC_GENERATOR, 1433.142212f, 402.493835f, -80.515945f, TEMPSUMMON_MANUAL_DESPAWN))
                                {
                                    generatorGUID[i] = temp->GetGUID();
                                    if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                        temp->CastSpell(putress, SPELL_BLIGHT_EMPOWERMENT);
                                }
                                break;
                            case 1:
                                if (Unit* temp = me->SummonCreature(NPC_GENERATOR, 1428.677979f, 399.753418f, -79.141609f, TEMPSUMMON_MANUAL_DESPAWN))
                                {
                                    generatorGUID[i] = temp->GetGUID();
                                    if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                        temp->CastSpell(putress, SPELL_BLIGHT_EMPOWERMENT, false, 0, 0, generatorGUID[0]);
                                }
                                break;
                            case 2:
                                if (Unit* temp = me->SummonCreature(NPC_GENERATOR, 1425.163330f, 402.268951f, -79.299744f, TEMPSUMMON_MANUAL_DESPAWN))
                                {
                                    generatorGUID[i] = temp->GetGUID();
                                    if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                        temp->CastSpell(putress, SPELL_BLIGHT_EMPOWERMENT, false, 0, 0, generatorGUID[0]);
                                }
                                break;
                            case 3:
                                if (Unit* temp = me->SummonCreature(NPC_GENERATOR, 1427.323242f, 406.853088f, -78.195641f, TEMPSUMMON_MANUAL_DESPAWN))
                                {
                                    generatorGUID[i] = temp->GetGUID();
                                    if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                        temp->CastSpell(putress, SPELL_BLIGHT_EMPOWERMENT, false, 0, 0, generatorGUID[0]);
                                }
                                break;
                            case 4:
                                if (Unit* temp = me->SummonCreature(NPC_GENERATOR, 1432.465210f, 407.460022f, -81.689384f, TEMPSUMMON_MANUAL_DESPAWN))
                                {
                                    generatorGUID[i] = temp->GetGUID();
                                    if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                        temp->CastSpell(putress, SPELL_BLIGHT_EMPOWERMENT, false, 0, 0, generatorGUID[0]);
                                }
                                break;
                        }
                    }
                    break;
                case 7:
                    if (Unit* temp = me->SummonCreature(NPC_SW_SOLDIER, AllianceSpawn[8].x, AllianceSpawn[8].y, AllianceSpawn[8].z, 0, TEMPSUMMON_TIMED_DESPAWN, 90000))
                    {
                        allianceGuardsGUID.push_back(temp->GetGUID());
                        temp->GetMotionMaster()->MovePath(NPC_SW_SOLDIER * 10, false);
                    }
                    if (Unit* temp = me->SummonCreature(NPC_SW_SOLDIER, AllianceSpawn[8].x, AllianceSpawn[8].y, AllianceSpawn[8].z, 0, TEMPSUMMON_TIMED_DESPAWN, 90000))
                    {
                        allianceGuardsGUID.push_back(temp->GetGUID());
                        temp->GetMotionMaster()->MovePath((NPC_SW_SOLDIER * 10) + 1, false);
                    }
                    if (Unit* temp = me->SummonCreature(NPC_SW_SOLDIER, AllianceSpawn[8].x, AllianceSpawn[8].y, AllianceSpawn[8].z, 0, TEMPSUMMON_TIMED_DESPAWN, 90000))
                    {
                        allianceGuardsGUID.push_back(temp->GetGUID());
                        temp->GetMotionMaster()->MovePath((NPC_SW_SOLDIER * 10) + 2, false);
                    }
                    if (Unit* temp = me->SummonCreature(NPC_SW_SOLDIER, AllianceSpawn[8].x, AllianceSpawn[8].y, AllianceSpawn[8].z, 0, TEMPSUMMON_TIMED_DESPAWN, 90000))
                    {
                        allianceGuardsGUID.push_back(temp->GetGUID());
                        temp->GetMotionMaster()->MovePath((NPC_SW_SOLDIER * 10) + 3, false);
                    }
                    break;
                case 8:
                    break;
                case 9:
                    for (uint8 i = 0; i < WAVE_MAXCOUNT; ++i)
                        me->SummonCreature(NPC_GUARDIAN, AllianceSpawn[10].x + rand32() % 13, AllianceSpawn[10].y + rand32() % 13, AllianceSpawn[10].z, TEMPSUMMON_DEAD_DESPAWN);
                    break;
                case 10:
                    if (Unit* temp = me->SummonCreature(NPC_DREADLORD, AllianceSpawn[11].x, AllianceSpawn[11].y, AllianceSpawn[11].z, TEMPSUMMON_DEAD_DESPAWN))
                    {
                        temp->GetMotionMaster()->MovePath(NPC_DREADLORD * 10, false);
                        temp->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true);
                        temp->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, true);
                    }
                    break;
                case 11:
                    for (uint8 i = 0; i < WAVE_MAXCOUNT; ++i)
                    {
                        switch (urand(0, 3))
                        {
                            case 0:
                                if (Unit* temp = me->SummonCreature(NPC_EXPERIMENT, AllianceSpawn[13].x + rand32() % 5, AllianceSpawn[13].y + rand32() % 5, AllianceSpawn[13].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
                                break;
                            case 1:
                                if (Unit* temp = me->SummonCreature(NPC_EXPERIMENT, AllianceSpawn[14].x + rand32() % 5, AllianceSpawn[14].y + rand32() % 5, AllianceSpawn[14].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
                                break;
                            case 2:
                                if (Unit* temp = me->SummonCreature(NPC_EXPERIMENT, AllianceSpawn[15].x + rand32() % 5, AllianceSpawn[15].y + rand32() % 5, AllianceSpawn[15].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
                                break;
                            case 3:
                                if (Unit* temp = me->SummonCreature(NPC_EXPERIMENT, AllianceSpawn[16].x + rand32() % 5, AllianceSpawn[16].y + rand32() % 5, AllianceSpawn[16].z, TEMPSUMMON_DEAD_DESPAWN))
                                    temp->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
                                break;
                        }
                    }
                    break;
                case 12:
                    if (Creature* temp = me->SummonCreature(NPC_THRALL, AllianceSpawn[17].x, AllianceSpawn[17].y, AllianceSpawn[17].z, TEMPSUMMON_MANUAL_DESPAWN))
                    {
                        thrallGUID = temp->GetGUID();
                        temp->SetReactState(REACT_PASSIVE);
                        temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                        temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                        temp->CastSpell(temp, SPELL_THRALL_BUFF);
                        temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
                    }
                    if (Creature* temp = me->SummonCreature(NPC_SYLVANAS, AllianceSpawn[18].x, AllianceSpawn[18].y, AllianceSpawn[18].z, TEMPSUMMON_MANUAL_DESPAWN))
                    {
                        sylvanasGUID = temp->GetGUID();
                        temp->SetReactState(REACT_PASSIVE);
                        temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                        temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                        temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
                    }
                    for (uint8 i = 0; i < HORDE_FORCE_MAXCOUNT; ++i)
                    {
                        if (Creature* temp = me->SummonCreature(NPC_HORDE_SOLDIER, AllianceSpawn[i + 19].x, AllianceSpawn[i + 19].y, AllianceSpawn[i + 19].z, TEMPSUMMON_MANUAL_DESPAWN))
                        {
                            hordeForcesGUID[i] = temp->GetGUID();
                            temp->SetReactState(REACT_PASSIVE);
                            temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                            temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                            temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
                        }
                    }
                    break;
                case 13:
                    for (uint8 i = 0; i < ALLIANCE_FORCE_MAXCOUNT; ++i)
                    {
                        if (Unit* temp = me->SummonCreature(NPC_SW_SOLDIER, AllianceSpawn[i + 25].x, AllianceSpawn[i + 25].y, AllianceSpawn[i + 25].z, AllianceSpawn[i + 25].o, TEMPSUMMON_MANUAL_DESPAWN))
                        {
                            allianceForcesGUID[i] = temp->GetGUID();
                            temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                            temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                            temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
                        }
                    }
                    break;
            }
        }

        void WaypointReached(uint32 waypointId) override
        {
            switch (waypointId)
            {
                case 0:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 2:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 38:
                    if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                    {
                        jaina->GetMotionMaster()->Clear();
                        jaina->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                        jaina->SetReactState(REACT_AGGRESSIVE);
                    }
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 45:
                    SetHoldState(true);
                    bStepping = true;
                    if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                    {
                        jaina->GetMotionMaster()->Clear();
                        jaina->GetMotionMaster()->MovePoint(0, AllianceWP[6].x, AllianceWP[6].y, AllianceWP[6].z);
                    }
                    break;
                case 46:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 48:
                    if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                        putress->AI()->Talk(PUTRESS_SAY_2);
                    if (Player* player = GetPlayerForEscort())
                        player->PlayDirectSound(SOUND_PUTRESS, player);
                    break;
                case 50:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 63:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 65:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 66:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 87:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 88:
                    SetHoldState(true);
                    bStepping = true;
                    break;
            }
        }

        void UpdateAI(uint32 diff) override
        {
            npc_escortAI::UpdateAI(diff);

            if (bStepping)
            {
                if (phaseTimer <= diff)
                {
                    switch (step)
                    {
                        //Preparation
                        case 0:
                            me->setActive(true);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_MANHUNT_COUNTDOWN_A, 1);
                            Talk(WRYNN_SAY_PREP_1);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 1:
                            Talk(WRYNN_SAY_PREP_2);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 2:
                            Talk(WRYNN_SAY_PREP_3);
                            JumpToNextStep(20 * IN_MILLISECONDS);
                            break;
                        case 3:
                            Talk(WRYNN_SAY_PREP_4);
                            JumpToNextStep(20 * IN_MILLISECONDS);
                            break;
                        case 4:
                            UpdateWorldState(me->GetMap(), WORLD_STATE_MANHUNT_COUNTDOWN_A, 0);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_MANHUNT_STARTS_A, 1);
                            Talk(WRYNN_SAY_PREP_5);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 5:
                            DoCast(me, SPELL_WRYNN_BUFF);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 6:
                            Talk(WRYNN_SAY_PREP_6);
                            JumpToNextStep(1 * IN_MILLISECONDS);
                            break;
                        case 7:
                            SetEscortPaused(false);
                            JumpToNextStep(1.5 * IN_MILLISECONDS);
                            break;
                        case 8:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                            {
                                jaina->GetMotionMaster()->MovePath(NPC_JAINA * 10, false);
                                jaina->setActive(true);
                            }
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        //Sewers
                        case 9:
                            Talk(WRYNN_SAY_SEWERS_1);
                            SpawnWave(0);
                            SpawnWave(1);
                            SpawnWave(2);
                            SpawnWave(3);
                            JumpToNextStep(9500);
                            break;
                        case 10:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                                jaina->AI()->Talk(JAINA_SAY_SEWERS_1);
                            JumpToNextStep(2 * IN_MILLISECONDS);
                            break;
                        case 11:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                                jaina->AI()->DoCast(SPELL_JAINA_BUFF);
                            JumpToNextStep(1 * IN_MILLISECONDS);
                            break;
                        case 12:
                            SetEscortPaused(false);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_MANHUNT_STARTS_A, 0);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_SEWERS_FIGHT_A, 1);
                            JumpToNextStep(1 * IN_MILLISECONDS);
                            break;
                        case 13:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                            {
                                jaina->GetMotionMaster()->MoveFollow(me, 5, PET_FOLLOW_ANGLE);
                                jaina->SetReactState(REACT_AGGRESSIVE);
                                jaina->setFaction(FACTION_ESCORT_N_NEUTRAL_ACTIVE);
                            }
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 14:
                            Talk(WRYNN_SAY_SEWERS_2);
                            JumpToNextStep(3.5 * IN_MILLISECONDS);
                            break;
                        case 15:
                            SpawnWave(4);
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 16:
                            SpawnWave(5);
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 17:
                            SpawnWave(4);
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 18:
                            SpawnWave(4);
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 19:
                            SpawnWave(5);
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 20:
                            SpawnWave(6);
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 21:
                            Talk(WRYNN_SAY_SEWERS_3);
                            SpawnWave(7);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 22:
                            Talk(WRYNN_SAY_SEWERS_4);
                            SetRun(false);
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                            {
                                jaina->GetMotionMaster()->Clear();
                                jaina->GetMotionMaster()->MoveFollow(me, 1, 0);
                            }
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 23:
                            bStepping = false;
                            SetEscortPaused(false);
                            JumpToNextStep(0);
                            break;
                        //Apothecarium
                        case 24:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                                jaina->AI()->Talk(JAINA_SAY_APO_1);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 25:
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 26:
                            me->SetStandState(UNIT_STAND_STATE_KNEEL);
                            me->SetTarget(khanokGUID);
                            JumpToNextStep(1 * IN_MILLISECONDS);
                            break;
                        case 27:
                            Talk(WRYNN_SAY_APO_1);
                            JumpToNextStep(12 * IN_MILLISECONDS);
                            break;
                        case 28:
                            Talk(WRYNN_SAY_APO_2);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 29:
                            Talk(WRYNN_SAY_APO_3);
                            JumpToNextStep(1.5 * IN_MILLISECONDS);
                            break;
                        case 30:
                            UpdateWorldState(me->GetMap(), WORLD_STATE_APOTHECARIUM_FIGHT_A, 1);
                            if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                putress->AI()->Talk(PUTRESS_SAY_1);
                            if (Player* player = GetPlayerForEscort())
                                player->PlayDirectSound(SOUND_PUTRESS, player);
                            SpawnWave(9);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 31:
                            SetRun(true);
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                                jaina->GetMotionMaster()->MoveFollow(me, 1, 0);
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 32:
                            Talk(WRYNN_SAY_APO_4);
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 33:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 34:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 35:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 36:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 37:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 38:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 39:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 40:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 41:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 42:
                            SpawnWave(10);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 43:
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 44:
                            Talk(WRYNN_SAY_APO_5);
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                            {
                                jaina->GetMotionMaster()->Clear();
                                jaina->GetMotionMaster()->MovePoint(0, AllianceWP[7].x, AllianceWP[7].y, AllianceWP[7].z, false);
                            }
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 45:
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 46:
                            Talk(WRYNN_SAY_APO_6);
                            JumpToNextStep(4 * IN_MILLISECONDS);
                            break;
                        case 47:
                            if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                putress->AI()->Talk(PUTRESS_SAY_3);
                            SpawnWave(11);
                            JumpToNextStep(7.5 * IN_MILLISECONDS);
                            break;
                        case 48:
                            if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                putress->AI()->Talk(PUTRESS_SAY_4);
                            JumpToNextStep(7.5 * IN_MILLISECONDS);
                            break;
                        case 49:
                            if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                putress->AI()->Talk(PUTRESS_SAY_5);
                            SpawnWave(11);
                            JumpToNextStep(7.5 * IN_MILLISECONDS);
                            break;
                        case 50:
                            if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                putress->AI()->Talk(PUTRESS_SAY_6);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 51:
                            if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                                putress->AI()->Talk(PUTRESS_SAY_7);
                            SpawnWave(11);
                            JumpToNextStep(7.5 * IN_MILLISECONDS);
                            break;
                        case 52:
                            if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                            {
                                putress->AI()->Talk(PUTRESS_SAY_8);
                                putress->AI()->DoCast(SPELL_BLIGHT_OVERLOAD);
                            }
                            JumpToNextStep(0.5 * IN_MILLISECONDS);
                            break;
                        case 53:
                            if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID))
                            {
                                putress->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                                putress->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                                putress->AddThreat(me, 100.0f);
                                me->AddThreat(putress, 100.0f);
                                putress->RemoveAura(SPELL_PUTRESS_CASTING_STATE);
                            }
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 54:
                            Talk(WRYNN_SAY_APO_7);
                            SetRun(false);
                            JumpToNextStep(4 * IN_MILLISECONDS);
                            break;
                        case 55:
                            if (ObjectAccessor::GetCreature(*me, putressGUID))
                                me->SetTarget(putressGUID);
                            Talk(WRYNN_SAY_APO_8);
                            JumpToNextStep(4 * IN_MILLISECONDS);
                            break;
                        case 56:
                            Talk(WRYNN_SAY_APO_9);
                            me->RemoveStandFlags(UNIT_STAND_STATE_KNEEL);
                            me->SetStandFlags(UNIT_STAND_STATE_STAND);
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 57:
                            JumpToNextStep(4 * IN_MILLISECONDS);
                            break;
                        case 58:
                            Talk(WRYNN_SAY_APO_10);
                            JumpToNextStep(7.5 * IN_MILLISECONDS);
                            break;
                        case 59:
                            Talk(WRYNN_SAY_APO_11);
                            JumpToNextStep(7.5 * IN_MILLISECONDS);
                            break;
                        case 60:
                            Talk(WRYNN_SAY_APO_12);
                            SpawnWave(12); // thrall sylvanas horde soldiers
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 61:
                            if (Creature* thrall = ObjectAccessor::GetCreature(*me, thrallGUID))
                                thrall->AI()->Talk(THRALL_SAY_THRONE_A_1);
                            if (Player* player = GetPlayerForEscort())
                                player->PlayDirectSound(SOUND_THRALL_1, player);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 62:
                            if (Creature* thrall = ObjectAccessor::GetCreature(*me, thrallGUID))
                                thrall->AI()->Talk(THRALL_SAY_THRONE_A_2);
                            if (Player* player = GetPlayerForEscort())
                                player->PlayDirectSound(SOUND_THRALL_2, player);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 63:
                            Talk(WRYNN_SAY_THRONE_1);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 64:
                            Talk(WRYNN_SAY_THRONE_2);
                            JumpToNextStep(1.5 * IN_MILLISECONDS);
                            break;
                        case 65:
                            SetRun(true);
                            SetEscortPaused(false);
                            JumpToNextStep(0.25 * IN_MILLISECONDS);
                            break;
                        case 66:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                                jaina->AI()->Talk(JAINA_SAY_THRONE_1);
                            me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 67:
                            Talk(WRYNN_SAY_THRONE_3);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 68:
                            Talk(WRYNN_SAY_THRONE_4);
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                            {
                                jaina->GetMotionMaster()->MovePoint(0, AllianceWP[8].x, AllianceWP[8].y, AllianceWP[8].z);
                                jaina->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                                jaina->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                            }
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 69:
                            Talk(WRYNN_SAY_THRONE_5);
                            SpawnWave(13); //alliance soldiers
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 70:
                            Talk(WRYNN_SAY_THRONE_6);
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 71:
                            Talk(WRYNN_SAY_THRONE_7);
                            JumpToNextStep(16.5 * IN_MILLISECONDS);
                            break;
                        case 72:
                            Talk(WRYNN_SAY_THRONE_8);
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 73:
                            Talk(WRYNN_SAY_THRONE_9);
                            me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                            if (Creature* thrall = ObjectAccessor::GetCreature(*me, thrallGUID))
                            {
                                thrall->SetReactState(REACT_AGGRESSIVE);
                                thrall->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                                thrall->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                                thrall->AddThreat(me, 100.0f);
                                me->AddThreat(thrall, 100.0f);
                                thrall->AI()->AttackStart(me);
                            }
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID))
                            {
                                sylvanas->SetReactState(REACT_AGGRESSIVE);
                                sylvanas->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                                sylvanas->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                                sylvanas->AddThreat(me, 100.0f);
                                sylvanas->AI()->AttackStart(me);
                                me->AddThreat(sylvanas, 100.0f);
                            }
                            for (uint8 i = 0; i < HORDE_FORCE_MAXCOUNT; ++i)
                            {
                                if (Creature* temp = ObjectAccessor::GetCreature(*me, hordeForcesGUID[i]))
                                {
                                    temp->SetReactState(REACT_AGGRESSIVE);
                                    temp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                                    temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                                }
                            }
                            for (uint8 i = 0; i < ALLIANCE_FORCE_MAXCOUNT; ++i)
                            {
                                if (Creature* temp = ObjectAccessor::GetCreature(*me, allianceForcesGUID[i]))
                                {
                                    if (Creature* temp2 = ObjectAccessor::GetCreature(*me, hordeForcesGUID[i]))
                                    {
                                        temp->SetReactState(REACT_AGGRESSIVE);
                                        temp2->SetReactState(REACT_AGGRESSIVE);
                                        temp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                                        temp2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                                        temp->AddThreat(temp2, 100.0f);
                                        temp->AI()->AttackStart(temp2);
                                        temp2->AddThreat(temp, 100.0f);
                                    }
                                }
                            }
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 74:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                            {
                                jaina->GetMotionMaster()->MovePoint(0, AllianceWP[9].x, AllianceWP[9].y, AllianceWP[9].z);
                                jaina->AI()->Talk(JAINA_SAY_THRONE_2);
                            }
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 75:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                            {
                                jaina->AI()->DoCastAOE(SPELL_DEEPFREEZE);
                                jaina->AI()->Talk(JAINA_SAY_THRONE_3);
                            }
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 76:
                            std::list<Creature*> HelperList;
                            me->GetCreatureListWithEntryInGrid(HelperList, NPC_SW_SOLDIER, 100.0f);
                            me->GetCreatureListWithEntryInGrid(HelperList, NPC_HORDE_SOLDIER, 100.0f);
                            me->GetCreatureListWithEntryInGrid(HelperList, NPC_JAINA, 100.0f);
                            me->GetCreatureListWithEntryInGrid(HelperList, NPC_SYLVANAS, 100.0f);
                            me->GetCreatureListWithEntryInGrid(HelperList, NPC_THRALL, 100.0f);
                            if (!HelperList.empty())
                                for (std::list<Creature*>::iterator itr = HelperList.begin(); itr != HelperList.end(); itr++)
                                    (*itr)->DespawnOrUnsummon();
                            if (Map* map = me->GetMap())
                            {
                                Map::PlayerList const& PlayerList = map->GetPlayers();
                                for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
                                {
                                    if (Player* player = i->GetSource())
                                    {
                                        if (me->IsInRange(player, 0.0f, 50.0f))
                                        {
                                            if (player->GetTeamId() == TEAM_ALLIANCE)
                                            {
                                                player->RemoveAura(SPELL_WRYNN_BUFF);
                                                player->RemoveAura(SPELL_JAINA_BUFF);
                                                player->CompleteQuest(QUEST_BATTLE_A);
                                                player->NearTeleportTo(-8445.213867f, 337.384277f, 121.746056f, 5.401534f, false);
                                            }
                                        }
                                    }
                                }
                            }
                            UpdateWorldState(me->GetMap(), WORLD_STATE_MANHUNT_STARTS_A, 0);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_SEWERS_DONE_A, 0);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_APOTHECARIUM_DONE_A, 0);
                            me->DespawnOrUnsummon();
                            break;
                    }
                }
                else phaseTimer -= diff;
            }

            if (!UpdateVictim())
                return;

            _events.Update(diff);

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

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_WHIRLWIND:
                        DoCast(me, SPELL_WHIRLWIND);
                        _events.ScheduleEvent(EVENT_WHIRLWIND, 20 * IN_MILLISECONDS);
                        break;
                    case EVENT_HEROIC_LEAP:
                        DoCastVictim(SPELL_HEROIC_LEAP);
                        _events.ScheduleEvent(EVENT_HEROIC_LEAP, urand(15, 30) * IN_MILLISECONDS);
                        break;
                    case EVENT_AGGRO_JAINA:
                        if (me->GetVictim())
                        {
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID))
                            {
                                jaina->AI()->AttackStart(me->GetVictim());
                            }
                        }
                        DoCast(me, SPELL_THUNDER);
                        _events.ScheduleEvent(EVENT_AGGRO_JAINA, 2 * IN_MILLISECONDS);
                        break;
                    case EVENT_WRYNN_BUFF:
                        DoCast(me, SPELL_WRYNN_BUFF);
                        _events.ScheduleEvent(EVENT_WRYNN_BUFF, 10 * IN_MILLISECONDS);
                        break;
                    default:
                        break;
                }
            }

            DoMeleeAttackIfReady();
        }
    };

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

/*######
## npc_jaina_proudmoore
######*/

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

    struct npc_jaina_proudmoore_bfuAI : public ScriptedAI
    {
        npc_jaina_proudmoore_bfuAI(Creature* creature) : ScriptedAI(creature)
        {
            Reset();
        }

        void Reset() override
        {
            me->SetCorpseDelay(1);
            me->SetRespawnTime(1);
            _events.ScheduleEvent(EVENT_FIREBALL, 1 * IN_MILLISECONDS);
            _events.ScheduleEvent(EVENT_BLIZZARD, 8 * IN_MILLISECONDS);
            _events.ScheduleEvent(EVENT_ELEMENTAL, 30 * IN_MILLISECONDS);
            me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_THRALL_BUFF, true);
            me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);
        }

        void UpdateAI(uint32 diff) override
        {
            if (!me->GetVictim())
                return;

            _events.Update(diff);

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

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_FIREBALL:
                        if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                            DoCast(target, SPELL_FIREBALL);
                        _events.ScheduleEvent(EVENT_FIREBALL, 3 * IN_MILLISECONDS);
                        break;
                    case EVENT_BLIZZARD:
                        DoCast(SPELL_BLIZZARD);
                        _events.ScheduleEvent(EVENT_BLIZZARD, 15 * IN_MILLISECONDS);
                        break;
                    case EVENT_ELEMENTAL:
                        DoCast(SPELL_ELEMENTALS);
                        _events.ScheduleEvent(EVENT_ELEMENTAL, 90 * IN_MILLISECONDS);
                        break;
                    default:
                        break;
                }
            }

            DoMeleeAttackIfReady();
        }

    private:
        EventMap _events;
    };

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

/*######
## ENEMY
#######*/

/*######
## boss_blight_worm
######*/

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

    struct boss_blight_wormAI : public ScriptedAI
    {
        boss_blight_wormAI(Creature* creature) : ScriptedAI(creature)
        {
            SetCombatMovement(false);
        }

        void Reset() override
        {
            _events.ScheduleEvent(EVENT_INFEST, 2 * IN_MILLISECONDS);
            _events.ScheduleEvent(EVENT_BLIGHT_BREATH, 7.5 * IN_MILLISECONDS);
        }

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

            _events.Update(diff);

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

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_INFEST:
                        if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0, true))
                            DoCast(target, SPELL_INGEST);
                        _events.ScheduleEvent(EVENT_INFEST, 20 * IN_MILLISECONDS);
                        break;
                    case EVENT_BLIGHT_BREATH:
                        DoCast(SPELL_BLIGHT_BREATH);
                        _events.ScheduleEvent(EVENT_BLIGHT_BREATH, 15 * IN_MILLISECONDS);
                        break;
                    default:
                        break;
                }
            }

            DoMeleeAttackIfReady();
        }

    private:
        EventMap _events;
    };

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

/*######
## Spells
######*/

// - 61123 - Ingest
class spell_blight_worm_ingest : public SpellScriptLoader
{
public:
    spell_blight_worm_ingest() : SpellScriptLoader("spell_blight_worm_ingest") { }

    class spell_blight_worm_ingest_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_blight_worm_ingest_SpellScript);

        bool Validate(SpellInfo const* /*spellInfo*/) override
        {
            return ValidateSpellInfo({ SPELL_INGEST });
        }

        void HandleScript(SpellEffIndex /*effIndex*/)
        {
            if (Unit* target = GetHitUnit())
                if (Unit* caster = GetCaster())
                    target->CastSpell(caster, SPELL_INGEST_TRIGGER, true);
        }

        void Register() override
        {
            OnEffectHitTarget += SpellEffectFn(spell_blight_worm_ingest_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_blight_worm_ingest_SpellScript();
    }
};

/*######
## HORDE
#######*/

/*######
## npc_thrall_bfu
######*/

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

    bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
    {
        ClearGossipMenuFor(player);

        switch (action)
        {
            case GOSSIP_ACTION_INFO_DEF + 1:
                {
                    CloseGossipMenuFor(player);

                    if (auto thrall_ai = CAST_AI(npc_thrall_bfu::npc_thrall_bfuAI, creature->AI()))
                    {
                        if (Creature* sylvannas = GetClosestCreatureWithEntry(creature, NPC_SYLVANAS, 50.0f))
                        {
                            thrall_ai->sylvanasfollowGUID = sylvannas->GetGUID();
                            thrall_ai->Start(true, true, player->GetGUID());
                            thrall_ai->SetDespawnAtEnd(false);
                            thrall_ai->SetDespawnAtFar(false);
                        }
                        else
                            thrall_ai->sylvanasfollowGUID.Clear();
                    }
                    break;
                }
        }

        return true;
    }

    bool OnGossipHello(Player* player, Creature* creature) override
    {
        if (creature->IsQuestGiver())
            player->PrepareQuestMenu(creature->GetGUID());

        if (player->GetQuestStatus(QUEST_BATTLE_H) == QUEST_STATUS_INCOMPLETE)
            AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_THRALL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);

        SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());

        return true;
    }

    bool OnQuestReward(Player* player, Creature* /*creature*/, Quest const* quest, uint32 /*item*/) override
    {
        if (quest->GetQuestId() == QUEST_BATTLE_H)
        {
            player->RemoveAura(SPELL_THRALL_BUFF);
            player->RemoveAura(SPELL_SYLVANAS_BUFF);
            player->CastSpell(player, SPELL_TELEPORT_OG, true);
        }

        return true;
    }

    struct npc_thrall_bfuAI : public npc_escortAI
    {
        npc_thrall_bfuAI(Creature* creature) : npc_escortAI(creature)
        {
            hordeGuardsGUID.clear();
        }

        bool bStepping;
        bool EnableAttack;

        uint32 step;
        uint32 phaseTimer;

        ObjectGuid sylvanasfollowGUID;
        ObjectGuid allianceForcesGUID[ALLIANCE_FORCE_MAXCOUNT];
        ObjectGuid ValimathrasGUID;
        ObjectGuid ValimathrasPortalGUID;
        ObjectGuid WrynnGUID;
        ObjectGuid JainaGUID;
        ObjectGuid SaurfangGUID;
        GuidVector hordeGuardsGUID;

        EventMap _events;

        void EnterEvadeMode() override
        {
            me->RemoveAura(SPELL_HEROIC_VANGUARD);
            me->DeleteThreatList();
            me->CombatStop(true);
            me->SetLootRecipient(nullptr);

            if (HasEscortState(STATE_ESCORT_ESCORTING))
            {
                AddEscortState(STATE_ESCORT_RETURNING);
                ReturnToLastPoint();
            }
            else
            {
                me->GetMotionMaster()->MoveTargetedHome();
                Reset();
            }
        }

        void Reset() override
        {
            if (!HasEscortState(STATE_ESCORT_ESCORTING))
            {
                me->SetStandState(UNIT_STAND_STATE_STAND);
                me->Mount(MODEL_WHITE_WULF);
                me->SetCorpseDelay(1);
                me->SetRespawnTime(1);
                me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
                me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);

                bStepping = false;
                EnableAttack = false;
                step = 0;
                phaseTimer = 0;
                sylvanasfollowGUID.Clear();
                _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 3 * IN_MILLISECONDS);
                _events.ScheduleEvent(EVENT_LAVA_BURST, 5 * IN_MILLISECONDS);
                _events.ScheduleEvent(EVENT_THUNDER, 8 * IN_MILLISECONDS);
                _events.ScheduleEvent(EVENT_AGGRO_SYLVANAS, 2 * IN_MILLISECONDS);
                _events.ScheduleEvent(EVENT_THRALL_BUFF, 2 * IN_MILLISECONDS);

                if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                {
                    valimathras->DespawnOrUnsummon();
                    ValimathrasGUID.Clear();
                }

                if (Creature* valimathrasportal = ObjectAccessor::GetCreature(*me, ValimathrasPortalGUID))
                {
                    valimathrasportal->DespawnOrUnsummon();
                    ValimathrasPortalGUID.Clear();
                }

                if (Creature* wrynn = ObjectAccessor::GetCreature(*me, WrynnGUID))
                {
                    wrynn->DespawnOrUnsummon();
                    WrynnGUID.Clear();
                }

                if (Creature* jaina = ObjectAccessor::GetCreature(*me, JainaGUID))
                {
                    jaina->DespawnOrUnsummon();
                    JainaGUID.Clear();
                }

                if (Creature* saurfang = ObjectAccessor::GetCreature(*me, SaurfangGUID))
                {
                    saurfang->DespawnOrUnsummon();
                    SaurfangGUID.Clear();
                }

                for (ObjectGuid const& guid : hordeGuardsGUID)
                    if (Creature* temp = ObjectAccessor::GetCreature(*me, guid))
                        temp->DespawnOrUnsummon();

                hordeGuardsGUID.clear();
            }
        }

        void JustSummoned(Creature* summoned) override
        {
            switch (summoned->GetEntry())
            {
                case NPC_BLIGHT_ABBERATION:
                    summoned->AI()->AttackStart(me);
                    break;
                case NPC_WARSONG_BATTLEGUARD:
                    summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);
                    summoned->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
                    break;
                case NPC_VARIMATHRAS_PORTAL:
                    summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                    summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_THRALL_BUFF, true);
                    summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);
                    break;
                case NPC_CAVE_DUMMY:
                    summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                    summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_THRALL_BUFF, true);
                    summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);
                    summoned->AddAura(SPELL_CYCLONE_FALL, summoned);
                    break;
                case NPC_TREACHEROUS_GUARDIAN_H:
                case NPC_DOCTOR_H:
                case NPC_CHEMIST_H:
                    summoned->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true);
                    summoned->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, true);
                    summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_THRALL_BUFF, true);
                    summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);
                    if (!EnableAttack)
                        summoned->setFaction(FACTION_FRIENDLY_TO_ALL);
                    summoned->AddThreat(me, 100.0f);
                    me->AddThreat(summoned, 100.0f);
                    summoned->AI()->AttackStart(me);
                    break;
                case NPC_LEGION_INVADER:
                case NPC_LEGION_DREADWHISPER:
                case NPC_FELGUARD_MORADEUR:
                case NPC_DREADLORD:
                case NPC_BETRAYER_H:
                case NPC_FELBEAST_H:
                    summoned->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true);
                    summoned->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, true);
                    summoned->AddThreat(me, 100.0f);
                    me->AddThreat(summoned, 100.0f);
                    summoned->AI()->AttackStart(me);
                    break;
                default:
                    break;
            }
        }

        void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
        {
            switch (summon->GetEntry())
            {
                case NPC_BLIGHT_ABBERATION:
                    UpdateWorldState(me->GetMap(), WORLD_STATE_COURTYARD_FIGHT_H, 0);
                    UpdateWorldState(me->GetMap(), WORLD_STATE_COURTYARD_DONE_H, 1);
                    bStepping = true;
                    break;
                case NPC_KHANOK:
                    {
                        UpdateWorldState(me->GetMap(), WORLD_STATE_INNER_SANKTUM_FIGHT_H, 0);
                        UpdateWorldState(me->GetMap(), WORLD_STATE_INNER_SANKTUM_DONE_H, 1);
                        FollowThrall();
                        SetEscortPaused(false);
                        std::list<Creature*> SanktumList;
                        me->GetCreatureListWithEntryInGrid(SanktumList, NPC_FELGUARD_MORADEUR, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(SanktumList, NPC_DREADLORD, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(SanktumList, NPC_TREACHEROUS_GUARDIAN_H, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(SanktumList, NPC_DOCTOR_H, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(SanktumList, NPC_CHEMIST_H, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(SanktumList, NPC_BETRAYER_H, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(SanktumList, NPC_FELBEAST_H, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(SanktumList, NPC_DOOMGUARD_PILLARGER, 1000.0f);
                        if (!SanktumList.empty())
                            for (std::list<Creature*>::iterator itr = SanktumList.begin(); itr != SanktumList.end(); itr++)
                                (*itr)->DespawnOrUnsummon();
                        break;
                    }
                case NPC_VARIMATHRAS:
                    {
                        UpdateWorldState(me->GetMap(), WORLD_STATE_ROYAL_QUARTER_FIGHT_H, 0);
                        UpdateWorldState(me->GetMap(), WORLD_STATE_ROYAL_QUARTER_DONE_H, 1);
                        std::list<Creature*> ThroneList;
                        me->GetCreatureListWithEntryInGrid(ThroneList, NPC_LEGION_OVERLORD, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(ThroneList, NPC_LEGION_INVADER, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(ThroneList, NPC_LEGION_DREADWHISPER, 1000.0f);
                        me->GetCreatureListWithEntryInGrid(ThroneList, NPC_VARIMATHRAS_PORTAL, 1000.0f);
                        if (!ThroneList.empty())
                            for (std::list<Creature*>::iterator itr = ThroneList.begin(); itr != ThroneList.end(); itr++)
                                (*itr)->DespawnOrUnsummon();
                        SetEscortPaused(false);
                        SetRun(false);
                        break;
                    }
                default:
                    break;
            }
        }

        void UpdateWorldState(Map* map, uint32 id, uint32 state)
        {
            Map::PlayerList const& players = map->GetPlayers();

            if (!players.isEmpty())
            {
                for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
                {
                    if (Player* player = itr->GetSource())
                        player->SendUpdateWorldState(id, state);
                }
            }
        }

        void SetHoldState(bool bOnHold)
        {
            SetEscortPaused(bOnHold);
        }

        void JumpToNextStep(uint32 uiTimer)
        {
            phaseTimer = uiTimer;
            ++step;
        }

        void FollowThrall()
        {
            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
            {
                sylvanas->GetMotionMaster()->Clear();
                sylvanas->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                sylvanas->SetReactState(REACT_AGGRESSIVE);
                sylvanas->setFaction(FACTION_ESCORT_N_NEUTRAL_ACTIVE);
                sylvanas->GetMotionMaster()->MoveFollow(me, 1, M_PI * 0.1f);
            }
        }

        void ActivateValimathrasPortal()
        {
            if (Creature* portal = me->FindNearestCreature(NPC_VARIMATHRAS_PORTAL, 500.0f))
            {
                portal->SummonCreature(NPC_LEGION_OVERLORD, ThrallSpawn[81].x + rand32() % 5, ThrallSpawn[81].y + rand32() % 5, ThrallSpawn[81].z, TEMPSUMMON_DEAD_DESPAWN);
                portal->DespawnOrUnsummon();
            }
        }

        void SpawnWave(uint32 waveId)
        {
            switch (waveId)
            {
                case 0: // Vortex
                    if (Creature* whirlwind1 = me->SummonCreature(NPC_VORTEX, ThrallSpawn[0].x, ThrallSpawn[0].y, ThrallSpawn[0].z, ThrallSpawn[0].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30 * IN_MILLISECONDS))
                        whirlwind1->GetMotionMaster()->MovePath(NPC_WHIRLWIND * 10, false);
                    if (Creature* whirlwind2 = me->SummonCreature(NPC_VORTEX, ThrallSpawn[0].x, ThrallSpawn[0].y, ThrallSpawn[0].z, ThrallSpawn[0].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30 * IN_MILLISECONDS))
                        whirlwind2->GetMotionMaster()->MovePath(NPC_WHIRLWIND * 100, false);
                    break;
                case 1:
                    // BATTLING_COURTYARD Initial Spawn
                    for (uint8 i = 0; i < 3; ++i)
                        me->SummonCreature(NPC_DOCTOR_H, ThrallSpawn[i + 1].x, ThrallSpawn[i + 1].y, ThrallSpawn[i + 1].z, ThrallSpawn[i + 1].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS);
                    for (uint8 i = 0; i < 9; ++i)
                        me->SummonCreature(NPC_CHEMIST_H, ThrallSpawn[i + 4].x, ThrallSpawn[i + 4].y, ThrallSpawn[i + 4].z, ThrallSpawn[i + 4].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS);
                    for (uint8 i = 0; i < 6; ++i)
                        me->SummonCreature(NPC_TREACHEROUS_GUARDIAN_H, ThrallSpawn[i + 13].x, ThrallSpawn[i + 13].y, ThrallSpawn[i + 14].z, ThrallSpawn[i + 14].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS);
                    for (uint8 i = 0; i < 4; ++i)
                        me->SummonCreature(NPC_BLIGHT_SLINGER, ThrallSpawn[i + 19].x, ThrallSpawn[i + 19].y, ThrallSpawn[i + 19].z, ThrallSpawn[i + 19].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900 * IN_MILLISECONDS);
                    break;
                case 2:
                    // Valimathras
                    if (Unit* temp = me->SummonCreature(NPC_VARIMATHRAS, ThrallSpawn[23].x, ThrallSpawn[23].y, ThrallSpawn[23].z, ThrallSpawn[23].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900 * IN_MILLISECONDS))
                    {
                        ValimathrasGUID = temp->GetGUID();
                        temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                    }
                    break;
                case 3:
                    if (Unit* temp = me->SummonCreature(NPC_VARIMATHRAS_PORTAL, ThrallSpawn[24].x, ThrallSpawn[24].y, ThrallSpawn[24].z, ThrallSpawn[24].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900 * IN_MILLISECONDS))
                        ValimathrasPortalGUID = temp->GetGUID();
                    break;
                case 4:
                    // COURTYARD_FIGHT Spawns
                    for (uint8 i = 0; i < WAVE_COURTYARD_FIGHT; ++i)
                    {
                        switch (urand(0, 2))
                        {
                            case 0:
                                me->SummonCreature(NPC_TREACHEROUS_GUARDIAN_H, ThrallSpawn[25].x + rand32() % 5, ThrallSpawn[25].y + rand32() % 5, ThrallSpawn[25].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                                me->SummonCreature(NPC_DOCTOR_H, ThrallSpawn[26].x + rand32() % 5, ThrallSpawn[26].y + rand32() % 5, ThrallSpawn[26].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 2:
                                me->SummonCreature(NPC_CHEMIST_H, ThrallSpawn[27].x + rand32() % 5, ThrallSpawn[27].y + rand32() % 5, ThrallSpawn[27].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                case 5:
                    // Bossspawn 1
                    if (Creature* temp = me->SummonCreature(NPC_BLIGHT_ABBERATION, ThrallSpawn[28].x, ThrallSpawn[28].y, ThrallSpawn[28].z, ThrallSpawn[28].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900 * IN_MILLISECONDS))
                    {
                        temp->GetMotionMaster()->MoveJump(ThrallSpawn[62].x, ThrallSpawn[62].y, ThrallSpawn[62].z, 10.0f, 20.0f, 0);
                        temp->AddThreat(me, 100.0f);
                        me->AddThreat(temp, 100.0f);
                        temp->AI()->AttackStart(me);
                    }
                    break;
                case 6:
                    // COURTYARD_DONE Spawn
                    if (Unit* temp = me->SummonCreature(NPC_WARSONG_BATTLEGUARD, ThrallSpawn[29].x, ThrallSpawn[29].y, ThrallSpawn[29].z, ThrallSpawn[29].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS))
                    {
                        hordeGuardsGUID.push_back(temp->GetGUID());
                        temp->GetMotionMaster()->MovePoint(0, ThrallSpawn[30].x, ThrallSpawn[30].y, ThrallSpawn[30].z);
                    }
                    if (Unit* temp = me->SummonCreature(NPC_WARSONG_BATTLEGUARD, ThrallSpawn[31].x, ThrallSpawn[31].y, ThrallSpawn[31].z, ThrallSpawn[31].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS))
                    {
                        hordeGuardsGUID.push_back(temp->GetGUID());
                        temp->GetMotionMaster()->MovePoint(0, ThrallSpawn[32].x, ThrallSpawn[32].y, ThrallSpawn[32].z);
                    }
                    if (Unit* temp = me->SummonCreature(NPC_WARSONG_BATTLEGUARD, ThrallSpawn[33].x, ThrallSpawn[33].y, ThrallSpawn[33].z, ThrallSpawn[33].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS))
                    {
                        hordeGuardsGUID.push_back(temp->GetGUID());
                        temp->GetMotionMaster()->MovePoint(0, ThrallSpawn[34].x, ThrallSpawn[34].y, ThrallSpawn[34].z);
                    }
                    if (Unit* temp = me->SummonCreature(NPC_WARSONG_BATTLEGUARD, ThrallSpawn[35].x, ThrallSpawn[35].y, ThrallSpawn[35].z, ThrallSpawn[35].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS))
                    {
                        hordeGuardsGUID.push_back(temp->GetGUID());
                        temp->GetMotionMaster()->MovePoint(0, ThrallSpawn[36].x, ThrallSpawn[36].y, ThrallSpawn[36].z);
                    }
                    for (uint8 i = 0; i < 2; ++i)
                        if (Unit* temp = me->SummonCreature(NPC_WARSONG_BATTLEGUARD, ThrallSpawn[i + 44].x, ThrallSpawn[i + 44].y, ThrallSpawn[i + 44].z, ThrallSpawn[i + 44].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS))
                            hordeGuardsGUID.push_back(temp->GetGUID());
                    break;
                case 7:
                    for (uint8 i = 0; i < 9; ++i)
                        me->SummonCreature(NPC_CAVE_DUMMY, ThrallSpawn[i + 46].x, ThrallSpawn[i + 46].y, ThrallSpawn[i + 46].z, ThrallSpawn[i + 46].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 600 * IN_MILLISECONDS);
                    break;
                case 8:
                    for (uint8 i = 0; i < 2; ++i)
                        if (Unit* temp = me->SummonCreature(NPC_WARSONG_BATTLEGUARD, ThrallSpawn[i + 57].x, ThrallSpawn[i + 57].y, ThrallSpawn[i + 57].z, ThrallSpawn[i + 57].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS))
                            hordeGuardsGUID.push_back(temp->GetGUID());
                    break;
                case 9:
                    // Top of Undercity - Attacktrashpack
                    for (uint8 i = 0; i < 3; ++i)
                    {
                        switch (urand(0, 2))
                        {
                            case 0:
                                me->SummonCreature(NPC_TREACHEROUS_GUARDIAN_H, ThrallSpawn[59].x + rand32() % 2, ThrallSpawn[59].y + rand32() % 2, ThrallSpawn[59].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                                me->SummonCreature(NPC_DOCTOR_H, ThrallSpawn[59].x + rand32() % 2, ThrallSpawn[59].y + rand32() % 2, ThrallSpawn[59].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 2:
                                me->SummonCreature(NPC_CHEMIST_H, ThrallSpawn[59].x + rand32() % 2, ThrallSpawn[59].y + rand32() % 2, ThrallSpawn[59].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                case 10:
                    // Top of Undercity - Attacktrashpack
                    for (uint8 i = 0; i < 3; ++i)
                    {
                        switch (urand(0, 1))
                        {
                            case 0:
                                me->SummonCreature(NPC_TREACHEROUS_GUARDIAN_H, ThrallSpawn[60].x + rand32() % 5, ThrallSpawn[60].y + rand32() % 5, ThrallSpawn[60].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                                me->SummonCreature(NPC_FELGUARD_MORADEUR, ThrallSpawn[60].x + rand32() % 5, ThrallSpawn[60].y + rand32() % 5, ThrallSpawn[60].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                case 11:
                    // Bottom of Undercity - Attacktrashpack
                    for (uint8 i = 0; i < 3; ++i)
                    {
                        switch (urand(0, 1))
                        {
                            case 0:
                                me->SummonCreature(NPC_FELGUARD_MORADEUR, ThrallSpawn[61].x + rand32() % 5, ThrallSpawn[61].y + rand32() % 5, ThrallSpawn[61].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                                me->SummonCreature(NPC_DREADLORD, ThrallSpawn[61].x + rand32() % 5, ThrallSpawn[61].y + rand32() % 5, ThrallSpawn[61].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                case 12:
                    // Valimathras 2
                    if (Unit* temp = me->SummonCreature(NPC_VARIMATHRAS, ThrallSpawn[63].x, ThrallSpawn[63].y, ThrallSpawn[63].z, ThrallSpawn[63].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300 * IN_MILLISECONDS))
                    {
                        ValimathrasGUID = temp->GetGUID();
                        temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                    }
                    break;
                case 13:
                    if (Unit* temp = me->SummonCreature(NPC_VARIMATHRAS_PORTAL, ThrallSpawn[64].x, ThrallSpawn[64].y, ThrallSpawn[64].z, ThrallSpawn[64].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900 * IN_MILLISECONDS))
                        ValimathrasPortalGUID = temp->GetGUID();
                    break;
                // NPC_KHANOK - Inner Sunktum Spawn Left
                case 14:
                    for (uint8 i = 0; i < 4; ++i)
                    {
                        switch (urand(0, 6))
                        {
                            case 0:
                                me->SummonCreature(NPC_FELGUARD_MORADEUR, ThrallSpawn[65].x + rand32() % 5, ThrallSpawn[65].y + rand32() % 5, ThrallSpawn[65].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                                me->SummonCreature(NPC_DREADLORD, ThrallSpawn[65].x + rand32() % 5, ThrallSpawn[65].y + rand32() % 5, ThrallSpawn[65].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 2:
                                me->SummonCreature(NPC_TREACHEROUS_GUARDIAN_H, ThrallSpawn[65].x + rand32() % 5, ThrallSpawn[65].y + rand32() % 5, ThrallSpawn[65].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 3:
                                me->SummonCreature(NPC_DOCTOR_H, ThrallSpawn[65].x + rand32() % 5, ThrallSpawn[65].y + rand32() % 5, ThrallSpawn[65].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 4:
                                me->SummonCreature(NPC_CHEMIST_H, ThrallSpawn[65].x + rand32() % 5, ThrallSpawn[65].y + rand32() % 5, ThrallSpawn[65].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 5:
                                me->SummonCreature(NPC_BETRAYER_H, ThrallSpawn[65].x + rand32() % 5, ThrallSpawn[65].y + rand32() % 5, ThrallSpawn[65].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 6:
                                me->SummonCreature(NPC_FELBEAST_H, ThrallSpawn[65].x + rand32() % 5, ThrallSpawn[65].y + rand32() % 5, ThrallSpawn[65].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                // NPC_KHANOK - Inner Sunktum Spawn Right
                case 15:
                    for (uint8 i = 0; i < 4; ++i)
                    {
                        switch (urand(0, 6))
                        {
                            case 0:
                                me->SummonCreature(NPC_FELGUARD_MORADEUR, ThrallSpawn[66].x + rand32() % 5, ThrallSpawn[66].y + rand32() % 5, ThrallSpawn[66].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                                me->SummonCreature(NPC_DREADLORD, ThrallSpawn[66].x + rand32() % 5, ThrallSpawn[66].y + rand32() % 5, ThrallSpawn[66].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 2:
                                me->SummonCreature(NPC_TREACHEROUS_GUARDIAN_H, ThrallSpawn[66].x + rand32() % 5, ThrallSpawn[66].y + rand32() % 5, ThrallSpawn[66].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 3:
                                me->SummonCreature(NPC_DOCTOR_H, ThrallSpawn[66].x + rand32() % 5, ThrallSpawn[66].y + rand32() % 5, ThrallSpawn[66].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 4:
                                me->SummonCreature(NPC_CHEMIST_H, ThrallSpawn[66].x + rand32() % 5, ThrallSpawn[66].y + rand32() % 5, ThrallSpawn[66].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 5:
                                me->SummonCreature(NPC_BETRAYER_H, ThrallSpawn[66].x + rand32() % 5, ThrallSpawn[66].y + rand32() % 5, ThrallSpawn[66].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 6:
                                me->SummonCreature(NPC_FELBEAST_H, ThrallSpawn[66].x + rand32() % 5, ThrallSpawn[66].y + rand32() % 5, ThrallSpawn[66].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                // NPC_KHANOK - Inner Sunktum Spawn Top
                case 16:
                    me->SummonCreature(NPC_DOOMGUARD_PILLARGER, ThrallSpawn[67].x + rand32() % 15, ThrallSpawn[67].y + rand32() % 15, ThrallSpawn[67].z + rand32() % 5, TEMPSUMMON_DEAD_DESPAWN);
                    break;
                // NPC_KHANOK - Inner Sunktum Spawn Middle
                case 17:
                    me->SummonCreature(NPC_KHANOK, ThrallSpawn[68].x, ThrallSpawn[68].y, ThrallSpawn[68].z, TEMPSUMMON_DEAD_DESPAWN);
                    break;
                case 18:
                    if (Creature* temp = me->SummonCreature(NPC_WARSONG_BATTLEGUARD, ThrallSpawn[69].x, ThrallSpawn[69].y, ThrallSpawn[69].z, ThrallSpawn[69].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 240 * IN_MILLISECONDS))
                    {
                        hordeGuardsGUID.push_back(temp->GetGUID());
                        temp->AI()->Talk(SAY_FOR_THE_HORDE);
                        temp->GetMotionMaster()->MovePath(NPC_WARSONG_BATTLEGUARD * 100, false);
                    }
                    break;
                // Valimathras Room Preparation
                case 19:
                    for (uint8 i = 0; i < 3; ++i)
                        me->SummonGameObject(GO_BLOCKED_PASSAGE, ThrallSpawn[i + 70].x, ThrallSpawn[i + 70].y, ThrallSpawn[70].z, ThrallSpawn[i + 70].o, 0.0f, 0.0f, 0.0f, 0.0f, 120 * IN_MILLISECONDS);
                    // Valimathras BossSpawn
                    if (Creature* temp = me->SummonCreature(NPC_VARIMATHRAS, ThrallSpawn[73].x, ThrallSpawn[73].y, ThrallSpawn[73].z, ThrallSpawn[73].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 1200 * IN_MILLISECONDS))
                    {
                        ValimathrasGUID = temp->GetGUID();
                        temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                        temp->CastSpell(me, SPELL_AURA_OF_VARIMATHRAS);
                        temp->CastSpell(me, SPELL_OPENING_LEGION_PORTALS);
                        temp->AI()->Talk(SAY_CLOSE_DOOR);
                    }
                    for (uint8 i = 0; i < 6; ++i)
                    {
                        if (Unit* temp = me->SummonCreature(NPC_VARIMATHRAS_PORTAL, ThrallSpawn[i + 74].x, ThrallSpawn[i + 74].y, ThrallSpawn[i + 74].z, TEMPSUMMON_MANUAL_DESPAWN))
                            temp->CastSpell(me, SPELL_VALIMATHRAS_PORTAL);
                    }
                    break;
                case 20:
                    for (uint8 i = 0; i < 3; ++i)
                    {
                        switch (urand(0, 1))
                        {
                            case 0:
                                me->SummonCreature(NPC_LEGION_INVADER, ThrallSpawn[80].x + rand32() % 5, ThrallSpawn[80].y + rand32() % 5, ThrallSpawn[80].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                                me->SummonCreature(NPC_LEGION_DREADWHISPER, ThrallSpawn[81].x + rand32() % 5, ThrallSpawn[81].y + rand32() % 5, ThrallSpawn[81].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
                case 21:
                    for (uint8 i = 0; i < 3; ++i)
                    {
                        switch (urand(0, 1))
                        {
                            case 0:
                                me->SummonCreature(NPC_LEGION_INVADER, ThrallSpawn[81].x + rand32() % 5, ThrallSpawn[81].y + rand32() % 5, ThrallSpawn[81].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                            case 1:
                                me->SummonCreature(NPC_LEGION_DREADWHISPER, ThrallSpawn[80].x + rand32() % 5, ThrallSpawn[80].y + rand32() % 5, ThrallSpawn[80].z, TEMPSUMMON_DEAD_DESPAWN);
                                break;
                        }
                    }
                    break;
            }
        }

        void WaypointReached(uint32 waypointId) override
        {
            switch (waypointId)
            {
                case 1:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 2:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 11:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 13:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 14:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 34:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 36:
                    Talk(THRALL_SAY_SANCTUM_1);
                    UpdateWorldState(me->GetMap(), WORLD_STATE_INNER_SANKTUM_FIGHT_H, 1);
                    break;
                case 46:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 57:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 61:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 65:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 66:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 75:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 81:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 104:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 109:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 113:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 117:
                    SetHoldState(true);
                    bStepping = true;
                    break;
                case 118:
                    Talk(THRALL_SAY_THRONE_8);
                    break;
                case 120:
                    SetHoldState(true);
                    bStepping = true;
                    break;
            }
        }

        bool CanAIAttack(Unit const* victim) const override
        {
            return victim->GetEntry() != NPC_BLIGHT_SLINGER;
        }

        void UpdateAI(uint32 diff) override
        {
            npc_escortAI::UpdateAI(diff);

            if (bStepping)
            {
                if (phaseTimer <= diff)
                {
                    switch (step)
                    {
                        //Preparation
                        case 0:
                            me->setActive(true);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 1:
                            UpdateWorldState(me->GetMap(), WORLD_STATE_BATTLE_COUNTDOWN_H, 1);
                            Talk(THRALL_SAY_PREP_1);
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 2:
                            Talk(THRALL_SAY_PREP_2);
                            JumpToNextStep(14 * IN_MILLISECONDS);
                            break;
                        case 3:
                            if (Creature* guard = me->FindNearestCreature(NPC_HORDE_GUARD, 100.0f))
                                guard->AI()->Talk(SAY_BURN_UC);
                            JumpToNextStep(19 * IN_MILLISECONDS);
                            break;
                        case 4:
                            if (Creature* guard = me->FindNearestCreature(NPC_HORDE_GUARD, 100.0f))
                                guard->AI()->Talk(SAY_PUTRESS_ANGER);
                            JumpToNextStep(25 * IN_MILLISECONDS);
                            break;
                        case 5:
                            Talk(THRALL_SAY_PREP_3);
                            JumpToNextStep(14 * IN_MILLISECONDS);
                            break;
                        case 6:
                            Talk(THRALL_SAY_PREP_4);
                            JumpToNextStep(14 * IN_MILLISECONDS);
                            break;
                        case 7:
                            Talk(THRALL_SAY_PREP_5);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 8:
                            Talk(THRALL_SAY_PREP_6);
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 9:
                            Talk(THRALL_SAY_PREP_7);
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 10:
                            DoCast(me, SPELL_THRALL_BUFF);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        // Start Event
                        case 11:
                            UpdateWorldState(me->GetMap(), WORLD_STATE_BATTLE_COUNTDOWN_H, 0);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_BATTLE_START_H, 1);
                            Talk(THRALL_SAY_PREP_8);
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0);
                            SetRun(true);
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                            {
                                sylvanas->GetMotionMaster()->MovePath(NPC_SYLVANAS * 100, false);
                                sylvanas->setActive(true);
                            }
                            break;
                        case 12:
                            me->Dismount();
                            JumpToNextStep(1 * IN_MILLISECONDS);
                            break;
                        case 13:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                sylvanas->Dismount();
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 14:
                            Talk(THRALL_SAY_COURTYARD_1);
                            JumpToNextStep(4 * IN_MILLISECONDS);
                            break;
                        case 15:
                            me->CastSpell(me, SPELL_CALL_OF_AIR);
                            SpawnWave(0);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 16:
                            {
                                std::list<Creature*> PlagueList;
                                me->GetCreatureListWithEntryInGrid(PlagueList, NPC_PLAGUE_TRIGGER, 50.0f);
                                if (!PlagueList.empty())
                                    for (std::list<Creature*>::iterator itr = PlagueList.begin(); itr != PlagueList.end(); itr++)
                                        (*itr)->DespawnOrUnsummon();
                                SetEscortPaused(false);
                                SetRun(false);
                                if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                    sylvanas->GetMotionMaster()->MovePath(NPC_SYLVANAS * 1000, false);
                                JumpToNextStep(3 * IN_MILLISECONDS);
                                break;
                            }
                        case 17:
                            bStepping = false;
                            JumpToNextStep(0);
                            break;
                        case 18:
                            SpawnWave(1);
                            Talk(THRALL_SAY_COURTYARD_2);
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 19:
                            {
                                SpawnWave(2);
                                JumpToNextStep(3 * IN_MILLISECONDS);
                                break;
                            }
                        case 20:
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_VALIMATHRAS_INTRO_0);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 21:
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_VALIMATHRAS_INTRO_1);
                            JumpToNextStep(9 * IN_MILLISECONDS);
                            break;
                        case 22:
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_VALIMATHRAS_INTRO_2);
                            JumpToNextStep(7 * IN_MILLISECONDS);
                            break;
                        case 23:
                            SpawnWave(3);
                            JumpToNextStep(1 * IN_MILLISECONDS);
                            break;
                        case 24:
                            if (Creature* valimathrasportal = ObjectAccessor::GetCreature(*me, ValimathrasPortalGUID))
                                valimathrasportal->CastSpell(valimathrasportal, SPELL_VALIMATHRAS_PORTAL);
                            JumpToNextStep(12 * IN_MILLISECONDS);
                            break;
                        case 25:
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                            {
                                valimathras->GetMotionMaster()->MovePoint(0, 1804.559f, 235.504f, 62.753f);
                                valimathras->DespawnOrUnsummon(3 * IN_MILLISECONDS);
                            }
                            if (Creature* valimathrasportal = ObjectAccessor::GetCreature(*me, ValimathrasPortalGUID))
                                valimathrasportal->DespawnOrUnsummon(6 * IN_MILLISECONDS);
                            JumpToNextStep(1 * IN_MILLISECONDS);
                            break;
                        case 26:
                            {
                                Talk(THRALL_SAY_COURTYARD_3);
                                me->CastSpell(me, SPELL_TIDAL_WAVE_SUMMON);
                                std::list<Creature*> HelperList;
                                me->GetCreatureListWithEntryInGrid(HelperList, NPC_SLINGER_TRIGGER, 1000.0f);
                                if (!HelperList.empty())
                                    for (std::list<Creature*>::iterator itr = HelperList.begin(); itr != HelperList.end(); itr++)
                                        (*itr)->DespawnOrUnsummon();
                                JumpToNextStep(5 * IN_MILLISECONDS);
                                break;
                            }
                        // Start COURTYARD_FIGHT
                        case 27:
                            FollowThrall();
                            SetEscortPaused(false);
                            bStepping = false;
                            SetRun(true);
                            Talk(THRALL_SAY_COURTYARD_4);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_BATTLE_START_H, 0);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_COURTYARD_FIGHT_H, 1);
                            JumpToNextStep(0);
                            break;
                        case 28:
                            {
                                EnableAttack = true;
                                DoCast(me, SPELL_HEROIC_VANGUARD, true);
                                std::list<Creature*> HostileEndList;
                                me->GetCreatureListWithEntryInGrid(HostileEndList, NPC_TREACHEROUS_GUARDIAN_H, 1000.0f);
                                me->GetCreatureListWithEntryInGrid(HostileEndList, NPC_DOCTOR_H, 1000.0f);
                                me->GetCreatureListWithEntryInGrid(HostileEndList, NPC_CHEMIST_H, 1000.0f);
                                if (!HostileEndList.empty())
                                    for (std::list<Creature*>::iterator itr = HostileEndList.begin(); itr != HostileEndList.end(); itr++)
                                        (*itr)->setFaction(FACTION_HOSTILE);
                                SpawnWave(4);
                                JumpToNextStep(10 * IN_MILLISECONDS);
                                break;
                            }
                        case 29:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 30:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 31:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 32:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 33:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 34:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 35:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 36:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 37:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 38:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 39:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 40:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 41:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 42:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 43:
                            SpawnWave(4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 44:
                            SpawnWave(4);
                            JumpToNextStep(2 * IN_MILLISECONDS);
                            break;
                        case 45:
                            SpawnWave(5);
                            bStepping = false;
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        // End COURTYARD_FIGHT
                        case 46:
                            {
                                FollowThrall();
                                std::list<Creature*> HostileList;
                                me->GetCreatureListWithEntryInGrid(HostileList, NPC_TREACHEROUS_GUARDIAN_H, 1000.0f);
                                me->GetCreatureListWithEntryInGrid(HostileList, NPC_DOCTOR_H, 1000.0f);
                                me->GetCreatureListWithEntryInGrid(HostileList, NPC_CHEMIST_H, 1000.0f);
                                me->GetCreatureListWithEntryInGrid(HostileList, NPC_BLIGHT_SLINGER, 1000.0f);
                                if (!HostileList.empty())
                                    for (std::list<Creature*>::iterator itr = HostileList.begin(); itr != HostileList.end(); itr++)
                                        (*itr)->DespawnOrUnsummon();
                                for (uint8 i = 0; i < 7; ++i)
                                    me->SummonGameObject(GO_HORDE_BANNER, ThrallSpawn[i + 37].x, ThrallSpawn[i + 37].y, ThrallSpawn[i + 37].z, ThrallSpawn[i + 37].o, 0.0f, 0.0f, 0.0f, 0.0f, 120 * IN_MILLISECONDS);
                                SpawnWave(6);
                                SetEscortPaused(false);
                                bStepping = false;
                                SetRun(false);
                                JumpToNextStep(0 * IN_MILLISECONDS);
                                break;
                            }
                        case 47:
                            Talk(THRALL_SAY_COURTYARD_5);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 48:
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        // Elevator Event
                        case 49:
                            Talk(THRALL_SAY_ELEVATOR_1);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 50:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                sylvanas->AI()->Talk(SYLVANAS_SAY_ELEVATOR_1);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 51:
                            Talk(THRALL_SAY_ELEVATOR_2);
                            DoCast(me, SPELL_CALL_OF_AIR);
                            SpawnWave(7);
                            JumpToNextStep(16 * IN_MILLISECONDS);
                            break;
                        case 52:
                            Talk(THRALL_SAY_ELEVATOR_3);
                            JumpToNextStep(4 * IN_MILLISECONDS);
                            break;
                        case 53:
                            SpawnWave(8);
                            me->GetMotionMaster()->MoveJump(ThrallSpawn[55].x, ThrallSpawn[55].y, ThrallSpawn[55].z, 40.0f, 40.0f, 0);
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                sylvanas->GetMotionMaster()->MoveJump(ThrallSpawn[56].x, ThrallSpawn[56].y, ThrallSpawn[56].z, 40.0f, 40.0f, 0);
                            JumpToNextStep(4 * IN_MILLISECONDS);
                            break;
                        case 54:
                            FollowThrall();
                            SetEscortPaused(false);
                            bStepping = false;
                            SetRun(false);
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        // Top of Undercity Discussion
                        case 55:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                sylvanas->AI()->Talk(SYLVANAS_SAY_SANCTUM_1);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 56:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                sylvanas->AI()->Talk(SYLVANAS_SAY_SANCTUM_2);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 57:
                            Talk(THRALL_SAY_SANCTUM_2);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 58:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                sylvanas->AI()->Talk(SYLVANAS_SAY_SANCTUM_3);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        // Top of Undercity - Fight
                        case 59:
                            SpawnWave(9);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 60:
                            SpawnWave(9);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 61:
                            SpawnWave(9);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 62:
                            FollowThrall();
                            SetEscortPaused(false);
                            bStepping = false;
                            SetRun(false);
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        case 63:
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 64:
                            SpawnWave(10);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 65:
                            SpawnWave(10);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 66:
                            FollowThrall();
                            SetEscortPaused(false);
                            bStepping = false;
                            SetRun(false);
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        case 67:
                            SpawnWave(11);
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 68:
                            SpawnWave(11);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 69:
                            FollowThrall();
                            SetEscortPaused(false);
                            bStepping = false;
                            SetRun(false);
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        // KHANOK - Valimathtas Intro
                        case 70:
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 71:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                sylvanas->AI()->Talk(SYLVANAS_SAY_SANCTUM_4);
                            SpawnWave(12);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 72:
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_VALIMATHRAS_INNER_SANKTUM_0);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 73:
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_VALIMATHRAS_INNER_SANKTUM_1);
                            SpawnWave(13);
                            JumpToNextStep(2 * IN_MILLISECONDS);
                            break;
                        case 74:
                            if (Creature* valimathrasportal = ObjectAccessor::GetCreature(*me, ValimathrasPortalGUID))
                                valimathrasportal->CastSpell(valimathrasportal, SPELL_VALIMATHRAS_PORTAL);
                            JumpToNextStep(4 * IN_MILLISECONDS);
                            break;
                        case 75:
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                            {
                                valimathras->GetMotionMaster()->MovePoint(0, 1596.642f, 429.811f, -46.3429f);
                                valimathras->DespawnOrUnsummon(3 * IN_MILLISECONDS);
                            }
                            if (Creature* valimathrasportal = ObjectAccessor::GetCreature(*me, ValimathrasPortalGUID))
                                valimathrasportal->DespawnOrUnsummon(3 * IN_MILLISECONDS);
                            JumpToNextStep(2 * IN_MILLISECONDS);
                            break;
                        // KHANOK - Trashspawn
                        case 76:
                            SpawnWave(14);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 77:
                            SpawnWave(15);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 78:
                            SpawnWave(16);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 79:
                            SpawnWave(14);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 80:
                            SpawnWave(15);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 81:
                            SpawnWave(16);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 82:
                            SpawnWave(14);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 83:
                            SpawnWave(15);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 84:
                            SpawnWave(16);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 85:
                            SpawnWave(14);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 86:
                            SpawnWave(15);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 87:
                            SpawnWave(16);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 88:
                            SpawnWave(14);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 89:
                            SpawnWave(15);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 90:
                            SpawnWave(16);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 91:
                            SpawnWave(14);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 92:
                            SpawnWave(15);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 93:
                            SpawnWave(16);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 94:
                            // Spawn Boss 2 KHANOK
                            SpawnWave(17);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 95:
                            SpawnWave(14);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 96:
                            SpawnWave(15);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 97:
                            SpawnWave(16);
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            bStepping = false;
                            break;
                        case 98:
                            // KHANOK - Won
                            Talk(THRALL_SAY_SANCTUM_3);
                            JumpToNextStep(7 * IN_MILLISECONDS);
                            break;
                        case 99:
                            SpawnWave(18);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 100:
                            SpawnWave(18);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 101:
                            SpawnWave(18);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 102:
                            SpawnWave(18);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 103:
                            SpawnWave(18);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 104:
                            SpawnWave(18);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 105:
                            SpawnWave(18);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 106:
                            SpawnWave(18);
                            JumpToNextStep(0.35 * IN_MILLISECONDS);
                            break;
                        case 107:
                            SpawnWave(18);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 108:
                            Talk(THRALL_SAY_SANCTUM_4);
                            FollowThrall();
                            SetEscortPaused(false);
                            bStepping = false;
                            SetRun(false);
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        case 109:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                sylvanas->AI()->Talk(SYLVANAS_SAY_SANCTUM_5);
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 110:
                            SpawnWave(19);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 111:
                            FollowThrall();
                            SetEscortPaused(false);
                            bStepping = false;
                            SetRun(true);
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        case 112:
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 113:
                            Talk(THRALL_SAY_SANCTUM_5);
                            JumpToNextStep(12 * IN_MILLISECONDS);
                            break;
                        case 114:
                            Talk(THRALL_SAY_SANCTUM_6);
                            DoCast(me, SPELL_CALL_OF_EARTH);
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 115:
                            {
                                std::list<GameObject*> SaronitList;
                                GetGameObjectListWithEntryInGrid(SaronitList, me, GO_BLOCKED_PASSAGE, 80.0f);
                                for (std::list<GameObject*>::const_iterator itr = SaronitList.begin(); itr != SaronitList.end(); ++itr)
                                    if (GameObject* saronit = (*itr))
                                        saronit->UseDoorOrButton();
                                JumpToNextStep(5 * IN_MILLISECONDS);
                                break;
                            }
                        case 116:
                            Talk(THRALL_SAY_SANCTUM_7);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_ROYAL_QUARTER_FIGHT_H, 1);
                            FollowThrall();
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        // Valimathras Intro
                        case 117:
                            Talk(THRALL_SAY_THRONE_1);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 118:
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                            {
                                valimathras->AI()->Talk(SAY_THRONE_1);
                                valimathras->CastSpell(me, SPELL_OPENING_LEGION_PORTALS);
                            }
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 119:
                            SpawnWave(20);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 120:
                            SpawnWave(21);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 121:
                            ActivateValimathrasPortal();
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_THRONE_2);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 122:
                            SpawnWave(20);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 123:
                            SpawnWave(21);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 124:
                            ActivateValimathrasPortal();
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_THRONE_3);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 125:
                            SpawnWave(20);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 126:
                            SpawnWave(21);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 127:
                            ActivateValimathrasPortal();
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_THRONE_4);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 128:
                            SpawnWave(20);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 129:
                            SpawnWave(21);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 130:
                            ActivateValimathrasPortal();
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_THRONE_5);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 131:
                            SpawnWave(20);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 132:
                            SpawnWave(21);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 133:
                            ActivateValimathrasPortal();
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                                valimathras->AI()->Talk(SAY_THRONE_6);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 134:
                            SpawnWave(20);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 135:
                            SpawnWave(21);
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 136:
                            ActivateValimathrasPortal();
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        // Valimathras Fight
                        case 137:
                            if (Creature* valimathras = ObjectAccessor::GetCreature(*me, ValimathrasGUID))
                            {
                                valimathras->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                                valimathras->RemoveAura(SPELL_AURA_OF_VARIMATHRAS);
                                valimathras->RemoveAura(SPELL_OPENING_LEGION_PORTALS);
                                valimathras->AI()->Talk(SAY_VALIMATHRAS_ATTACK);
                                valimathras->AddThreat(me, 100.0f);
                                me->AddThreat(valimathras, 100.0f);
                                valimathras->AI()->AttackStart(me);
                            }
                            bStepping = false;
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        // Valimathras Won
                        case 138:
                            Talk(THRALL_SAY_THRONE_2);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 139:
                            Talk(THRALL_SAY_THRONE_3);
                            JumpToNextStep(2 * IN_MILLISECONDS);
                            break;
                        case 140:
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        case 141:
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 142:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                me->SetFacingToObject(sylvanas);
                            Talk(THRALL_SAY_THRONE_4);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 143:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                            {
                                sylvanas->GetMotionMaster()->MovePoint(0, 1289.48f, 314.33f, -57.32f, true);
                                sylvanas->CastSpell(sylvanas, SPELL_LEAP_TO_PLATFORM);
                            }
                            JumpToNextStep(10 * IN_MILLISECONDS);
                            break;
                        case 144:
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                            {
                                sylvanas->AI()->Talk(SYLVANAS_SAY_THRONE_1);
                                me->SetFacingToObject(sylvanas);
                                sylvanas->SetFacingToObject(me);
                                me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE);
                            }
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 145:
                            Talk(THRALL_SAY_THRONE_5);
                            FollowThrall();
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        case 146:
                            Talk(THRALL_SAY_THRONE_6);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        case 147:
                            for (uint8 i = 0; i < ALLIANCE_FORCE_MAXCOUNT; ++i)
                            {
                                if (Creature* temp = me->SummonCreature(NPC_SW_SOLDIER, AllianceSpawn[i + 25].x, AllianceSpawn[i + 25].y, AllianceSpawn[i + 25].z, AllianceSpawn[i + 25].o, TEMPSUMMON_MANUAL_DESPAWN))
                                {
                                    allianceForcesGUID[i] = temp->GetGUID();
                                    temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                                    temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                                    temp->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);
                                    temp->SetReactState(REACT_PASSIVE);
                                    temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
                                }
                            }
                            if (Creature* wrynn = me->SummonCreature(NPC_WRYNN, 1308.862f, 381.809f, -66.044243f, TEMPSUMMON_MANUAL_DESPAWN))
                            {
                                WrynnGUID = wrynn->GetGUID();
                                wrynn->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                                wrynn->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                                wrynn->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
                                wrynn->SetReactState(REACT_PASSIVE);
                                wrynn->GetMotionMaster()->MovePoint(0, 1302.543f, 359.472f, -67.295f, true);
                            }
                            if (Creature* jaina = me->SummonCreature(NPC_JAINA, 1308.862f, 381.809f, -66.044243f, TEMPSUMMON_MANUAL_DESPAWN))
                            {
                                JainaGUID = jaina->GetGUID();
                                jaina->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                                jaina->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                                jaina->SetReactState(REACT_PASSIVE);
                            }
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        // Wrynn Intro
                        case 148:
                            if (Creature* wrynn = ObjectAccessor::GetCreature(*me, WrynnGUID))
                                wrynn->AI()->Talk(WRYNN_SAY_THRONE_5);
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 149:
                            if (Creature* wrynn = ObjectAccessor::GetCreature(*me, WrynnGUID))
                                wrynn->AI()->Talk(WRYNN_SAY_THRONE_6);
                            JumpToNextStep(15 * IN_MILLISECONDS);
                            break;
                        case 150:
                            if (Creature* wrynn = ObjectAccessor::GetCreature(*me, WrynnGUID))
                                wrynn->AI()->Talk(WRYNN_SAY_THRONE_7);
                            JumpToNextStep(16.5 * IN_MILLISECONDS);
                            break;
                        case 151:
                            if (Creature* wrynn = ObjectAccessor::GetCreature(*me, WrynnGUID))
                                wrynn->AI()->Talk(WRYNN_SAY_THRONE_8);
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        // Wrynn Fight
                        case 152:
                            me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                            if (Creature* wrynn = ObjectAccessor::GetCreature(*me, WrynnGUID))
                            {
                                wrynn->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
                                wrynn->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                                wrynn->SetReactState(REACT_AGGRESSIVE);
                                wrynn->AddThreat(me, 100.0f);
                                me->AddThreat(wrynn, 100.0f);
                                wrynn->AI()->AttackStart(me);
                            }

                            for (uint8 i = 0; i < ALLIANCE_FORCE_MAXCOUNT; ++i)
                            {
                                if (Creature* temp = ObjectAccessor::GetCreature(*me, allianceForcesGUID[i]))
                                {
                                    temp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
                                    temp->SetReactState(REACT_AGGRESSIVE);
                                    temp->AddThreat(me, 100.0f);
                                    temp->AI()->AttackStart(me);
                                }
                            }
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 153:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, JainaGUID))
                            {
                                jaina->GetMotionMaster()->MovePoint(0, AllianceWP[9].x, AllianceWP[9].y, AllianceWP[9].z);
                                jaina->AI()->Talk(JAINA_SAY_THRONE_2);
                            }
                            JumpToNextStep(8 * IN_MILLISECONDS);
                            break;
                        case 154:
                            if (Creature* jaina = ObjectAccessor::GetCreature(*me, JainaGUID))
                            {
                                jaina->AI()->DoCastAOE(SPELL_DEEPFREEZE);
                                jaina->AI()->Talk(JAINA_SAY_THRONE_3);
                            }
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 155:
                            {
                                std::list<Creature*> HelperList;
                                me->GetCreatureListWithEntryInGrid(HelperList, NPC_SW_SOLDIER, 100.0f);
                                me->GetCreatureListWithEntryInGrid(HelperList, NPC_JAINA, 100.0f);
                                me->GetCreatureListWithEntryInGrid(HelperList, NPC_WRYNN, 100.0f);
                                if (!HelperList.empty())
                                    for (std::list<Creature*>::iterator itr = HelperList.begin(); itr != HelperList.end(); itr++)
                                        (*itr)->DespawnOrUnsummon();
                                JumpToNextStep(8 * IN_MILLISECONDS);
                                break;
                            }
                        case 156:
                            Talk(THRALL_SAY_THRONE_7);
                            SetEscortPaused(false);
                            bStepping = false;
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                        case 157:
                            Talk(THRALL_SAY_THRONE_9);
                            me->SetStandState(UNIT_STAND_STATE_SIT);
                            JumpToNextStep(3 * IN_MILLISECONDS);
                            break;
                        // Ending
                        case 158:
                            if (Creature* saurfang = me->SummonCreature(NPC_OVERLORD_SAURFANG, 1297.574f, 347.154f, -65.027f, TEMPSUMMON_MANUAL_DESPAWN))
                            {
                                SaurfangGUID = saurfang->GetGUID();
                                saurfang->SetWalk(true);
                                saurfang->GetMotionMaster()->MovePoint(0, 1300.862f, 353.670f, -66.187f, true);
                            }
                            JumpToNextStep(7 * IN_MILLISECONDS);
                            break;
                        case 159:
                            if (Creature* saurfang = ObjectAccessor::GetCreature(*me, SaurfangGUID))
                            {
                                saurfang->AI()->Talk(SAY_SAURFANG_ARRIVAL_1);
                                saurfang->SetStandState(UNIT_STAND_STATE_SIT);
                            }
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 160:
                            if (Creature* saurfang = ObjectAccessor::GetCreature(*me, SaurfangGUID))
                                saurfang->AI()->Talk(SAY_SAURFANG_ARRIVAL_2);
                            JumpToNextStep(6 * IN_MILLISECONDS);
                            break;
                        case 161:
                            if (Creature* saurfang = ObjectAccessor::GetCreature(*me, SaurfangGUID))
                                saurfang->AI()->Talk(SAY_SAURFANG_ARRIVAL_3);
                            if (Map* map = me->GetMap())
                            {
                                Map::PlayerList const& PlayerList = map->GetPlayers();
                                for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
                                {
                                    if (Player* player = i->GetSource())
                                    {
                                        if (me->IsInRange(player, 0.0f, 50.0f))
                                        {
                                            if (player->GetTeamId() == TEAM_HORDE)
                                            {
                                                player->RemoveAura(SPELL_SYLVANAS_BUFF);
                                                player->RemoveAura(SPELL_THRALL_BUFF);
                                                player->CompleteQuest(QUEST_BATTLE_H);
                                                player->CastSpell(player, SPELL_PHASING_HORDE);
                                            }
                                        }
                                    }
                                }
                            }
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 162:
                            Talk(THRALL_SAY_THRONE_10);
                            JumpToNextStep(5 * IN_MILLISECONDS);
                            break;
                        case 163:
                            me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
                            me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER);
                            Talk(THRALL_SAY_THRONE_11);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_ROYAL_QUARTER_FIGHT_H, 0);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_INNER_SANKTUM_FIGHT_H, 0);
                            UpdateWorldState(me->GetMap(), WORLD_STATE_COURTYARD_FIGHT_H, 0);
                            std::list<Creature*> HelperList;
                            me->GetCreatureListWithEntryInGrid(HelperList, NPC_SYLVANAS, 100.0f);
                            me->GetCreatureListWithEntryInGrid(HelperList, NPC_OVERLORD_SAURFANG, 100.0f);
                            if (!HelperList.empty())
                                for (std::list<Creature*>::iterator itr = HelperList.begin(); itr != HelperList.end(); itr++)
                                    (*itr)->DespawnOrUnsummon(120 * IN_MILLISECONDS);
                            me->DespawnOrUnsummon(120 * IN_MILLISECONDS);
                            bStepping = false;
                            JumpToNextStep(0 * IN_MILLISECONDS);
                            break;
                    }
                }
                else phaseTimer -= diff;
            }

            if (!UpdateVictim())
                return;

            _events.Update(diff);

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

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_CHAIN_LIGHTNING:
                        DoCastVictim(SPELL_CHAIN_LIGHTNING);
                        _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, urand(5, 8) * IN_MILLISECONDS);
                        break;
                    case EVENT_LAVA_BURST:
                        DoCastVictim(SPELL_LAVA_BURST);
                        _events.ScheduleEvent(EVENT_LAVA_BURST, urand(3, 5) * IN_MILLISECONDS);
                        break;
                    case EVENT_THUNDER:
                        DoCast(me, SPELL_THUNDER);
                        _events.ScheduleEvent(EVENT_THUNDER, 15 * IN_MILLISECONDS);
                        break;
                    case EVENT_AGGRO_SYLVANAS:
                        if (me->GetVictim())
                            if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasfollowGUID))
                                sylvanas->AI()->AttackStart(me->GetVictim());
                        _events.ScheduleEvent(EVENT_AGGRO_SYLVANAS, 2 * IN_MILLISECONDS);
                        break;
                    case EVENT_THRALL_BUFF:
                        DoCast(me, SPELL_THRALL_BUFF);
                        _events.ScheduleEvent(EVENT_THRALL_BUFF, 10 * IN_MILLISECONDS);
                        break;
                    default:
                        break;
                }
            }

            DoMeleeAttackIfReady();
        }
    };

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

/*######
## npc_lady_sylvanas_windrunner_bfu
######*/

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

    struct npc_lady_sylvanas_windrunner_bfuAI : public ScriptedAI
    {
        npc_lady_sylvanas_windrunner_bfuAI(Creature* creature) : ScriptedAI(creature)
        {
            Reset();
        }

        void InitializeAI() override
        {
            me->Mount(MODEL_SKELETON_MOUNT);
        }

        void JustRespawned() override
        {
            me->Mount(MODEL_SKELETON_MOUNT);
        }

        void Reset() override
        {
            me->SetCorpseDelay(1);
            me->SetRespawnTime(1);
            _events.ScheduleEvent(EVENT_SUMMON_SKELETON, 20 * IN_MILLISECONDS);
            _events.ScheduleEvent(EVENT_BLACK_ARROW, 15 * IN_MILLISECONDS);
            _events.ScheduleEvent(EVENT_SHOOT, 5 * IN_MILLISECONDS);
            _events.ScheduleEvent(EVENT_MULTI_SHOT, 6 * IN_MILLISECONDS);
            _events.ScheduleEvent(EVENT_SHRIEK_OF_HIGHBORN, 3 * IN_MILLISECONDS);
            _events.ScheduleEvent(EVENT_SYLVANAS_BUFF, 1 * IN_MILLISECONDS);
            me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_WRYNN_BUFF, true);
            me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_INCREASE_SPEED, true);
        }

        bool CanAIAttack(Unit const* victim) const override
        {
            return victim->GetEntry() != NPC_BLIGHT_SLINGER;
        }

        void UpdateAI(uint32 diff) override
        {
            if (!me->GetVictim())
                return;

            _events.Update(diff);

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

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_SUMMON_SKELETON:
                        DoCast(me, SPELL_SUMMON_SKELETON);
                        _events.ScheduleEvent(EVENT_SUMMON_SKELETON, urand(20, 30) * IN_MILLISECONDS);
                        break;
                    case EVENT_BLACK_ARROW:
                        if (Unit* victim = me->GetVictim())
                            DoCast(victim, SPELL_BLACK_ARROW);
                        _events.ScheduleEvent(EVENT_BLACK_ARROW, urand(6, 9) * IN_MILLISECONDS);
                        break;
                    case EVENT_SHOOT:
                        if (Unit* victim = me->GetVictim())
                            DoCast(victim, SPELL_SHOT);
                        _events.ScheduleEvent(EVENT_SHOOT, urand(5, 10) * IN_MILLISECONDS);
                        break;
                    case EVENT_MULTI_SHOT:
                        if (Unit* victim = me->GetVictim())
                            DoCast(victim, SPELL_MULTI_SHOT);
                        _events.ScheduleEvent(EVENT_MULTI_SHOT, urand(10, 13) * IN_MILLISECONDS);
                        break;
                    case EVENT_SHRIEK_OF_HIGHBORN:
                        DoCastVictim(SPELL_SHRIEK_OF_HIGHBORN);
                        _events.ScheduleEvent(EVENT_SHRIEK_OF_HIGHBORN, 3 * IN_MILLISECONDS);
                        break;
                    case EVENT_SYLVANAS_BUFF:
                        DoCast(me, SPELL_SYLVANAS_BUFF, true);
                        break;
                    default:
                        break;
                }
            }

            DoMeleeAttackIfReady();
        }

    private:
        EventMap _events;
    };

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

/*######
## AddSC
######*/

void AddSC_undercity()
{
    new npc_lady_sylvanas_windrunner();
    new npc_highborne_lamenter();
    new npc_parqual_fintallas();

    new npc_varian_wrynn();
    new npc_thrall_bfu();
    new npc_jaina_proudmoore_bfu();
    new npc_lady_sylvanas_windrunner_bfu();
    new boss_blight_worm();
    new spell_blight_worm_ingest();
}
