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

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "violet_hold.h"
#include "Player.h"

#define CLEANUP_CHECK_INTERVAL  5000
#define SPAWN_TIME              20000

enum vYells
{
    CYANIGOSA_SAY_SPAWN       = 3,
    SAY_SINCLARI_1            = 0
};

class instance_violet_hold : public InstanceMapScript
{
public:
    instance_violet_hold() : InstanceMapScript("instance_violet_hold", 608) { }

    InstanceScript* GetInstanceScript(InstanceMap* pMap) const
    {
        return new instance_violet_hold_InstanceMapScript(pMap);
    }

    struct instance_violet_hold_InstanceMapScript : public InstanceScript
    {
        instance_violet_hold_InstanceMapScript(Map* pMap) : InstanceScript(pMap) {}

        uint32 m_auiEncounter[MAX_ENCOUNTER];
        bool CLEANED;
        uint8 EncounterStatus;
        uint32 uiFirstBoss, uiSecondBoss;
        std::string str_data;
        EventMap events;
        uint8 GateHealth;
        uint8 WaveCount;
        uint8 PortalLocation;
        bool bAchiev;
        bool bDefensesUsed;

        std::vector<uint64> GO_ActivationCrystalGUID;
        uint64 GO_MainGateGUID;

        uint64 GO_MoraggCellGUID;
        uint64 GO_ErekemCellGUID;
        uint64 GO_ErekemRightGuardCellGUID;
        uint64 GO_ErekemLeftGuardCellGUID;
        uint64 GO_IchoronCellGUID;
        uint64 GO_LavanthorCellGUID;
        uint64 GO_XevozzCellGUID;
        uint64 GO_ZuramatCellGUID;

        std::set<uint64> trashMobs;
        uint64 NPC_SinclariGUID;
        uint64 NPC_GuardGUID[4];
        uint64 NPC_PortalGUID;
        uint64 NPC_DoorSealGUID;

        uint64 NPC_MoraggGUID;
        uint64 NPC_ErekemGUID;
        uint64 NPC_ErekemGuardGUID[2];
        uint64 NPC_IchoronGUID;
        uint64 NPC_LavanthorGUID;
        uint64 NPC_XevozzGUID;
        uint64 NPC_ZuramatGUID;
        uint64 NPC_CyanigosaGUID;

        void Initialize()
        {
            memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
            CLEANED = false;
            EncounterStatus = NOT_STARTED;
            uiFirstBoss = 0;
            uiSecondBoss = 0;
            events.Reset();
            events.RescheduleEvent(EVENT_CHECK_PLAYERS, 0);
            GateHealth = 100;
            WaveCount = 0;
            PortalLocation = 0;
            bDefensesUsed = false;

            GO_ActivationCrystalGUID.clear();
            GO_MainGateGUID = 0;

            GO_MoraggCellGUID = 0;
            GO_ErekemCellGUID = 0;
            GO_ErekemRightGuardCellGUID = 0;
            GO_ErekemLeftGuardCellGUID = 0;
            GO_IchoronCellGUID = 0;
            GO_LavanthorCellGUID = 0;
            GO_XevozzCellGUID = 0;
            GO_ZuramatCellGUID = 0;

            NPC_SinclariGUID = 0;
            memset(&NPC_GuardGUID, 0, sizeof(NPC_GuardGUID));
            NPC_PortalGUID = 0;
            NPC_DoorSealGUID = 0;

            NPC_MoraggGUID = 0;
            NPC_ErekemGUID = 0;
            NPC_ErekemGuardGUID[0] = NPC_ErekemGuardGUID[1] = 0;
            NPC_IchoronGUID = 0;
            NPC_LavanthorGUID = 0;
            NPC_XevozzGUID = 0;
            NPC_ZuramatGUID = 0;
            NPC_CyanigosaGUID = 0;
        }

        bool IsEncounterInProgress() const
        {
            return false;
        }

        void OnCreatureCreate(Creature* creature)
        {
            switch(creature->GetEntry())
            {
                case NPC_SINCLARI:
                    NPC_SinclariGUID = creature->GetGUID();
                    break;
                case NPC_VIOLET_HOLD_GUARD:
                    for (uint8 i=0; i<4; ++i)
                        if (NPC_GuardGUID[i] == 0)
                        {
                            NPC_GuardGUID[i] = creature->GetGUID();
                            break;
                        }
                    break;
                case NPC_DEFENSE_DUMMY_TARGET:
                    creature->ApplySpellImmune(0, IMMUNITY_ID, SPELL_ARCANE_LIGHTNING, true);
                    break;
                case NPC_TELEPORTATION_PORTAL:
                    NPC_PortalGUID = creature->GetGUID();
                    break;
                case NPC_PRISON_DOOR_SEAL:
                    NPC_DoorSealGUID = creature->GetGUID();
                    break;
                // BOSSES BELOW:
                case NPC_XEVOZZ:
                    NPC_XevozzGUID = creature->GetGUID();
                    break;
                case NPC_LAVANTHOR:
                    NPC_LavanthorGUID = creature->GetGUID();
                    break;
                case NPC_ICHORON:
                    NPC_IchoronGUID = creature->GetGUID();
                    break;
                case NPC_ZURAMAT:
                    NPC_ZuramatGUID = creature->GetGUID();
                    break;
                case NPC_EREKEM:
                    NPC_ErekemGUID = creature->GetGUID();
                    break;
                case NPC_EREKEM_GUARD:
                    if (NPC_ErekemGuardGUID[0] == 0)
                        NPC_ErekemGuardGUID[0] = creature->GetGUID();
                    else
                        NPC_ErekemGuardGUID[1] = creature->GetGUID();
                    break;
                case NPC_MORAGG:
                    NPC_MoraggGUID = creature->GetGUID();
                    break;
                case NPC_CYANIGOSA:
                    NPC_CyanigosaGUID = creature->GetGUID();
                    break;
            }
        }

        void OnGameObjectCreate(GameObject* go)
        {
            switch(go->GetEntry())
            {
                case GO_ACTIVATION_CRYSTAL:
                    HandleGameObject(0, false, go); // make go not used yet
                    go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); // not useable at the beginning
                    GO_ActivationCrystalGUID.push_back(go->GetGUID());
                    break;
                case GO_MAIN_DOOR:
                    GO_MainGateGUID = go->GetGUID();
                    break;
                // BOSS GATES BELOW:
                case GO_EREKEM_GUARD_1_DOOR:
                    GO_ErekemLeftGuardCellGUID = go->GetGUID();
                    break;
                case GO_EREKEM_GUARD_2_DOOR:
                    GO_ErekemRightGuardCellGUID = go->GetGUID();
                    break;
                case GO_EREKEM_DOOR:
                    GO_ErekemCellGUID = go->GetGUID();
                    break;
                case GO_ZURAMAT_DOOR:
                    GO_ZuramatCellGUID = go->GetGUID();
                    break;
                case GO_LAVANTHOR_DOOR:
                    GO_LavanthorCellGUID = go->GetGUID();
                    break;
                case GO_MORAGG_DOOR:
                    GO_MoraggCellGUID = go->GetGUID();
                    break;
                case GO_ICHORON_DOOR:
                    GO_IchoronCellGUID = go->GetGUID();
                    break;
                case GO_XEVOZZ_DOOR:
                    GO_XevozzCellGUID = go->GetGUID();
                    break;
            }
        }

        void SetData(uint32 type, uint32 data)
        {
            switch(type)
            {
                case DATA_ACTIVATE_DEFENSE_SYSTEM:
                    {
                        if (data)
                            bDefensesUsed = true;
                        const Position pos = {1919.09546f, 812.29724f, 86.2905f, M_PI};
                        instance->SummonCreature(NPC_DEFENSE_SYSTEM, pos, 0, 6499);
                    }
                    break;
                case DATA_START_INSTANCE:
                    if (EncounterStatus == NOT_STARTED)
                    {
                        EncounterStatus = IN_PROGRESS;
                        if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
                            c->AI()->Talk(SAY_SINCLARI_1);
                        events.RescheduleEvent(EVENT_GUARDS_FALL_BACK, 4000);
                    }
                    break;
                case DATA_PORTAL_DEFEATED:
                    events.RescheduleEvent(EVENT_SUMMON_PORTAL, 3000);
                    break;
                case DATA_PORTAL_LOCATION:
                    PortalLocation = data;
                    break;
                case DATA_DECRASE_DOOR_HEALTH:
                    if (GateHealth>0)
                        --GateHealth;
                    if (GateHealth==0)
                    {
                        CLEANED = false;
                        InstanceCleanup();
                    }
                    DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, (uint32)GateHealth);
                    break;
                case DATA_RELEASE_BOSS:
                    if (WaveCount == 6)
                        StartBossEncounter(uiFirstBoss);
                    else
                        StartBossEncounter(uiSecondBoss);
                    break;
                case DATA_BOSS_DIED:
                    if (WaveCount == 6)
                        m_auiEncounter[0] = DONE;
                    else if (WaveCount == 12)
                        m_auiEncounter[1] = DONE;
                    else if (WaveCount == 18)
                    {
                        m_auiEncounter[2] = DONE;
                        EncounterStatus = DONE;
                        HandleGameObject(GO_MainGateGUID, true);
                        DoUpdateWorldState(WORLD_STATE_VH_SHOW, 0);
                        if (Creature* c = instance->GetCreature(NPC_SinclariGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); }
                    }
                    SaveToDB();
                    if (WaveCount < 18)
                        events.RescheduleEvent(EVENT_SUMMON_PORTAL, 35000);
                    break;
                case DATA_FAILED:
                    CLEANED = false;
                    InstanceCleanup();
                    break;
                case DATA_ACHIEV:
                    bAchiev = data ? true : false;
                    break;
            }
        }

        void SetData64(uint32 type, uint64 data)
        {
            switch(type)
            {
                case DATA_ADD_TRASH_MOB:
                    trashMobs.insert(data);
                    break;
                case DATA_DELETE_TRASH_MOB:
                    if (!CLEANED)
                        trashMobs.erase(data);
                    break;
            }
        }

        uint32 GetData(uint32 type) const
        {
            switch(type)
            {
                case DATA_ENCOUNTER_STATUS:
                    return (uint32)EncounterStatus;
                case DATA_WAVE_COUNT:
                    return (uint32)WaveCount;
                case DATA_PORTAL_LOCATION:
                    return PortalLocation;
                case DATA_FIRST_BOSS_NUMBER:
                    return uiFirstBoss;
                case DATA_SECOND_BOSS_NUMBER:
                    return uiSecondBoss;
            }

            return 0;
        }

        uint64 GetData64(uint32 identifier) const
        {
            switch(identifier)
            {
                case DATA_TELEPORTATION_PORTAL_GUID:
                    return NPC_PortalGUID;
                case DATA_DOOR_SEAL_GUID:
                    return NPC_DoorSealGUID;
                case DATA_EREKEM_GUID:
                    return NPC_ErekemGUID;
                case DATA_EREKEM_GUARD_1_GUID:
                    return NPC_ErekemGuardGUID[0];
                case DATA_EREKEM_GUARD_2_GUID:
                    return NPC_ErekemGuardGUID[1];
                case DATA_ICHORON_GUID:
                    return NPC_IchoronGUID;
            }

            return 0;
        }

        void StartBossEncounter(uint8 uiBoss)
        {
            Creature* pBoss = NULL;

            switch(uiBoss)
            {
                case BOSS_MORAGG:
                    HandleGameObject(GO_MoraggCellGUID, true);
                    pBoss = instance->GetCreature(NPC_MoraggGUID);
                    if (pBoss)
                        pBoss->GetMotionMaster()->MovePoint(0, BossStartMove1);
                    break;
                case BOSS_EREKEM:
                    HandleGameObject(GO_ErekemCellGUID, true);
                    HandleGameObject(GO_ErekemRightGuardCellGUID, true);
                    HandleGameObject(GO_ErekemLeftGuardCellGUID, true);
                    pBoss = instance->GetCreature(NPC_ErekemGUID);
                    if (pBoss)
                        pBoss->GetMotionMaster()->MovePoint(0, BossStartMove2);
                    if (Creature* pGuard1 = instance->GetCreature(NPC_ErekemGuardGUID[0]))
                    {
                        pGuard1->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
                        pGuard1->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC);
                        pGuard1->GetMotionMaster()->MovePoint(0, BossStartMove21);
                    }
                    if (Creature* pGuard2 = instance->GetCreature(NPC_ErekemGuardGUID[1]))
                    {
                        pGuard2->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
                        pGuard2->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC);
                        pGuard2->GetMotionMaster()->MovePoint(0, BossStartMove22);
                    }
                    break;
                case BOSS_ICHORON:
                    HandleGameObject(GO_IchoronCellGUID, true);
                    pBoss = instance->GetCreature(NPC_IchoronGUID);
                    if (pBoss)
                        pBoss->GetMotionMaster()->MovePoint(0, BossStartMove3);
                    break;
                case BOSS_LAVANTHOR:
                    HandleGameObject(GO_LavanthorCellGUID, true);
                    pBoss = instance->GetCreature(NPC_LavanthorGUID);
                    if (pBoss)
                        pBoss->GetMotionMaster()->MovePoint(0, BossStartMove4);
                    break;
                case BOSS_XEVOZZ:
                    HandleGameObject(GO_XevozzCellGUID, true);
                    pBoss = instance->GetCreature(NPC_XevozzGUID);
                    if (pBoss)
                        pBoss->GetMotionMaster()->MovePoint(0, BossStartMove5);
                    break;
                case BOSS_ZURAMAT:
                    HandleGameObject(GO_ZuramatCellGUID, true);
                    pBoss = instance->GetCreature(NPC_ZuramatGUID);
                    if (pBoss)
                        pBoss->GetMotionMaster()->MovePoint(0, BossStartMove6);
                    break;
            }

            if (pBoss)
            {
                pBoss->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
                pBoss->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC);
                pBoss->SetReactState(REACT_AGGRESSIVE);
                if (WaveCount == 6 && m_auiEncounter[0] == DONE || WaveCount == 12 && m_auiEncounter[1] == DONE)
                    pBoss->SetLootMode(0);
            }
        }

        void Update(uint32 diff)
        {
            events.Update(diff);
            switch( events.GetEvent() )
            {
                case 0:
                    break;
                case EVENT_CHECK_PLAYERS:
                    {
                        if( DoNeedCleanup(false) )
                            InstanceCleanup();
                        events.RepeatEvent(CLEANUP_CHECK_INTERVAL);
                    }
                    break;
                case EVENT_GUARDS_FALL_BACK:
                    {
                        for (uint8 i=0; i<4; ++i)
                            if (Creature* c = instance->GetCreature(NPC_GuardGUID[i]))
                            {
                                c->SetReactState(REACT_PASSIVE);
                                c->CombatStop();
                                c->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
                                c->GetMotionMaster()->MovePoint(0, guardMovePosition);
                            }
                        events.PopEvent();
                        events.RescheduleEvent(EVENT_GUARDS_DISAPPEAR, 5000);
                    }
                    break;
                case EVENT_GUARDS_DISAPPEAR:
                    {
                        for (uint8 i=0; i<4; ++i)
                            if (Creature* c = instance->GetCreature(NPC_GuardGUID[i]))
                                c->SetVisible(false);
                        events.PopEvent();
                        events.RescheduleEvent(EVENT_SINCLARI_FALL_BACK, 2000);
                    }
                    break;
                case EVENT_SINCLARI_FALL_BACK:
                    {
                        if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
                        {
                            c->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
                            c->GetMotionMaster()->MovePoint(0, sinclariOutsidePosition);
                        }
                        SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 0);
                        events.PopEvent();
                        events.RescheduleEvent(EVENT_START_ENCOUNTER, 4000);
                    }
                    break;
                case EVENT_START_ENCOUNTER:
                    {
                        if (Creature* c = instance->GetCreature(NPC_DoorSealGUID))
                            c->RemoveAllAuras(); // just to be sure...
                        GateHealth = 100;
                        HandleGameObject(GO_MainGateGUID, false);
                        DoUpdateWorldState(WORLD_STATE_VH_SHOW, 1);
                        DoUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, (uint32)GateHealth);
                        DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, (uint32)WaveCount);

                        for (std::vector<uint64>::iterator itr = GO_ActivationCrystalGUID.begin(); itr != GO_ActivationCrystalGUID.end(); ++itr)
                            if (GameObject* go = instance->GetGameObject(*itr))
                            {
                                HandleGameObject(0, false, go); // not used yet
                                go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); // make it useable
                            }
                        events.PopEvent();
                        events.RescheduleEvent(EVENT_SUMMON_PORTAL, 4000);
                    }
                    break;
                case EVENT_SUMMON_PORTAL:
                    ++WaveCount;
                    DoUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, (uint32)WaveCount);
                    SetData(DATA_PORTAL_LOCATION, (GetData(DATA_PORTAL_LOCATION) + urand(1, 5))%6);
                    if (Creature* c = instance->GetCreature(NPC_SinclariGUID))
                    {
                        if (WaveCount%6 != 0)
                            c->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocations[GetData(DATA_PORTAL_LOCATION)], TEMPSUMMON_CORPSE_DESPAWN);
                        else if (WaveCount == 6 || WaveCount == 12) // first or second boss
                        {
                            if (!uiFirstBoss || !uiSecondBoss)
                            {
                                uiFirstBoss = urand(1,6);
                                do { uiSecondBoss = urand(1,6); } while (uiFirstBoss==uiSecondBoss);
                                SaveToDB();
                            }
                            c->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN);
                        }
                        else // cyanigossa
                        {
                            if (Creature* cyanigosa = c->SummonCreature(NPC_CYANIGOSA, CyanigosasSpawnLocation, TEMPSUMMON_DEAD_DESPAWN))
                            {
                                cyanigosa->CastSpell(cyanigosa, SPELL_CYANIGOSA_BLUE_AURA, false);
                                cyanigosa->AI()->Talk(CYANIGOSA_SAY_SPAWN);
                                cyanigosa->GetMotionMaster()->MoveJump(MiddleRoomLocation.GetPositionX(), MiddleRoomLocation.GetPositionY(), MiddleRoomLocation.GetPositionZ(), 10.0f, 20.0f);
                            }
                            events.RescheduleEvent(EVENT_CYANIGOSSA_TRANSFORM, 10000);
                        }
                    }
                    events.PopEvent();
                    break;
                case EVENT_CYANIGOSSA_TRANSFORM:
                    if (Creature* c = instance->GetCreature(NPC_CyanigosaGUID))
                    {
                        c->RemoveAurasDueToSpell(SPELL_CYANIGOSA_BLUE_AURA);
                        c->CastSpell(c, SPELL_CYANIGOSA_TRANSFORM, 0);
                        events.RescheduleEvent(EVENT_CYANIGOSA_ATTACK, 2500);
                    }
                    events.PopEvent();
                    break;
                case EVENT_CYANIGOSA_ATTACK:
                    if (Creature* c = instance->GetCreature(NPC_CyanigosaGUID))
                        c->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC);
                    events.PopEvent();
                    break;
            }
        }

        void OnPlayerEnter(Player* plr)
        {
            if( DoNeedCleanup(plr->IsAlive()) )
                InstanceCleanup();

            if (EncounterStatus == IN_PROGRESS)
            {
                plr->SendUpdateWorldState(WORLD_STATE_VH_SHOW, 1);
                plr->SendUpdateWorldState(WORLD_STATE_VH_PRISON_STATE, (uint32)GateHealth);
                plr->SendUpdateWorldState(WORLD_STATE_VH_WAVE_COUNT, (uint32)WaveCount);
            }
            else
                plr->SendUpdateWorldState(WORLD_STATE_VH_SHOW, 0);

            events.RescheduleEvent(EVENT_CHECK_PLAYERS, CLEANUP_CHECK_INTERVAL);
        }

        bool DoNeedCleanup(bool enter)
        {
            uint8 aliveCount = 0;
            Map::PlayerList const &pl = instance->GetPlayers();
            for( Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr )
                if( Player* plr = itr->GetSource() )
                    if( plr->IsAlive() && !plr->IsGameMaster() && !plr->HasAura(27827)/*spirit of redemption aura*/ )
                        ++aliveCount;

            bool need = enter ? aliveCount<=1 : aliveCount==0;
            if( !need && CLEANED )
                CLEANED = false;
            return need;
        }

        void InstanceCleanup()
        {
            if( CLEANED )
                return;
            CLEANED = true;

            // reset defense crystals
            for (std::vector<uint64>::iterator itr = GO_ActivationCrystalGUID.begin(); itr != GO_ActivationCrystalGUID.end(); ++itr)
                if (GameObject* go = instance->GetGameObject(*itr))
                {
                    HandleGameObject(0, false, go); // not used yet
                    go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); // not useable at the beginning
                }

            // reset positions of Sinclari and Guards
            if (Creature* c = instance->GetCreature(NPC_SinclariGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); }
            for (uint8 i=0; i<4; ++i)
                if (Creature* c = instance->GetCreature(NPC_GuardGUID[i]))
                {
                    c->DespawnOrUnsummon();
                    c->SetRespawnTime(3);
                    if (m_auiEncounter[MAX_ENCOUNTER-1] == DONE)
                        c->SetVisible(false);
                    else
                        c->SetVisible(true);
                    c->SetReactState(REACT_AGGRESSIVE);
                }

            // remove portal if any
            if (Creature* c = instance->GetCreature(NPC_PortalGUID))
                c->DespawnOrUnsummon();
            NPC_PortalGUID = 0;

            // remove trash
            for (std::set<uint64>::iterator itr = trashMobs.begin(); itr != trashMobs.end(); ++itr)
                if (Creature* c = instance->GetCreature(*itr))
                    c->DespawnOrUnsummon();
            trashMobs.clear();

            // clear door seal damaging auras:
            if (Creature* c = instance->GetCreature(NPC_DoorSealGUID))
                c->RemoveAllAuras();

            // open main gate
            HandleGameObject(GO_MainGateGUID, true);
            
            if (m_auiEncounter[MAX_ENCOUNTER-1] != DONE) // instance not finished
            {
                // close all cells
                HandleGameObject(GO_MoraggCellGUID, false);
                HandleGameObject(GO_ErekemCellGUID, false);
                HandleGameObject(GO_ErekemRightGuardCellGUID, false);
                HandleGameObject(GO_ErekemLeftGuardCellGUID, false);
                HandleGameObject(GO_IchoronCellGUID, false);
                HandleGameObject(GO_LavanthorCellGUID, false);
                HandleGameObject(GO_XevozzCellGUID, false);
                HandleGameObject(GO_ZuramatCellGUID, false);

                // respawn bosses
                if (Creature* c = instance->GetCreature(NPC_MoraggGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); }
                if (Creature* c = instance->GetCreature(NPC_MoraggGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); }
                if (Creature* c = instance->GetCreature(NPC_ErekemGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); }
                if (Creature* c = instance->GetCreature(NPC_ErekemGuardGUID[0])) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); }
                if (Creature* c = instance->GetCreature(NPC_ErekemGuardGUID[1])) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); }
                if (Creature* c = instance->GetCreature(NPC_IchoronGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); }
                if (Creature* c = instance->GetCreature(NPC_LavanthorGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); }
                if (Creature* c = instance->GetCreature(NPC_XevozzGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); }
                if (Creature* c = instance->GetCreature(NPC_ZuramatGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_NPC); }
                if (Creature* c = instance->GetCreature(NPC_CyanigosaGUID)) { c->DespawnOrUnsummon(); }
            }

            // reinitialize variables and events
            DoUpdateWorldState(WORLD_STATE_VH_SHOW, 0);
            EncounterStatus = NOT_STARTED;
            GateHealth = 100;
            WaveCount = 0;
            bDefensesUsed = false;
            if (m_auiEncounter[MAX_ENCOUNTER-1] == DONE)
                EncounterStatus = DONE;
            events.Reset();
            events.RescheduleEvent(EVENT_CHECK_PLAYERS, CLEANUP_CHECK_INTERVAL);
        }

        bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* source, Unit const* target = NULL, uint32 miscvalue1 = 0)
        {
            switch(criteria_id)
            {
                case CRITERIA_DEFENSELESS:
                    return GateHealth == 100 && !bDefensesUsed;
                case CRITERIA_A_VOID_DANCE:
                case CRITERIA_DEHYDRATION:
                    return bAchiev;
            }
            return false;
        }

        std::string GetSaveData()
        {
            OUT_SAVE_INST_DATA;

            std::ostringstream saveStream;
            saveStream << "V H " << m_auiEncounter[0] << ' ' << m_auiEncounter[1] << ' ' << m_auiEncounter[2] << ' ' << uiFirstBoss << ' ' << uiSecondBoss;
            str_data = saveStream.str();

            OUT_SAVE_INST_DATA_COMPLETE;
            return str_data;
        }

        void Load(const char* in)
        {
            EncounterStatus = NOT_STARTED;
            CLEANED = false;
            events.Reset();
            events.RescheduleEvent(EVENT_CHECK_PLAYERS, 0);

            if (!in)
            {
                OUT_LOAD_INST_DATA_FAIL;
                return;
            }

            OUT_LOAD_INST_DATA(in);

            char dataHead1, dataHead2;
            uint32 data0, data1, data2, data3, data4;

            std::istringstream loadStream(in);
            loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3 >> data4;

            if (dataHead1 == 'V' && dataHead2 == 'H')
            {
                m_auiEncounter[0] = data0;
                m_auiEncounter[1] = data1;
                m_auiEncounter[2] = data2;
                uiFirstBoss = data3;
                uiSecondBoss = data4;

                for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
                    if (m_auiEncounter[i] == IN_PROGRESS)
                        m_auiEncounter[i] = NOT_STARTED;

                if (m_auiEncounter[MAX_ENCOUNTER-1] == DONE)
                    EncounterStatus = DONE;
            }
                else OUT_LOAD_INST_DATA_FAIL;

            OUT_LOAD_INST_DATA_COMPLETE;
        }
    };
};

void AddSC_instance_violet_hold()
{
    new instance_violet_hold();
}
