diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp | 948 |
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); } |