diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp index 23f09541b..a312ea1d7 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp @@ -15,16 +15,161 @@ * with this program. If not, see . */ -#include "boss_anubrekhan.h" #include "CreatureScript.h" #include "ScriptedCreature.h" #include "naxxramas.h" +enum Says +{ + SAY_AGGRO = 0, + SAY_GREET = 1, + SAY_SLAY = 2, + EMOTE_LOCUST = 3 +}; -using namespace Anubrekhan; +enum GuardSays +{ + EMOTE_SPAWN = 1, + EMOTE_SCARAB = 2 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Spells +{ + SPELL_IMPALE = 28783, + SPELL_LOCUST_SWARM = 28785, + SPELL_SUMMON_CORPSE_SCRABS_5 = 29105, + SPELL_SUMMON_CORPSE_SCRABS_10 = 28864, + SPELL_BERSERK = 26662 +}; + +enum Misc +{ + NPC_CORPSE_SCARAB = 16698, + NPC_CRYPT_GUARD = 16573, + + ACHIEV_TIMED_START_EVENT = 9891 +}; + +class boss_anubrekhan : public CreatureScript +{ +public: + boss_anubrekhan() : CreatureScript("boss_anubrekhan") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_anubrekhanAI : public BossAI + { + boss_anubrekhanAI(Creature* c) : BossAI(c, BOSS_ANUB) + { + sayGreet = false; + } + + void SummonCryptGuards() + { + if (Is25ManRaid()) + { + me->SummonCreature(NPC_CRYPT_GUARD, 3299.732f, -3502.489f, 287.077f, 2.378f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); + me->SummonCreature(NPC_CRYPT_GUARD, 3299.086f, -3450.929f, 287.077f, 3.999f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); + } + } + + void Reset() override + { + BossAI::Reset(); + SummonCryptGuards(); + me->m_Events.KillAllEvents(false); + } + + void JustSummoned(Creature* cr) override + { + if (me->IsInCombat()) + { + cr->SetInCombatWithZone(); + if (cr->GetEntry() == NPC_CRYPT_GUARD) + cr->AI()->Talk(EMOTE_SPAWN, me); + } + summons.Summon(cr); + } + + void SummonedCreatureDies(Creature* cr, Unit*) override + { + if (cr->GetEntry() == NPC_CRYPT_GUARD) + { + cr->CastSpell(cr, SPELL_SUMMON_CORPSE_SCRABS_10, true, nullptr, nullptr, me->GetGUID()); + cr->AI()->Talk(EMOTE_SCARAB); + } + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); + } + + void KilledUnit(Unit* victim) override + { + if (!victim->IsPlayer()) + return; + + Talk(SAY_SLAY); + victim->CastSpell(victim, SPELL_SUMMON_CORPSE_SCRABS_5, true, nullptr, nullptr, me->GetGUID()); + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + me->CallForHelp(30.0f); + Talk(SAY_AGGRO); + + if (!summons.HasEntry(NPC_CRYPT_GUARD)) + SummonCryptGuards(); + if (!Is25ManRaid()) + { + me->m_Events.AddEventAtOffset([&] + { + me->SummonCreature(NPC_CRYPT_GUARD, 3331.217f, -3476.607f, 287.074f, 3.269f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); + }, Milliseconds(urand(15000, 20000))); + } + + ScheduleTimedEvent(15s, [&] { + DoCastRandomTarget(SPELL_IMPALE); + }, 20s); + + ScheduleTimedEvent(70s, 2min, [&] { + Talk(EMOTE_LOCUST); + DoCastSelf(SPELL_LOCUST_SWARM); + + me->m_Events.AddEventAtOffset([&] + { + me->SummonCreature(NPC_CRYPT_GUARD, 3331.217f, -3476.607f, 287.074f, 3.269f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); + }, 3s); + + }, 90s); + + me->m_Events.AddEventAtOffset([&] + { + DoCastSelf(SPELL_BERSERK, true); + }, 10min); + } + + void MoveInLineOfSight(Unit* who) override + { + if (!sayGreet && who->IsPlayer()) + { + Talk(SAY_GREET); + sayGreet = true; + } + ScriptedAI::MoveInLineOfSight(who); + } + + private: + bool sayGreet; + }; +}; void AddSC_boss_anubrekhan() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.h b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.h deleted file mode 100644 index ef8d96c0c..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.h +++ /dev/null @@ -1,165 +0,0 @@ -#ifndef BOSS_ANUBREKHAN_H_ -#define BOSS_ANUBREKHAN_H_ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Anubrekhan { - -enum Says -{ - SAY_AGGRO = 0, - SAY_GREET = 1, - SAY_SLAY = 2, - EMOTE_LOCUST = 3 -}; - -enum GuardSays -{ - EMOTE_SPAWN = 1, - EMOTE_SCARAB = 2 -}; - -enum Spells -{ - SPELL_IMPALE = 28783, - SPELL_LOCUST_SWARM = 28785, - SPELL_SUMMON_CORPSE_SCRABS_5 = 29105, - SPELL_SUMMON_CORPSE_SCRABS_10 = 28864, - SPELL_BERSERK = 26662 -}; - -enum Misc -{ - NPC_CORPSE_SCARAB = 16698, - NPC_CRYPT_GUARD = 16573, - - ACHIEV_TIMED_START_EVENT = 9891 -}; - -class boss_anubrekhan : public CreatureScript -{ -public: - boss_anubrekhan() : CreatureScript("boss_anubrekhan") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_anubrekhanAI : public BossAI - { - boss_anubrekhanAI(Creature* c) : BossAI(c, BOSS_ANUB) - { - sayGreet = false; - } - - void SummonCryptGuards() - { - if (Is25ManRaid()) - { - me->SummonCreature(NPC_CRYPT_GUARD, 3299.732f, -3502.489f, 287.077f, 2.378f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); - me->SummonCreature(NPC_CRYPT_GUARD, 3299.086f, -3450.929f, 287.077f, 3.999f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); - } - } - - void Reset() override - { - BossAI::Reset(); - SummonCryptGuards(); - me->m_Events.KillAllEvents(false); - } - - void JustSummoned(Creature* cr) override - { - if (me->IsInCombat()) - { - cr->SetInCombatWithZone(); - if (cr->GetEntry() == NPC_CRYPT_GUARD) - cr->AI()->Talk(EMOTE_SPAWN, me); - } - summons.Summon(cr); - } - - void SummonedCreatureDies(Creature* cr, Unit*) override - { - if (cr->GetEntry() == NPC_CRYPT_GUARD) - { - cr->CastSpell(cr, SPELL_SUMMON_CORPSE_SCRABS_10, true, nullptr, nullptr, me->GetGUID()); - cr->AI()->Talk(EMOTE_SCARAB); - } - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); - } - - void KilledUnit(Unit* victim) override - { - if (!victim->IsPlayer()) - return; - - Talk(SAY_SLAY); - victim->CastSpell(victim, SPELL_SUMMON_CORPSE_SCRABS_5, true, nullptr, nullptr, me->GetGUID()); - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - me->CallForHelp(30.0f); - Talk(SAY_AGGRO); - - if (!summons.HasEntry(NPC_CRYPT_GUARD)) - SummonCryptGuards(); - if (!Is25ManRaid()) - { - me->m_Events.AddEventAtOffset([&] - { - me->SummonCreature(NPC_CRYPT_GUARD, 3331.217f, -3476.607f, 287.074f, 3.269f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); - }, Milliseconds(urand(15000, 20000))); - } - - ScheduleTimedEvent(15s, [&] { - DoCastRandomTarget(SPELL_IMPALE); - }, 20s); - - ScheduleTimedEvent(70s, 2min, [&] { - Talk(EMOTE_LOCUST); - DoCastSelf(SPELL_LOCUST_SWARM); - - me->m_Events.AddEventAtOffset([&] - { - me->SummonCreature(NPC_CRYPT_GUARD, 3331.217f, -3476.607f, 287.074f, 3.269f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000); - }, 3s); - - }, 90s); - - me->m_Events.AddEventAtOffset([&] - { - DoCastSelf(SPELL_BERSERK, true); - }, 10min); - } - - void MoveInLineOfSight(Unit* who) override - { - if (!sayGreet && who->IsPlayer()) - { - Talk(SAY_GREET); - sayGreet = true; - } - ScriptedAI::MoveInLineOfSight(who); - } - - private: - bool sayGreet; - }; -}; - -} - -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.h b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.h deleted file mode 100644 index 545ed7fa9..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.h +++ /dev/null @@ -1,152 +0,0 @@ -#ifndef BOSS_FAERLINA_H_ -#define BOSS_FAERLINA_H_ - -#include "CreatureScript.h" -#include "ScriptedCreature.h" -#include "SpellInfo.h" -#include "SpellMgr.h" -#include "naxxramas.h" - -namespace Faerlina { - -enum Yells -{ - SAY_GREET = 0, - SAY_AGGRO = 1, - SAY_SLAY = 2, - SAY_DEATH = 3, - EMOTE_WIDOWS_EMBRACE = 4, - EMOTE_FRENZY = 5, - SAY_FRENZY = 6 -}; - -enum Spells -{ - SPELL_POISON_BOLT_VOLLEY = 28796, - SPELL_RAIN_OF_FIRE = 28794, - SPELL_FRENZY = 28798, - SPELL_WIDOWS_EMBRACE = 28732, -}; - -enum Groups -{ - GROUP_FRENZY = 1 -}; - -enum Misc -{ - NPC_NAXXRAMAS_WORSHIPPER = 16506, - NPC_NAXXRAMAS_FOLLOWER = 16505 -}; - -class boss_faerlina : public CreatureScript -{ -public: - boss_faerlina() : CreatureScript("boss_faerlina") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_faerlinaAI : public BossAI - { - boss_faerlinaAI(Creature* c) : BossAI(c, BOSS_FAERLINA), _introDone(false) { } - - void SummonHelpers() - { - me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3362.66f, -3620.97f, 261.08f, 4.57276f); - me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3344.3f, -3618.31f, 261.08f, 4.69494f); - me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3356.71f, -3620.05f, 261.08f, 4.57276f); - me->SummonCreature(NPC_NAXXRAMAS_WORSHIPPER, 3350.26f, -3619.11f, 261.08f, 4.67748f); - if (Is25ManRaid()) - { - me->SummonCreature(NPC_NAXXRAMAS_FOLLOWER, 3347.49f, -3617.59f, 261.0f, 4.49f); - me->SummonCreature(NPC_NAXXRAMAS_FOLLOWER, 3359.64f, -3619.16f, 261.0f, 4.56f); - } - } - - void Reset() override - { - BossAI::Reset(); - summons.DespawnAll(); - SummonHelpers(); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - me->CallForHelp(VISIBLE_RANGE); - summons.DoZoneInCombat(); - Talk(SAY_AGGRO); - - ScheduleTimedEvent(7s, 15s, [&]{ - if (!me->HasAura(SPELL_WIDOWS_EMBRACE)) - DoCastVictim(SPELL_POISON_BOLT_VOLLEY); - }, 7s, 15s); - - ScheduleTimedEvent(8s, 18s, [&] { - DoCastRandomTarget(SPELL_RAIN_OF_FIRE); - }, 8s, 18s); - - scheduler.Schedule(60s, 80s, GROUP_FRENZY, [this](TaskContext context) { - if (!me->HasAura(SPELL_WIDOWS_EMBRACE)) - { - Talk(SAY_FRENZY); - Talk(EMOTE_FRENZY); - DoCastSelf(SPELL_FRENZY, true); - context.Repeat(1min); - } - else - context.Repeat(30s); - }); - } - - void MoveInLineOfSight(Unit* who) override - { - if (!_introDone && who->IsPlayer()) - { - Talk(SAY_GREET); - _introDone = true; - } - ScriptedAI::MoveInLineOfSight(who); - } - - void KilledUnit(Unit* who) override - { - if (!who->IsPlayer()) - return; - - if (!urand(0, 3)) - Talk(SAY_SLAY); - - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - Talk(SAY_DEATH); - } - - void SpellHit(Unit* caster, SpellInfo const* spell) override - { - if (spell->Id == sSpellMgr->GetSpellIdForDifficulty(SPELL_WIDOWS_EMBRACE, me)) - { - Talk(EMOTE_WIDOWS_EMBRACE); - scheduler.RescheduleGroup(GROUP_FRENZY, 1min); - me->RemoveAurasDueToSpell(SPELL_FRENZY); - instance->SetData(DATA_FRENZY_REMOVED, 0); - if (Is25ManRaid()) - caster->KillSelf(); - } - } - - private: - bool _introDone; - }; -}; - -} - -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.h b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.h deleted file mode 100644 index bb60b98ef..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.h +++ /dev/null @@ -1,381 +0,0 @@ -#ifndef BOSS_FOURHORSEMEN_H_ -#define BOSS_FOURHORSEMEN_H_ - -#include "CreatureScript.h" -#include "Player.h" -#include "ScriptedCreature.h" -#include "SpellAuraEffects.h" -#include "SpellScript.h" -#include "SpellScriptLoader.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace FourHorsemen { - -enum Spells -{ - SPELL_BERSERK = 26662, - // Marks - SPELL_MARK_OF_KORTHAZZ = 28832, - SPELL_MARK_OF_BLAUMEUX = 28833, - SPELL_MARK_OF_RIVENDARE = 28834, - SPELL_MARK_OF_ZELIEK = 28835, - SPELL_MARK_DAMAGE = 28836, - // Korth'azz - SPELL_KORTHAZZ_METEOR = 28884, - // Blaumeux - SPELL_BLAUMEUX_SHADOW_BOLT = 57374, - SPELL_BLAUMEUX_VOID_ZONE = 28863, - SPELL_BLAUMEUX_UNYIELDING_PAIN = 57381, - // Zeliek - SPELL_ZELIEK_HOLY_WRATH = 28883, - SPELL_ZELIEK_HOLY_BOLT = 57376, - SPELL_ZELIEK_CONDEMNATION = 57377, - // Rivendare - SPELL_RIVENDARE_UNHOLY_SHADOW = 28882, -}; - -enum Events -{ - EVENT_MARK_CAST = 1, - EVENT_PRIMARY_SPELL = 2, - EVENT_SECONDARY_SPELL = 3, - EVENT_BERSERK = 4 -}; - -enum Misc -{ - // Movement - MOVE_PHASE_NONE = 0, - MOVE_PHASE_STARTED = 1, - MOVE_PHASE_FINISHED = 2, - // Horseman - HORSEMAN_ZELIEK = 0, - HORSEMAN_BLAUMEUX = 1, - HORSEMAN_RIVENDARE = 2, - HORSEMAN_KORTHAZZ = 3 -}; - -enum FourHorsemen -{ - SAY_AGGRO = 0, - SAY_TAUNT = 1, - SAY_SPECIAL = 2, - SAY_SLAY = 3, - SAY_DEATH = 4, - EMOTE_RAGECAST = 7 -}; - -// MARKS -const uint32 TABLE_SPELL_MARK[4] = {SPELL_MARK_OF_ZELIEK, SPELL_MARK_OF_BLAUMEUX, SPELL_MARK_OF_RIVENDARE, SPELL_MARK_OF_KORTHAZZ}; - -// PRIMARY SPELL -const uint32 TABLE_SPELL_PRIMARY[4] = {SPELL_ZELIEK_HOLY_BOLT, SPELL_BLAUMEUX_SHADOW_BOLT, SPELL_RIVENDARE_UNHOLY_SHADOW, SPELL_KORTHAZZ_METEOR}; - -// PUNISH -const uint32 TABLE_SPELL_PUNISH[4] = {SPELL_ZELIEK_CONDEMNATION, SPELL_BLAUMEUX_UNYIELDING_PAIN, 0, 0}; - -// SECONDARY SPELL -const uint32 TABLE_SPELL_SECONDARY[4] = {SPELL_ZELIEK_HOLY_WRATH, SPELL_BLAUMEUX_VOID_ZONE, 0, 0}; - -const Position WaypointPositions[12] = -{ - // Thane waypoints - {2542.3f, -2984.1f, 241.49f, 5.362f}, - {2547.6f, -2999.4f, 241.34f, 5.049f}, - {2542.9f, -3015.0f, 241.35f, 4.654f}, - // Lady waypoints - {2498.3f, -2961.8f, 241.28f, 3.267f}, - {2487.7f, -2959.2f, 241.28f, 2.890f}, - {2469.4f, -2947.6f, 241.28f, 2.576f}, - // Baron waypoints - {2553.8f, -2968.4f, 241.33f, 5.757f}, - {2564.3f, -2972.5f, 241.33f, 5.890f}, - {2583.9f, -2971.6f, 241.35f, 0.008f}, - // Sir waypoints - {2534.5f, -2921.7f, 241.53f, 1.363f}, - {2523.5f, -2902.8f, 241.28f, 2.095f}, - {2517.8f, -2896.6f, 241.28f, 2.315f} -}; - -class boss_four_horsemen : public CreatureScript -{ -public: - boss_four_horsemen() : CreatureScript("boss_four_horsemen") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_four_horsemenAI : public BossAI - { - explicit boss_four_horsemenAI(Creature* c) : BossAI(c, BOSS_HORSEMAN) - { - switch (me->GetEntry()) - { - case NPC_SIR_ZELIEK: - horsemanId = HORSEMAN_ZELIEK; - break; - case NPC_LADY_BLAUMEUX: - horsemanId = HORSEMAN_BLAUMEUX; - break; - case NPC_BARON_RIVENDARE: - horsemanId = HORSEMAN_RIVENDARE; - break; - case NPC_THANE_KORTHAZZ: - horsemanId = HORSEMAN_KORTHAZZ; - break; - } - } - - EventMap events; - uint8 currentWaypoint{}; - uint8 movementPhase{}; - uint8 horsemanId; - - void MoveToCorner() - { - switch (me->GetEntry()) - { - case NPC_THANE_KORTHAZZ: - currentWaypoint = 0; - break; - case NPC_LADY_BLAUMEUX: - currentWaypoint = 3; - break; - case NPC_BARON_RIVENDARE: - currentWaypoint = 6; - break; - case NPC_SIR_ZELIEK: - currentWaypoint = 9; - break; - } - me->GetMotionMaster()->MovePoint(currentWaypoint, WaypointPositions[currentWaypoint]); - } - - bool IsInRoom() - { - if (me->GetExactDist(2535.1f, -2968.7f, 241.3f) > 100.0f) - { - EnterEvadeMode(); - return false; - } - return true; - } - - void Reset() override - { - BossAI::Reset(); - me->SetPosition(me->GetHomePosition()); - movementPhase = MOVE_PHASE_NONE; - currentWaypoint = 0; - me->SetReactState(REACT_AGGRESSIVE); - events.Reset(); - events.RescheduleEvent(EVENT_MARK_CAST, 24s); - events.RescheduleEvent(EVENT_BERSERK, 10min); - if ((me->GetEntry() != NPC_LADY_BLAUMEUX && me->GetEntry() != NPC_SIR_ZELIEK)) - { - events.RescheduleEvent(EVENT_PRIMARY_SPELL, 10s, 15s); - } - else - { - events.RescheduleEvent(EVENT_SECONDARY_SPELL, 15s); - } - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE) - return; - - // final waypoint - if (id % 3 == 2) - { - movementPhase = MOVE_PHASE_FINISHED; - me->SetReactState(REACT_AGGRESSIVE); - me->SetInCombatWithZone(); - if (!UpdateVictim()) - { - EnterEvadeMode(); - return; - } - if (me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK) - { - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MoveIdle(); - } - return; - } - currentWaypoint = id + 1; - } - - void AttackStart(Unit* who) override - { - if (movementPhase == MOVE_PHASE_FINISHED) - { - if (me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK) - { - me->Attack(who, false); - } - else - { - ScriptedAI::AttackStart(who); - } - } - } - - void KilledUnit(Unit* who) override - { - if (!who->IsPlayer()) - return; - - Talk(SAY_SLAY); - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - Talk(SAY_DEATH); - - if (instance->GetBossState(BOSS_HORSEMAN) == DONE) - if (!me->GetMap()->GetPlayers().IsEmpty()) - if (Player* player = me->GetMap()->GetPlayers().getFirst()->GetSource()) - if (GameObject* chest = player->SummonGameObject(RAID_MODE(GO_HORSEMEN_CHEST_10, GO_HORSEMEN_CHEST_25), 2514.8f, -2944.9f, 245.55f, 5.51f, 0, 0, 0, 0, 0)) - chest->SetLootRecipient(me); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - if (movementPhase == MOVE_PHASE_NONE) - { - Talk(SAY_AGGRO); - me->SetReactState(REACT_PASSIVE); - movementPhase = MOVE_PHASE_STARTED; - me->SetSpeed(MOVE_RUN, me->GetSpeedRate(MOVE_RUN), true); - MoveToCorner(); - } - } - - void UpdateAI(uint32 diff) override - { - if (movementPhase == MOVE_PHASE_STARTED && currentWaypoint) - { - me->GetMotionMaster()->MovePoint(currentWaypoint, WaypointPositions[currentWaypoint]); - currentWaypoint = 0; - } - - if (!IsInRoom()) - return; - - if (movementPhase < MOVE_PHASE_FINISHED || !UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_MARK_CAST: - me->CastSpell(me, TABLE_SPELL_MARK[horsemanId], false); - events.Repeat((me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK) ? 15s : 12s); - return; - case EVENT_BERSERK: - Talk(SAY_SPECIAL); - me->CastSpell(me, SPELL_BERSERK, true); - return; - case EVENT_PRIMARY_SPELL: - Talk(SAY_TAUNT); - me->CastSpell(me->GetVictim(), TABLE_SPELL_PRIMARY[horsemanId], false); - events.Repeat(15s); - return; - case EVENT_SECONDARY_SPELL: - me->CastSpell(me->GetVictim(), TABLE_SPELL_SECONDARY[horsemanId], false); - events.Repeat(15s); - return; - } - - if (me->GetEntry() == NPC_LADY_BLAUMEUX || me->GetEntry() == NPC_SIR_ZELIEK) - { - if (Unit* pTarget = me->SelectNearestPlayer(300.0f)) - { - if (pTarget && me->IsValidAttackTarget(pTarget)) - { - AttackStart(pTarget); - } - } - if (me->IsWithinDistInMap(me->GetVictim(), 45.0f) && me->IsValidAttackTarget(me->GetVictim())) - { - DoCastVictim(TABLE_SPELL_PRIMARY[horsemanId]); - } - else if (!me->IsWithinDistInMap(me->GetVictim(), 45.0f) || !me->IsValidAttackTarget(me->GetVictim())) - { - DoCastAOE(TABLE_SPELL_PUNISH[horsemanId]); - Talk(EMOTE_RAGECAST); - } - } - else - { - DoMeleeAttackIfReady(); - } - } - }; -}; - -class spell_four_horsemen_mark_aura : public AuraScript -{ - PrepareAuraScript(spell_four_horsemen_mark_aura); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_MARK_DAMAGE }); - } - - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - { - int32 damage; - switch (GetStackAmount()) - { - case 1: - damage = 0; - break; - case 2: - damage = 500; - break; - case 3: - damage = 1500; - break; - case 4: - damage = 4000; - break; - case 5: - damage = 12500; - break; - case 6: - damage = 20000; - break; - default: - damage = 20000 + 1000 * (GetStackAmount() - 7); - break; - } - if (damage) - { - caster->CastCustomSpell(SPELL_MARK_DAMAGE, SPELLVALUE_BASE_POINT0, damage, GetTarget()); - } - } - } - - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_four_horsemen_mark_aura::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - } -}; - -} // namespace FourHorsemen - -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp index 8eb03fe59..189b24880 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "boss_gluth.h" #include "CreatureScript.h" #include "Player.h" #include "ScriptedCreature.h" @@ -23,11 +22,248 @@ #include "SpellScriptLoader.h" #include "naxxramas.h" +enum Spells +{ + SPELL_MORTAL_WOUND = 25646, + SPELL_ENRAGE = 28371, + SPELL_DECIMATE = 28374, + SPELL_DECIMATE_DAMAGE = 28375, + SPELL_BERSERK = 26662, + SPELL_INFECTED_WOUND = 29306, + SPELL_CHOW_SEARCHER = 28404 +}; -using namespace Gluth; +enum Events +{ + EVENT_MORTAL_WOUND = 1, + EVENT_ENRAGE = 2, + EVENT_DECIMATE = 3, + EVENT_BERSERK = 4, + EVENT_SUMMON_ZOMBIE = 5, + EVENT_CAN_EAT_ZOMBIE = 6 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Misc +{ + NPC_ZOMBIE_CHOW = 16360 +}; + +enum Emotes +{ + EMOTE_SPOTS_ONE = 0, + EMOTE_DECIMATE = 1, + EMOTE_ENRAGE = 2, + EMOTE_DEVOURS_ALL = 3, + EMOTE_BERSERK = 4 +}; + +const Position zombiePos[3] = +{ + {3267.9f, -3172.1f, 297.42f, 0.94f}, + {3253.2f, -3132.3f, 297.42f, 0}, + {3308.3f, -3185.8f, 297.42f, 1.58f} +}; + +class boss_gluth : public CreatureScript +{ +public: + boss_gluth() : CreatureScript("boss_gluth") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_gluthAI : public BossAI + { + explicit boss_gluthAI(Creature* c) : BossAI(c, BOSS_GLUTH), summons(me) + {} + + EventMap events; + SummonList summons; + + void Reset() override + { + BossAI::Reset(); + me->ApplySpellImmune(SPELL_INFECTED_WOUND, IMMUNITY_ID, SPELL_INFECTED_WOUND, true); + events.Reset(); + summons.DespawnAll(); + me->SetReactState(REACT_AGGRESSIVE); + } + + void MoveInLineOfSight(Unit* who) override + { + if (!me->GetVictim() || me->GetVictim()->GetEntry() != NPC_ZOMBIE_CHOW) + { + if (who->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinDistInMap(who, 6.5f)) + { + SetGazeOn(who); + Talk(EMOTE_SPOTS_ONE); + } + else + { + ScriptedAI::MoveInLineOfSight(who); + } + } + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + me->SetInCombatWithZone(); + events.ScheduleEvent(EVENT_MORTAL_WOUND, 10s); + events.ScheduleEvent(EVENT_ENRAGE, 22s); + events.ScheduleEvent(EVENT_DECIMATE, RAID_MODE(110s, 90s)); + events.ScheduleEvent(EVENT_BERSERK, 6min); + events.ScheduleEvent(EVENT_SUMMON_ZOMBIE, 10s); + events.ScheduleEvent(EVENT_CAN_EAT_ZOMBIE, 1s); + } + + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_ZOMBIE_CHOW) + { + summon->AI()->AttackStart(me); + } + summons.Summon(summon); + } + + void SummonedCreatureDies(Creature* cr, Unit*) override { summons.Despawn(cr); } + + void KilledUnit(Unit* who) override + { + if (me->IsAlive() && who->GetEntry() == NPC_ZOMBIE_CHOW) + me->ModifyHealth(int32(me->GetMaxHealth() * 0.05f)); + + if (who->IsPlayer()) + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + summons.DespawnAll(); + } + + bool SelectPlayerInRoom() + { + if (me->IsInCombat()) + return false; + + Map::PlayerList const& pList = me->GetMap()->GetPlayers(); + for (auto const& itr : pList) + { + Player* player = itr.GetSource(); + if (!player || !player->IsAlive()) + continue; + + if (player->GetPositionZ() > 300.0f || me->GetExactDist(player) > 50.0f) + continue; + + AttackStart(player); + return true; + } + return false; + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictimWithGaze() && !SelectPlayerInRoom()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_BERSERK: + me->CastSpell(me, SPELL_BERSERK, true); + break; + case EVENT_ENRAGE: + Talk(EMOTE_ENRAGE); + me->CastSpell(me, SPELL_ENRAGE, true); + events.Repeat(22s); + break; + case EVENT_MORTAL_WOUND: + me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false); + events.Repeat(10s); + break; + case EVENT_DECIMATE: + Talk(EMOTE_DECIMATE); + me->CastSpell(me, SPELL_DECIMATE, false); + events.Repeat(RAID_MODE(110s, 90s)); + break; + case EVENT_SUMMON_ZOMBIE: + { + uint8 rand = urand(0, 2); + for (int32 i = 0; i < RAID_MODE(1, 2); ++i) + { + // In 10 man raid, normal mode - should spawn only from mid gate + // \1 |0 /2 pos + // In 25 man raid - should spawn from all 3 gates + if (me->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) + { + me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[0]); + } + else + { + me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[urand(0, 2)]); + } + (rand == 2 ? rand = 0 : rand++); + } + events.Repeat(10s); + break; + } + case EVENT_CAN_EAT_ZOMBIE: + events.Repeat(1s); + if (me->GetVictim()->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinMeleeRange(me->GetVictim())) + { + me->CastCustomSpell(SPELL_CHOW_SEARCHER, SPELLVALUE_RADIUS_MOD, 20000, me, true); + Talk(EMOTE_DEVOURS_ALL); + return; // leave it to skip DoMeleeAttackIfReady + } + break; + } + DoMeleeAttackIfReady(); + } + }; +}; + +class spell_gluth_decimate : public SpellScript +{ + PrepareSpellScript(spell_gluth_decimate); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DECIMATE_DAMAGE }); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* unitTarget = GetHitUnit()) + { + int32 damage = int32(unitTarget->GetHealth()) - int32(unitTarget->CountPctFromMaxHealth(5)); + if (damage <= 0) + return; + + if (Creature* cTarget = unitTarget->ToCreature()) + { + cTarget->SetWalk(true); + cTarget->GetMotionMaster()->MoveFollow(GetCaster(), 0.0f, 0.0f, MOTION_SLOT_CONTROLLED); + cTarget->SetReactState(REACT_PASSIVE); + Unit::DealDamage(GetCaster(), cTarget, damage); + return; + } + GetCaster()->CastCustomSpell(SPELL_DECIMATE_DAMAGE, SPELLVALUE_BASE_POINT0, damage, unitTarget); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gluth_decimate::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; void AddSC_boss_gluth() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.h b/src/server/scripts/Northrend/Naxxramas/boss_gluth.h deleted file mode 100644 index da733719e..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.h +++ /dev/null @@ -1,258 +0,0 @@ -#ifndef BOSSGLUTH_H_ -#define BOSSGLUTH_H_ - -#include "Player.h" -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellScript.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Gluth { - -enum Spells -{ - SPELL_MORTAL_WOUND = 25646, - SPELL_ENRAGE = 28371, - SPELL_DECIMATE = 28374, - SPELL_DECIMATE_DAMAGE = 28375, - SPELL_BERSERK = 26662, - SPELL_INFECTED_WOUND = 29306, - SPELL_CHOW_SEARCHER = 28404 -}; - -enum Events -{ - EVENT_MORTAL_WOUND = 1, - EVENT_ENRAGE = 2, - EVENT_DECIMATE = 3, - EVENT_BERSERK = 4, - EVENT_SUMMON_ZOMBIE = 5, - EVENT_CAN_EAT_ZOMBIE = 6 -}; - -enum Misc -{ - NPC_ZOMBIE_CHOW = 16360 -}; - -enum Emotes -{ - EMOTE_SPOTS_ONE = 0, - EMOTE_DECIMATE = 1, - EMOTE_ENRAGE = 2, - EMOTE_DEVOURS_ALL = 3, - EMOTE_BERSERK = 4 -}; - -const Position zombiePos[3] = -{ - {3267.9f, -3172.1f, 297.42f, 0.94f}, - {3253.2f, -3132.3f, 297.42f, 0}, - {3308.3f, -3185.8f, 297.42f, 1.58f} -}; - -class boss_gluth : public CreatureScript -{ -public: - boss_gluth() : CreatureScript("boss_gluth") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_gluthAI : public BossAI - { - explicit boss_gluthAI(Creature* c) : BossAI(c, BOSS_GLUTH), summons(me) - {} - - EventMap events; - SummonList summons; - - void Reset() override - { - BossAI::Reset(); - me->ApplySpellImmune(SPELL_INFECTED_WOUND, IMMUNITY_ID, SPELL_INFECTED_WOUND, true); - events.Reset(); - summons.DespawnAll(); - me->SetReactState(REACT_AGGRESSIVE); - } - - void MoveInLineOfSight(Unit* who) override - { - if (!me->GetVictim() || me->GetVictim()->GetEntry() != NPC_ZOMBIE_CHOW) - { - if (who->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinDistInMap(who, 6.5f)) - { - SetGazeOn(who); - Talk(EMOTE_SPOTS_ONE); - } - else - { - ScriptedAI::MoveInLineOfSight(who); - } - } - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - me->SetInCombatWithZone(); - events.ScheduleEvent(EVENT_MORTAL_WOUND, 10s); - events.ScheduleEvent(EVENT_ENRAGE, 22s); - events.ScheduleEvent(EVENT_DECIMATE, RAID_MODE(110s, 90s)); - events.ScheduleEvent(EVENT_BERSERK, 6min); - events.ScheduleEvent(EVENT_SUMMON_ZOMBIE, 10s); - events.ScheduleEvent(EVENT_CAN_EAT_ZOMBIE, 1s); - } - - void JustSummoned(Creature* summon) override - { - if (summon->GetEntry() == NPC_ZOMBIE_CHOW) - { - summon->AI()->AttackStart(me); - } - summons.Summon(summon); - } - - void SummonedCreatureDies(Creature* cr, Unit*) override { summons.Despawn(cr); } - - void KilledUnit(Unit* who) override - { - if (me->IsAlive() && who->GetEntry() == NPC_ZOMBIE_CHOW) - me->ModifyHealth(int32(me->GetMaxHealth() * 0.05f)); - - if (who->IsPlayer()) - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - summons.DespawnAll(); - } - - bool SelectPlayerInRoom() - { - if (me->IsInCombat()) - return false; - - Map::PlayerList const& pList = me->GetMap()->GetPlayers(); - for (auto const& itr : pList) - { - Player* player = itr.GetSource(); - if (!player || !player->IsAlive()) - continue; - - if (player->GetPositionZ() > 300.0f || me->GetExactDist(player) > 50.0f) - continue; - - AttackStart(player); - return true; - } - return false; - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictimWithGaze() && !SelectPlayerInRoom()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_BERSERK: - me->CastSpell(me, SPELL_BERSERK, true); - break; - case EVENT_ENRAGE: - Talk(EMOTE_ENRAGE); - me->CastSpell(me, SPELL_ENRAGE, true); - events.Repeat(22s); - break; - case EVENT_MORTAL_WOUND: - me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false); - events.Repeat(10s); - break; - case EVENT_DECIMATE: - Talk(EMOTE_DECIMATE); - me->CastSpell(me, SPELL_DECIMATE, false); - events.Repeat(RAID_MODE(110s, 90s)); - break; - case EVENT_SUMMON_ZOMBIE: - { - uint8 rand = urand(0, 2); - for (int32 i = 0; i < RAID_MODE(1, 2); ++i) - { - // In 10 man raid, normal mode - should spawn only from mid gate - // \1 |0 /2 pos - // In 25 man raid - should spawn from all 3 gates - if (me->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) - { - me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[0]); - } - else - { - me->SummonCreature(NPC_ZOMBIE_CHOW, zombiePos[urand(0, 2)]); - } - (rand == 2 ? rand = 0 : rand++); - } - events.Repeat(10s); - break; - } - case EVENT_CAN_EAT_ZOMBIE: - events.Repeat(1s); - if (me->GetVictim()->GetEntry() == NPC_ZOMBIE_CHOW && me->IsWithinMeleeRange(me->GetVictim())) - { - me->CastCustomSpell(SPELL_CHOW_SEARCHER, SPELLVALUE_RADIUS_MOD, 20000, me, true); - Talk(EMOTE_DEVOURS_ALL); - return; // leave it to skip DoMeleeAttackIfReady - } - break; - } - DoMeleeAttackIfReady(); - } - }; -}; - -class spell_gluth_decimate : public SpellScript -{ - PrepareSpellScript(spell_gluth_decimate); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DECIMATE_DAMAGE }); - } - - void HandleScriptEffect(SpellEffIndex /*effIndex*/) - { - if (Unit* unitTarget = GetHitUnit()) - { - int32 damage = int32(unitTarget->GetHealth()) - int32(unitTarget->CountPctFromMaxHealth(5)); - if (damage <= 0) - return; - - if (Creature* cTarget = unitTarget->ToCreature()) - { - cTarget->SetWalk(true); - cTarget->GetMotionMaster()->MoveFollow(GetCaster(), 0.0f, 0.0f, MOTION_SLOT_CONTROLLED); - cTarget->SetReactState(REACT_PASSIVE); - Unit::DealDamage(GetCaster(), cTarget, damage); - return; - } - GetCaster()->CastCustomSpell(SPELL_DECIMATE_DAMAGE, SPELLVALUE_BASE_POINT0, damage, unitTarget); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gluth_decimate::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } -}; - -} - -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index 46f97a5e4..23eb083d2 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "boss_gothik.h" #include "CombatAI.h" #include "CreatureScript.h" #include "GridNotifiers.h" @@ -24,11 +23,730 @@ #include "SpellScriptLoader.h" #include "naxxramas.h" +enum Yells +{ + SAY_INTRO_1 = 0, + SAY_INTRO_2 = 1, + SAY_INTRO_3 = 2, + SAY_INTRO_4 = 3, + SAY_PHASE_TWO = 4, + SAY_DEATH = 5, + SAY_KILL = 6, -using namespace Gothik; + EMOTE_PHASE_TWO = 7, + EMOTE_GATE_OPENED = 8 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Spells +{ + // Gothik + SPELL_HARVEST_SOUL = 28679, + SPELL_SHADOW_BOLT = 29317, + // Teleport spells + SPELL_TELEPORT_DEAD = 28025, + SPELL_TELEPORT_LIVE = 28026, + // Visual spells + SPELL_ANCHOR_1_TRAINEE = 27892, + SPELL_ANCHOR_1_DK = 27928, + SPELL_ANCHOR_1_RIDER = 27935, + SPELL_ANCHOR_2_TRAINEE = 27893, + SPELL_ANCHOR_2_DK = 27929, + SPELL_ANCHOR_2_RIDER = 27936, + SPELL_SKULLS_TRAINEE = 27915, + SPELL_SKULLS_DK = 27931, + SPELL_SKULLS_RIDER = 27937, + // Living trainee + SPELL_DEATH_PLAGUE = 55604, + // Dead trainee + SPELL_ARCANE_EXPLOSION = 27989, + // Living knight + SPELL_SHADOW_MARK = 27825, + // Dead knight + SPELL_WHIRLWIND = 56408, + // Living rider + SPELL_SHADOW_BOLT_VOLLEY = 27831, + // Dead rider + SPELL_DRAIN_LIFE = 27994, + SPELL_UNHOLY_FRENZY = 55648, + // Horse + SPELL_STOMP = 27993 +}; + +enum Misc +{ + NPC_LIVING_TRAINEE = 16124, + NPC_LIVING_KNIGHT = 16125, + NPC_LIVING_RIDER = 16126, + NPC_DEAD_TRAINEE = 16127, + NPC_DEAD_KNIGHT = 16148, + NPC_DEAD_HORSE = 16149, + NPC_DEAD_RIDER = 16150, + NPC_TRIGGER = 16137 +}; + +enum Events +{ + // Gothik + EVENT_SUMMON_ADDS = 1, + EVENT_HARVEST_SOUL = 2, + EVENT_SHADOW_BOLT = 3, + EVENT_TELEPORT = 4, + EVENT_CHECK_HEALTH = 5, + EVENT_CHECK_PLAYERS = 6, + // Living trainee + EVENT_DEATH_PLAGUE = 7, + // Dead trainee + EVENT_ARCANE_EXPLOSION = 8, + // Living knight + EVENT_SHADOW_MARK = 9, + // Dead knight + EVENT_WHIRLWIND = 10, + // Living rider + EVENT_SHADOW_BOLT_VOLLEY = 11, + // Dead rider + EVENT_DRAIN_LIFE = 12, + EVENT_UNHOLY_FRENZY = 13, + // HORSE + EVENT_STOMP = 14, + // Intro + EVENT_INTRO_2 = 15, + EVENT_INTRO_3 = 16, + EVENT_INTRO_4 = 17 +}; + +const uint32 gothikWaves[24][2] = +{ + {NPC_LIVING_TRAINEE, 20000}, + {NPC_LIVING_TRAINEE, 20000}, + {NPC_LIVING_TRAINEE, 10000}, + {NPC_LIVING_KNIGHT, 10000}, + {NPC_LIVING_TRAINEE, 15000}, + {NPC_LIVING_KNIGHT, 10000}, + {NPC_LIVING_TRAINEE, 15000}, + {NPC_LIVING_TRAINEE, 0}, + {NPC_LIVING_KNIGHT, 10000}, + {NPC_LIVING_RIDER, 10000}, + {NPC_LIVING_TRAINEE, 5000}, + {NPC_LIVING_KNIGHT, 15000}, + {NPC_LIVING_RIDER, 0}, + {NPC_LIVING_TRAINEE, 10000}, + {NPC_LIVING_KNIGHT, 10000}, + {NPC_LIVING_TRAINEE, 10000}, + {NPC_LIVING_RIDER, 5000}, + {NPC_LIVING_KNIGHT, 5000}, + {NPC_LIVING_TRAINEE, 20000}, + {NPC_LIVING_RIDER, 0}, + {NPC_LIVING_KNIGHT, 0}, + {NPC_LIVING_TRAINEE, 15000}, + {NPC_LIVING_TRAINEE, 29000}, + {0, 0} +}; + +const Position PosSummonLiving[6] = +{ + {2669.7f, -3428.76f, 268.56f, 1.6f}, + {2692.1f, -3428.76f, 268.56f, 1.6f}, + {2714.4f, -3428.76f, 268.56f, 1.6f}, + {2669.7f, -3431.67f, 268.56f, 1.6f}, + {2692.1f, -3431.67f, 268.56f, 1.6f}, + {2714.4f, -3431.67f, 268.56f, 1.6f} +}; + +const Position PosSummonDead[5] = +{ + {2725.1f, -3310.0f, 268.85f, 3.4f}, + {2699.3f, -3322.8f, 268.60f, 3.3f}, + {2733.1f, -3348.5f, 268.84f, 3.1f}, + {2682.8f, -3304.2f, 268.85f, 3.9f}, + {2664.8f, -3340.7f, 268.23f, 3.7f} +}; + +//const Position PosGroundLivingSide = {2691.2f, -3387.0f, 267.68f, 1.52f}; +//const Position PosGroundDeadSide = {2693.5f, -3334.6f, 267.68f, 4.67f}; +//const Position PosPlatform = {2640.5f, -3360.6f, 285.26f, 0.0f}; + +#define POS_Y_GATE -3360.78f +#define POS_Y_WEST -3285.0f +#define POS_Y_EAST -3434.0f +#define POS_X_NORTH 2750.49f +#define POS_X_SOUTH 2633.84f +#define IN_LIVE_SIDE(who) (who->GetPositionY() < POS_Y_GATE) + +// Predicate function to check that the r efzr unit is NOT on the same side as the source. +struct NotOnSameSide +{ +public: + explicit NotOnSameSide(Unit* pSource) : m_inLiveSide(IN_LIVE_SIDE(pSource)) { } + + bool operator() (Unit const* pTarget) + { + return (m_inLiveSide != IN_LIVE_SIDE(pTarget)); + } + +private: + bool m_inLiveSide; +}; + +class boss_gothik : public CreatureScript +{ +public: + boss_gothik() : CreatureScript("boss_gothik") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_gothikAI : public BossAI + { + explicit boss_gothikAI(Creature* c) : BossAI(c, BOSS_GOTHIK), summons(me) + {} + + EventMap events; + SummonList summons; + bool secondPhase{}; + bool gateOpened{}; + uint8 waveCount{}; + + bool IsInRoom() + { + if (me->GetPositionX() > 2767 || me->GetPositionX() < 2618 || me->GetPositionY() > -3285 || me->GetPositionY() < -3435) + { + EnterEvadeMode(); + return false; + } + return true; + } + + void Reset() override + { + BossAI::Reset(); + events.Reset(); + summons.DespawnAll(); + me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); + me->SetImmuneToPC(false); + me->SetReactState(REACT_PASSIVE); + secondPhase = false; + gateOpened = false; + waveCount = 0; + me->NearTeleportTo(2642.139f, -3386.959f, 285.492f, 6.265f); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + me->SetInCombatWithZone(); + Talk(SAY_INTRO_1); + events.ScheduleEvent(EVENT_INTRO_2, 4s); + events.ScheduleEvent(EVENT_INTRO_3, 9s); + events.ScheduleEvent(EVENT_INTRO_4, 14s); + me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE); + events.ScheduleEvent(EVENT_SUMMON_ADDS, 30s); + events.ScheduleEvent(EVENT_CHECK_PLAYERS, 2min); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + // If central gate is open, attack any one + if (gateOpened) + { + if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 200.0f)) + { + summon->AI()->AttackStart(target); + summon->SetInCombatWithZone(); + summon->SetReactState(REACT_AGGRESSIVE); + summon->CallForHelp(150.0f); + } + } + // Else look for a random target on the side the summoned NPC is + else + { + Map::PlayerList const& pList = me->GetMap()->GetPlayers(); + std::vector tList; + for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr) + { + if (!me->IsWithinDistInMap(itr->GetSource(), 200.0f, true, false, false) || !itr->GetSource()->IsAlive() || itr->GetSource()->IsGameMaster()) + { + continue; + } + if (IN_LIVE_SIDE(itr->GetSource()) != IN_LIVE_SIDE(summon)) + { + continue; + } + tList.push_back(itr->GetSource()); + } + if (!tList.empty()) + { + Player* target = tList[urand(0, tList.size() - 1)]; + summon->AI()->AttackStart(target); + summon->SetInCombatWithZone(); + summon->SetReactState(REACT_AGGRESSIVE); + summon->CallForHelp(150.0f); + } + } + } + + void SummonedCreatureDespawn(Creature* cr) override + { + summons.Despawn(cr); + } + + void KilledUnit(Unit* who) override + { + if (!who->IsPlayer()) + return; + + Talk(SAY_KILL); + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + summons.DespawnAll(); + } + + void SummonHelpers(uint32 entry) + { + switch (entry) + { + case NPC_LIVING_TRAINEE: + me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[0].GetPositionX(), PosSummonLiving[0].GetPositionY(), PosSummonLiving[0].GetPositionZ(), PosSummonLiving[0].GetOrientation()); + me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[1].GetPositionX(), PosSummonLiving[1].GetPositionY(), PosSummonLiving[1].GetPositionZ(), PosSummonLiving[1].GetOrientation()); + if (Is25ManRaid()) + { + me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[2].GetPositionX(), PosSummonLiving[2].GetPositionY(), PosSummonLiving[2].GetPositionZ(), PosSummonLiving[2].GetOrientation()); + } + break; + case NPC_LIVING_KNIGHT: + me->SummonCreature(NPC_LIVING_KNIGHT, PosSummonLiving[3].GetPositionX(), PosSummonLiving[3].GetPositionY(), PosSummonLiving[3].GetPositionZ(), PosSummonLiving[3].GetOrientation()); + if (Is25ManRaid()) + { + me->SummonCreature(NPC_LIVING_KNIGHT, PosSummonLiving[5].GetPositionX(), PosSummonLiving[5].GetPositionY(), PosSummonLiving[5].GetPositionZ(), PosSummonLiving[5].GetOrientation()); + } + break; + case NPC_LIVING_RIDER: + me->SummonCreature(NPC_LIVING_RIDER, PosSummonLiving[4].GetPositionX(), PosSummonLiving[4].GetPositionY(), PosSummonLiving[4].GetPositionZ(), PosSummonLiving[4].GetOrientation()); + break; + } + } + + bool CheckGroupSplitted() + { + Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); + if (!PlayerList.IsEmpty()) + { + bool checklife = false; + bool checkdead = false; + for (auto const& i : PlayerList) + { + Player* player = i.GetSource(); + if (player->IsAlive() && + player->GetPositionX() <= POS_X_NORTH && + player->GetPositionX() >= POS_X_SOUTH && + player->GetPositionY() <= POS_Y_GATE && + player->GetPositionY() >= POS_Y_EAST) + { + checklife = true; + } + else if (player->IsAlive() && + player->GetPositionX() <= POS_X_NORTH && + player->GetPositionX() >= POS_X_SOUTH && + player->GetPositionY() >= POS_Y_GATE && + player->GetPositionY() <= POS_Y_WEST) + { + checkdead = true; + } + + if (checklife && checkdead) + return true; + } + } + return false; + } + + void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + if (!secondPhase) + { + damage = 0; + } + } + + void UpdateAI(uint32 diff) override + { + if (!IsInRoom()) + return; + + if (!UpdateVictim()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + 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; + case EVENT_SHADOW_BOLT: + me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT, false); + events.Repeat(1s); + break; + case EVENT_HARVEST_SOUL: + me->CastSpell(me, SPELL_HARVEST_SOUL, false); + events.Repeat(15s); + break; + case EVENT_TELEPORT: + me->AttackStop(); + if (IN_LIVE_SIDE(me)) + { + me->CastSpell(me, SPELL_TELEPORT_DEAD, false); + } + else + { + me->CastSpell(me, SPELL_TELEPORT_LIVE, false); + } + me->GetThreatMgr().resetAggro(NotOnSameSide(me)); + if (Unit* pTarget = SelectTarget(SelectTargetMethod::MaxDistance, 0)) + { + me->GetThreatMgr().AddThreat(pTarget, 100.0f); + AttackStart(pTarget); + } + events.Repeat(20s); + break; + case EVENT_CHECK_HEALTH: + if (me->HealthBelowPct(30)) + { + if (GameObject* go = instance->GetGameObject(DATA_GOTHIK_INNER_GATE)) + go->SetGoState(GO_STATE_ACTIVE); + + events.CancelEvent(EVENT_TELEPORT); + break; + } + events.Repeat(1s); + break; + case EVENT_SUMMON_ADDS: + if (gothikWaves[waveCount][0]) + { + SummonHelpers(gothikWaves[waveCount][0]); + events.Repeat(Milliseconds(gothikWaves[waveCount][1])); + } + else + { + secondPhase = true; + Talk(SAY_PHASE_TWO); + Talk(EMOTE_PHASE_TWO); + me->CastSpell(me, SPELL_TELEPORT_LIVE, false); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); + me->SetImmuneToPC(false); + me->RemoveAllAuras(); + events.ScheduleEvent(EVENT_SHADOW_BOLT, 1s); + events.ScheduleEvent(EVENT_HARVEST_SOUL, 5s, 15s); + events.ScheduleEvent(EVENT_TELEPORT, 20s); + events.ScheduleEvent(EVENT_CHECK_HEALTH, 1s); + } + waveCount++; + break; + case EVENT_CHECK_PLAYERS: + if (!CheckGroupSplitted()) + { + if (GameObject* go = instance->GetGameObject(DATA_GOTHIK_INNER_GATE)) + go->SetGoState(GO_STATE_ACTIVE); + + gateOpened = true; + Talk(EMOTE_GATE_OPENED); + } + break; + } + DoMeleeAttackIfReady(); + } + }; +}; + +class npc_boss_gothik_minion : public CreatureScript +{ +public: + npc_boss_gothik_minion() : CreatureScript("npc_boss_gothik_minion") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct npc_boss_gothik_minionAI : public CombatAI + { + explicit npc_boss_gothik_minionAI(Creature* c) : CombatAI(c) + { + livingSide = IN_LIVE_SIDE(me); + } + EventMap events; + bool livingSide; + bool IsOnSameSide(Unit const* who) const { return livingSide == IN_LIVE_SIDE(who); } + + void Reset() override + { + me->SetReactState(REACT_AGGRESSIVE); + me->SetNoCallAssistance(false); + events.Reset(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + switch (me->GetEntry()) + { + case NPC_LIVING_TRAINEE: + events.ScheduleEvent(EVENT_DEATH_PLAGUE, 3s); + break; + case NPC_DEAD_TRAINEE: + events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, 2500ms); + break; + case NPC_LIVING_KNIGHT: + events.ScheduleEvent(EVENT_SHADOW_MARK, 3s); + break; + case NPC_DEAD_KNIGHT: + events.ScheduleEvent(EVENT_WHIRLWIND, 2s); + break; + case NPC_LIVING_RIDER: + events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 3s); + break; + case NPC_DEAD_RIDER: + events.ScheduleEvent(EVENT_DRAIN_LIFE, 2s, 3500ms); + events.ScheduleEvent(EVENT_UNHOLY_FRENZY, 5s, 9s); + break; + case NPC_DEAD_HORSE: + events.ScheduleEvent(EVENT_STOMP, 2s, 5s); + break; + } + } + + void JustDied(Unit*) override + { + switch (me->GetEntry()) + { + case NPC_LIVING_TRAINEE: + DoCastAOE(SPELL_ANCHOR_1_TRAINEE, true); + break; + case NPC_LIVING_KNIGHT: + DoCastAOE(SPELL_ANCHOR_1_DK, true); + break; + case NPC_LIVING_RIDER: + DoCastAOE(SPELL_ANCHOR_1_RIDER, true); + break; + } + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + if (me->GetUnitState() == UNIT_STATE_CASTING) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_DEATH_PLAGUE: + me->CastSpell(me->GetVictim(), SPELL_DEATH_PLAGUE, false); + events.Repeat(4s, 7s); + break; + case EVENT_ARCANE_EXPLOSION: + if (Unit* victim = me->GetVictim()) + { + if (victim->IsWithinDist(me, 20)) + { + me->CastSpell(victim, SPELL_ARCANE_EXPLOSION, false); + } + } + events.Repeat(2500ms); + break; + case EVENT_SHADOW_MARK: + if (Unit* victim = me->GetVictim()) + { + if (victim->IsWithinDist(me, 10)) + { + me->CastSpell(victim, SPELL_SHADOW_MARK, false); + } + } + events.Repeat(5s, 7s); + break; + case EVENT_WHIRLWIND: + if (Unit* victim = me->GetVictim()) + { + if (victim->IsWithinDist(me, 10)) + { + me->CastSpell(victim, SPELL_WHIRLWIND, false); + } + } + events.Repeat(4s, 6s); + break; + case EVENT_SHADOW_BOLT_VOLLEY: + me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT_VOLLEY, false); + events.Repeat(5s); + break; + case EVENT_DRAIN_LIFE: + if (Unit* victim = me->GetVictim()) + { + if (victim->IsWithinDist(me, 20)) + { + me->CastSpell(victim, SPELL_DRAIN_LIFE, false); + } + } + events.Repeat(8s, 12s); + break; + case EVENT_UNHOLY_FRENZY: + me->AddAura(SPELL_UNHOLY_FRENZY, me); + events.Repeat(15s, 17s); + break; + case EVENT_STOMP: + if (Unit* victim = me->GetVictim()) + { + if (victim->IsWithinDist(me, 10)) + { + me->CastSpell(victim, SPELL_STOMP, false); + } + } + events.Repeat(4s, 9s); + break; + } + DoMeleeAttackIfReady(); + } + }; +}; + +class npc_gothik_trigger : public CreatureScript +{ +public: + npc_gothik_trigger() : CreatureScript("npc_gothik_trigger") { } + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_gothik_triggerAI(creature); + } + + struct npc_gothik_triggerAI : public ScriptedAI + { + 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, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override { damage = 0; } + + Creature* SelectRandomSkullPile() + { + std::list triggers; + me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f); + // Remove triggers that are on live side or soul triggers on the platform + triggers.remove_if([](Creature* trigger){ + return ((trigger->GetPositionY() < POS_Y_GATE) || (trigger->GetPositionZ() > 280.0f)); + }); + if (!triggers.empty()) + { + std::list::iterator itr = triggers.begin(); + std::advance(itr, urand(0, triggers.size() - 1)); + return *itr; + } + return nullptr; + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (!spell) + { + return; + } + + switch (spell->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, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); + break; + case SPELL_SKULLS_DK: + DoSummon(NPC_DEAD_KNIGHT, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); + break; + case SPELL_SKULLS_RIDER: + DoSummon(NPC_DEAD_RIDER, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); + DoSummon(NPC_DEAD_HORSE, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); + break; + } + } + + // dead side summons are "owned" by gothik + void JustSummoned(Creature* summon) override + { + if (Creature* gothik = me->GetInstanceScript()->GetCreature(DATA_GOTHIK_BOSS)) + gothik->AI()->JustSummoned(summon); + } + + void SummonedCreatureDespawn(Creature* summon) override + { + if (Creature* gothik = me->GetInstanceScript()->GetCreature(DATA_GOTHIK_BOSS)) + gothik->AI()->SummonedCreatureDespawn(summon); + } + }; +}; + +class spell_gothik_shadow_bolt_volley : public SpellScript +{ + PrepareSpellScript(spell_gothik_shadow_bolt_volley); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SHADOW_MARK }); + } + + void FilterTargets(std::list& targets) + { + targets.remove_if(Acore::UnitAuraCheck(false, SPELL_SHADOW_MARK)); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gothik_shadow_bolt_volley::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } +}; void AddSC_boss_gothik() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.h b/src/server/scripts/Northrend/Naxxramas/boss_gothik.h deleted file mode 100644 index 9edfd0d7b..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.h +++ /dev/null @@ -1,741 +0,0 @@ -#ifndef BOSSGOTHIK_H_ -#define BOSSGOTHIK_H_ - -#include "CombatAI.h" -#include "GridNotifiers.h" -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellScript.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Gothik { - -enum Yells -{ - SAY_INTRO_1 = 0, - SAY_INTRO_2 = 1, - SAY_INTRO_3 = 2, - SAY_INTRO_4 = 3, - SAY_PHASE_TWO = 4, - SAY_DEATH = 5, - SAY_KILL = 6, - - EMOTE_PHASE_TWO = 7, - EMOTE_GATE_OPENED = 8 -}; - -enum Spells -{ - // Gothik - SPELL_HARVEST_SOUL = 28679, - SPELL_SHADOW_BOLT = 29317, - // Teleport spells - SPELL_TELEPORT_DEAD = 28025, - SPELL_TELEPORT_LIVE = 28026, - // Visual spells - SPELL_ANCHOR_1_TRAINEE = 27892, - SPELL_ANCHOR_1_DK = 27928, - SPELL_ANCHOR_1_RIDER = 27935, - SPELL_ANCHOR_2_TRAINEE = 27893, - SPELL_ANCHOR_2_DK = 27929, - SPELL_ANCHOR_2_RIDER = 27936, - SPELL_SKULLS_TRAINEE = 27915, - SPELL_SKULLS_DK = 27931, - SPELL_SKULLS_RIDER = 27937, - // Living trainee - SPELL_DEATH_PLAGUE = 55604, - // Dead trainee - SPELL_ARCANE_EXPLOSION = 27989, - // Living knight - SPELL_SHADOW_MARK = 27825, - // Dead knight - SPELL_WHIRLWIND = 56408, - // Living rider - SPELL_SHADOW_BOLT_VOLLEY = 27831, - // Dead rider - SPELL_DRAIN_LIFE = 27994, - SPELL_UNHOLY_FRENZY = 55648, - // Horse - SPELL_STOMP = 27993 -}; - -enum Misc -{ - NPC_LIVING_TRAINEE = 16124, - NPC_LIVING_KNIGHT = 16125, - NPC_LIVING_RIDER = 16126, - NPC_DEAD_TRAINEE = 16127, - NPC_DEAD_KNIGHT = 16148, - NPC_DEAD_HORSE = 16149, - NPC_DEAD_RIDER = 16150, - NPC_TRIGGER = 16137 -}; - -enum Events -{ - // Gothik - EVENT_SUMMON_ADDS = 1, - EVENT_HARVEST_SOUL = 2, - EVENT_SHADOW_BOLT = 3, - EVENT_TELEPORT = 4, - EVENT_CHECK_HEALTH = 5, - EVENT_CHECK_PLAYERS = 6, - // Living trainee - EVENT_DEATH_PLAGUE = 7, - // Dead trainee - EVENT_ARCANE_EXPLOSION = 8, - // Living knight - EVENT_SHADOW_MARK = 9, - // Dead knight - EVENT_WHIRLWIND = 10, - // Living rider - EVENT_SHADOW_BOLT_VOLLEY = 11, - // Dead rider - EVENT_DRAIN_LIFE = 12, - EVENT_UNHOLY_FRENZY = 13, - // HORSE - EVENT_STOMP = 14, - // Intro - EVENT_INTRO_2 = 15, - EVENT_INTRO_3 = 16, - EVENT_INTRO_4 = 17 -}; - -const uint32 gothikWaves[24][2] = -{ - {NPC_LIVING_TRAINEE, 20000}, - {NPC_LIVING_TRAINEE, 20000}, - {NPC_LIVING_TRAINEE, 10000}, - {NPC_LIVING_KNIGHT, 10000}, - {NPC_LIVING_TRAINEE, 15000}, - {NPC_LIVING_KNIGHT, 10000}, - {NPC_LIVING_TRAINEE, 15000}, - {NPC_LIVING_TRAINEE, 0}, - {NPC_LIVING_KNIGHT, 10000}, - {NPC_LIVING_RIDER, 10000}, - {NPC_LIVING_TRAINEE, 5000}, - {NPC_LIVING_KNIGHT, 15000}, - {NPC_LIVING_RIDER, 0}, - {NPC_LIVING_TRAINEE, 10000}, - {NPC_LIVING_KNIGHT, 10000}, - {NPC_LIVING_TRAINEE, 10000}, - {NPC_LIVING_RIDER, 5000}, - {NPC_LIVING_KNIGHT, 5000}, - {NPC_LIVING_TRAINEE, 20000}, - {NPC_LIVING_RIDER, 0}, - {NPC_LIVING_KNIGHT, 0}, - {NPC_LIVING_TRAINEE, 15000}, - {NPC_LIVING_TRAINEE, 29000}, - {0, 0} -}; - -const Position PosSummonLiving[6] = -{ - {2669.7f, -3428.76f, 268.56f, 1.6f}, - {2692.1f, -3428.76f, 268.56f, 1.6f}, - {2714.4f, -3428.76f, 268.56f, 1.6f}, - {2669.7f, -3431.67f, 268.56f, 1.6f}, - {2692.1f, -3431.67f, 268.56f, 1.6f}, - {2714.4f, -3431.67f, 268.56f, 1.6f} -}; - -const Position PosSummonDead[5] = -{ - {2725.1f, -3310.0f, 268.85f, 3.4f}, - {2699.3f, -3322.8f, 268.60f, 3.3f}, - {2733.1f, -3348.5f, 268.84f, 3.1f}, - {2682.8f, -3304.2f, 268.85f, 3.9f}, - {2664.8f, -3340.7f, 268.23f, 3.7f} -}; - -//const Position PosGroundLivingSide = {2691.2f, -3387.0f, 267.68f, 1.52f}; -//const Position PosGroundDeadSide = {2693.5f, -3334.6f, 267.68f, 4.67f}; -//const Position PosPlatform = {2640.5f, -3360.6f, 285.26f, 0.0f}; - -#define POS_Y_GATE -3360.78f -#define POS_Y_WEST -3285.0f -#define POS_Y_EAST -3434.0f -#define POS_X_NORTH 2750.49f -#define POS_X_SOUTH 2633.84f -#define IN_LIVE_SIDE(who) (who->GetPositionY() < POS_Y_GATE) - -// Predicate function to check that the r efzr unit is NOT on the same side as the source. -struct NotOnSameSide -{ -public: - explicit NotOnSameSide(Unit* pSource) : m_inLiveSide(IN_LIVE_SIDE(pSource)) { } - - bool operator() (Unit const* pTarget) - { - return (m_inLiveSide != IN_LIVE_SIDE(pTarget)); - } - -private: - bool m_inLiveSide; -}; - -class boss_gothik : public CreatureScript -{ -public: - boss_gothik() : CreatureScript("boss_gothik") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_gothikAI : public BossAI - { - explicit boss_gothikAI(Creature* c) : BossAI(c, BOSS_GOTHIK), summons(me) - {} - - EventMap events; - SummonList summons; - bool secondPhase{}; - bool gateOpened{}; - uint8 waveCount{}; - - bool IsInRoom() - { - if (me->GetPositionX() > 2767 || me->GetPositionX() < 2618 || me->GetPositionY() > -3285 || me->GetPositionY() < -3435) - { - EnterEvadeMode(); - return false; - } - return true; - } - - void Reset() override - { - BossAI::Reset(); - events.Reset(); - summons.DespawnAll(); - me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); - me->SetImmuneToPC(false); - me->SetReactState(REACT_PASSIVE); - secondPhase = false; - gateOpened = false; - waveCount = 0; - me->NearTeleportTo(2642.139f, -3386.959f, 285.492f, 6.265f); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - me->SetInCombatWithZone(); - Talk(SAY_INTRO_1); - events.ScheduleEvent(EVENT_INTRO_2, 4s); - events.ScheduleEvent(EVENT_INTRO_3, 9s); - events.ScheduleEvent(EVENT_INTRO_4, 14s); - me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE); - events.ScheduleEvent(EVENT_SUMMON_ADDS, 30s); - events.ScheduleEvent(EVENT_CHECK_PLAYERS, 2min); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - // If central gate is open, attack any one - if (gateOpened) - { - if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 200.0f)) - { - summon->AI()->AttackStart(target); - summon->SetInCombatWithZone(); - summon->SetReactState(REACT_AGGRESSIVE); - summon->CallForHelp(150.0f); - } - } - // Else look for a random target on the side the summoned NPC is - else - { - Map::PlayerList const& pList = me->GetMap()->GetPlayers(); - std::vector tList; - for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr) - { - if (!me->IsWithinDistInMap(itr->GetSource(), 200.0f, true, false, false) || !itr->GetSource()->IsAlive() || itr->GetSource()->IsGameMaster()) - { - continue; - } - if (IN_LIVE_SIDE(itr->GetSource()) != IN_LIVE_SIDE(summon)) - { - continue; - } - tList.push_back(itr->GetSource()); - } - if (!tList.empty()) - { - Player* target = tList[urand(0, tList.size() - 1)]; - summon->AI()->AttackStart(target); - summon->SetInCombatWithZone(); - summon->SetReactState(REACT_AGGRESSIVE); - summon->CallForHelp(150.0f); - } - } - } - - void SummonedCreatureDespawn(Creature* cr) override - { - summons.Despawn(cr); - } - - void KilledUnit(Unit* who) override - { - if (!who->IsPlayer()) - return; - - Talk(SAY_KILL); - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - Talk(SAY_DEATH); - summons.DespawnAll(); - } - - void SummonHelpers(uint32 entry) - { - switch (entry) - { - case NPC_LIVING_TRAINEE: - me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[0].GetPositionX(), PosSummonLiving[0].GetPositionY(), PosSummonLiving[0].GetPositionZ(), PosSummonLiving[0].GetOrientation()); - me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[1].GetPositionX(), PosSummonLiving[1].GetPositionY(), PosSummonLiving[1].GetPositionZ(), PosSummonLiving[1].GetOrientation()); - if (Is25ManRaid()) - { - me->SummonCreature(NPC_LIVING_TRAINEE, PosSummonLiving[2].GetPositionX(), PosSummonLiving[2].GetPositionY(), PosSummonLiving[2].GetPositionZ(), PosSummonLiving[2].GetOrientation()); - } - break; - case NPC_LIVING_KNIGHT: - me->SummonCreature(NPC_LIVING_KNIGHT, PosSummonLiving[3].GetPositionX(), PosSummonLiving[3].GetPositionY(), PosSummonLiving[3].GetPositionZ(), PosSummonLiving[3].GetOrientation()); - if (Is25ManRaid()) - { - me->SummonCreature(NPC_LIVING_KNIGHT, PosSummonLiving[5].GetPositionX(), PosSummonLiving[5].GetPositionY(), PosSummonLiving[5].GetPositionZ(), PosSummonLiving[5].GetOrientation()); - } - break; - case NPC_LIVING_RIDER: - me->SummonCreature(NPC_LIVING_RIDER, PosSummonLiving[4].GetPositionX(), PosSummonLiving[4].GetPositionY(), PosSummonLiving[4].GetPositionZ(), PosSummonLiving[4].GetOrientation()); - break; - } - } - - bool CheckGroupSplitted() - { - Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); - if (!PlayerList.IsEmpty()) - { - bool checklife = false; - bool checkdead = false; - for (auto const& i : PlayerList) - { - Player* player = i.GetSource(); - if (player->IsAlive() && - player->GetPositionX() <= POS_X_NORTH && - player->GetPositionX() >= POS_X_SOUTH && - player->GetPositionY() <= POS_Y_GATE && - player->GetPositionY() >= POS_Y_EAST) - { - checklife = true; - } - else if (player->IsAlive() && - player->GetPositionX() <= POS_X_NORTH && - player->GetPositionX() >= POS_X_SOUTH && - player->GetPositionY() >= POS_Y_GATE && - player->GetPositionY() <= POS_Y_WEST) - { - checkdead = true; - } - - if (checklife && checkdead) - return true; - } - } - return false; - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (!secondPhase) - { - damage = 0; - } - } - - void UpdateAI(uint32 diff) override - { - if (!IsInRoom()) - return; - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - 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; - case EVENT_SHADOW_BOLT: - me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT, false); - events.Repeat(1s); - break; - case EVENT_HARVEST_SOUL: - me->CastSpell(me, SPELL_HARVEST_SOUL, false); - events.Repeat(15s); - break; - case EVENT_TELEPORT: - me->AttackStop(); - if (IN_LIVE_SIDE(me)) - { - me->CastSpell(me, SPELL_TELEPORT_DEAD, false); - } - else - { - me->CastSpell(me, SPELL_TELEPORT_LIVE, false); - } - me->GetThreatMgr().resetAggro(NotOnSameSide(me)); - if (Unit* pTarget = SelectTarget(SelectTargetMethod::MaxDistance, 0)) - { - me->GetThreatMgr().AddThreat(pTarget, 100.0f); - AttackStart(pTarget); - } - events.Repeat(20s); - break; - case EVENT_CHECK_HEALTH: - if (me->HealthBelowPct(30)) - { - if (GameObject* go = instance->GetGameObject(DATA_GOTHIK_INNER_GATE)) - go->SetGoState(GO_STATE_ACTIVE); - - events.CancelEvent(EVENT_TELEPORT); - break; - } - events.Repeat(1s); - break; - case EVENT_SUMMON_ADDS: - if (gothikWaves[waveCount][0]) - { - SummonHelpers(gothikWaves[waveCount][0]); - events.Repeat(Milliseconds(gothikWaves[waveCount][1])); - } - else - { - secondPhase = true; - Talk(SAY_PHASE_TWO); - Talk(EMOTE_PHASE_TWO); - me->CastSpell(me, SPELL_TELEPORT_LIVE, false); - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE); - me->SetImmuneToPC(false); - me->RemoveAllAuras(); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 1s); - events.ScheduleEvent(EVENT_HARVEST_SOUL, 5s, 15s); - events.ScheduleEvent(EVENT_TELEPORT, 20s); - events.ScheduleEvent(EVENT_CHECK_HEALTH, 1s); - } - waveCount++; - break; - case EVENT_CHECK_PLAYERS: - if (!CheckGroupSplitted()) - { - if (GameObject* go = instance->GetGameObject(DATA_GOTHIK_INNER_GATE)) - go->SetGoState(GO_STATE_ACTIVE); - - gateOpened = true; - Talk(EMOTE_GATE_OPENED); - } - break; - } - DoMeleeAttackIfReady(); - } - }; -}; - -class npc_boss_gothik_minion : public CreatureScript -{ -public: - npc_boss_gothik_minion() : CreatureScript("npc_boss_gothik_minion") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct npc_boss_gothik_minionAI : public CombatAI - { - explicit npc_boss_gothik_minionAI(Creature* c) : CombatAI(c) - { - livingSide = IN_LIVE_SIDE(me); - } - EventMap events; - bool livingSide; - bool IsOnSameSide(Unit const* who) const { return livingSide == IN_LIVE_SIDE(who); } - - void Reset() override - { - me->SetReactState(REACT_AGGRESSIVE); - me->SetNoCallAssistance(false); - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - switch (me->GetEntry()) - { - case NPC_LIVING_TRAINEE: - events.ScheduleEvent(EVENT_DEATH_PLAGUE, 3s); - break; - case NPC_DEAD_TRAINEE: - events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, 2500ms); - break; - case NPC_LIVING_KNIGHT: - events.ScheduleEvent(EVENT_SHADOW_MARK, 3s); - break; - case NPC_DEAD_KNIGHT: - events.ScheduleEvent(EVENT_WHIRLWIND, 2s); - break; - case NPC_LIVING_RIDER: - events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 3s); - break; - case NPC_DEAD_RIDER: - events.ScheduleEvent(EVENT_DRAIN_LIFE, 2s, 3500ms); - events.ScheduleEvent(EVENT_UNHOLY_FRENZY, 5s, 9s); - break; - case NPC_DEAD_HORSE: - events.ScheduleEvent(EVENT_STOMP, 2s, 5s); - break; - } - } - - void JustDied(Unit*) override - { - switch (me->GetEntry()) - { - case NPC_LIVING_TRAINEE: - DoCastAOE(SPELL_ANCHOR_1_TRAINEE, true); - break; - case NPC_LIVING_KNIGHT: - DoCastAOE(SPELL_ANCHOR_1_DK, true); - break; - case NPC_LIVING_RIDER: - DoCastAOE(SPELL_ANCHOR_1_RIDER, true); - break; - } - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void UpdateAI(uint32 diff) override - { - events.Update(diff); - if (me->GetUnitState() == UNIT_STATE_CASTING) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_DEATH_PLAGUE: - me->CastSpell(me->GetVictim(), SPELL_DEATH_PLAGUE, false); - events.Repeat(4s, 7s); - break; - case EVENT_ARCANE_EXPLOSION: - if (Unit* victim = me->GetVictim()) - { - if (victim->IsWithinDist(me, 20)) - { - me->CastSpell(victim, SPELL_ARCANE_EXPLOSION, false); - } - } - events.Repeat(2500ms); - break; - case EVENT_SHADOW_MARK: - if (Unit* victim = me->GetVictim()) - { - if (victim->IsWithinDist(me, 10)) - { - me->CastSpell(victim, SPELL_SHADOW_MARK, false); - } - } - events.Repeat(5s, 7s); - break; - case EVENT_WHIRLWIND: - if (Unit* victim = me->GetVictim()) - { - if (victim->IsWithinDist(me, 10)) - { - me->CastSpell(victim, SPELL_WHIRLWIND, false); - } - } - events.Repeat(4s, 6s); - break; - case EVENT_SHADOW_BOLT_VOLLEY: - me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT_VOLLEY, false); - events.Repeat(5s); - break; - case EVENT_DRAIN_LIFE: - if (Unit* victim = me->GetVictim()) - { - if (victim->IsWithinDist(me, 20)) - { - me->CastSpell(victim, SPELL_DRAIN_LIFE, false); - } - } - events.Repeat(8s, 12s); - break; - case EVENT_UNHOLY_FRENZY: - me->AddAura(SPELL_UNHOLY_FRENZY, me); - events.Repeat(15s, 17s); - break; - case EVENT_STOMP: - if (Unit* victim = me->GetVictim()) - { - if (victim->IsWithinDist(me, 10)) - { - me->CastSpell(victim, SPELL_STOMP, false); - } - } - events.Repeat(4s, 9s); - break; - } - DoMeleeAttackIfReady(); - } - }; -}; - -class npc_gothik_trigger : public CreatureScript -{ -public: - npc_gothik_trigger() : CreatureScript("npc_gothik_trigger") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_gothik_triggerAI(creature); - } - - struct npc_gothik_triggerAI : public ScriptedAI - { - 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, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override { damage = 0; } - - Creature* SelectRandomSkullPile() - { - std::list triggers; - me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f); - // Remove triggers that are on live side or soul triggers on the platform - triggers.remove_if([](Creature* trigger){ - return ((trigger->GetPositionY() < POS_Y_GATE) || (trigger->GetPositionZ() > 280.0f)); - }); - if (!triggers.empty()) - { - std::list::iterator itr = triggers.begin(); - std::advance(itr, urand(0, triggers.size() - 1)); - return *itr; - } - return nullptr; - } - - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override - { - if (!spell) - { - return; - } - - switch (spell->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, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); - break; - case SPELL_SKULLS_DK: - DoSummon(NPC_DEAD_KNIGHT, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); - break; - case SPELL_SKULLS_RIDER: - DoSummon(NPC_DEAD_RIDER, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); - DoSummon(NPC_DEAD_HORSE, me, 0.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); - break; - } - } - - // dead side summons are "owned" by gothik - void JustSummoned(Creature* summon) override - { - if (Creature* gothik = me->GetInstanceScript()->GetCreature(DATA_GOTHIK_BOSS)) - gothik->AI()->JustSummoned(summon); - } - - void SummonedCreatureDespawn(Creature* summon) override - { - if (Creature* gothik = me->GetInstanceScript()->GetCreature(DATA_GOTHIK_BOSS)) - gothik->AI()->SummonedCreatureDespawn(summon); - } - }; -}; - -class spell_gothik_shadow_bolt_volley : public SpellScript -{ - PrepareSpellScript(spell_gothik_shadow_bolt_volley); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SHADOW_MARK }); - } - - void FilterTargets(std::list& targets) - { - targets.remove_if(Acore::UnitAuraCheck(false, SPELL_SHADOW_MARK)); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gothik_shadow_bolt_volley::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - } -}; - -} - -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp index 28514cb8b..e37646f12 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "boss_grobbulus.h" #include "CreatureScript.h" #include "PassiveAI.h" #include "ScriptedCreature.h" @@ -25,10 +24,278 @@ #include "SpellScriptLoader.h" #include "naxxramas.h" -using namespace Grobbulus; +enum Spells +{ + SPELL_POISON_CLOUD = 28240, + SPELL_MUTATING_INJECTION = 28169, + SPELL_MUTATING_EXPLOSION = 28206, + SPELL_SLIME_SPRAY = 28157, + SPELL_POISON_CLOUD_DAMAGE_AURA = 28158, + SPELL_BERSERK = 26662, + SPELL_BOMBARD_SLIME = 28280 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Emotes +{ + EMOTE_SLIME = 0 +}; + +enum Events +{ + EVENT_BERSERK = 1, + EVENT_POISON_CLOUD = 2, + EVENT_SLIME_SPRAY = 3, + EVENT_MUTATING_INJECTION = 4 +}; + +enum Misc +{ + NPC_FALLOUT_SLIME = 16290, + NPC_SEWAGE_SLIME = 16375, + NPC_STICHED_GIANT = 16025 +}; + +class boss_grobbulus : public CreatureScript +{ +public: + boss_grobbulus() : CreatureScript("boss_grobbulus") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_grobbulusAI : public BossAI + { + explicit boss_grobbulusAI(Creature* c) : BossAI(c, BOSS_GROBBULUS), summons(me) + {} + + EventMap events; + SummonList summons; + uint32 dropSludgeTimer{}; + + void Reset() override + { + BossAI::Reset(); + events.Reset(); + summons.DespawnAll(); + dropSludgeTimer = 0; + } + + void PullChamberAdds() + { + std::list StichedGiants; + me->GetCreaturesWithEntryInRange(StichedGiants, 300.0f, NPC_STICHED_GIANT); + for (std::list::const_iterator itr = StichedGiants.begin(); itr != StichedGiants.end(); ++itr) + { + (*itr)->ToCreature()->AI()->AttackStart(me->GetVictim()); + } + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + PullChamberAdds(); + me->SetInCombatWithZone(); + events.ScheduleEvent(EVENT_POISON_CLOUD, 15s); + events.ScheduleEvent(EVENT_MUTATING_INJECTION, 20s); + events.ScheduleEvent(EVENT_SLIME_SPRAY, 10s); + events.ScheduleEvent(EVENT_BERSERK, RAID_MODE(720s, 540s)); + } + + void JustSummoned(Creature* cr) override + { + if (cr->GetEntry() == NPC_FALLOUT_SLIME) + { + cr->SetInCombatWithZone(); + } + summons.Summon(cr); + } + + void SummonedCreatureDespawn(Creature* summon) override + { + summons.Despawn(summon); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + summons.DespawnAll(); + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void UpdateAI(uint32 diff) override + { + dropSludgeTimer += diff; + if (!me->IsInCombat() && dropSludgeTimer >= 5000) + { + if (me->IsWithinDist3d(3178, -3305, 319, 5.0f) && !summons.HasEntry(NPC_SEWAGE_SLIME)) + { + me->CastSpell(3128.96f + irand(-20, 20), -3312.96f + irand(-20, 20), 293.25f, SPELL_BOMBARD_SLIME, false); + } + dropSludgeTimer = 0; + } + + if (!UpdateVictim()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_POISON_CLOUD: + me->CastSpell(me, SPELL_POISON_CLOUD, true); + events.Repeat(15s); + break; + case EVENT_BERSERK: + me->CastSpell(me, SPELL_BERSERK, true); + break; + case EVENT_SLIME_SPRAY: + Talk(EMOTE_SLIME); + me->CastSpell(me->GetVictim(), SPELL_SLIME_SPRAY, false); + events.Repeat(20s); + break; + case EVENT_MUTATING_INJECTION: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true, false, -SPELL_MUTATING_INJECTION)) + { + me->CastSpell(target, SPELL_MUTATING_INJECTION, false); + } + events.Repeat(Milliseconds(6000 + uint32(120 * me->GetHealthPct()))); + break; + } + DoMeleeAttackIfReady(); + } + }; +}; + +class boss_grobbulus_poison_cloud : public CreatureScript +{ +public: + boss_grobbulus_poison_cloud() : CreatureScript("boss_grobbulus_poison_cloud") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_grobbulus_poison_cloudAI : public NullCreatureAI + { + explicit boss_grobbulus_poison_cloudAI(Creature* pCreature) : NullCreatureAI(pCreature) { } + + uint32 sizeTimer{}; + uint32 auraVisualTimer{}; + + void Reset() override + { + sizeTimer = 0; + auraVisualTimer = 1; + me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f); + me->SetFaction(FACTION_BOOTY_BAY); + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void UpdateAI(uint32 diff) override + { + if (auraVisualTimer) // this has to be delayed to be visible + { + auraVisualTimer += diff; + if (auraVisualTimer >= 1000) + { + me->CastSpell(me, SPELL_POISON_CLOUD_DAMAGE_AURA, true); + auraVisualTimer = 0; + } + } + sizeTimer += diff; // increase size to 15yd in 60 seconds, 0.00025 is the growth of size in 1ms + me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f + (0.00025f * sizeTimer)); + } + }; +}; + +class spell_grobbulus_poison : public SpellScript +{ + PrepareSpellScript(spell_grobbulus_poison); + + void FilterTargets(std::list& targets) + { + std::list tmplist; + for (auto& target : targets) + { + if (GetCaster()->IsWithinDist3d(target, 0.0f)) + { + tmplist.push_back(target); + } + } + targets.clear(); + for (auto& itr : tmplist) + { + targets.push_back(itr); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_grobbulus_poison::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } +}; + +class spell_grobbulus_mutating_injection_aura : public AuraScript +{ + PrepareAuraScript(spell_grobbulus_mutating_injection_aura); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_MUTATING_EXPLOSION }); + } + + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + switch (GetTargetApplication()->GetRemoveMode()) + { + case AURA_REMOVE_BY_ENEMY_SPELL: + case AURA_REMOVE_BY_EXPIRE: + if (auto caster = GetCaster()) + { + caster->CastSpell(GetTarget(), SPELL_MUTATING_EXPLOSION, true); + } + break; + default: + return; + } + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_grobbulus_mutating_injection_aura::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } +}; + +class spell_grobbulus_slime_spray : public SpellScript +{ + PrepareSpellScript(spell_grobbulus_slime_spray); + + void HandleHit() + { + if (Unit* target = GetHitUnit()) + GetCaster()->SummonCreature(NPC_FALLOUT_SLIME, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()); + } + + void Register() override + { + OnHit += SpellHitFn(spell_grobbulus_slime_spray::HandleHit); + } +}; void AddSC_boss_grobbulus() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.h b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.h deleted file mode 100644 index 3e904a681..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.h +++ /dev/null @@ -1,291 +0,0 @@ -#ifndef BOSSGROBBULUS_H_ -#define BOSSGROBBULUS_H_ - -#include "PassiveAI.h" -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellAuraEffects.h" -#include "SpellAuras.h" -#include "SpellScript.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Grobbulus { - -enum Spells -{ - SPELL_POISON_CLOUD = 28240, - SPELL_MUTATING_INJECTION = 28169, - SPELL_MUTATING_EXPLOSION = 28206, - SPELL_SLIME_SPRAY = 28157, - SPELL_POISON_CLOUD_DAMAGE_AURA = 28158, - SPELL_BERSERK = 26662, - SPELL_BOMBARD_SLIME = 28280 -}; - -enum Emotes -{ - EMOTE_SLIME = 0 -}; - -enum Events -{ - EVENT_BERSERK = 1, - EVENT_POISON_CLOUD = 2, - EVENT_SLIME_SPRAY = 3, - EVENT_MUTATING_INJECTION = 4 -}; - -enum Misc -{ - NPC_FALLOUT_SLIME = 16290, - NPC_SEWAGE_SLIME = 16375, - NPC_STICHED_GIANT = 16025 -}; - -class boss_grobbulus : public CreatureScript -{ -public: - boss_grobbulus() : CreatureScript("boss_grobbulus") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_grobbulusAI : public BossAI - { - explicit boss_grobbulusAI(Creature* c) : BossAI(c, BOSS_GROBBULUS), summons(me) - {} - - EventMap events; - SummonList summons; - uint32 dropSludgeTimer{}; - - void Reset() override - { - BossAI::Reset(); - events.Reset(); - summons.DespawnAll(); - dropSludgeTimer = 0; - } - - void PullChamberAdds() - { - std::list StichedGiants; - me->GetCreaturesWithEntryInRange(StichedGiants, 300.0f, NPC_STICHED_GIANT); - for (std::list::const_iterator itr = StichedGiants.begin(); itr != StichedGiants.end(); ++itr) - { - (*itr)->ToCreature()->AI()->AttackStart(me->GetVictim()); - } - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - PullChamberAdds(); - me->SetInCombatWithZone(); - events.ScheduleEvent(EVENT_POISON_CLOUD, 15s); - events.ScheduleEvent(EVENT_MUTATING_INJECTION, 20s); - events.ScheduleEvent(EVENT_SLIME_SPRAY, 10s); - events.ScheduleEvent(EVENT_BERSERK, RAID_MODE(720s, 540s)); - } - - void JustSummoned(Creature* cr) override - { - if (cr->GetEntry() == NPC_FALLOUT_SLIME) - { - cr->SetInCombatWithZone(); - } - summons.Summon(cr); - } - - void SummonedCreatureDespawn(Creature* summon) override - { - summons.Despawn(summon); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - summons.DespawnAll(); - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void UpdateAI(uint32 diff) override - { - dropSludgeTimer += diff; - if (!me->IsInCombat() && dropSludgeTimer >= 5000) - { - if (me->IsWithinDist3d(3178, -3305, 319, 5.0f) && !summons.HasEntry(NPC_SEWAGE_SLIME)) - { - me->CastSpell(3128.96f + irand(-20, 20), -3312.96f + irand(-20, 20), 293.25f, SPELL_BOMBARD_SLIME, false); - } - dropSludgeTimer = 0; - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_POISON_CLOUD: - me->CastSpell(me, SPELL_POISON_CLOUD, true); - events.Repeat(15s); - break; - case EVENT_BERSERK: - me->CastSpell(me, SPELL_BERSERK, true); - break; - case EVENT_SLIME_SPRAY: - Talk(EMOTE_SLIME); - me->CastSpell(me->GetVictim(), SPELL_SLIME_SPRAY, false); - events.Repeat(20s); - break; - case EVENT_MUTATING_INJECTION: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true, false, -SPELL_MUTATING_INJECTION)) - { - me->CastSpell(target, SPELL_MUTATING_INJECTION, false); - } - events.Repeat(Milliseconds(6000 + uint32(120 * me->GetHealthPct()))); - break; - } - DoMeleeAttackIfReady(); - } - }; -}; - -class boss_grobbulus_poison_cloud : public CreatureScript -{ -public: - boss_grobbulus_poison_cloud() : CreatureScript("boss_grobbulus_poison_cloud") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_grobbulus_poison_cloudAI : public NullCreatureAI - { - explicit boss_grobbulus_poison_cloudAI(Creature* pCreature) : NullCreatureAI(pCreature) { } - - uint32 sizeTimer{}; - uint32 auraVisualTimer{}; - - void Reset() override - { - sizeTimer = 0; - auraVisualTimer = 1; - me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f); - me->SetFaction(FACTION_BOOTY_BAY); - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void UpdateAI(uint32 diff) override - { - if (auraVisualTimer) // this has to be delayed to be visible - { - auraVisualTimer += diff; - if (auraVisualTimer >= 1000) - { - me->CastSpell(me, SPELL_POISON_CLOUD_DAMAGE_AURA, true); - auraVisualTimer = 0; - } - } - sizeTimer += diff; // increase size to 15yd in 60 seconds, 0.00025 is the growth of size in 1ms - me->SetFloatValue(UNIT_FIELD_COMBATREACH, 2.0f + (0.00025f * sizeTimer)); - } - }; -}; - -class spell_grobbulus_poison : public SpellScript -{ - PrepareSpellScript(spell_grobbulus_poison); - - void FilterTargets(std::list& targets) - { - std::list tmplist; - for (auto& target : targets) - { - if (GetCaster()->IsWithinDist3d(target, 0.0f)) - { - tmplist.push_back(target); - } - } - targets.clear(); - for (auto& itr : tmplist) - { - targets.push_back(itr); - } - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_grobbulus_poison::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - } -}; - -class spell_grobbulus_mutating_injection_aura : public AuraScript -{ - PrepareAuraScript(spell_grobbulus_mutating_injection_aura); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_MUTATING_EXPLOSION }); - } - - void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - switch (GetTargetApplication()->GetRemoveMode()) - { - case AURA_REMOVE_BY_ENEMY_SPELL: - case AURA_REMOVE_BY_EXPIRE: - if (auto caster = GetCaster()) - { - caster->CastSpell(GetTarget(), SPELL_MUTATING_EXPLOSION, true); - } - break; - default: - return; - } - } - - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_grobbulus_mutating_injection_aura::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } -}; - -class spell_grobbulus_slime_spray : public SpellScript -{ - PrepareSpellScript(spell_grobbulus_slime_spray); - - void HandleHit() - { - if (Unit* target = GetHitUnit()) - GetCaster()->SummonCreature(NPC_FALLOUT_SLIME, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()); - } - - void Register() override - { - OnHit += SpellHitFn(spell_grobbulus_slime_spray::HandleHit); - } -}; - - -} - -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_heigan.h b/src/server/scripts/Northrend/Naxxramas/boss_heigan.h deleted file mode 100644 index a199067bb..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_heigan.h +++ /dev/null @@ -1,215 +0,0 @@ -#ifndef BOSS_HEIGAN_H_ -#define BOSS_HEIGAN_H_ - -#include "Player.h" -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Heigan { - -enum Says -{ - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_TAUNT = 2, - EMOTE_DEATH = 3, - EMOTE_DANCE = 4, - EMOTE_DANCE_END = 5, - SAY_DANCE = 6 -}; - -enum Spells -{ - SPELL_SPELL_DISRUPTION = 29310, - SPELL_DECREPIT_FEVER = 29998, - SPELL_PLAGUE_CLOUD = 29350, - SPELL_TELEPORT_SELF = 30211 -}; - -enum Events -{ - EVENT_DISRUPTION = 1, - EVENT_DECEPIT_FEVER = 2, - EVENT_ERUPT_SECTION = 3, - EVENT_SWITCH_PHASE = 4, - EVENT_SAFETY_DANCE = 5, - EVENT_PLAGUE_CLOUD = 6 -}; - -enum Misc -{ - PHASE_SLOW_DANCE = 0, - PHASE_FAST_DANCE = 1 -}; - -class boss_heigan : public CreatureScript -{ -public: - boss_heigan() : CreatureScript("boss_heigan") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_heiganAI : public BossAI - { - explicit boss_heiganAI(Creature* c) : BossAI(c, BOSS_HEIGAN) - {} - - EventMap events; - uint8 currentPhase{}; - uint8 currentSection{}; - bool moveRight{}; - - void Reset() override - { - BossAI::Reset(); - events.Reset(); - currentPhase = 0; - currentSection = 3; - moveRight = true; - } - - void KilledUnit(Unit* who) override - { - if (!who->IsPlayer()) - return; - - Talk(SAY_SLAY); - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - Talk(EMOTE_DEATH); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - me->SetInCombatWithZone(); - Talk(SAY_AGGRO); - StartFightPhase(PHASE_SLOW_DANCE); - } - - void StartFightPhase(uint8 phase) - { - currentSection = 3; - currentPhase = phase; - events.Reset(); - if (phase == PHASE_SLOW_DANCE) - { - me->CastStop(); - me->SetReactState(REACT_AGGRESSIVE); - DoZoneInCombat(); - events.ScheduleEvent(EVENT_DISRUPTION, 12s, 15s); - events.ScheduleEvent(EVENT_DECEPIT_FEVER, 17s); - events.ScheduleEvent(EVENT_ERUPT_SECTION, 15s); - events.ScheduleEvent(EVENT_SWITCH_PHASE, 90s); - } - else // if (phase == PHASE_FAST_DANCE) - { - Talk(EMOTE_DANCE); - Talk(SAY_DANCE); - me->AttackStop(); - me->StopMoving(); - me->SetReactState(REACT_PASSIVE); - me->CastSpell(me, SPELL_TELEPORT_SELF, false); - me->SetFacingTo(2.40f); - events.ScheduleEvent(EVENT_PLAGUE_CLOUD, 1s); - events.ScheduleEvent(EVENT_ERUPT_SECTION, 7s); - events.ScheduleEvent(EVENT_SWITCH_PHASE, 45s); - } - events.ScheduleEvent(EVENT_SAFETY_DANCE, 5s); - } - - bool IsInRoom(Unit* who) - { - if (who->GetPositionX() > 2826 || who->GetPositionX() < 2723 || who->GetPositionY() > -3641 || who->GetPositionY() < -3736) - { - if (who->GetGUID() == me->GetGUID()) - EnterEvadeMode(); - - return false; - } - return true; - } - - void UpdateAI(uint32 diff) override - { - if (!IsInRoom(me)) - return; - - if (!UpdateVictim()) - return; - - events.Update(diff); - - switch (events.ExecuteEvent()) - { - case EVENT_DISRUPTION: - me->CastSpell(me, SPELL_SPELL_DISRUPTION, false); - events.Repeat(10s); - break; - case EVENT_DECEPIT_FEVER: - me->CastSpell(me, SPELL_DECREPIT_FEVER, false); - events.Repeat(22s, 25s); - break; - case EVENT_PLAGUE_CLOUD: - me->CastSpell(me, SPELL_PLAGUE_CLOUD, false); - break; - case EVENT_SWITCH_PHASE: - if (currentPhase == PHASE_SLOW_DANCE) - { - StartFightPhase(PHASE_FAST_DANCE); - } - else - { - StartFightPhase(PHASE_SLOW_DANCE); - Talk(EMOTE_DANCE_END); // avoid play the emote on aggro - } - break; - case EVENT_ERUPT_SECTION: - { - instance->SetData(DATA_HEIGAN_ERUPTION, currentSection); - if (currentSection == 3) - moveRight = false; - else if (currentSection == 0) - moveRight = true; - - moveRight ? currentSection++ : currentSection--; - - if (currentPhase == PHASE_SLOW_DANCE) - Talk(SAY_TAUNT); - - events.Repeat(currentPhase == PHASE_SLOW_DANCE ? 10s : 4s); - break; - } - case EVENT_SAFETY_DANCE: - { - Map::PlayerList const& pList = me->GetMap()->GetPlayers(); - for (auto const& itr : pList) - { - if (IsInRoom(itr.GetSource()) && !itr.GetSource()->IsAlive()) - { - instance->SetData(DATA_DANCE_FAIL, 0); - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - return; - } - } - events.Repeat(5s); - return; - } - } - - DoMeleeAttackIfReady(); - } - }; -}; - -} -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp index d7be6124c..38fce6620 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "boss_kelthuzad.h" #include "CreatureScript.h" #include "Player.h" #include "ScriptedCreature.h" @@ -23,11 +22,661 @@ #include "SpellScriptLoader.h" #include "naxxramas.h" +enum Yells +{ + SAY_ANSWER_REQUEST = 3, + SAY_TAUNT = 6, + SAY_AGGRO = 7, + SAY_SLAY = 8, + SAY_DEATH = 9, + SAY_CHAIN = 10, + SAY_FROST_BLAST = 11, + SAY_REQUEST_AID = 12, + EMOTE_PHASE_TWO = 13, + SAY_SUMMON_MINIONS = 14, + SAY_SPECIAL = 15, -using namespace Kelthuzad; + EMOTE_GUARDIAN_FLEE = 0, + EMOTE_GUARDIAN_APPEAR = 1 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Spells +{ + // Kel'Thzuad + SPELL_FROST_BOLT_SINGLE = 28478, + SPELL_FROST_BOLT_MULTI = 28479, + SPELL_SHADOW_FISURE = 27810, + SPELL_VOID_BLAST = 27812, + SPELL_DETONATE_MANA = 27819, + SPELL_MANA_DETONATION_DAMAGE = 27820, + SPELL_FROST_BLAST = 27808, + SPELL_CHAINS_OF_KELTHUZAD = 28410, // 28408 script effect + SPELL_BERSERK = 28498, + SPELL_KELTHUZAD_CHANNEL = 29423, + + // Minions + SPELL_FRENZY = 28468, + SPELL_MORTAL_WOUND = 28467, + SPELL_BLOOD_TAP = 28470 +}; + +enum Misc +{ + NPC_SOLDIER_OF_THE_FROZEN_WASTES = 16427, + NPC_UNSTOPPABLE_ABOMINATION = 16428, + NPC_SOUL_WEAVER = 16429, + NPC_GUARDIAN_OF_ICECROWN = 16441, + + ACTION_CALL_HELP_ON = 1, + ACTION_CALL_HELP_OFF = 2, + ACTION_SECOND_PHASE = 3, + ACTION_GUARDIANS_OFF = 4 +}; + +enum Event +{ + // Kel'Thuzad + EVENT_SUMMON_SOLDIER = 1, + EVENT_SUMMON_UNSTOPPABLE_ABOMINATION = 2, + EVENT_SUMMON_SOUL_WEAVER = 3, + EVENT_PHASE_2 = 4, + EVENT_FROST_BOLT_SINGLE = 5, + EVENT_FROST_BOLT_MULTI = 6, + EVENT_DETONATE_MANA = 7, + EVENT_PHASE_3 = 8, + EVENT_P3_LICH_KING_SAY = 9, + EVENT_SHADOW_FISSURE = 10, + EVENT_FROST_BLAST = 11, + EVENT_CHAINS = 12, + EVENT_SUMMON_GUARDIAN_OF_ICECROWN = 13, + EVENT_FLOOR_CHANGE = 14, + EVENT_ENRAGE = 15, + EVENT_SPAWN_POOL = 16, + + // Minions + EVENT_MINION_FRENZY = 17, + EVENT_MINION_MORTAL_WOUND = 18, + EVENT_MINION_BLOOD_TAP = 19 +}; + +const Position SummonGroups[12] = +{ + // Portals + {3783.272705f, -5062.697266f, 143.711203f, 3.617599f}, // LEFT_FAR + {3730.291260f, -5027.239258f, 143.956909f, 4.461900f}, // LEFT_MIDDLE + {3683.868652f, -5057.281250f, 143.183884f, 5.237086f}, // LEFT_NEAR + {3759.355225f, -5174.128418f, 143.802383f, 2.170104f}, // RIGHT_FAR + {3700.724365f, -5185.123047f, 143.928024f, 1.309310f}, // RIGHT_MIDDLE + {3665.121094f, -5138.679199f, 143.183212f, 0.604023f}, // RIGHT_NEAR + + // Middle + {3769.34f, -5071.80f, 143.2082f, 3.658f}, + {3729.78f, -5043.56f, 143.3867f, 4.475f}, + {3682.75f, -5055.26f, 143.1848f, 5.295f}, + {3752.58f, -5161.82f, 143.2944f, 2.126f}, + {3702.83f, -5171.70f, 143.4356f, 1.305f}, + {3665.30f, -5141.55f, 143.1846f, 0.566f} +}; + +const Position SpawnPool[7] = +{ + // Portals + {3783.272705f, -5062.697266f, 143.711203f, 3.617599f}, // LEFT_FAR + {3730.291260f, -5027.239258f, 143.956909f, 4.461900f}, // LEFT_MIDDLE + {3683.868652f, -5057.281250f, 143.183884f, 5.237086f}, // LEFT_NEAR + {3759.355225f, -5174.128418f, 143.802383f, 2.170104f}, // RIGHT_FAR + {3700.724365f, -5185.123047f, 143.928024f, 1.309310f}, // RIGHT_MIDDLE + {3665.121094f, -5138.679199f, 143.183212f, 0.604023f}, // RIGHT_NEAR + {3651.729980f, -5092.620117f, 143.380005f, 6.050000f} // GATE +}; + +class boss_kelthuzad : public CreatureScript +{ +public: + boss_kelthuzad() : CreatureScript("boss_kelthuzad") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_kelthuzadAI : public BossAI + { + explicit boss_kelthuzadAI(Creature* c) : BossAI(c, BOSS_KELTHUZAD), summons(me) + {} + + EventMap events; + SummonList summons; + + float NormalizeOrientation(float o) + { + return std::fmod(o, 2.0f * static_cast(M_PI)); // Only positive values will be passed + } + + void SpawnHelpers() + { + // spawn at gate + me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3656.19f, -5093.78f, 143.33f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo center + me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3657.94f, -5087.68f, 143.60f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo left + me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3655.48f, -5100.05f, 143.53f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo right + me->SummonCreature(NPC_SOUL_WEAVER, 3651.73f, -5092.62f, 143.38f, 6.05, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // soul behind + me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3660.17f, -5092.45f, 143.37f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front left + me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.39f, -5096.21f, 143.29f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front right + me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.29f, -5090.19f, 143.48f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske left left + me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3657.43f, -5098.03f, 143.41f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right + me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3654.36f, -5090.51f, 143.48f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske behind left + me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3653.35f, -5095.91f, 143.41f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right + + // 6 rooms, 8 soldiers, 3 abominations and 1 weaver in each room | middle positions in table starts from 6 + for (uint8 i = 6; i < 12; ++i) + { + for (uint8 j = 0; j < 8; ++j) + { + float angle = M_PI * 2 / 8 * j; + me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + } + } + for (uint8 i = 6; i < 12; ++i) + { + for (uint8 j = 1; j < 4; ++j) + { + float dist = j == 2 ? 0.0f : 8.0f; // second in middle + float angle = SummonGroups[i].GetOrientation() + M_PI * 2 / 4 * j; + me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, SummonGroups[i].GetPositionX() + dist * cos(angle), SummonGroups[i].GetPositionY() + dist * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + } + } + for (uint8 i = 6; i < 12; ++i) + { + for (uint8 j = 0; j < 1; ++j) + { + float angle = SummonGroups[i].GetOrientation() + M_PI; + me->SummonCreature(NPC_SOUL_WEAVER, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ() + 0.5f, SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + } + } + } + + void SummonHelper(uint32 entry, uint32 count) + { + for (uint8 i = 0; i < count; ++i) + { + if (Creature* cr = me->SummonCreature(entry, SpawnPool[urand(0, 6)], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000)) + { + if (Unit* target = SelectTargetFromPlayerList(100.0f)) + { + cr->AI()->DoAction(ACTION_CALL_HELP_OFF); + cr->AI()->AttackStart(target); + } + } + } + } + + void Reset() override + { + BossAI::Reset(); + events.Reset(); + summons.DespawnAll(); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetReactState(REACT_AGGRESSIVE); + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_FLOOR)) + { + go->SetPhaseMask(1, true); + go->SetGoState(GO_STATE_READY); + } + + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_1)) + go->SetGoState(GO_STATE_READY); + + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_2)) + go->SetGoState(GO_STATE_READY); + + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_3)) + go->SetGoState(GO_STATE_READY); + + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_4)) + go->SetGoState(GO_STATE_READY); + } + + void EnterEvadeMode(EvadeReason why) override + { + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + ScriptedAI::EnterEvadeMode(why); + } + + void KilledUnit(Unit* who) override + { + if (!who->IsPlayer()) + return; + + Talk(SAY_SLAY); + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + summons.DoAction(ACTION_GUARDIANS_OFF); + if (Creature* guardian = summons.GetCreatureWithEntry(NPC_GUARDIAN_OF_ICECROWN)) + { + guardian->AI()->Talk(EMOTE_GUARDIAN_FLEE); + } + Talk(SAY_DEATH); + } + + void MoveInLineOfSight(Unit* who) override + { + if (!me->IsInCombat() && who->IsPlayer() && who->IsAlive() && me->GetDistance(who) <= 50.0f) + AttackStart(who); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_SUMMON_MINIONS); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->RemoveAllAttackers(); + me->SetTarget(); + me->SetReactState(REACT_PASSIVE); + me->CastSpell(me, SPELL_KELTHUZAD_CHANNEL, false); + events.ScheduleEvent(EVENT_SPAWN_POOL, 5s); + events.ScheduleEvent(EVENT_SUMMON_SOLDIER, 6400ms); + events.ScheduleEvent(EVENT_SUMMON_UNSTOPPABLE_ABOMINATION, 10s); + events.ScheduleEvent(EVENT_SUMMON_SOUL_WEAVER, 12s); + events.ScheduleEvent(EVENT_PHASE_2, 228s); + events.ScheduleEvent(EVENT_ENRAGE, 15min); + + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_FLOOR)) + { + events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15s); + go->SetGoState(GO_STATE_ACTIVE); + } + } + + void JustSummoned(Creature* cr) override + { + summons.Summon(cr); + if (!cr->IsInCombat()) + { + cr->GetMotionMaster()->MoveRandom(5); + } + if (cr->GetEntry() == NPC_GUARDIAN_OF_ICECROWN) + { + cr->SetHomePosition(cr->GetPositionX(), cr->GetPositionY(), cr->GetPositionZ(), cr->GetOrientation()); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + if (!me->HasAura(SPELL_KELTHUZAD_CHANNEL)) + { + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + switch (events.ExecuteEvent()) + { + case EVENT_FLOOR_CHANGE: + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_FLOOR)) + { + events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15s); + go->SetGoState(GO_STATE_READY); + go->SetPhaseMask(2, true); + } + break; + case EVENT_SPAWN_POOL: + SpawnHelpers(); + break; + case EVENT_SUMMON_SOLDIER: + SummonHelper(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 1); + events.Repeat(3100ms); + break; + case EVENT_SUMMON_UNSTOPPABLE_ABOMINATION: + SummonHelper(NPC_UNSTOPPABLE_ABOMINATION, 1); + events.Repeat(18s + 500ms); + break; + case EVENT_SUMMON_SOUL_WEAVER: + SummonHelper(NPC_SOUL_WEAVER, 1); + events.Repeat(30s); + break; + case EVENT_PHASE_2: + Talk(EMOTE_PHASE_TWO); + Talk(SAY_AGGRO); + events.Reset(); + summons.DoAction(ACTION_SECOND_PHASE); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->GetMotionMaster()->MoveChase(me->GetVictim()); + me->RemoveAura(SPELL_KELTHUZAD_CHANNEL); + me->SetReactState(REACT_AGGRESSIVE); + events.ScheduleEvent(EVENT_FROST_BOLT_SINGLE, 2s, 10s); + events.ScheduleEvent(EVENT_FROST_BOLT_MULTI, 15s, 30s); + events.ScheduleEvent(EVENT_DETONATE_MANA, 30s); + events.ScheduleEvent(EVENT_PHASE_3, 1s); + events.ScheduleEvent(EVENT_SHADOW_FISSURE, 25s); + events.ScheduleEvent(EVENT_FROST_BLAST, 45s); + if (Is25ManRaid()) + { + events.ScheduleEvent(EVENT_CHAINS, 90s); + } + break; + case EVENT_ENRAGE: + me->CastSpell(me, SPELL_BERSERK, true); + break; + case EVENT_FROST_BOLT_SINGLE: + me->CastSpell(me->GetVictim(), SPELL_FROST_BOLT_SINGLE, false); + events.Repeat(2s, 10s); + break; + case EVENT_FROST_BOLT_MULTI: + me->CastSpell(me, SPELL_FROST_BOLT_MULTI, false); + events.Repeat(15s, 30s); + break; + case EVENT_SHADOW_FISSURE: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) + { + me->CastSpell(target, SPELL_SHADOW_FISURE, false); + } + events.Repeat(25s); + break; + case EVENT_FROST_BLAST: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0, true, RAID_MODE(false, true))) + { + me->CastSpell(target, SPELL_FROST_BLAST, false); + } + Talk(SAY_FROST_BLAST); + events.Repeat(45s); + break; + case EVENT_CHAINS: + for (uint8 i = 0; i < 3; ++i) + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 200, true, false, -SPELL_CHAINS_OF_KELTHUZAD)) + { + me->CastSpell(target, SPELL_CHAINS_OF_KELTHUZAD, true); + } + } + Talk(SAY_CHAIN); + events.Repeat(90s); + break; + case EVENT_DETONATE_MANA: + { + std::vector unitList; + ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList(); + for (auto itr : threatList) + { + if (itr->getTarget()->IsPlayer() + && itr->getTarget()->getPowerType() == POWER_MANA + && itr->getTarget()->GetPower(POWER_MANA)) + { + unitList.push_back(itr->getTarget()); + } + } + if (!unitList.empty()) + { + auto itr = unitList.begin(); + advance(itr, urand(0, unitList.size() - 1)); + me->CastSpell(*itr, SPELL_DETONATE_MANA, false); + Talk(SAY_SPECIAL); + } + events.Repeat(30s); + break; + } + case EVENT_PHASE_3: + if (me->HealthBelowPct(45)) + { + Talk(SAY_REQUEST_AID); + events.DelayEvents(5500ms); + events.ScheduleEvent(EVENT_P3_LICH_KING_SAY, 5s); + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_1)) + go->SetGoState(GO_STATE_ACTIVE); + + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_2)) + go->SetGoState(GO_STATE_ACTIVE); + + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_3)) + go->SetGoState(GO_STATE_ACTIVE); + + if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_4)) + go->SetGoState(GO_STATE_ACTIVE); + + break; + } + events.Repeat(1s); + break; + case EVENT_P3_LICH_KING_SAY: + { + if (Creature* cr = instance->GetCreature(DATA_LICH_KING_BOSS)) + cr->AI()->Talk(SAY_ANSWER_REQUEST); + + for (uint8 i = 0 ; i < RAID_MODE(2, 4); ++i) + events.ScheduleEvent(EVENT_SUMMON_GUARDIAN_OF_ICECROWN, Milliseconds(10000 + (i * 5000))); + + break; + } + case EVENT_SUMMON_GUARDIAN_OF_ICECROWN: + if (Creature* cr = me->SummonCreature(NPC_GUARDIAN_OF_ICECROWN, SpawnPool[RAND(0, 1, 3, 4)])) + { + cr->AI()->Talk(EMOTE_GUARDIAN_APPEAR); + cr->AI()->AttackStart(me->GetVictim()); + } + break; + } + if (!me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) + DoMeleeAttackIfReady(); + } + }; +}; + +class boss_kelthuzad_minion : public CreatureScript +{ +public: + boss_kelthuzad_minion() : CreatureScript("boss_kelthuzad_minion") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_kelthuzad_minionAI : public ScriptedAI + { + explicit boss_kelthuzad_minionAI(Creature* c) : ScriptedAI(c) { } + + EventMap events; + bool callHelp{}; + + void Reset() override + { + me->SetNoCallAssistance(true); + callHelp = true; + events.Reset(); + } + + void DoAction(int32 param) override + { + if (param == ACTION_CALL_HELP_ON) + { + callHelp = true; + } + else if (param == ACTION_CALL_HELP_OFF) + { + callHelp = false; + } + else if (param == ACTION_SECOND_PHASE) + { + if (!me->IsInCombat()) + { + me->DespawnOrUnsummon(500ms); + } + } + if (param == ACTION_GUARDIANS_OFF) + { + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAllAuras(); + EnterEvadeMode(); + me->SetPosition(me->GetHomePosition()); + } + } + + void MoveInLineOfSight(Unit* who) override + { + if (!who->IsPlayer() && !who->IsPet()) + return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void JustDied(Unit* /*killer*/) override + { + if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION) + me->GetInstanceScript()->SetData(DATA_ABOMINATION_KILLED, 0); + } + + void AttackStart(Unit* who) override + { + ScriptedAI::AttackStart(who); + if (callHelp) + { + std::list targets; + me->GetCreaturesWithEntryInRange(targets, 15.0f, me->GetEntry()); + for (std::list::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) + { + if ((*itr)->GetGUID() != me->GetGUID()) + { + (*itr)->ToCreature()->AI()->DoAction(ACTION_CALL_HELP_OFF); + (*itr)->ToCreature()->AI()->AttackStart(who); + } + } + } + + if (me->GetEntry() != NPC_UNSTOPPABLE_ABOMINATION && me->GetEntry() != NPC_GUARDIAN_OF_ICECROWN) + { + me->AddThreat(who, 1000000.0f); + } + } + + void JustEngagedWith(Unit* /*who*/) override + { + me->SetInCombatWithZone(); + if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION) + { + events.ScheduleEvent(EVENT_MINION_FRENZY, 1s); + events.ScheduleEvent(EVENT_MINION_MORTAL_WOUND, 5s); + } + else if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN) + { + events.ScheduleEvent(EVENT_MINION_BLOOD_TAP, 15s); + } + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void JustReachedHome() override + { + if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN) + { + me->DespawnOrUnsummon(); + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_MINION_MORTAL_WOUND: + me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false); + events.Repeat(15s); + break; + case EVENT_MINION_FRENZY: + if (me->HealthBelowPct(35)) + { + me->CastSpell(me, SPELL_FRENZY, true); + break; + } + events.Repeat(1s); + break; + case EVENT_MINION_BLOOD_TAP: + me->CastSpell(me->GetVictim(), SPELL_BLOOD_TAP, false); + events.Repeat(15s); + break; + } + DoMeleeAttackIfReady(); + } + }; +}; + +class spell_kelthuzad_frost_blast : public SpellScript +{ + PrepareSpellScript(spell_kelthuzad_frost_blast); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_FROST_BLAST }); + } + + void FilterTargets(std::list& targets) + { + Unit* caster = GetCaster(); + if (!caster || !caster->ToCreature()) + return; + + std::list tmplist; + for (auto& target : targets) + { + if (!target->ToUnit()->HasAura(SPELL_FROST_BLAST)) + { + tmplist.push_back(target); + } + } + targets.clear(); + for (auto& itr : tmplist) + { + targets.push_back(itr); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_kelthuzad_frost_blast::FilterTargets, EFFECT_ALL, TARGET_UNIT_DEST_AREA_ENEMY); + } +}; + +class spell_kelthuzad_detonate_mana_aura : public AuraScript +{ + PrepareAuraScript(spell_kelthuzad_detonate_mana_aura); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_MANA_DETONATION_DAMAGE }); + } + + void HandleScript(AuraEffect const* aurEff) + { + PreventDefaultAction(); + Unit* target = GetTarget(); + if (auto mana = int32(target->GetMaxPower(POWER_MANA) / 10)) + { + mana = target->ModifyPower(POWER_MANA, -mana); + target->CastCustomSpell(SPELL_MANA_DETONATION_DAMAGE, SPELLVALUE_BASE_POINT0, -mana * 10, target, true, nullptr, aurEff); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_kelthuzad_detonate_mana_aura::HandleScript, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } +}; void AddSC_boss_kelthuzad() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.h b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.h deleted file mode 100644 index c3f9ef8b5..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.h +++ /dev/null @@ -1,670 +0,0 @@ -#ifndef BOSS_KELTHUZAD_H_ -#define BOSS_KELTHUZAD_H_ - -#include "Player.h" -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellScript.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Kelthuzad { - -enum Yells -{ - SAY_ANSWER_REQUEST = 3, - SAY_TAUNT = 6, - SAY_AGGRO = 7, - SAY_SLAY = 8, - SAY_DEATH = 9, - SAY_CHAIN = 10, - SAY_FROST_BLAST = 11, - SAY_REQUEST_AID = 12, - EMOTE_PHASE_TWO = 13, - SAY_SUMMON_MINIONS = 14, - SAY_SPECIAL = 15, - - EMOTE_GUARDIAN_FLEE = 0, - EMOTE_GUARDIAN_APPEAR = 1 -}; - -enum Spells -{ - // Kel'Thzuad - SPELL_FROST_BOLT_SINGLE = 28478, - SPELL_FROST_BOLT_MULTI = 28479, - SPELL_SHADOW_FISURE = 27810, - SPELL_VOID_BLAST = 27812, - SPELL_DETONATE_MANA = 27819, - SPELL_MANA_DETONATION_DAMAGE = 27820, - SPELL_FROST_BLAST = 27808, - SPELL_CHAINS_OF_KELTHUZAD = 28410, // 28408 script effect - SPELL_BERSERK = 28498, - SPELL_KELTHUZAD_CHANNEL = 29423, - - // Minions - SPELL_FRENZY = 28468, - SPELL_MORTAL_WOUND = 28467, - SPELL_BLOOD_TAP = 28470 -}; - -enum Misc -{ - NPC_SOLDIER_OF_THE_FROZEN_WASTES = 16427, - NPC_UNSTOPPABLE_ABOMINATION = 16428, - NPC_SOUL_WEAVER = 16429, - NPC_GUARDIAN_OF_ICECROWN = 16441, - - ACTION_CALL_HELP_ON = 1, - ACTION_CALL_HELP_OFF = 2, - ACTION_SECOND_PHASE = 3, - ACTION_GUARDIANS_OFF = 4 -}; - -enum Event -{ - // Kel'Thuzad - EVENT_SUMMON_SOLDIER = 1, - EVENT_SUMMON_UNSTOPPABLE_ABOMINATION = 2, - EVENT_SUMMON_SOUL_WEAVER = 3, - EVENT_PHASE_2 = 4, - EVENT_FROST_BOLT_SINGLE = 5, - EVENT_FROST_BOLT_MULTI = 6, - EVENT_DETONATE_MANA = 7, - EVENT_PHASE_3 = 8, - EVENT_P3_LICH_KING_SAY = 9, - EVENT_SHADOW_FISSURE = 10, - EVENT_FROST_BLAST = 11, - EVENT_CHAINS = 12, - EVENT_SUMMON_GUARDIAN_OF_ICECROWN = 13, - EVENT_FLOOR_CHANGE = 14, - EVENT_ENRAGE = 15, - EVENT_SPAWN_POOL = 16, - - // Minions - EVENT_MINION_FRENZY = 17, - EVENT_MINION_MORTAL_WOUND = 18, - EVENT_MINION_BLOOD_TAP = 19 -}; - -const Position SummonGroups[12] = -{ - // Portals - {3783.272705f, -5062.697266f, 143.711203f, 3.617599f}, // LEFT_FAR - {3730.291260f, -5027.239258f, 143.956909f, 4.461900f}, // LEFT_MIDDLE - {3683.868652f, -5057.281250f, 143.183884f, 5.237086f}, // LEFT_NEAR - {3759.355225f, -5174.128418f, 143.802383f, 2.170104f}, // RIGHT_FAR - {3700.724365f, -5185.123047f, 143.928024f, 1.309310f}, // RIGHT_MIDDLE - {3665.121094f, -5138.679199f, 143.183212f, 0.604023f}, // RIGHT_NEAR - - // Middle - {3769.34f, -5071.80f, 143.2082f, 3.658f}, - {3729.78f, -5043.56f, 143.3867f, 4.475f}, - {3682.75f, -5055.26f, 143.1848f, 5.295f}, - {3752.58f, -5161.82f, 143.2944f, 2.126f}, - {3702.83f, -5171.70f, 143.4356f, 1.305f}, - {3665.30f, -5141.55f, 143.1846f, 0.566f} -}; - -const Position SpawnPool[7] = -{ - // Portals - {3783.272705f, -5062.697266f, 143.711203f, 3.617599f}, // LEFT_FAR - {3730.291260f, -5027.239258f, 143.956909f, 4.461900f}, // LEFT_MIDDLE - {3683.868652f, -5057.281250f, 143.183884f, 5.237086f}, // LEFT_NEAR - {3759.355225f, -5174.128418f, 143.802383f, 2.170104f}, // RIGHT_FAR - {3700.724365f, -5185.123047f, 143.928024f, 1.309310f}, // RIGHT_MIDDLE - {3665.121094f, -5138.679199f, 143.183212f, 0.604023f}, // RIGHT_NEAR - {3651.729980f, -5092.620117f, 143.380005f, 6.050000f} // GATE -}; - -class boss_kelthuzad : public CreatureScript -{ -public: - boss_kelthuzad() : CreatureScript("boss_kelthuzad") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_kelthuzadAI : public BossAI - { - explicit boss_kelthuzadAI(Creature* c) : BossAI(c, BOSS_KELTHUZAD), summons(me) - {} - - EventMap events; - SummonList summons; - - float NormalizeOrientation(float o) - { - return std::fmod(o, 2.0f * static_cast(M_PI)); // Only positive values will be passed - } - - void SpawnHelpers() - { - // spawn at gate - me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3656.19f, -5093.78f, 143.33f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo center - me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3657.94f, -5087.68f, 143.60f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo left - me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, 3655.48f, -5100.05f, 143.53f, 6.08, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);// abo right - me->SummonCreature(NPC_SOUL_WEAVER, 3651.73f, -5092.62f, 143.38f, 6.05, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // soul behind - me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3660.17f, -5092.45f, 143.37f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front left - me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.39f, -5096.21f, 143.29f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske front right - me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3659.29f, -5090.19f, 143.48f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske left left - me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3657.43f, -5098.03f, 143.41f, 6.07, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right - me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3654.36f, -5090.51f, 143.48f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske behind left - me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 3653.35f, -5095.91f, 143.41f, 6.09, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000); // ske right right - - // 6 rooms, 8 soldiers, 3 abominations and 1 weaver in each room | middle positions in table starts from 6 - for (uint8 i = 6; i < 12; ++i) - { - for (uint8 j = 0; j < 8; ++j) - { - float angle = M_PI * 2 / 8 * j; - me->SummonCreature(NPC_SOLDIER_OF_THE_FROZEN_WASTES, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); - } - } - for (uint8 i = 6; i < 12; ++i) - { - for (uint8 j = 1; j < 4; ++j) - { - float dist = j == 2 ? 0.0f : 8.0f; // second in middle - float angle = SummonGroups[i].GetOrientation() + M_PI * 2 / 4 * j; - me->SummonCreature(NPC_UNSTOPPABLE_ABOMINATION, SummonGroups[i].GetPositionX() + dist * cos(angle), SummonGroups[i].GetPositionY() + dist * std::sin(angle), SummonGroups[i].GetPositionZ(), SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); - } - } - for (uint8 i = 6; i < 12; ++i) - { - for (uint8 j = 0; j < 1; ++j) - { - float angle = SummonGroups[i].GetOrientation() + M_PI; - me->SummonCreature(NPC_SOUL_WEAVER, SummonGroups[i].GetPositionX() + 6 * cos(angle), SummonGroups[i].GetPositionY() + 6 * std::sin(angle), SummonGroups[i].GetPositionZ() + 0.5f, SummonGroups[i].GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); - } - } - } - - void SummonHelper(uint32 entry, uint32 count) - { - for (uint8 i = 0; i < count; ++i) - { - if (Creature* cr = me->SummonCreature(entry, SpawnPool[urand(0, 6)], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000)) - { - if (Unit* target = SelectTargetFromPlayerList(100.0f)) - { - cr->AI()->DoAction(ACTION_CALL_HELP_OFF); - cr->AI()->AttackStart(target); - } - } - } - } - - void Reset() override - { - BossAI::Reset(); - events.Reset(); - summons.DespawnAll(); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); - me->SetReactState(REACT_AGGRESSIVE); - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_FLOOR)) - { - go->SetPhaseMask(1, true); - go->SetGoState(GO_STATE_READY); - } - - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_1)) - go->SetGoState(GO_STATE_READY); - - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_2)) - go->SetGoState(GO_STATE_READY); - - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_3)) - go->SetGoState(GO_STATE_READY); - - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_4)) - go->SetGoState(GO_STATE_READY); - } - - void EnterEvadeMode(EvadeReason why) override - { - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); - ScriptedAI::EnterEvadeMode(why); - } - - void KilledUnit(Unit* who) override - { - if (!who->IsPlayer()) - return; - - Talk(SAY_SLAY); - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - summons.DoAction(ACTION_GUARDIANS_OFF); - if (Creature* guardian = summons.GetCreatureWithEntry(NPC_GUARDIAN_OF_ICECROWN)) - { - guardian->AI()->Talk(EMOTE_GUARDIAN_FLEE); - } - Talk(SAY_DEATH); - } - - void MoveInLineOfSight(Unit* who) override - { - if (!me->IsInCombat() && who->IsPlayer() && who->IsAlive() && me->GetDistance(who) <= 50.0f) - AttackStart(who); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - Talk(SAY_SUMMON_MINIONS); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); - me->RemoveAllAttackers(); - me->SetTarget(); - me->SetReactState(REACT_PASSIVE); - me->CastSpell(me, SPELL_KELTHUZAD_CHANNEL, false); - events.ScheduleEvent(EVENT_SPAWN_POOL, 5s); - events.ScheduleEvent(EVENT_SUMMON_SOLDIER, 6400ms); - events.ScheduleEvent(EVENT_SUMMON_UNSTOPPABLE_ABOMINATION, 10s); - events.ScheduleEvent(EVENT_SUMMON_SOUL_WEAVER, 12s); - events.ScheduleEvent(EVENT_PHASE_2, 228s); - events.ScheduleEvent(EVENT_ENRAGE, 15min); - - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_FLOOR)) - { - events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15s); - go->SetGoState(GO_STATE_ACTIVE); - } - } - - void JustSummoned(Creature* cr) override - { - summons.Summon(cr); - if (!cr->IsInCombat()) - { - cr->GetMotionMaster()->MoveRandom(5); - } - if (cr->GetEntry() == NPC_GUARDIAN_OF_ICECROWN) - { - cr->SetHomePosition(cr->GetPositionX(), cr->GetPositionY(), cr->GetPositionZ(), cr->GetOrientation()); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - if (!me->HasAura(SPELL_KELTHUZAD_CHANNEL)) - { - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - switch (events.ExecuteEvent()) - { - case EVENT_FLOOR_CHANGE: - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_FLOOR)) - { - events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15s); - go->SetGoState(GO_STATE_READY); - go->SetPhaseMask(2, true); - } - break; - case EVENT_SPAWN_POOL: - SpawnHelpers(); - break; - case EVENT_SUMMON_SOLDIER: - SummonHelper(NPC_SOLDIER_OF_THE_FROZEN_WASTES, 1); - events.Repeat(3100ms); - break; - case EVENT_SUMMON_UNSTOPPABLE_ABOMINATION: - SummonHelper(NPC_UNSTOPPABLE_ABOMINATION, 1); - events.Repeat(18s + 500ms); - break; - case EVENT_SUMMON_SOUL_WEAVER: - SummonHelper(NPC_SOUL_WEAVER, 1); - events.Repeat(30s); - break; - case EVENT_PHASE_2: - Talk(EMOTE_PHASE_TWO); - Talk(SAY_AGGRO); - events.Reset(); - summons.DoAction(ACTION_SECOND_PHASE); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - me->RemoveAura(SPELL_KELTHUZAD_CHANNEL); - me->SetReactState(REACT_AGGRESSIVE); - events.ScheduleEvent(EVENT_FROST_BOLT_SINGLE, 2s, 10s); - events.ScheduleEvent(EVENT_FROST_BOLT_MULTI, 15s, 30s); - events.ScheduleEvent(EVENT_DETONATE_MANA, 30s); - events.ScheduleEvent(EVENT_PHASE_3, 1s); - events.ScheduleEvent(EVENT_SHADOW_FISSURE, 25s); - events.ScheduleEvent(EVENT_FROST_BLAST, 45s); - if (Is25ManRaid()) - { - events.ScheduleEvent(EVENT_CHAINS, 90s); - } - break; - case EVENT_ENRAGE: - me->CastSpell(me, SPELL_BERSERK, true); - break; - case EVENT_FROST_BOLT_SINGLE: - me->CastSpell(me->GetVictim(), SPELL_FROST_BOLT_SINGLE, false); - events.Repeat(2s, 10s); - break; - case EVENT_FROST_BOLT_MULTI: - me->CastSpell(me, SPELL_FROST_BOLT_MULTI, false); - events.Repeat(15s, 30s); - break; - case EVENT_SHADOW_FISSURE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) - { - me->CastSpell(target, SPELL_SHADOW_FISURE, false); - } - events.Repeat(25s); - break; - case EVENT_FROST_BLAST: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0, true, RAID_MODE(false, true))) - { - me->CastSpell(target, SPELL_FROST_BLAST, false); - } - Talk(SAY_FROST_BLAST); - events.Repeat(45s); - break; - case EVENT_CHAINS: - for (uint8 i = 0; i < 3; ++i) - { - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 200, true, false, -SPELL_CHAINS_OF_KELTHUZAD)) - { - me->CastSpell(target, SPELL_CHAINS_OF_KELTHUZAD, true); - } - } - Talk(SAY_CHAIN); - events.Repeat(90s); - break; - case EVENT_DETONATE_MANA: - { - std::vector unitList; - ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList(); - for (auto itr : threatList) - { - if (itr->getTarget()->IsPlayer() - && itr->getTarget()->getPowerType() == POWER_MANA - && itr->getTarget()->GetPower(POWER_MANA)) - { - unitList.push_back(itr->getTarget()); - } - } - if (!unitList.empty()) - { - auto itr = unitList.begin(); - advance(itr, urand(0, unitList.size() - 1)); - me->CastSpell(*itr, SPELL_DETONATE_MANA, false); - Talk(SAY_SPECIAL); - } - events.Repeat(30s); - break; - } - case EVENT_PHASE_3: - if (me->HealthBelowPct(45)) - { - Talk(SAY_REQUEST_AID); - events.DelayEvents(5500ms); - events.ScheduleEvent(EVENT_P3_LICH_KING_SAY, 5s); - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_1)) - go->SetGoState(GO_STATE_ACTIVE); - - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_2)) - go->SetGoState(GO_STATE_ACTIVE); - - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_3)) - go->SetGoState(GO_STATE_ACTIVE); - - if (GameObject* go = instance->GetGameObject(DATA_KELTHUZAD_PORTAL_4)) - go->SetGoState(GO_STATE_ACTIVE); - - break; - } - events.Repeat(1s); - break; - case EVENT_P3_LICH_KING_SAY: - { - if (Creature* cr = instance->GetCreature(DATA_LICH_KING_BOSS)) - cr->AI()->Talk(SAY_ANSWER_REQUEST); - - for (uint8 i = 0 ; i < RAID_MODE(2, 4); ++i) - events.ScheduleEvent(EVENT_SUMMON_GUARDIAN_OF_ICECROWN, Milliseconds(10000 + (i * 5000))); - - break; - } - case EVENT_SUMMON_GUARDIAN_OF_ICECROWN: - if (Creature* cr = me->SummonCreature(NPC_GUARDIAN_OF_ICECROWN, SpawnPool[RAND(0, 1, 3, 4)])) - { - cr->AI()->Talk(EMOTE_GUARDIAN_APPEAR); - cr->AI()->AttackStart(me->GetVictim()); - } - break; - } - if (!me->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) - DoMeleeAttackIfReady(); - } - }; -}; - -class boss_kelthuzad_minion : public CreatureScript -{ -public: - boss_kelthuzad_minion() : CreatureScript("boss_kelthuzad_minion") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_kelthuzad_minionAI : public ScriptedAI - { - explicit boss_kelthuzad_minionAI(Creature* c) : ScriptedAI(c) { } - - EventMap events; - bool callHelp{}; - - void Reset() override - { - me->SetNoCallAssistance(true); - callHelp = true; - events.Reset(); - } - - void DoAction(int32 param) override - { - if (param == ACTION_CALL_HELP_ON) - { - callHelp = true; - } - else if (param == ACTION_CALL_HELP_OFF) - { - callHelp = false; - } - else if (param == ACTION_SECOND_PHASE) - { - if (!me->IsInCombat()) - { - me->DespawnOrUnsummon(500ms); - } - } - if (param == ACTION_GUARDIANS_OFF) - { - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->RemoveAllAuras(); - EnterEvadeMode(); - me->SetPosition(me->GetHomePosition()); - } - } - - void MoveInLineOfSight(Unit* who) override - { - if (!who->IsPlayer() && !who->IsPet()) - return; - - ScriptedAI::MoveInLineOfSight(who); - } - - void JustDied(Unit* /*killer*/) override - { - if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION) - me->GetInstanceScript()->SetData(DATA_ABOMINATION_KILLED, 0); - } - - void AttackStart(Unit* who) override - { - ScriptedAI::AttackStart(who); - if (callHelp) - { - std::list targets; - me->GetCreaturesWithEntryInRange(targets, 15.0f, me->GetEntry()); - for (std::list::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) - { - if ((*itr)->GetGUID() != me->GetGUID()) - { - (*itr)->ToCreature()->AI()->DoAction(ACTION_CALL_HELP_OFF); - (*itr)->ToCreature()->AI()->AttackStart(who); - } - } - } - - if (me->GetEntry() != NPC_UNSTOPPABLE_ABOMINATION && me->GetEntry() != NPC_GUARDIAN_OF_ICECROWN) - { - me->AddThreat(who, 1000000.0f); - } - } - - void JustEngagedWith(Unit* /*who*/) override - { - me->SetInCombatWithZone(); - if (me->GetEntry() == NPC_UNSTOPPABLE_ABOMINATION) - { - events.ScheduleEvent(EVENT_MINION_FRENZY, 1s); - events.ScheduleEvent(EVENT_MINION_MORTAL_WOUND, 5s); - } - else if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN) - { - events.ScheduleEvent(EVENT_MINION_BLOOD_TAP, 15s); - } - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustReachedHome() override - { - if (me->GetEntry() == NPC_GUARDIAN_OF_ICECROWN) - { - me->DespawnOrUnsummon(); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_MINION_MORTAL_WOUND: - me->CastSpell(me->GetVictim(), SPELL_MORTAL_WOUND, false); - events.Repeat(15s); - break; - case EVENT_MINION_FRENZY: - if (me->HealthBelowPct(35)) - { - me->CastSpell(me, SPELL_FRENZY, true); - break; - } - events.Repeat(1s); - break; - case EVENT_MINION_BLOOD_TAP: - me->CastSpell(me->GetVictim(), SPELL_BLOOD_TAP, false); - events.Repeat(15s); - break; - } - DoMeleeAttackIfReady(); - } - }; -}; - -class spell_kelthuzad_frost_blast : public SpellScript -{ - PrepareSpellScript(spell_kelthuzad_frost_blast); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_FROST_BLAST }); - } - - void FilterTargets(std::list& targets) - { - Unit* caster = GetCaster(); - if (!caster || !caster->ToCreature()) - return; - - std::list tmplist; - for (auto& target : targets) - { - if (!target->ToUnit()->HasAura(SPELL_FROST_BLAST)) - { - tmplist.push_back(target); - } - } - targets.clear(); - for (auto& itr : tmplist) - { - targets.push_back(itr); - } - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_kelthuzad_frost_blast::FilterTargets, EFFECT_ALL, TARGET_UNIT_DEST_AREA_ENEMY); - } -}; - -class spell_kelthuzad_detonate_mana_aura : public AuraScript -{ - PrepareAuraScript(spell_kelthuzad_detonate_mana_aura); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_MANA_DETONATION_DAMAGE }); - } - - void HandleScript(AuraEffect const* aurEff) - { - PreventDefaultAction(); - Unit* target = GetTarget(); - if (auto mana = int32(target->GetMaxPower(POWER_MANA) / 10)) - { - mana = target->ModifyPower(POWER_MANA, -mana); - target->CastCustomSpell(SPELL_MANA_DETONATION_DAMAGE, SPELLVALUE_BASE_POINT0, -mana * 10, target, true, nullptr, aurEff); - } - } - - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_kelthuzad_detonate_mana_aura::HandleScript, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } -}; - -} -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp index 5dc78fc08..0a91706ea 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp @@ -15,16 +15,158 @@ * with this program. If not, see . */ -#include "boss_loatheb.h" #include "CreatureScript.h" #include "ScriptedCreature.h" #include "naxxramas.h" +enum Spells +{ + SPELL_NECROTIC_AURA = 55593, + SPELL_SUMMON_SPORE = 29234, + SPELL_DEATHBLOOM = 29865, + SPELL_INEVITABLE_DOOM = 29204, + SPELL_BERSERK = 26662 +}; -using namespace Loatheb; +enum Events +{ + EVENT_NECROTIC_AURA = 1, + EVENT_DEATHBLOOM = 2, + EVENT_INEVITABLE_DOOM = 3, + EVENT_BERSERK = 4, + EVENT_SUMMON_SPORE = 5, + EVENT_NECROTIC_AURA_FADING = 6, + EVENT_NECROTIC_AURA_REMOVED = 7 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Texts +{ + SAY_NECROTIC_AURA_APPLIED = 0, + SAY_NECROTIC_AURA_REMOVED = 1, + SAY_NECROTIC_AURA_FADING = 2 +}; + +class boss_loatheb : public CreatureScript +{ +public: + boss_loatheb() : CreatureScript("boss_loatheb") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_loathebAI : public BossAI + { + explicit boss_loathebAI(Creature* c) : BossAI(c, BOSS_LOATHEB), summons(me) + { + me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation()); + } + + uint8 doomCounter; + EventMap events; + SummonList summons; + + void Reset() override + { + BossAI::Reset(); + events.Reset(); + summons.DespawnAll(); + doomCounter = 0; + } + + void JustSummoned(Creature* cr) override + { + cr->SetInCombatWithZone(); + summons.Summon(cr); + } + + void SummonedCreatureDies(Creature* /*cr*/, Unit*) override + { + instance->SetData(DATA_SPORE_KILLED, 0); + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + me->SetInCombatWithZone(); + events.ScheduleEvent(EVENT_NECROTIC_AURA, 10s); + events.ScheduleEvent(EVENT_DEATHBLOOM, 5s); + events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 2min); + events.ScheduleEvent(EVENT_SUMMON_SPORE, 15s); + events.ScheduleEvent(EVENT_BERSERK, 12min); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + summons.DespawnAll(); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() || !IsInRoom()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_SUMMON_SPORE: + me->CastSpell(me, SPELL_SUMMON_SPORE, true); + events.Repeat(35s); + break; + case EVENT_NECROTIC_AURA: + me->CastSpell(me, SPELL_NECROTIC_AURA, true); + Talk(SAY_NECROTIC_AURA_APPLIED); + events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, 14s); + events.ScheduleEvent(EVENT_NECROTIC_AURA_REMOVED, 17s); + events.Repeat(20s); + break; + case EVENT_DEATHBLOOM: + me->CastSpell(me, SPELL_DEATHBLOOM, false); + events.Repeat(30s); + break; + case EVENT_INEVITABLE_DOOM: + me->CastSpell(me, SPELL_INEVITABLE_DOOM, false); + doomCounter++; + events.Repeat(doomCounter < 6 ? 30s : 15s); + break; + case EVENT_BERSERK: + me->CastSpell(me, SPELL_BERSERK, true); + break; + case EVENT_NECROTIC_AURA_FADING: + Talk(SAY_NECROTIC_AURA_FADING); + break; + case EVENT_NECROTIC_AURA_REMOVED: + Talk(SAY_NECROTIC_AURA_REMOVED); + break; + } + DoMeleeAttackIfReady(); + } + + bool IsInRoom() + { + // Calculate the distance between his home position to the gate + if (me->GetExactDist(me->GetHomePosition().GetPositionX(), + me->GetHomePosition().GetPositionY(), + me->GetHomePosition().GetPositionZ()) > 50.0f) + { + EnterEvadeMode(); + return false; + } + return true; + } + }; +}; void AddSC_boss_loatheb() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.h b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.h deleted file mode 100644 index a2ab34642..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.h +++ /dev/null @@ -1,162 +0,0 @@ -#ifndef BOSS_LOATHEB_H_ -#define BOSS_LOATHEB_H_ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Loatheb { - -enum Spells -{ - SPELL_NECROTIC_AURA = 55593, - SPELL_SUMMON_SPORE = 29234, - SPELL_DEATHBLOOM = 29865, - SPELL_INEVITABLE_DOOM = 29204, - SPELL_BERSERK = 26662 -}; - -enum Events -{ - EVENT_NECROTIC_AURA = 1, - EVENT_DEATHBLOOM = 2, - EVENT_INEVITABLE_DOOM = 3, - EVENT_BERSERK = 4, - EVENT_SUMMON_SPORE = 5, - EVENT_NECROTIC_AURA_FADING = 6, - EVENT_NECROTIC_AURA_REMOVED = 7 -}; - -enum Texts -{ - SAY_NECROTIC_AURA_APPLIED = 0, - SAY_NECROTIC_AURA_REMOVED = 1, - SAY_NECROTIC_AURA_FADING = 2 -}; - -class boss_loatheb : public CreatureScript -{ -public: - boss_loatheb() : CreatureScript("boss_loatheb") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_loathebAI : public BossAI - { - explicit boss_loathebAI(Creature* c) : BossAI(c, BOSS_LOATHEB), summons(me) - { - me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation()); - } - - uint8 doomCounter; - EventMap events; - SummonList summons; - - void Reset() override - { - BossAI::Reset(); - events.Reset(); - summons.DespawnAll(); - doomCounter = 0; - } - - void JustSummoned(Creature* cr) override - { - cr->SetInCombatWithZone(); - summons.Summon(cr); - } - - void SummonedCreatureDies(Creature* /*cr*/, Unit*) override - { - instance->SetData(DATA_SPORE_KILLED, 0); - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - me->SetInCombatWithZone(); - events.ScheduleEvent(EVENT_NECROTIC_AURA, 10s); - events.ScheduleEvent(EVENT_DEATHBLOOM, 5s); - events.ScheduleEvent(EVENT_INEVITABLE_DOOM, 2min); - events.ScheduleEvent(EVENT_SUMMON_SPORE, 15s); - events.ScheduleEvent(EVENT_BERSERK, 12min); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - summons.DespawnAll(); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() || !IsInRoom()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SUMMON_SPORE: - me->CastSpell(me, SPELL_SUMMON_SPORE, true); - events.Repeat(35s); - break; - case EVENT_NECROTIC_AURA: - me->CastSpell(me, SPELL_NECROTIC_AURA, true); - Talk(SAY_NECROTIC_AURA_APPLIED); - events.ScheduleEvent(EVENT_NECROTIC_AURA_FADING, 14s); - events.ScheduleEvent(EVENT_NECROTIC_AURA_REMOVED, 17s); - events.Repeat(20s); - break; - case EVENT_DEATHBLOOM: - me->CastSpell(me, SPELL_DEATHBLOOM, false); - events.Repeat(30s); - break; - case EVENT_INEVITABLE_DOOM: - me->CastSpell(me, SPELL_INEVITABLE_DOOM, false); - doomCounter++; - events.Repeat(doomCounter < 6 ? 30s : 15s); - break; - case EVENT_BERSERK: - me->CastSpell(me, SPELL_BERSERK, true); - break; - case EVENT_NECROTIC_AURA_FADING: - Talk(SAY_NECROTIC_AURA_FADING); - break; - case EVENT_NECROTIC_AURA_REMOVED: - Talk(SAY_NECROTIC_AURA_REMOVED); - break; - } - DoMeleeAttackIfReady(); - } - - bool IsInRoom() - { - // Calculate the distance between his home position to the gate - if (me->GetExactDist(me->GetHomePosition().GetPositionX(), - me->GetHomePosition().GetPositionY(), - me->GetHomePosition().GetPositionZ()) > 50.0f) - { - EnterEvadeMode(); - return false; - } - return true; - } - }; -}; - -} - -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp index bf72579cd..ce6c18f11 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "boss_maexxna.h" #include "CreatureScript.h" #include "PassiveAI.h" #include "Player.h" @@ -25,11 +24,336 @@ #include "SpellScriptLoader.h" #include "naxxramas.h" +enum Spells +{ + SPELL_WEB_SPRAY = 29484, + SPELL_POISON_SHOCK = 28741, + SPELL_NECROTIC_POISON = 54121, + SPELL_FRENZY = 54123, + SPELL_WEB_WRAP_STUN = 28622, + SPELL_WEB_WRAP_SUMMON = 28627, + SPELL_WEB_WRAP_KILL_WEBS = 52512, + SPELL_WEB_WRAP_PACIFY_5 = 28618 // 5 seconds pacify silence +}; -using namespace Maexxna; +enum Events +{ + EVENT_WEB_SPRAY = 1, + EVENT_POISON_SHOCK = 2, + EVENT_NECROTIC_POISON = 3, + EVENT_WEB_WRAP = 4, + EVENT_HEALTH_CHECK = 5, + EVENT_SUMMON_SPIDERLINGS = 6, + EVENT_WEB_WRAP_APPLY_STUN = 7 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Emotes +{ + EMOTE_SPIDERS = 0, + EMOTE_WEB_WRAP = 1, + EMOTE_WEB_SPRAY = 2 +}; + +enum Misc +{ + NPC_WEB_WRAP = 16486, + NPC_MAEXXNA_SPIDERLING = 17055 +}; + +const Position PosWrap[7] = +{ + {3496.615f, -3834.182f, 320.7863f}, + {3509.108f, -3833.922f, 320.4750f}, + {3523.644f, -3838.309f, 320.5775f}, + {3538.152f, -3846.353f, 320.5188f}, + {3546.219f, -3856.167f, 320.9324f}, + {3555.135f, -3869.507f, 320.8307f}, + {3560.282f, -3886.143f, 321.2827f} +}; + +struct WebTargetSelector +{ + WebTargetSelector(Unit* maexxna) : _maexxna(maexxna) {} + bool operator()(Unit const* target) const + { + if (!target->IsPlayer()) // never web nonplayers (pets, guardians, etc.) + return false; + if (_maexxna->GetVictim() == target) // never target tank + return false; + if (target->HasAura(SPELL_WEB_WRAP_STUN)) // never target targets that are already webbed + return false; + return true; + } + + private: + Unit const* _maexxna; +}; + +class boss_maexxna : public CreatureScript +{ +public: + boss_maexxna() : CreatureScript("boss_maexxna") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_maexxnaAI : public BossAI + { + explicit boss_maexxnaAI(Creature* c) : BossAI(c, BOSS_MAEXXNA), summons(me) + {} + + EventMap events; + SummonList summons; + + GuidList wraps; + + bool IsInRoom() + { + if (me->GetExactDist(3486.6f, -3890.6f, 291.8f) > 100.0f) + { + EnterEvadeMode(); + return false; + } + return true; + } + + void Reset() override + { + BossAI::Reset(); + events.Reset(); + summons.DespawnAll(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + me->SetInCombatWithZone(); + events.ScheduleEvent(EVENT_WEB_WRAP, 20s); + events.ScheduleEvent(EVENT_WEB_SPRAY, 40s); + events.ScheduleEvent(EVENT_POISON_SHOCK, 10s); + events.ScheduleEvent(EVENT_NECROTIC_POISON, 5s); + events.ScheduleEvent(EVENT_HEALTH_CHECK, 1s); + events.ScheduleEvent(EVENT_SUMMON_SPIDERLINGS, 30s); + } + + void JustSummoned(Creature* cr) override + { + if (cr->GetEntry() == NPC_MAEXXNA_SPIDERLING) + { + cr->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + cr->AI()->AttackStart(target); + } + } + summons.Summon(cr); + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + } + + void DoCastWebWrap() + { + std::list candidates; + SelectTargetList(candidates, RAID_MODE(1, 2), SelectTargetMethod::Random, 0, WebTargetSelector(me)); + + std::vector positions {0, 1, 2, 3, 4, 5, 6}; + Acore::Containers::RandomShuffle(positions); + + if (candidates.empty()) + return; + + for (int i = 0; i < RAID_MODE(1, 2) ; i++) + { + if (candidates.empty()) + break; + const Position &randomPos = PosWrap[positions[i]]; + + auto itr = candidates.begin(); + + if (candidates.size() > 1) + std::advance(itr, urand(0, candidates.size() - 1)); + + Unit *target = *itr; + candidates.erase(itr); + + float dx = randomPos.GetPositionX() - target->GetPositionX(); + float dy = randomPos.GetPositionY() - target->GetPositionY(); + float distXY = std::hypotf(dx, dy); + + // smooth knockback arc that avoids the ceiling + float horizontalSpeed = distXY / 1.5f; + float verticalSpeed = 28.0f; + if (distXY <= 10.0f) + verticalSpeed = 12.0f; + else if (distXY <= 20.0f) + verticalSpeed = 16.0f; + else if (distXY <= 30.0f) + verticalSpeed = 20.0f; + else if (distXY <= 40.0f) + verticalSpeed = 24.0f; + + target->KnockbackFrom(randomPos.GetPositionX(), randomPos.GetPositionY(), -horizontalSpeed, verticalSpeed); + me->CastSpell(target, SPELL_WEB_WRAP_PACIFY_5, true); // pacify silence for 5 seconds + + wraps.push_back(target->GetGUID()); + } + events.ScheduleEvent(EVENT_WEB_WRAP_APPLY_STUN, 2s); + } + + void UpdateAI(uint32 diff) override + { + if (!IsInRoom()) + return; + + if (!UpdateVictim()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_WEB_SPRAY: + Talk(EMOTE_WEB_SPRAY); + me->CastSpell(me, SPELL_WEB_SPRAY, true); + events.Repeat(40s); + break; + case EVENT_POISON_SHOCK: + me->CastSpell(me->GetVictim(), SPELL_POISON_SHOCK, false); + events.Repeat(10s); + break; + case EVENT_NECROTIC_POISON: + me->CastSpell(me->GetVictim(), SPELL_NECROTIC_POISON, false); + events.Repeat(30s); + break; + case EVENT_SUMMON_SPIDERLINGS: + Talk(EMOTE_SPIDERS); + for (uint8 i = 0; i < 8; ++i) + { + me->SummonCreature(NPC_MAEXXNA_SPIDERLING, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation()); + } + events.Repeat(40s); + break; + case EVENT_HEALTH_CHECK: + if (me->GetHealthPct() < 30) + { + me->CastSpell(me, SPELL_FRENZY, true); + break; + } + events.Repeat(1s); + break; + case EVENT_WEB_WRAP: + Talk(EMOTE_WEB_WRAP); + DoCastWebWrap(); + events.Repeat(40s); + break; + case EVENT_WEB_WRAP_APPLY_STUN: + { + for (auto& p : wraps) + { + if (Player* player = ObjectAccessor::GetPlayer(*me, p)) + { + player->CastSpell(player, SPELL_WEB_WRAP_STUN, true); + } + } + wraps.clear(); + break; + } + } + DoMeleeAttackIfReady(); + } + }; +}; + +class boss_maexxna_webwrap : public CreatureScript +{ +public: + boss_maexxna_webwrap() : CreatureScript("boss_maexxna_webwrap") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_maexxna_webwrapAI : public NullCreatureAI + { + explicit boss_maexxna_webwrapAI(Creature* c) : NullCreatureAI(c) { } + + ObjectGuid victimGUID; + + void IsSummonedBy(WorldObject* summoner) override + { + if (!summoner) + return; + victimGUID = summoner->GetGUID(); + } + + void JustDied(Unit* /*killer*/) override + { + if (victimGUID) + { + if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID)) + { + if (victim->IsAlive()) + { + victim->RemoveAurasDueToSpell(SPELL_WEB_WRAP_STUN); + victim->RemoveAurasDueToSpell(SPELL_WEB_WRAP_SUMMON); + } + } + } + } + + void UpdateAI(uint32 /*diff*/) override + { + if (victimGUID) + { + if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID)) + { + if (!victim->IsAlive()) + { + me->CastSpell(me, SPELL_WEB_WRAP_KILL_WEBS, true); + } + } + } + } + }; +}; + +class spell_web_wrap_damage : public AuraScript +{ +public: + PrepareAuraScript(spell_web_wrap_damage); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_WEB_WRAP_SUMMON }); + } + + void OnPeriodic(AuraEffect const* aurEff) + { + if (aurEff->GetTickNumber() == 2) + { + GetTarget()->CastSpell(GetTarget(), SPELL_WEB_WRAP_SUMMON, true); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_web_wrap_damage::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE); + } +}; void AddSC_boss_maexxna() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.h b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.h deleted file mode 100644 index 455f330e6..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.h +++ /dev/null @@ -1,348 +0,0 @@ -#ifndef BOSS_MAEXXNA_H_ -#define BOSS_MAEXXNA_H_ - -#include "CreatureScript.h" -#include "Player.h" -#include "PassiveAI.h" -#include "ScriptedCreature.h" -#include "SpellAuraEffects.h" -#include "SpellScript.h" -#include "SpellScriptLoader.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Maexxna { - -enum Spells -{ - SPELL_WEB_SPRAY = 29484, - SPELL_POISON_SHOCK = 28741, - SPELL_NECROTIC_POISON = 54121, - SPELL_FRENZY = 54123, - SPELL_WEB_WRAP_STUN = 28622, - SPELL_WEB_WRAP_SUMMON = 28627, - SPELL_WEB_WRAP_KILL_WEBS = 52512, - SPELL_WEB_WRAP_PACIFY_5 = 28618 // 5 seconds pacify silence -}; - -enum Events -{ - EVENT_WEB_SPRAY = 1, - EVENT_POISON_SHOCK = 2, - EVENT_NECROTIC_POISON = 3, - EVENT_WEB_WRAP = 4, - EVENT_HEALTH_CHECK = 5, - EVENT_SUMMON_SPIDERLINGS = 6, - EVENT_WEB_WRAP_APPLY_STUN = 7 -}; - -enum Emotes -{ - EMOTE_SPIDERS = 0, - EMOTE_WEB_WRAP = 1, - EMOTE_WEB_SPRAY = 2 -}; - -enum Misc -{ - NPC_WEB_WRAP = 16486, - NPC_MAEXXNA_SPIDERLING = 17055 -}; - -const Position PosWrap[7] = -{ - {3496.615f, -3834.182f, 320.7863f}, - {3509.108f, -3833.922f, 320.4750f}, - {3523.644f, -3838.309f, 320.5775f}, - {3538.152f, -3846.353f, 320.5188f}, - {3546.219f, -3856.167f, 320.9324f}, - {3555.135f, -3869.507f, 320.8307f}, - {3560.282f, -3886.143f, 321.2827f} -}; - -struct WebTargetSelector -{ - WebTargetSelector(Unit* maexxna) : _maexxna(maexxna) {} - bool operator()(Unit const* target) const - { - if (!target->IsPlayer()) // never web nonplayers (pets, guardians, etc.) - return false; - if (_maexxna->GetVictim() == target) // never target tank - return false; - if (target->HasAura(SPELL_WEB_WRAP_STUN)) // never target targets that are already webbed - return false; - return true; - } - - private: - Unit const* _maexxna; -}; - -class boss_maexxna : public CreatureScript -{ -public: - boss_maexxna() : CreatureScript("boss_maexxna") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_maexxnaAI : public BossAI - { - explicit boss_maexxnaAI(Creature* c) : BossAI(c, BOSS_MAEXXNA), summons(me) - {} - - EventMap events; - SummonList summons; - - GuidList wraps; - - bool IsInRoom() - { - if (me->GetExactDist(3486.6f, -3890.6f, 291.8f) > 100.0f) - { - EnterEvadeMode(); - return false; - } - return true; - } - - void Reset() override - { - BossAI::Reset(); - events.Reset(); - summons.DespawnAll(); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - me->SetInCombatWithZone(); - events.ScheduleEvent(EVENT_WEB_WRAP, 20s); - events.ScheduleEvent(EVENT_WEB_SPRAY, 40s); - events.ScheduleEvent(EVENT_POISON_SHOCK, 10s); - events.ScheduleEvent(EVENT_NECROTIC_POISON, 5s); - events.ScheduleEvent(EVENT_HEALTH_CHECK, 1s); - events.ScheduleEvent(EVENT_SUMMON_SPIDERLINGS, 30s); - } - - void JustSummoned(Creature* cr) override - { - if (cr->GetEntry() == NPC_MAEXXNA_SPIDERLING) - { - cr->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - { - cr->AI()->AttackStart(target); - } - } - summons.Summon(cr); - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - } - - void DoCastWebWrap() - { - std::list candidates; - SelectTargetList(candidates, RAID_MODE(1, 2), SelectTargetMethod::Random, 0, WebTargetSelector(me)); - - std::vector positions {0, 1, 2, 3, 4, 5, 6}; - Acore::Containers::RandomShuffle(positions); - - if (candidates.empty()) - return; - - for (int i = 0; i < RAID_MODE(1, 2) ; i++) - { - if (candidates.empty()) - break; - const Position &randomPos = PosWrap[positions[i]]; - - auto itr = candidates.begin(); - - if (candidates.size() > 1) - std::advance(itr, urand(0, candidates.size() - 1)); - - Unit *target = *itr; - candidates.erase(itr); - - float dx = randomPos.GetPositionX() - target->GetPositionX(); - float dy = randomPos.GetPositionY() - target->GetPositionY(); - float distXY = std::hypotf(dx, dy); - - // smooth knockback arc that avoids the ceiling - float horizontalSpeed = distXY / 1.5f; - float verticalSpeed = 28.0f; - if (distXY <= 10.0f) - verticalSpeed = 12.0f; - else if (distXY <= 20.0f) - verticalSpeed = 16.0f; - else if (distXY <= 30.0f) - verticalSpeed = 20.0f; - else if (distXY <= 40.0f) - verticalSpeed = 24.0f; - - target->KnockbackFrom(randomPos.GetPositionX(), randomPos.GetPositionY(), -horizontalSpeed, verticalSpeed); - me->CastSpell(target, SPELL_WEB_WRAP_PACIFY_5, true); // pacify silence for 5 seconds - - wraps.push_back(target->GetGUID()); - } - events.ScheduleEvent(EVENT_WEB_WRAP_APPLY_STUN, 2s); - } - - void UpdateAI(uint32 diff) override - { - if (!IsInRoom()) - return; - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_WEB_SPRAY: - Talk(EMOTE_WEB_SPRAY); - me->CastSpell(me, SPELL_WEB_SPRAY, true); - events.Repeat(40s); - break; - case EVENT_POISON_SHOCK: - me->CastSpell(me->GetVictim(), SPELL_POISON_SHOCK, false); - events.Repeat(10s); - break; - case EVENT_NECROTIC_POISON: - me->CastSpell(me->GetVictim(), SPELL_NECROTIC_POISON, false); - events.Repeat(30s); - break; - case EVENT_SUMMON_SPIDERLINGS: - Talk(EMOTE_SPIDERS); - for (uint8 i = 0; i < 8; ++i) - { - me->SummonCreature(NPC_MAEXXNA_SPIDERLING, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation()); - } - events.Repeat(40s); - break; - case EVENT_HEALTH_CHECK: - if (me->GetHealthPct() < 30) - { - me->CastSpell(me, SPELL_FRENZY, true); - break; - } - events.Repeat(1s); - break; - case EVENT_WEB_WRAP: - Talk(EMOTE_WEB_WRAP); - DoCastWebWrap(); - events.Repeat(40s); - break; - case EVENT_WEB_WRAP_APPLY_STUN: - { - for (auto& p : wraps) - { - if (Player* player = ObjectAccessor::GetPlayer(*me, p)) - { - player->CastSpell(player, SPELL_WEB_WRAP_STUN, true); - } - } - wraps.clear(); - break; - } - } - DoMeleeAttackIfReady(); - } - }; -}; - -class boss_maexxna_webwrap : public CreatureScript -{ -public: - boss_maexxna_webwrap() : CreatureScript("boss_maexxna_webwrap") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_maexxna_webwrapAI : public NullCreatureAI - { - explicit boss_maexxna_webwrapAI(Creature* c) : NullCreatureAI(c) { } - - ObjectGuid victimGUID; - - void IsSummonedBy(WorldObject* summoner) override - { - if (!summoner) - return; - victimGUID = summoner->GetGUID(); - } - - void JustDied(Unit* /*killer*/) override - { - if (victimGUID) - { - if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID)) - { - if (victim->IsAlive()) - { - victim->RemoveAurasDueToSpell(SPELL_WEB_WRAP_STUN); - victim->RemoveAurasDueToSpell(SPELL_WEB_WRAP_SUMMON); - } - } - } - } - - void UpdateAI(uint32 /*diff*/) override - { - if (victimGUID) - { - if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID)) - { - if (!victim->IsAlive()) - { - me->CastSpell(me, SPELL_WEB_WRAP_KILL_WEBS, true); - } - } - } - } - }; -}; - -class spell_web_wrap_damage : public AuraScript -{ -public: - PrepareAuraScript(spell_web_wrap_damage); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_WEB_WRAP_SUMMON }); - } - - void OnPeriodic(AuraEffect const* aurEff) - { - if (aurEff->GetTickNumber() == 2) - { - GetTarget()->CastSpell(GetTarget(), SPELL_WEB_WRAP_SUMMON, true); - } - } - - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_web_wrap_damage::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE); - } -}; - -} // namespace Maexxna -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp index 667f61d01..33d6ff391 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp @@ -15,16 +15,260 @@ * with this program. If not, see . */ -#include "boss_noth.h" #include "CreatureScript.h" #include "ScriptedCreature.h" #include "naxxramas.h" +enum Says +{ + SAY_AGGRO = 0, + SAY_SUMMON = 1, + SAY_SLAY = 2, + SAY_DEATH = 3, + EMOTE_SUMMON = 4, + EMOTE_SUMMON_WAVE = 5, + EMOTE_TELEPORT_BALCONY = 6, + EMOTE_TELEPORT_BACK = 7, + EMOTE_BLINK = 8 +}; -using namespace Noth; +enum Spells +{ + SPELL_CURSE_OF_THE_PLAGUEBRINGER = 29213, + SPELL_CRIPPLE = 29212, + SPELL_SUMMON_PLAGUED_WARRIORS = 29237, + SPELL_TELEPORT = 29216, + SPELL_TELEPORT_BACK = 29231, + SPELL_BERSERK = 68378, + SPELL_BLINK = 29208 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Events +{ + EVENT_CURSE = 1, + EVENT_CRIPPLE = 2, + EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE = 3, + EVENT_MOVE_TO_BALCONY = 4, + EVENT_BLINK = 5, + EVENT_MOVE_TO_GROUND = 6, + EVENT_SUMMON_PLAGUED_WARRIOR_REAL = 7, + EVENT_BALCONY_SUMMON_ANNOUNCE = 8, + EVENT_BALCONY_SUMMON_REAL = 9 +}; + +enum Misc +{ + NPC_PLAGUED_WARRIOR = 16984, + NPC_PLAGUED_CHAMPION = 16983, + NPC_PLAGUED_GUARDIAN = 16981 +}; + +const Position summoningPosition[5] = +{ + {2728.06f, -3535.38f, 263.21f, 2.75f}, + {2725.71f, -3514.80f, 263.23f, 2.86f}, + {2728.24f, -3465.08f, 264.20f, 3.56f}, + {2704.79f, -3459.17f, 263.74f, 4.25f}, + {2652.02f, -3459.13f, 262.50f, 5.39f} +}; + +const Position nothPosition = {2684.94f, -3502.53f, 261.31f, 4.7f}; + +class boss_noth : public CreatureScript +{ +public: + boss_noth() : CreatureScript("boss_noth") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_nothAI : public BossAI + { + explicit boss_nothAI(Creature* c) : BossAI(c, BOSS_NOTH), summons(me) + {} + + uint8 timesInBalcony; + EventMap events; + SummonList summons; + + void StartGroundPhase() + { + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetControlled(false, UNIT_STATE_ROOT); + events.Reset(); + events.ScheduleEvent(EVENT_MOVE_TO_BALCONY, 110s); + events.ScheduleEvent(EVENT_CURSE, 15s); + events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE, 10s); + if (Is25ManRaid()) + { + events.ScheduleEvent(EVENT_BLINK, 26s); + } + } + + void StartBalconyPhase() + { + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetControlled(true, UNIT_STATE_ROOT); + events.Reset(); + events.ScheduleEvent(EVENT_BALCONY_SUMMON_ANNOUNCE, 4s); + events.ScheduleEvent(EVENT_MOVE_TO_GROUND, 70s); + } + + void SummonHelper(uint32 entry, uint32 count) + { + for (uint8 i = 0; i < count; ++i) + { + me->SummonCreature(entry, summoningPosition[urand(0, 4)]); + } + } + + bool IsInRoom() + { + if (me->GetExactDist(2684.8f, -3502.5f, 261.3f) > 80.0f) + { + EnterEvadeMode(EVADE_REASON_OTHER); + return false; + } + return true; + } + + void Reset() override + { + BossAI::Reset(); + events.Reset(); + summons.DespawnAll(); + me->CastSpell(me, SPELL_TELEPORT_BACK, true); + me->SetControlled(false, UNIT_STATE_ROOT); + me->SetReactState(REACT_AGGRESSIVE); + timesInBalcony = 0; + } + + void EnterEvadeMode(EvadeReason why) override + { + me->SetControlled(false, UNIT_STATE_ROOT); + ScriptedAI::EnterEvadeMode(why); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + StartGroundPhase(); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + summon->SetInCombatWithZone(); + } + + void JustDied(Unit* killer) override + { + if (me->GetPositionZ() > 270.27f) + { + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE); + me->NearTeleportTo(nothPosition.GetPositionX(), nothPosition.GetPositionY(), nothPosition.GetPositionZ(), nothPosition.GetOrientation(), true); + } + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } + + void KilledUnit(Unit* who) override + { + if (!who->IsPlayer()) + return; + + Talk(SAY_SLAY); + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void UpdateAI(uint32 diff) override + { + if (!IsInRoom()) + return; + + if (!UpdateVictim()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + // GROUND + case EVENT_CURSE: + if (events.GetPhaseMask() == 0) + { + me->CastCustomSpell(SPELL_CURSE_OF_THE_PLAGUEBRINGER, SPELLVALUE_MAX_TARGETS, RAID_MODE(3, 10), me, false); + } + events.Repeat(25s); + break; + case EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE: + Talk(SAY_SUMMON); + Talk(EMOTE_SUMMON); + events.Repeat(30s); + events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_REAL, 4s); + break; + case EVENT_SUMMON_PLAGUED_WARRIOR_REAL: + me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true); + SummonHelper(NPC_PLAGUED_WARRIOR, RAID_MODE(2, 3)); + break; + case EVENT_MOVE_TO_BALCONY: + Talk(EMOTE_TELEPORT_BALCONY); + me->CastSpell(me, SPELL_TELEPORT, true); + StartBalconyPhase(); + break; + case EVENT_BLINK: + DoResetThreatList(); + me->CastSpell(me, SPELL_CRIPPLE, false); + me->CastSpell(me, SPELL_BLINK, true); + Talk(EMOTE_BLINK); + events.Repeat(30s); + break; + // BALCONY + case EVENT_BALCONY_SUMMON_ANNOUNCE: + Talk(EMOTE_SUMMON_WAVE); + events.Repeat(30s); + events.ScheduleEvent(EVENT_BALCONY_SUMMON_REAL, 4s); + break; + case EVENT_BALCONY_SUMMON_REAL: + me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true); // visual + switch (timesInBalcony) + { + case 0: + SummonHelper(NPC_PLAGUED_CHAMPION, RAID_MODE(2, 4)); + break; + case 1: + SummonHelper(NPC_PLAGUED_CHAMPION, RAID_MODE(1, 2)); + SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(1, 2)); + break; + default: + SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(2, 4)); + break; + } + break; + case EVENT_MOVE_TO_GROUND: + Talk(EMOTE_TELEPORT_BACK); + me->CastSpell(me, SPELL_TELEPORT_BACK, true); + timesInBalcony++; + if (timesInBalcony == 3) + { + DoCastSelf(SPELL_BERSERK); + } + StartGroundPhase(); + break; + } + if (me->HasReactState(REACT_AGGRESSIVE)) + DoMeleeAttackIfReady(); + } + }; +}; void AddSC_boss_noth() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.h b/src/server/scripts/Northrend/Naxxramas/boss_noth.h deleted file mode 100644 index 3e88eec6c..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_noth.h +++ /dev/null @@ -1,264 +0,0 @@ -#ifndef BOSS_NOTH_H_ -#define BOSS_NOTH_H_ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Noth { - -enum Says -{ - SAY_AGGRO = 0, - SAY_SUMMON = 1, - SAY_SLAY = 2, - SAY_DEATH = 3, - EMOTE_SUMMON = 4, - EMOTE_SUMMON_WAVE = 5, - EMOTE_TELEPORT_BALCONY = 6, - EMOTE_TELEPORT_BACK = 7, - EMOTE_BLINK = 8 -}; - -enum Spells -{ - SPELL_CURSE_OF_THE_PLAGUEBRINGER = 29213, - SPELL_CRIPPLE = 29212, - SPELL_SUMMON_PLAGUED_WARRIORS = 29237, - SPELL_TELEPORT = 29216, - SPELL_TELEPORT_BACK = 29231, - SPELL_BERSERK = 68378, - SPELL_BLINK = 29208 -}; - -enum Events -{ - EVENT_CURSE = 1, - EVENT_CRIPPLE = 2, - EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE = 3, - EVENT_MOVE_TO_BALCONY = 4, - EVENT_BLINK = 5, - EVENT_MOVE_TO_GROUND = 6, - EVENT_SUMMON_PLAGUED_WARRIOR_REAL = 7, - EVENT_BALCONY_SUMMON_ANNOUNCE = 8, - EVENT_BALCONY_SUMMON_REAL = 9 -}; - -enum Misc -{ - NPC_PLAGUED_WARRIOR = 16984, - NPC_PLAGUED_CHAMPION = 16983, - NPC_PLAGUED_GUARDIAN = 16981 -}; - -const Position summoningPosition[5] = -{ - {2728.06f, -3535.38f, 263.21f, 2.75f}, - {2725.71f, -3514.80f, 263.23f, 2.86f}, - {2728.24f, -3465.08f, 264.20f, 3.56f}, - {2704.79f, -3459.17f, 263.74f, 4.25f}, - {2652.02f, -3459.13f, 262.50f, 5.39f} -}; - -const Position nothPosition = {2684.94f, -3502.53f, 261.31f, 4.7f}; - -class boss_noth : public CreatureScript -{ -public: - boss_noth() : CreatureScript("boss_noth") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_nothAI : public BossAI - { - explicit boss_nothAI(Creature* c) : BossAI(c, BOSS_NOTH), summons(me) - {} - - uint8 timesInBalcony; - EventMap events; - SummonList summons; - - void StartGroundPhase() - { - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE); - me->SetControlled(false, UNIT_STATE_ROOT); - events.Reset(); - events.ScheduleEvent(EVENT_MOVE_TO_BALCONY, 110s); - events.ScheduleEvent(EVENT_CURSE, 15s); - events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE, 10s); - if (Is25ManRaid()) - { - events.ScheduleEvent(EVENT_BLINK, 26s); - } - } - - void StartBalconyPhase() - { - me->SetReactState(REACT_PASSIVE); - me->AttackStop(); - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE); - me->SetControlled(true, UNIT_STATE_ROOT); - events.Reset(); - events.ScheduleEvent(EVENT_BALCONY_SUMMON_ANNOUNCE, 4s); - events.ScheduleEvent(EVENT_MOVE_TO_GROUND, 70s); - } - - void SummonHelper(uint32 entry, uint32 count) - { - for (uint8 i = 0; i < count; ++i) - { - me->SummonCreature(entry, summoningPosition[urand(0, 4)]); - } - } - - bool IsInRoom() - { - if (me->GetExactDist(2684.8f, -3502.5f, 261.3f) > 80.0f) - { - EnterEvadeMode(EVADE_REASON_OTHER); - return false; - } - return true; - } - - void Reset() override - { - BossAI::Reset(); - events.Reset(); - summons.DespawnAll(); - me->CastSpell(me, SPELL_TELEPORT_BACK, true); - me->SetControlled(false, UNIT_STATE_ROOT); - me->SetReactState(REACT_AGGRESSIVE); - timesInBalcony = 0; - } - - void EnterEvadeMode(EvadeReason why) override - { - me->SetControlled(false, UNIT_STATE_ROOT); - ScriptedAI::EnterEvadeMode(why); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - Talk(SAY_AGGRO); - StartGroundPhase(); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - summon->SetInCombatWithZone(); - } - - void JustDied(Unit* killer) override - { - if (me->GetPositionZ() > 270.27f) - { - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE); - me->NearTeleportTo(nothPosition.GetPositionX(), nothPosition.GetPositionY(), nothPosition.GetPositionZ(), nothPosition.GetOrientation(), true); - } - BossAI::JustDied(killer); - Talk(SAY_DEATH); - } - - void KilledUnit(Unit* who) override - { - if (!who->IsPlayer()) - return; - - Talk(SAY_SLAY); - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void UpdateAI(uint32 diff) override - { - if (!IsInRoom()) - return; - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - // GROUND - case EVENT_CURSE: - if (events.GetPhaseMask() == 0) - { - me->CastCustomSpell(SPELL_CURSE_OF_THE_PLAGUEBRINGER, SPELLVALUE_MAX_TARGETS, RAID_MODE(3, 10), me, false); - } - events.Repeat(25s); - break; - case EVENT_SUMMON_PLAGUED_WARRIOR_ANNOUNCE: - Talk(SAY_SUMMON); - Talk(EMOTE_SUMMON); - events.Repeat(30s); - events.ScheduleEvent(EVENT_SUMMON_PLAGUED_WARRIOR_REAL, 4s); - break; - case EVENT_SUMMON_PLAGUED_WARRIOR_REAL: - me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true); - SummonHelper(NPC_PLAGUED_WARRIOR, RAID_MODE(2, 3)); - break; - case EVENT_MOVE_TO_BALCONY: - Talk(EMOTE_TELEPORT_BALCONY); - me->CastSpell(me, SPELL_TELEPORT, true); - StartBalconyPhase(); - break; - case EVENT_BLINK: - DoResetThreatList(); - me->CastSpell(me, SPELL_CRIPPLE, false); - me->CastSpell(me, SPELL_BLINK, true); - Talk(EMOTE_BLINK); - events.Repeat(30s); - break; - // BALCONY - case EVENT_BALCONY_SUMMON_ANNOUNCE: - Talk(EMOTE_SUMMON_WAVE); - events.Repeat(30s); - events.ScheduleEvent(EVENT_BALCONY_SUMMON_REAL, 4s); - break; - case EVENT_BALCONY_SUMMON_REAL: - me->CastSpell(me, SPELL_SUMMON_PLAGUED_WARRIORS, true); // visual - switch (timesInBalcony) - { - case 0: - SummonHelper(NPC_PLAGUED_CHAMPION, RAID_MODE(2, 4)); - break; - case 1: - SummonHelper(NPC_PLAGUED_CHAMPION, RAID_MODE(1, 2)); - SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(1, 2)); - break; - default: - SummonHelper(NPC_PLAGUED_GUARDIAN, RAID_MODE(2, 4)); - break; - } - break; - case EVENT_MOVE_TO_GROUND: - Talk(EMOTE_TELEPORT_BACK); - me->CastSpell(me, SPELL_TELEPORT_BACK, true); - timesInBalcony++; - if (timesInBalcony == 3) - { - DoCastSelf(SPELL_BERSERK); - } - StartGroundPhase(); - break; - } - if (me->HasReactState(REACT_AGGRESSIVE)) - DoMeleeAttackIfReady(); - } - }; -}; - -} - -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp index 56ae47e2c..319f08966 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp @@ -15,16 +15,173 @@ * with this program. If not, see . */ -#include "boss_patchwerk.h" #include "CreatureScript.h" #include "ScriptedCreature.h" #include "naxxramas.h" +enum Yells +{ + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + EMOTE_BERSERK = 3, + EMOTE_ENRAGE = 4 +}; -using namespace PatchWerk; +enum Spells +{ + SPELL_HATEFUL_STRIKE = 41926, + SPELL_FRENZY = 28131, + SPELL_BERSERK = 26662, + SPELL_SLIME_BOLT = 32309 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Events +{ + EVENT_HEALTH_CHECK = 1, + EVENT_HATEFUL_STRIKE = 2, + EVENT_SLIME_BOLT = 3, + EVENT_BERSERK = 4 +}; + +enum Misc +{ + ACHIEV_TIMED_START_EVENT = 10286 +}; + +class boss_patchwerk : public CreatureScript +{ +public: + boss_patchwerk() : CreatureScript("boss_patchwerk") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_patchwerkAI : public BossAI + { + explicit boss_patchwerkAI(Creature* c) : BossAI(c, BOSS_PATCHWERK) + {} + + EventMap events; + + void Reset() override + { + BossAI::Reset(); + events.Reset(); + } + + void KilledUnit(Unit* who) override + { + if (!who->IsPlayer()) + return; + + if (!urand(0, 3)) + Talk(SAY_SLAY); + + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + me->SetInCombatWithZone(); + events.ScheduleEvent(EVENT_HATEFUL_STRIKE, 1500ms); + events.ScheduleEvent(EVENT_BERSERK, 6min); + events.ScheduleEvent(EVENT_HEALTH_CHECK, 1s); + instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_HATEFUL_STRIKE: + { + // Cast Hateful strike on the player with the highest amount of HP within melee distance, and second threat amount + std::list meleeRangeTargets; + Unit* finalTarget = nullptr; + uint8 counter = 0; + auto i = me->GetThreatMgr().GetThreatList().begin(); + for (; i != me->GetThreatMgr().GetThreatList().end(); ++i, ++counter) + { + // Gather all units with melee range + Unit* target = (*i)->getTarget(); + if (me->IsWithinMeleeRange(target)) + { + meleeRangeTargets.push_back(target); + } + // and add threat to most hated + if (counter < RAID_MODE(2, 3)) + { + me->AddThreat(target, 500.0f); + } + } + counter = 0; + std::list>::iterator itr; + for (itr = meleeRangeTargets.begin(); itr != meleeRangeTargets.end(); ++itr, ++counter) + { + // if there is only one target available + if (meleeRangeTargets.size() == 1) + { + finalTarget = (*itr); + } + else if (counter > 0) // skip first target + { + if (!finalTarget || (*itr)->GetHealth() > finalTarget->GetHealth()) + { + finalTarget = (*itr); + } + // third loop + if (counter >= 2) + break; + } + } + if (finalTarget) + { + me->CastSpell(finalTarget, SPELL_HATEFUL_STRIKE, false); + } + events.Repeat(1s); + break; + } + case EVENT_BERSERK: + Talk(EMOTE_BERSERK); + me->CastSpell(me, SPELL_BERSERK, true); + events.ScheduleEvent(EVENT_SLIME_BOLT, 3s); + break; + case EVENT_SLIME_BOLT: + me->CastSpell(me, SPELL_SLIME_BOLT, false); + events.Repeat(3s); + break; + case EVENT_HEALTH_CHECK: + if (me->GetHealthPct() <= 5) + { + Talk(EMOTE_ENRAGE); + me->CastSpell(me, SPELL_FRENZY, true); + break; + } + events.Repeat(1s); + break; + } + DoMeleeAttackIfReady(); + } + }; +}; void AddSC_boss_patchwerk() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.h b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.h deleted file mode 100644 index d216e16a3..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef BOSS_PATCHWERK_H_ -#define BOSS_PATCHWERK_H_ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace PatchWerk { - -enum Yells -{ - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - EMOTE_BERSERK = 3, - EMOTE_ENRAGE = 4 -}; - -enum Spells -{ - SPELL_HATEFUL_STRIKE = 41926, - SPELL_FRENZY = 28131, - SPELL_BERSERK = 26662, - SPELL_SLIME_BOLT = 32309 -}; - -enum Events -{ - EVENT_HEALTH_CHECK = 1, - EVENT_HATEFUL_STRIKE = 2, - EVENT_SLIME_BOLT = 3, - EVENT_BERSERK = 4 -}; - -enum Misc -{ - ACHIEV_TIMED_START_EVENT = 10286 -}; - -class boss_patchwerk : public CreatureScript -{ -public: - boss_patchwerk() : CreatureScript("boss_patchwerk") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_patchwerkAI : public BossAI - { - explicit boss_patchwerkAI(Creature* c) : BossAI(c, BOSS_PATCHWERK) - {} - - EventMap events; - - void Reset() override - { - BossAI::Reset(); - events.Reset(); - } - - void KilledUnit(Unit* who) override - { - if (!who->IsPlayer()) - return; - - if (!urand(0, 3)) - Talk(SAY_SLAY); - - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - Talk(SAY_DEATH); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - Talk(SAY_AGGRO); - me->SetInCombatWithZone(); - events.ScheduleEvent(EVENT_HATEFUL_STRIKE, 1500ms); - events.ScheduleEvent(EVENT_BERSERK, 6min); - events.ScheduleEvent(EVENT_HEALTH_CHECK, 1s); - instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_HATEFUL_STRIKE: - { - // Cast Hateful strike on the player with the highest amount of HP within melee distance, and second threat amount - std::list meleeRangeTargets; - Unit* finalTarget = nullptr; - uint8 counter = 0; - auto i = me->GetThreatMgr().GetThreatList().begin(); - for (; i != me->GetThreatMgr().GetThreatList().end(); ++i, ++counter) - { - // Gather all units with melee range - Unit* target = (*i)->getTarget(); - if (me->IsWithinMeleeRange(target)) - { - meleeRangeTargets.push_back(target); - } - // and add threat to most hated - if (counter < RAID_MODE(2, 3)) - { - me->AddThreat(target, 500.0f); - } - } - counter = 0; - std::list>::iterator itr; - for (itr = meleeRangeTargets.begin(); itr != meleeRangeTargets.end(); ++itr, ++counter) - { - // if there is only one target available - if (meleeRangeTargets.size() == 1) - { - finalTarget = (*itr); - } - else if (counter > 0) // skip first target - { - if (!finalTarget || (*itr)->GetHealth() > finalTarget->GetHealth()) - { - finalTarget = (*itr); - } - // third loop - if (counter >= 2) - break; - } - } - if (finalTarget) - { - me->CastSpell(finalTarget, SPELL_HATEFUL_STRIKE, false); - } - events.Repeat(1s); - break; - } - case EVENT_BERSERK: - Talk(EMOTE_BERSERK); - me->CastSpell(me, SPELL_BERSERK, true); - events.ScheduleEvent(EVENT_SLIME_BOLT, 3s); - break; - case EVENT_SLIME_BOLT: - me->CastSpell(me, SPELL_SLIME_BOLT, false); - events.Repeat(3s); - break; - case EVENT_HEALTH_CHECK: - if (me->GetHealthPct() <= 5) - { - Talk(EMOTE_ENRAGE); - me->CastSpell(me, SPELL_FRENZY, true); - break; - } - events.Repeat(1s); - break; - } - DoMeleeAttackIfReady(); - } - }; -}; - -} -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp index 27d73c469..4aae788f5 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp @@ -15,17 +15,372 @@ * with this program. If not, see . */ -#include "boss_razuvious.h" #include "CreatureScript.h" #include "ScriptedCreature.h" #include "naxxramas.h" #include "SpellInfo.h" +enum Says +{ + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_TAUNTED = 2, + SAY_DEATH = 3, + SAY_PATHETIC = 4, + SAY_TARGET_DUMMY = 5, + SAY_DEATH_KNIGHT_UNDERSTUDY = 0, +}; -using namespace Razuvious; +enum Spells +{ + SPELL_UNBALANCING_STRIKE = 26613, + SPELL_DISRUPTING_SHOUT = 55543, + SPELL_JAGGED_KNIFE = 55550, + SPELL_HOPELESS = 29125, + SPELL_TAUNT = 29060 +}; -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file +enum Events +{ + EVENT_UNBALANCING_STRIKE = 1, + EVENT_DISRUPTING_SHOUT = 2, + EVENT_JAGGED_KNIFE = 3 +}; + +enum NPCs +{ + NPC_DEATH_KNIGHT_UNDERSTUDY = 16803, + NPC_TARGET_DUMMY = 16211, +}; + +enum Actions +{ + ACTION_FACE_ME = 0, + ACTION_TALK = 1, + ACTION_EMOTE = 2, + ACTION_SALUTE = 3, + ACTION_BACK_TO_TRAINING = 4, +}; + +enum Misc +{ + GROUP_OOC_RP = 0, + POINT_DEATH_KNIGHT = 0, +}; + +class boss_razuvious : public CreatureScript +{ +public: + boss_razuvious() : CreatureScript("boss_razuvious") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_razuviousAI : public BossAI + { + explicit boss_razuviousAI(Creature* c) : BossAI(c, BOSS_RAZUVIOUS), summons(me) + {} + + EventMap events; + SummonList summons; + + void SpawnHelpers() + { + me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2762.23f, -3085.07f, 267.685f, 1.95f); + me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2758.24f, -3110.97f, 267.685f, 3.94f); + if (Is25ManRaid()) + { + me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2782.45f, -3088.03f, 267.685f, 0.75f); + me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2778.56f, -3113.74f, 267.685f, 5.28f); + } + } + + void JustSummoned(Creature* cr) override + { + summons.Summon(cr); + } + + void Reset() override + { + BossAI::Reset(); + summons.DespawnAll(); + events.Reset(); + SpawnHelpers(); + ScheduleRP(); + } + + void ScheduleInteractWithDeathKnight() + { + if (_rpBuddyGUID) + if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) + me->SetFacingToObject(understudy); + + scheduler.Schedule(2s, GROUP_OOC_RP, [this](TaskContext /*context*/) + { + if (roll_chance_i(75)) + { + bool longText = roll_chance_i(50); + Talk(longText ? SAY_TARGET_DUMMY : SAY_PATHETIC); + scheduler.Schedule(4s, GROUP_OOC_RP, [this](TaskContext /*context*/) + { + if (_rpBuddyGUID) + if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) + understudy->AI()->DoAction(ACTION_TALK); + }); + if (longText) + scheduler.DelayGroup(GROUP_OOC_RP, 5s); + } + else + { + me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + scheduler.Schedule(4s, GROUP_OOC_RP, [this](TaskContext /*context*/) + { + if (_rpBuddyGUID) + if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) + { + if (roll_chance_i(25)) + understudy->AI()->DoAction(ACTION_EMOTE); + else + understudy->AI()->DoAction(ACTION_TALK); + } + }); + } + }).Schedule(4s, GROUP_OOC_RP, [this](TaskContext /*context*/) + { + if (_rpBuddyGUID) + if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) + understudy->AI()->DoAction(ACTION_FACE_ME); + }).Schedule(10s, GROUP_OOC_RP, [this](TaskContext /*context*/) + { + if (_rpBuddyGUID) + if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) + understudy->AI()->DoAction(ACTION_SALUTE); + }).Schedule(13s, GROUP_OOC_RP, [this](TaskContext /*context*/) + { + me->ResumeMovement(); + }).Schedule(16s, GROUP_OOC_RP, [this](TaskContext /*context*/) + { + if (_rpBuddyGUID) + if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) + understudy->AI()->DoAction(ACTION_BACK_TO_TRAINING); + ScheduleRP(); + }); + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE && id == POINT_DEATH_KNIGHT) + { + ScheduleInteractWithDeathKnight(); + } + } + + void ScheduleRP() + { + _rpBuddyGUID = Acore::Containers::SelectRandomContainerElement(summons); + scheduler.Schedule(60s, 80s, GROUP_OOC_RP, [this](TaskContext context) + { + if (_rpBuddyGUID) + { + if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) + { + if (me->GetDistance2d(understudy) <= 6.0f) + { + me->PauseMovement(); + scheduler.Schedule(500ms, GROUP_OOC_RP, [this](TaskContext /*context*/) + { + if (_rpBuddyGUID) + if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) + me->GetMotionMaster()->MovePoint(POINT_DEATH_KNIGHT, understudy->GetNearPosition(3.2f, understudy->GetRelativeAngle(me))); + }); + return; + } + } + } + context.Repeat(2s); + }); + } + + void KilledUnit(Unit* who) override + { + if (roll_chance_i(30)) + Talk(SAY_SLAY); + + if (who->IsPlayer()) + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override + { + // Damage done by the controlled Death Knight understudies should also count toward damage done by players + if (who && who->IsCreature() && who->GetEntry() == NPC_DEATH_KNIGHT_UNDERSTUDY) + { + me->LowerPlayerDamageReq(damage); + } + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + me->CastSpell(me, SPELL_HOPELESS, true); + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_TAUNT) + { + Talk(SAY_TAUNTED, caster); + } + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + scheduler.CancelGroup(GROUP_OOC_RP); + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_UNBALANCING_STRIKE, 20s); + events.ScheduleEvent(EVENT_DISRUPTING_SHOUT, 15s); + events.ScheduleEvent(EVENT_JAGGED_KNIFE, 10s); + summons.DoZoneInCombat(); + } + + void UpdateAI(uint32 diff) override + { + if (!me->IsInCombat()) + scheduler.Update(diff); + + if (!UpdateVictim()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_UNBALANCING_STRIKE: + me->CastSpell(me->GetVictim(), SPELL_UNBALANCING_STRIKE, false); + events.Repeat(20s); + break; + case EVENT_DISRUPTING_SHOUT: + me->CastSpell(me, SPELL_DISRUPTING_SHOUT, false); + events.Repeat(15s); + break; + case EVENT_JAGGED_KNIFE: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.0f)) + { + me->CastSpell(target, SPELL_JAGGED_KNIFE, false); + } + events.Repeat(10s); + break; + } + DoMeleeAttackIfReady(); + } + + private: + ObjectGuid _rpBuddyGUID; + }; +}; + +class boss_razuvious_minion : public CreatureScript +{ +public: + boss_razuvious_minion() : CreatureScript("boss_razuvious_minion") { } + + CreatureAI* GetAI(Creature* creature) const override + { + return GetNaxxramasAI(creature); + } + + struct boss_razuvious_minionAI : public ScriptedAI + { + explicit boss_razuvious_minionAI(Creature* creature) : ScriptedAI(creature) { } + + void Reset() override + { + scheduler.CancelAll(); + ScheduleAttackDummy(); + } + + void ScheduleAttackDummy() + { + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H); + if (Creature* targetDummy = me->FindNearestCreature(NPC_TARGET_DUMMY, 10.0f)) + { + me->SetFacingToObject(targetDummy); + } + scheduler.Schedule(6s, 9s, GROUP_OOC_RP, [this](TaskContext context) + { + me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK1H); + context.Repeat(6s, 9s); + }); + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_FACE_ME: + { + scheduler.CancelGroup(GROUP_OOC_RP); + me->SetSheath(SHEATH_STATE_UNARMED); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); + + if (Creature* creature = me->GetInstanceScript()->GetCreature(DATA_RAZUVIOUS_BOSS)) + me->SetFacingToObject(creature); + + break; + } + case ACTION_TALK: + Talk(SAY_DEATH_KNIGHT_UNDERSTUDY); + break; + case ACTION_EMOTE: + me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + break; + case ACTION_SALUTE: + me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + break; + case ACTION_BACK_TO_TRAINING: + me->SetSheath(SHEATH_STATE_MELEE); + ScheduleAttackDummy(); + break; + } + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void JustEngagedWith(Unit* who) override + { + scheduler.CancelGroup(GROUP_OOC_RP); + + if (Creature* creature = me->GetInstanceScript()->GetCreature(DATA_RAZUVIOUS_BOSS)) + { + creature->SetInCombatWithZone(); + creature->AI()->AttackStart(who); + } + } + + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + + if (UpdateVictim()) + { + if (!me->HasUnitState(UNIT_STATE_CASTING) || !me->IsCharmed()) + { + DoMeleeAttackIfReady(); + } + } + } + }; +}; void AddSC_boss_razuvious() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.h b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.h deleted file mode 100644 index 14966a0ac..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.h +++ /dev/null @@ -1,374 +0,0 @@ -#ifndef BOSS_RAZUVIOUS_H_ -#define BOSS_RAZUVIOUS_H_ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Razuvious { - -enum Says -{ - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_TAUNTED = 2, - SAY_DEATH = 3, - SAY_PATHETIC = 4, - SAY_TARGET_DUMMY = 5, - SAY_DEATH_KNIGHT_UNDERSTUDY = 0, -}; - -enum Spells -{ - SPELL_UNBALANCING_STRIKE = 26613, - SPELL_DISRUPTING_SHOUT = 55543, - SPELL_JAGGED_KNIFE = 55550, - SPELL_HOPELESS = 29125, - SPELL_TAUNT = 29060 -}; - -enum Events -{ - EVENT_UNBALANCING_STRIKE = 1, - EVENT_DISRUPTING_SHOUT = 2, - EVENT_JAGGED_KNIFE = 3 -}; - -enum NPCs -{ - NPC_DEATH_KNIGHT_UNDERSTUDY = 16803, - NPC_TARGET_DUMMY = 16211, -}; - -enum Actions -{ - ACTION_FACE_ME = 0, - ACTION_TALK = 1, - ACTION_EMOTE = 2, - ACTION_SALUTE = 3, - ACTION_BACK_TO_TRAINING = 4, -}; - -enum Misc -{ - GROUP_OOC_RP = 0, - POINT_DEATH_KNIGHT = 0, -}; - -class boss_razuvious : public CreatureScript -{ -public: - boss_razuvious() : CreatureScript("boss_razuvious") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_razuviousAI : public BossAI - { - explicit boss_razuviousAI(Creature* c) : BossAI(c, BOSS_RAZUVIOUS), summons(me) - {} - - EventMap events; - SummonList summons; - - void SpawnHelpers() - { - me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2762.23f, -3085.07f, 267.685f, 1.95f); - me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2758.24f, -3110.97f, 267.685f, 3.94f); - if (Is25ManRaid()) - { - me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2782.45f, -3088.03f, 267.685f, 0.75f); - me->SummonCreature(NPC_DEATH_KNIGHT_UNDERSTUDY, 2778.56f, -3113.74f, 267.685f, 5.28f); - } - } - - void JustSummoned(Creature* cr) override - { - summons.Summon(cr); - } - - void Reset() override - { - BossAI::Reset(); - summons.DespawnAll(); - events.Reset(); - SpawnHelpers(); - ScheduleRP(); - } - - void ScheduleInteractWithDeathKnight() - { - if (_rpBuddyGUID) - if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) - me->SetFacingToObject(understudy); - - scheduler.Schedule(2s, GROUP_OOC_RP, [this](TaskContext /*context*/) - { - if (roll_chance_i(75)) - { - bool longText = roll_chance_i(50); - Talk(longText ? SAY_TARGET_DUMMY : SAY_PATHETIC); - scheduler.Schedule(4s, GROUP_OOC_RP, [this](TaskContext /*context*/) - { - if (_rpBuddyGUID) - if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) - understudy->AI()->DoAction(ACTION_TALK); - }); - if (longText) - scheduler.DelayGroup(GROUP_OOC_RP, 5s); - } - else - { - me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); - scheduler.Schedule(4s, GROUP_OOC_RP, [this](TaskContext /*context*/) - { - if (_rpBuddyGUID) - if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) - { - if (roll_chance_i(25)) - understudy->AI()->DoAction(ACTION_EMOTE); - else - understudy->AI()->DoAction(ACTION_TALK); - } - }); - } - }).Schedule(4s, GROUP_OOC_RP, [this](TaskContext /*context*/) - { - if (_rpBuddyGUID) - if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) - understudy->AI()->DoAction(ACTION_FACE_ME); - }).Schedule(10s, GROUP_OOC_RP, [this](TaskContext /*context*/) - { - if (_rpBuddyGUID) - if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) - understudy->AI()->DoAction(ACTION_SALUTE); - }).Schedule(13s, GROUP_OOC_RP, [this](TaskContext /*context*/) - { - me->ResumeMovement(); - }).Schedule(16s, GROUP_OOC_RP, [this](TaskContext /*context*/) - { - if (_rpBuddyGUID) - if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) - understudy->AI()->DoAction(ACTION_BACK_TO_TRAINING); - ScheduleRP(); - }); - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type == POINT_MOTION_TYPE && id == POINT_DEATH_KNIGHT) - { - ScheduleInteractWithDeathKnight(); - } - } - - void ScheduleRP() - { - _rpBuddyGUID = Acore::Containers::SelectRandomContainerElement(summons); - scheduler.Schedule(60s, 80s, GROUP_OOC_RP, [this](TaskContext context) - { - if (_rpBuddyGUID) - { - if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) - { - if (me->GetDistance2d(understudy) <= 6.0f) - { - me->PauseMovement(); - scheduler.Schedule(500ms, GROUP_OOC_RP, [this](TaskContext /*context*/) - { - if (_rpBuddyGUID) - if (Creature* understudy = ObjectAccessor::GetCreature(*me, _rpBuddyGUID)) - me->GetMotionMaster()->MovePoint(POINT_DEATH_KNIGHT, understudy->GetNearPosition(3.2f, understudy->GetRelativeAngle(me))); - }); - return; - } - } - } - context.Repeat(2s); - }); - } - - void KilledUnit(Unit* who) override - { - if (roll_chance_i(30)) - Talk(SAY_SLAY); - - if (who->IsPlayer()) - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - // Damage done by the controlled Death Knight understudies should also count toward damage done by players - if (who && who->IsCreature() && who->GetEntry() == NPC_DEATH_KNIGHT_UNDERSTUDY) - { - me->LowerPlayerDamageReq(damage); - } - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - Talk(SAY_DEATH); - me->CastSpell(me, SPELL_HOPELESS, true); - } - - void SpellHit(Unit* caster, SpellInfo const* spell) override - { - if (spell->Id == SPELL_TAUNT) - { - Talk(SAY_TAUNTED, caster); - } - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - scheduler.CancelGroup(GROUP_OOC_RP); - Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_UNBALANCING_STRIKE, 20s); - events.ScheduleEvent(EVENT_DISRUPTING_SHOUT, 15s); - events.ScheduleEvent(EVENT_JAGGED_KNIFE, 10s); - summons.DoZoneInCombat(); - } - - void UpdateAI(uint32 diff) override - { - if (!me->IsInCombat()) - scheduler.Update(diff); - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_UNBALANCING_STRIKE: - me->CastSpell(me->GetVictim(), SPELL_UNBALANCING_STRIKE, false); - events.Repeat(20s); - break; - case EVENT_DISRUPTING_SHOUT: - me->CastSpell(me, SPELL_DISRUPTING_SHOUT, false); - events.Repeat(15s); - break; - case EVENT_JAGGED_KNIFE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.0f)) - { - me->CastSpell(target, SPELL_JAGGED_KNIFE, false); - } - events.Repeat(10s); - break; - } - DoMeleeAttackIfReady(); - } - - private: - ObjectGuid _rpBuddyGUID; - }; -}; - -class boss_razuvious_minion : public CreatureScript -{ -public: - boss_razuvious_minion() : CreatureScript("boss_razuvious_minion") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetNaxxramasAI(creature); - } - - struct boss_razuvious_minionAI : public ScriptedAI - { - explicit boss_razuvious_minionAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override - { - scheduler.CancelAll(); - ScheduleAttackDummy(); - } - - void ScheduleAttackDummy() - { - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H); - if (Creature* targetDummy = me->FindNearestCreature(NPC_TARGET_DUMMY, 10.0f)) - { - me->SetFacingToObject(targetDummy); - } - scheduler.Schedule(6s, 9s, GROUP_OOC_RP, [this](TaskContext context) - { - me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK1H); - context.Repeat(6s, 9s); - }); - } - - void DoAction(int32 action) override - { - switch (action) - { - case ACTION_FACE_ME: - { - scheduler.CancelGroup(GROUP_OOC_RP); - me->SetSheath(SHEATH_STATE_UNARMED); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); - - if (Creature* creature = me->GetInstanceScript()->GetCreature(DATA_RAZUVIOUS_BOSS)) - me->SetFacingToObject(creature); - - break; - } - case ACTION_TALK: - Talk(SAY_DEATH_KNIGHT_UNDERSTUDY); - break; - case ACTION_EMOTE: - me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); - break; - case ACTION_SALUTE: - me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); - break; - case ACTION_BACK_TO_TRAINING: - me->SetSheath(SHEATH_STATE_MELEE); - ScheduleAttackDummy(); - break; - } - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustEngagedWith(Unit* who) override - { - scheduler.CancelGroup(GROUP_OOC_RP); - - if (Creature* creature = me->GetInstanceScript()->GetCreature(DATA_RAZUVIOUS_BOSS)) - { - creature->SetInCombatWithZone(); - creature->AI()->AttackStart(who); - } - } - - void UpdateAI(uint32 diff) override - { - scheduler.Update(diff); - - if (UpdateVictim()) - { - if (!me->HasUnitState(UNIT_STATE_CASTING) || !me->IsCharmed()) - { - DoMeleeAttackIfReady(); - } - } - } - }; -}; - -} -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp index 5e28fbd8f..50c51861e 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp @@ -15,7 +15,6 @@ * with this program. If not, see . */ -#include "boss_sapphiron.h" #include "CreatureScript.h" #include "Player.h" #include "ScriptedCreature.h" @@ -23,11 +22,423 @@ #include "SpellScriptLoader.h" #include "naxxramas.h" +enum Yells +{ + EMOTE_AIR_PHASE = 0, + EMOTE_GROUND_PHASE = 1, + EMOTE_BREATH = 2, + EMOTE_ENRAGE = 3 +}; -using namespace Sapphiron; +enum Spells +{ + // Fight + SPELL_FROST_AURA = 28531, + SPELL_CLEAVE = 19983, + SPELL_TAIL_SWEEP = 55697, + SPELL_SUMMON_BLIZZARD = 28560, + SPELL_LIFE_DRAIN = 28542, + SPELL_BERSERK = 26662, -// no custom changes has been made for mod-playerbot other then placing -// the impl in a header file + // Ice block + SPELL_ICEBOLT_CAST = 28526, + SPELL_ICEBOLT_TRIGGER = 28522, + SPELL_FROST_MISSILE = 30101, + SPELL_FROST_EXPLOSION = 28524, + + // Visuals + SPELL_SAPPHIRON_DIES = 29357 +}; + +enum Misc +{ + GO_ICE_BLOCK = 181247, + NPC_BLIZZARD = 16474, + + POINT_CENTER = 1 +}; + +enum Events +{ + EVENT_BERSERK = 1, + EVENT_CLEAVE = 2, + EVENT_TAIL_SWEEP = 3, + EVENT_LIFE_DRAIN = 4, + EVENT_BLIZZARD = 5, + EVENT_FLIGHT_START = 6, + EVENT_FLIGHT_LIFTOFF = 7, + EVENT_FLIGHT_ICEBOLT = 8, + EVENT_FLIGHT_BREATH = 9, + EVENT_FLIGHT_SPELL_EXPLOSION = 10, + EVENT_FLIGHT_START_LAND = 11, + EVENT_LAND = 12, + EVENT_GROUND = 13, + EVENT_HUNDRED_CLUB = 14 +}; + +class boss_sapphiron : public CreatureScript +{ +public: + boss_sapphiron() : CreatureScript("boss_sapphiron") { } + + CreatureAI* GetAI(Creature* pCreature) const override + { + return GetNaxxramasAI(pCreature); + } + + struct boss_sapphironAI : public BossAI + { + explicit boss_sapphironAI(Creature* c) : BossAI(c, BOSS_SAPPHIRON) + {} + + EventMap events; + uint8 iceboltCount{}; + uint32 spawnTimer{}; + GuidList blockList; + ObjectGuid currentTarget; + + void InitializeAI() override + { + me->SummonGameObject(GO_SAPPHIRON_BIRTH, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, 0, 0, 0, 0, 0); + me->SetVisible(false); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_PASSIVE); + ScriptedAI::InitializeAI(); + } + + bool IsInRoom() + { + if (me->GetExactDist(3523.5f, -5235.3f, 137.6f) > 100.0f) + { + EnterEvadeMode(); + return false; + } + return true; + } + + void Reset() override + { + BossAI::Reset(); + if (me->IsVisible()) + { + me->SetReactState(REACT_AGGRESSIVE); + } + events.Reset(); + iceboltCount = 0; + spawnTimer = 0; + currentTarget.Clear(); + blockList.clear(); + } + + void EnterCombatSelfFunction() + { + Map::PlayerList const& PlList = me->GetMap()->GetPlayers(); + if (PlList.IsEmpty()) + return; + + for (auto const& i : PlList) + { + if (Player* player = i.GetSource()) + { + if (player->IsGameMaster()) + continue; + + if (player->IsAlive() && me->GetDistance(player) < 80.0f) + { + me->SetInCombatWith(player); + player->SetInCombatWith(me); + me->AddThreat(player, 0.0f); + } + } + } + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + EnterCombatSelfFunction(); + me->CastSpell(me, SPELL_FROST_AURA, true); + events.ScheduleEvent(EVENT_BERSERK, 15min); + events.ScheduleEvent(EVENT_CLEAVE, 5s); + events.ScheduleEvent(EVENT_TAIL_SWEEP, 10s); + events.ScheduleEvent(EVENT_LIFE_DRAIN, 17s); + events.ScheduleEvent(EVENT_BLIZZARD, 17s); + events.ScheduleEvent(EVENT_FLIGHT_START, 45s); + events.ScheduleEvent(EVENT_HUNDRED_CLUB, 5s); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + me->CastSpell(me, SPELL_SAPPHIRON_DIES, true); + } + + void DoAction(int32 param) override + { + if (param == ACTION_SAPPHIRON_BIRTH) + { + spawnTimer = 1; + } + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE && id == POINT_CENTER) + { + events.ScheduleEvent(EVENT_FLIGHT_LIFTOFF, 500ms); + } + } + + void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override + { + if (spellInfo->Id == SPELL_ICEBOLT_CAST) + { + me->CastSpell(target, SPELL_ICEBOLT_TRIGGER, true); + } + } + + bool IsValidExplosionTarget(WorldObject* target) + { + for (ObjectGuid const& guid : blockList) + { + if (target->GetGUID() == guid) + return false; + + if (Unit* block = ObjectAccessor::GetUnit(*me, guid)) + { + if (block->IsInBetween(me, target, 2.0f) && block->IsWithinDist(target, 10.0f)) + return false; + } + } + return true; + } + + void KilledUnit(Unit* who) override + { + if (who->IsPlayer()) + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void UpdateAI(uint32 diff) override + { + if (spawnTimer) + { + spawnTimer += diff; + if (spawnTimer >= 21500) + { + me->SetVisible(true); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_AGGRESSIVE); + spawnTimer = 0; + } + return; + } + + if (!IsInRoom()) + return; + + if (!UpdateVictim()) + return; + + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (events.ExecuteEvent()) + { + case EVENT_BERSERK: + Talk(EMOTE_ENRAGE); + me->CastSpell(me, SPELL_BERSERK, true); + return; + case EVENT_CLEAVE: + me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false); + events.Repeat(10s); + return; + case EVENT_TAIL_SWEEP: + me->CastSpell(me, SPELL_TAIL_SWEEP, false); + events.Repeat(10s); + return; + case EVENT_LIFE_DRAIN: + me->CastCustomSpell(SPELL_LIFE_DRAIN, SPELLVALUE_MAX_TARGETS, RAID_MODE(2, 5), me, false); + events.Repeat(24s); + return; + case EVENT_BLIZZARD: + { + Creature* cr; + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true)) + { + cr = me->SummonCreature(NPC_BLIZZARD, *target, TEMPSUMMON_TIMED_DESPAWN, 16000); + } + else + { + cr = me->SummonCreature(NPC_BLIZZARD, *me, TEMPSUMMON_TIMED_DESPAWN, 16000); + } + if (cr) + { + cr->GetMotionMaster()->MoveRandom(40); + } + events.Repeat(RAID_MODE(8000ms, 6500ms)); + return; + } + case EVENT_FLIGHT_START: + if (me->HealthBelowPct(11)) + { + return; + } + events.Repeat(45s); + events.DelayEvents(35s); + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + float x, y, z, o; + me->GetHomePosition(x, y, z, o); + me->GetMotionMaster()->MovePoint(POINT_CENTER, x, y, z); + return; + case EVENT_FLIGHT_LIFTOFF: + Talk(EMOTE_AIR_PHASE); + me->GetMotionMaster()->MoveIdle(); + me->SendMeleeAttackStop(me->GetVictim()); + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + me->SetDisableGravity(true); + currentTarget.Clear(); + events.ScheduleEvent(EVENT_FLIGHT_ICEBOLT, 3s); + iceboltCount = RAID_MODE(2, 3); + return; + case EVENT_FLIGHT_ICEBOLT: + { + if (currentTarget) + { + if (Unit* target = ObjectAccessor::GetUnit(*me, currentTarget)) + { + me->SummonGameObject(GO_ICE_BLOCK, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0); + } + } + + std::vector targets; + auto i = me->GetThreatMgr().GetThreatList().begin(); + for (; i != me->GetThreatMgr().GetThreatList().end(); ++i) + { + if ((*i)->getTarget()->IsPlayer()) + { + bool inList = false; + if (!blockList.empty()) + { + for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr) + { + if ((*i)->getTarget()->GetGUID() == *itr) + { + inList = true; + break; + } + } + } + if (!inList) + { + targets.push_back((*i)->getTarget()); + } + } + } + + if (!targets.empty() && iceboltCount) + { + auto itr = targets.begin(); + advance(itr, urand(0, targets.size() - 1)); + me->CastSpell(*itr, SPELL_ICEBOLT_CAST, false); + blockList.push_back((*itr)->GetGUID()); + currentTarget = (*itr)->GetGUID(); + --iceboltCount; + events.ScheduleEvent(EVENT_FLIGHT_ICEBOLT, Seconds(uint32(me->GetExactDist(*itr) / 13.0f))); + } + else + { + events.ScheduleEvent(EVENT_FLIGHT_BREATH, 1s); + } + return; + } + case EVENT_FLIGHT_BREATH: + currentTarget.Clear(); + Talk(EMOTE_BREATH); + me->CastSpell(me, SPELL_FROST_MISSILE, false); + events.ScheduleEvent(EVENT_FLIGHT_SPELL_EXPLOSION, 8500ms); + return; + case EVENT_FLIGHT_SPELL_EXPLOSION: + me->CastSpell(me, SPELL_FROST_EXPLOSION, true); + events.ScheduleEvent(EVENT_FLIGHT_START_LAND, 3s); + return; + case EVENT_FLIGHT_START_LAND: + if (!blockList.empty()) + { + for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr) + { + if (Unit* block = ObjectAccessor::GetUnit(*me, *itr)) + { + block->RemoveAurasDueToSpell(SPELL_ICEBOLT_TRIGGER); + } + } + } + blockList.clear(); + me->RemoveAllGameObjects(); + events.ScheduleEvent(EVENT_LAND, 1s); + return; + case EVENT_LAND: + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + me->SetDisableGravity(false); + events.ScheduleEvent(EVENT_GROUND, 1500ms); + return; + case EVENT_GROUND: + Talk(EMOTE_GROUND_PHASE); + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + return; + case EVENT_HUNDRED_CLUB: + { + Map::PlayerList const& pList = me->GetMap()->GetPlayers(); + for (auto const& itr : pList) + { + if (itr.GetSource()->GetResistance(SPELL_SCHOOL_FROST) > 100) + { + instance->SetData(DATA_HUNDRED_CLUB, 0); + return; + } + } + events.Repeat(5s); + return; + } + } + DoMeleeAttackIfReady(); + } + }; +}; + +class spell_sapphiron_frost_explosion : public SpellScript +{ + PrepareSpellScript(spell_sapphiron_frost_explosion); + + void FilterTargets(std::list& targets) + { + Unit* caster = GetCaster(); + if (!caster || !caster->ToCreature()) + return; + + std::list tmplist; + for (auto& target : targets) + { + if (CAST_AI(boss_sapphiron::boss_sapphironAI, caster->ToCreature()->AI())->IsValidExplosionTarget(target)) + { + tmplist.push_back(target); + } + } + targets.clear(); + for (auto& itr : tmplist) + { + targets.push_back(itr); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sapphiron_frost_explosion::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY); + } +}; void AddSC_boss_sapphiron() { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.h b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.h deleted file mode 100644 index c26180087..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.h +++ /dev/null @@ -1,433 +0,0 @@ -#ifndef BOSS_SAPPHIRON_H_ -#define BOSS_SAPPHIRON_H_ - -#include "Player.h" -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellScript.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Sapphiron { - -enum Yells -{ - EMOTE_AIR_PHASE = 0, - EMOTE_GROUND_PHASE = 1, - EMOTE_BREATH = 2, - EMOTE_ENRAGE = 3 -}; - -enum Spells -{ - // Fight - SPELL_FROST_AURA = 28531, - SPELL_CLEAVE = 19983, - SPELL_TAIL_SWEEP = 55697, - SPELL_SUMMON_BLIZZARD = 28560, - SPELL_LIFE_DRAIN = 28542, - SPELL_BERSERK = 26662, - - // Ice block - SPELL_ICEBOLT_CAST = 28526, - SPELL_ICEBOLT_TRIGGER = 28522, - SPELL_FROST_MISSILE = 30101, - SPELL_FROST_EXPLOSION = 28524, - - // Visuals - SPELL_SAPPHIRON_DIES = 29357 -}; - -enum Misc -{ - GO_ICE_BLOCK = 181247, - NPC_BLIZZARD = 16474, - - POINT_CENTER = 1 -}; - -enum Events -{ - EVENT_BERSERK = 1, - EVENT_CLEAVE = 2, - EVENT_TAIL_SWEEP = 3, - EVENT_LIFE_DRAIN = 4, - EVENT_BLIZZARD = 5, - EVENT_FLIGHT_START = 6, - EVENT_FLIGHT_LIFTOFF = 7, - EVENT_FLIGHT_ICEBOLT = 8, - EVENT_FLIGHT_BREATH = 9, - EVENT_FLIGHT_SPELL_EXPLOSION = 10, - EVENT_FLIGHT_START_LAND = 11, - EVENT_LAND = 12, - EVENT_GROUND = 13, - EVENT_HUNDRED_CLUB = 14 -}; - -class boss_sapphiron : public CreatureScript -{ -public: - boss_sapphiron() : CreatureScript("boss_sapphiron") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_sapphironAI : public BossAI - { - explicit boss_sapphironAI(Creature* c) : BossAI(c, BOSS_SAPPHIRON) - {} - - EventMap events; - uint8 iceboltCount{}; - uint32 spawnTimer{}; - GuidList blockList; - ObjectGuid currentTarget; - - void InitializeAI() override - { - me->SummonGameObject(GO_SAPPHIRON_BIRTH, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, 0, 0, 0, 0, 0); - me->SetVisible(false); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->SetReactState(REACT_PASSIVE); - ScriptedAI::InitializeAI(); - } - - bool IsInRoom() - { - if (me->GetExactDist(3523.5f, -5235.3f, 137.6f) > 100.0f) - { - EnterEvadeMode(); - return false; - } - return true; - } - - void Reset() override - { - BossAI::Reset(); - if (me->IsVisible()) - { - me->SetReactState(REACT_AGGRESSIVE); - } - events.Reset(); - iceboltCount = 0; - spawnTimer = 0; - currentTarget.Clear(); - blockList.clear(); - } - - void EnterCombatSelfFunction() - { - Map::PlayerList const& PlList = me->GetMap()->GetPlayers(); - if (PlList.IsEmpty()) - return; - - for (auto const& i : PlList) - { - if (Player* player = i.GetSource()) - { - if (player->IsGameMaster()) - continue; - - if (player->IsAlive() && me->GetDistance(player) < 80.0f) - { - me->SetInCombatWith(player); - player->SetInCombatWith(me); - me->AddThreat(player, 0.0f); - } - } - } - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - EnterCombatSelfFunction(); - me->CastSpell(me, SPELL_FROST_AURA, true); - events.ScheduleEvent(EVENT_BERSERK, 15min); - events.ScheduleEvent(EVENT_CLEAVE, 5s); - events.ScheduleEvent(EVENT_TAIL_SWEEP, 10s); - events.ScheduleEvent(EVENT_LIFE_DRAIN, 17s); - events.ScheduleEvent(EVENT_BLIZZARD, 17s); - events.ScheduleEvent(EVENT_FLIGHT_START, 45s); - events.ScheduleEvent(EVENT_HUNDRED_CLUB, 5s); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - me->CastSpell(me, SPELL_SAPPHIRON_DIES, true); - } - - void DoAction(int32 param) override - { - if (param == ACTION_SAPPHIRON_BIRTH) - { - spawnTimer = 1; - } - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type == POINT_MOTION_TYPE && id == POINT_CENTER) - { - events.ScheduleEvent(EVENT_FLIGHT_LIFTOFF, 500ms); - } - } - - void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override - { - if (spellInfo->Id == SPELL_ICEBOLT_CAST) - { - me->CastSpell(target, SPELL_ICEBOLT_TRIGGER, true); - } - } - - bool IsValidExplosionTarget(WorldObject* target) - { - for (ObjectGuid const& guid : blockList) - { - if (target->GetGUID() == guid) - return false; - - if (Unit* block = ObjectAccessor::GetUnit(*me, guid)) - { - if (block->IsInBetween(me, target, 2.0f) && block->IsWithinDist(target, 10.0f)) - return false; - } - } - return true; - } - - void KilledUnit(Unit* who) override - { - if (who->IsPlayer()) - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void UpdateAI(uint32 diff) override - { - if (spawnTimer) - { - spawnTimer += diff; - if (spawnTimer >= 21500) - { - me->SetVisible(true); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->SetReactState(REACT_AGGRESSIVE); - spawnTimer = 0; - } - return; - } - - if (!IsInRoom()) - return; - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_BERSERK: - Talk(EMOTE_ENRAGE); - me->CastSpell(me, SPELL_BERSERK, true); - return; - case EVENT_CLEAVE: - me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false); - events.Repeat(10s); - return; - case EVENT_TAIL_SWEEP: - me->CastSpell(me, SPELL_TAIL_SWEEP, false); - events.Repeat(10s); - return; - case EVENT_LIFE_DRAIN: - me->CastCustomSpell(SPELL_LIFE_DRAIN, SPELLVALUE_MAX_TARGETS, RAID_MODE(2, 5), me, false); - events.Repeat(24s); - return; - case EVENT_BLIZZARD: - { - Creature* cr; - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true)) - { - cr = me->SummonCreature(NPC_BLIZZARD, *target, TEMPSUMMON_TIMED_DESPAWN, 16000); - } - else - { - cr = me->SummonCreature(NPC_BLIZZARD, *me, TEMPSUMMON_TIMED_DESPAWN, 16000); - } - if (cr) - { - cr->GetMotionMaster()->MoveRandom(40); - } - events.Repeat(RAID_MODE(8000ms, 6500ms)); - return; - } - case EVENT_FLIGHT_START: - if (me->HealthBelowPct(11)) - { - return; - } - events.Repeat(45s); - events.DelayEvents(35s); - me->SetReactState(REACT_PASSIVE); - me->AttackStop(); - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - me->GetMotionMaster()->MovePoint(POINT_CENTER, x, y, z); - return; - case EVENT_FLIGHT_LIFTOFF: - Talk(EMOTE_AIR_PHASE); - me->GetMotionMaster()->MoveIdle(); - me->SendMeleeAttackStop(me->GetVictim()); - me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); - me->SetDisableGravity(true); - currentTarget.Clear(); - events.ScheduleEvent(EVENT_FLIGHT_ICEBOLT, 3s); - iceboltCount = RAID_MODE(2, 3); - return; - case EVENT_FLIGHT_ICEBOLT: - { - if (currentTarget) - { - if (Unit* target = ObjectAccessor::GetUnit(*me, currentTarget)) - { - me->SummonGameObject(GO_ICE_BLOCK, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0); - } - } - - std::vector targets; - auto i = me->GetThreatMgr().GetThreatList().begin(); - for (; i != me->GetThreatMgr().GetThreatList().end(); ++i) - { - if ((*i)->getTarget()->IsPlayer()) - { - bool inList = false; - if (!blockList.empty()) - { - for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr) - { - if ((*i)->getTarget()->GetGUID() == *itr) - { - inList = true; - break; - } - } - } - if (!inList) - { - targets.push_back((*i)->getTarget()); - } - } - } - - if (!targets.empty() && iceboltCount) - { - auto itr = targets.begin(); - advance(itr, urand(0, targets.size() - 1)); - me->CastSpell(*itr, SPELL_ICEBOLT_CAST, false); - blockList.push_back((*itr)->GetGUID()); - currentTarget = (*itr)->GetGUID(); - --iceboltCount; - events.ScheduleEvent(EVENT_FLIGHT_ICEBOLT, Seconds(uint32(me->GetExactDist(*itr) / 13.0f))); - } - else - { - events.ScheduleEvent(EVENT_FLIGHT_BREATH, 1s); - } - return; - } - case EVENT_FLIGHT_BREATH: - currentTarget.Clear(); - Talk(EMOTE_BREATH); - me->CastSpell(me, SPELL_FROST_MISSILE, false); - events.ScheduleEvent(EVENT_FLIGHT_SPELL_EXPLOSION, 8500ms); - return; - case EVENT_FLIGHT_SPELL_EXPLOSION: - me->CastSpell(me, SPELL_FROST_EXPLOSION, true); - events.ScheduleEvent(EVENT_FLIGHT_START_LAND, 3s); - return; - case EVENT_FLIGHT_START_LAND: - if (!blockList.empty()) - { - for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr) - { - if (Unit* block = ObjectAccessor::GetUnit(*me, *itr)) - { - block->RemoveAurasDueToSpell(SPELL_ICEBOLT_TRIGGER); - } - } - } - blockList.clear(); - me->RemoveAllGameObjects(); - events.ScheduleEvent(EVENT_LAND, 1s); - return; - case EVENT_LAND: - me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); - me->SetDisableGravity(false); - events.ScheduleEvent(EVENT_GROUND, 1500ms); - return; - case EVENT_GROUND: - Talk(EMOTE_GROUND_PHASE); - me->SetReactState(REACT_AGGRESSIVE); - me->SetInCombatWithZone(); - return; - case EVENT_HUNDRED_CLUB: - { - Map::PlayerList const& pList = me->GetMap()->GetPlayers(); - for (auto const& itr : pList) - { - if (itr.GetSource()->GetResistance(SPELL_SCHOOL_FROST) > 100) - { - instance->SetData(DATA_HUNDRED_CLUB, 0); - return; - } - } - events.Repeat(5s); - return; - } - } - DoMeleeAttackIfReady(); - } - }; -}; - -class spell_sapphiron_frost_explosion : public SpellScript -{ - PrepareSpellScript(spell_sapphiron_frost_explosion); - - void FilterTargets(std::list& targets) - { - Unit* caster = GetCaster(); - if (!caster || !caster->ToCreature()) - return; - - std::list tmplist; - for (auto& target : targets) - { - if (CAST_AI(boss_sapphiron::boss_sapphironAI, caster->ToCreature()->AI())->IsValidExplosionTarget(target)) - { - tmplist.push_back(target); - } - } - targets.clear(); - for (auto& itr : tmplist) - { - targets.push_back(itr); - } - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sapphiron_frost_explosion::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY); - } -}; - -} - -#endif \ No newline at end of file diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.h b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.h deleted file mode 100644 index 0acf9e171..000000000 --- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.h +++ /dev/null @@ -1,690 +0,0 @@ -#ifndef BOSS_THADDIUS_H_ -#define BOSS_THADDIUS_H_ - -#include "Player.h" -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "SpellScript.h" -#include "SpellInfo.h" -#include "naxxramas.h" - -namespace Thaddius { - -enum Says -{ - // Stalagg - SAY_STAL_AGGRO = 0, - SAY_STAL_SLAY = 1, - SAY_STAL_DEATH = 2, - EMOTE_STAL_DEATH = 3, - EMOTE_STAL_REVIVE = 4, - - // Feugen - SAY_FEUG_AGGRO = 0, - SAY_FEUG_SLAY = 1, - SAY_FEUG_DEATH = 2, - EMOTE_FEUG_DEATH = 3, - EMOTE_FEUG_REVIVE = 4, - - // Thaddius - SAY_GREET = 0, - SAY_AGGRO = 1, - SAY_SLAY = 2, - SAY_ELECT = 3, - SAY_DEATH = 4, - EMOTE_POLARITY_SHIFTED = 6, - - // Tesla Coil - EMOTE_TESLA_LINK_BREAKS = 0, - EMOTE_TESLA_OVERLOAD = 1 -}; - -enum Spells -{ - SPELL_MAGNETIC_PULL = 28337, - SPELL_TESLA_SHOCK = 28099, - SPELL_SHOCK_VISUAL = 28159, - - // Stalagg - SPELL_POWER_SURGE = 54529, - SPELL_STALAGG_CHAIN = 28096, - - // Feugen - SPELL_STATIC_FIELD = 28135, - SPELL_FEUGEN_CHAIN = 28111, - - // Thaddius - SPELL_POLARITY_SHIFT = 28089, - SPELL_BALL_LIGHTNING = 28299, - SPELL_CHAIN_LIGHTNING = 28167, - SPELL_BERSERK = 27680, - SPELL_THADDIUS_VISUAL_LIGHTNING = 28136, - SPELL_THADDIUS_SPAWN_STUN = 28160, - - SPELL_POSITIVE_CHARGE = 28062, - SPELL_POSITIVE_CHARGE_STACK = 29659, - SPELL_NEGATIVE_CHARGE = 28085, - SPELL_NEGATIVE_CHARGE_STACK = 29660, - SPELL_POSITIVE_POLARITY = 28059, - SPELL_NEGATIVE_POLARITY = 28084 -}; - -enum Events -{ - EVENT_MINION_POWER_SURGE = 1, - EVENT_MINION_MAGNETIC_PULL = 2, - EVENT_MINION_CHECK_DISTANCE = 3, - EVENT_MINION_STATIC_FIELD = 4, - - EVENT_THADDIUS_INIT = 5, - EVENT_THADDIUS_ENTER_COMBAT = 6, - EVENT_THADDIUS_CHAIN_LIGHTNING = 7, - EVENT_THADDIUS_BERSERK = 8, - EVENT_THADDIUS_POLARITY_SHIFT = 9, - EVENT_ALLOW_BALL_LIGHTNING = 10 -}; - -enum Misc -{ - ACTION_MAGNETIC_PULL = 1, - ACTION_SUMMON_DIED = 2, - ACTION_RESTORE = 3, - GO_TESLA_COIL_LEFT = 181478, - GO_TESLA_COIL_RIGHT = 181477, - NPC_TESLA_COIL = 16218 -}; - -class boss_thaddius : public CreatureScript -{ -public: - boss_thaddius() : CreatureScript("boss_thaddius") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_thaddiusAI : public BossAI - { - explicit boss_thaddiusAI(Creature* c) : BossAI(c, BOSS_THADDIUS), summons(me), ballLightningEnabled(false) - {} - - EventMap events; - SummonList summons; - uint32 summonTimer{}; - uint32 reviveTimer{}; - uint32 resetTimer{}; - bool ballLightningEnabled; - - void DoAction(int32 param) override - { - if (param == ACTION_SUMMON_DIED) - { - if (summonTimer) - { - summonTimer = 0; - reviveTimer = 1; - return; - } - summonTimer = 1; - } - } - - void Reset() override - { - BossAI::Reset(); - events.Reset(); - summons.DespawnAll(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->SetControlled(true, UNIT_STATE_ROOT); - summonTimer = 0; - reviveTimer = 0; - resetTimer = 1; - me->SetPosition(me->GetHomePosition()); - ballLightningEnabled = false; - - me->SummonCreature(NPC_STALAGG, 3450.45f, -2931.42f, 312.091f, 5.49779f); - me->SummonCreature(NPC_FEUGEN, 3508.14f, -2988.65f, 312.092f, 2.37365f); - if (Creature* cr = me->SummonCreature(NPC_TESLA_COIL, 3527.34f, -2951.56f, 318.75f, 0.0f)) - { - cr->RemoveAllAuras(); - cr->InterruptNonMeleeSpells(true); - cr->CastSpell(cr, SPELL_FEUGEN_CHAIN, false); - cr->SetDisableGravity(true); - cr->SetImmuneToPC(false); - cr->SetControlled(true, UNIT_STATE_ROOT); - } - if (Creature* cr = me->SummonCreature(NPC_TESLA_COIL, 3487.04f, -2911.68f, 318.75f, 0.0f)) - { - cr->RemoveAllAuras(); - cr->InterruptNonMeleeSpells(true); - cr->CastSpell(cr, SPELL_STALAGG_CHAIN, false); - cr->SetDisableGravity(true); - cr->SetImmuneToPC(false); - cr->SetControlled(true, UNIT_STATE_ROOT); - } - - if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_LEFT, 100.0f)) - go->SetGoState(GO_STATE_ACTIVE); - - if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_RIGHT, 100.0f)) - go->SetGoState(GO_STATE_ACTIVE); - - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK); - } - - void KilledUnit(Unit* who) override - { - if (!who->IsPlayer()) - return; - - Talk(SAY_SLAY); - instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void JustDied(Unit* killer) override - { - BossAI::JustDied(killer); - Talk(SAY_DEATH); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_POLARITY); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_STACK); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_POLARITY); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_STACK); - } - - void JustSummoned(Creature* cr) override - { - summons.Summon(cr); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - me->SetInCombatWithZone(); - summons.DoZoneInCombat(NPC_FEUGEN); - summons.DoZoneInCombat(NPC_STALAGG); - } - - void UpdateAI(uint32 diff) override - { - if (resetTimer) - { - resetTimer += diff; - if (resetTimer > 1000) - { - resetTimer = 0; - me->CastSpell(me, SPELL_THADDIUS_SPAWN_STUN, true); - } - return; - } - if (reviveTimer) - { - reviveTimer += diff; - if (reviveTimer >= 12000) - { - for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr) - { - if (Creature* cr = ObjectAccessor::GetCreature(*me, (*itr))) - { - if (cr->GetEntry() == NPC_TESLA_COIL) - { - cr->AI()->Talk(EMOTE_TESLA_OVERLOAD); - cr->CastSpell(me, SPELL_SHOCK_VISUAL, true); - } - } - } - reviveTimer = 0; - events.ScheduleEvent(EVENT_THADDIUS_INIT, 750ms); - } - return; - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - if (summonTimer) // Revive - { - summonTimer += diff; - if (summonTimer >= 5000) - { - summons.DoAction(ACTION_RESTORE); - summonTimer = 0; - } - } - - switch (events.ExecuteEvent()) - { - case EVENT_THADDIUS_INIT: - { - me->RemoveAllAuras(); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr) - { - if (Creature* cr = ObjectAccessor::GetCreature(*me, (*itr))) - { - if (cr->GetEntry() == NPC_TESLA_COIL) - { - Unit::Kill(cr, cr); - } - } - } - if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_LEFT, 100.0f)) - { - go->SetGoState(GO_STATE_READY); - } - if (GameObject* go = me->FindNearestGameObject(GO_TESLA_COIL_RIGHT, 100.0f)) - { - go->SetGoState(GO_STATE_READY); - } - me->CastSpell(me, SPELL_THADDIUS_VISUAL_LIGHTNING, true); - events.ScheduleEvent(EVENT_THADDIUS_ENTER_COMBAT, 1s); - break; - } - case EVENT_THADDIUS_ENTER_COMBAT: - Talk(SAY_AGGRO); - me->SetReactState(REACT_AGGRESSIVE); - me->SetControlled(false, UNIT_STATE_STUNNED); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->SetControlled(false, UNIT_STATE_ROOT); - events.ScheduleEvent(EVENT_THADDIUS_CHAIN_LIGHTNING, 14s); - events.ScheduleEvent(EVENT_THADDIUS_BERSERK, 6min); - events.ScheduleEvent(EVENT_THADDIUS_POLARITY_SHIFT, 20s); - events.ScheduleEvent(EVENT_ALLOW_BALL_LIGHTNING, 5s); - return; - case EVENT_THADDIUS_BERSERK: - me->CastSpell(me, SPELL_BERSERK, true); - break; - case EVENT_THADDIUS_CHAIN_LIGHTNING: - me->CastSpell(me->GetVictim(), SPELL_CHAIN_LIGHTNING, false); - events.Repeat(15s); - break; - case EVENT_THADDIUS_POLARITY_SHIFT: - me->CastSpell(me, SPELL_POLARITY_SHIFT, false); - events.Repeat(30s); - break; - case EVENT_ALLOW_BALL_LIGHTNING: - ballLightningEnabled = true; - break; - } - - if (me->IsWithinMeleeRange(me->GetVictim())) - { - DoMeleeAttackIfReady(); - } - else if (ballLightningEnabled) - { - if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat)) - { - me->CastSpell(target, SPELL_BALL_LIGHTNING, false); - } - } - } - }; -}; - -class boss_thaddius_summon : public CreatureScript -{ -public: - boss_thaddius_summon() : CreatureScript("boss_thaddius_summon") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetNaxxramasAI(pCreature); - } - - struct boss_thaddius_summonAI : public ScriptedAI - { - explicit boss_thaddius_summonAI(Creature* c) : ScriptedAI(c) - { - overload = false; - } - - EventMap events; - uint32 pullTimer{}; - uint32 visualTimer{}; - bool overload; - ObjectGuid myCoil; - - void Reset() override - { - pullTimer = 0; - visualTimer = 1; - overload = false; - events.Reset(); - me->SetControlled(false, UNIT_STATE_STUNNED); - if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f)) - { - cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false); - cr->SetImmuneToPC(false); - myCoil = cr->GetGUID(); - } - } - - void EnterEvadeMode(EvadeReason why) override - { - me->SetControlled(false, UNIT_STATE_STUNNED); - ScriptedAI::EnterEvadeMode(why); - } - - void JustEngagedWith(Unit* pWho) override - { - me->SetInCombatWithZone(); - if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.f, true)) - { - myCoil = cr->GetGUID(); - } - if (me->GetEntry() == NPC_STALAGG) - { - events.ScheduleEvent(EVENT_MINION_POWER_SURGE, 10s); - Talk(SAY_STAL_AGGRO); - } - else - { - events.ScheduleEvent(EVENT_MINION_STATIC_FIELD, 5s); - Talk(SAY_FEUG_AGGRO); - } - events.ScheduleEvent(EVENT_MINION_CHECK_DISTANCE, 5s); - - if (me->GetEntry() == NPC_STALAGG) // This event needs synchronisation, called for stalagg only - { - events.ScheduleEvent(EVENT_MINION_MAGNETIC_PULL, 20s); - } - - if (Creature* cr = me->GetInstanceScript()->GetCreature(DATA_THADDIUS_BOSS)) - { - cr->AI()->AttackStart(pWho); - cr->AddThreat(pWho, 10.0f); - } - } - - void DoAction(int32 param) override - { - if (param == ACTION_MAGNETIC_PULL) - { - pullTimer = 1; - me->SetControlled(true, UNIT_STATE_STUNNED); - } - else if (param == ACTION_RESTORE) - { - if (!me->IsAlive()) - { - me->Respawn(); - me->SetInCombatWithZone(); - Talk(me->GetEntry() == NPC_STALAGG ? EMOTE_STAL_REVIVE : EMOTE_FEUG_REVIVE); - } - else - { - me->SetHealth(me->GetMaxHealth()); - } - } - } - - void JustDied(Unit* /*killer*/) override - { - Talk(me->GetEntry() == NPC_STALAGG ? SAY_STAL_DEATH : SAY_FEUG_DEATH); - Talk(me->GetEntry() == NPC_STALAGG ? EMOTE_STAL_DEATH : EMOTE_FEUG_DEATH); - - if (Creature* cr = me->GetInstanceScript()->GetCreature(DATA_THADDIUS_BOSS)) - cr->AI()->DoAction(ACTION_SUMMON_DIED); - } - - void KilledUnit(Unit* who) override - { - if (!who->IsPlayer()) - return; - - if (!urand(0, 2)) - Talk(me->GetEntry() == NPC_STALAGG ? SAY_STAL_SLAY : SAY_FEUG_SLAY); - - me->GetInstanceScript()->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); - } - - void UpdateAI(uint32 diff) override - { - if (visualTimer) - { - visualTimer += diff; - if (visualTimer >= 3000) - { - visualTimer = 0; - if (Creature* cr = me->FindNearestCreature(NPC_TESLA_COIL, 150.0f)) - { - cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false); - } - } - } - - if (!UpdateVictim()) - return; - - if (pullTimer) // Disable AI during pull - { - pullTimer += diff; - if (pullTimer >= 3000) - { - me->SetControlled(false, UNIT_STATE_STUNNED); - pullTimer = 0; - } - return; - } - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_MINION_POWER_SURGE: - me->CastSpell(me, SPELL_POWER_SURGE, false); - events.Repeat(19s); - break; - case EVENT_MINION_STATIC_FIELD: - me->CastSpell(me, SPELL_STATIC_FIELD, false); - events.Repeat(3s); - break; - case EVENT_MINION_MAGNETIC_PULL: - { - events.Repeat(20s); - if (Creature* feugen = me->GetInstanceScript()->GetCreature(DATA_FEUGEN_BOSS)) - { - if (!feugen->IsAlive() || !feugen->GetVictim() || !me->GetVictim()) - return; - - float threatFeugen = feugen->GetThreatMgr().GetThreat(feugen->GetVictim()); - float threatStalagg = me->GetThreatMgr().GetThreat(me->GetVictim()); - Unit* tankFeugen = feugen->GetVictim(); - Unit* tankStalagg = me->GetVictim(); - - feugen->GetThreatMgr().ModifyThreatByPercent(tankFeugen, -100); - feugen->AddThreat(tankStalagg, threatFeugen); - feugen->CastSpell(tankStalagg, SPELL_MAGNETIC_PULL, true); - feugen->AI()->DoAction(ACTION_MAGNETIC_PULL); - - me->GetThreatMgr().ModifyThreatByPercent(tankStalagg, -100); - me->AddThreat(tankFeugen, threatStalagg); - me->CastSpell(tankFeugen, SPELL_MAGNETIC_PULL, true); - DoAction(ACTION_MAGNETIC_PULL); - } - break; - } - case EVENT_MINION_CHECK_DISTANCE: - if (Creature* cr = ObjectAccessor::GetCreature(*me, myCoil)) - { - if (!me->GetHomePosition().IsInDist(me, 28) && me->IsInCombat()) - { - if (!overload) - { - overload = true; - cr->AI()->Talk(EMOTE_TESLA_LINK_BREAKS); - me->RemoveAurasDueToSpell(me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN); - cr->InterruptNonMeleeSpells(true); - } - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 1000.f, true)) - { - cr->CastStop(SPELL_TESLA_SHOCK); - cr->CastSpell(target, SPELL_TESLA_SHOCK, true); - } - events.Repeat(1500ms); - break; - } - else - { - overload = false; - cr->CastSpell(cr, me->GetEntry() == NPC_STALAGG ? SPELL_STALAGG_CHAIN : SPELL_FEUGEN_CHAIN, false); - } - } - events.Repeat(5s); - break; - } - DoMeleeAttackIfReady(); - } - }; -}; - -class spell_thaddius_pos_neg_charge : public SpellScript -{ - PrepareSpellScript(spell_thaddius_pos_neg_charge); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_POSITIVE_CHARGE, SPELL_POSITIVE_CHARGE_STACK }); - } - - void HandleTargets(std::list& targets) - { - uint8 count = 0; - for (auto& ihit : targets) - { - if (ihit->GetGUID() != GetCaster()->GetGUID()) - { - if (Player* target = ihit->ToPlayer()) - { - if (target->HasAura(GetTriggeringSpell()->Id)) - { - ++count; - } - } - } - } - - if (count) - { - uint32 spellId = GetSpellInfo()->Id == SPELL_POSITIVE_CHARGE ? SPELL_POSITIVE_CHARGE_STACK : SPELL_NEGATIVE_CHARGE_STACK; - GetCaster()->SetAuraStack(spellId, GetCaster(), count); - } - } - - void HandleDamage(SpellEffIndex /*effIndex*/) - { - if (!GetTriggeringSpell()) - return; - - Unit* target = GetHitUnit(); - if (!target) - return; - - if (target->HasAura(GetTriggeringSpell()->Id) || !target->IsPlayer()) - { - SetHitDamage(0); - } - else if (InstanceScript* instance = target->GetInstanceScript()) - { - instance->SetData(DATA_CHARGES_CROSSED, 0); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_thaddius_pos_neg_charge::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_pos_neg_charge::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); - } -}; - -class spell_thaddius_polarity_shift : public SpellScript -{ - PrepareSpellScript(spell_thaddius_polarity_shift); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_POSITIVE_POLARITY, SPELL_NEGATIVE_POLARITY }); - } - - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - if (Unit* target = GetHitUnit()) - { - target->RemoveAurasDueToSpell(SPELL_POSITIVE_CHARGE_STACK); - target->RemoveAurasDueToSpell(SPELL_NEGATIVE_CHARGE_STACK); - target->CastSpell(target, roll_chance_i(50) ? SPELL_POSITIVE_POLARITY : SPELL_NEGATIVE_POLARITY, true, nullptr, nullptr, caster->GetGUID()); - } - } - - void HandleAfterCast() - { - if (GetCaster()) - { - if (Creature* caster = GetCaster()->ToCreature()) - { - if (caster->GetEntry() == NPC_THADDIUS) - { - caster->AI()->Talk(SAY_ELECT); - caster->AI()->Talk(EMOTE_POLARITY_SHIFTED); - } - } - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_thaddius_polarity_shift::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - AfterCast += SpellCastFn(spell_thaddius_polarity_shift::HandleAfterCast); - } -}; - -class npc_tesla : public CreatureScript -{ -public: - npc_tesla() : CreatureScript("npc_tesla") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetNaxxramasAI(creature); - } - - struct npc_teslaAI : public ScriptedAI - { - public: - npc_teslaAI(Creature* creature) : ScriptedAI(creature) { } - void EnterEvadeMode(EvadeReason /*why*/) override { } // never stop casting due to evade - void UpdateAI(uint32 /*diff*/) override { } // never do anything unless told - void JustEngagedWith(Unit* /*who*/) override { } - void DamageTaken(Unit* /*who*/, uint32& damage, DamageEffectType, SpellSchoolMask) override { damage = 0; } // no, you can't kill it - }; -}; - -class at_thaddius_entrance : public OnlyOnceAreaTriggerScript -{ -public: - at_thaddius_entrance() : OnlyOnceAreaTriggerScript("at_thaddius_entrance") { } - - bool _OnTrigger(Player* player, const AreaTrigger* /*trigger*/) override - { - if (InstanceScript* instance = player->GetInstanceScript()) - if (instance->GetBossState(BOSS_THADDIUS) != DONE) - if (Creature* thaddius = instance->GetCreature(DATA_THADDIUS_BOSS)) - thaddius->AI()->Talk(SAY_GREET); - return false; - } -}; - -} - -#endif \ No newline at end of file