diff --git a/src/naxx40Scripts/instance_naxxramas.cpp b/src/naxx40Scripts/instance_naxxramas.cpp index 5fb34a7..fd7933c 100644 --- a/src/naxx40Scripts/instance_naxxramas.cpp +++ b/src/naxx40Scripts/instance_naxxramas.cpp @@ -15,1493 +15,826 @@ * with this program. If not, see . */ +#include "AreaTriggerScript.h" #include "CellImpl.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" +#include "CreatureAIImpl.h" +#include "CreatureScript.h" +#include "InstanceMapScript.h" +#include "InstanceScript.h" #include "PassiveAI.h" -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "DBCEnums.h" -#include "ObjectMgr.h" +#include "Player.h" #include "naxxramas.h" -const float HeiganPos[2] = {2796, -3707}; -const float HeiganEruptionSlope[3] = +struct LivingPoisonData +{ + Position Start {}; + Position End {}; + uint32 DespawnTime {}; +}; + +static const LivingPoisonData LivingPoisonDataList[3] +{ + { Position { 3128.59, -3118.81, 293.346, 4.76754 }, Position { 3130.322, -3156.51, 293.324 }, 15200 }, + { Position { 3154.25, -3125.7, 293.43, 4.47694 }, Position { 3144.779, -3158.416, 293.324 }, 14800 }, + { Position { 3175.42, -3134.86, 293.34, 4.284 }, Position { 3158.778, -3164.201, 293.312 }, 14800 } +}; + +static const float HeiganPos[2] +{ + 2796, -3707 +}; + +static const float HeiganEruptionSlope[3] { (-3685 - HeiganPos[1]) / (2724 - HeiganPos[0]), (-3647 - HeiganPos[1]) / (2749 - HeiganPos[0]), (-3637 - HeiganPos[1]) / (2771 - HeiganPos[0]), }; -inline uint8 GetEruptionSection(float x, float y) +static constexpr std::array HorsemanDataGroup { - y -= HeiganPos[1]; - if (y < 1.0f) - return 0; - - x -= HeiganPos[0]; - if (x > -1.0f) - return 3; - - float slope = y / x; - for (uint32 i = 0; i < 3; ++i) - { - if (slope > HeiganEruptionSlope[i]) - return i; - } - return 3; -} - -ObjectData const creatureData[] = -{ - { NPC_RAZUVIOUS, DATA_RAZUVIOUS }, - { NPC_RAZUVIOUS_40, DATA_RAZUVIOUS_40 }, - { 0, 0 } + DATA_BARON_RIVENDARE_BOSS, + DATA_SIR_ZELIEK_BOSS, + DATA_LADY_BLAUMEUX_BOSS, + DATA_THANE_KORTHAZZ_BOSS }; -ObjectData const gameObjectData[] = +static WorldLocation const SapphironTeleportPos { - { 0, 0 } + NaxxramasMapId, 3498.300049f, -5349.490234f, 144.968002f, 1.3698910f }; -class instance_naxxramas_combined : public InstanceMapScript +static DoorData const doorData[] +{ + { GO_PATCHWERK_GATE, BOSS_PATCHWERK, DOOR_TYPE_PASSAGE }, + { GO_PATCHWERK_GATE, BOSS_GROBBULUS, DOOR_TYPE_ROOM }, + { GO_GLUTH_GATE, BOSS_GLUTH, DOOR_TYPE_PASSAGE }, + { GO_THADDIUS_GATE, BOSS_GLUTH, DOOR_TYPE_PASSAGE }, + { GO_NOTH_ENTRY_GATE, BOSS_NOTH, DOOR_TYPE_ROOM }, + { GO_NOTH_EXIT_GATE, BOSS_NOTH, DOOR_TYPE_PASSAGE }, + { GO_HEIGAN_ENTRY_GATE, BOSS_NOTH, DOOR_TYPE_PASSAGE }, + { GO_HEIGAN_ENTRY_GATE, BOSS_HEIGAN, DOOR_TYPE_ROOM }, + { GO_HEIGAN_EXIT_GATE, BOSS_HEIGAN, DOOR_TYPE_PASSAGE }, + { GO_HEIGAN_EXIT_GATE_40, BOSS_HEIGAN, DOOR_TYPE_PASSAGE }, + { GO_LOATHEB_GATE, BOSS_HEIGAN, DOOR_TYPE_PASSAGE }, + { GO_LOATHEB_GATE, BOSS_LOATHEB, DOOR_TYPE_ROOM }, + { GO_PLAGUE_EYE_PORTAL, BOSS_LOATHEB, DOOR_TYPE_PASSAGE }, + { GO_PLAG_EYE_RAMP_BOSS, BOSS_LOATHEB, DOOR_TYPE_PASSAGE }, + { GO_ANUB_GATE, BOSS_ANUB, DOOR_TYPE_ROOM }, + { GO_ANUB_NEXT_GATE, BOSS_ANUB, DOOR_TYPE_PASSAGE }, + { GO_FAERLINA_WEB, BOSS_FAERLINA, DOOR_TYPE_ROOM }, + { GO_FAERLINA_GATE, BOSS_FAERLINA, DOOR_TYPE_PASSAGE }, + { GO_MAEXXNA_GATE, BOSS_FAERLINA, DOOR_TYPE_PASSAGE }, + { GO_MAEXXNA_GATE, BOSS_MAEXXNA, DOOR_TYPE_ROOM }, + { GO_SPIDER_EYE_PORTAL, BOSS_MAEXXNA, DOOR_TYPE_PASSAGE }, + { GO_ARAC_EYE_RAMP_BOSS, BOSS_MAEXXNA, DOOR_TYPE_PASSAGE }, + { GO_THADDIUS_GATE, BOSS_THADDIUS, DOOR_TYPE_ROOM }, + { GO_ABOM_EYE_PORTAL, BOSS_THADDIUS, DOOR_TYPE_PASSAGE }, + { GO_CONS_EYE_RAMP_BOSS, BOSS_THADDIUS, DOOR_TYPE_PASSAGE }, + { GO_GOTHIK_ENTER_GATE, BOSS_GOTHIK, DOOR_TYPE_ROOM }, + { GO_GOTHIK_INNER_GATE, BOSS_GOTHIK, DOOR_TYPE_ROOM }, + { GO_GOTHIK_EXIT_GATE, BOSS_GOTHIK, DOOR_TYPE_PASSAGE }, + { GO_HORSEMEN_GATE, BOSS_GOTHIK, DOOR_TYPE_PASSAGE }, + { GO_HORSEMEN_GATE, BOSS_HORSEMAN, DOOR_TYPE_ROOM }, + { GO_DEATHKNIGHT_EYE_PORTAL, BOSS_HORSEMAN, DOOR_TYPE_PASSAGE }, + { GO_MILI_EYE_RAMP_BOSS, BOSS_HORSEMAN, DOOR_TYPE_PASSAGE }, + { GO_KELTHUZAD_GATE, BOSS_KELTHUZAD, DOOR_TYPE_ROOM }, + { 0, 0, DOOR_TYPE_ROOM } +}; + +static ObjectData const creatureData[] +{ + { NPC_PATCHWERK, DATA_PATCHWERK_BOSS }, + { NPC_STALAGG, DATA_STALAGG_BOSS }, + { NPC_FEUGEN, DATA_FEUGEN_BOSS }, + { NPC_THADDIUS, DATA_THADDIUS_BOSS }, + { NPC_RAZUVIOUS, DATA_RAZUVIOUS_BOSS }, + { NPC_GOTHIK, DATA_GOTHIK_BOSS }, + { NPC_BARON_RIVENDARE, DATA_BARON_RIVENDARE_BOSS }, + { NPC_SIR_ZELIEK, DATA_SIR_ZELIEK_BOSS }, + { NPC_LADY_BLAUMEUX, DATA_LADY_BLAUMEUX_BOSS }, + { NPC_THANE_KORTHAZZ, DATA_THANE_KORTHAZZ_BOSS }, + { NPC_SAPPHIRON, DATA_SAPPHIRON_BOSS }, + { NPC_KELTHUZAD, DATA_KELTHUZAD_BOSS }, + { NPC_LICH_KING, DATA_LICH_KING_BOSS }, + { 0, 0 } +}; + +// overwrite the default ObjectData if Naxx40 +static ObjectData const creatureDataNX40[] +{ + { NPC_PATCHWERK_40, DATA_PATCHWERK_BOSS }, + { NPC_STALAGG_40, DATA_STALAGG_BOSS }, + { NPC_FEUGEN_40, DATA_FEUGEN_BOSS }, + { NPC_THADDIUS_40, DATA_THADDIUS_BOSS }, + { NPC_RAZUVIOUS_40, DATA_RAZUVIOUS_BOSS }, + { NPC_GOTHIK_40, DATA_GOTHIK_BOSS }, + { NPC_HIGHLORD_MOGRAINE_40, DATA_BARON_RIVENDARE_BOSS }, + { NPC_SIR_ZELIEK_40, DATA_SIR_ZELIEK_BOSS }, + { NPC_LADY_BLAUMEUX_40, DATA_LADY_BLAUMEUX_BOSS }, + { NPC_THANE_KORTHAZZ_40, DATA_THANE_KORTHAZZ_BOSS }, + { NPC_SAPPHIRON, DATA_SAPPHIRON_BOSS }, + { NPC_KELTHUZAD_40, DATA_KELTHUZAD_BOSS }, + // { NPC_LICH_KING, DATA_LICH_KING_BOSS }, + { 0, 0 } +}; + +static ObjectData const gameObjectData[] +{ + { GO_GOTHIK_INNER_GATE, DATA_GOTHIK_INNER_GATE }, + { GO_LOATHEB_PORTAL, DATA_LOATHEB_PORTAL }, + { GO_MAEXXNA_PORTAL, DATA_MAEXXNA_PORTAL }, + { GO_THADDIUS_PORTAL, DATA_THADDIUS_PORTAL }, + { GO_HORSEMAN_PORTAL, DATA_HORSEMAN_PORTAL }, + { GO_SAPPHIRON_GATE, DATA_SAPPHIRON_GATE }, + { GO_KELTHUZAD_FLOOR, DATA_KELTHUZAD_FLOOR }, + { GO_KELTHUZAD_GATE, DATA_KELTHUZAD_GATE }, + { GO_KELTHUZAD_PORTAL_1, DATA_KELTHUZAD_PORTAL_1 }, + { GO_KELTHUZAD_PORTAL_2, DATA_KELTHUZAD_PORTAL_2 }, + { GO_KELTHUZAD_PORTAL_3, DATA_KELTHUZAD_PORTAL_3 }, + { GO_KELTHUZAD_PORTAL_4, DATA_KELTHUZAD_PORTAL_4 }, + { 0, 0 } +}; + +class instance_naxxramas : public InstanceScript { public: - instance_naxxramas_combined() : InstanceMapScript("instance_naxxramas", 533) { } - - InstanceScript* GetInstanceScript(InstanceMap* pMap) const override + instance_naxxramas(Map* map) : InstanceScript(map) { - return new instance_naxxramas_combined_InstanceMapScript(pMap); - } + SetHeaders(DataHeader); + SetBossNumber(MAX_ENCOUNTERS); + SetPersistentDataCount(PERSISTENT_DATA_COUNT); + LoadDoorData(doorData); + LoadObjectData(creatureData, gameObjectData); - struct instance_naxxramas_combined_InstanceMapScript : public InstanceScript - { - explicit instance_naxxramas_combined_InstanceMapScript(Map* pMap) : InstanceScript(pMap) - { - SetHeaders(DataHeader); - SetBossNumber(MAX_ENCOUNTERS); - LoadObjectData(creatureData, gameObjectData); - for (auto& i : HeiganEruption) - i.clear(); + // NX40 specific data + if (instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC) + LoadObjectData(creatureDataNX40, gameObjectData); - // NPCs - PatchwerkRoomTrash.clear(); - HeiganBackRoomAdds.clear(); - - // Controls - _horsemanKilled = 0; - _speakTimer = 0; - _horsemanTimer = 0; - _screamTimer = 2 * MINUTE * IN_MILLISECONDS; - _hadThaddiusGreet = false; - _currentWingTaunt = SAY_FIRST_WING_TAUNT; - _horsemanLoadDoneState = false; - - // Achievements - abominationsKilled = 0; - faerlinaAchievement = true; - thaddiusAchievement = true; - loathebAchievement = true; - sapphironAchievement = true; - heiganAchievement = true; - immortalAchievement = 1; - } - - std::set HeiganEruption[4]; - std::set HeiganEruptionTunnel; - - // GOs - ObjectGuid _patchwerkGateGUID; - ObjectGuid _gluthGateGUID; - ObjectGuid _nothEntryGateGUID; - ObjectGuid _nothExitGateGUID; - ObjectGuid _heiganGateGUID; - ObjectGuid _heiganGateExitGUID; - ObjectGuid _heiganGateExitOldGUID; - ObjectGuid _loathebGateGUID; - ObjectGuid _anubGateGUID; - ObjectGuid _anubNextGateGUID; - ObjectGuid _faerlinaWebGUID; - ObjectGuid _faerlinaGateGUID; - ObjectGuid _maexxnaGateGUID; - ObjectGuid _thaddiusGateGUID; - ObjectGuid _gothikEnterGateGUID; - ObjectGuid _gothikInnerGateGUID; - ObjectGuid _gothikExitGateGUID{}; - ObjectGuid _horsemanGateGUID; - ObjectGuid _kelthuzadFloorGUID; - ObjectGuid _kelthuzadGateGUID; - ObjectGuid _kelthuzadPortal1GUID; - ObjectGuid _kelthuzadPortal2GUID; - ObjectGuid _kelthuzadPortal3GUID; - ObjectGuid _kelthuzadPortal4GUID; - ObjectGuid _sapphironGateGUID; - ObjectGuid _horsemanPortalGUID; - ObjectGuid _loathebPortalGUID; - ObjectGuid _maexxnaPortalGUID; - ObjectGuid _thaddiusPortalGUID; - ObjectGuid _deathknightEyePortalGUID; - ObjectGuid _plagueEyePortalGUID; - ObjectGuid _spiderEyePortalGUID; - ObjectGuid _abomEyePortalGUID; - ObjectGuid _deathknightGlowEyePortalGUID; - ObjectGuid _plagueGlowEyePortalGUID; - ObjectGuid _spiderGlowEyePortalGUID; - ObjectGuid _abomGlowEyePortalGUID; + // GameObjects + for (auto& i : _heiganEruption) + i.clear(); // NPCs - GuidList PatchwerkRoomTrash; - GuidList HeiganBackRoomAdds; - ObjectGuid _patchwerkGUID; - ObjectGuid _thaddiusGUID; - ObjectGuid _gothikGUID; - ObjectGuid _stalaggGUID; - ObjectGuid _feugenGUID; - ObjectGuid _zeliekGUID; - ObjectGuid _rivendareGUID; - ObjectGuid _blaumeuxGUID; - ObjectGuid _korthazzGUID; - ObjectGuid _sapphironGUID; - ObjectGuid _kelthuzadGUID; - ObjectGuid _lichkingGUID; + _patchwerkRoomTrash.clear(); + _heiganBackRoomTrash.clear(); // Controls - uint8 _horsemanKilled; - uint32 _speakTimer; - uint32 _horsemanTimer; - uint32 _screamTimer; - bool _hadThaddiusGreet; - EventMap events; - uint8 _currentWingTaunt; - bool _horsemanLoadDoneState; + _events.Reset(); + _currentWingTaunt = SAY_FIRST_WING_TAUNT; + _horsemanLoaded = 0; // Achievements - uint8 abominationsKilled; - bool faerlinaAchievement; - bool thaddiusAchievement; - bool loathebAchievement; - bool sapphironAchievement; - bool heiganAchievement; - uint32 immortalAchievement; + _abominationsKilled = 0; + _faerlinaAchievement = true; + _thaddiusAchievement = true; + _loathebAchievement = true; + _heiganAchievement = true; + _sapphironAchievement = true; + _horsemanAchievement = true; + } - void HeiganEruptSections(uint32 section) - { - for (uint8 i = 0; i < 4; ++i) - { - if (i == section) - continue; + inline void CreatureTalk(uint32 dataCreature, uint8 dialog) + { + if (Creature* creature = GetCreature(dataCreature)) + creature->AI()->Talk(dialog); + } - for (auto itr : HeiganEruption[i]) - { - itr->SendCustomAnim(itr->GetGoAnimProgress()); - itr->CastSpell(nullptr, SPELL_ERUPTION); - } + inline void SetGoState(uint32 dataGameObject, GOState state) + { + if (GameObject* go = GetGameObject(dataGameObject)) + go->SetGoState(state); + } - } - } + inline void ActivateWingPortal(GameObject* go, EncounterState state) + { + if (!go || state != DONE) + return; - void HeiganEruptSectionsTunnel() - { - // doesn't work - for (auto itr : HeiganEruptionTunnel) - { - itr->SendCustomAnim(itr->GetGoAnimProgress()); - itr->CastSpell(nullptr, SPELL_ERUPTION); - } - } + go->SetGoState(GO_STATE_ACTIVE); + go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + } - bool IsEncounterInProgress() const override - { - for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) - { - if (GetBossState(i) == IN_PROGRESS) - return true; - } - return false; - } - - void OnCreatureCreate(Creature* creature) override - { - - switch(creature->GetEntry()) - { - case NPC_ROTTING_MAGGOT_40: - HeiganBackRoomAdds.push_back(creature->GetGUID()); - return; - case NPC_DISEASED_MAGGOT_40: - HeiganBackRoomAdds.push_back(creature->GetGUID()); - return; - case NPC_EYE_STALK_40: - HeiganBackRoomAdds.push_back(creature->GetGUID()); - return; - case NPC_PATCHWERK: - _patchwerkGUID = creature->GetGUID(); - return; - case NPC_PATCHWORK_GOLEM: - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_BILE_RETCHER: - if (creature->GetPositionY() > -3258.0f) // we want only those inside the room, not before - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_SLUDGE_BELCHER: - if (creature->GetPositionY() > -3258.0f) // we want only those inside the room, not before - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_MAD_SCIENTIST: - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_LIVING_MONSTROSITY: - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_SURGICAL_ASSIST: - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_THADDIUS: - _thaddiusGUID = creature->GetGUID(); - return; - case NPC_STALAGG: - _stalaggGUID = creature->GetGUID(); - return; - case NPC_FEUGEN: - _feugenGUID = creature->GetGUID(); - return; - case NPC_GOTHIK: - _gothikGUID = creature->GetGUID(); - return; - case NPC_LADY_BLAUMEUX: - _blaumeuxGUID = creature->GetGUID(); - return; - case NPC_SIR_ZELIEK: - _zeliekGUID = creature->GetGUID(); - return; - case NPC_BARON_RIVENDARE: - _rivendareGUID = creature->GetGUID(); - return; - case NPC_THANE_KORTHAZZ: - _korthazzGUID = creature->GetGUID(); - return; - case NPC_SAPPHIRON: - _sapphironGUID = creature->GetGUID(); - return; - case NPC_KELTHUZAD: - _kelthuzadGUID = creature->GetGUID(); - return; - case NPC_LICH_KING: - _lichkingGUID = creature->GetGUID(); - return; - // Naxx 40 NPCs - case NPC_GOTHIK_40: - _gothikGUID = creature->GetGUID(); - return; - case NPC_PATCHWERK_40: - _patchwerkGUID = creature->GetGUID(); - return; - case NPC_PATCHWORK_GOLEM_40: - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_BILE_RETCHER_40: - if (creature->GetPositionY() > -3258.0f) // we want only those inside the room, not before - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_SLUDGE_BELCHER_40: - if (creature->GetPositionY() > -3258.0f) // we want only those inside the room, not before - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_MAD_SCIENTIST_40: - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_LIVING_MONSTROSITY_40: - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_SURGICAL_ASSIST_40: - PatchwerkRoomTrash.push_back(creature->GetGUID()); - return; - case NPC_THADDIUS_40: - _thaddiusGUID = creature->GetGUID(); - return; - case NPC_STALAGG_40: - _stalaggGUID = creature->GetGUID(); - return; - case NPC_FEUGEN_40: - _feugenGUID = creature->GetGUID(); - return; - case NPC_LADY_BLAUMEUX_40: - _blaumeuxGUID = creature->GetGUID(); - return; - case NPC_SIR_ZELIEK_40: - _zeliekGUID = creature->GetGUID(); - return; - case NPC_HIGHLORD_MOGRAINE_40: - _rivendareGUID = creature->GetGUID(); - return; - case NPC_THANE_KORTHAZZ_40: - _korthazzGUID = creature->GetGUID(); - return; - case NPC_SAPPHIRON_40: - _sapphironGUID = creature->GetGUID(); - return; - case NPC_KELTHUZAD_40: - _kelthuzadGUID = creature->GetGUID(); - return; - case NPC_ARCHMAGE_TARSIS: - creature->SetStandState(UNIT_STAND_STATE_DEAD); - return; - } - - InstanceScript::OnCreatureCreate(creature); - } - - void OnGameObjectCreate(GameObject* pGo) override - { - if (pGo->GetGOInfo()->displayId == 6785 || pGo->GetGOInfo()->displayId == 1287) - { - HeiganEruption[GetEruptionSection(pGo->GetPositionX(), pGo->GetPositionY())].insert(pGo); - return; - } - if (pGo->GetGOInfo()->entry == 361001) - { - HeiganEruptionTunnel.insert(pGo); - return; - } - - switch(pGo->GetEntry()) - { - case GO_PATCHWERK_GATE: - _patchwerkGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_PATCHWERK) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_GLUTH_GATE: - _gluthGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_GLUTH) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_NOTH_ENTRY_GATE: - _nothEntryGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_NOTH) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_NOTH_EXIT_GATE: - _nothExitGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_NOTH) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_HEIGAN_ENTRY_GATE: - _heiganGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_HEIGAN) == DONE || GetBossState(BOSS_NOTH) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_HEIGAN_EXIT_GATE: - _heiganGateExitGUID = pGo->GetGUID(); - if (GetBossState(BOSS_HEIGAN) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_HEIGAN_EXIT_GATE_OLD: - _heiganGateExitOldGUID = pGo->GetGUID(); - if (GetBossState(BOSS_HEIGAN) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_LOATHEB_GATE: - _loathebGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_LOATHEB) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_ANUB_GATE: - _anubGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_ANUB) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_ANUB_NEXT_GATE: - _anubNextGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_ANUB) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_FAERLINA_GATE: - _faerlinaGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_FAERLINA) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_FAERLINA_WEB: - _faerlinaWebGUID = pGo->GetGUID(); - if (GetBossState(BOSS_FAERLINA) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_MAEXXNA_GATE: - _maexxnaGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_FAERLINA) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_THADDIUS_GATE: - _thaddiusGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_GLUTH) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_GOTHIK_ENTER_GATE: - _gothikEnterGateGUID = pGo->GetGUID(); - break; - case GO_GOTHIK_INNER_GATE: - _gothikInnerGateGUID = pGo->GetGUID(); - break; - case GO_GOTHIK_EXIT_GATE: - _gothikExitGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_GOTHIK) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_HORSEMEN_GATE: - _horsemanGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_GOTHIK) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_KELTHUZAD_FLOOR: - _kelthuzadFloorGUID = pGo->GetGUID(); - break; - case GO_KELTHUZAD_GATE: - _kelthuzadGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_SAPPHIRON) == DONE && _speakTimer == 0) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_KELTHUZAD_PORTAL_1: - _kelthuzadPortal1GUID = pGo->GetGUID(); - break; - case GO_KELTHUZAD_PORTAL_2: - _kelthuzadPortal2GUID = pGo->GetGUID(); - break; - case GO_KELTHUZAD_PORTAL_3: - _kelthuzadPortal3GUID = pGo->GetGUID(); - break; - case GO_KELTHUZAD_PORTAL_4: - _kelthuzadPortal4GUID = pGo->GetGUID(); - break; - case GO_SAPPHIRON_GATE: - _sapphironGateGUID = pGo->GetGUID(); - if (GetBossState(BOSS_SAPPHIRON) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_LOATHEB_PORTAL: - _loathebPortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_LOATHEB) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - pGo->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - break; - case GO_THADDIUS_PORTAL: - _thaddiusPortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_THADDIUS) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - pGo->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - break; - case GO_MAEXXNA_PORTAL: - _maexxnaPortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_MAEXXNA) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - pGo->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - break; - case GO_HORSEMAN_PORTAL: - _horsemanPortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_HORSEMAN) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - pGo->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - break; - - // Glow portals at center-side - case GO_DEATHKNIGHT_EYE_PORTAL: - _deathknightEyePortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_HORSEMAN) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_PLAGUE_EYE_PORTAL: - _plagueEyePortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_LOATHEB) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_SPIDER_EYE_PORTAL: - _spiderEyePortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_MAEXXNA) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_ABOM_EYE_PORTAL: - _abomEyePortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_THADDIUS) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - - // Glow portals at boss-side - case GO_MILI_EYE_RAMP_BOSS: - _deathknightGlowEyePortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_HORSEMAN) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_PLAG_EYE_RAMP_BOSS: - _plagueGlowEyePortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_LOATHEB) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_ARAC_EYE_RAMP_BOSS: - _spiderGlowEyePortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_MAEXXNA) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - case GO_CONS_EYE_RAMP_BOSS: - _abomGlowEyePortalGUID = pGo->GetGUID(); - if (GetBossState(BOSS_THADDIUS) == DONE) - { - pGo->SetGoState(GO_STATE_ACTIVE); - } - break; - } - - InstanceScript::OnGameObjectCreate(pGo); - } - - void OnGameObjectRemove(GameObject* pGo) override - { - if (pGo->GetGOInfo()->displayId == 6785 || pGo->GetGOInfo()->displayId == 1287) - { - uint32 section = GetEruptionSection(pGo->GetPositionX(), pGo->GetPositionY()); - HeiganEruption[section].erase(pGo); - return; - } - if (pGo->GetEntry() == GO_SAPPHIRON_BIRTH) - { - if (Creature* cr = instance->GetCreature(_sapphironGUID)) - { - cr->AI()->DoAction(ACTION_SAPPHIRON_BIRTH); - } - } - } - - bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override - { - if (instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC) - { - return false; // No achievements in Naxx 40 - } - switch (criteria_id) - { - case 7600: // And They Would All Go Down Together (10 player) - case 7601: // And They Would All Go Down Together (25 player) - return (_horsemanTimer < 15 * IN_MILLISECONDS); - case 7614: // Just Can't Get Enough (10 player) - case 7615: // Just Can't Get Enough (25 player) - return abominationsKilled >= 18; - case 7265: // Momma Said Knock You Out (10 player) - case 7549: // Momma Said Knock You Out (25 player) - return faerlinaAchievement; - case 7604: // Shocking! (10 player) - case 7605: // Shocking! (25 player) - return thaddiusAchievement; - case 7612: // Spore Loser (10 player) - case 7613: // Spore Loser (25 player) - return loathebAchievement; - case 7264: // The Safety Dance (10 player) - case 7548: // The Safety Dance (25 player) - return heiganAchievement; - case 7608: // Subtraction (10 player) - // The Dedicated few (10 player) - case 6802: - case 7146: - case 7147: - case 7148: - case 7149: - case 7150: - case 7151: - case 7152: - case 7153: - case 7154: - case 7155: - case 7156: - case 7157: - case 7158: - return (instance->GetPlayersCountExceptGMs() < 9); - case 7609: // Subtraction (25 player) - // The Dedicated few (25 player) - case 7159: - case 7160: - case 7161: - case 7162: - case 7163: - case 7164: - case 7165: - case 7166: - case 7167: - case 7168: - case 7169: - case 7170: - case 7171: - case 7172: - return (instance->GetPlayersCountExceptGMs() < 21); - case 7567: // The Hundred Club (10 player) - case 7568: // The Hundred Club (25 player) - return sapphironAchievement; - // The Undying - case 7617: - case 13237: - case 13238: - case 13239: - case 13240: - // The Immortal - case 7616: - case 13233: - case 13234: - case 13235: - case 13236: - { - uint8 count = 0; - for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) - { - if (GetBossState(i) == NOT_STARTED) - ++count; - } - return !count && immortalAchievement; - } - - default: - return false; - } - } - - void SetData(uint32 id, uint32 data) override - { - switch(id) - { - case DATA_ABOMINATION_KILLED: - abominationsKilled++; - return; - case DATA_FRENZY_REMOVED: - faerlinaAchievement = false; - return; - case DATA_CHARGES_CROSSED: - thaddiusAchievement = false; - return; - case DATA_SPORE_KILLED: - loathebAchievement = false; - return; - case DATA_HUNDRED_CLUB: - sapphironAchievement = false; - return; - case DATA_DANCE_FAIL: - heiganAchievement = false; - return; - case DATA_IMMORTAL_FAIL: - immortalAchievement = 0; - SaveToDB(); - return; - case DATA_HEIGAN_ERUPTION: - HeiganEruptSections(data); - return; - case DATA_HEIGAN_ERUPTION_TUNNEL_40: - HeiganEruptSectionsTunnel(); - return; - case DATA_HAD_THADDIUS_GREET: - _hadThaddiusGreet = (data == 1); - default: - return; - } - } - - uint32 GetData(uint32 id) const override - { - if (id == DATA_HAD_THADDIUS_GREET && _hadThaddiusGreet) - return 1; + inline void ActivateWingPortal(uint32 wingPortal) + { + ActivateWingPortal(GetGameObject(wingPortal), DONE); + _events.RescheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6s); + } + static inline uint8 GetEruptionSection(float x, float y) + { + y -= HeiganPos[1]; + if (y < 1.0f) return 0; - } - bool AreAllWingsCleared() const - { - return (GetBossState(BOSS_MAEXXNA) == DONE) && (GetBossState(BOSS_LOATHEB) == DONE) && (GetBossState(BOSS_THADDIUS) == DONE) && (GetBossState(BOSS_HORSEMAN) == DONE); - } + x -= HeiganPos[0]; + if (x > -1.0f) + return 3; - bool CheckRequiredBosses(uint32 bossId, Player const* /* player */) const override + float slope = y / x; + for (uint32 i = 0; i < 3; ++i) + if (slope > HeiganEruptionSlope[i]) + return i; + + return 3; + } + + inline void HeiganEruptSections(uint32 section) + { + for (uint8 i = 0; i < HeiganEruptSectionCount; ++i) { - switch (bossId) + if (i == section) + continue; + + for (GameObject* go : _heiganEruption[i]) { - case BOSS_SAPPHIRON: - if (!AreAllWingsCleared()) - { + go->SendCustomAnim(go->GetGoAnimProgress()); + go->CastSpell(nullptr, SPELL_ERUPTION); + } + } + } + + void OnPlayerEnter(Player* player) override + { + InstanceScript::OnPlayerEnter(player); + + _events.ScheduleEvent(EVENT_THADDIUS_SCREAMS, 2min, 2min + 30s); + } + + void OnCreatureCreate(Creature* creature) override + { + switch (creature->GetEntry()) + { + case NPC_ROTTING_MAGGOT_40: + case NPC_DISEASED_MAGGOT_40: + case NPC_EYE_STALK_40: + _heiganBackRoomTrash.push_back(creature->GetGUID()); + return; + case NPC_LIVING_MONSTROSITY: + case NPC_MAD_SCIENTIST: + case NPC_PATCHWORK_GOLEM: + case NPC_SURGICAL_ASSIST: + case NPC_LIVING_MONSTROSITY_40: + case NPC_MAD_SCIENTIST_40: + case NPC_PATCHWORK_GOLEM_40: + case NPC_SURGICAL_ASSIST_40: + _patchwerkRoomTrash.push_back(creature->GetGUID()); + return; + case NPC_BILE_RETCHER: + case NPC_SLUDGE_BELCHER: + case NPC_BILE_RETCHER_40: + case NPC_SLUDGE_BELCHER_40: + if (creature->GetPositionY() > -3258.0f) // we want only those inside the room, not before + _patchwerkRoomTrash.push_back(creature->GetGUID()); + return; + case NPC_BARON_RIVENDARE: + case NPC_SIR_ZELIEK: + case NPC_LADY_BLAUMEUX: + case NPC_THANE_KORTHAZZ: + case NPC_HIGHLORD_MOGRAINE_40: + case NPC_SIR_ZELIEK_40: + case NPC_LADY_BLAUMEUX_40: + case NPC_THANE_KORTHAZZ_40: + if (++_horsemanLoaded == HorsemanCount) + SetBossState(BOSS_HORSEMAN, GetBossState(BOSS_HORSEMAN)); + break; + default: + break; + } + + InstanceScript::OnCreatureCreate(creature); + } + + void OnGameObjectCreate(GameObject* go) override + { + switch (go->GetGOInfo()->displayId) + { + case GO_DISPLAY_ID_HEIGAN_ERUPTION1: + case GO_DISPLAY_ID_HEIGAN_ERUPTION2: + _heiganEruption[GetEruptionSection(go->GetPositionX(), go->GetPositionY())].insert(go); + break; + default: + break; + } + + switch (go->GetEntry()) + { + case GO_SAPPHIRON_GATE: + if (GetBossState(BOSS_SAPPHIRON) == DONE) + go->SetGoState(GO_STATE_ACTIVE); + break; + case GO_LOATHEB_PORTAL: + ActivateWingPortal(go, GetBossState(BOSS_LOATHEB)); + break; + case GO_THADDIUS_PORTAL: + ActivateWingPortal(go, GetBossState(BOSS_THADDIUS)); + break; + case GO_MAEXXNA_PORTAL: + ActivateWingPortal(go, GetBossState(BOSS_MAEXXNA)); + break; + case GO_HORSEMAN_PORTAL: + ActivateWingPortal(go, GetBossState(BOSS_HORSEMAN)); + break; + default: + break; + } + + InstanceScript::OnGameObjectCreate(go); + } + + void OnGameObjectRemove(GameObject* go) override + { + switch (go->GetGOInfo()->displayId) + { + case GO_DISPLAY_ID_HEIGAN_ERUPTION1: + case GO_DISPLAY_ID_HEIGAN_ERUPTION2: + _heiganEruption[GetEruptionSection(go->GetPositionX(), go->GetPositionY())].erase(go); + break; + default: + break; + } + + switch (go->GetEntry()) + { + case GO_SAPPHIRON_BIRTH: + if (Creature* cr = GetCreature(DATA_SAPPHIRON_BOSS)) + cr->AI()->DoAction(ACTION_SAPPHIRON_BIRTH); + break; + default: + break; + } + + InstanceScript::OnGameObjectRemove(go); + } + + bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override + { + switch (criteria_id) + { + case ACHIEV_CRITERIA_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER_10_PLAYER: + case ACHIEV_CRITERIA_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER_25_PLAYER: + return _horsemanAchievement; + case ACHIEV_CRITERIA_JUST_CANT_GET_ENOUGH_10_PLAYER: + case ACHIEV_CRITERIA_JUST_CANT_GET_ENOUGH_25_PLAYER: + return _abominationsKilled >= AbominationKillCountReq; + case ACHIEV_CRITERIA_MOMMA_SAID_KNOCK_YOU_OUT_10_PLAYER: + case ACHIEV_CRITERIA_MOMMA_SAID_KNOCK_YOU_OUT_25_PLAYER: + return _faerlinaAchievement; + case ACHIEV_CRITERIA_SHOKING_10_PLAYER: + case ACHIEV_CRITERIA_SHOKING_25_PLAYER: + return _thaddiusAchievement; + case ACHIEV_CRITERIA_SPORE_LOSER_10_PLAYER: + case ACHIEV_CRITERIA_SPORE_LOSER_25_PLAYER: + return _loathebAchievement; + case ACHIEV_CRITERIA_THE_SAFETY_DANCE_10_PLAYER: + case ACHIEV_CRITERIA_THE_SAFETY_DANCE_25_PLAYER: + return _heiganAchievement; + case ACHIEV_CRITERIA_SUBTRACTION_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_KELTHUZAD_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GOTHIK_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_ANUB_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GROBBULUS_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_HEIGAN_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_FAERLINA_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_MAEXXNA_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_SAPPHIRON_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_LOATHEB_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GLUTH_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_THADDIUS_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_PATCHWERK_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_RAZUVIOUS_10_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_NOTH_10_PLAYER: + return instance->GetPlayersCountExceptGMs() < TheDedicatedFew10PlayerReq; + case ACHIEV_CRITERIA_SUBTRACTION_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_ANUB_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_FAERLINA_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_MAEXXNA_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_PATCHWERK_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GROBBULUS_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GLUTH_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_THADDIUS_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_NOTH_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_HEIGAN_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_LOATHEB_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_RAZUVIOUS_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_GOTHIK_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_SAPPHIRON_25_PLAYER: + case ACHIEV_CRITERIA_THE_DEDICATED_FEW_KELTHUZAD_25_PLAYER: + return instance->GetPlayersCountExceptGMs() < TheDedicatedFew25PlayerReq; + case ACHIEV_CRITERIA_THE_HUNDRED_CLUB_10_PLAYER: + case ACHIEV_CRITERIA_THE_HUNDRED_CLUB_25_PLAYER: + return _sapphironAchievement; + case ACHIEV_CRITERIA_THE_UNDYING_KELTHUZAD: + case ACHIEV_CRITERIA_THE_UNDYING_THE_FOUR_HORSEMEN: + case ACHIEV_CRITERIA_THE_UNDYING_MAEXXNA: + case ACHIEV_CRITERIA_THE_UNDYING_LOATHEB: + case ACHIEV_CRITERIA_THE_UNDYING_THADDIUS: + case ACHIEV_CRITERIA_THE_IMMORTAL_KELTHUZAD: + case ACHIEV_CRITERIA_THE_IMMORTAL_THE_FOUR_HORSEMEN: + case ACHIEV_CRITERIA_THE_IMMORTAL_MAEXXNA: + case ACHIEV_CRITERIA_THE_IMMORTAL_LOATHEB: + case ACHIEV_CRITERIA_THE_IMMORTAL_THADDIUS: + for (int i = 0; i < MAX_ENCOUNTERS; ++i) + if (GetBossState(i) != DONE) return false; - } - break; - default: - break; - } - return true; - } - void Load(const char* data) override - { - _horsemanLoadDoneState = true; - InstanceScript::Load(data); - if (GetBossState(BOSS_HORSEMAN) != DONE) - { - _horsemanLoadDoneState = false; - } - } - - bool SetBossState(uint32 bossId, EncounterState state) override - { - // pull all the trash if not killed - if (bossId == BOSS_PATCHWERK && state == IN_PROGRESS) - { - if (Creature* patch = instance->GetCreature(_patchwerkGUID)) - { - for (auto& itr : PatchwerkRoomTrash) - { - Creature* trash = ObjectAccessor::GetCreature(*patch, itr); - if (trash && trash->IsAlive() && !trash->IsInCombat()) - { - trash->AI()->AttackStart(patch->GetVictim()); - } - } - } - } - - // Horseman handling - if (bossId == BOSS_HORSEMAN) - { - if (state == DONE && !_horsemanLoadDoneState) - { - _horsemanTimer++; - _horsemanKilled++; - if (_horsemanKilled < 4) - { - return false; - } - // All horsemans are killed - if (Creature* cr = instance->GetCreature(_blaumeuxGUID)) - { - cr->CastSpell(cr, 59450, true); // credit - } - } - - // respawn - else if (state == NOT_STARTED && _horsemanKilled > 0) - { - Creature* cr; - _horsemanKilled = 0; - if ((cr = instance->GetCreature(_blaumeuxGUID))) - { - if (!cr->IsAlive()) - { - cr->SetPosition(cr->GetHomePosition()); - cr->Respawn(); - } - } - if ((cr = instance->GetCreature(_rivendareGUID))) - { - if (!cr->IsAlive()) - { - cr->SetPosition(cr->GetHomePosition()); - cr->Respawn(); - } - } - if ((cr = instance->GetCreature(_zeliekGUID))) - { - if (!cr->IsAlive()) - { - cr->SetPosition(cr->GetHomePosition()); - cr->Respawn(); - } - } - if ((cr = instance->GetCreature(_korthazzGUID))) - { - if (!cr->IsAlive()) - { - cr->SetPosition(cr->GetHomePosition()); - cr->Respawn(); - } - } - } - else if (state == IN_PROGRESS) - { - Creature* cr; - if ((cr = instance->GetCreature(_blaumeuxGUID))) - { - cr->SetInCombatWithZone(); - } - if ((cr = instance->GetCreature(_rivendareGUID))) - { - cr->SetInCombatWithZone(); - } - if ((cr = instance->GetCreature(_zeliekGUID))) - { - cr->SetInCombatWithZone(); - } - if ((cr = instance->GetCreature(_korthazzGUID))) - { - cr->SetInCombatWithZone(); - } - } - - if (state == NOT_STARTED) - { - _horsemanTimer = 0; - } - } - - if (!InstanceScript::SetBossState(bossId, state)) + return !GetPersistentData(PERSISTENT_DATA_IMMORTAL_FAIL); + default: return false; + } + } - // Bosses data - switch(bossId) + void SetData(uint32 id, uint32 data) override + { + switch (id) + { + case DATA_ABOMINATION_KILLED: + ++_abominationsKilled; + return; + case DATA_FRENZY_REMOVED: + _faerlinaAchievement = false; + return; + case DATA_CHARGES_CROSSED: + _thaddiusAchievement = false; + return; + case DATA_SPORE_KILLED: + _loathebAchievement = false; + return; + case DATA_HUNDRED_CLUB: + _sapphironAchievement = false; + return; + case DATA_DANCE_FAIL: + _heiganAchievement = false; + return; + case DATA_HEIGAN_ERUPTION: + HeiganEruptSections(data); + return; + default: + return; + } + } + + bool SetBossState(uint32 bossId, EncounterState state) override + { + switch (bossId) + { + case BOSS_PATCHWERK: { - case BOSS_KELTHUZAD: - if (state == NOT_STARTED) - { - abominationsKilled = 0; - } + if (state != IN_PROGRESS) break; - case BOSS_FAERLINA: - if (state == NOT_STARTED) - { - faerlinaAchievement = true; - } - break; - case BOSS_THADDIUS: - if (state == NOT_STARTED) - { - thaddiusAchievement = true; - } - break; - case BOSS_LOATHEB: - if (state == NOT_STARTED) - { - loathebAchievement = true; - } - break; - case BOSS_HEIGAN: - if (state == NOT_STARTED) - { - heiganAchievement = true; - } - break; - case BOSS_SAPPHIRON: - if (state == DONE) - { - _speakTimer = 1; - // Load KT's grid so he can talk - instance->LoadGrid(3763.43f, -5115.87f); - } - else if (state == NOT_STARTED) - { - sapphironAchievement = true; - } - break; - default: - break; - } - // Save instance and open gates - if (state == DONE) - { - SaveToDB(); - - switch (bossId) + // pull all the trash if not killed + if (Creature* patchwerk = GetCreature(DATA_PATCHWERK_BOSS)) { - case BOSS_PATCHWERK: - if (GameObject* go = instance->GetGameObject(_patchwerkGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } + for (auto& itr : _patchwerkRoomTrash) + { + Creature* trash = ObjectAccessor::GetCreature(*patchwerk, itr); + if (trash && trash->IsAlive() && !trash->IsInCombat()) + trash->AI()->AttackStart(patchwerk->GetVictim()); + } + } + + break; + } + case BOSS_HEIGAN: + { + if (state == NOT_STARTED) + _heiganAchievement = true; + + if (state == DONE) + for (auto const& guid : _heiganBackRoomTrash) + if (Creature* creature = instance->GetCreature(guid)) + creature->DespawnOrUnsummon(); + break; + } + case BOSS_LOATHEB: + { + switch (state) + { + case NOT_STARTED: + _loathebAchievement = true; break; - case BOSS_GLUTH: - if (GameObject* go = instance->GetGameObject(_gluthGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_thaddiusGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - break; - case BOSS_NOTH: - if (GameObject* go = instance->GetGameObject(_nothExitGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_heiganGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - break; - case BOSS_HEIGAN: - for (auto& mobGUID : HeiganBackRoomAdds) - { - if (Creature* mob = instance->GetCreature(mobGUID)) - { - mob->DespawnOrUnsummon(); - } - } - if (GameObject* go = instance->GetGameObject(_heiganGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_heiganGateExitGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_heiganGateExitOldGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - break; - case BOSS_LOATHEB: - if (GameObject* go = instance->GetGameObject(_loathebGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_loathebPortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - if (GameObject* go = instance->GetGameObject(_plagueEyePortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_plagueGlowEyePortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000); - break; - case BOSS_ANUB: - if (GameObject* go = instance->GetGameObject(_anubGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_anubNextGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - break; - case BOSS_FAERLINA: - if (GameObject* go = instance->GetGameObject(_faerlinaGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_maexxnaGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - break; - case BOSS_MAEXXNA: - if (GameObject* go = instance->GetGameObject(_maexxnaGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_maexxnaPortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - if (GameObject* go = instance->GetGameObject(_spiderEyePortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_spiderGlowEyePortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000); - break; - case BOSS_GOTHIK: - if (GameObject* go = instance->GetGameObject(_gothikEnterGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_gothikExitGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_horsemanGateGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - break; - case BOSS_SAPPHIRON: - events.ScheduleEvent(EVENT_FROSTWYRM_WATERFALL_DOOR, 5000); - break; - case BOSS_THADDIUS: - if (GameObject* go = instance->GetGameObject(_thaddiusPortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - if (GameObject* go = instance->GetGameObject(_abomEyePortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_abomGlowEyePortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000); - break; - case BOSS_HORSEMAN: - if (GameObject* go = instance->GetGameObject(_horsemanPortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); - } - if (GameObject* go = instance->GetGameObject(_deathknightEyePortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - if (GameObject* go = instance->GetGameObject(_deathknightGlowEyePortalGUID)) - { - go->SetGoState(GO_STATE_ACTIVE); - } - events.ScheduleEvent(EVENT_KELTHUZAD_WING_TAUNT, 6000); + case DONE: + ActivateWingPortal(DATA_LOATHEB_PORTAL); break; default: break; } - } - return true; - } - void Update(uint32 diff) override - { - if (_speakTimer) + break; + } + case BOSS_FAERLINA: { - Creature* kel = instance->GetCreature(_kelthuzadGUID); - Creature* lich = instance->GetCreature(_lichkingGUID); - if (kel && lich) + if (state == NOT_STARTED) + _faerlinaAchievement = true; + + break; + } + case BOSS_MAEXXNA: + { + if (state == DONE) + ActivateWingPortal(DATA_MAEXXNA_PORTAL); + + break; + } + case BOSS_THADDIUS: + { + switch (state) { - _speakTimer += diff; + case NOT_STARTED: + _thaddiusAchievement = true; + break; + case DONE: + ActivateWingPortal(DATA_THADDIUS_PORTAL); + break; + default: + break; } - else + + break; + } + case BOSS_HORSEMAN: + { + uint32 horsemanKilled = std::count_if(HorsemanDataGroup.begin(), HorsemanDataGroup.end(), [this](auto&& entry) { - return; - } - if (_speakTimer > 10000 && _speakTimer < 20000) + Creature* cr = GetCreature(entry); + return cr && !cr->IsAlive(); + }); + + switch (state) { - kel->AI()->Talk(SAY_SAPP_DIALOG1); - _speakTimer = 20000; - } - else if (_speakTimer > 30000 && _speakTimer < 40000) - { - lich->AI()->Talk(SAY_SAPP_DIALOG2_LICH); - _speakTimer = 40000; - } - else if (_speakTimer > 54000 && _speakTimer < 60000) - { - kel->AI()->Talk(SAY_SAPP_DIALOG3); - _speakTimer = 60000; - } - else if (_speakTimer > 70000 && _speakTimer < 80000) - { - lich->AI()->Talk(SAY_SAPP_DIALOG4_LICH); - _speakTimer = 80000; - } - else if (_speakTimer > 92000 && _speakTimer < 100000) - { - kel->AI()->Talk(SAY_SAPP_DIALOG5); - _speakTimer = 100000; - } - else if (_speakTimer > 105000) - { - kel->AI()->Talk(SAY_SAPP_DIALOG6); - _speakTimer = 0; - if (GameObject* go = instance->GetGameObject(_kelthuzadGateGUID)) + case NOT_STARTED: { - go->SetGoState(GO_STATE_ACTIVE); + _horsemanAchievement = true; + + if (!horsemanKilled) + break; + + for (auto&& entry : HorsemanDataGroup) + { + if (Creature* cr = GetCreature(entry)) + { + cr->SetPosition(cr->GetHomePosition()); + cr->Respawn(); + } + } + + break; } - } - } - - // And They would all - if (_horsemanTimer) - { - _horsemanTimer += diff; - } - - if (_screamTimer && GetBossState(BOSS_THADDIUS) != DONE) - { - if (_screamTimer <= diff) - { - instance->PlayDirectSoundToMap(SOUND_SCREAM + urand(0, 3)); - _screamTimer = (2 * MINUTE + urand(0, 30)) * IN_MILLISECONDS; - } - else - { - _screamTimer -= diff; - } - } - - events.Update(diff); - switch (events.ExecuteEvent()) - { - case EVENT_KELTHUZAD_WING_TAUNT: - // Loads Kel'Thuzad's grid. We need this as he must be active in order for his texts to work. - instance->LoadGrid(3749.67f, -5114.06f); - if (Creature* kelthuzad = instance->GetCreature(_kelthuzadGUID)) + case IN_PROGRESS: { - kelthuzad->AI()->Talk(_currentWingTaunt); + for (auto&& entry : HorsemanDataGroup) + if (Creature* cr = GetCreature(entry)) + cr->SetInCombatWithZone(); + + break; } - ++_currentWingTaunt; - break; - case EVENT_FROSTWYRM_WATERFALL_DOOR: - if (GameObject* go = instance->GetGameObject(_sapphironGateGUID)) + case DONE: { - go->SetGoState(GO_STATE_ACTIVE); + _events.RescheduleEvent(EVENT_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER, 15s); + + if (horsemanKilled != HorsemanCount) + return false; + + // all horsemans are killed + if (Creature* cr = GetCreature(DATA_BARON_RIVENDARE_BOSS)) + cr->CastSpell(cr, SPELL_THE_FOUR_HORSEMAN_CREDIT, true); + + ActivateWingPortal(DATA_HORSEMAN_PORTAL); + break; } - break; + default: + break; + } + + break; } - } - - ObjectGuid GetGuidData(uint32 id) const override - { - switch (id) + case BOSS_SAPPHIRON: { - // GameObjects - case DATA_HEIGAN_ENTER_GATE: - return _heiganGateGUID; - case DATA_HEIGAN_EXIT_GATE_OLD_40: - return _heiganGateExitOldGUID; - case DATA_HEIGAN_EXIT_GATE_40: - return _heiganGateExitGUID; - case DATA_LOATHEB_GATE: - return _loathebGateGUID; - case DATA_ANUB_GATE: - return _anubGateGUID; - case DATA_FAERLINA_WEB: - return _faerlinaWebGUID; - case DATA_MAEXXNA_GATE: - return _maexxnaGateGUID; - case DATA_GOTHIK_ENTER_GATE: - return _gothikEnterGateGUID; - case DATA_GOTHIK_INNER_GATE: - return _gothikInnerGateGUID; - case DATA_GOTHIK_EXIT_GATE: - return _gothikExitGateGUID; - case DATA_HORSEMEN_GATE: - return _horsemanGateGUID; - case DATA_THADDIUS_GATE: - return _thaddiusGateGUID; - case DATA_NOTH_ENTRY_GATE: - return _nothEntryGateGUID; - case DATA_KELTHUZAD_FLOOR: - return _kelthuzadFloorGUID; - case DATA_KELTHUZAD_GATE: - return _kelthuzadGateGUID; - case DATA_KELTHUZAD_PORTAL_1: - return _kelthuzadPortal1GUID; - case DATA_KELTHUZAD_PORTAL_2: - return _kelthuzadPortal2GUID; - case DATA_KELTHUZAD_PORTAL_3: - return _kelthuzadPortal3GUID; - case DATA_KELTHUZAD_PORTAL_4: - return _kelthuzadPortal4GUID; + switch (state) + { + case NOT_STARTED: + _sapphironAchievement = true; + break; + case DONE: + { + if (GetPersistentData(PERSISTENT_DATA_KELTHUZAD_DIALOG)) + break; - // NPCs - case DATA_THADDIUS_BOSS: - return _thaddiusGUID; - case DATA_STALAGG_BOSS: - return _stalaggGUID; - case DATA_FEUGEN_BOSS: - return _feugenGUID; - case DATA_GOTHIK_BOSS: - return _gothikGUID; - case DATA_LICH_KING_BOSS: - return _lichkingGUID; - default: - break; - } - - return ObjectGuid::Empty; - } - - void ReadSaveDataMore(std::istringstream& data) override - { - data >> immortalAchievement; + StorePersistentData(PERSISTENT_DATA_KELTHUZAD_DIALOG, 1); + SetGoState(DATA_KELTHUZAD_GATE, GO_STATE_READY); + _events.ScheduleEvent(EVENT_FROSTWYRM_WATERFALL_DOOR, 5s); + break; } + default: + break; + } - void WriteSaveDataMore(std::ostringstream& data) override + break; + } + case BOSS_KELTHUZAD: { - data << immortalAchievement; + if (state == NOT_STARTED) + _abominationsKilled = 0; + + break; + } + default: + break; } - }; -}; -class boss_naxxramas_misc : public CreatureScript -{ -public: - boss_naxxramas_misc() : CreatureScript("boss_naxxramas_misc") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); + return InstanceScript::SetBossState(bossId, state); } - struct boss_naxxramas_miscAI : public NullCreatureAI + void Update(uint32 diff) override { - explicit boss_naxxramas_miscAI(Creature* c) : NullCreatureAI(c) - { - timer = 0; - } + _events.Update(diff); - uint32 timer; - - void JustDied(Unit* /*killer*/) override + switch (_events.ExecuteEvent()) { - if (me->GetEntry() == NPC_MR_BIGGLESWORTH && me->GetInstanceScript()) + case EVENT_THADDIUS_SCREAMS: { - if (Creature* cr = me->SummonCreature(20350/*NPC_KELTHUZAD*/, *me, TEMPSUMMON_TIMED_DESPAWN, 1)) - { - cr->SetDisplayId(11686); - cr->AI()->Talk(SAY_CAT_DIED); - } - } - } + if (GetBossState(BOSS_THADDIUS) == DONE) + break; - void UpdateAI(uint32 diff) override - { - if (me->GetEntry() == NPC_NAXXRAMAS_TRIGGER) - { - timer += diff; - if (timer >= 5000) - { - if (Creature* cr = me->SummonCreature(NPC_LIVING_POISON, 3128.59, -3118.81, 293.346, 4.76754, TEMPSUMMON_TIMED_DESPAWN, 15200)) - { - cr->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); - cr->GetMotionMaster()->MovePoint(0, 3130.322, -3156.51, 293.324, false); - } - if (Creature* cr = me->SummonCreature(NPC_LIVING_POISON, *me, TEMPSUMMON_TIMED_DESPAWN, 14800)) - { - cr->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); - cr->GetMotionMaster()->MovePoint(0, 3144.779, -3158.416, 293.324, false); - } - if (Creature* cr = me->SummonCreature(NPC_LIVING_POISON, 3175.42, -3134.86, 293.34, 4.284, TEMPSUMMON_TIMED_DESPAWN, 14800)) - { - cr->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); - cr->GetMotionMaster()->MovePoint(0, 3158.778, -3164.201, 293.312, false); - } - timer = 0; - } - } - else if (me->GetEntry() == NPC_LIVING_POISON) - { - Unit* target = nullptr; - Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, 0.5f); - Acore::UnitLastSearcher searcher(me, target, u_check); - Cell::VisitAllObjects(me, searcher, 1.5f); - if (target) - { - me->CastSpell(me, SPELL_FROGGER_EXPLODE, true); - } + instance->PlayDirectSoundToMap(SOUND_SCREAM + urand(0, 3)); + return _events.ScheduleEvent(EVENT_THADDIUS_SCREAMS, 2min, 2min + 30s); } + case EVENT_AND_THEY_WOULD_ALL_GO_DOWN_TOGETHER: + _horsemanAchievement = false; + break; + case EVENT_KELTHUZAD_WING_TAUNT: + return CreatureTalk(DATA_KELTHUZAD_BOSS, _currentWingTaunt++); + case EVENT_HORSEMEN_INTRO1: + CreatureTalk(DATA_THANE_KORTHAZZ_BOSS, SAY_HORSEMEN_DIALOG1); + return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO2, 4500ms); + case EVENT_HORSEMEN_INTRO2: + CreatureTalk(DATA_SIR_ZELIEK_BOSS, SAY_HORSEMEN_DIALOG1); + return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO3, 6500ms); + case EVENT_HORSEMEN_INTRO3: + CreatureTalk(DATA_LADY_BLAUMEUX_BOSS, SAY_HORSEMEN_DIALOG1); + return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO4, 6500ms); + case EVENT_HORSEMEN_INTRO4: + CreatureTalk(DATA_BARON_RIVENDARE_BOSS, SAY_HORSEMEN_DIALOG1); + return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO5, 6500ms); + case EVENT_HORSEMEN_INTRO5: + CreatureTalk(DATA_LADY_BLAUMEUX_BOSS, SAY_HORSEMEN_DIALOG2); + return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO6, 6500ms); + case EVENT_HORSEMEN_INTRO6: + CreatureTalk(DATA_SIR_ZELIEK_BOSS, SAY_HORSEMEN_DIALOG2); + return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO7, 6500ms); + case EVENT_HORSEMEN_INTRO7: + CreatureTalk(DATA_THANE_KORTHAZZ_BOSS, SAY_HORSEMEN_DIALOG2); + return _events.ScheduleEvent(EVENT_HORSEMEN_INTRO8, 6500ms); + case EVENT_HORSEMEN_INTRO8: + return CreatureTalk(DATA_BARON_RIVENDARE_BOSS, SAY_HORSEMEN_DIALOG2); + case EVENT_FROSTWYRM_WATERFALL_DOOR: + SetGoState(DATA_SAPPHIRON_GATE, GO_STATE_ACTIVE); + return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK1, 5s); + case EVENT_KELTHUZAD_LICH_KING_TALK1: + CreatureTalk(DATA_KELTHUZAD_BOSS, SAY_SAPP_DIALOG1); + return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK2, 10s); + case EVENT_KELTHUZAD_LICH_KING_TALK2: + CreatureTalk(DATA_LICH_KING_BOSS, SAY_SAPP_DIALOG2_LICH); + return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK3, 14s); + case EVENT_KELTHUZAD_LICH_KING_TALK3: + CreatureTalk(DATA_KELTHUZAD_BOSS, SAY_SAPP_DIALOG3); + return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK4, 10s); + case EVENT_KELTHUZAD_LICH_KING_TALK4: + CreatureTalk(DATA_LICH_KING_BOSS, SAY_SAPP_DIALOG4_LICH); + return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK5, 12s); + case EVENT_KELTHUZAD_LICH_KING_TALK5: + CreatureTalk(DATA_KELTHUZAD_BOSS, SAY_SAPP_DIALOG5); + return _events.ScheduleEvent(EVENT_KELTHUZAD_LICH_KING_TALK6, 5s); + case EVENT_KELTHUZAD_LICH_KING_TALK6: + CreatureTalk(DATA_KELTHUZAD_BOSS, SAY_SAPP_DIALOG6); + return SetGoState(DATA_KELTHUZAD_GATE, GO_STATE_ACTIVE); + default: + break; } - }; + } + +private: + // Controls + EventMap _events; + uint8 _currentWingTaunt; + uint8 _horsemanLoaded; + + // GameObjects + std::set _heiganEruption[HeiganEruptSectionCount]; + + // NPCs + GuidList _patchwerkRoomTrash; + GuidList _heiganBackRoomTrash; + + // Achievements + uint8 _abominationsKilled; + bool _faerlinaAchievement; + bool _thaddiusAchievement; + bool _loathebAchievement; + bool _sapphironAchievement; + bool _heiganAchievement; + bool _horsemanAchievement; }; -const Position sapphironEntryTP = { 3498.300049f, -5349.490234f, 144.968002f, 1.3698910f }; +class npc_mr_bigglesworth : public NullCreatureAI +{ +public: + npc_mr_bigglesworth(Creature* c) : NullCreatureAI(c) { } + + void JustDied(Unit* /*killer*/) override + { + InstanceScript* instance = me->GetInstanceScript(); + if (!instance) + return; + + Creature* kelThuzard = instance->GetCreature(DATA_KELTHUZAD_BOSS); + if (!kelThuzard) + return; + + kelThuzard->AI()->Talk(SAY_CAT_DIED); + } +}; + +class npc_living_poison : public NullCreatureAI +{ +public: + npc_living_poison(Creature* c) : NullCreatureAI(c) { } + + void UpdateAI(uint32 /*diff*/) override + { + if (me->SelectNearestTarget(1.5f, true)) + me->CastSpell(me, SPELL_EXPLODE, true); + } +}; + +class npc_naxxramas_trigger : public NullCreatureAI +{ +public: + npc_naxxramas_trigger(Creature* c) : NullCreatureAI(c) { } + + void Reset() override + { + _events.Reset(); + _events.ScheduleEvent(EVENT_SUMMON_LIVING_POISON, 5s); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + switch (_events.ExecuteEvent()) + { + case EVENT_SUMMON_LIVING_POISON: + { + for (LivingPoisonData const& entry : LivingPoisonDataList) + if (Creature* cr = me->SummonCreature(NPC_LIVING_POISON, entry.Start, TEMPSUMMON_TIMED_DESPAWN, entry.DespawnTime)) + { + cr->AddUnitMovementFlag(MOVEMENTFLAG_WALKING); + cr->GetMotionMaster()->MovePoint(0, entry.End, false); + } + + _events.Repeat(5s); + break; + } + default: + break; + } + } + +private: + EventMap _events; +}; class at_naxxramas_hub_portal : public AreaTriggerScript { public: at_naxxramas_hub_portal() : AreaTriggerScript("at_naxxramas_hub_portal") { } - bool OnTrigger(Player* player, AreaTrigger const* /*areaTrigger*/) override + bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override { - if (player->GetMap()->GetSpawnMode() == RAID_DIFFICULTY_10MAN_HEROIC) - { - InstanceScript* instance = player->GetInstanceScript(); - for (int i = 0; i < BOSS_SAPPHIRON; ++i) - { - if (instance->GetBossState(i) != DONE) - return false; - } - } - if (player->IsAlive() && !player->IsInCombat()) - { - if (InstanceScript *instance = player->GetInstanceScript()) - { - if (instance->CheckRequiredBosses(BOSS_SAPPHIRON)) - { - player->TeleportTo(533, sapphironEntryTP.m_positionX, sapphironEntryTP.m_positionY, sapphironEntryTP.m_positionZ, sapphironEntryTP.m_orientation); - return true; - } - } - } - return false; - } -}; - -class NaxxPlayerScript : public PlayerScript -{ -public: - NaxxPlayerScript() : PlayerScript("NaxxPlayerScript") { } - - void OnPlayerBeforeChooseGraveyard(Player* player, TeamId /*teamId*/, bool /*nearCorpse*/, uint32& graveyardOverride) override - { - if (player->GetMapId() == MAP_NAXX && player->GetMap()->GetSpawnMode() == RAID_DIFFICULTY_10MAN_HEROIC) - { - graveyardOverride = NAXX40_GRAVEYARD; - } - } -}; - -class naxx_northrend_entrance : public AreaTriggerScript -{ -public: - naxx_northrend_entrance() : AreaTriggerScript("naxx_northrend_entrance") { } - - bool OnTrigger(Player* player, AreaTrigger const* areaTrigger) override - { - // Do not allow entrance to Naxx 40 from Northrend - // Change 10 man heroic to regular 10 man, as when 10 man heroic is not available - Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficulty(true) : player->GetDifficulty(true); - if (diff == RAID_DIFFICULTY_10MAN_HEROIC) - { - player->SetRaidDifficulty(RAID_DIFFICULTY_10MAN_NORMAL); - } - switch (areaTrigger->entry) - { - // Naxx 10 and 25 entrances - case 5191: - player->TeleportTo(533, 3005.68f, -3447.77f, 293.93f, 4.65f); - break; - case 5192: - player->TeleportTo(533, 3019.34f, -3434.36f, 293.99f, 6.27f); - break; - case 5193: - player->TeleportTo(533, 3005.9f, -3420.58f, 294.11f, 1.58f); - break; - case 5194: - player->TeleportTo(533, 2992.5f, -3434.42f, 293.94f, 3.13f); - break; - } - return true; - } -}; - -class naxx_exit_trigger : public AreaTriggerScript -{ -public: - naxx_exit_trigger() : AreaTriggerScript("naxx_exit_trigger") { } - - bool OnTrigger(Player* player, AreaTrigger const* areaTrigger) override - { - if (player->GetMap()->GetSpawnMode() == RAID_DIFFICULTY_10MAN_HEROIC) - { - // Naxx 40 cannot be exited via portals, as in Classic + if (!player->IsAlive() || player->IsInCombat()) return false; - } - switch (areaTrigger->entry) - { - // Naxx 10 and 25 exits - case 5196: - player->TeleportTo(571, 3679.25f, -1278.58f, 243.55f, 2.39f); - break; - case 5197: - player->TeleportTo(571, 3679.03f, -1259.68f, 243.55f, 3.98f); - break; - case 5198: - player->TeleportTo(571, 3661.14f, -1279.55f, 243.55f, 0.82f); - break; - case 5199: - player->TeleportTo(571, 3660.01f, -1260.99f, 243.55f, 5.51f); - break; - } + + InstanceScript* instance = player->GetInstanceScript(); + if (!instance) + return false; + + if ((instance->GetBossState(BOSS_MAEXXNA) != DONE) || + (instance->GetBossState(BOSS_LOATHEB) != DONE) || + (instance->GetBossState(BOSS_THADDIUS) != DONE) || + (instance->GetBossState(BOSS_HORSEMAN) != DONE)) + return false; + + player->TeleportTo(SapphironTeleportPos); return true; } }; -class NaxxEntryFlag_AllMapScript : public AllMapScript +void AddSC_instance_naxxramas() { -public: - NaxxEntryFlag_AllMapScript() : AllMapScript("NaxxEntryFlag_AllMapScript") { } - - void OnPlayerEnterAll(Map* map, Player* player) override - { - if (player->IsGameMaster()) - return; - - // Check if mapId equals to Naxxramas (mapId: 533) - if (map->GetId() != 533) - return; - - // Cast on player Naxxramas Entry Flag Trigger DND - Classic (spellID: 29296) - if (player->GetQuestStatus(NAXX40_ENTRANCE_FLAG) != QUEST_STATUS_REWARDED) - { - // Mark player as having entered - Quest const* quest = sObjectMgr->GetQuestTemplate(NAXX40_ENTRANCE_FLAG); - player->AddQuest(quest, nullptr); - player->CompleteQuest(NAXX40_ENTRANCE_FLAG); - player->RewardQuest(quest, 0, player, false, false); - // Cast on player Naxxramas Entry Flag Trigger DND - Classic (spellID: 29296) - player->CastSpell(player, 29296, true); // for visual effect only, possible crash if cast on login - } - } -}; - -void AddSC_instance_naxxramas_combined() -{ - new instance_naxxramas_combined(); -// new boss_naxxramas_misc(); + RegisterInstanceScript(instance_naxxramas, NaxxramasMapId); + RegisterNaxxramasCreatureAI(npc_mr_bigglesworth); + RegisterNaxxramasCreatureAI(npc_living_poison); + RegisterNaxxramasCreatureAI(npc_naxxramas_trigger); new at_naxxramas_hub_portal(); - new NaxxPlayerScript(); - new naxx_exit_trigger(); - new naxx_northrend_entrance(); - new NaxxEntryFlag_AllMapScript(); }