diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp index 0a8d6473e72..dd72982a514 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp @@ -73,268 +73,257 @@ const uint32 SummonGuardianSpells[N_GUARDIAN_SPELLS] = { 29239, 29256, 29268 }; #define SPELL_BLINK RAND(29208, 29209, 29210, 29211) -class boss_noth : public CreatureScript +struct boss_noth : public BossAI { -public: - boss_noth() : CreatureScript("boss_noth") { } - - struct boss_nothAI : public BossAI + boss_noth(Creature* creature) : BossAI(creature, BOSS_NOTH), balconyCount(0), justBlinked(false) { - boss_nothAI(Creature* creature) : BossAI(creature, BOSS_NOTH), balconyCount(0), justBlinked(false) + std::copy(SummonWarriorSpells, SummonWarriorSpells + N_WARRIOR_SPELLS, _SummonWarriorSpells); + std::copy(SummonChampionSpells, SummonChampionSpells + N_CHAMPION_SPELLS, _SummonChampionSpells); + std::copy(SummonGuardianSpells, SummonGuardianSpells + N_GUARDIAN_SPELLS, _SummonGuardianSpells); + + events.SetPhase(PHASE_NONE); + } + + void EnterEvadeMode(EvadeReason why) override + { + // in case we reset during balcony phase + if (events.IsInPhase(PHASE_BALCONY)) + DoCastAOE(SPELL_TELEPORT_BACK); + BossAI::EnterEvadeMode(why); + } + + void Reset() override + { + _Reset(); + + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + balconyCount = 0; + events.SetPhase(PHASE_NONE); + justBlinked = false; + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + EnterPhaseGround(); + } + + void EnterPhaseGround() + { + events.SetPhase(PHASE_GROUND); + + DoZoneInCombat(); + + if (!me->IsThreatened()) + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + else { - std::copy(SummonWarriorSpells, SummonWarriorSpells + N_WARRIOR_SPELLS, _SummonWarriorSpells); - std::copy(SummonChampionSpells, SummonChampionSpells + N_CHAMPION_SPELLS, _SummonChampionSpells); - std::copy(SummonGuardianSpells, SummonGuardianSpells + N_GUARDIAN_SPELLS, _SummonGuardianSpells); - - events.SetPhase(PHASE_NONE); - } - - void EnterEvadeMode(EvadeReason why) override - { - // in case we reset during balcony phase - if (events.IsInPhase(PHASE_BALCONY)) - DoCastAOE(SPELL_TELEPORT_BACK); - BossAI::EnterEvadeMode(why); - } - - void Reset() override - { - _Reset(); - - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - - balconyCount = 0; - events.SetPhase(PHASE_NONE); - justBlinked = false; - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - Talk(SAY_AGGRO); - EnterPhaseGround(); - } - - void EnterPhaseGround() - { - events.SetPhase(PHASE_GROUND); - - DoZoneInCombat(); - - if (!me->IsThreatened()) - EnterEvadeMode(EVADE_REASON_NO_HOSTILES); - else + uint8 timeGround; + switch (balconyCount) { - uint8 timeGround; - switch (balconyCount) - { - case 0: - timeGround = 90; - break; - case 1: - timeGround = 110; - break; - case 2: - default: - timeGround = 180; - } - events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, Seconds(2), 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_BALCONY, Seconds(timeGround), 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_CURSE, randtime(Seconds(10), Seconds(25)), 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_WARRIOR, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND); - if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) - events.ScheduleEvent(EVENT_BLINK, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND); + case 0: + timeGround = 90; + break; + case 1: + timeGround = 110; + break; + case 2: + default: + timeGround = 180; } + events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, Seconds(2), 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_BALCONY, Seconds(timeGround), 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_CURSE, randtime(Seconds(10), Seconds(25)), 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_WARRIOR, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND); + if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + events.ScheduleEvent(EVENT_BLINK, randtime(Seconds(20), Seconds(30)), 0, PHASE_GROUND); } + } - void KilledUnit(Unit* victim) override + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + summon->setActive(true); + summon->SetFarVisible(true); + summon->AI()->DoZoneInCombat(); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } + + void DamageTaken(Unit* /*who*/, uint32& damage) override // prevent noth from somehow dying in the balcony phase + { + if (!events.IsInPhase(PHASE_BALCONY)) + return; + if (damage < me->GetHealth()) + return; + + me->SetHealth(1u); + damage = 0u; + } + + void HandleSummon(uint32* spellsList, const uint8 nSpells, uint8 num) + { // this ensures we do not spawn two mobs using the same spell (<=> in the same position) if we can help it + while (num) + for (uint8 it = 0; it < nSpells && num; ++it) + { + num--; + uint8 selected = urand(it, nSpells - 1); + DoCastAOE(spellsList[selected]); + if (selected != it) // shuffle the selected into the part of the array that is no longer being searched + std::swap(spellsList[selected], spellsList[it]); + } + } + + void CastSummon(uint8 nWarrior, uint8 nChampion, uint8 nGuardian) + { + HandleSummon(_SummonWarriorSpells, N_WARRIOR_SPELLS, nWarrior); + HandleSummon(_SummonChampionSpells, N_CHAMPION_SPELLS, nChampion); + HandleSummon(_SummonGuardianSpells, N_GUARDIAN_SPELLS, nGuardian); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - summon->setActive(true); - summon->SetFarVisible(true); - summon->AI()->DoZoneInCombat(); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - Talk(SAY_DEATH); - } - - void DamageTaken(Unit* /*who*/, uint32& damage) override // prevent noth from somehow dying in the balcony phase - { - if (!events.IsInPhase(PHASE_BALCONY)) - return; - if (damage < me->GetHealth()) - return; - - me->SetHealth(1u); - damage = 0u; - } - - void HandleSummon(uint32* spellsList, const uint8 nSpells, uint8 num) - { // this ensures we do not spawn two mobs using the same spell (<=> in the same position) if we can help it - while (num) - for (uint8 it = 0; it < nSpells && num; ++it) + switch (eventId) + { + case EVENT_CURSE: { - num--; - uint8 selected = urand(it, nSpells - 1); - DoCastAOE(spellsList[selected]); - if (selected != it) // shuffle the selected into the part of the array that is no longer being searched - std::swap(spellsList[selected], spellsList[it]); + DoCastAOE(SPELL_CURSE); + events.Repeat(randtime(Seconds(50), Seconds(70))); + break; } - } + case EVENT_WARRIOR: + Talk(SAY_SUMMON); + Talk(EMOTE_SUMMON); - void CastSummon(uint8 nWarrior, uint8 nChampion, uint8 nGuardian) - { - HandleSummon(_SummonWarriorSpells, N_WARRIOR_SPELLS, nWarrior); - HandleSummon(_SummonChampionSpells, N_CHAMPION_SPELLS, nChampion); - HandleSummon(_SummonGuardianSpells, N_GUARDIAN_SPELLS, nGuardian); - } + CastSummon(RAID_MODE(2, 3), 0, 0); - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + events.Repeat(Seconds(40)); + break; + case EVENT_BLINK: + DoCastAOE(SPELL_CRIPPLE, true); + DoCastAOE(SPELL_BLINK); + ResetThreatList(); + justBlinked = true; - events.Update(diff); + events.Repeat(Seconds(40)); + break; + case EVENT_BALCONY: + events.SetPhase(PHASE_BALCONY); + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->AttackStop(); + me->StopMoving(); + me->RemoveAllAuras(); + + events.ScheduleEvent(EVENT_BALCONY_TELEPORT, Seconds(3), 0, PHASE_BALCONY); + events.ScheduleEvent(EVENT_WAVE, randtime(Seconds(5), Seconds(8)), 0, PHASE_BALCONY); + + uint8 timeBalcony; + switch (balconyCount) + { + case 0: + timeBalcony = 70; + break; + case 1: + timeBalcony = 97; + break; + case 2: + default: + timeBalcony = 120; + break; + } + events.ScheduleEvent(EVENT_GROUND, Seconds(timeBalcony), 0, PHASE_BALCONY); + break; + case EVENT_BALCONY_TELEPORT: + Talk(EMOTE_TELEPORT_1); + DoCastAOE(SPELL_TELEPORT); + break; + case EVENT_WAVE: + Talk(EMOTE_SUMMON_WAVE); + switch (balconyCount) + { + case 0: + CastSummon(0, RAID_MODE(2, 4), 0); + break; + case 1: + CastSummon(0, RAID_MODE(1, 2), RAID_MODE(1, 2)); + break; + case 2: + CastSummon(0, 0, RAID_MODE(2, 4)); + break; + default: + CastSummon(0, RAID_MODE(5, 10), RAID_MODE(5, 10)); + break; + } + events.Repeat(randtime(Seconds(30), Seconds(45))); + break; + case EVENT_GROUND: + ++balconyCount; + + DoCastAOE(SPELL_TELEPORT_BACK); + Talk(EMOTE_TELEPORT_2); + + EnterPhaseGround(); + break; + case EVENT_GROUND_ATTACKABLE: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + break; + } if (me->HasUnitState(UNIT_STATE_CASTING)) return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_CURSE: - { - DoCastAOE(SPELL_CURSE); - events.Repeat(randtime(Seconds(50), Seconds(70))); - break; - } - case EVENT_WARRIOR: - Talk(SAY_SUMMON); - Talk(EMOTE_SUMMON); - - CastSummon(RAID_MODE(2, 3), 0, 0); - - events.Repeat(Seconds(40)); - break; - case EVENT_BLINK: - DoCastAOE(SPELL_CRIPPLE, true); - DoCastAOE(SPELL_BLINK); - ResetThreatList(); - justBlinked = true; - - events.Repeat(Seconds(40)); - break; - case EVENT_BALCONY: - events.SetPhase(PHASE_BALCONY); - me->SetReactState(REACT_PASSIVE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->AttackStop(); - me->StopMoving(); - me->RemoveAllAuras(); - - events.ScheduleEvent(EVENT_BALCONY_TELEPORT, Seconds(3), 0, PHASE_BALCONY); - events.ScheduleEvent(EVENT_WAVE, randtime(Seconds(5), Seconds(8)), 0, PHASE_BALCONY); - - uint8 timeBalcony; - switch (balconyCount) - { - case 0: - timeBalcony = 70; - break; - case 1: - timeBalcony = 97; - break; - case 2: - default: - timeBalcony = 120; - break; - } - events.ScheduleEvent(EVENT_GROUND, Seconds(timeBalcony), 0, PHASE_BALCONY); - break; - case EVENT_BALCONY_TELEPORT: - Talk(EMOTE_TELEPORT_1); - DoCastAOE(SPELL_TELEPORT); - break; - case EVENT_WAVE: - Talk(EMOTE_SUMMON_WAVE); - switch (balconyCount) - { - case 0: - CastSummon(0, RAID_MODE(2, 4), 0); - break; - case 1: - CastSummon(0, RAID_MODE(1, 2), RAID_MODE(1, 2)); - break; - case 2: - CastSummon(0, 0, RAID_MODE(2, 4)); - break; - default: - CastSummon(0, RAID_MODE(5, 10), RAID_MODE(5, 10)); - break; - } - events.Repeat(randtime(Seconds(30), Seconds(45))); - break; - case EVENT_GROUND: - ++balconyCount; - - DoCastAOE(SPELL_TELEPORT_BACK); - Talk(EMOTE_TELEPORT_2); - - EnterPhaseGround(); - break; - case EVENT_GROUND_ATTACKABLE: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_AGGRESSIVE); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - if (events.IsInPhase(PHASE_GROUND)) - { - /* workaround for movechase breaking after blinking - without this noth would just stand there unless his current target moves */ - if (justBlinked && me->GetVictim() && !me->IsWithinMeleeRange(me->EnsureVictim())) - { - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveChase(me->EnsureVictim()); - justBlinked = false; - } - else - DoMeleeAttackIfReady(); - } } - private: - uint32 balconyCount; - - bool justBlinked; - - uint32 _SummonWarriorSpells[N_WARRIOR_SPELLS]; - uint32 _SummonChampionSpells[N_CHAMPION_SPELLS]; - uint32 _SummonGuardianSpells[N_GUARDIAN_SPELLS]; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetNaxxramasAI(creature); + if (events.IsInPhase(PHASE_GROUND)) + { + /* workaround for movechase breaking after blinking + without this noth would just stand there unless his current target moves */ + if (justBlinked && me->GetVictim() && !me->IsWithinMeleeRange(me->EnsureVictim())) + { + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveChase(me->EnsureVictim()); + justBlinked = false; + } + else + DoMeleeAttackIfReady(); + } } + + private: + uint32 balconyCount; + + bool justBlinked; + + uint32 _SummonWarriorSpells[N_WARRIOR_SPELLS]; + uint32 _SummonChampionSpells[N_CHAMPION_SPELLS]; + uint32 _SummonGuardianSpells[N_GUARDIAN_SPELLS]; }; void AddSC_boss_noth() { - new boss_noth(); + RegisterNaxxramasCreatureAI(boss_noth); }