EverWrath/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
Walter Pagani 3b45e90e41
fix(Scripts/ICC): NPC Muradin Dialogues (#18931)
Co-authored-by: Meji <alvaro.megias@outlook.com>
Co-authored-by: 天鹿 <18535853+PkllonG@users.noreply.github.com>
2024-08-31 17:34:56 -03:00

2034 lines
94 KiB
C++

/*
* 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/>.
*/
#include "AccountMgr.h"
#include "CreatureTextMgr.h"
#include "Group.h"
#include "InstanceMapScript.h"
#include "InstanceScript.h"
#include "Map.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "Transport.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "icecrown_citadel.h"
enum EventIds
{
EVENT_PLAYERS_GUNSHIP_SPAWN = 22663,
EVENT_PLAYERS_GUNSHIP_COMBAT = 22664,
EVENT_PLAYERS_GUNSHIP_SAURFANG = 22665,
EVENT_ENEMY_GUNSHIP_COMBAT = 22860,
EVENT_ENEMY_GUNSHIP_DESPAWN = 22861,
EVENT_QUAKE = 23437,
EVENT_SECOND_REMORSELESS_WINTER = 23507,
EVENT_TELEPORT_TO_FROSMOURNE = 23617,
EVENT_FESTERGUT_VALVE_USED = 23438,
EVENT_ROTFACE_VALVE_USED = 23426,
};
enum TimedEvents
{
EVENT_UPDATE_EXECUTION_TIME = 1,
EVENT_QUAKE_SHATTER = 2,
EVENT_REBUILD_PLATFORM = 3,
EVENT_RESPAWN_GUNSHIP = 4,
EVENT_RESPAWN_SINDRAGOSA = 5
};
enum Spells
{
SPELL_GAS_VARIABLE = 74119,
SPELL_OOZE_VARIABLE = 74118,
BLOOD_BEAM_VISUAL_RHAND = 72304,
BLOOD_BEAM_VISUAL_LHAND = 72303,
BLOOD_BEAM_VISUAL_LLEG = 72302,
BLOOD_BEAM_VISUAL_RLEG = 72301,
VOID_ZONE_VISUAL = 69422
};
enum Say
{
SAY_SOULS_LICH_KING_RAND_WHISPER = 5
};
BossBoundaryData const boundaries =
{
{ DATA_LORD_MARROWGAR, new CircleBoundary(Position(-428.0f,2211.0f), 95.0) },
{ DATA_LORD_MARROWGAR, new RectangleBoundary(-430.0f, -330.0f, 2110.0f, 2310.0f) },
{ DATA_LADY_DEATHWHISPER, new RectangleBoundary(-670.0f, -520.0f, 2145.0f, 2280.0f) },
{ DATA_DEATHBRINGER_SAURFANG, new RectangleBoundary(-565.0f, -465.0f, 2160.0f, 2260.0f) },
{ DATA_ROTFACE, new RectangleBoundary(4385.0f, 4505.0f, 3082.0f, 3195.0f) },
{ DATA_FESTERGUT, new RectangleBoundary(4205.0f, 4325.0f, 3082.0f, 3195.0f) },
{ DATA_PROFESSOR_PUTRICIDE, new ParallelogramBoundary(Position(4356.0f, 3290.0f), Position(4435.0f, 3194.0f), Position(4280.0f, 3194.0f)) },
{ DATA_PROFESSOR_PUTRICIDE, new RectangleBoundary(4280.0f, 4435.0f, 3150.0f, 4360.0f) },
{ DATA_BLOOD_PRINCE_COUNCIL, new EllipseBoundary(Position(4660.95f, 2769.194f), 85.0, 60.0) },
{ DATA_BLOOD_QUEEN_LANA_THEL, new CircleBoundary(Position(4595.93f, 2769.365f), 64.0) },
{ DATA_SISTER_SVALNA, new RectangleBoundary(4291.0f, 4423.0f, 2438.0f, 2653.0f) },
{ DATA_VALITHRIA_DREAMWALKER, new RectangleBoundary(4112.5f, 4293.5f, 2385.0f, 2585.0f) },
{ DATA_SINDRAGOSA, new EllipseBoundary(Position(4418.6f, 2484.0f), 110.0, 75.0) }
};
DoorData const doorData[] =
{
{GO_LORD_MARROWGAR_S_ENTRANCE, DATA_LORD_MARROWGAR, DOOR_TYPE_ROOM },
{GO_SCOURGE_TRANSPORTER_FIRST, DATA_LORD_MARROWGAR, DOOR_TYPE_PASSAGE },
{GO_ICEWALL, DATA_LORD_MARROWGAR, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ICECROWN_ICEWALL02, DATA_LORD_MARROWGAR, DOOR_TYPE_PASSAGE },
{GO_ORATORY_OF_THE_DAMNED_ENTRANCE, DATA_LADY_DEATHWHISPER, DOOR_TYPE_ROOM },
{GO_SAURFANG_S_DOOR, DATA_DEATHBRINGER_SAURFANG, DOOR_TYPE_PASSAGE },
{GO_ORANGE_PLAGUE_MONSTER_ENTRANCE, DATA_FESTERGUT, DOOR_TYPE_ROOM },
{GO_GREEN_PLAGUE_MONSTER_ENTRANCE, DATA_ROTFACE, DOOR_TYPE_ROOM },
{GO_CRIMSON_HALL_DOOR, DATA_BLOOD_PRINCE_COUNCIL, DOOR_TYPE_ROOM },
{GO_CRIMSON_HALL_DOOR, DATA_BLOOD_PRINCE_TRASH, DOOR_TYPE_PASSAGE },
{GO_BLOOD_ELF_COUNCIL_DOOR, DATA_BLOOD_PRINCE_COUNCIL, DOOR_TYPE_PASSAGE },
{GO_BLOOD_ELF_COUNCIL_DOOR_RIGHT, DATA_BLOOD_PRINCE_COUNCIL, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ICECROWN_BLOODPRINCE_DOOR_01, DATA_BLOOD_QUEEN_LANA_THEL, DOOR_TYPE_ROOM },
{GO_DOODAD_ICECROWN_GRATE_01, DATA_BLOOD_QUEEN_LANA_THEL, DOOR_TYPE_PASSAGE },
{GO_GREEN_DRAGON_BOSS_ENTRANCE, DATA_SISTER_SVALNA, DOOR_TYPE_PASSAGE },
{GO_GREEN_DRAGON_BOSS_ENTRANCE, DATA_VALITHRIA_DREAMWALKER, DOOR_TYPE_ROOM },
{GO_GREEN_DRAGON_BOSS_EXIT, DATA_VALITHRIA_DREAMWALKER, DOOR_TYPE_PASSAGE },
{GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_01, DATA_VALITHRIA_DREAMWALKER, DOOR_TYPE_SPAWN_HOLE },
{GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_02, DATA_VALITHRIA_DREAMWALKER, DOOR_TYPE_SPAWN_HOLE },
{GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_03, DATA_VALITHRIA_DREAMWALKER, DOOR_TYPE_SPAWN_HOLE },
{GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_04, DATA_VALITHRIA_DREAMWALKER, DOOR_TYPE_SPAWN_HOLE },
{GO_SINDRAGOSA_ENTRANCE_DOOR, DATA_SINDRAGOSA, DOOR_TYPE_ROOM },
{GO_SINDRAGOSA_ENTRANCE_DOOR, DATA_SINDRAGOSA_GAUNTLET, DOOR_TYPE_PASSAGE },
{GO_SINDRAGOSA_SHORTCUT_ENTRANCE_DOOR, DATA_SINDRAGOSA, DOOR_TYPE_PASSAGE },
{GO_SINDRAGOSA_SHORTCUT_EXIT_DOOR, DATA_SINDRAGOSA, DOOR_TYPE_PASSAGE },
{GO_ICE_WALL, DATA_SINDRAGOSA, DOOR_TYPE_ROOM },
{GO_ICE_WALL, DATA_SINDRAGOSA, DOOR_TYPE_ROOM },
{0, 0, DOOR_TYPE_ROOM }
};
ObjectData const creatureData[] =
{
{ NPC_SINDRAGOSA, DATA_SINDRAGOSA },
{ NPC_THE_SKYBREAKER, DATA_THE_SKYBREAKER },
{ NPC_ORGRIMS_HAMMER, DATA_ORGRIMS_HAMMER },
{ 0, 0 }
};
// this doesnt have to only store questgivers, also can be used for related quest spawns
struct WeeklyQuest
{
uint32 creatureEntry;
uint32 questId[2]; // 10 and 25 man versions
};
// when changing the content, remember to update SetData, DATA_BLOOD_QUICKENING_STATE case for NPC_ALRIN_THE_AGILE index
WeeklyQuest const WeeklyQuestData[WeeklyNPCs] =
{
{NPC_INFILTRATOR_MINCHAR, {QUEST_DEPROGRAMMING_10, QUEST_DEPROGRAMMING_25 }}, // Deprogramming
{NPC_KOR_KRON_LIEUTENANT, {QUEST_SECURING_THE_RAMPARTS_10, QUEST_SECURING_THE_RAMPARTS_25 }}, // Securing the Ramparts
{NPC_ROTTING_FROST_GIANT_10, {QUEST_SECURING_THE_RAMPARTS_10, QUEST_SECURING_THE_RAMPARTS_25 }}, // Securing the Ramparts
{NPC_ROTTING_FROST_GIANT_25, {QUEST_SECURING_THE_RAMPARTS_10, QUEST_SECURING_THE_RAMPARTS_25 }}, // Securing the Ramparts
{NPC_ALCHEMIST_ADRIANNA, {QUEST_RESIDUE_RENDEZVOUS_10, QUEST_RESIDUE_RENDEZVOUS_25 }}, // Residue Rendezvous
{NPC_ALRIN_THE_AGILE, {QUEST_BLOOD_QUICKENING_10, QUEST_BLOOD_QUICKENING_25 }}, // Blood Quickening
{NPC_INFILTRATOR_MINCHAR_BQ, {QUEST_BLOOD_QUICKENING_10, QUEST_BLOOD_QUICKENING_25 }}, // Blood Quickening
{NPC_MINCHAR_BEAM_STALKER, {QUEST_BLOOD_QUICKENING_10, QUEST_BLOOD_QUICKENING_25 }}, // Blood Quickening
{NPC_VALITHRIA_DREAMWALKER_QUEST, {QUEST_RESPITE_FOR_A_TORMENTED_SOUL_10, QUEST_RESPITE_FOR_A_TORMENTED_SOUL_25 }}, // Respite for a Tormented Soul
};
Position const JainaSpawnPos = { -48.65278f, 2211.026f, 27.98586f, 3.124139f };
Position const MuradinSpawnPos = { -47.34549f, 2208.087f, 27.98586f, 3.106686f };
Position const UtherSpawnPos = { -26.58507f, 2211.524f, 30.19898f, 3.124139f };
Position const SylvanasSpawnPos = { -41.45833f, 2222.891f, 27.98586f, 3.647738f };
Position const SindragosaSpawnPos = { 4818.6997f, 2483.7102f, 287.06497f, 3.286661f };
// Set position traps Spirit Alarm
std::vector<Position> GoSpiritAlarm_1 = { { -160.96f, 2210.46f, 35.24f, 0.0f }, { -176.27f, 2201.93f, 35.24f, 0.0f}, { -207.83f, 2207.38f, 35.24f, 0.0f } };
std::vector<Position> GoSpiritAlarm_2 = { { -178.41f, 2225.11f, 35.24f, 0.0f }, { -195.23f, 2221.55f, 35.24f, 0.0f}, { -209.94f, 2250.34f, 37.99f, 0.0f } };
std::vector<Position> GoSpiritAlarm_3 = { { -289.80f, 2216.60f, 42.39f, 0.0f }, { -317.76f, 2216.11f, 42.57f, 0.0f}, { -301.07f, 2216.62f, 42.0f, 0.0f } };
std::vector<Position> GoSpiritAlarm_4 = { { -276.07f, 2206.76f, 42.57f, 0.0f }, { -304.44f, 2199.11f, 41.99f, 0.0f}, { -292.82f, 2204.61f, 42.02f, 0.0f } };
class RespawnEvent : public BasicEvent
{
public:
RespawnEvent(Creature& owner) : _owner(owner) { }
bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override
{
_owner.RemoveCorpse(false);
_owner.Respawn();
return true;
}
private:
Creature& _owner;
};
class DelayedCastMincharEvent : public BasicEvent
{
public:
DelayedCastMincharEvent(Creature* trigger, uint32 spellId) : _trigger(trigger), _spellId(spellId) {}
bool Execute(uint64 /*time*/, uint32 /*diff*/) override
{
if (Creature* minchar = _trigger->FindNearestCreature(NPC_INFILTRATOR_MINCHAR_BQ, 50.0f, true))
_trigger->CastSpell(minchar, _spellId, true);
return true;
}
private:
Creature* _trigger;
uint32 _spellId;
};
class instance_icecrown_citadel : public InstanceMapScript
{
public:
instance_icecrown_citadel() : InstanceMapScript(ICCScriptName, 631) { }
struct instance_icecrown_citadel_InstanceMapScript : public InstanceScript
{
instance_icecrown_citadel_InstanceMapScript(InstanceMap* map) : InstanceScript(map)
{
// pussywizard:
IsBuffAvailable = true;
WeeklyQuestId10 = 0;
PutricideEventProgress = 0;
LichKingHeroicAvailable = true;
LichKingRandomWhisperTimer = 120 * IN_MILLISECONDS;
DarkwhisperElevatorTimer = 3000;
SetHeaders(DataHeader);
SetBossNumber(MAX_ENCOUNTERS);
SetPersistentDataCount(MAX_DATA_INDEXES);
LoadBossBoundaries(boundaries);
LoadObjectData(creatureData, nullptr);
LoadDoorData(doorData);
TeamIdInInstance = TEAM_NEUTRAL;
HeroicAttempts = MaxHeroicAttempts;
IsBonedEligible = true;
IsOozeDanceEligible = true;
IsNauseaEligible = true;
IsOrbWhispererEligible = true;
ColdflameJetsState = NOT_STARTED;
BloodQuickeningState = NOT_STARTED;
BloodQuickeningMinutes = 0;
BloodPrinceTrashCount = 0;
IsSindragosaIntroDone = false;
}
void FillInitialWorldStates(WorldPacket& data) override
{
if (instance->IsHeroic())
{
data << uint32(WORLDSTATE_SHOW_TIMER) << uint32(BloodQuickeningState == IN_PROGRESS);
data << uint32(WORLDSTATE_EXECUTION_TIME) << uint32(BloodQuickeningMinutes);
data << uint32(WORLDSTATE_SHOW_ATTEMPTS) << uint32(1);
data << uint32(WORLDSTATE_ATTEMPTS_REMAINING) << uint32(HeroicAttempts);
data << uint32(WORLDSTATE_ATTEMPTS_MAX) << uint32(MaxHeroicAttempts);
}
}
void OnPlayerAreaUpdate(Player* player, uint32 /*oldArea*/, uint32 newArea) override
{
if (newArea == 4890 /*Putricide's Laboratory of Alchemical Horrors and Fun*/ ||
newArea == 4891 /*The Sanctum of Blood*/ ||
newArea == 4889 /*The Frost Queen's Lair*/ ||
newArea == 4859 /*The Frozen Throne*/ ||
newArea == 4910 /*Frostmourne*/)
{
player->SendInitWorldStates(player->GetZoneId(), player->GetAreaId());
}
else
{
player->SendUpdateWorldState(WORLDSTATE_SHOW_ATTEMPTS, 0);
}
}
void OnPlayerEnter(Player* player) override
{
if (TeamIdInInstance == TEAM_NEUTRAL)
TeamIdInInstance = player->GetTeamId();
// for professor putricide hc
DoRemoveAurasDueToSpellOnPlayers(SPELL_GAS_VARIABLE);
DoRemoveAurasDueToSpellOnPlayers(SPELL_OOZE_VARIABLE);
if (GetBossState(DATA_LADY_DEATHWHISPER) == DONE && GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != DONE)
SpawnGunship();
if (GetBossState(DATA_SINDRAGOSA) != DONE && IsSindragosaIntroDone && !GetCreature(DATA_SINDRAGOSA) && Events.GetTimeUntilEvent(EVENT_RESPAWN_SINDRAGOSA) == Milliseconds::max())
{
Events.ScheduleEvent(EVENT_RESPAWN_SINDRAGOSA, 30s);
}
if (IsBuffAvailable)
{
SpellAreaForAreaMapBounds saBounds = sSpellMgr->GetSpellAreaForAreaMapBounds(4812);
for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
if ((itr->second->raceMask & player->getRaceMask()) && !player->HasAura(itr->second->spellId))
{
if (SpellInfo const* si = sSpellMgr->GetSpellInfo(itr->second->spellId))
{
if (si->HasAura(SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT))
{
DoCastSpellOnPlayer(player, itr->second->spellId, false, false);
}
}
}
}
}
void OnCreatureCreate(Creature* creature) override
{
if (TeamIdInInstance == TEAM_NEUTRAL)
{
Map::PlayerList const& players = instance->GetPlayers();
if (!players.IsEmpty())
if (Player* player = players.begin()->GetSource())
TeamIdInInstance = player->GetTeamId();
}
// apply ICC buff to pets/summons
if (GetData(DATA_BUFF_AVAILABLE) && creature->GetOwnerGUID().IsPlayer() && creature->HasUnitTypeMask(UNIT_MASK_MINION | UNIT_MASK_GUARDIAN | UNIT_MASK_CONTROLABLE_GUARDIAN) && creature->CanHaveThreatList())
if (Unit* owner = creature->GetOwner())
if (Player* plr = owner->ToPlayer())
{
SpellAreaForAreaMapBounds saBounds = sSpellMgr->GetSpellAreaForAreaMapBounds(4812);
for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
if ((itr->second->raceMask & plr->getRaceMask()) && !creature->HasAura(itr->second->spellId))
if (SpellInfo const* si = sSpellMgr->GetSpellInfo(itr->second->spellId))
if (si->HasAura(SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT))
creature->AddAura(itr->second->spellId, creature);
}
// fighting npcs in Rampart of Skulls
std::string name1("Skybreaker ");
std::string name2("Kor'kron ");
if (!creature->GetTransport() && creature->GetPositionZ() <= 205.0f && creature->GetExactDist2d(-439.0f, 2210.0f) <= 150.0f && (creature->GetEntry() == 37544 || creature->GetEntry() == 37545 || creature->GetName().compare(0, name1.length(), name1) == 0 || creature->GetName().compare(0, name2.length(), name2) == 0))
creature->AddToNotify(NOTIFY_AI_RELOCATION);
// pussywizard: check weekly here, before possible UpdateEntry
// allow creating all of them, because after killing Marrowgar some have to appear, so just hide them
switch (creature->GetEntry())
{
case NPC_INFILTRATOR_MINCHAR:
case NPC_KOR_KRON_LIEUTENANT:
case NPC_ALCHEMIST_ADRIANNA:
case NPC_ALRIN_THE_AGILE:
case NPC_INFILTRATOR_MINCHAR_BQ:
case NPC_MINCHAR_BEAM_STALKER:
case NPC_VALITHRIA_DREAMWALKER_QUEST:
for (uint8 i = 0; i < WeeklyNPCs; ++i)
if (WeeklyQuestData[i].creatureEntry == creature->GetEntry())
{
WeeklyQuestNpcGUID[i] = creature->GetGUID();
if (WeeklyQuestId10 != WeeklyQuestData[i].questId[0])
creature->SetVisible(false);
else if (WeeklyQuestData[i].creatureEntry == NPC_VALITHRIA_DREAMWALKER_QUEST && GetBossState(DATA_VALITHRIA_DREAMWALKER) != DONE)
creature->SetVisible(false);
}
break;
}
switch (creature->GetEntry())
{
case NPC_KOR_KRON_GENERAL:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_ALLIANCE_COMMANDER);
break;
case NPC_KOR_KRON_LIEUTENANT:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_SKYBREAKER_LIEUTENANT);
break;
case NPC_TORTUNOK:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_ALANA_MOONSTRIKE);
break;
case NPC_GERARDO_THE_SUAVE:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_TALAN_MOONSTRIKE);
break;
case NPC_UVLUS_BANEFIRE:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_MALFUS_GRIMFROST);
break;
case NPC_IKFIRUS_THE_VILE:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_YILI);
break;
case NPC_VOL_GUK:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_JEDEBIA);
break;
case NPC_HARAGG_THE_UNSEEN:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_NIBY_THE_ALMIGHTY);
break;
case NPC_GARROSH_HELLSCREAM:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_KING_VARIAN_WRYNN);
// Xinef: summon in case of instance unload
if (GetBossState(DATA_THE_LICH_KING) == DONE)
{
instance->SummonCreature(NPC_LADY_JAINA_PROUDMOORE_QUEST, JainaSpawnPos);
instance->SummonCreature(NPC_MURADIN_BRONZEBEARD_QUEST, MuradinSpawnPos);
instance->SummonCreature(NPC_UTHER_THE_LIGHTBRINGER_QUEST, UtherSpawnPos);
instance->SummonCreature(NPC_LADY_SYLVANAS_WINDRUNNER_QUEST, SylvanasSpawnPos);
}
break;
case NPC_LADY_DEATHWHISPER:
LadyDeathwhisperGUID = creature->GetGUID();
break;
case NPC_DEATHBRINGER_SAURFANG:
DeathbringerSaurfangGUID = creature->GetGUID();
break;
case NPC_SE_HIGH_OVERLORD_SAURFANG:
if (TeamIdInInstance == TEAM_ALLIANCE)
{
creature->UpdateEntry(NPC_SE_MURADIN_BRONZEBEARD, true);
creature->LoadEquipment();
}
DeathbringerSaurfangEventGUID = creature->GetGUID();
creature->LastUsedScriptID = creature->GetScriptId();
break;
case NPC_SE_MURADIN_BRONZEBEARD:
DeathbringerSaurfangEventGUID = creature->GetGUID();
break;
case NPC_HIGH_OVERLORD_SAURFANG_DUMMY:
if (TeamIdInInstance == TEAM_ALLIANCE)
{
creature->UpdateEntry(NPC_MURADIN_BRONZEBEARD_DUMMY, creature->GetCreatureData());
creature->LoadEquipment();
}
break;
case NPC_SE_KOR_KRON_REAVER:
if (TeamIdInInstance == TEAM_ALLIANCE)
creature->UpdateEntry(NPC_SE_SKYBREAKER_MARINE);
break;
case NPC_FESTERGUT:
FestergutGUID = creature->GetGUID();
break;
case NPC_ROTFACE:
RotfaceGUID = creature->GetGUID();
break;
case NPC_PROFESSOR_PUTRICIDE:
ProfessorPutricideGUID = creature->GetGUID();
if (GetBossState(DATA_ROTFACE) == DONE && GetBossState(DATA_FESTERGUT) == DONE && !HeroicAttempts && GetData(DATA_HAS_LIMITED_ATTEMPTS) && creature->IsAlive())
creature->SetVisible(false);
break;
case NPC_PRINCE_KELESETH:
BloodCouncilGUIDs[0] = creature->GetGUID();
break;
case NPC_PRINCE_TALDARAM:
BloodCouncilGUIDs[1] = creature->GetGUID();
break;
case NPC_PRINCE_VALANAR:
BloodCouncilGUIDs[2] = creature->GetGUID();
break;
case NPC_BLOOD_ORB_CONTROLLER:
BloodCouncilControllerGUID = creature->GetGUID();
break;
case NPC_BLOOD_QUEEN_LANA_THEL:
BloodQueenLanaThelGUID = creature->GetGUID();
if (!HeroicAttempts && GetData(DATA_HAS_LIMITED_ATTEMPTS) && creature->IsAlive())
creature->SetVisible(false);
break;
case NPC_CROK_SCOURGEBANE:
CrokScourgebaneGUID = creature->GetGUID();
break;
// we can only do this because there are no gaps in their entries
case NPC_CAPTAIN_ARNATH:
case NPC_CAPTAIN_BRANDON:
case NPC_CAPTAIN_GRONDEL:
case NPC_CAPTAIN_RUPERT:
CrokCaptainGUIDs[creature->GetEntry() - NPC_CAPTAIN_ARNATH] = creature->GetGUID();
break;
case NPC_SISTER_SVALNA:
SisterSvalnaGUID = creature->GetGUID();
break;
case NPC_VALITHRIA_DREAMWALKER:
ValithriaDreamwalkerGUID = creature->GetGUID();
break;
case NPC_THE_LICH_KING_VALITHRIA:
ValithriaLichKingGUID = creature->GetGUID();
break;
case NPC_THE_LICH_KING_LH:
TheLichKingLhGUID = creature->GetGUID();
break;
case NPC_GREEN_DRAGON_COMBAT_TRIGGER:
ValithriaTriggerGUID = creature->GetGUID();
break;
case NPC_PUTRICADES_TRAP:
PutricadeTrapGUID = creature->GetGUID();
break;
case NPC_SINDRAGOSA_GAUNTLET:
SindragosaGauntletGUID = creature->GetGUID();
break;
case NPC_SINDRAGOSA:
SindragosaGUID = creature->GetGUID();
if (!HeroicAttempts && GetData(DATA_HAS_LIMITED_ATTEMPTS) && creature->IsAlive())
creature->SetVisible(false);
break;
case NPC_SPINESTALKER:
SpinestalkerGUID = creature->GetGUID();
break;
case NPC_RIMEFANG:
RimefangGUID = creature->GetGUID();
break;
case NPC_INVISIBLE_STALKER:
// Teleporter visual at center
if (creature->GetExactDist2d(4357.052f, 2769.421f) < 10.0f && GetBossState(DATA_PROFESSOR_PUTRICIDE) == DONE && GetBossState(DATA_BLOOD_QUEEN_LANA_THEL) == DONE && GetBossState(DATA_SINDRAGOSA) == DONE)
creature->CastSpell(creature, SPELL_ARTHAS_TELEPORTER_CEREMONY, false);
break;
case NPC_THE_LICH_KING:
TheLichKingGUID = creature->GetGUID();
if (!HeroicAttempts && GetData(DATA_HAS_LIMITED_ATTEMPTS) && creature->IsAlive())
creature->SetVisible(false);
break;
case NPC_HIGHLORD_TIRION_FORDRING_LK:
HighlordTirionFordringGUID = creature->GetGUID();
break;
case NPC_TERENAS_MENETHIL_FROSTMOURNE:
case NPC_TERENAS_MENETHIL_FROSTMOURNE_H:
TerenasMenethilGUID = creature->GetGUID();
break;
case NPC_INFILTRATOR_MINCHAR_BQ:
if (BloodQuickeningState == DONE)
creature->DespawnOrUnsummon(1);
break;
case NPC_MINCHAR_BEAM_STALKER:
if (BloodQuickeningState != DONE)
{
uint32 spellId = 0;
if (creature->GetPositionY() > 2790.0f && creature->GetPositionZ() > 420.0f)
spellId = BLOOD_BEAM_VISUAL_RHAND;
else if (creature->GetPositionY() < 2790.0f && creature->GetPositionZ() > 420.0f)
spellId = BLOOD_BEAM_VISUAL_LHAND;
else if (creature->GetPositionY() < 2790.0f && creature->GetPositionZ() < 420.0f)
spellId = BLOOD_BEAM_VISUAL_LLEG;
else
spellId = BLOOD_BEAM_VISUAL_RLEG;
creature->m_Events.AddEvent(new DelayedCastMincharEvent(creature, spellId), creature->m_Events.CalculateTime(1000));
}
break;
case NPC_SKYBREAKER_DECKHAND:
case NPC_ORGRIMS_HAMMER_CREW:
if (!creature->IsAlive())
creature->Respawn();
break;
default:
break;
}
InstanceScript::OnCreatureCreate(creature);
}
void OnCreatureRemove(Creature* creature) override
{
if (creature->GetEntry() == NPC_SINDRAGOSA)
SindragosaGUID.Clear();
InstanceScript::OnCreatureRemove(creature);
}
uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) override
{
if (TeamIdInInstance == TEAM_NEUTRAL)
{
Map::PlayerList const& players = instance->GetPlayers();
if (!players.IsEmpty())
if (Player* player = players.begin()->GetSource())
TeamIdInInstance = player->GetTeamId();
}
uint32 entry = data->id1;
switch (entry)
{
case NPC_HORDE_GUNSHIP_CANNON:
case NPC_ORGRIMS_HAMMER_CREW:
case NPC_SKY_REAVER_KORM_BLACKSCAR:
if (TeamIdInInstance == TEAM_ALLIANCE)
return 0;
break;
case NPC_ALLIANCE_GUNSHIP_CANNON:
case NPC_SKYBREAKER_DECKHAND:
case NPC_HIGH_CAPTAIN_JUSTIN_BARTLETT:
if (TeamIdInInstance == TEAM_HORDE)
return 0;
break;
case NPC_ZAFOD_BOOMBOX:
if (GameObjectTemplate const* go = sObjectMgr->GetGameObjectTemplate(GO_THE_SKYBREAKER_A))
if ((TeamIdInInstance == TEAM_ALLIANCE && data->mapid == go->moTransport.mapID) ||
(TeamIdInInstance == TEAM_HORDE && data->mapid != go->moTransport.mapID))
return entry;
return 0;
case NPC_IGB_MURADIN_BRONZEBEARD:
if ((TeamIdInInstance == TEAM_ALLIANCE && data->posX > 10.0f) ||
(TeamIdInInstance == TEAM_HORDE && data->posX < 10.0f))
return entry;
return 0;
case NPC_SPIRE_FROSTWYRM:
if ((TeamIdInInstance == TEAM_ALLIANCE && data->posY < 2200.0f) || (TeamIdInInstance == TEAM_HORDE && data->posY > 2200.0f))
return 0;
break;
}
return entry;
}
uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) override
{
if (TeamIdInInstance == TEAM_NEUTRAL)
{
Map::PlayerList const& players = instance->GetPlayers();
if (!players.IsEmpty())
if (Player* player = players.begin()->GetSource())
TeamIdInInstance = player->GetTeamId();
}
switch (entry)
{
case GO_GUNSHIP_ARMORY_H_10N:
case GO_GUNSHIP_ARMORY_H_25N:
case GO_GUNSHIP_ARMORY_H_10H:
case GO_GUNSHIP_ARMORY_H_25H:
if (TeamIdInInstance == TEAM_ALLIANCE)
return 0;
break;
case GO_GUNSHIP_ARMORY_A_10N:
case GO_GUNSHIP_ARMORY_A_25N:
case GO_GUNSHIP_ARMORY_A_10H:
case GO_GUNSHIP_ARMORY_A_25H:
if (TeamIdInInstance == TEAM_HORDE)
return 0;
break;
}
return entry;
}
void OnUnitDeath(Unit* unit) override
{
Creature* creature = unit->ToCreature();
if (!creature)
return;
// fighting npcs in Rampart of Skulls
std::string name1("Skybreaker ");
std::string name2("Kor'kron ");
if (!creature->GetTransport() && creature->GetPositionZ() <= 205.0f && creature->GetExactDist2d(-439.0f, 2210.0f) <= 150.0f && (creature->GetEntry() == 37544 || creature->GetEntry() == 37545 || creature->GetName().compare(0, name1.length(), name1) == 0 || creature->GetName().compare(0, name2.length(), name2) == 0))
if (!creature->GetLootRecipient())
creature->m_Events.AddEvent(new RespawnEvent(*creature), creature->m_Events.CalculateTime(3000));
switch (creature->GetEntry())
{
case NPC_YMIRJAR_BATTLE_MAIDEN:
case NPC_YMIRJAR_DEATHBRINGER:
case NPC_YMIRJAR_FROSTBINDER:
case NPC_YMIRJAR_HUNTRESS:
case NPC_YMIRJAR_WARLORD:
if (Creature* crok = instance->GetCreature(CrokScourgebaneGUID))
crok->AI()->SetGUID(creature->GetGUID(), ACTION_VRYKUL_DEATH);
break;
case NPC_FROSTWING_WHELP:
if (FrostwyrmGUIDs.empty())
return;
if (creature->AI()->GetData(1/*DATA_FROSTWYRM_OWNER*/) == DATA_SPINESTALKER)
{
SpinestalkerTrash.erase(creature->GetSpawnId());
if (SpinestalkerTrash.empty())
if (Creature* spinestalk = instance->GetCreature(SpinestalkerGUID))
spinestalk->AI()->DoAction(ACTION_START_FROSTWYRM);
}
else
{
RimefangTrash.erase(creature->GetSpawnId());
if (RimefangTrash.empty())
if (Creature* spinestalk = instance->GetCreature(RimefangGUID))
spinestalk->AI()->DoAction(ACTION_START_FROSTWYRM);
}
break;
case NPC_RIMEFANG:
case NPC_SPINESTALKER:
{
if (GetData(DATA_HAS_LIMITED_ATTEMPTS) && !HeroicAttempts)
return;
if (GetBossState(DATA_SINDRAGOSA) == DONE)
return;
FrostwyrmGUIDs.erase(creature->GetSpawnId());
if (FrostwyrmGUIDs.empty())
{
instance->LoadGrid(SindragosaSpawnPos.GetPositionX(), SindragosaSpawnPos.GetPositionY());
if (Creature* boss = instance->SummonCreature(NPC_SINDRAGOSA, SindragosaSpawnPos))
boss->AI()->DoAction(ACTION_START_FROSTWYRM);
}
break;
}
case NPC_DEATHSPEAKER_SERVANT:
if (Creature* c = unit->SummonCreature(WORLD_TRIGGER, *unit, TEMPSUMMON_TIMED_DESPAWN, 10000))
{
c->CastSpell(c, VOID_ZONE_VISUAL, true);
unit->SummonCreature(NPC_RISEN_DEATHSPEAKER_SERVANT, *unit, TEMPSUMMON_MANUAL_DESPAWN);
unit->ToCreature()->DespawnOrUnsummon(3000);
}
break;
default:
break;
}
}
void OnGameObjectCreate(GameObject* go) override
{
if (TeamIdInInstance == TEAM_NEUTRAL)
{
Map::PlayerList const& players = instance->GetPlayers();
if (!players.IsEmpty())
if (Player* player = players.begin()->GetSource())
TeamIdInInstance = player->GetTeamId();
}
switch (go->GetEntry())
{
case GO_SPIRIT_ALARM_1:
case GO_SPIRIT_ALARM_2:
case GO_SPIRIT_ALARM_3:
case GO_SPIRIT_ALARM_4:
SetPositionTraps(go);
break;
case GO_GEIST_ALARM_1:
case GO_GEIST_ALARM_2:
go->SetPosition(go->GetPositionX() + urand(0, 2) * 20.0f * (go->GetEntry() == GO_GEIST_ALARM_1 ? -1.0f : 1.0f), go->GetPositionY(), go->GetPositionZ(), go->GetOrientation());
break;
case GO_DOODAD_ICECROWN_ICEWALL02:
case GO_ICEWALL:
case GO_LORD_MARROWGAR_S_ENTRANCE:
case GO_ORATORY_OF_THE_DAMNED_ENTRANCE:
case GO_ORANGE_PLAGUE_MONSTER_ENTRANCE:
case GO_GREEN_PLAGUE_MONSTER_ENTRANCE:
case GO_CRIMSON_HALL_DOOR:
case GO_BLOOD_ELF_COUNCIL_DOOR:
case GO_BLOOD_ELF_COUNCIL_DOOR_RIGHT:
case GO_DOODAD_ICECROWN_BLOODPRINCE_DOOR_01:
case GO_DOODAD_ICECROWN_GRATE_01:
case GO_GREEN_DRAGON_BOSS_ENTRANCE:
case GO_GREEN_DRAGON_BOSS_EXIT:
case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_02:
case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_03:
case GO_SINDRAGOSA_SHORTCUT_ENTRANCE_DOOR:
case GO_SINDRAGOSA_SHORTCUT_EXIT_DOOR:
case GO_ICE_WALL:
case GO_SINDRAGOSA_ENTRANCE_DOOR:
AddDoor(go);
break;
case GO_SCIENTIST_ENTRANCE:
PutricideEnteranceDoorGUID = go->GetGUID();
HandleGameObject(PutricideEnteranceDoorGUID, PutricideEventProgress & PUTRICIDE_EVENT_FLAG_TRAP_FINISHED, go);
break;
// these 2 gates are functional only on 25man modes
case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_01:
case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_04:
if (instance->Is25ManRaid())
AddDoor(go);
break;
case GO_LADY_DEATHWHISPER_ELEVATOR:
LadyDeathwisperElevatorGUID = go->GetGUID();
break;
case GO_THE_SKYBREAKER_H:
case GO_ORGRIMS_HAMMER_A:
EnemyGunshipGUID = go->GetGUID();
break;
case GO_GUNSHIP_ARMORY_H_10N:
case GO_GUNSHIP_ARMORY_H_25N:
case GO_GUNSHIP_ARMORY_H_10H:
case GO_GUNSHIP_ARMORY_H_25H:
case GO_GUNSHIP_ARMORY_A_10N:
case GO_GUNSHIP_ARMORY_A_25N:
case GO_GUNSHIP_ARMORY_A_10H:
case GO_GUNSHIP_ARMORY_A_25H:
GunshipArmoryGUID = go->GetGUID();
break;
case GO_SAURFANG_S_DOOR:
DeathbringerSaurfangDoorGUID = go->GetGUID();
AddDoor(go);
break;
case GO_DEATHBRINGER_S_CACHE_10N:
case GO_DEATHBRINGER_S_CACHE_25N:
case GO_DEATHBRINGER_S_CACHE_10H:
case GO_DEATHBRINGER_S_CACHE_25H:
DeathbringersCacheGUID = go->GetGUID();
break;
case GO_SCOURGE_TRANSPORTER_SAURFANG:
SaurfangTeleportGUID = go->GetGUID();
break;
case GO_PLAGUE_SIGIL:
PlagueSigilGUID = go->GetGUID();
if (GetBossState(DATA_PROFESSOR_PUTRICIDE) == DONE)
HandleGameObject(PlagueSigilGUID, false, go);
break;
case GO_BLOODWING_SIGIL:
BloodwingSigilGUID = go->GetGUID();
if (GetBossState(DATA_BLOOD_QUEEN_LANA_THEL) == DONE)
HandleGameObject(BloodwingSigilGUID, false, go);
break;
case GO_SIGIL_OF_THE_FROSTWING:
FrostwingSigilGUID = go->GetGUID();
if (GetBossState(DATA_SINDRAGOSA) == DONE)
HandleGameObject(FrostwingSigilGUID, false, go);
break;
case GO_SCIENTIST_AIRLOCK_DOOR_COLLISION:
PutricideCollisionGUID = go->GetGUID();
HandleGameObject(PutricideCollisionGUID, ((PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE) && (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE)), go);
break;
case GO_SCIENTIST_AIRLOCK_DOOR_ORANGE:
PutricideGateGUIDs[0] = go->GetGUID();
if ((PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE) && (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE))
go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
else
HandleGameObject(PutricideGateGUIDs[0], !(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE), go);
break;
case GO_SCIENTIST_AIRLOCK_DOOR_GREEN:
PutricideGateGUIDs[1] = go->GetGUID();
if ((PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE) && (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE))
go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
else
HandleGameObject(PutricideGateGUIDs[1], !(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE), go);
break;
case GO_DOODAD_ICECROWN_ORANGETUBES02:
PutricidePipeGUIDs[0] = go->GetGUID();
if (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE)
HandleGameObject(PutricidePipeGUIDs[0], true, go);
break;
case GO_DOODAD_ICECROWN_GREENTUBES02:
PutricidePipeGUIDs[1] = go->GetGUID();
if (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE)
HandleGameObject(PutricidePipeGUIDs[1], true, go);
break;
case GO_GAS_RELEASE_VALVE:
GasReleaseValveGUID = go->GetGUID();
if (GetBossState(DATA_FESTERGUT) != DONE)
go->SetGameObjectFlag(GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE);
break;
case GO_OOZE_RELEASE_VALVE:
OozeReleaseValveGUID = go->GetGUID();
if (GetBossState(DATA_ROTFACE) != DONE)
go->SetGameObjectFlag(GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE);
break;
case GO_DRINK_ME:
PutricideTableGUID = go->GetGUID();
break;
case GO_CACHE_OF_THE_DREAMWALKER_10N:
case GO_CACHE_OF_THE_DREAMWALKER_25N:
case GO_CACHE_OF_THE_DREAMWALKER_10H:
case GO_CACHE_OF_THE_DREAMWALKER_25H:
if (Creature* valithria = instance->GetCreature(ValithriaDreamwalkerGUID))
go->SetLootRecipient(valithria);
go->RemoveGameObjectFlag(GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE | GO_FLAG_NODESPAWN);
break;
case GO_SCOURGE_TRANSPORTER_LK:
TheLichKingTeleportGUID = go->GetGUID();
if (GetBossState(DATA_PROFESSOR_PUTRICIDE) == DONE && GetBossState(DATA_BLOOD_QUEEN_LANA_THEL) == DONE && GetBossState(DATA_SINDRAGOSA) == DONE)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ARTHAS_PLATFORM:
// this enables movement at The Frozen Throne, when printed this value is 0.000000f
// however, when represented as integer client will accept only this value
go->SetUInt32Value(GAMEOBJECT_PARENTROTATION, 5535469);
ArthasPlatformGUID = go->GetGUID();
break;
case GO_ARTHAS_PRECIPICE:
go->SetUInt32Value(GAMEOBJECT_PARENTROTATION, 4178312);
ArthasPrecipiceGUID = go->GetGUID();
break;
case GO_DOODAD_ICECROWN_THRONEFROSTYEDGE01:
FrozenThroneEdgeGUID = go->GetGUID();
break;
case GO_DOODAD_ICECROWN_THRONEFROSTYWIND01:
FrozenThroneWindGUID = go->GetGUID();
break;
case GO_DOODAD_ICECROWN_SNOWEDGEWARNING01:
FrozenThroneWarningGUID = go->GetGUID();
break;
case GO_FROZEN_LAVAMAN:
FrozenBolvarGUID = go->GetGUID();
if (GetBossState(DATA_THE_LICH_KING) == DONE)
go->SetRespawnTime(7 * DAY);
break;
case GO_LAVAMAN_PILLARS_CHAINED:
PillarsChainedGUID = go->GetGUID();
if (GetBossState(DATA_THE_LICH_KING) == DONE)
go->SetRespawnTime(7 * DAY);
break;
case GO_LAVAMAN_PILLARS_UNCHAINED:
PillarsUnchainedGUID = go->GetGUID();
if (GetBossState(DATA_THE_LICH_KING) == DONE)
go->SetRespawnTime(7 * DAY);
break;
case GO_SCOURGE_TRANSPORTER_FIRST:
AddDoor(go);
ScourgeTransporterFirstGUID = go->GetGUID();
if (GetBossState(DATA_LORD_MARROWGAR) == DONE)
go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
break;
default:
break;
}
}
void OnGameObjectRemove(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_DOODAD_ICECROWN_ICEWALL02:
case GO_ICEWALL:
case GO_LORD_MARROWGAR_S_ENTRANCE:
case GO_ORATORY_OF_THE_DAMNED_ENTRANCE:
case GO_SAURFANG_S_DOOR:
case GO_ORANGE_PLAGUE_MONSTER_ENTRANCE:
case GO_GREEN_PLAGUE_MONSTER_ENTRANCE:
case GO_SCIENTIST_ENTRANCE:
case GO_CRIMSON_HALL_DOOR:
case GO_BLOOD_ELF_COUNCIL_DOOR:
case GO_BLOOD_ELF_COUNCIL_DOOR_RIGHT:
case GO_DOODAD_ICECROWN_BLOODPRINCE_DOOR_01:
case GO_DOODAD_ICECROWN_GRATE_01:
case GO_GREEN_DRAGON_BOSS_ENTRANCE:
case GO_GREEN_DRAGON_BOSS_EXIT:
case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_01:
case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_02:
case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_03:
case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_04:
//case GO_SINDRAGOSA_ENTRANCE_DOOR:
case GO_SINDRAGOSA_SHORTCUT_ENTRANCE_DOOR:
case GO_SINDRAGOSA_SHORTCUT_EXIT_DOOR:
case GO_ICE_WALL:
case GO_SCOURGE_TRANSPORTER_FIRST:
RemoveDoor(go);
break;
case GO_THE_SKYBREAKER_A:
case GO_ORGRIMS_HAMMER_H:
GunshipGUID.Clear();
break;
default:
break;
}
}
uint32 GetData(uint32 type) const override
{
switch (type)
{
case DATA_BUFF_AVAILABLE:
return (IsBuffAvailable ? 1 : 0);
case DATA_WEEKLY_QUEST_ID:
return WeeklyQuestId10;
case DATA_PUTRICIDE_TRAP_STATE:
if (!(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE) || !(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE))
return TO_BE_DECIDED;
if (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_TRAP_INPROGRESS)
return IN_PROGRESS;
if (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_TRAP_FINISHED)
return DONE;
return NOT_STARTED;
case DATA_HAS_LIMITED_ATTEMPTS:
return (instance->IsHeroic() ? 1 : 0);
case DATA_LK_HC_AVAILABLE:
return (LichKingHeroicAvailable ? 1 : 0);
case DATA_SINDRAGOSA_FROSTWYRMS:
return FrostwyrmGUIDs.size();
case DATA_SPINESTALKER:
return SpinestalkerTrash.size();
case DATA_RIMEFANG:
return RimefangTrash.size();
case DATA_COLDFLAME_JETS:
return ColdflameJetsState;
case DATA_TEAMID_IN_INSTANCE:
return TeamIdInInstance;
case DATA_BLOOD_QUICKENING_STATE:
return BloodQuickeningState;
case DATA_HEROIC_ATTEMPTS:
return HeroicAttempts;
case DATA_SINDRAGOSA_INTRO:
return (IsSindragosaIntroDone ? 1 : 0);
default:
break;
}
return 0;
}
ObjectGuid GetGuidData(uint32 type) const override
{
switch (type)
{
case DATA_LADY_DEATHWHISPER:
return LadyDeathwhisperGUID;
case DATA_ICECROWN_GUNSHIP_BATTLE:
return GunshipGUID;
case DATA_ENEMY_GUNSHIP:
return EnemyGunshipGUID;
case DATA_DEATHBRINGER_SAURFANG:
return DeathbringerSaurfangGUID;
case DATA_SAURFANG_EVENT_NPC:
return DeathbringerSaurfangEventGUID;
case GO_SAURFANG_S_DOOR:
return DeathbringerSaurfangDoorGUID;
case GO_SCOURGE_TRANSPORTER_SAURFANG:
return SaurfangTeleportGUID;
case DATA_FESTERGUT:
return FestergutGUID;
case DATA_ROTFACE:
return RotfaceGUID;
case DATA_PROFESSOR_PUTRICIDE:
return ProfessorPutricideGUID;
case DATA_PUTRICIDE_TABLE:
return PutricideTableGUID;
case DATA_PRINCE_KELESETH_GUID:
return BloodCouncilGUIDs[0];
case DATA_PRINCE_TALDARAM_GUID:
return BloodCouncilGUIDs[1];
case DATA_PRINCE_VALANAR_GUID:
return BloodCouncilGUIDs[2];
case DATA_BLOOD_PRINCES_CONTROL:
return BloodCouncilControllerGUID;
case DATA_BLOOD_QUEEN_LANA_THEL:
return BloodQueenLanaThelGUID;
case DATA_CROK_SCOURGEBANE:
return CrokScourgebaneGUID;
case DATA_CAPTAIN_ARNATH:
case DATA_CAPTAIN_BRANDON:
case DATA_CAPTAIN_GRONDEL:
case DATA_CAPTAIN_RUPERT:
return CrokCaptainGUIDs[type - DATA_CAPTAIN_ARNATH];
case DATA_SISTER_SVALNA:
return SisterSvalnaGUID;
case DATA_VALITHRIA_DREAMWALKER:
return ValithriaDreamwalkerGUID;
case DATA_VALITHRIA_LICH_KING:
return ValithriaLichKingGUID;
case DATA_VALITHRIA_TRIGGER:
return ValithriaTriggerGUID;
case NPC_SINDRAGOSA_GAUNTLET:
return SindragosaGauntletGUID;
case NPC_PUTRICADES_TRAP:
return PutricadeTrapGUID;
case DATA_SINDRAGOSA:
return SindragosaGUID;
case DATA_SPINESTALKER:
return SpinestalkerGUID;
case DATA_RIMEFANG:
return RimefangGUID;
case DATA_THE_LICH_KING:
return TheLichKingGUID;
case DATA_HIGHLORD_TIRION_FORDRING:
return HighlordTirionFordringGUID;
case DATA_ARTHAS_PLATFORM:
return ArthasPlatformGUID;
case DATA_TERENAS_MENETHIL:
return TerenasMenethilGUID;
default:
break;
}
return ObjectGuid::Empty;
}
void HandleDropAttempt(bool drop = true)
{
if (!GetData(DATA_HAS_LIMITED_ATTEMPTS))
return;
if (drop && HeroicAttempts)
{
--HeroicAttempts;
DoUpdateWorldState(WORLDSTATE_ATTEMPTS_REMAINING, HeroicAttempts);
SaveToDB();
}
if (HeroicAttempts)
return;
if (GetBossState(DATA_ROTFACE) == DONE && GetBossState(DATA_FESTERGUT) == DONE)
if (Creature* professor = instance->GetCreature(ProfessorPutricideGUID))
if (professor->IsAlive())
professor->SetVisible(false);
if (Creature* bq = instance->GetCreature(BloodQueenLanaThelGUID))
if (bq->IsAlive())
bq->SetVisible(false);
if (Creature* sindra = instance->GetCreature(SindragosaGUID))
if (sindra->IsAlive())
sindra->SetVisible(false);
if (Creature* theLichKing = instance->GetCreature(TheLichKingGUID))
if (theLichKing->IsAlive())
theLichKing->SetVisible(false);
}
void RemoveBackPack()
{
for (auto const& itr : instance->GetPlayers())
if (Player* _player = itr.GetSource())
_player->DestroyItemCount(ITEM_GOBLIN_ROCKET_PACK, _player->GetItemCount(ITEM_GOBLIN_ROCKET_PACK), true);
}
bool SetBossState(uint32 type, EncounterState state) override
{
if (!InstanceScript::SetBossState(type, state))
return false;
switch (type)
{
case DATA_LORD_MARROWGAR:
if (state == DONE)
{
WeeklyQuestId10 = RAND(QUEST_BLOOD_QUICKENING_10, QUEST_RESIDUE_RENDEZVOUS_10, QUEST_RESPITE_FOR_A_TORMENTED_SOUL_10, QUEST_DEPROGRAMMING_10, QUEST_SECURING_THE_RAMPARTS_10);
SetData(DATA_WEEKLY_QUEST_ID, 0); // show required hidden npcs
if (GameObject* transporter = instance->GetGameObject(ScourgeTransporterFirstGUID))
transporter->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
SaveToDB();
}
break;
case DATA_LADY_DEATHWHISPER:
if (state == DONE)
SpawnGunship();
break;
case DATA_ICECROWN_GUNSHIP_BATTLE:
if (state == DONE)
{
if (GameObject* loot = instance->GetGameObject(GunshipArmoryGUID))
{
loot->SetLootRecipient(instance);
loot->RemoveGameObjectFlag(GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE | GO_FLAG_NODESPAWN);
}
}
else if (state == FAIL)
Events.ScheduleEvent(EVENT_RESPAWN_GUNSHIP, 30s);
break;
case DATA_DEATHBRINGER_SAURFANG:
switch (state)
{
case DONE:
if (GameObject* loot = instance->GetGameObject(DeathbringersCacheGUID))
{
if (Creature* deathbringer = instance->GetCreature(DeathbringerSaurfangGUID))
loot->SetLootRecipient(deathbringer);
loot->RemoveGameObjectFlag(GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE | GO_FLAG_NODESPAWN);
}
[[fallthrough]];
case NOT_STARTED:
if (GameObject* teleporter = instance->GetGameObject(SaurfangTeleportGUID))
{
HandleGameObject(SaurfangTeleportGUID, true, teleporter);
teleporter->RemoveGameObjectFlag(GO_FLAG_IN_USE);
}
break;
default:
break;
}
break;
case DATA_FESTERGUT:
if (state == DONE)
{
if (GameObject* go = instance->GetGameObject(GasReleaseValveGUID))
go->RemoveGameObjectFlag(GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE);
if (GetBossState(DATA_ROTFACE) == DONE)
HandleDropAttempt(false);
}
break;
case DATA_ROTFACE:
if (state == DONE)
{
if (GameObject* go = instance->GetGameObject(OozeReleaseValveGUID))
go->RemoveGameObjectFlag(GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE);
if (GetBossState(DATA_FESTERGUT) == DONE)
HandleDropAttempt(false);
}
break;
case DATA_PROFESSOR_PUTRICIDE:
HandleGameObject(PutricideEnteranceDoorGUID, (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_TRAP_FINISHED) && state != IN_PROGRESS);
HandleGameObject(PlagueSigilGUID, state != DONE);
if (state == DONE)
CheckLichKingAvailability();
else if (state == FAIL)
HandleDropAttempt();
if (state == DONE && !instance->IsHeroic() && LichKingHeroicAvailable)
{
LichKingHeroicAvailable = false;
SaveToDB();
}
break;
case DATA_BLOOD_QUEEN_LANA_THEL:
HandleGameObject(BloodwingSigilGUID, state != DONE);
if (state == DONE)
CheckLichKingAvailability();
else if (state == FAIL)
HandleDropAttempt();
if (state == DONE && !instance->IsHeroic() && LichKingHeroicAvailable)
{
LichKingHeroicAvailable = false;
SaveToDB();
}
break;
case DATA_VALITHRIA_DREAMWALKER:
if (state == DONE)
SetData(DATA_WEEKLY_QUEST_ID, GetData(DATA_WEEKLY_QUEST_ID)); // will show weekly quest npc if necessary
break;
case DATA_SINDRAGOSA:
HandleGameObject(FrostwingSigilGUID, state != DONE);
if (state == DONE)
CheckLichKingAvailability();
else if (state == FAIL)
{
IsSindragosaIntroDone = true;
HandleDropAttempt();
if (instance->IsHeroic())
{
if (HeroicAttempts)
{
Events.ScheduleEvent(EVENT_RESPAWN_SINDRAGOSA, 30s);
}
}
else
{
Events.ScheduleEvent(EVENT_RESPAWN_SINDRAGOSA, 30s);
}
}
if (state == DONE && !instance->IsHeroic() && LichKingHeroicAvailable)
{
LichKingHeroicAvailable = false;
SaveToDB();
}
break;
case DATA_THE_LICH_KING:
{
// dramatically increase visibility range during fight to seeing frostmourne room
instance->SetVisibilityRange(state == IN_PROGRESS ? 500.0f : 200.0f);
if (state == FAIL)
{
Events.CancelEvent(EVENT_QUAKE_SHATTER);
Events.CancelEvent(EVENT_REBUILD_PLATFORM);
HandleDropAttempt();
}
if (state == DONE)
{
if (GameObject* bolvar = instance->GetGameObject(FrozenBolvarGUID))
bolvar->SetRespawnTime(7 * DAY);
if (GameObject* pillars = instance->GetGameObject(PillarsChainedGUID))
pillars->SetRespawnTime(7 * DAY);
if (GameObject* pillars = instance->GetGameObject(PillarsUnchainedGUID))
pillars->SetRespawnTime(7 * DAY);
instance->LoadGrid(JainaSpawnPos.GetPositionX(), JainaSpawnPos.GetPositionY());
instance->SummonCreature(NPC_LADY_JAINA_PROUDMOORE_QUEST, JainaSpawnPos);
instance->SummonCreature(NPC_MURADIN_BRONZEBEARD_QUEST, MuradinSpawnPos);
instance->SummonCreature(NPC_UTHER_THE_LIGHTBRINGER_QUEST, UtherSpawnPos);
instance->SummonCreature(NPC_LADY_SYLVANAS_WINDRUNNER_QUEST, SylvanasSpawnPos);
}
break;
}
default:
break;
}
return true;
}
void SpawnGunship()
{
if (!GunshipGUID && instance->HavePlayers())
{
SetBossState(DATA_ICECROWN_GUNSHIP_BATTLE, NOT_STARTED);
uint32 gunshipEntry = TeamIdInInstance == TEAM_HORDE ? GO_ORGRIMS_HAMMER_H : GO_THE_SKYBREAKER_A;
if (MotionTransport* gunship = sTransportMgr->CreateTransport(gunshipEntry, 0, instance))
{
GunshipGUID = gunship->GetGUID();
gunship->setActive(false);
}
}
}
void SetData(uint32 type, uint32 data) override
{
switch (type)
{
case DATA_BUFF_AVAILABLE:
IsBuffAvailable = !!data;
if (!IsBuffAvailable)
{
instance->DoForAllPlayers([&](Player* player)
{
player->UpdateAreaDependentAuras(player->GetAreaId());
for (Unit::ControlSet::const_iterator itr = player->m_Controlled.begin(); itr != player->m_Controlled.end(); ++itr)
{
Unit::AuraMap& am = (*itr)->GetOwnedAuras();
for (Unit::AuraMap::iterator itra = am.begin(); itra != am.end();)
switch (itra->second->GetId())
{
// Hellscream's Warsong
case 73816:
case 73818:
case 73819:
case 73820:
case 73821:
case 73822:
// Strength of Wrynn
case 73762:
case 73824:
case 73825:
case 73826:
case 73827:
case 73828:
(*itr)->RemoveOwnedAura(itra);
break;
default:
++itra;
break;
}
}
});
}
SaveToDB();
break;
case DATA_WEEKLY_QUEST_ID:
for (uint8 i = 0; i < WeeklyNPCs; ++i)
if (WeeklyQuestData[i].questId[0] == WeeklyQuestId10 && (WeeklyQuestData[i].creatureEntry != NPC_VALITHRIA_DREAMWALKER_QUEST || GetBossState(DATA_VALITHRIA_DREAMWALKER) == DONE) /*appears after killing valithria*/)
if (WeeklyQuestNpcGUID[i])
if (Creature* c = instance->GetCreature(WeeklyQuestNpcGUID[i]))
c->SetVisible(true);
break;
case DATA_PUTRICIDE_TRAP_STATE:
if (data == NOT_STARTED)
{
PutricideEventProgress &= ~PUTRICIDE_EVENT_FLAG_TRAP_INPROGRESS;
HandleGameObject(PutricideCollisionGUID, ((PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE) && (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE)));
if ((PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE) && (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE))
{
for (uint8 i = 0; i < 2; ++i)
if (GameObject* go = instance->GetGameObject(PutricideGateGUIDs[i]))
go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
}
else
{
HandleGameObject(PutricideGateGUIDs[0], !(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE));
HandleGameObject(PutricideGateGUIDs[1], !(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE));
}
SaveToDB();
}
else if (data == IN_PROGRESS)
{
PutricideEventProgress |= PUTRICIDE_EVENT_FLAG_TRAP_INPROGRESS;
HandleGameObject(PutricideCollisionGUID, false);
HandleGameObject(PutricideGateGUIDs[0], false);
HandleGameObject(PutricideGateGUIDs[1], false);
SaveToDB();
}
else if (data == DONE)
{
PutricideEventProgress &= ~PUTRICIDE_EVENT_FLAG_TRAP_INPROGRESS;
PutricideEventProgress |= PUTRICIDE_EVENT_FLAG_TRAP_FINISHED;
HandleGameObject(PutricideEnteranceDoorGUID, true);
HandleGameObject(PutricideCollisionGUID, ((PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE) && (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE)));
if ((PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE) && (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE))
{
for (uint8 i = 0; i < 2; ++i)
if (GameObject* go = instance->GetGameObject(PutricideGateGUIDs[i]))
go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
}
else
{
HandleGameObject(PutricideGateGUIDs[0], !(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE));
HandleGameObject(PutricideGateGUIDs[1], !(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE));
}
SaveToDB();
}
return;
case DATA_BONED_ACHIEVEMENT:
IsBonedEligible = !!data;
break;
case DATA_OOZE_DANCE_ACHIEVEMENT:
IsOozeDanceEligible = !!data;
break;
case DATA_NAUSEA_ACHIEVEMENT:
IsNauseaEligible = !!data;
break;
case DATA_ORB_WHISPERER_ACHIEVEMENT:
IsOrbWhispererEligible = !!data;
break;
case DATA_SINDRAGOSA_FROSTWYRMS:
FrostwyrmGUIDs.insert(data);
break;
case DATA_SPINESTALKER:
SpinestalkerTrash.insert(data);
break;
case DATA_RIMEFANG:
RimefangTrash.insert(data);
break;
case DATA_COLDFLAME_JETS:
ColdflameJetsState = data;
if (ColdflameJetsState == DONE)
SaveToDB();
break;
case DATA_BLOOD_QUICKENING_STATE:
{
if (data == IN_PROGRESS && BloodQuickeningState != NOT_STARTED)
break;
if (BloodQuickeningState == data)
break;
if (WeeklyQuestId10 != QUEST_BLOOD_QUICKENING_10)
break;
switch (data)
{
case IN_PROGRESS:
Events.ScheduleEvent(EVENT_UPDATE_EXECUTION_TIME, 1min);
BloodQuickeningMinutes = 30;
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 1);
DoUpdateWorldState(WORLDSTATE_EXECUTION_TIME, BloodQuickeningMinutes);
break;
case DONE:
Events.CancelEvent(EVENT_UPDATE_EXECUTION_TIME);
BloodQuickeningMinutes = 0;
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 0);
break;
default:
break;
}
BloodQuickeningState = data;
SaveToDB();
break;
}
case DATA_BPC_TRASH_DIED:
{
if (++BloodPrinceTrashCount >= 4)
{
SetBossState(DATA_BLOOD_PRINCE_TRASH, NOT_STARTED);
SetBossState(DATA_BLOOD_PRINCE_TRASH, DONE);
}
SaveToDB();
break;
}
default:
break;
}
}
bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override
{
switch (criteria_id)
{
case CRITERIA_BONED_10N:
case CRITERIA_BONED_25N:
case CRITERIA_BONED_10H:
case CRITERIA_BONED_25H:
return IsBonedEligible;
case CRITERIA_DANCES_WITH_OOZES_10N:
case CRITERIA_DANCES_WITH_OOZES_25N:
case CRITERIA_DANCES_WITH_OOZES_10H:
case CRITERIA_DANCES_WITH_OOZES_25H:
return IsOozeDanceEligible;
case CRITERIA_NAUSEA_10N:
case CRITERIA_NAUSEA_25N:
case CRITERIA_NAUSEA_10H:
case CRITERIA_NAUSEA_25H:
return IsNauseaEligible;
case CRITERIA_ORB_WHISPERER_10N:
case CRITERIA_ORB_WHISPERER_25N:
case CRITERIA_ORB_WHISPERER_10H:
case CRITERIA_ORB_WHISPERER_25H:
return IsOrbWhispererEligible;
// Only one criteria for both modes, need to do it like this
case CRITERIA_KILL_LANA_THEL_10M:
return instance->ToInstanceMap()->GetMaxPlayers() == 10;
case CRITERIA_KILL_LANA_THEL_25M:
return instance->ToInstanceMap()->GetMaxPlayers() == 25;
default:
break;
}
return false;
}
bool CheckRequiredBosses(uint32 bossId, Player const* player) const override
{
if (player && player->GetSession() && player->GetSession()->GetSecurity() >= SEC_MODERATOR)
{
return true;
}
switch (bossId)
{
case DATA_THE_LICH_KING:
if (!CheckPlagueworks(bossId))
return false;
if (!CheckCrimsonHalls(bossId))
return false;
if (!CheckFrostwingHalls(bossId))
return false;
break;
case DATA_SINDRAGOSA:
case DATA_VALITHRIA_DREAMWALKER:
if (!CheckFrostwingHalls(bossId))
return false;
break;
case DATA_BLOOD_QUEEN_LANA_THEL:
case DATA_BLOOD_PRINCE_COUNCIL:
if (!CheckCrimsonHalls(bossId))
return false;
break;
case DATA_FESTERGUT:
case DATA_ROTFACE:
case DATA_PROFESSOR_PUTRICIDE:
if (!CheckPlagueworks(bossId))
return false;
break;
default:
break;
}
if (!CheckLowerSpire(bossId))
return false;
return true;
}
bool CheckPlagueworks(uint32 bossId) const
{
switch (bossId)
{
case DATA_THE_LICH_KING:
if (GetBossState(DATA_PROFESSOR_PUTRICIDE) != DONE)
{
return false;
}
[[fallthrough]];
case DATA_PROFESSOR_PUTRICIDE:
if (GetBossState(DATA_FESTERGUT) != DONE || GetBossState(DATA_ROTFACE) != DONE)
return false;
break;
default:
break;
}
return true;
}
bool CheckCrimsonHalls(uint32 bossId) const
{
switch (bossId)
{
case DATA_THE_LICH_KING:
if (GetBossState(DATA_BLOOD_QUEEN_LANA_THEL) != DONE)
{
return false;
}
[[fallthrough]];
case DATA_BLOOD_QUEEN_LANA_THEL:
if (GetBossState(DATA_BLOOD_PRINCE_COUNCIL) != DONE)
return false;
break;
default:
break;
}
return true;
}
bool CheckFrostwingHalls(uint32 bossId) const
{
switch (bossId)
{
case DATA_THE_LICH_KING:
if (GetBossState(DATA_SINDRAGOSA) != DONE)
{
return false;
}
[[fallthrough]];
case DATA_SINDRAGOSA:
if (GetBossState(DATA_VALITHRIA_DREAMWALKER) != DONE)
{
return false;
}
if (GetBossState(DATA_SINDRAGOSA_GAUNTLET) != DONE)
{
return false;
}
break;
default:
break;
}
return true;
}
bool CheckLowerSpire(uint32 bossId) const
{
switch (bossId)
{
case DATA_THE_LICH_KING:
case DATA_SINDRAGOSA:
case DATA_BLOOD_QUEEN_LANA_THEL:
case DATA_PROFESSOR_PUTRICIDE:
case DATA_VALITHRIA_DREAMWALKER:
case DATA_BLOOD_PRINCE_COUNCIL:
case DATA_ROTFACE:
case DATA_FESTERGUT:
if (GetBossState(DATA_DEATHBRINGER_SAURFANG) != DONE)
{
return false;
}
[[fallthrough]];
case DATA_DEATHBRINGER_SAURFANG:
if (GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != DONE)
{
return false;
}
[[fallthrough]];
case DATA_ICECROWN_GUNSHIP_BATTLE:
if (GetBossState(DATA_LADY_DEATHWHISPER) != DONE)
{
return false;
}
[[fallthrough]];
case DATA_LADY_DEATHWHISPER:
if (GetBossState(DATA_LORD_MARROWGAR) != DONE)
{
return false;
}
[[fallthrough]];
case DATA_LORD_MARROWGAR:
default:
break;
}
return true;
}
void CheckLichKingAvailability()
{
if (GetBossState(DATA_PROFESSOR_PUTRICIDE) == DONE && GetBossState(DATA_BLOOD_QUEEN_LANA_THEL) == DONE && GetBossState(DATA_SINDRAGOSA) == DONE)
{
if (GameObject* teleporter = instance->GetGameObject(TheLichKingTeleportGUID))
{
teleporter->SetGoState(GO_STATE_ACTIVE);
std::list<Creature*> stalkers;
GetCreatureListWithEntryInGrid(stalkers, teleporter, NPC_INVISIBLE_STALKER, 100.0f);
if (stalkers.empty())
return;
stalkers.sort(Acore::ObjectDistanceOrderPred(teleporter));
stalkers.front()->CastSpell((Unit*)nullptr, SPELL_ARTHAS_TELEPORTER_CEREMONY, false);
stalkers.pop_front();
for (std::list<Creature*>::iterator itr = stalkers.begin(); itr != stalkers.end(); ++itr)
(*itr)->AI()->Reset();
}
}
}
void ReadSaveDataMore(std::istringstream& data) override
{
data >> HeroicAttempts;
uint32 temp = 0;
data >> temp;
if (temp == IN_PROGRESS)
{
ColdflameJetsState = NOT_STARTED;
}
else
{
ColdflameJetsState = temp ? DONE : NOT_STARTED;
}
data >> BloodQuickeningState;
data >> BloodQuickeningMinutes;
if (BloodQuickeningState == IN_PROGRESS)
{
Events.ScheduleEvent(EVENT_UPDATE_EXECUTION_TIME, 1min);
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 1);
DoUpdateWorldState(WORLDSTATE_EXECUTION_TIME, BloodQuickeningMinutes);
}
data >> WeeklyQuestId10;
data >> PutricideEventProgress;
PutricideEventProgress &= ~PUTRICIDE_EVENT_FLAG_TRAP_INPROGRESS;
data >> LichKingHeroicAvailable;
data >> BloodPrinceTrashCount;
data >> IsBuffAvailable;
data >> IsSindragosaIntroDone;
SetData(DATA_BUFF_AVAILABLE, IsBuffAvailable);
}
void WriteSaveDataMore(std::ostringstream& data) override
{
data << HeroicAttempts << ' '
<< ColdflameJetsState << ' '
<< BloodQuickeningState << ' '
<< BloodQuickeningMinutes << ' '
<< WeeklyQuestId10 << ' '
<< PutricideEventProgress << ' '
<< uint32(LichKingHeroicAvailable ? 1 : 0) << ' '
<< BloodPrinceTrashCount << ' '
<< uint32(IsBuffAvailable ? 1 : 0) << ' '
<< uint32(IsSindragosaIntroDone ? 1 : 0);
}
void Update(uint32 diff) override
{
// Xinef: A Feast of Souls (24547) whispers
if (LichKingRandomWhisperTimer <= diff)
{
LichKingRandomWhisperTimer = urand(100, 300) * IN_MILLISECONDS;
Map::PlayerList const& players = instance->GetPlayers();
if (!players.IsEmpty())
if (Player* player = players.begin()->GetSource())
if (player->GetQuestStatus(QUEST_A_FEAST_OF_SOULS) == QUEST_STATUS_INCOMPLETE)
{
if (Creature* theLichKing = instance->GetCreature(TheLichKingLhGUID))
{
theLichKing->AI()->Talk(SAY_SOULS_LICH_KING_RAND_WHISPER, player);
}
}
}
else
LichKingRandomWhisperTimer -= diff;
if (DarkwhisperElevatorTimer <= diff)
{
DarkwhisperElevatorTimer = 3000;
if (GetBossState(DATA_LADY_DEATHWHISPER) == DONE)
if (GameObject* elevator = instance->GetGameObject(LadyDeathwisperElevatorGUID))
if (StaticTransport* trans = elevator->ToStaticTransport())
{
if (trans->GetGoState() == GO_STATE_READY && trans->GetPathProgress() == 0)
trans->SetGoState(GO_STATE_ACTIVE);
else if (trans->GetGoState() == GO_STATE_ACTIVE && trans->GetPathProgress() == trans->GetPauseTime())
trans->SetGoState(GO_STATE_READY);
}
}
else
DarkwhisperElevatorTimer -= diff;
if (Events.Empty())
return;
Events.Update(diff);
while (uint32 eventId = Events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_UPDATE_EXECUTION_TIME:
{
--BloodQuickeningMinutes;
if (BloodQuickeningMinutes)
{
Events.ScheduleEvent(EVENT_UPDATE_EXECUTION_TIME, 1min);
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 1);
DoUpdateWorldState(WORLDSTATE_EXECUTION_TIME, BloodQuickeningMinutes);
}
else
{
BloodQuickeningState = DONE;
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 0);
if (Creature* bq = instance->GetCreature(BloodQueenLanaThelGUID))
bq->AI()->DoAction(ACTION_KILL_MINCHAR);
}
SaveToDB();
break;
}
case EVENT_QUAKE_SHATTER:
{
if (GameObject* platform = instance->GetGameObject(ArthasPlatformGUID))
platform->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED);
if (GameObject* edge = instance->GetGameObject(FrozenThroneEdgeGUID))
edge->SetGoState(GO_STATE_ACTIVE);
if (GameObject* wind = instance->GetGameObject(FrozenThroneWindGUID))
wind->SetGoState(GO_STATE_READY);
if (GameObject* warning = instance->GetGameObject(FrozenThroneWarningGUID))
warning->SetGoState(GO_STATE_READY);
if (Creature* theLichKing = instance->GetCreature(TheLichKingGUID))
theLichKing->AI()->DoAction(ACTION_RESTORE_LIGHT);
break;
}
case EVENT_REBUILD_PLATFORM:
if (GameObject* platform = instance->GetGameObject(ArthasPlatformGUID))
platform->SetDestructibleState(GO_DESTRUCTIBLE_REBUILDING, nullptr, true);
if (GameObject* edge = instance->GetGameObject(FrozenThroneEdgeGUID))
edge->SetGoState(GO_STATE_READY);
if (GameObject* wind = instance->GetGameObject(FrozenThroneWindGUID))
wind->SetGoState(GO_STATE_ACTIVE);
break;
case EVENT_RESPAWN_GUNSHIP:
SpawnGunship();
break;
case EVENT_RESPAWN_SINDRAGOSA:
if (!GetCreature(DATA_SINDRAGOSA))
{
if (Creature* sindragosa = instance->SummonCreature(NPC_SINDRAGOSA, SindragosaSpawnPos))
{
sindragosa->setActive(true);
sindragosa->SetDisableGravity(true);
sindragosa->GetMotionMaster()->MovePath(NPC_SINDRAGOSA * 10, true);
if (TempSummon* summon = sindragosa->ToTempSummon())
{
summon->SetTempSummonType(TEMPSUMMON_DEAD_DESPAWN);
}
}
}
// Could happen more than once if more than one player enters before she respawns.
Events.CancelEvent(EVENT_RESPAWN_SINDRAGOSA);
break;
default:
break;
}
}
}
void ProcessEvent(WorldObject* source, uint32 eventId) override
{
switch (eventId)
{
case EVENT_ENEMY_GUNSHIP_DESPAWN:
if (GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == DONE)
{
if (GameObject* go = source->ToGameObject())
if (MotionTransport* transport = go->ToMotionTransport())
transport->UnloadNonStaticPassengers();
source->AddObjectToRemoveList();
}
break;
case EVENT_ENEMY_GUNSHIP_COMBAT:
if (Creature* captain = source->FindNearestCreature(TeamIdInInstance == TEAM_HORDE ? NPC_IGB_HIGH_OVERLORD_SAURFANG : NPC_IGB_MURADIN_BRONZEBEARD, 200.0f))
{
captain->AI()->DoAction(ACTION_ENEMY_GUNSHIP_TALK);
}
[[fallthrough]];
case EVENT_PLAYERS_GUNSHIP_SPAWN:
case EVENT_PLAYERS_GUNSHIP_COMBAT:
if (GameObject* go = source->ToGameObject())
if (MotionTransport* transport = go->ToMotionTransport())
transport->EnableMovement(false);
break;
case EVENT_PLAYERS_GUNSHIP_SAURFANG:
if (GameObject* go = source->ToGameObject())
if (MotionTransport* transport = go->ToMotionTransport())
{
transport->setActive(false);
transport->EnableMovement(false);
//After movement is stopped remove the backpack
RemoveBackPack();
}
if (Creature* captain = source->FindNearestCreature(TeamIdInInstance == TEAM_HORDE ? NPC_IGB_HIGH_OVERLORD_SAURFANG : NPC_IGB_MURADIN_BRONZEBEARD, 200.0f))
captain->AI()->DoAction(ACTION_EXIT_SHIP);
break;
case EVENT_QUAKE:
if (GameObject* warning = instance->GetGameObject(FrozenThroneWarningGUID))
warning->SetGoState(GO_STATE_ACTIVE);
Events.ScheduleEvent(EVENT_QUAKE_SHATTER, 5s);
break;
case EVENT_SECOND_REMORSELESS_WINTER:
if (GameObject* platform = instance->GetGameObject(ArthasPlatformGUID))
{
platform->SetDestructibleState(GO_DESTRUCTIBLE_DESTROYED);
Events.ScheduleEvent(EVENT_REBUILD_PLATFORM, 1500ms);
}
break;
case EVENT_TELEPORT_TO_FROSMOURNE: // Harvest Soul (normal mode)
if (Creature* terenas = instance->SummonCreature(NPC_TERENAS_MENETHIL_FROSTMOURNE, TerenasSpawn, nullptr, 65000))
{
terenas->AI()->DoAction(ACTION_FROSTMOURNE_INTRO);
std::list<Creature*> triggers;
GetCreatureListWithEntryInGrid(triggers, terenas, NPC_WORLD_TRIGGER_INFINITE_AOI, 100.0f);
if (!triggers.empty())
{
triggers.sort(Acore::ObjectDistanceOrderPred(terenas, false));
Unit* visual = triggers.front();
visual->CastSpell(visual, SPELL_FROSTMOURNE_TELEPORT_VISUAL, true);
}
if (Creature* warden = instance->SummonCreature(NPC_SPIRIT_WARDEN, SpiritWardenSpawn, nullptr, 65000))
{
terenas->AI()->AttackStart(warden);
warden->AddThreat(terenas, 300000.0f);
}
}
break;
case EVENT_FESTERGUT_VALVE_USED:
if (!(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE))
{
if (GameObject* goGas = instance->GetGameObject(GasReleaseValveGUID))
goGas->SetGameObjectFlag(GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE);
PutricideEventProgress |= PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE;
if (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE)
{
HandleGameObject(PutricideCollisionGUID, true);
for (uint8 i = 0; i < 2; ++i)
if (GameObject* go = instance->GetGameObject(PutricideGateGUIDs[i]))
go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
}
else
HandleGameObject(PutricideGateGUIDs[0], false);
HandleGameObject(PutricidePipeGUIDs[0], true);
SaveToDB();
}
break;
case EVENT_ROTFACE_VALVE_USED:
if (!(PutricideEventProgress & PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE))
{
if (GameObject* goOoze = instance->GetGameObject(OozeReleaseValveGUID))
goOoze->SetGameObjectFlag(GO_FLAG_INTERACT_COND | GO_FLAG_NOT_SELECTABLE);
PutricideEventProgress |= PUTRICIDE_EVENT_FLAG_ROTFACE_VALVE;
if (PutricideEventProgress & PUTRICIDE_EVENT_FLAG_FESTERGUT_VALVE)
{
HandleGameObject(PutricideCollisionGUID, true);
for (uint8 i = 0; i < 2; ++i)
if (GameObject* go = instance->GetGameObject(PutricideGateGUIDs[i]))
go->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
}
else
HandleGameObject(PutricideGateGUIDs[1], false);
HandleGameObject(PutricidePipeGUIDs[1], true);
SaveToDB();
}
break;
}
}
void SetPositionTraps(GameObject* go)
{
std::vector<Position> trapPositions;
switch (go->GetEntry())
{
case GO_SPIRIT_ALARM_1:
trapPositions = GoSpiritAlarm_1;
break;
case GO_SPIRIT_ALARM_2:
trapPositions = GoSpiritAlarm_2;
break;
case GO_SPIRIT_ALARM_3:
trapPositions = GoSpiritAlarm_3;
break;
case GO_SPIRIT_ALARM_4:
trapPositions = GoSpiritAlarm_4;
break;
default:
return;
}
go->Relocate(Acore::Containers::SelectRandomContainerElement(trapPositions));
}
protected:
// pussywizard:
bool IsBuffAvailable;
uint32 WeeklyQuestId10; // contains id from 10man for any difficulty (for simplicity)
ObjectGuid WeeklyQuestNpcGUID[WeeklyNPCs];
ObjectGuid PutricideEnteranceDoorGUID;
uint32 PutricideEventProgress;
ObjectGuid GasReleaseValveGUID;
ObjectGuid OozeReleaseValveGUID;
bool LichKingHeroicAvailable;
uint32 LichKingRandomWhisperTimer;
uint32 DarkwhisperElevatorTimer;
ObjectGuid ScourgeTransporterFirstGUID;
EventMap Events;
ObjectGuid LadyDeathwhisperGUID;
ObjectGuid LadyDeathwisperElevatorGUID;
ObjectGuid GunshipGUID;
ObjectGuid EnemyGunshipGUID;
ObjectGuid GunshipArmoryGUID;
ObjectGuid DeathbringerSaurfangGUID;
ObjectGuid DeathbringerSaurfangDoorGUID;
ObjectGuid DeathbringerSaurfangEventGUID; // Muradin Bronzebeard or High Overlord Saurfang
ObjectGuid DeathbringersCacheGUID;
ObjectGuid SaurfangTeleportGUID;
ObjectGuid PlagueSigilGUID;
ObjectGuid BloodwingSigilGUID;
ObjectGuid FrostwingSigilGUID;
ObjectGuid PutricidePipeGUIDs[2];
ObjectGuid PutricideGateGUIDs[2];
ObjectGuid PutricideCollisionGUID;
ObjectGuid FestergutGUID;
ObjectGuid RotfaceGUID;
ObjectGuid ProfessorPutricideGUID;
ObjectGuid PutricideTableGUID;
ObjectGuid BloodCouncilGUIDs[3];
ObjectGuid BloodCouncilControllerGUID;
ObjectGuid BloodQueenLanaThelGUID;
ObjectGuid CrokScourgebaneGUID;
ObjectGuid CrokCaptainGUIDs[4];
ObjectGuid SisterSvalnaGUID;
ObjectGuid ValithriaDreamwalkerGUID;
ObjectGuid ValithriaLichKingGUID;
ObjectGuid ValithriaTriggerGUID;
ObjectGuid PutricadeTrapGUID;
ObjectGuid SindragosaGauntletGUID;
ObjectGuid SindragosaGUID;
ObjectGuid SpinestalkerGUID;
ObjectGuid RimefangGUID;
ObjectGuid TheLichKingTeleportGUID;
ObjectGuid TheLichKingGUID;
ObjectGuid TheLichKingLhGUID;
ObjectGuid HighlordTirionFordringGUID;
ObjectGuid TerenasMenethilGUID;
ObjectGuid ArthasPlatformGUID;
ObjectGuid ArthasPrecipiceGUID;
ObjectGuid FrozenThroneEdgeGUID;
ObjectGuid FrozenThroneWindGUID;
ObjectGuid FrozenThroneWarningGUID;
ObjectGuid FrozenBolvarGUID;
ObjectGuid PillarsChainedGUID;
ObjectGuid PillarsUnchainedGUID;
TeamId TeamIdInInstance;
uint32 ColdflameJetsState;
std::set<ObjectGuid::LowType> FrostwyrmGUIDs;
std::set<ObjectGuid::LowType> SpinestalkerTrash;
std::set<ObjectGuid::LowType> RimefangTrash;
uint32 BloodQuickeningState;
uint32 HeroicAttempts;
uint16 BloodQuickeningMinutes;
uint32 BloodPrinceTrashCount;
bool IsBonedEligible;
bool IsOozeDanceEligible;
bool IsNauseaEligible;
bool IsOrbWhispererEligible;
bool IsSindragosaIntroDone;
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override
{
return new instance_icecrown_citadel_InstanceMapScript(map);
}
};
void AddSC_instance_icecrown_citadel()
{
new instance_icecrown_citadel();
}