void instance_culling_of_stratholme::OnPlayerEnter(Player* pPlayer)
{
    if (instance->GetPlayersCountExceptGMs() == 0)
    {
        DoSpawnArthasIfNeeded();
        DoSpawnChromieIfNeeded();
        
        // Show World States if needed, TODO verify if needed and if this is the right way
        if (m_auiEncounter[TYPE_GRAIN_EVENT] == IN_PROGRESS || m_auiEncounter[TYPE_GRAIN_EVENT] == SPECIAL)
            DoUpdateWorldState(WORLD_STATE_CRATES, 1);      // Show Crates Counter
        else
            DoUpdateWorldState(WORLD_STATE_CRATES, 0);      // Remove Crates Counter
        
        if (m_auiEncounter[TYPE_MEATHOOK_EVENT] == IN_PROGRESS)
            DoUpdateWorldState(WORLD_STATE_WAVE, 1);        // Add WaveCounter
        else if (m_auiEncounter[TYPE_SALRAMM_EVENT] == IN_PROGRESS)
            DoUpdateWorldState(WORLD_STATE_WAVE, 6);        // Add WaveCounter
        else
            DoUpdateWorldState(WORLD_STATE_WAVE, 0);        // Remove WaveCounter

        if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME])
            DoUpdateWorldState(WORLD_STATE_TIME, 1);        // Show Timer
        else
            DoUpdateWorldState(WORLD_STATE_TIME, 0);        // Remove Timer
    }
}
void instance_culling_of_stratholme::OnPlayerEnter(Player* pPlayer)
{
    // spawn Chromie
    DoSpawnChromieIfNeeded(pPlayer);

    // spawn Arthas if intro is completed / passed
    if (GetData(TYPE_ARTHAS_INTRO_EVENT) == DONE)
    {
        DoSpawnArthasIfNeeded(pPlayer);

        // resume scourge waves if required - this can only happen in case of server reload
        if (GetData(TYPE_SALRAMM_EVENT) != DONE)
        {
            // this will restart the wave event with a delayed timer
            if (GetData(TYPE_MEATHOOK_EVENT) == DONE)
            {
                if (GetData(TYPE_SALRAMM_EVENT) != IN_PROGRESS)
                {
                    SetData(TYPE_SALRAMM_EVENT, IN_PROGRESS);
                    m_uiScourgeWaveTimer = 30000;
                    m_uiScourgeWaveCount = 5;
                }
            }
            else
            {
                if (GetData(TYPE_MEATHOOK_EVENT) != IN_PROGRESS)
                {
                    SetData(TYPE_MEATHOOK_EVENT, IN_PROGRESS);
                    m_uiScourgeWaveTimer = 30000;
                }
            }
        }
    }

    // Show World States if needed
    // Grain event world states
    if (GetData(TYPE_GRAIN_EVENT) == IN_PROGRESS || GetData(TYPE_GRAIN_EVENT) == SPECIAL)
        pPlayer->SendUpdateWorldState(WORLD_STATE_CRATES, 1);      // Show Crates Counter
    else
        pPlayer->SendUpdateWorldState(WORLD_STATE_CRATES, 0);      // Remove Crates Counter

    // Scourge waves
    if (GetData(TYPE_MEATHOOK_EVENT) == IN_PROGRESS || GetData(TYPE_SALRAMM_EVENT) == IN_PROGRESS)
        pPlayer->SendUpdateWorldState(WORLD_STATE_WAVE, m_uiScourgeWaveCount);      // Add WaveCounter
    else
        pPlayer->SendUpdateWorldState(WORLD_STATE_WAVE, 0);                         // Remove WaveCounter

    // Infinite corruptor
    if (GetData(TYPE_INFINITE_CORRUPTER_TIME))
    {
        DoSpawnCorruptorIfNeeded(pPlayer);

        pPlayer->SendUpdateWorldState(WORLD_STATE_TIME, 1);        // Show Timer
        pPlayer->SendUpdateWorldState(WORLD_STATE_TIME_COUNTER, GetData(TYPE_INFINITE_CORRUPTER_TIME) / (MINUTE * IN_MILLISECONDS));
    }
    else
        pPlayer->SendUpdateWorldState(WORLD_STATE_TIME, 0);        // Remove Timer
}
void instance_culling_of_stratholme::Update(uint32 uiDiff)
{
    // 25min Run - decrease time, update worldstate every ~20s
    // as the time is always saved by m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME], there is no need for an extra timer
    if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME])
    {
        if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] <= uiDiff)
            SetData(TYPE_INFINITE_CORRUPTER, FAIL);
        else
        {
            m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] -= uiDiff;
            if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME]/IN_MILLISECONDS % 20 == 0)
                SetData(TYPE_INFINITE_CORRUPTER_TIME, m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME]);
        }

        // This part is needed for a small "hurry up guys" note, TODO, verify 20min
        if (m_auiEncounter[TYPE_INFINITE_CORRUPTER] == IN_PROGRESS && m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] <= 24*MINUTE*IN_MILLISECONDS)
            SetData(TYPE_INFINITE_CORRUPTER, SPECIAL);
    }

    // Small Timer, to remove Grain-Crate WorldState and Spawn Second Chromie
    if (m_uiRemoveCrateStateTimer)
    {
        if (m_uiRemoveCrateStateTimer <= uiDiff)
        {
            DoUpdateWorldState(WORLD_STATE_CRATES, 0);
            DoSpawnChromieIfNeeded();
            m_uiRemoveCrateStateTimer = 0;
        }
        else
            m_uiRemoveCrateStateTimer -= uiDiff;
    }

    // Respawn Arthas after some time
    if (m_uiArthasRespawnTimer)
    {
        if (m_uiArthasRespawnTimer <= uiDiff)
        {
            DoSpawnArthasIfNeeded();
            m_uiArthasRespawnTimer = 0;
        }
        else
            m_uiArthasRespawnTimer -= uiDiff;
    }
}
void instance_culling_of_stratholme::Update(uint32 uiDiff)
{
    // 25min Run - decrease time, update worldstate every ~20s
    // as the time is always saved by m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME], there is no need for an extra timer
    if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME])
    {
        if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] <= uiDiff)
            SetData(TYPE_INFINITE_CORRUPTER, FAIL);
        else
        {
            m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] -= uiDiff;
            if (m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] / IN_MILLISECONDS % 20 == 0)
                SetData(TYPE_INFINITE_CORRUPTER_TIME, m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME]);
        }

        // This part is needed for a small "hurry up guys" note, TODO, verify 20min
        if (m_auiEncounter[TYPE_INFINITE_CORRUPTER] == IN_PROGRESS && m_auiEncounter[TYPE_INFINITE_CORRUPTER_TIME] <= 24 * MINUTE * IN_MILLISECONDS)
            SetData(TYPE_INFINITE_CORRUPTER, SPECIAL);
    }

    // Small Timer, to remove Grain-Crate WorldState and Spawn Second Chromie
    if (m_uiRemoveCrateStateTimer)
    {
        if (m_uiRemoveCrateStateTimer <= uiDiff)
        {
            if (Player* pPlayer = GetPlayerInMap())
                DoSpawnChromieIfNeeded(pPlayer);

            DoUpdateWorldState(WORLD_STATE_CRATES, 0);
            DoChromieWhisper(WHISPER_CHROMIE_CRATES);
            m_uiRemoveCrateStateTimer = 0;
        }
        else
            m_uiRemoveCrateStateTimer -= uiDiff;
    }

    // Respawn Arthas after some time
    if (m_uiArthasRespawnTimer)
    {
        if (m_uiArthasRespawnTimer <= uiDiff)
        {
            if (Player* pPlayer = GetPlayerInMap())
                DoSpawnArthasIfNeeded(pPlayer);

            m_uiArthasRespawnTimer = 0;
        }
        else
            m_uiArthasRespawnTimer -= uiDiff;
    }

    // Handle undead waves
    if (m_uiScourgeWaveTimer)
    {
        if (m_uiScourgeWaveTimer <= uiDiff)
        {
            if (GetData(TYPE_SALRAMM_EVENT) == DONE)
            {
                DoOrSimulateScriptTextForThisInstance(SAY_MEET_TOWN_HALL, NPC_ARTHAS);
                DoUpdateWorldState(WORLD_STATE_WAVE, 0);    // Remove WaveCounter

                // despawn and respawn Arthas in the new location
                if (Creature* pArthas = GetSingleCreatureFromStorage(NPC_ARTHAS))
                    pArthas->ForcedDespawn();
                if (Player* pPlayer = GetPlayerInMap())
                    DoSpawnArthasIfNeeded(pPlayer);
            }
            else
            {
                ++m_uiScourgeWaveCount;
                DoUpdateWorldState(WORLD_STATE_WAVE, m_uiScourgeWaveCount);
                DoSpawnNextScourgeWave();
            }

            m_uiScourgeWaveTimer = 0;
        }
        else
            m_uiScourgeWaveTimer -= uiDiff;
    }
}
void instance_culling_of_stratholme::SetData(uint32 uiType, uint32 uiData)
{
    switch (uiType)
    {
        case TYPE_GRAIN_EVENT:
            m_auiEncounter[uiType] = uiData;
            if (uiData == SPECIAL)
                DoUpdateWorldState(WORLD_STATE_CRATES, 1);
            else if (uiData == IN_PROGRESS)
            {
                // safety check
                if (m_uiGrainCrateCount >= MAX_GRAIN_CRATES)
                    return;

                ++m_uiGrainCrateCount;
                DoUpdateWorldState(WORLD_STATE_CRATES_COUNT, m_uiGrainCrateCount);

                if (m_uiGrainCrateCount == MAX_GRAIN_CRATES)
                {
                    m_uiRemoveCrateStateTimer = 20000;
                    SetData(TYPE_GRAIN_EVENT, DONE);
                }
            }
            break;
        case TYPE_ARTHAS_INTRO_EVENT:
            m_auiEncounter[uiType] = uiData;
            if (uiData == DONE)
            {
                m_uiScourgeWaveCount = 0;
                m_uiScourgeWaveTimer = 1000;
                DoUpdateZombieResidents();

                SetData(TYPE_MEATHOOK_EVENT, IN_PROGRESS);
            }
            break;
        case TYPE_MEATHOOK_EVENT:
            m_auiEncounter[uiType] = uiData;
            if (uiData == DONE)
            {
                m_uiScourgeWaveTimer = 20000;
                SetData(TYPE_SALRAMM_EVENT, IN_PROGRESS);
            }
            break;
        case TYPE_SALRAMM_EVENT:
            m_auiEncounter[uiType] = uiData;
            if (uiData == DONE)
                m_uiScourgeWaveTimer = 5000;
            break;
        case TYPE_ARTHAS_TOWNHALL_EVENT:
            m_auiEncounter[uiType] = uiData;
            if (uiData == DONE)
            {
                // despawn arthas and spawn him in the next point
                if (Creature* pArthas = GetSingleCreatureFromStorage(NPC_ARTHAS))
                    pArthas->ForcedDespawn();

                if (Player* pPlayer = GetPlayerInMap())
                    DoSpawnArthasIfNeeded(pPlayer);
            }
            break;
        case TYPE_EPOCH_EVENT:
            m_auiEncounter[uiType] = uiData;
            break;
        case TYPE_ARTHAS_ESCORT_EVENT:
            // use fail in order to respawn Arthas
            if (uiData == FAIL)
            {
                m_uiArthasRespawnTimer = 10000;

                // despawn the bosses if Arthas dies in order to avoid exploits
                if (Creature* pEpoch = GetSingleCreatureFromStorage(NPC_LORD_EPOCH, true))
                    pEpoch->ForcedDespawn();
                if (Creature* pMalganis = GetSingleCreatureFromStorage(NPC_MALGANIS, true))
                    pMalganis->ForcedDespawn();
            }
            else
                m_auiEncounter[uiType] = uiData;
            break;
        case TYPE_MALGANIS_EVENT:
            m_auiEncounter[uiType] = uiData;
            if (uiData == DONE)
            {
                DoUseDoorOrButton(GO_CITY_ENTRANCE_GATE);
                DoToggleGameObjectFlags(instance->IsRegularDifficulty() ? GO_DARK_RUNED_CHEST : GO_DARK_RUNED_CHEST_H, GO_FLAG_NO_INTERACT, false);
                DoRespawnGameObject(instance->IsRegularDifficulty() ? GO_DARK_RUNED_CHEST : GO_DARK_RUNED_CHEST_H, 30 * MINUTE);

                if (Player* pPlayer = GetPlayerInMap())
                    DoSpawnChromieIfNeeded(pPlayer);
            }
            break;
        case TYPE_INFINITE_CORRUPTER_TIME:
            m_auiEncounter[uiType] = uiData;
            if (!uiData)
            {
                DoUpdateWorldState(WORLD_STATE_TIME, 0);    // Remove Timer
                DoUpdateWorldState(WORLD_STATE_TIME_COUNTER, 0);
            }
            else
                DoUpdateWorldState(WORLD_STATE_TIME_COUNTER, uiData / (MINUTE * IN_MILLISECONDS));
            break;
        case TYPE_INFINITE_CORRUPTER:
            m_auiEncounter[uiType] = uiData;
            switch (uiData)
            {
                case IN_PROGRESS:
                    if (!GetData(TYPE_INFINITE_CORRUPTER_TIME))
                    {
                        SetData(TYPE_INFINITE_CORRUPTER_TIME, MINUTE * 25 * IN_MILLISECONDS);
                        DoUpdateWorldState(WORLD_STATE_TIME, 1);
                        DoChromieWhisper(WHISPER_CHROMIE_GUARDIAN);

                        // spawn the corruptor for the first time
                        if (Creature* pArthas = GetSingleCreatureFromStorage(NPC_ARTHAS))
                            DoSpawnCorruptorIfNeeded(pArthas);
                    }
                    break;
                case DONE:
                    // event completed - epilog handled by dbscript
                    SetData(TYPE_INFINITE_CORRUPTER_TIME, 0);
                    break;
                case SPECIAL:
                    DoChromieWhisper(WHISPER_CHROMIE_HURRY);
                    break;
                case FAIL:
                    // event failed - despawn the corruptor
                    SetData(TYPE_INFINITE_CORRUPTER_TIME, 0);
                    if (Creature* pCorrupter = GetSingleCreatureFromStorage(NPC_INFINITE_CORRUPTER))
                    {
                        DoOrSimulateScriptTextForThisInstance(SAY_CORRUPTOR_DESPAWN, NPC_INFINITE_CORRUPTER);

                        if (pCorrupter->isAlive())
                            pCorrupter->ForcedDespawn();
                    }
                    break;
            }
            break;
    }

    if (uiData == DONE || uiType == TYPE_INFINITE_CORRUPTER_TIME)
    {
        OUT_SAVE_INST_DATA;

        std::ostringstream saveStream;
        saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " "
                   << m_auiEncounter[3] << " " << m_auiEncounter[4] << " " << m_auiEncounter[5] << " "
                   << m_auiEncounter[6] << " " << m_auiEncounter[7] << " " << m_auiEncounter[8] << " "
                   << m_auiEncounter[9];

        m_strInstData = saveStream.str();

        SaveToDB();
        OUT_SAVE_INST_DATA_COMPLETE;
    }
}