aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoroffl <11556157+offl@users.noreply.github.com>2021-04-13 10:27:00 +0300
committerShauren <shauren.trinity@gmail.com>2022-03-08 16:18:57 +0100
commitee48dda445496a3a52a021d75b0258639a31cf46 (patch)
treedfab75e5a5c6fa0ca24689a34d0cc6a4f0ef80c5 /src
parent848dfd09e5fe64cef8f2bbbb22c93caf80fbe154 (diff)
Scripts/Naxxramas: Update Gothik to new model (#26381)
Co-authored-by: offl <offl@users.noreply.github.com> (cherry picked from commit 9c72ca2b04c5595d2a637f0827eb6b2374734c70)
Diffstat (limited to 'src')
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp948
1 files changed, 418 insertions, 530 deletions
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp
index 817dff2326f..8f59f52e852 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp
@@ -51,9 +51,6 @@ enum Spells
/* spectral knight spells */
SPELL_WHIRLWIND = 56408,
- /* spectral rider spells */
- SPELL_UNHOLY_FRENZY = 55648,
-
/* spectral horse spells */
SPELL_STOMP = 27993,
@@ -78,11 +75,12 @@ enum Spells
SPELL_TELEPORT_DEAD = 28025,
SPELL_TELEPORT_LIVE = 28026
};
-#define SPELLHELPER_DEATH_PLAGUE RAID_MODE<uint32>(55604, 55645)
-#define SPELLHELPER_SHADOW_BOLT_VOLLEY RAID_MODE<uint32>(27831, 55638)
-#define SPELLHELPER_ARCANE_EXPLOSION RAID_MODE<uint32>(27989, 56407)
-#define SPELLHELPER_DRAIN_LIFE RAID_MODE<uint32>(27994, 55646)
-#define SPELLHELPER_UNHOLY_FRENZY RAID_MODE<uint32>(SPELL_UNHOLY_FRENZY,27995)
+
+#define SPELL_DEATH_PLAGUE RAID_MODE<uint32>(55604, 55645)
+#define SPELL_SHADOW_BOLT_VOLLEY RAID_MODE<uint32>(27831, 55638)
+#define SPELL_ARCANE_EXPLOSION RAID_MODE<uint32>(27989, 56407)
+#define SPELL_DRAIN_LIFE RAID_MODE<uint32>(27994, 55646)
+#define SPELL_UNHOLY_FRENZY RAID_MODE<uint32>(55648,27995)
enum Creatures
{
@@ -288,274 +286,263 @@ const GothikWaveData waves25 =
// 0-1 are living side soul triggers, 2-3 are spectral side soul triggers, 4 is living rider spawn trigger, 5-7 are living other spawn trigger, 8-12 are skull pile triggers
const uint32 CGUID_TRIGGER = 127618;
/* Creature AI */
-class boss_gothik : public CreatureScript
+struct boss_gothik : public BossAI
{
- public:
- boss_gothik() : CreatureScript("boss_gothik") { }
+ boss_gothik(Creature* creature) : BossAI(creature, BOSS_GOTHIK)
+ {
+ Initialize();
+ }
+
+ void Initialize()
+ {
+ _waveCount = 0;
+ _gateCanOpen = false;
+ _gateIsOpen = true;
+ _lastTeleportDead = false;
+ }
- struct boss_gothikAI : public BossAI
+ void Reset() override
+ {
+ me->SetReactState(REACT_PASSIVE);
+ instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE);
+ _Reset();
+ Initialize();
+ }
+
+ void JustEngagedWith(Unit* who) override
+ {
+ BossAI::JustEngagedWith(who);
+ events.SetPhase(PHASE_ONE);
+ events.ScheduleEvent(EVENT_SUMMON, Seconds(25), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_DOORS_UNLOCK, Minutes(3) + Seconds(25), 0, PHASE_ONE);
+ events.ScheduleEvent(EVENT_PHASE_TWO, Minutes(4) + Seconds(30), 0, PHASE_ONE);
+ Talk(SAY_INTRO_1);
+ events.ScheduleEvent(EVENT_INTRO_2, Seconds(4));
+ events.ScheduleEvent(EVENT_INTRO_3, Seconds(9));
+ events.ScheduleEvent(EVENT_INTRO_4, Seconds(14));
+ instance->SetData(DATA_GOTHIK_GATE, GO_STATE_READY);
+ _gateIsOpen = false;
+ }
+
+ void JustSummoned(Creature* summon) override
+ {
+ summons.Summon(summon);
+ if (me->IsInCombat())
{
- boss_gothikAI(Creature* creature) : BossAI(creature, BOSS_GOTHIK)
- {
- Initialize();
- }
+ summon->AI()->DoAction(_gateIsOpen ? ACTION_GATE_OPENED : ACTION_ACQUIRE_TARGET);
+ summon->SetCombatPulseDelay(5);
+ }
+ else
+ summon->DespawnOrUnsummon();
+ }
- void Initialize()
- {
- _waveCount = 0;
- _gateCanOpen = false;
- _gateIsOpen = true;
- _lastTeleportDead = false;
- }
+ void SummonedCreatureDespawn(Creature* summon) override
+ {
+ summons.Despawn(summon);
+ }
- void Reset() override
- {
- me->SetReactState(REACT_PASSIVE);
- instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE);
- _Reset();
- Initialize();
- }
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim && victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_KILL);
+ }
- void JustEngagedWith(Unit* who) override
- {
- BossAI::JustEngagedWith(who);
- events.SetPhase(PHASE_ONE);
- events.ScheduleEvent(EVENT_SUMMON, Seconds(25), 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_DOORS_UNLOCK, Minutes(3) + Seconds(25), 0, PHASE_ONE);
- events.ScheduleEvent(EVENT_PHASE_TWO, Minutes(4) + Seconds(30), 0, PHASE_ONE);
- Talk(SAY_INTRO_1);
- events.ScheduleEvent(EVENT_INTRO_2, Seconds(4));
- events.ScheduleEvent(EVENT_INTRO_3, Seconds(9));
- events.ScheduleEvent(EVENT_INTRO_4, Seconds(14));
- instance->SetData(DATA_GOTHIK_GATE, GO_STATE_READY);
- _gateIsOpen = false;
- }
+ void JustDied(Unit* /*killer*/) override
+ {
+ _JustDied();
+ Talk(SAY_DEATH);
+ instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE);
+ _gateIsOpen = false;
+ }
- void JustSummoned(Creature* summon) override
- {
- summons.Summon(summon);
- if (me->IsInCombat())
- {
- summon->AI()->DoAction(_gateIsOpen ? ACTION_GATE_OPENED : ACTION_ACQUIRE_TARGET);
- summon->SetCombatPulseDelay(5);
- }
- else
- summon->DespawnOrUnsummon();
- }
+ void OpenGate()
+ {
+ if (_gateIsOpen)
+ return;
+ instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE);
+ Talk(EMOTE_GATE_OPENED);
+ _gateIsOpen = true;
- void SummonedCreatureDespawn(Creature* summon) override
- {
- summons.Despawn(summon);
- }
+ for (ObjectGuid summonGuid : summons)
+ {
+ if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid))
+ summon->AI()->DoAction(ACTION_GATE_OPENED);
+ if (summons.empty()) // ACTION_GATE_OPENED may cause an evade, despawning summons and invalidating our iterator
+ break;
+ }
+ }
- void KilledUnit(Unit* victim) override
- {
- if (victim && victim->GetTypeId() == TYPEID_PLAYER)
- Talk(SAY_KILL);
- }
+ void DamageTaken(Unit* /*who*/, uint32& damage) override
+ {
+ if (!events.IsInPhase(PHASE_TWO))
+ damage = 0;
+ }
- void JustDied(Unit* /*killer*/) override
- {
- _JustDied();
- Talk(SAY_DEATH);
- instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE);
- _gateIsOpen = false;
- }
+ void DoAction(int32 action) override
+ {
+ switch (action)
+ {
+ case ACTION_MINION_EVADE:
+ if (_gateIsOpen || me->GetThreatManager().IsThreatListEmpty())
+ return EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
+ if (_gateCanOpen)
+ OpenGate();
+ break;
+ }
+ }
- void OpenGate()
- {
- if (_gateIsOpen)
- return;
- instance->SetData(DATA_GOTHIK_GATE, GO_STATE_ACTIVE);
- Talk(EMOTE_GATE_OPENED);
- _gateIsOpen = true;
+ void EnterEvadeMode(EvadeReason why) override
+ {
+ BossAI::EnterEvadeMode(why);
+ Position const& home = me->GetHomePosition();
+ me->NearTeleportTo(home.GetPositionX(), home.GetPositionY(), home.GetPositionZ(), home.GetOrientation());
+ }
- for (ObjectGuid summonGuid : summons)
- {
- if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid))
- summon->AI()->DoAction(ACTION_GATE_OPENED);
- if (summons.empty()) // ACTION_GATE_OPENED may cause an evade, despawning summons and invalidating our iterator
- break;
- }
- }
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
- void DamageTaken(Unit* /*who*/, uint32& damage) override
+ if (me->HasReactState(REACT_AGGRESSIVE) && !_gateIsOpen && !IsOnSameSide(me, me->GetVictim()))
+ {
+ // NBD: this should only happen in practice if there is nobody left alive on our side (we should open gate)
+ // thus we only do a cursory check to make sure (edge cases?)
+ if (Player* newTarget = FindEligibleTarget(me, _gateIsOpen))
{
- if (!events.IsInPhase(PHASE_TWO))
- damage = 0;
+ ResetThreatList();
+ AddThreat(newTarget, 1.0f);
+ AttackStart(newTarget);
}
+ else
+ OpenGate();
+ }
- void DoAction(int32 action) override
- {
- switch (action)
- {
- case ACTION_MINION_EVADE:
- if (_gateIsOpen || me->GetThreatManager().IsThreatListEmpty())
- return EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
- if (_gateCanOpen)
- OpenGate();
- break;
- }
- }
+ events.Update(diff);
- void EnterEvadeMode(EvadeReason why) override
- {
- BossAI::EnterEvadeMode(why);
- Position const& home = me->GetHomePosition();
- me->NearTeleportTo(home.GetPositionX(), home.GetPositionY(), home.GetPositionZ(), home.GetOrientation());
- }
+ if (!_gateIsOpen && HealthBelowPct(30) && events.IsInPhase(PHASE_TWO))
+ OpenGate();
- void UpdateAI(uint32 diff) override
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
{
- if (!UpdateVictim())
- return;
-
- if (me->HasReactState(REACT_AGGRESSIVE) && !_gateIsOpen && !IsOnSameSide(me, me->GetVictim()))
+ case EVENT_SUMMON:
{
- // NBD: this should only happen in practice if there is nobody left alive on our side (we should open gate)
- // thus we only do a cursory check to make sure (edge cases?)
- if (Player* newTarget = FindEligibleTarget(me, _gateIsOpen))
+ if (RAID_MODE(waves10,waves25).size() <= _waveCount) // bounds check
{
- ResetThreatList();
- AddThreat(newTarget, 1.0f);
- AttackStart(newTarget);
+ TC_LOG_INFO("scripts", "GothikAI: Wave count %d is out of range for difficulty %d.", _waveCount, GetDifficulty());
+ break;
}
- else
- OpenGate();
- }
-
- events.Update(diff);
-
- if (!_gateIsOpen && HealthBelowPct(30) && events.IsInPhase(PHASE_TWO))
- OpenGate();
- while (uint32 eventId = events.ExecuteEvent())
- {
- switch (eventId)
- {
- case EVENT_SUMMON:
+ std::list<Creature*> triggers;
+ me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f);
+ for (GothikWaveEntry entry : RAID_MODE(waves10, waves25)[_waveCount].first)
+ for (uint8 i = 0; i < entry.second; ++i)
{
- if (RAID_MODE(waves10,waves25).size() <= _waveCount) // bounds check
+ // GUID layout is as follows:
+ // CGUID+4: center (back of platform) - primary rider spawn
+ // CGUID+5: north (back of platform) - primary knight spawn
+ // CGUID+6: center (front of platform) - second spawn
+ // CGUID+7: south (front of platform) - primary trainee spawn
+ uint32 targetDBGuid;
+ switch (entry.first)
{
- TC_LOG_INFO("scripts", "GothikAI: Wave count %d is out of range for difficulty %d.", _waveCount, GetDifficulty());
- break;
+ case NPC_LIVE_RIDER: // only spawns from center (back) > north
+ targetDBGuid = (CGUID_TRIGGER + 4) + (i % 2);
+ break;
+ case NPC_LIVE_KNIGHT: // spawns north > center (front) > south
+ targetDBGuid = (CGUID_TRIGGER + 5) + (i % 3);
+ break;
+ case NPC_LIVE_TRAINEE: // spawns south > center (front) > north
+ targetDBGuid = (CGUID_TRIGGER + 7) - (i % 3);
+ break;
+ default:
+ targetDBGuid = 0;
}
- std::list<Creature*> triggers;
- me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f);
- for (GothikWaveEntry entry : RAID_MODE(waves10, waves25)[_waveCount].first)
- for (uint8 i = 0; i < entry.second; ++i)
+ for (Creature* trigger : triggers)
+ if (trigger && trigger->GetSpawnId() == targetDBGuid)
{
- // GUID layout is as follows:
- // CGUID+4: center (back of platform) - primary rider spawn
- // CGUID+5: north (back of platform) - primary knight spawn
- // CGUID+6: center (front of platform) - second spawn
- // CGUID+7: south (front of platform) - primary trainee spawn
- uint32 targetDBGuid;
- switch (entry.first)
- {
- case NPC_LIVE_RIDER: // only spawns from center (back) > north
- targetDBGuid = (CGUID_TRIGGER + 4) + (i % 2);
- break;
- case NPC_LIVE_KNIGHT: // spawns north > center (front) > south
- targetDBGuid = (CGUID_TRIGGER + 5) + (i % 3);
- break;
- case NPC_LIVE_TRAINEE: // spawns south > center (front) > north
- targetDBGuid = (CGUID_TRIGGER + 7) - (i % 3);
- break;
- default:
- targetDBGuid = 0;
- }
-
- for (Creature* trigger : triggers)
- if (trigger && trigger->GetSpawnId() == targetDBGuid)
- {
- DoSummon(entry.first, trigger, 1.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
- break;
- }
+ DoSummon(entry.first, trigger, 1.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
+ break;
}
+ }
- if (uint8 timeToNext = RAID_MODE(waves10, waves25)[_waveCount].second)
- events.Repeat(Seconds(timeToNext));
+ if (uint8 timeToNext = RAID_MODE(waves10, waves25)[_waveCount].second)
+ events.Repeat(Seconds(timeToNext));
- ++_waveCount;
- break;
- }
- case EVENT_DOORS_UNLOCK:
- _gateCanOpen = true;
- for (ObjectGuid summonGuid : summons)
- if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid))
- if (summon->IsAlive() && (!summon->IsInCombat() || summon->IsInEvadeMode()))
- {
- OpenGate();
- break;
- }
- break;
- case EVENT_PHASE_TWO:
- events.SetPhase(PHASE_TWO);
- events.ScheduleEvent(EVENT_TELEPORT, Seconds(20), 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_HARVEST, Seconds(15), 0, PHASE_TWO);
- events.ScheduleEvent(EVENT_RESUME_ATTACK, Seconds(2), 0, PHASE_TWO);
- Talk(SAY_PHASE_TWO);
- Talk(EMOTE_PHASE_TWO);
- me->SetReactState(REACT_PASSIVE);
- ResetThreatList();
- DoCastAOE(SPELL_TELEPORT_LIVE);
- break;
- case EVENT_TELEPORT:
- if (!HealthBelowPct(30))
+ ++_waveCount;
+ break;
+ }
+ case EVENT_DOORS_UNLOCK:
+ _gateCanOpen = true;
+ for (ObjectGuid summonGuid : summons)
+ if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid))
+ if (summon->IsAlive() && (!summon->IsInCombat() || summon->IsInEvadeMode()))
{
- me->CastStop();
- me->AttackStop();
- me->StopMoving();
- me->SetReactState(REACT_PASSIVE);
- ResetThreatList();
- DoCastAOE(_lastTeleportDead ? SPELL_TELEPORT_LIVE : SPELL_TELEPORT_DEAD);
- _lastTeleportDead = !_lastTeleportDead;
-
- events.CancelEvent(EVENT_BOLT);
- events.ScheduleEvent(EVENT_RESUME_ATTACK, 2s, 0, PHASE_TWO);
- events.Repeat(Seconds(20));
+ OpenGate();
+ break;
}
- break;
-
- case EVENT_HARVEST:
- DoCastAOE(SPELL_HARVEST_SOUL, true); // triggered allows this to go "through" shadow bolt
- events.Repeat(Seconds(15));
- break;
- case EVENT_RESUME_ATTACK:
- me->SetReactState(REACT_AGGRESSIVE);
- events.ScheduleEvent(EVENT_BOLT, 0s, 0, PHASE_TWO);
- // return to the start of this method so victim side etc is re-evaluated
- return UpdateAI(0u); // tail recursion for efficiency
- case EVENT_BOLT:
- DoCastVictim(SPELL_SHADOW_BOLT);
- events.Repeat(Seconds(2));
- break;
- case EVENT_INTRO_2:
- Talk(SAY_INTRO_2);
- break;
- case EVENT_INTRO_3:
- Talk(SAY_INTRO_3);
- break;
- case EVENT_INTRO_4:
- Talk(SAY_INTRO_4);
- break;
- }
- }
- }
+ break;
+ case EVENT_PHASE_TWO:
+ events.SetPhase(PHASE_TWO);
+ events.ScheduleEvent(EVENT_TELEPORT, Seconds(20), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_HARVEST, Seconds(15), 0, PHASE_TWO);
+ events.ScheduleEvent(EVENT_RESUME_ATTACK, Seconds(2), 0, PHASE_TWO);
+ Talk(SAY_PHASE_TWO);
+ Talk(EMOTE_PHASE_TWO);
+ me->SetReactState(REACT_PASSIVE);
+ ResetThreatList();
+ DoCastAOE(SPELL_TELEPORT_LIVE);
+ break;
+ case EVENT_TELEPORT:
+ if (!HealthBelowPct(30))
+ {
+ me->CastStop();
+ me->AttackStop();
+ me->StopMoving();
+ me->SetReactState(REACT_PASSIVE);
+ ResetThreatList();
+ DoCastAOE(_lastTeleportDead ? SPELL_TELEPORT_LIVE : SPELL_TELEPORT_DEAD);
+ _lastTeleportDead = !_lastTeleportDead;
- private:
- uint32 _waveCount;
- bool _gateCanOpen;
- bool _gateIsOpen;
- bool _lastTeleportDead;
- };
+ events.CancelEvent(EVENT_BOLT);
+ events.ScheduleEvent(EVENT_RESUME_ATTACK, 2s, 0, PHASE_TWO);
+ events.Repeat(Seconds(20));
+ }
+ break;
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<boss_gothikAI>(creature);
+ case EVENT_HARVEST:
+ DoCastAOE(SPELL_HARVEST_SOUL, true); // triggered allows this to go "through" shadow bolt
+ events.Repeat(Seconds(15));
+ break;
+ case EVENT_RESUME_ATTACK:
+ me->SetReactState(REACT_AGGRESSIVE);
+ events.ScheduleEvent(EVENT_BOLT, 0s, 0, PHASE_TWO);
+ // return to the start of this method so victim side etc is re-evaluated
+ return UpdateAI(0u); // tail recursion for efficiency
+ case EVENT_BOLT:
+ DoCastVictim(SPELL_SHADOW_BOLT);
+ events.Repeat(Seconds(2));
+ break;
+ case EVENT_INTRO_2:
+ Talk(SAY_INTRO_2);
+ break;
+ case EVENT_INTRO_3:
+ Talk(SAY_INTRO_3);
+ break;
+ case EVENT_INTRO_4:
+ Talk(SAY_INTRO_4);
+ break;
+ }
}
+ }
+
+ private:
+ uint32 _waveCount;
+ bool _gateCanOpen;
+ bool _gateIsOpen;
+ bool _lastTeleportDead;
};
struct npc_gothik_minion_baseAI : public ScriptedAI
@@ -636,365 +623,266 @@ struct npc_gothik_minion_baseAI : public ScriptedAI
bool _gateIsOpen;
};
-class npc_gothik_minion_livingtrainee : public CreatureScript
+struct npc_gothik_minion_livingtrainee : public npc_gothik_minion_baseAI
{
- public:
- npc_gothik_minion_livingtrainee() : CreatureScript("npc_gothik_minion_livingtrainee") { }
-
- struct npc_gothik_minion_livingtraineeAI : public npc_gothik_minion_baseAI
- {
- npc_gothik_minion_livingtraineeAI(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_TRAINEE), _deathPlagueTimer(urandms(5,20)) { }
-
- void _UpdateAI(uint32 diff)
- {
- if (diff < _deathPlagueTimer)
- _deathPlagueTimer -= diff;
- else
- {
- DoCastAOE(SPELLHELPER_DEATH_PLAGUE);
- _deathPlagueTimer = urandms(5, 20);
- }
- DoMeleeAttackIfReady();
- }
- uint32 _deathPlagueTimer;
- };
+ npc_gothik_minion_livingtrainee(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_TRAINEE), _deathPlagueTimer(urandms(5,20)) { }
- CreatureAI* GetAI(Creature* creature) const override
+ void _UpdateAI(uint32 diff)
+ {
+ if (diff < _deathPlagueTimer)
+ _deathPlagueTimer -= diff;
+ else
{
- return GetNaxxramasAI<npc_gothik_minion_livingtraineeAI>(creature);
+ DoCastAOE(SPELL_DEATH_PLAGUE);
+ _deathPlagueTimer = urandms(5, 20);
}
+ DoMeleeAttackIfReady();
+ }
+ uint32 _deathPlagueTimer;
};
-class npc_gothik_minion_livingknight : public CreatureScript
+struct npc_gothik_minion_livingknight : public npc_gothik_minion_baseAI
{
- public:
- npc_gothik_minion_livingknight() : CreatureScript("npc_gothik_minion_livingknight") { }
-
- struct npc_gothik_minion_livingknightAI : public npc_gothik_minion_baseAI
- {
- npc_gothik_minion_livingknightAI(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_DK), _whirlwindTimer(urandms(5,10)) { }
+ npc_gothik_minion_livingknight(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_DK), _whirlwindTimer(urandms(5,10)) { }
- void _UpdateAI(uint32 diff)
- {
- if (diff < _whirlwindTimer)
- _whirlwindTimer -= diff;
- else
- {
- DoCastAOE(SPELL_SHADOW_MARK);
- _whirlwindTimer = urandms(15, 20);
- }
- DoMeleeAttackIfReady();
- }
- uint32 _whirlwindTimer;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
+ void _UpdateAI(uint32 diff)
+ {
+ if (diff < _whirlwindTimer)
+ _whirlwindTimer -= diff;
+ else
{
- return GetNaxxramasAI<npc_gothik_minion_livingknightAI>(creature);
+ DoCastAOE(SPELL_SHADOW_MARK);
+ _whirlwindTimer = urandms(15, 20);
}
+ DoMeleeAttackIfReady();
+ }
+ uint32 _whirlwindTimer;
};
-class npc_gothik_minion_livingrider : public CreatureScript
+struct npc_gothik_minion_livingrider : public npc_gothik_minion_baseAI
{
- public:
- npc_gothik_minion_livingrider() : CreatureScript("npc_gothik_minion_livingrider") { }
-
- struct npc_gothik_minion_livingriderAI : public npc_gothik_minion_baseAI
- {
- npc_gothik_minion_livingriderAI(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_RIDER), _boltVolleyTimer(urandms(5,10)) { }
-
- void _UpdateAI(uint32 diff)
- {
- if (diff < _boltVolleyTimer)
- _boltVolleyTimer -= diff;
- else
- {
- DoCastAOE(SPELLHELPER_SHADOW_BOLT_VOLLEY);
- _boltVolleyTimer = urandms(10, 15);
- }
- if (!me->HasUnitState(UNIT_STATE_CASTING))
- DoMeleeAttackIfReady();
- }
- uint32 _boltVolleyTimer;
- };
+ npc_gothik_minion_livingrider(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_RIDER), _boltVolleyTimer(urandms(5,10)) { }
- CreatureAI* GetAI(Creature* creature) const override
+ void _UpdateAI(uint32 diff)
+ {
+ if (diff < _boltVolleyTimer)
+ _boltVolleyTimer -= diff;
+ else
{
- return GetNaxxramasAI<npc_gothik_minion_livingriderAI>(creature);
+ DoCastAOE(SPELL_SHADOW_BOLT_VOLLEY);
+ _boltVolleyTimer = urandms(10, 15);
}
+ if (!me->HasUnitState(UNIT_STATE_CASTING))
+ DoMeleeAttackIfReady();
+ }
+ uint32 _boltVolleyTimer;
};
-class npc_gothik_minion_spectraltrainee : public CreatureScript
+struct npc_gothik_minion_spectraltrainee : public npc_gothik_minion_baseAI
{
- public:
- npc_gothik_minion_spectraltrainee() : CreatureScript("npc_gothik_minion_spectraltrainee") { }
+ npc_gothik_minion_spectraltrainee(Creature* creature) : npc_gothik_minion_baseAI(creature), _explosionTimer(2 * IN_MILLISECONDS) { }
- struct npc_gothik_minion_spectraltraineeAI : public npc_gothik_minion_baseAI
+ void _UpdateAI(uint32 diff)
{
- npc_gothik_minion_spectraltraineeAI(Creature* creature) : npc_gothik_minion_baseAI(creature), _explosionTimer(2 * IN_MILLISECONDS) { }
-
- void _UpdateAI(uint32 diff)
+ if (diff < _explosionTimer)
+ _explosionTimer -= diff;
+ else
{
- if (diff < _explosionTimer)
- _explosionTimer -= diff;
- else
- {
- DoCastAOE(SPELLHELPER_ARCANE_EXPLOSION);
- _explosionTimer = 2 * IN_MILLISECONDS;
- }
- DoMeleeAttackIfReady();
+ DoCastAOE(SPELL_ARCANE_EXPLOSION);
+ _explosionTimer = 2 * IN_MILLISECONDS;
}
- uint32 _explosionTimer;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<npc_gothik_minion_spectraltraineeAI>(creature);
+ DoMeleeAttackIfReady();
}
+ uint32 _explosionTimer;
};
-class npc_gothik_minion_spectralknight : public CreatureScript
+struct npc_gothik_minion_spectralknight : public npc_gothik_minion_baseAI
{
- public:
- npc_gothik_minion_spectralknight() : CreatureScript("npc_gothik_minion_spectralknight") { }
+ npc_gothik_minion_spectralknight(Creature* creature) : npc_gothik_minion_baseAI(creature), _whirlwindTimer(urandms(15,25)) { }
- struct npc_gothik_minion_spectralknightAI : public npc_gothik_minion_baseAI
+ void _UpdateAI(uint32 diff)
{
- npc_gothik_minion_spectralknightAI(Creature* creature) : npc_gothik_minion_baseAI(creature), _whirlwindTimer(urandms(15,25)) { }
-
- void _UpdateAI(uint32 diff)
+ if (diff < _whirlwindTimer)
+ _whirlwindTimer -= diff;
+ else
{
- if (diff < _whirlwindTimer)
- _whirlwindTimer -= diff;
- else
- {
- DoCastAOE(SPELL_WHIRLWIND);
- _whirlwindTimer = urandms(20, 25);
- }
- DoMeleeAttackIfReady();
+ DoCastAOE(SPELL_WHIRLWIND);
+ _whirlwindTimer = urandms(20, 25);
}
- uint32 _whirlwindTimer;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<npc_gothik_minion_spectralknightAI>(creature);
+ DoMeleeAttackIfReady();
}
+ uint32 _whirlwindTimer;
};
-class npc_gothik_minion_spectralrider : public CreatureScript
+struct npc_gothik_minion_spectralrider : public npc_gothik_minion_baseAI
{
- public:
- npc_gothik_minion_spectralrider() : CreatureScript("npc_gothik_minion_spectralrider") { }
-
- struct npc_gothik_minion_spectralriderAI : public npc_gothik_minion_baseAI
- {
- npc_gothik_minion_spectralriderAI(Creature* creature) : npc_gothik_minion_baseAI(creature), _frenzyTimer(urandms(2,5)), _drainTimer(urandms(8,12)) { }
-
- void _UpdateAI(uint32 diff)
- {
- if (diff < _frenzyTimer)
- _frenzyTimer -= diff;
- else if (me->HasUnitState(UNIT_STATE_CASTING))
- _frenzyTimer = 0;
- else
- { // target priority: knight > other rider > horse > gothik
- std::list<Creature*> potentialTargets = DoFindFriendlyMissingBuff(30.0, SPELLHELPER_UNHOLY_FRENZY);
- Creature *knightTarget = nullptr, *riderTarget = nullptr, *horseTarget = nullptr, *gothikTarget = nullptr;
- for (Creature* pTarget : potentialTargets)
+ npc_gothik_minion_spectralrider(Creature* creature) : npc_gothik_minion_baseAI(creature), _frenzyTimer(urandms(2,5)), _drainTimer(urandms(8,12)) { }
+
+ void _UpdateAI(uint32 diff)
+ {
+ if (diff < _frenzyTimer)
+ _frenzyTimer -= diff;
+ else if (me->HasUnitState(UNIT_STATE_CASTING))
+ _frenzyTimer = 0;
+ else
+ { // target priority: knight > other rider > horse > gothik
+ std::list<Creature*> potentialTargets = DoFindFriendlyMissingBuff(30.0, SPELL_UNHOLY_FRENZY);
+ Creature *knightTarget = nullptr, *riderTarget = nullptr, *horseTarget = nullptr, *gothikTarget = nullptr;
+ for (Creature* pTarget : potentialTargets)
+ {
+ switch (pTarget->GetEntry())
{
- switch (pTarget->GetEntry())
- {
- case NPC_DEAD_KNIGHT:
- knightTarget = pTarget;
- break;
- case NPC_DEAD_RIDER:
- riderTarget = pTarget;
- break;
- case NPC_DEAD_HORSE:
- horseTarget = pTarget;
- break;
- case NPC_GOTHIK:
- gothikTarget = pTarget;
- break;
- }
- if (knightTarget)
+ case NPC_DEAD_KNIGHT:
+ knightTarget = pTarget;
+ break;
+ case NPC_DEAD_RIDER:
+ riderTarget = pTarget;
+ break;
+ case NPC_DEAD_HORSE:
+ horseTarget = pTarget;
+ break;
+ case NPC_GOTHIK:
+ gothikTarget = pTarget;
break;
}
- Creature* target = knightTarget ? knightTarget : riderTarget ? riderTarget : horseTarget ? horseTarget : gothikTarget ? gothikTarget : nullptr;
- if (target)
- DoCast(target, SPELL_UNHOLY_FRENZY);
- _frenzyTimer = 20 * IN_MILLISECONDS;
- }
-
- if (diff < _drainTimer)
- _drainTimer -= diff;
- else
- {
- DoCastVictim(SPELLHELPER_DRAIN_LIFE);
- _drainTimer = urandms(10,15);
+ if (knightTarget)
+ break;
}
+ Creature* target = knightTarget ? knightTarget : riderTarget ? riderTarget : horseTarget ? horseTarget : gothikTarget ? gothikTarget : nullptr;
+ if (target)
+ DoCast(target, SPELL_UNHOLY_FRENZY);
+ _frenzyTimer = 20 * IN_MILLISECONDS;
+ }
- if (!me->HasUnitState(UNIT_STATE_CASTING))
- DoMeleeAttackIfReady();
+ if (diff < _drainTimer)
+ _drainTimer -= diff;
+ else
+ {
+ DoCastVictim(SPELL_DRAIN_LIFE);
+ _drainTimer = urandms(10,15);
}
- uint32 _frenzyTimer, _drainTimer;
- };
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<npc_gothik_minion_spectralriderAI>(creature);
+ if (!me->HasUnitState(UNIT_STATE_CASTING))
+ DoMeleeAttackIfReady();
}
+ uint32 _frenzyTimer, _drainTimer;
};
-class npc_gothik_minion_spectralhorse : public CreatureScript
+struct npc_gothik_minion_spectralhorse : public npc_gothik_minion_baseAI
{
- public:
- npc_gothik_minion_spectralhorse() : CreatureScript("npc_gothik_minion_spectralhorse") { }
+ npc_gothik_minion_spectralhorse(Creature* creature) : npc_gothik_minion_baseAI(creature), _stompTimer(urandms(10,15)) { }
- struct npc_gothik_minion_spectralhorseAI : public npc_gothik_minion_baseAI
+ void _UpdateAI(uint32 diff)
{
- npc_gothik_minion_spectralhorseAI(Creature* creature) : npc_gothik_minion_baseAI(creature), _stompTimer(urandms(10,15)) { }
-
- void _UpdateAI(uint32 diff)
+ if (diff < _stompTimer)
+ _stompTimer -= diff;
+ else
{
- if (diff < _stompTimer)
- _stompTimer -= diff;
- else
- {
- DoCastAOE(SPELL_STOMP);
- _stompTimer = urandms(14, 18);
- }
- DoMeleeAttackIfReady();
+ DoCastAOE(SPELL_STOMP);
+ _stompTimer = urandms(14, 18);
}
- uint32 _stompTimer;
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<npc_gothik_minion_spectralhorseAI>(creature);
+ DoMeleeAttackIfReady();
}
+ uint32 _stompTimer;
};
-class npc_gothik_trigger : public CreatureScript
+struct npc_gothik_trigger : public ScriptedAI
{
-public:
- npc_gothik_trigger() : CreatureScript("npc_gothik_trigger") { }
+ npc_gothik_trigger(Creature* creature) : ScriptedAI(creature) { creature->SetDisableGravity(true); }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return GetNaxxramasAI<npc_gothik_triggerAI>(creature);
- }
+ void EnterEvadeMode(EvadeReason /*why*/) override { }
+ void UpdateAI(uint32 /*diff*/) override { }
+ void JustEngagedWith(Unit* /*who*/) override { }
+ void DamageTaken(Unit* /*who*/, uint32& damage) override { damage = 0; }
- struct npc_gothik_triggerAI : public ScriptedAI
+ Creature* SelectRandomSkullPile()
{
- npc_gothik_triggerAI(Creature* creature) : ScriptedAI(creature) { creature->SetDisableGravity(true); }
+ std::list<Creature*> triggers;
+ me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f);
+ uint32 targetDBGuid = CGUID_TRIGGER + urand(8, 12); // CGUID+8 to CGUID+12 are the triggers for the skull piles on dead side
+ for (Creature* trigger : triggers)
+ if (trigger && trigger->GetSpawnId() == targetDBGuid)
+ return trigger;
- void EnterEvadeMode(EvadeReason /*why*/) override { }
- void UpdateAI(uint32 /*diff*/) override { }
- void JustEngagedWith(Unit* /*who*/) override { }
- void DamageTaken(Unit* /*who*/, uint32& damage) override { damage = 0; }
+ return nullptr;
+ }
- Creature* SelectRandomSkullPile()
+ void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
+ {
+ switch (spellInfo->Id)
{
- std::list<Creature*> triggers;
- me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f);
- uint32 targetDBGuid = CGUID_TRIGGER + urand(8, 12); // CGUID+8 to CGUID+12 are the triggers for the skull piles on dead side
- for (Creature* trigger : triggers)
- if (trigger && trigger->GetSpawnId() == targetDBGuid)
- return trigger;
-
- return nullptr;
- }
-
- void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
- {
- switch (spellInfo->Id)
- {
- case SPELL_ANCHOR_1_TRAINEE:
- DoCastAOE(SPELL_ANCHOR_2_TRAINEE, true);
- break;
- case SPELL_ANCHOR_1_DK:
- DoCastAOE(SPELL_ANCHOR_2_DK, true);
- break;
- case SPELL_ANCHOR_1_RIDER:
- DoCastAOE(SPELL_ANCHOR_2_RIDER, true);
- break;
- case SPELL_ANCHOR_2_TRAINEE:
- if (Creature* target = SelectRandomSkullPile())
- DoCast(target, SPELL_SKULLS_TRAINEE, true);
- break;
- case SPELL_ANCHOR_2_DK:
- if (Creature* target = SelectRandomSkullPile())
- DoCast(target, SPELL_SKULLS_DK, true);
- break;
- case SPELL_ANCHOR_2_RIDER:
- if (Creature* target = SelectRandomSkullPile())
- DoCast(target, SPELL_SKULLS_RIDER, true);
- break;
- case SPELL_SKULLS_TRAINEE:
- DoSummon(NPC_DEAD_TRAINEE, me, 0.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
- break;
- case SPELL_SKULLS_DK:
- DoSummon(NPC_DEAD_KNIGHT, me, 0.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
- break;
- case SPELL_SKULLS_RIDER:
- DoSummon(NPC_DEAD_RIDER, me, 0.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
- DoSummon(NPC_DEAD_HORSE, me, 0.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
- break;
- }
+ case SPELL_ANCHOR_1_TRAINEE:
+ DoCastAOE(SPELL_ANCHOR_2_TRAINEE, true);
+ break;
+ case SPELL_ANCHOR_1_DK:
+ DoCastAOE(SPELL_ANCHOR_2_DK, true);
+ break;
+ case SPELL_ANCHOR_1_RIDER:
+ DoCastAOE(SPELL_ANCHOR_2_RIDER, true);
+ break;
+ case SPELL_ANCHOR_2_TRAINEE:
+ if (Creature* target = SelectRandomSkullPile())
+ DoCast(target, SPELL_SKULLS_TRAINEE, true);
+ break;
+ case SPELL_ANCHOR_2_DK:
+ if (Creature* target = SelectRandomSkullPile())
+ DoCast(target, SPELL_SKULLS_DK, true);
+ break;
+ case SPELL_ANCHOR_2_RIDER:
+ if (Creature* target = SelectRandomSkullPile())
+ DoCast(target, SPELL_SKULLS_RIDER, true);
+ break;
+ case SPELL_SKULLS_TRAINEE:
+ DoSummon(NPC_DEAD_TRAINEE, me, 0.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
+ break;
+ case SPELL_SKULLS_DK:
+ DoSummon(NPC_DEAD_KNIGHT, me, 0.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
+ break;
+ case SPELL_SKULLS_RIDER:
+ DoSummon(NPC_DEAD_RIDER, me, 0.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
+ DoSummon(NPC_DEAD_HORSE, me, 0.0f, 15s, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
+ break;
}
+ }
- // dead side summons are "owned" by gothik
- void JustSummoned(Creature* summon) override
- {
- if (Creature* gothik = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetGuidData(DATA_GOTHIK)))
- gothik->AI()->JustSummoned(summon);
- }
- void SummonedCreatureDespawn(Creature* summon) override
- {
- if (Creature* gothik = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetGuidData(DATA_GOTHIK)))
- gothik->AI()->SummonedCreatureDespawn(summon);
- }
- };
+ // dead side summons are "owned" by gothik
+ void JustSummoned(Creature* summon) override
+ {
+ if (Creature* gothik = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetGuidData(DATA_GOTHIK)))
+ gothik->AI()->JustSummoned(summon);
+ }
+ void SummonedCreatureDespawn(Creature* summon) override
+ {
+ if (Creature* gothik = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetGuidData(DATA_GOTHIK)))
+ gothik->AI()->SummonedCreatureDespawn(summon);
+ }
};
-class spell_gothik_shadow_bolt_volley : public SpellScriptLoader
+class spell_gothik_shadow_bolt_volley : public SpellScript
{
- public:
- spell_gothik_shadow_bolt_volley() : SpellScriptLoader("spell_gothik_shadow_bolt_volley") { }
-
- class spell_gothik_shadow_bolt_volley_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_gothik_shadow_bolt_volley_SpellScript);
+ PrepareSpellScript(spell_gothik_shadow_bolt_volley);
- void FilterTargets(std::list<WorldObject*>& targets)
- {
- targets.remove_if(Trinity::UnitAuraCheck(false, SPELL_SHADOW_MARK));
- }
-
- void Register() override
- {
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gothik_shadow_bolt_volley_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
- }
- };
+ void FilterTargets(std::list<WorldObject*>& targets)
+ {
+ targets.remove_if(Trinity::UnitAuraCheck(false, SPELL_SHADOW_MARK));
+ }
- SpellScript* GetSpellScript() const override
- {
- return new spell_gothik_shadow_bolt_volley_SpellScript();
- }
+ void Register() override
+ {
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gothik_shadow_bolt_volley::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
+ }
};
void AddSC_boss_gothik()
{
- new boss_gothik();
- new npc_gothik_minion_livingtrainee();
- new npc_gothik_minion_livingknight();
- new npc_gothik_minion_livingrider();
- new npc_gothik_minion_spectraltrainee();
- new npc_gothik_minion_spectralknight();
- new npc_gothik_minion_spectralrider();
- new npc_gothik_minion_spectralhorse();
- new npc_gothik_trigger();
- new spell_gothik_shadow_bolt_volley();
+ RegisterNaxxramasCreatureAI(boss_gothik);
+ RegisterNaxxramasCreatureAI(npc_gothik_minion_livingtrainee);
+ RegisterNaxxramasCreatureAI(npc_gothik_minion_livingknight);
+ RegisterNaxxramasCreatureAI(npc_gothik_minion_livingrider);
+ RegisterNaxxramasCreatureAI(npc_gothik_minion_spectraltrainee);
+ RegisterNaxxramasCreatureAI(npc_gothik_minion_spectralknight);
+ RegisterNaxxramasCreatureAI(npc_gothik_minion_spectralrider);
+ RegisterNaxxramasCreatureAI(npc_gothik_minion_spectralhorse);
+ RegisterNaxxramasCreatureAI(npc_gothik_trigger);
+ RegisterSpellScript(spell_gothik_shadow_bolt_volley);
}