diff options
Diffstat (limited to 'src/server/scripts/Northrend')
| -rw-r--r-- | src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp | 934 | 
1 files changed, 412 insertions, 522 deletions
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index dabd3ef350c..aa1110a44ff 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -293,274 +293,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 @@ -641,365 +630,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(SPELL_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(SPELL_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(SPELL_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, SPELLHELPER_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(SPELL_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); } - -        void EnterEvadeMode(EvadeReason /*why*/) override { } -        void UpdateAI(uint32 /*diff*/) override { } -        void JustEngagedWith(Unit* /*who*/) override { } -        void DamageTaken(Unit* /*who*/, uint32& damage) override { damage = 0;  } +        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; -        Creature* SelectRandomSkullPile() -        { -            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; -        } +        return nullptr; +    } -        void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override +    void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override +    { +        switch (spellInfo->Id)          { -            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);  }  | 
