mirror of
https://github.com/ZhengPeiRu21/mod-individual-progression
synced 2025-11-29 23:44:51 +08:00
841 lines
31 KiB
C++
841 lines
31 KiB
C++
/*
|
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Affero General Public License as published by the
|
|
* Free Software Foundation; either version 3 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "AreaTriggerScript.h"
|
|
#include "CellImpl.h"
|
|
#include "CreatureAIImpl.h"
|
|
#include "CreatureScript.h"
|
|
#include "InstanceMapScript.h"
|
|
#include "InstanceScript.h"
|
|
#include "PassiveAI.h"
|
|
#include "Player.h"
|
|
#include "naxxramas.h"
|
|
|
|
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]),
|
|
};
|
|
|
|
static constexpr std::array<uint32, HorsemanCount> HorsemanDataGroup
|
|
{
|
|
DATA_BARON_RIVENDARE_BOSS,
|
|
DATA_SIR_ZELIEK_BOSS,
|
|
DATA_LADY_BLAUMEUX_BOSS,
|
|
DATA_THANE_KORTHAZZ_BOSS
|
|
};
|
|
|
|
static WorldLocation const SapphironTeleportPos
|
|
{
|
|
NaxxramasMapId, 3498.300049f, -5349.490234f, 144.968002f, 1.3698910f
|
|
};
|
|
|
|
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(Map* map) : InstanceScript(map)
|
|
{
|
|
SetHeaders(DataHeader);
|
|
SetBossNumber(MAX_ENCOUNTERS);
|
|
SetPersistentDataCount(PERSISTENT_DATA_COUNT);
|
|
LoadDoorData(doorData);
|
|
LoadObjectData(creatureData, gameObjectData);
|
|
|
|
// NX40 specific data
|
|
if (instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC)
|
|
LoadObjectData(creatureDataNX40, gameObjectData);
|
|
|
|
// GameObjects
|
|
for (auto& i : _heiganEruption)
|
|
i.clear();
|
|
|
|
// NPCs
|
|
_patchwerkRoomTrash.clear();
|
|
_heiganBackRoomTrash.clear();
|
|
|
|
// Controls
|
|
_events.Reset();
|
|
_currentWingTaunt = SAY_FIRST_WING_TAUNT;
|
|
_horsemanLoaded = 0;
|
|
|
|
// Achievements
|
|
_abominationsKilled = 0;
|
|
_faerlinaAchievement = true;
|
|
_thaddiusAchievement = true;
|
|
_loathebAchievement = true;
|
|
_heiganAchievement = true;
|
|
_sapphironAchievement = true;
|
|
_horsemanAchievement = true;
|
|
}
|
|
|
|
inline void CreatureTalk(uint32 dataCreature, uint8 dialog)
|
|
{
|
|
if (Creature* creature = GetCreature(dataCreature))
|
|
creature->AI()->Talk(dialog);
|
|
}
|
|
|
|
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;
|
|
|
|
go->SetGoState(GO_STATE_ACTIVE);
|
|
go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
inline void HeiganEruptSections(uint32 section)
|
|
{
|
|
for (uint8 i = 0; i < HeiganEruptSectionCount; ++i)
|
|
{
|
|
if (i == section)
|
|
continue;
|
|
|
|
for (GameObject* go : _heiganEruption[i])
|
|
{
|
|
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;
|
|
|
|
return !GetPersistentData(PERSISTENT_DATA_IMMORTAL_FAIL);
|
|
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_HEIGAN_ERUPTION:
|
|
HeiganEruptSections(data);
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool SetBossState(uint32 bossId, EncounterState state) override
|
|
{
|
|
switch (bossId)
|
|
{
|
|
case BOSS_PATCHWERK:
|
|
{
|
|
if (state != IN_PROGRESS)
|
|
break;
|
|
|
|
// pull all the trash if not killed
|
|
if (Creature* patchwerk = GetCreature(DATA_PATCHWERK_BOSS))
|
|
{
|
|
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 DONE:
|
|
ActivateWingPortal(DATA_LOATHEB_PORTAL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BOSS_FAERLINA:
|
|
{
|
|
if (state == NOT_STARTED)
|
|
_faerlinaAchievement = true;
|
|
|
|
break;
|
|
}
|
|
case BOSS_MAEXXNA:
|
|
{
|
|
if (state == DONE)
|
|
ActivateWingPortal(DATA_MAEXXNA_PORTAL);
|
|
|
|
break;
|
|
}
|
|
case BOSS_THADDIUS:
|
|
{
|
|
switch (state)
|
|
{
|
|
case NOT_STARTED:
|
|
_thaddiusAchievement = true;
|
|
break;
|
|
case DONE:
|
|
ActivateWingPortal(DATA_THADDIUS_PORTAL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BOSS_HORSEMAN:
|
|
{
|
|
uint32 horsemanKilled = std::count_if(HorsemanDataGroup.begin(), HorsemanDataGroup.end(), [this](auto&& entry)
|
|
{
|
|
Creature* cr = GetCreature(entry);
|
|
return cr && !cr->IsAlive();
|
|
});
|
|
|
|
switch (state)
|
|
{
|
|
case NOT_STARTED:
|
|
{
|
|
_horsemanAchievement = true;
|
|
|
|
if (!horsemanKilled)
|
|
break;
|
|
|
|
for (auto&& entry : HorsemanDataGroup)
|
|
{
|
|
if (Creature* cr = GetCreature(entry))
|
|
{
|
|
cr->SetPosition(cr->GetHomePosition());
|
|
cr->Respawn();
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case IN_PROGRESS:
|
|
{
|
|
for (auto&& entry : HorsemanDataGroup)
|
|
if (Creature* cr = GetCreature(entry))
|
|
cr->SetInCombatWithZone();
|
|
|
|
break;
|
|
}
|
|
case DONE:
|
|
{
|
|
_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;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BOSS_SAPPHIRON:
|
|
{
|
|
switch (state)
|
|
{
|
|
case NOT_STARTED:
|
|
_sapphironAchievement = true;
|
|
break;
|
|
case DONE:
|
|
{
|
|
if (GetPersistentData(PERSISTENT_DATA_KELTHUZAD_DIALOG))
|
|
break;
|
|
|
|
StorePersistentData(PERSISTENT_DATA_KELTHUZAD_DIALOG, 1);
|
|
SetGoState(DATA_KELTHUZAD_GATE, GO_STATE_READY);
|
|
_events.ScheduleEvent(EVENT_FROSTWYRM_WATERFALL_DOOR, 5s);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case BOSS_KELTHUZAD:
|
|
{
|
|
if (state == NOT_STARTED)
|
|
_abominationsKilled = 0;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return InstanceScript::SetBossState(bossId, state);
|
|
}
|
|
|
|
void Update(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
|
|
switch (_events.ExecuteEvent())
|
|
{
|
|
case EVENT_THADDIUS_SCREAMS:
|
|
{
|
|
if (GetBossState(BOSS_THADDIUS) == DONE)
|
|
break;
|
|
|
|
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<GameObject*> _heiganEruption[HeiganEruptSectionCount];
|
|
|
|
// NPCs
|
|
GuidList _patchwerkRoomTrash;
|
|
GuidList _heiganBackRoomTrash;
|
|
|
|
// Achievements
|
|
uint8 _abominationsKilled;
|
|
bool _faerlinaAchievement;
|
|
bool _thaddiusAchievement;
|
|
bool _loathebAchievement;
|
|
bool _sapphironAchievement;
|
|
bool _heiganAchievement;
|
|
bool _horsemanAchievement;
|
|
};
|
|
|
|
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* /*trigger*/) override
|
|
{
|
|
if (!player->IsAlive() || player->IsInCombat())
|
|
return false;
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
void AddSC_instance_naxxramas()
|
|
{
|
|
RegisterInstanceScript(instance_naxxramas, NaxxramasMapId);
|
|
RegisterNaxxramasCreatureAI(npc_mr_bigglesworth);
|
|
RegisterNaxxramasCreatureAI(npc_living_poison);
|
|
RegisterNaxxramasCreatureAI(npc_naxxramas_trigger);
|
|
new at_naxxramas_hub_portal();
|
|
}
|