aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoroffl <11556157+offl@users.noreply.github.com>2021-04-13 10:28:52 +0300
committerShauren <shauren.trinity@gmail.com>2022-03-08 16:26:22 +0100
commitfae9a182df6b0646d0e8e16457619bd788a20424 (patch)
treec52a17dafac5eac981ccf43bc8d89536e7322469
parent5a6e99bb219a619eab5701bf0f9e76dab4fc3dcb (diff)
Scripts/Naxxramas: Update Kel'Thuzad to new model (#26384)
Co-authored-by: offl <offl@users.noreply.github.com> (cherry picked from commit ff4d9a81ae7408a4b99bbd2d69f72ff0f9f5479f)
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp965
1 files changed, 444 insertions, 521 deletions
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp
index ee0bfb57efa..6b493c8fd50 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp
@@ -205,383 +205,372 @@ struct ManaUserTargetSelector
}
};
-class boss_kelthuzad : public CreatureScript
+struct boss_kelthuzad : public BossAI
{
-public:
- boss_kelthuzad() : CreatureScript("boss_kelthuzad") { }
-
- struct boss_kelthuzadAI : public BossAI
- {
- public:
- boss_kelthuzadAI(Creature* creature) : BossAI(creature, BOSS_KELTHUZAD), _skeletonCount(0), _bansheeCount(0), _abominationCount(0), _abominationDeathCount(0), _frostboltCooldown(0), _phaseThree(false), _guardianCount(0)
- {
- for (uint8 i = 0; i < nGuardianSpawns; ++i)
- _guardianGroups[i] = SUMMON_GROUP_GUARDIAN_FIRST + i;
- }
+ public:
+ boss_kelthuzad(Creature* creature) : BossAI(creature, BOSS_KELTHUZAD), _skeletonCount(0), _bansheeCount(0), _abominationCount(0), _abominationDeathCount(0), _frostboltCooldown(0), _phaseThree(false), _guardianCount(0)
+ {
+ for (uint8 i = 0; i < nGuardianSpawns; ++i)
+ _guardianGroups[i] = SUMMON_GROUP_GUARDIAN_FIRST + i;
+ }
- void Reset() override
- {
- if (!me->IsAlive())
- return;
- _Reset();
- me->SetReactState(REACT_PASSIVE);
- me->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- me->SetImmuneToPC(true);
- _skeletonCount = 0;
- _bansheeCount = 0;
- _abominationCount = 0;
- _abominationDeathCount = 0;
- _phaseThree = false;
- }
+ void Reset() override
+ {
+ if (!me->IsAlive())
+ return;
+ _Reset();
+ me->SetReactState(REACT_PASSIVE);
+ me->AddUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->SetImmuneToPC(true);
+ _skeletonCount = 0;
+ _bansheeCount = 0;
+ _abominationCount = 0;
+ _abominationDeathCount = 0;
+ _phaseThree = false;
+ }
- void EnterEvadeMode(EvadeReason /*why*/) override
- {
- if (!me->IsAlive())
- return;
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ if (!me->IsAlive())
+ return;
- for (NAXData64 portalData : portalList)
- if (GameObject* portal = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(portalData)))
- portal->SetGoState(GO_STATE_READY);
+ for (NAXData64 portalData : portalList)
+ if (GameObject* portal = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(portalData)))
+ portal->SetGoState(GO_STATE_READY);
- Reset();
- _DespawnAtEvade();
- }
+ Reset();
+ _DespawnAtEvade();
+ }
- void JustSummoned (Creature* summon) override
- { // prevent DoZoneInCombat
- summons.Summon(summon);
- }
+ void JustSummoned (Creature* summon) override
+ { // prevent DoZoneInCombat
+ summons.Summon(summon);
+ }
- void KilledUnit(Unit* victim) override
- {
- if (victim->GetTypeId() == TYPEID_PLAYER)
- Talk(SAY_SLAY);
- }
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
+ }
- void JustDied(Unit* /*killer*/) override
- {
- SummonList::iterator it = summons.begin();
- while (it != summons.end())
- if (Creature* cSummon = ObjectAccessor::GetCreature(*me, *it))
+ void JustDied(Unit* /*killer*/) override
+ {
+ SummonList::iterator it = summons.begin();
+ while (it != summons.end())
+ if (Creature* cSummon = ObjectAccessor::GetCreature(*me, *it))
+ {
+ if (cSummon->IsAlive() && cSummon->GetEntry() == NPC_GUARDIAN)
{
- if (cSummon->IsAlive() && cSummon->GetEntry() == NPC_GUARDIAN)
- {
- cSummon->AI()->DoAction(ACTION_KELTHUZAD_DIED);
- it = summons.erase(it); // prevent them from being despawned by _JustDied
- }
- else
- ++it;
+ cSummon->AI()->DoAction(ACTION_KELTHUZAD_DIED);
+ it = summons.erase(it); // prevent them from being despawned by _JustDied
}
+ else
+ ++it;
+ }
- _JustDied();
- Talk(SAY_DEATH);
- }
+ _JustDied();
+ Talk(SAY_DEATH);
+ }
- void DamageTaken(Unit* /*attacker*/, uint32& damage) override
- {
- if (events.IsInPhase(PHASE_ONE))
- damage = 0;
- }
+ void DamageTaken(Unit* /*attacker*/, uint32& damage) override
+ {
+ if (events.IsInPhase(PHASE_ONE))
+ damage = 0;
+ }
- void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+ void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+ {
+ if (spellInfo->Id == SPELL_CHAINS_DUMMY)
{
- if (spellInfo->Id == SPELL_CHAINS_DUMMY)
- {
- Talk(SAY_CHAINS);
- std::list<Unit*> targets;
- SelectTargetList(targets, 3, SelectTargetMethod::Random, 0, 0.0f, true, false);
- for (Unit* target : targets)
- DoCast(target, SPELL_CHAINS);
- }
+ Talk(SAY_CHAINS);
+ std::list<Unit*> targets;
+ SelectTargetList(targets, 3, SelectTargetMethod::Random, 0, 0.0f, true, false);
+ for (Unit* target : targets)
+ DoCast(target, SPELL_CHAINS);
}
+ }
- void UpdateAI(uint32 diff) override
- {
- if (!UpdateVictim())
- return;
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- events.Update(diff);
+ events.Update(diff);
- if (_frostboltCooldown < diff)
- _frostboltCooldown = 0;
- else
- _frostboltCooldown -= diff;
+ if (_frostboltCooldown < diff)
+ _frostboltCooldown = 0;
+ else
+ _frostboltCooldown -= diff;
- if (!events.IsInPhase(PHASE_ONE) && me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ if (!events.IsInPhase(PHASE_ONE) && me->HasUnitState(UNIT_STATE_CASTING))
+ return;
- if (!_phaseThree && HealthBelowPct(45))
+ if (!_phaseThree && HealthBelowPct(45))
+ {
+ _phaseThree = true;
+ _guardianCount = 0;
+ Talk(SAY_REQUEST_AID);
+ events.ScheduleEvent(EVENT_TRANSITION_REPLY, Seconds(4), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_TRANSITION_SUMMON, randtime(Seconds(7), Seconds(9)), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_TRANSITION_SUMMON, randtime(Seconds(13), Seconds(15)), 0, PHASE_TWO);
+ if (Is25ManRaid())
{
- _phaseThree = true;
- _guardianCount = 0;
- Talk(SAY_REQUEST_AID);
- events.ScheduleEvent(EVENT_TRANSITION_REPLY, Seconds(4), 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_TRANSITION_SUMMON, randtime(Seconds(7), Seconds(9)), 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_TRANSITION_SUMMON, randtime(Seconds(13), Seconds(15)), 0, PHASE_TWO);
- if (Is25ManRaid())
- {
- events.ScheduleEvent(EVENT_TRANSITION_SUMMON, randtime(Seconds(19), Seconds(21)), 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_TRANSITION_SUMMON, randtime(Seconds(25), Seconds(27)), 0, PHASE_TWO);
- }
+ events.ScheduleEvent(EVENT_TRANSITION_SUMMON, randtime(Seconds(19), Seconds(21)), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_TRANSITION_SUMMON, randtime(Seconds(25), Seconds(27)), 0, PHASE_TWO);
}
+ }
- while (uint32 eventId = events.ExecuteEvent())
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ if (_frostboltCooldown <= 4 * IN_MILLISECONDS) // stop casting bolts for 4 seconds after doing another action
+ _frostboltCooldown = 4 * IN_MILLISECONDS;
+ switch (eventId)
{
- if (_frostboltCooldown <= 4 * IN_MILLISECONDS) // stop casting bolts for 4 seconds after doing another action
- _frostboltCooldown = 4 * IN_MILLISECONDS;
- switch (eventId)
+ case EVENT_SKELETON:
{
- case EVENT_SKELETON:
+ ++_skeletonCount;
+ if (_skeletonCount == 1) // the first skeleton is actually one of the pre-existing ones - I'm not sure why, but that's what the sniffs say
{
- ++_skeletonCount;
- if (_skeletonCount == 1) // the first skeleton is actually one of the pre-existing ones - I'm not sure why, but that's what the sniffs say
- {
- std::list<Creature*> skeletons;
- me->GetCreatureListWithEntryInGrid(skeletons, NPC_SKELETON2, 200.0f);
- if (skeletons.empty())
- { // prevent UB
- EnterEvadeMode(EVADE_REASON_OTHER);
- return;
- }
- std::list<Creature*>::iterator it = skeletons.begin();
- std::advance(it, urand(0, skeletons.size() - 1));
- (*it)->SetReactState(REACT_AGGRESSIVE);
- (*it)->AI()->DoZoneInCombat(); // will select a player on our threat list as we are the summoner
- }
- else
- {
- // retail uses server-side spell 28421 for this
- Creature* summon = me->SummonCreature(NPC_SKELETON1, GetRandomMinionSpawnPoint(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2s);
- summon->AI()->DoZoneInCombat();
+ std::list<Creature*> skeletons;
+ me->GetCreatureListWithEntryInGrid(skeletons, NPC_SKELETON2, 200.0f);
+ if (skeletons.empty())
+ { // prevent UB
+ EnterEvadeMode(EVADE_REASON_OTHER);
+ return;
}
-
- uint8 nextTime = 0;
- if (_skeletonCount < 10)
- nextTime = 5;
- else if (_skeletonCount < 19)
- nextTime = 4;
- else if (_skeletonCount < 31)
- nextTime = 3;
- else if (_skeletonCount == 31)
- nextTime = 4;
- else if (_skeletonCount < 72)
- nextTime = 2;
-
- if (nextTime)
- events.ScheduleEvent(EVENT_SKELETON, Seconds(nextTime), 0, PHASE_ONE);
- break;
+ std::list<Creature*>::iterator it = skeletons.begin();
+ std::advance(it, urand(0, skeletons.size() - 1));
+ (*it)->SetReactState(REACT_AGGRESSIVE);
+ (*it)->AI()->DoZoneInCombat(); // will select a player on our threat list as we are the summoner
}
-
- case EVENT_BANSHEE:
+ else
{
- ++_bansheeCount;
- // retail uses server-side spell 28423 for this
- Creature* summon = me->SummonCreature(NPC_BANSHEE1, GetRandomMinionSpawnPoint(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2s);
+ // retail uses server-side spell 28421 for this
+ Creature* summon = me->SummonCreature(NPC_SKELETON1, GetRandomMinionSpawnPoint(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2s);
summon->AI()->DoZoneInCombat();
-
- uint8 nextTime = 0;
- if (_bansheeCount < 3)
- nextTime = 30;
- else if (_bansheeCount < 7)
- nextTime = 20;
- else if (_bansheeCount < 9)
- nextTime = 15;
-
- if (nextTime)
- events.ScheduleEvent(EVENT_BANSHEE, Seconds(nextTime), 0, PHASE_ONE);
- break;
}
- case EVENT_ABOMINATION:
- {
- ++_abominationCount;
- // retail uses server-side spell 28422 for this
- Creature* summon = me->SummonCreature(NPC_ABOMINATION1, GetRandomMinionSpawnPoint(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2s);
- summon->AI()->DoZoneInCombat();
+ uint8 nextTime = 0;
+ if (_skeletonCount < 10)
+ nextTime = 5;
+ else if (_skeletonCount < 19)
+ nextTime = 4;
+ else if (_skeletonCount < 31)
+ nextTime = 3;
+ else if (_skeletonCount == 31)
+ nextTime = 4;
+ else if (_skeletonCount < 72)
+ nextTime = 2;
+
+ if (nextTime)
+ events.ScheduleEvent(EVENT_SKELETON, Seconds(nextTime), 0, PHASE_ONE);
+ break;
+ }
- uint8 nextTime = 0;
- if (_abominationCount < 3)
- nextTime = 30;
- else if (_abominationCount < 7)
- nextTime = 20;
- else if (_abominationCount < 9)
- nextTime = 15;
-
- if (nextTime)
- events.ScheduleEvent(EVENT_ABOMINATION, Seconds(nextTime), 0, PHASE_ONE);
- break;
- }
+ case EVENT_BANSHEE:
+ {
+ ++_bansheeCount;
+ // retail uses server-side spell 28423 for this
+ Creature* summon = me->SummonCreature(NPC_BANSHEE1, GetRandomMinionSpawnPoint(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2s);
+ summon->AI()->DoZoneInCombat();
+
+ uint8 nextTime = 0;
+ if (_bansheeCount < 3)
+ nextTime = 30;
+ else if (_bansheeCount < 7)
+ nextTime = 20;
+ else if (_bansheeCount < 9)
+ nextTime = 15;
+
+ if (nextTime)
+ events.ScheduleEvent(EVENT_BANSHEE, Seconds(nextTime), 0, PHASE_ONE);
+ break;
+ }
- case EVENT_DESPAWN_MINIONS:
- {
- // we need a temp vector, as we can't modify summons while iterating (this would cause UB)
- std::vector<Creature*> toDespawn;
- toDespawn.reserve(summons.size());
- for (ObjectGuid sGuid : summons)
- if (Creature* summon = ObjectAccessor::GetCreature(*me, sGuid))
- if (!summon->IsInCombat())
- toDespawn.push_back(summon);
- for (Creature* summon : toDespawn)
- summon->DespawnOrUnsummon();
- Talk(SAY_AGGRO);
- break;
- }
+ case EVENT_ABOMINATION:
+ {
+ ++_abominationCount;
+ // retail uses server-side spell 28422 for this
+ Creature* summon = me->SummonCreature(NPC_ABOMINATION1, GetRandomMinionSpawnPoint(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2s);
+ summon->AI()->DoZoneInCombat();
+
+ uint8 nextTime = 0;
+ if (_abominationCount < 3)
+ nextTime = 30;
+ else if (_abominationCount < 7)
+ nextTime = 20;
+ else if (_abominationCount < 9)
+ nextTime = 15;
+
+ if (nextTime)
+ events.ScheduleEvent(EVENT_ABOMINATION, Seconds(nextTime), 0, PHASE_ONE);
+ break;
+ }
- case EVENT_PHASE_TWO:
- me->CastStop();
- events.SetPhase(PHASE_TWO);
- me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
- me->SetImmuneToPC(false);
- ResetThreatList();
- me->SetReactState(REACT_AGGRESSIVE);
- Talk(EMOTE_PHASE_TWO);
-
- _frostboltCooldown = 2 * IN_MILLISECONDS;
- events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, randtime(Seconds(24), Seconds(28)), 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_SHADOW_FISSURE, randtime(Seconds(6), Seconds(10)), 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_DETONATE_MANA, randtime(Seconds(27), Seconds(33)), 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_FROST_BLAST, randtime(Seconds(25), Seconds(45)), 0, PHASE_TWO);
- if (Is25ManRaid())
- events.ScheduleEvent(EVENT_CHAINS, randtime(Seconds(60), Seconds(80)), 0, PHASE_TWO);
- break;
-
- case EVENT_FROSTBOLT_VOLLEY:
- DoCastAOE(SPELL_FROSTBOLT_VOLLEY);
- events.Repeat(randtime(Seconds(16), Seconds(18)));
- break;
-
- case EVENT_SHADOW_FISSURE:
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true))
- DoCast(target, SPELL_SHADOW_FISSURE);
- events.Repeat(randtime(Seconds(14), Seconds(17)));
- break;
-
- case EVENT_DETONATE_MANA:
- {
- ManaUserTargetSelector pred;
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, pred))
- DoCast(target, SPELL_DETONATE_MANA);
- events.Repeat(randtime(Seconds(30), Seconds(40)));
- break;
- }
+ case EVENT_DESPAWN_MINIONS:
+ {
+ // we need a temp vector, as we can't modify summons while iterating (this would cause UB)
+ std::vector<Creature*> toDespawn;
+ toDespawn.reserve(summons.size());
+ for (ObjectGuid sGuid : summons)
+ if (Creature* summon = ObjectAccessor::GetCreature(*me, sGuid))
+ if (!summon->IsInCombat())
+ toDespawn.push_back(summon);
+ for (Creature* summon : toDespawn)
+ summon->DespawnOrUnsummon();
+ Talk(SAY_AGGRO);
+ break;
+ }
- case EVENT_FROST_BLAST:
- if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 0.0f, true))
- DoCast(target, SPELL_FROST_BLAST);
- events.Repeat(randtime(Seconds(25), Seconds(45)));
- break;
+ case EVENT_PHASE_TWO:
+ me->CastStop();
+ events.SetPhase(PHASE_TWO);
+ me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
+ me->SetImmuneToPC(false);
+ ResetThreatList();
+ me->SetReactState(REACT_AGGRESSIVE);
+ Talk(EMOTE_PHASE_TWO);
+
+ _frostboltCooldown = 2 * IN_MILLISECONDS;
+ events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, randtime(Seconds(24), Seconds(28)), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_SHADOW_FISSURE, randtime(Seconds(6), Seconds(10)), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_DETONATE_MANA, randtime(Seconds(27), Seconds(33)), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_FROST_BLAST, randtime(Seconds(25), Seconds(45)), 0, PHASE_TWO);
+ if (Is25ManRaid())
+ events.ScheduleEvent(EVENT_CHAINS, randtime(Seconds(60), Seconds(80)), 0, PHASE_TWO);
+ break;
- case EVENT_CHAINS:
- {
- DoCastAOE(SPELL_CHAINS_DUMMY);
- events.Repeat(Minutes(1) + randtime(Seconds(0), Seconds(20)));
- break;
- }
+ case EVENT_FROSTBOLT_VOLLEY:
+ DoCastAOE(SPELL_FROSTBOLT_VOLLEY);
+ events.Repeat(randtime(Seconds(16), Seconds(18)));
+ break;
- case EVENT_TRANSITION_REPLY:
- if (Creature* lichKing = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_LICH_KING)))
- lichKing->AI()->Talk(SAY_ANSWER_REQUEST);
- for (NAXData64 portalData : portalList)
- if (GameObject* portal = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(portalData)))
- portal->SetGoState(GO_STATE_ACTIVE);
- break;
+ case EVENT_SHADOW_FISSURE:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true))
+ DoCast(target, SPELL_SHADOW_FISSURE);
+ events.Repeat(randtime(Seconds(14), Seconds(17)));
+ break;
- case EVENT_TRANSITION_SUMMON:
- {
- uint8 selected = urand(_guardianCount, nGuardianSpawns - 1);
- if (selected != _guardianCount)
- std::swap(_guardianGroups[selected], _guardianGroups[_guardianCount]);
-
- std::list<TempSummon*> summoned;
- // server-side spell 28454 is used on retail - no point replicating this in spell_dbc
- me->SummonCreatureGroup(_guardianGroups[_guardianCount++], &summoned);
- for (TempSummon* guardian : summoned)
- guardian->AI()->DoAction(ACTION_JUST_SUMMONED);
- break;
- }
+ case EVENT_DETONATE_MANA:
+ {
+ ManaUserTargetSelector pred;
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, pred))
+ DoCast(target, SPELL_DETONATE_MANA);
+ events.Repeat(randtime(Seconds(30), Seconds(40)));
+ break;
}
- if (!events.IsInPhase(PHASE_ONE) && me->HasUnitState(UNIT_STATE_CASTING))
- return;
- }
+ case EVENT_FROST_BLAST:
+ if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 0.0f, true))
+ DoCast(target, SPELL_FROST_BLAST);
+ events.Repeat(randtime(Seconds(25), Seconds(45)));
+ break;
- if (!_frostboltCooldown)
- {
- DoCastVictim(SPELL_FROSTBOLT_SINGLE);
- _frostboltCooldown = 3 * IN_MILLISECONDS;
+ case EVENT_CHAINS:
+ {
+ DoCastAOE(SPELL_CHAINS_DUMMY);
+ events.Repeat(Minutes(1) + randtime(Seconds(0), Seconds(20)));
+ break;
+ }
+
+ case EVENT_TRANSITION_REPLY:
+ if (Creature* lichKing = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_LICH_KING)))
+ lichKing->AI()->Talk(SAY_ANSWER_REQUEST);
+ for (NAXData64 portalData : portalList)
+ if (GameObject* portal = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(portalData)))
+ portal->SetGoState(GO_STATE_ACTIVE);
+ break;
+
+ case EVENT_TRANSITION_SUMMON:
+ {
+ uint8 selected = urand(_guardianCount, nGuardianSpawns - 1);
+ if (selected != _guardianCount)
+ std::swap(_guardianGroups[selected], _guardianGroups[_guardianCount]);
+
+ std::list<TempSummon*> summoned;
+ // server-side spell 28454 is used on retail - no point replicating this in spell_dbc
+ me->SummonCreatureGroup(_guardianGroups[_guardianCount++], &summoned);
+ for (TempSummon* guardian : summoned)
+ guardian->AI()->DoAction(ACTION_JUST_SUMMONED);
+ break;
+ }
}
- else
- DoMeleeAttackIfReady();
+
+ if (!events.IsInPhase(PHASE_ONE) && me->HasUnitState(UNIT_STATE_CASTING))
+ return;
}
- uint32 GetData(uint32 data) const override
+ if (!_frostboltCooldown)
{
- if (data == DATA_ABOMINATION_DEATH_COUNT)
- return _abominationDeathCount;
- return 0;
+ DoCastVictim(SPELL_FROSTBOLT_SINGLE);
+ _frostboltCooldown = 3 * IN_MILLISECONDS;
}
+ else
+ DoMeleeAttackIfReady();
+ }
- void DoAction(int32 action) override
- {
- switch (action)
- {
- case ACTION_BEGIN_ENCOUNTER:
- if (instance->GetBossState(BOSS_KELTHUZAD) != NOT_STARTED)
- return;
- me->SetImmuneToPC(false);
- instance->SetBossState(BOSS_KELTHUZAD, IN_PROGRESS);
- events.SetPhase(PHASE_ONE);
- DoZoneInCombat();
- DoCastAOE(SPELL_VISUAL_CHANNEL);
- Talk(SAY_SUMMON_MINIONS);
+ uint32 GetData(uint32 data) const override
+ {
+ if (data == DATA_ABOMINATION_DEATH_COUNT)
+ return _abominationDeathCount;
+ return 0;
+ }
- for (uint8 group = SUMMON_GROUP_MINION_FIRST; group < SUMMON_GROUP_MINION_FIRST + nMinionGroups; ++group)
+ void DoAction(int32 action) override
+ {
+ switch (action)
+ {
+ case ACTION_BEGIN_ENCOUNTER:
+ if (instance->GetBossState(BOSS_KELTHUZAD) != NOT_STARTED)
+ return;
+ me->SetImmuneToPC(false);
+ instance->SetBossState(BOSS_KELTHUZAD, IN_PROGRESS);
+ events.SetPhase(PHASE_ONE);
+ DoZoneInCombat();
+ DoCastAOE(SPELL_VISUAL_CHANNEL);
+ Talk(SAY_SUMMON_MINIONS);
+
+ for (uint8 group = SUMMON_GROUP_MINION_FIRST; group < SUMMON_GROUP_MINION_FIRST + nMinionGroups; ++group)
+ {
+ std::list<TempSummon*> summoned;
+ me->SummonCreatureGroup(group, &summoned);
+ for (TempSummon* summon : summoned)
{
- std::list<TempSummon*> summoned;
- me->SummonCreatureGroup(group, &summoned);
- for (TempSummon* summon : summoned)
- {
- summon->SetReactState(REACT_PASSIVE);
- summon->AI()->SetData(DATA_MINION_POCKET_ID, group);
- }
+ summon->SetReactState(REACT_PASSIVE);
+ summon->AI()->SetData(DATA_MINION_POCKET_ID, group);
}
+ }
- events.ScheduleEvent(EVENT_SKELETON, Seconds(5), 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_BANSHEE, Seconds(30), 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_ABOMINATION, Seconds(30), 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_DESPAWN_MINIONS, Minutes(3) + Seconds(33), 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_PHASE_TWO, Minutes(3) + Seconds(48), 0, PHASE_ONE);
- break;
+ events.ScheduleEvent(EVENT_SKELETON, Seconds(5), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_BANSHEE, Seconds(30), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_ABOMINATION, Seconds(30), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_DESPAWN_MINIONS, Minutes(3) + Seconds(33), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_PHASE_TWO, Minutes(3) + Seconds(48), 0, PHASE_ONE);
+ break;
- case ACTION_ABOMINATION_DIED:
- ++_abominationDeathCount;
- break;
+ case ACTION_ABOMINATION_DIED:
+ ++_abominationDeathCount;
+ break;
- default:
- break;
- }
+ default:
+ break;
}
+ }
- PlayerAI* GetAIForCharmedPlayer(Player* player) override
- {
- return new KelThuzadCharmedPlayerAI(player);
- }
+ PlayerAI* GetAIForCharmedPlayer(Player* player) override
+ {
+ return new KelThuzadCharmedPlayerAI(player);
+ }
- private:
- uint8 _skeletonCount;
- uint8 _bansheeCount;
- uint8 _abominationCount;
- uint8 _abominationDeathCount;
- uint32 _frostboltCooldown;
- bool _phaseThree;
- uint32 _guardianCount;
- std::array<uint32, nGuardianSpawns> _guardianGroups;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<boss_kelthuzadAI>(creature);
- }
+ private:
+ uint8 _skeletonCount;
+ uint8 _bansheeCount;
+ uint8 _abominationCount;
+ uint8 _abominationDeathCount;
+ uint32 _frostboltCooldown;
+ bool _phaseThree;
+ uint32 _guardianCount;
+ std::array<uint32, nGuardianSpawns> _guardianGroups;
};
static const float MINION_AGGRO_DISTANCE = 20.0f;
@@ -689,265 +678,199 @@ struct npc_kelthuzad_minionAI : public ScriptedAI
Position const _home;
};
-class npc_kelthuzad_skeleton : public CreatureScript
+struct npc_kelthuzad_skeleton : public npc_kelthuzad_minionAI
{
-public:
- npc_kelthuzad_skeleton() : CreatureScript("npc_kelthuzad_skeleton") { }
+ npc_kelthuzad_skeleton(Creature* creature) : npc_kelthuzad_minionAI(creature) { }
- struct npc_kelthuzad_skeletonAI : public npc_kelthuzad_minionAI
+ void UpdateAI(uint32 diff) override
{
- npc_kelthuzad_skeletonAI(Creature* creature) : npc_kelthuzad_minionAI(creature) { }
-
- void UpdateAI(uint32 diff) override
- {
- UpdateRandomMovement(diff);
-
- if (!UpdateVictim())
- return;
+ UpdateRandomMovement(diff);
- DoMeleeAttackIfReady();
- }
- };
+ if (!UpdateVictim())
+ return;
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<npc_kelthuzad_skeletonAI>(creature);
+ DoMeleeAttackIfReady();
}
};
-class npc_kelthuzad_banshee : public CreatureScript
+struct npc_kelthuzad_banshee : public npc_kelthuzad_minionAI
{
-public:
- npc_kelthuzad_banshee() : CreatureScript("npc_kelthuzad_banshee") { }
+ npc_kelthuzad_banshee(Creature* creature) : npc_kelthuzad_minionAI(creature) { }
- struct npc_kelthuzad_bansheeAI : public npc_kelthuzad_minionAI
+ void UpdateAI(uint32 diff) override
{
- npc_kelthuzad_bansheeAI(Creature* creature) : npc_kelthuzad_minionAI(creature) { }
+ UpdateRandomMovement(diff);
- void UpdateAI(uint32 diff) override
- {
- UpdateRandomMovement(diff);
-
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
- }
- };
+ if (!UpdateVictim())
+ return;
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<npc_kelthuzad_bansheeAI>(creature);
+ DoMeleeAttackIfReady();
}
};
-class npc_kelthuzad_abomination : public CreatureScript
+struct npc_kelthuzad_abomination : public npc_kelthuzad_minionAI
{
-public:
- npc_kelthuzad_abomination() : CreatureScript("npc_kelthuzad_abomination") { }
+ npc_kelthuzad_abomination(Creature* creature) : npc_kelthuzad_minionAI(creature), _woundTimer(urandms(10, 20)) { }
- struct npc_kelthuzad_abominationAI : public npc_kelthuzad_minionAI
+ void UpdateAI(uint32 diff) override
{
- npc_kelthuzad_abominationAI(Creature* creature) : npc_kelthuzad_minionAI(creature), _woundTimer(urandms(10, 20)) { }
-
- void UpdateAI(uint32 diff) override
- {
- UpdateRandomMovement(diff);
-
- if (!UpdateVictim())
- return;
+ UpdateRandomMovement(diff);
- if (_woundTimer <= diff)
- {
- _woundTimer = urandms(14, 18);
- DoCastVictim(SPELL_MORTAL_WOUND);
- }
- else
- _woundTimer -= diff;
-
- DoMeleeAttackIfReady();
- }
+ if (!UpdateVictim())
+ return;
- void JustDied(Unit* killer) override
+ if (_woundTimer <= diff)
{
- if (Creature* kelThuzad = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KELTHUZAD)))
- kelThuzad->AI()->DoAction(ACTION_ABOMINATION_DIED);
- npc_kelthuzad_minionAI::JustDied(killer);
+ _woundTimer = urandms(14, 18);
+ DoCastVictim(SPELL_MORTAL_WOUND);
}
+ else
+ _woundTimer -= diff;
- uint32 _woundTimer;
- };
+ DoMeleeAttackIfReady();
+ }
- CreatureAI* GetAI(Creature* creature) const override
+ void JustDied(Unit* killer) override
{
- return GetNaxxramasAI<npc_kelthuzad_abominationAI>(creature);
+ if (Creature* kelThuzad = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KELTHUZAD)))
+ kelThuzad->AI()->DoAction(ACTION_ABOMINATION_DIED);
+ npc_kelthuzad_minionAI::JustDied(killer);
}
+
+ uint32 _woundTimer;
};
-class npc_kelthuzad_guardian : public CreatureScript
+struct npc_kelthuzad_guardian : public ScriptedAI
{
-public:
- npc_kelthuzad_guardian() : CreatureScript("npc_kelthuzad_guardian") { }
-
- struct npc_kelthuzad_guardianAI : public ScriptedAI
- {
- public:
- npc_kelthuzad_guardianAI(Creature* creature) : ScriptedAI(creature), instance(creature->GetInstanceScript()), _visibilityTimer(0), _bloodTapTimer(0) { }
+ public:
+ npc_kelthuzad_guardian(Creature* creature) : ScriptedAI(creature), instance(creature->GetInstanceScript()), _visibilityTimer(0), _bloodTapTimer(0) { }
- void DoAction(int32 action) override
+ void DoAction(int32 action) override
+ {
+ switch (action)
{
- switch (action)
- {
- case ACTION_JUST_SUMMONED:
- me->SetVisible(false);
- me->SetHomePosition(me->GetPosition());
- DoZoneInCombat();
- me->SetCombatPulseDelay(5);
- _visibilityTimer = 2 * IN_MILLISECONDS;
- _bloodTapTimer = 25 * IN_MILLISECONDS;
- break;
- case ACTION_KELTHUZAD_DIED:
- Talk(EMOTE_GUARDIAN_FLEE);
- me->SetReactState(REACT_PASSIVE);
- me->RemoveAllAuras();
- me->CombatStop();
- me->StopMoving();
- me->SetImmuneToPC(true);
- me->DespawnOrUnsummon(30s); // just in case anything interrupts the movement
- me->GetMotionMaster()->MoveTargetedHome();
- break;
- default:
- break;
- }
+ case ACTION_JUST_SUMMONED:
+ me->SetVisible(false);
+ me->SetHomePosition(me->GetPosition());
+ DoZoneInCombat();
+ me->SetCombatPulseDelay(5);
+ _visibilityTimer = 2 * IN_MILLISECONDS;
+ _bloodTapTimer = 25 * IN_MILLISECONDS;
+ break;
+ case ACTION_KELTHUZAD_DIED:
+ Talk(EMOTE_GUARDIAN_FLEE);
+ me->SetReactState(REACT_PASSIVE);
+ me->RemoveAllAuras();
+ me->CombatStop();
+ me->StopMoving();
+ me->SetImmuneToPC(true);
+ me->DespawnOrUnsummon(30s); // just in case anything interrupts the movement
+ me->GetMotionMaster()->MoveTargetedHome();
+ break;
+ default:
+ break;
}
+ }
- void EnterEvadeMode(EvadeReason why) override
- {
- if (Creature* kelthuzad = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KELTHUZAD)))
- kelthuzad->AI()->EnterEvadeMode();
- ScriptedAI::EnterEvadeMode(why);
- }
+ void EnterEvadeMode(EvadeReason why) override
+ {
+ if (Creature* kelthuzad = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KELTHUZAD)))
+ kelthuzad->AI()->EnterEvadeMode();
+ ScriptedAI::EnterEvadeMode(why);
+ }
- void JustReachedHome() override
- {
- me->DespawnOrUnsummon();
- }
+ void JustReachedHome() override
+ {
+ me->DespawnOrUnsummon();
+ }
- void Reset() override
- {
- me->SetCombatPulseDelay(0);
- ScriptedAI::Reset();
- }
+ void Reset() override
+ {
+ me->SetCombatPulseDelay(0);
+ ScriptedAI::Reset();
+ }
- void UpdateAI(uint32 diff) override
+ void UpdateAI(uint32 diff) override
+ {
+ if (_visibilityTimer)
{
- if (_visibilityTimer)
+ if (diff > _visibilityTimer)
+ _visibilityTimer -= diff;
+ else
{
- if (diff > _visibilityTimer)
- _visibilityTimer -= diff;
- else
- {
- me->SetVisible(true);
- Talk(EMOTE_GUARDIAN_APPEAR);
- _visibilityTimer = 0;
- }
+ me->SetVisible(true);
+ Talk(EMOTE_GUARDIAN_APPEAR);
+ _visibilityTimer = 0;
}
+ }
- if (!UpdateVictim())
- return;
-
- if (_bloodTapTimer <= diff)
- {
- DoCastVictim(SPELL_BLOOD_TAP);
- _bloodTapTimer = urandms(18, 26);
- }
- else
- _bloodTapTimer -= diff;
+ if (!UpdateVictim())
+ return;
- DoMeleeAttackIfReady();
+ if (_bloodTapTimer <= diff)
+ {
+ DoCastVictim(SPELL_BLOOD_TAP);
+ _bloodTapTimer = urandms(18, 26);
}
+ else
+ _bloodTapTimer -= diff;
- private:
- InstanceScript* const instance;
- uint32 _visibilityTimer;
- uint32 _bloodTapTimer;
- };
+ DoMeleeAttackIfReady();
+ }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<npc_kelthuzad_guardianAI>(creature);
- }
+ private:
+ InstanceScript* const instance;
+ uint32 _visibilityTimer;
+ uint32 _bloodTapTimer;
};
-class spell_kelthuzad_chains : public SpellScriptLoader
+class spell_kelthuzad_chains : public AuraScript
{
-public:
- spell_kelthuzad_chains() : SpellScriptLoader("spell_kelthuzad_chains") { }
+ PrepareAuraScript(spell_kelthuzad_chains);
- class spell_kelthuzad_chains_AuraScript : public AuraScript
+ void HandleApply(AuraEffect const* aurEff, AuraEffectHandleModes mode)
{
- PrepareAuraScript(spell_kelthuzad_chains_AuraScript);
-
- void HandleApply(AuraEffect const* aurEff, AuraEffectHandleModes mode)
- {
- aurEff->HandleAuraModScale(GetTargetApplication(), mode, true);
- }
-
- void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes mode)
- {
- aurEff->HandleAuraModScale(GetTargetApplication(), mode, false);
- }
+ aurEff->HandleAuraModScale(GetTargetApplication(), mode, true);
+ }
- void Register() override
- {
- AfterEffectApply += AuraEffectApplyFn(spell_kelthuzad_chains_AuraScript::HandleApply, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, AURA_EFFECT_HANDLE_REAL);
- AfterEffectRemove += AuraEffectApplyFn(spell_kelthuzad_chains_AuraScript::HandleRemove, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, AURA_EFFECT_HANDLE_REAL);
- }
- };
+ void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes mode)
+ {
+ aurEff->HandleAuraModScale(GetTargetApplication(), mode, false);
+ }
- AuraScript* GetAuraScript() const override
+ void Register() override
{
- return new spell_kelthuzad_chains_AuraScript();
+ AfterEffectApply += AuraEffectApplyFn(spell_kelthuzad_chains::HandleApply, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, AURA_EFFECT_HANDLE_REAL);
+ AfterEffectRemove += AuraEffectApplyFn(spell_kelthuzad_chains::HandleRemove, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE, AURA_EFFECT_HANDLE_REAL);
}
};
-class spell_kelthuzad_detonate_mana : public SpellScriptLoader
+class spell_kelthuzad_detonate_mana : public AuraScript
{
-public:
- spell_kelthuzad_detonate_mana() : SpellScriptLoader("spell_kelthuzad_detonate_mana") { }
+ PrepareAuraScript(spell_kelthuzad_detonate_mana);
- class spell_kelthuzad_detonate_mana_AuraScript : public AuraScript
+ bool Validate(SpellInfo const* /*spell*/) override
{
- PrepareAuraScript(spell_kelthuzad_detonate_mana_AuraScript);
-
- bool Validate(SpellInfo const* /*spell*/) override
- {
- return ValidateSpellInfo({ SPELL_MANA_DETONATION_DAMAGE });
- }
-
- void HandleScript(AuraEffect const* aurEff)
- {
- PreventDefaultAction();
+ return ValidateSpellInfo({ SPELL_MANA_DETONATION_DAMAGE });
+ }
- Unit* target = GetTarget();
- if (int32 mana = int32(target->GetMaxPower(POWER_MANA) / 10))
- {
- mana = target->ModifyPower(POWER_MANA, -mana);
- CastSpellExtraArgs args(aurEff);
- args.AddSpellBP0(-mana * 10);
- target->CastSpell(target, SPELL_MANA_DETONATION_DAMAGE, args);
- }
- }
+ void HandleScript(AuraEffect const* aurEff)
+ {
+ PreventDefaultAction();
- void Register() override
+ Unit* target = GetTarget();
+ if (int32 mana = int32(target->GetMaxPower(POWER_MANA) / 10))
{
- OnEffectPeriodic += AuraEffectPeriodicFn(spell_kelthuzad_detonate_mana_AuraScript::HandleScript, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ mana = target->ModifyPower(POWER_MANA, -mana);
+ CastSpellExtraArgs args(aurEff);
+ args.AddSpellBP0(-mana * 10);
+ target->CastSpell(target, SPELL_MANA_DETONATION_DAMAGE, args);
}
- };
+ }
- AuraScript* GetAuraScript() const override
+ void Register() override
{
- return new spell_kelthuzad_detonate_mana_AuraScript();
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_kelthuzad_detonate_mana::HandleScript, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
@@ -1025,13 +948,13 @@ class achievement_just_cant_get_enough : public AchievementCriteriaScript
void AddSC_boss_kelthuzad()
{
- new boss_kelthuzad();
- new npc_kelthuzad_skeleton();
- new npc_kelthuzad_banshee();
- new npc_kelthuzad_abomination();
- new npc_kelthuzad_guardian();
- new spell_kelthuzad_chains();
- new spell_kelthuzad_detonate_mana();
+ RegisterNaxxramasCreatureAI(boss_kelthuzad);
+ RegisterNaxxramasCreatureAI(npc_kelthuzad_skeleton);
+ RegisterNaxxramasCreatureAI(npc_kelthuzad_banshee);
+ RegisterNaxxramasCreatureAI(npc_kelthuzad_abomination);
+ RegisterNaxxramasCreatureAI(npc_kelthuzad_guardian);
+ RegisterSpellScript(spell_kelthuzad_chains);
+ RegisterSpellScript(spell_kelthuzad_detonate_mana);
RegisterSpellScript(spell_kelthuzad_frost_blast);
new at_kelthuzad_center();
new achievement_just_cant_get_enough();