/* * 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 . */ #include "CellImpl.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "PassiveAI.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "DBCEnums.h" #include "ObjectMgr.h" #include "naxxramas.h" const float HeiganPos[2] = {2796, -3707}; 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) { 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 } }; ObjectData const gameObjectData[] = { { 0, 0 } }; class instance_naxxramas_combined : public InstanceMapScript { public: instance_naxxramas_combined() : InstanceMapScript("instance_naxxramas", 533) { } InstanceScript* GetInstanceScript(InstanceMap* pMap) const override { return new instance_naxxramas_combined_InstanceMapScript(pMap); } 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(); // 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; // 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; // Controls uint8 _horsemanKilled; uint32 _speakTimer; uint32 _horsemanTimer; uint32 _screamTimer; bool _hadThaddiusGreet; EventMap events; uint8 _currentWingTaunt; bool _horsemanLoadDoneState; // Achievements uint8 abominationsKilled; bool faerlinaAchievement; bool thaddiusAchievement; bool loathebAchievement; bool sapphironAchievement; bool heiganAchievement; uint32 immortalAchievement; void HeiganEruptSections(uint32 section) { for (uint8 i = 0; i < 4; ++i) { if (i == section) continue; for (auto itr : HeiganEruption[i]) { itr->SendCustomAnim(itr->GetGoAnimProgress()); itr->CastSpell(nullptr, SPELL_ERUPTION); } } } void HeiganEruptSectionsTunnel() { // doesn't work for (auto itr : HeiganEruptionTunnel) { itr->SendCustomAnim(itr->GetGoAnimProgress()); itr->CastSpell(nullptr, SPELL_ERUPTION); } } 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; return 0; } bool AreAllWingsCleared() const { return (GetBossState(BOSS_MAEXXNA) == DONE) && (GetBossState(BOSS_LOATHEB) == DONE) && (GetBossState(BOSS_THADDIUS) == DONE) && (GetBossState(BOSS_HORSEMAN) == DONE); } bool CheckRequiredBosses(uint32 bossId, Player const* /* player */) const override { switch (bossId) { case BOSS_SAPPHIRON: if (!AreAllWingsCleared()) { 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 false; // Bosses data switch(bossId) { case BOSS_KELTHUZAD: if (state == NOT_STARTED) { abominationsKilled = 0; } 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) { case BOSS_PATCHWERK: if (GameObject* go = instance->GetGameObject(_patchwerkGateGUID)) { go->SetGoState(GO_STATE_ACTIVE); } 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); break; default: break; } } return true; } void Update(uint32 diff) override { if (_speakTimer) { Creature* kel = instance->GetCreature(_kelthuzadGUID); Creature* lich = instance->GetCreature(_lichkingGUID); if (kel && lich) { _speakTimer += diff; } else { return; } if (_speakTimer > 10000 && _speakTimer < 20000) { 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)) { go->SetGoState(GO_STATE_ACTIVE); } } } // 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)) { kelthuzad->AI()->Talk(_currentWingTaunt); } ++_currentWingTaunt; break; case EVENT_FROSTWYRM_WATERFALL_DOOR: if (GameObject* go = instance->GetGameObject(_sapphironGateGUID)) { go->SetGoState(GO_STATE_ACTIVE); } break; } } ObjectGuid GetGuidData(uint32 id) const override { switch (id) { // 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; // 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; } void WriteSaveDataMore(std::ostringstream& data) override { data << immortalAchievement; } }; }; class boss_naxxramas_misc : public CreatureScript { public: boss_naxxramas_misc() : CreatureScript("boss_naxxramas_misc") { } CreatureAI* GetAI(Creature* pCreature) const override { return GetNaxxramasAI(pCreature); } struct boss_naxxramas_miscAI : public NullCreatureAI { explicit boss_naxxramas_miscAI(Creature* c) : NullCreatureAI(c) { timer = 0; } uint32 timer; void JustDied(Unit* /*killer*/) override { if (me->GetEntry() == NPC_MR_BIGGLESWORTH && me->GetInstanceScript()) { if (Creature* cr = me->SummonCreature(20350/*NPC_KELTHUZAD*/, *me, TEMPSUMMON_TIMED_DESPAWN, 1)) { cr->SetDisplayId(11686); cr->AI()->Talk(SAY_CAT_DIED); } } } 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); } } } }; }; const Position sapphironEntryTP = { 3498.300049f, -5349.490234f, 144.968002f, 1.3698910f }; 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 { 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 OnBeforeChooseGraveyard(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 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; } return true; } }; class NaxxEntryFlag_AllMapScript : public AllMapScript { 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) player->CastSpell(player, 29296, true); 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); } } }; void AddSC_instance_naxxramas_combined() { new instance_naxxramas_combined(); // new boss_naxxramas_misc(); new at_naxxramas_hub_portal(); new NaxxPlayerScript(); new naxx_exit_trigger(); new naxx_northrend_entrance(); new NaxxEntryFlag_AllMapScript(); }