From 272da9fbaddee131f996f78001db889225755afd Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 11 Apr 2026 13:02:28 -0300 Subject: [PATCH] refactor(Scripts/VioletHold): Modernize Violet Hold dungeon scripts (#25187) Co-authored-by: joschiwald Co-authored-by: Claude Opus 4.6 (1M context) --- src/server/game/Maps/Map.cpp | 9 +- src/server/game/Maps/Map.h | 3 +- .../Northrend/VioletHold/boss_cyanigosa.cpp | 192 +-- .../Northrend/VioletHold/boss_erekem.cpp | 354 ++--- .../Northrend/VioletHold/boss_ichoron.cpp | 470 +++--- .../Northrend/VioletHold/boss_lavanthor.cpp | 114 +- .../Northrend/VioletHold/boss_moragg.cpp | 111 +- .../Northrend/VioletHold/boss_xevozz.cpp | 213 ++- .../Northrend/VioletHold/boss_zuramat.cpp | 310 ++-- .../VioletHold/instance_violet_hold.cpp | 768 +++++----- .../Northrend/VioletHold/violet_hold.cpp | 1324 ++++++++--------- .../Northrend/VioletHold/violet_hold.h | 99 +- 12 files changed, 1745 insertions(+), 2222 deletions(-) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 89cdd4bbf..3e37ef92a 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1810,12 +1810,13 @@ void Map::RemoveAllObjectsInRemoveList() } } -uint32 Map::GetPlayersCountExceptGMs() const +uint32 Map::GetPlayersCountExceptGMs(bool aliveOnly /*= false*/) const { uint32 count = 0; - for (MapRefMgr::const_iterator itr = m_mapRefMgr.begin(); itr != m_mapRefMgr.end(); ++itr) - if (!itr->GetSource()->IsGameMaster()) - ++count; + for (auto const& ref : m_mapRefMgr) + if (Player* player = ref.GetSource()) + if (!player->IsGameMaster() && (!aliveOnly || (player->IsAlive() && !player->HasSpiritOfRedemptionAura()))) + ++count; return count; } diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index e6027a717..bd3abd228 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -318,7 +318,8 @@ public: void markCell(uint32 pCellId) { marked_cells.set(pCellId); } [[nodiscard]] bool HavePlayers() const { return !m_mapRefMgr.IsEmpty(); } - [[nodiscard]] uint32 GetPlayersCountExceptGMs() const; + // When aliveOnly is true, counts only players that are alive and not in Spirit of Redemption form. + [[nodiscard]] uint32 GetPlayersCountExceptGMs(bool aliveOnly = false) const; void SendToPlayers(WorldPacket const* data) const; diff --git a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp index 34e07f1b5..5d7cd2a44 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_cyanigosa.cpp @@ -50,141 +50,95 @@ enum eEvents EVENT_UNROOT, }; -class boss_cyanigosa : public CreatureScript +struct boss_cyanigosa : public BossAI { -public: - boss_cyanigosa() : CreatureScript("boss_cyanigosa") { } + boss_cyanigosa(Creature* c) : BossAI(c, DATA_CYANIGOSA) { } - CreatureAI* GetAI(Creature* pCreature) const override + void JustEngagedWith(Unit* who) override { - return GetVioletHoldAI(pCreature); + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + events.RescheduleEvent(EVENT_SPELL_ARCANE_VACUUM, 30s); + events.RescheduleEvent(EVENT_SPELL_BLIZZARD, 5s, 10s); + events.RescheduleEvent(EVENT_SPELL_TAIL_SWEEP, 15s, 20s); + events.RescheduleEvent(EVENT_SPELL_UNCONTROLLABLE_ENERGY, 5s, 8s); + if (IsHeroic()) + events.RescheduleEvent(EVENT_SPELL_MANA_DESTRUCTION, 20s); } - struct boss_cyanigosaAI : public ScriptedAI + void SpellHitTarget(Unit* target, SpellInfo const* spell) override { - boss_cyanigosaAI(Creature* c) : ScriptedAI(c) + if (!target || !spell) + return; + if (spell->Id == SPELL_ARCANE_VACUUM) + target->NearTeleportTo(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 10.0f, target->GetOrientation()); + } + + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - pInstance = c->GetInstanceScript(); + case EVENT_SPELL_ARCANE_VACUUM: + DoCastAOE(SPELL_ARCANE_VACUUM); + DoResetThreatList(); + me->SetControlled(true, UNIT_STATE_ROOT); + me->setAttackTimer(BASE_ATTACK, 3000); + events.Repeat(30s); + events.ScheduleEvent(EVENT_UNROOT, 3s); + break; + case EVENT_UNROOT: + me->SetControlled(false, UNIT_STATE_ROOT); + break; + case EVENT_SPELL_BLIZZARD: + DoCastRandomTarget(SPELL_BLIZZARD, 0, 45.0f); + events.Repeat(15s); + break; + case EVENT_SPELL_MANA_DESTRUCTION: + DoCastRandomTarget(SPELL_MANA_DESTRUCTION, 0, 50.0f); + events.Repeat(20s); + break; + case EVENT_SPELL_TAIL_SWEEP: + DoCastVictim(SPELL_TAIL_SWEEP); + events.Repeat(15s, 20s); + break; + case EVENT_SPELL_UNCONTROLLABLE_ENERGY: + DoCastVictim(SPELL_UNCONTROLLABLE_ENERGY); + events.Repeat(20s, 25s); + break; } + } - InstanceScript* pInstance; - EventMap events; - - void Reset() override + void JustDied(Unit* killer) override + { + Talk(SAY_DEATH); + BossAI::JustDied(killer); + float h = me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); + if (h != INVALID_HEIGHT && me->GetPositionZ() - h > 3.0f) { - events.Reset(); + me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), h, me->GetOrientation(), true); + me->StopMovingOnCurrentPos(); + me->DestroyForVisiblePlayers(); } + } - void JustEngagedWith(Unit* /*who*/) override - { - DoZoneInCombat(); - Talk(SAY_AGGRO); - events.Reset(); - events.RescheduleEvent(EVENT_SPELL_ARCANE_VACUUM, 30s); - events.RescheduleEvent(EVENT_SPELL_BLIZZARD, 5s, 10s); - events.RescheduleEvent(EVENT_SPELL_TAIL_SWEEP, 15s, 20s); - events.RescheduleEvent(EVENT_SPELL_UNCONTROLLABLE_ENERGY, 5s, 8s); - if (IsHeroic()) - events.RescheduleEvent(EVENT_SPELL_MANA_DESTRUCTION, 20s); - } + void KilledUnit(Unit* victim) override + { + if (victim && victim->GetGUID() == me->GetGUID()) + return; + Talk(SAY_SLAY); + } - void SpellHitTarget(Unit* target, SpellInfo const* spell) override - { - if (!target || !spell) - return; - switch (spell->Id) - { - case SPELL_ARCANE_VACUUM: - target->NearTeleportTo(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 10.0f, target->GetOrientation()); - break; - } - } + void MoveInLineOfSight(Unit* /*who*/) override {} - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_ARCANE_VACUUM: - me->CastSpell((Unit*)nullptr, SPELL_ARCANE_VACUUM, false); - DoResetThreatList(); - me->SetControlled(true, UNIT_STATE_ROOT); - me->setAttackTimer(BASE_ATTACK, 3000); - events.Repeat(30s); - events.ScheduleEvent(EVENT_UNROOT, 3s); - break; - case EVENT_UNROOT: - me->SetControlled(false, UNIT_STATE_ROOT); - - break; - case EVENT_SPELL_BLIZZARD: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.0f, true)) - me->CastSpell(target, SPELL_BLIZZARD, false); - events.Repeat(15s); - break; - case EVENT_SPELL_MANA_DESTRUCTION: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) - me->CastSpell(target, SPELL_MANA_DESTRUCTION, false); - events.Repeat(20s); - break; - case EVENT_SPELL_TAIL_SWEEP: - me->CastSpell(me->GetVictim(), SPELL_TAIL_SWEEP, false); - events.Repeat(15s, 20s); - break; - case EVENT_SPELL_UNCONTROLLABLE_ENERGY: - me->CastSpell(me->GetVictim(), SPELL_UNCONTROLLABLE_ENERGY, false); - events.Repeat(20s, 25s); - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(DATA_BOSS_DIED, 0); - float h = me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); - if (h != INVALID_HEIGHT && me->GetPositionZ() - h > 3.0f) - { - me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), h, me->GetOrientation(), true); // move to ground - me->StopMovingOnCurrentPos(); - me->DestroyForVisiblePlayers(); - } - } - - void KilledUnit(Unit* victim) override - { - if (victim && victim->GetGUID() == me->GetGUID()) - return; - Talk(SAY_SLAY); - } - - void MoveInLineOfSight(Unit* /*who*/) override {} - - void EnterEvadeMode(EvadeReason why) override - { - me->SetControlled(false, UNIT_STATE_ROOT); - ScriptedAI::EnterEvadeMode(why); - events.Reset(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (pInstance) - pInstance->SetData(DATA_FAILED, 1); - } - }; + void EnterEvadeMode(EvadeReason why) override + { + me->SetControlled(false, UNIT_STATE_ROOT); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + _EnterEvadeMode(why); + } }; void AddSC_boss_cyanigosa() { - new boss_cyanigosa(); + RegisterVioletHoldCreatureAI(boss_cyanigosa); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp index 79e90041e..0dc259200 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_erekem.cpp @@ -25,8 +25,6 @@ enum eSpells SPELL_BREAK_BONDS = 59463, SPELL_CHAIN_HEAL = 54481, SPELL_EARTH_SHIELD = 54479, - //SPELL_EARTH_SHIELD_TRIGGERED_N = 54480, - //SPELL_EARTH_SHIELD_TRIGGERED_H = 59472, SPELL_EARTH_SHOCK = 54511, SPELL_LIGHTNING_BOLT = 53044, SPELL_STORMSTRIKE = 51876, @@ -53,164 +51,120 @@ enum eEvents EVENT_SPELL_STORMSTRIKE, }; -class boss_erekem : public CreatureScript +struct boss_erekem : public BossAI { -public: - boss_erekem() : CreatureScript("boss_erekem") { } + boss_erekem(Creature* c) : BossAI(c, BOSS_EREKEM) { } - CreatureAI* GetAI(Creature* pCreature) const override + void JustEngagedWith(Unit* who) override { - return GetVioletHoldAI(pCreature); + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + DoCastSelf(SPELL_EARTH_SHIELD); + events.RescheduleEvent(EVENT_SPELL_BLOODLUST, 15s); + events.RescheduleEvent(EVENT_SPELL_BREAK_BONDS, 9s, 14s); + events.RescheduleEvent(EVENT_SPELL_CHAIN_HEAL, 0ms); + events.RescheduleEvent(EVENT_SPELL_EARTH_SHIELD, 20s); + events.RescheduleEvent(EVENT_SPELL_EARTH_SHOCK, 2s, 8s); + events.RescheduleEvent(EVENT_SPELL_LIGHTNING_BOLT, 5s, 10s); + if (IsHeroic()) + events.RescheduleEvent(EVENT_SPELL_STORMSTRIKE, 3s); + + if (Creature* c = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_1_GUID))) + if (!c->IsInCombat()) + c->AI()->AttackStart(who); + if (Creature* c = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_2_GUID))) + if (!c->IsInCombat()) + c->AI()->AttackStart(who); } - struct boss_erekemAI : public ScriptedAI + void ExecuteEvent(uint32 eventId) override { - boss_erekemAI(Creature* c) : ScriptedAI(c) + switch (eventId) { - pInstance = c->GetInstanceScript(); - } + case EVENT_SPELL_BLOODLUST: + DoCastAOE(SPELL_BLOODLUST); + events.Repeat(35s, 45s); + break; + case EVENT_SPELL_BREAK_BONDS: + DoCastAOE(SPELL_BREAK_BONDS); + events.Repeat(16s, 22s); + break; + case EVENT_SPELL_CHAIN_HEAL: + if (ObjectGuid targetGuid = GetChainHealTargetGuid()) + { + if (Creature* target = instance->instance->GetCreature(targetGuid)) + DoCast(target, SPELL_CHAIN_HEAL); - InstanceScript* pInstance; - EventMap events; - - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* who) override - { - DoZoneInCombat(); - Talk(SAY_AGGRO); - DoCast(me, SPELL_EARTH_SHIELD); - events.Reset(); - events.RescheduleEvent(EVENT_SPELL_BLOODLUST, 15s); - events.RescheduleEvent(EVENT_SPELL_BREAK_BONDS, 9s, 14s); - events.RescheduleEvent(EVENT_SPELL_CHAIN_HEAL, 0ms); - events.RescheduleEvent(EVENT_SPELL_EARTH_SHIELD, 20s); - events.RescheduleEvent(EVENT_SPELL_EARTH_SHOCK, 2s, 8s); - events.RescheduleEvent(EVENT_SPELL_LIGHTNING_BOLT, 5s, 10s); - if (IsHeroic()) - events.RescheduleEvent(EVENT_SPELL_STORMSTRIKE, 3s); - - if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_1_GUID))) - if (!c->IsInCombat()) - c->AI()->AttackStart(who); - if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_2_GUID))) - if (!c->IsInCombat()) - c->AI()->AttackStart(who); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_BLOODLUST: - me->CastSpell((Unit*)nullptr, SPELL_BLOODLUST, false); - events.Repeat(35s, 45s); - break; - case EVENT_SPELL_BREAK_BONDS: - me->CastSpell((Unit*)nullptr, SPELL_BREAK_BONDS, false); - events.Repeat(16s, 22s); - break; - case EVENT_SPELL_CHAIN_HEAL: - if (ObjectGuid TargetGUID = GetChainHealTargetGUID()) - if (pInstance) - { - if (Creature* target = pInstance->instance->GetCreature(TargetGUID)) - me->CastSpell(target, SPELL_CHAIN_HEAL, false); - - Creature* pGuard1 = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_1_GUID)); - Creature* pGuard2 = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_2_GUID)); - if ((pGuard1 && !pGuard1->IsAlive()) || (pGuard2 && !pGuard2->IsAlive())) - { - events.Repeat(3s, 6s); - break; - } - } - events.Repeat(8s, 11s); - break; - case EVENT_SPELL_EARTH_SHIELD: - me->CastSpell(me, SPELL_EARTH_SHIELD, false); - events.Repeat(20s); - break; - case EVENT_SPELL_EARTH_SHOCK: - me->CastSpell(me->GetVictim(), SPELL_EARTH_SHOCK, false); - events.Repeat(8s, 13s); - break; - case EVENT_SPELL_LIGHTNING_BOLT: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 35.0f, true)) - me->CastSpell(target, SPELL_LIGHTNING_BOLT, false); - events.Repeat(15s, 25s); - break; - case EVENT_SPELL_STORMSTRIKE: + Creature* guard1 = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_1_GUID)); + Creature* guard2 = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_2_GUID)); + if ((guard1 && !guard1->IsAlive()) || (guard2 && !guard2->IsAlive())) { - Creature* pGuard1 = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_1_GUID)); - Creature* pGuard2 = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_2_GUID)); - if (pGuard1 && !pGuard1->IsAlive() && pGuard2 && !pGuard2->IsAlive()) // both dead - me->CastSpell(me->GetVictim(), SPELL_STORMSTRIKE, false); - events.Repeat(3s); + events.Repeat(3s, 6s); + break; } - break; - } - - DoMeleeAttackIfReady(); + } + events.Repeat(8s, 11s); + break; + case EVENT_SPELL_EARTH_SHIELD: + DoCastSelf(SPELL_EARTH_SHIELD); + events.Repeat(20s); + break; + case EVENT_SPELL_EARTH_SHOCK: + DoCastVictim(SPELL_EARTH_SHOCK); + events.Repeat(8s, 13s); + break; + case EVENT_SPELL_LIGHTNING_BOLT: + DoCastRandomTarget(SPELL_LIGHTNING_BOLT, 0, 35.0f); + events.Repeat(15s, 25s); + break; + case EVENT_SPELL_STORMSTRIKE: + { + Creature* guard1 = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_1_GUID)); + Creature* guard2 = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_2_GUID)); + if (guard1 && !guard1->IsAlive() && guard2 && !guard2->IsAlive()) + DoCastVictim(SPELL_STORMSTRIKE); + events.Repeat(3s); + } + break; } + } - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(DATA_BOSS_DIED, 0); - } + void JustDied(Unit* killer) override + { + Talk(SAY_DEATH); + BossAI::JustDied(killer); + } - void KilledUnit(Unit* victim) override - { - if (victim && victim->GetGUID() == me->GetGUID()) - return; - Talk(SAY_SLAY); - } + void KilledUnit(Unit* victim) override + { + if (victim && victim->GetGUID() == me->GetGUID()) + return; + Talk(SAY_SLAY); + } - void MoveInLineOfSight(Unit* /*who*/) override {} + void MoveInLineOfSight(Unit* /*who*/) override {} - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - events.Reset(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (pInstance) - pInstance->SetData(DATA_FAILED, 1); - } - - ObjectGuid GetChainHealTargetGUID() - { - if (HealthBelowPct(85)) - return me->GetGUID(); - - if (pInstance) - { - if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_1_GUID))) - if (c->IsAlive() && !c->HealthAbovePct(75)) - return c->GetGUID(); - - if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUARD_2_GUID))) - if (c->IsAlive() && !c->HealthAbovePct(75)) - return c->GetGUID(); - } + void EnterEvadeMode(EvadeReason why) override + { + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + _EnterEvadeMode(why); + } + ObjectGuid GetChainHealTargetGuid() + { + if (HealthBelowPct(85)) return me->GetGUID(); - } - }; + + if (Creature* c = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_1_GUID))) + if (c->IsAlive() && !c->HealthAbovePct(75)) + return c->GetGUID(); + + if (Creature* c = instance->instance->GetCreature(instance->GetGuidData(DATA_EREKEM_GUARD_2_GUID))) + if (c->IsAlive() && !c->HealthAbovePct(75)) + return c->GetGUID(); + + return me->GetGUID(); + } }; enum GuardSpells @@ -227,81 +181,69 @@ enum eGuardEvents EVENT_SPELL_STRIKE }; -class npc_erekem_guard : public CreatureScript +struct npc_erekem_guard : public ScriptedAI { -public: - npc_erekem_guard() : CreatureScript("npc_erekem_guard") { } - - CreatureAI* GetAI(Creature* pCreature) const override + npc_erekem_guard(Creature* c) : ScriptedAI(c) { - return GetVioletHoldAI(pCreature); + _instance = c->GetInstanceScript(); } - struct npc_erekem_guardAI : public ScriptedAI + void Reset() override { - npc_erekem_guardAI(Creature* c) : ScriptedAI(c) + _events.Reset(); + } + + void JustEngagedWith(Unit* who) override + { + DoZoneInCombat(); + _events.Reset(); + _events.RescheduleEvent(EVENT_SPELL_GUSHING_WOUND, 1s, 3s); + _events.RescheduleEvent(EVENT_SPELL_HOWLING_SCREECH, 8s, 13s); + _events.RescheduleEvent(EVENT_SPELL_STRIKE, 4s, 8s); + + if (Creature* c = _instance->GetCreature(BOSS_EREKEM)) + if (!c->IsInCombat()) + c->AI()->AttackStart(who); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + switch (_events.ExecuteEvent()) { - pInstance = c->GetInstanceScript(); + case EVENT_SPELL_GUSHING_WOUND: + DoCastVictim(SPELL_GUSHING_WOUND); + _events.Repeat(7s, 12s); + break; + case EVENT_SPELL_HOWLING_SCREECH: + DoCastVictim(SPELL_HOWLING_SCREECH); + _events.Repeat(8s, 13s); + break; + case EVENT_SPELL_STRIKE: + DoCastVictim(SPELL_STRIKE); + _events.Repeat(4s, 8s); + break; } - InstanceScript* pInstance; - EventMap events; + DoMeleeAttackIfReady(); + } - void Reset() override - { - events.Reset(); - } + void MoveInLineOfSight(Unit* /*who*/) override {} - void JustEngagedWith(Unit* who) override - { - DoZoneInCombat(); - events.Reset(); - events.RescheduleEvent(EVENT_SPELL_GUSHING_WOUND, 1s, 3s); - events.RescheduleEvent(EVENT_SPELL_HOWLING_SCREECH, 8s, 13s); - events.RescheduleEvent(EVENT_SPELL_STRIKE, 4s, 8s); - - if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_EREKEM_GUID))) - if (!c->IsInCombat()) - c->AI()->AttackStart(who); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_GUSHING_WOUND: - me->CastSpell(me->GetVictim(), SPELL_GUSHING_WOUND, false); - events.Repeat(7s, 12s); - break; - case EVENT_SPELL_HOWLING_SCREECH: - me->CastSpell(me->GetVictim(), SPELL_HOWLING_SCREECH, false); - events.Repeat(8s, 13s); - break; - case EVENT_SPELL_STRIKE: - me->CastSpell(me->GetVictim(), SPELL_STRIKE, false); - events.Repeat(4s, 8s); - break; - } - - DoMeleeAttackIfReady(); - } - - void MoveInLineOfSight(Unit* /*who*/) override {} - }; +private: + InstanceScript* _instance; + EventMap _events; }; void AddSC_boss_erekem() { - new boss_erekem(); - new npc_erekem_guard(); + RegisterVioletHoldCreatureAI(boss_erekem); + RegisterVioletHoldCreatureAI(npc_erekem_guard); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp index 1cbd12df6..b48dce183 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_ichoron.cpp @@ -58,302 +58,278 @@ enum eSpells SPELL_WATER_BLAST = 54237, SPELL_WATER_BOLT_VOLLEY = 54241, - SPELL_SPLASH = 59516, // casted by globule upon death - SPELL_WATER_GLOBULE = 54268, // casted when hit by visual - SPELL_CREATE_GLOBULE_VISUAL = 54260, // tar 25 + SPELL_SPLASH = 59516, + SPELL_WATER_GLOBULE = 54268, + SPELL_CREATE_GLOBULE_VISUAL = 54260, }; -class boss_ichoron : public CreatureScript +enum eEvents { -public: - boss_ichoron() : CreatureScript("boss_ichoron") { } + EVENT_WATER_BOLT_VOLLEY = 1, + EVENT_DRAINED_CHECK, +}; - CreatureAI* GetAI(Creature* pCreature) const override +struct boss_ichoron : public BossAI +{ + boss_ichoron(Creature* c) : BossAI(c, BOSS_ICHORON) { } + + void Reset() override { - return GetVioletHoldAI(pCreature); + BossAI::Reset(); + _isExploded = false; + _isFrenzy = false; + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetDisplayId(me->GetNativeDisplayId()); } - struct boss_ichoronAI : public ScriptedAI + void DoAction(int32 param) override { - boss_ichoronAI(Creature* c) : ScriptedAI(c), globules(me) + if (!me->IsAlive()) + return; + + switch (param) { - pInstance = c->GetInstanceScript(); + case ACTION_WATER_ELEMENT_HIT: + instance->SetData(DATA_ACHIEV, 0); + me->ModifyHealth(int32(me->CountPctFromMaxHealth(1))); + if (_isExploded) + DoExplodeCompleted(); + break; + case ACTION_WATER_ELEMENT_KILLED: + uint32 damage = me->CountPctFromMaxHealth(3); + damage = std::min(damage, me->GetHealth() - 1); + me->ModifyHealth(-int32(damage)); + me->LowerPlayerDamageReq(damage); + break; + } + } + + void DoExplodeCompleted() + { + _isExploded = false; + me->RemoveAura(SPELL_DRAINED); + if (!HealthBelowPct(25)) + { + Talk(SAY_BUBBLE); + DoCastSelf(SPELL_PROTECTIVE_BUBBLE, true); } - InstanceScript* pInstance; - SummonList globules; - bool bIsExploded; - bool bIsFrenzy; - uint32 uiWaterBoltVolleyTimer; - uint32 uiDrainedTimer; + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetDisplayId(me->GetNativeDisplayId()); + } - void Reset() override + void IchoronDoCastToAllHostilePlayers(uint32 spellId, bool triggered) + { + Map::PlayerList const& playerList = me->GetMap()->GetPlayers(); + if (playerList.IsEmpty()) + return; + + for (Map::PlayerList::const_iterator i = playerList.begin(); i != playerList.end(); ++i) + if (Player* plr = i->GetSource()) + DoCast(plr, spellId, triggered); + } + + void JustEngagedWith(Unit* who) override + { + _isExploded = false; + _isFrenzy = false; + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + DoCastSelf(SPELL_PROTECTIVE_BUBBLE, true); + events.RescheduleEvent(EVENT_WATER_BOLT_VOLLEY, 7s, 12s); + instance->SetData(DATA_ACHIEV, 1); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (!_isFrenzy && !_isExploded && HealthBelowPct(25)) { - globules.DespawnAll(); - bIsExploded = false; - bIsFrenzy = false; - uiDrainedTimer = 15000; - uiWaterBoltVolleyTimer = urand(7000, 12000); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetDisplayId(me->GetNativeDisplayId()); + Talk(SAY_ENRAGE); + DoCastSelf(SPELL_FRENZY, true); + _isFrenzy = true; } - void DoAction(int32 param) override + if (!_isFrenzy) { - if (!me->IsAlive()) - return; - - switch (param) + if (!_isExploded) { - case ACTION_WATER_ELEMENT_HIT: - if (pInstance) - pInstance->SetData(DATA_ACHIEV, 0); - me->ModifyHealth(int32(me->CountPctFromMaxHealth(1))); - if (bIsExploded) - DoExplodeCompleted(); - break; - case ACTION_WATER_ELEMENT_KILLED: - uint32 damage = me->CountPctFromMaxHealth(3); - damage = std::min(damage, me->GetHealth() - 1); - me->ModifyHealth(-int32(damage)); - me->LowerPlayerDamageReq(damage); - break; - } - } - - void DoExplodeCompleted() - { - bIsExploded = false; - me->RemoveAura(SPELL_DRAINED); - if (!HealthBelowPct(25)) - { - Talk(SAY_BUBBLE); - me->CastSpell(me, SPELL_PROTECTIVE_BUBBLE, true); - } - - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetDisplayId(me->GetNativeDisplayId()); - } - - void IchoronDoCastToAllHostilePlayers(uint32 spellId, bool triggered) - { - Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); - if (PlayerList.IsEmpty()) - return; - - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (Player* plr = i->GetSource()) - me->CastSpell(plr, spellId, triggered); - } - - void JustEngagedWith(Unit* /*who*/) override - { - bIsExploded = false; - bIsFrenzy = false; - uiDrainedTimer = 15000; - uiWaterBoltVolleyTimer = urand(7000, 12000); - DoZoneInCombat(); - Talk(SAY_AGGRO); - me->CastSpell(me, SPELL_PROTECTIVE_BUBBLE, true); - if (pInstance) - pInstance->SetData(DATA_ACHIEV, 1); - } - - void UpdateAI(uint32 uiDiff) override - { - if (!UpdateVictim()) - return; - - if (!bIsFrenzy && !bIsExploded && HealthBelowPct(25)) - { - Talk(SAY_ENRAGE); - me->CastSpell(me, SPELL_FRENZY, true); - bIsFrenzy = true; - } - - if (!bIsFrenzy) - { - if (!bIsExploded) + if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE)) { - if (!me->HasAura(SPELL_PROTECTIVE_BUBBLE)) + me->InterruptNonMeleeSpells(false); + Talk(SAY_SHATTER); + DoZoneInCombat(); + IchoronDoCastToAllHostilePlayers(SPELL_WATER_BLAST, true); + DoCastSelf(SPELL_DRAINED, true); + _isExploded = true; + events.CancelEvent(EVENT_WATER_BOLT_VOLLEY); + events.RescheduleEvent(EVENT_DRAINED_CHECK, 15s); + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetDisplayId(11686); + for (uint8 i = 0; i < MAX_SPAWN_LOC; ++i) { - me->InterruptNonMeleeSpells(false); - Talk(SAY_SHATTER); - DoZoneInCombat(); - IchoronDoCastToAllHostilePlayers(SPELL_WATER_BLAST, true); - me->CastSpell(me, SPELL_DRAINED, true); - bIsExploded = true; - uiDrainedTimer = 15000; - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetDisplayId(11686); - for (uint8 i = 0; i < MAX_SPAWN_LOC; ++i) - { - float angle = rand_norm() * 2 * M_PI; - Position p1(SpawnLoc[i]), p2(SpawnLoc[i]); - p1.m_positionX += 2.5f * cos(angle); - p1.m_positionY += 2.5f * std::sin(angle); - p2.m_positionX -= 2.5f * cos(angle); - p2.m_positionY -= 2.5f * std::sin(angle); - DoSummon(NPC_ICHOR_GLOBULE, p1, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN); - DoSummon(NPC_ICHOR_GLOBULE, p2, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN); - } + float angle = rand_norm() * 2 * M_PI; + Position p1(SpawnLoc[i]), p2(SpawnLoc[i]); + p1.m_positionX += 2.5f * cos(angle); + p1.m_positionY += 2.5f * std::sin(angle); + p2.m_positionX -= 2.5f * cos(angle); + p2.m_positionY -= 2.5f * std::sin(angle); + DoSummon(NPC_ICHOR_GLOBULE, p1, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN); + DoSummon(NPC_ICHOR_GLOBULE, p2, 60000, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN); } } + } + else + { + if (events.ExecuteEvent() == EVENT_DRAINED_CHECK) + DoExplodeCompleted(); else { - if (uiDrainedTimer <= uiDiff) - DoExplodeCompleted(); - else + bool isWaterElementsAlive = false; + if (!summons.empty()) { - uiDrainedTimer -= uiDiff; - - bool bIsWaterElementsAlive = false; - if (!globules.empty()) - { - for (ObjectGuid const& guid : globules) - if (Creature* pTemp = ObjectAccessor::GetCreature(*me, guid)) - if (pTemp->IsAlive()) - { - bIsWaterElementsAlive = true; - break; - } - } - - if (!bIsWaterElementsAlive) - DoExplodeCompleted(); + for (ObjectGuid const& guid : summons) + if (Creature* temp = ObjectAccessor::GetCreature(*me, guid)) + if (temp->IsAlive()) + { + isWaterElementsAlive = true; + break; + } } + + if (!isWaterElementsAlive) + DoExplodeCompleted(); } } - - if (!bIsExploded) - { - if (uiWaterBoltVolleyTimer <= uiDiff) - { - me->CastSpell((Unit*)nullptr, SPELL_WATER_BOLT_VOLLEY, false); - uiWaterBoltVolleyTimer = urand(10000, 15000); - } - else uiWaterBoltVolleyTimer -= uiDiff; - } - - DoMeleeAttackIfReady(); } - void JustSummoned(Creature* pSummoned) override + if (!_isExploded) { - if (pSummoned) + if (events.ExecuteEvent() == EVENT_WATER_BOLT_VOLLEY) { - pSummoned->SetSpeed(MOVE_RUN, 0.3f); - pSummoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - pSummoned->GetMotionMaster()->MoveFollow(me, 0, 0, MOTION_SLOT_ACTIVE, false, false); - me->CastSpell(pSummoned, SPELL_CREATE_GLOBULE_VISUAL, true); // triggered should ignore los - globules.Summon(pSummoned); - if (pInstance) - pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID()); + DoCastAOE(SPELL_WATER_BOLT_VOLLEY); + events.Repeat(10s, 15s); } } - void SummonedCreatureDespawn(Creature* pSummoned) override - { - if (pSummoned) - { - globules.Despawn(pSummoned); - if (pInstance) - pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID()); - } - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - bIsExploded = false; - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetDisplayId(me->GetNativeDisplayId()); - globules.DespawnAll(); - if (pInstance) - pInstance->SetData(DATA_BOSS_DIED, 0); - } - - void KilledUnit(Unit* victim) override - { - if (victim && victim->GetGUID() == me->GetGUID()) - return; - Talk(SAY_SLAY); - } - - void MoveInLineOfSight(Unit* /*who*/) override {} - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - - if (pInstance) - pInstance->SetData(DATA_FAILED, 1); - } - }; -}; - -class npc_ichor_globule : public CreatureScript -{ -public: - npc_ichor_globule() : CreatureScript("npc_ichor_globule") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetVioletHoldAI(pCreature); + DoMeleeAttackIfReady(); } - struct npc_ichor_globuleAI : public ScriptedAI + void JustSummoned(Creature* summoned) override { - npc_ichor_globuleAI(Creature* c) : ScriptedAI(c) + if (summoned) { - pInstance = c->GetInstanceScript(); - uiRangeCheck_Timer = 1000; + summoned->SetSpeed(MOVE_RUN, 0.3f); + summoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + summoned->GetMotionMaster()->MoveFollow(me, 0, 0, MOTION_SLOT_ACTIVE, false, false); + DoCast(summoned, SPELL_CREATE_GLOBULE_VISUAL, true); + BossAI::JustSummoned(summoned); + instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); } + } - InstanceScript* pInstance; - uint32 uiRangeCheck_Timer; - - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + void SummonedCreatureDespawn(Creature* summoned) override + { + if (summoned) { - if (spell->Id == SPELL_CREATE_GLOBULE_VISUAL) - me->CastSpell(me, SPELL_WATER_GLOBULE, true); + BossAI::SummonedCreatureDespawn(summoned); + instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID()); } + } - void UpdateAI(uint32 uiDiff) override + void JustDied(Unit* killer) override + { + Talk(SAY_DEATH); + _isExploded = false; + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetDisplayId(me->GetNativeDisplayId()); + BossAI::JustDied(killer); + } + + void KilledUnit(Unit* victim) override + { + if (victim && victim->GetGUID() == me->GetGUID()) + return; + Talk(SAY_SLAY); + } + + void MoveInLineOfSight(Unit* /*who*/) override {} + + void EnterEvadeMode(EvadeReason why) override + { + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + _EnterEvadeMode(why); + } + +private: + bool _isExploded; + bool _isFrenzy; +}; + +enum eGlobuleEvents +{ + EVENT_RANGE_CHECK = 1, +}; + +struct npc_ichor_globule : public ScriptedAI +{ + npc_ichor_globule(Creature* c) : ScriptedAI(c) + { + _instance = c->GetInstanceScript(); + } + + void Reset() override + { + _events.RescheduleEvent(EVENT_RANGE_CHECK, 1s); + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_CREATE_GLOBULE_VISUAL) + DoCastSelf(SPELL_WATER_GLOBULE, true); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + if (_events.ExecuteEvent() == EVENT_RANGE_CHECK) { - if (uiRangeCheck_Timer < uiDiff) - { - if (pInstance) - if (Creature* pIchoron = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_ICHORON_GUID))) - if (me->IsWithinDist(pIchoron, 2.0f, false)) - { - if (pIchoron->AI()) - pIchoron->AI()->DoAction(ACTION_WATER_ELEMENT_HIT); - me->DespawnOrUnsummon(); - } - uiRangeCheck_Timer = 1000; - } - else uiRangeCheck_Timer -= uiDiff; + if (Creature* ichoron = _instance->GetCreature(BOSS_ICHORON)) + if (me->IsWithinDist(ichoron, 2.0f, false)) + { + if (ichoron->AI()) + ichoron->AI()->DoAction(ACTION_WATER_ELEMENT_HIT); + me->DespawnOrUnsummon(); + return; + } + _events.Repeat(1s); } + } - void JustDied(Unit* /*killer*/) override - { - me->CastSpell(me, SPELL_SPLASH, true); - if (pInstance) - if (Creature* pIchoron = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_ICHORON_GUID))) - if (pIchoron->AI()) - pIchoron->AI()->DoAction(ACTION_WATER_ELEMENT_KILLED); - me->DespawnOrUnsummon(2500ms); - } + void JustDied(Unit* /*killer*/) override + { + DoCastSelf(SPELL_SPLASH, true); + if (Creature* ichoron = _instance->GetCreature(BOSS_ICHORON)) + if (ichoron->AI()) + ichoron->AI()->DoAction(ACTION_WATER_ELEMENT_KILLED); + me->DespawnOrUnsummon(2500ms); + } - void AttackStart(Unit* /*who*/) override {} - void MoveInLineOfSight(Unit* /*who*/) override {} - }; + void AttackStart(Unit* /*who*/) override {} + void MoveInLineOfSight(Unit* /*who*/) override {} + +private: + InstanceScript* _instance; + EventMap _events; }; void AddSC_boss_ichoron() { - new boss_ichoron(); - new npc_ichor_globule(); + RegisterVioletHoldCreatureAI(boss_ichoron); + RegisterVioletHoldCreatureAI(npc_ichor_globule); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp index cbd8b3f62..f088b5edb 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_lavanthor.cpp @@ -35,97 +35,53 @@ enum eEvents EVENT_SPELL_CAUTERIZING_FLAMES, }; -class boss_lavanthor : public CreatureScript +struct boss_lavanthor : public BossAI { -public: - boss_lavanthor() : CreatureScript("boss_lavanthor") { } + boss_lavanthor(Creature* c) : BossAI(c, BOSS_LAVANTHOR) { } - CreatureAI* GetAI(Creature* pCreature) const override + void JustEngagedWith(Unit* who) override { - return GetVioletHoldAI(pCreature); + BossAI::JustEngagedWith(who); + events.RescheduleEvent(EVENT_SPELL_FIREBOLT, 1s); + events.RescheduleEvent(EVENT_SPELL_FLAME_BREATH, 5s); + events.RescheduleEvent(EVENT_SPELL_LAVA_BURN, 10s); + if (IsHeroic()) + events.RescheduleEvent(EVENT_SPELL_CAUTERIZING_FLAMES, 3s); } - struct boss_lavanthorAI : public ScriptedAI + void ExecuteEvent(uint32 eventId) override { - boss_lavanthorAI(Creature* c) : ScriptedAI(c) + switch (eventId) { - pInstance = c->GetInstanceScript(); + case EVENT_SPELL_FIREBOLT: + DoCastVictim(SPELL_FIREBOLT); + events.Repeat(5s, 13s); + break; + case EVENT_SPELL_FLAME_BREATH: + DoCastVictim(SPELL_FLAME_BREATH); + events.Repeat(10s, 15s); + break; + case EVENT_SPELL_LAVA_BURN: + DoCastVictim(SPELL_LAVA_BURN); + events.Repeat(14s, 20s); + break; + case EVENT_SPELL_CAUTERIZING_FLAMES: + DoCastAOE(SPELL_CAUTERIZING_FLAMES); + events.Repeat(10s, 16s); + break; } + } - InstanceScript* pInstance; - EventMap events; + void MoveInLineOfSight(Unit* /*who*/) override {} - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - DoZoneInCombat(); - events.Reset(); - events.RescheduleEvent(EVENT_SPELL_FIREBOLT, 1s); - events.RescheduleEvent(EVENT_SPELL_FLAME_BREATH, 5s); - events.RescheduleEvent(EVENT_SPELL_LAVA_BURN, 10s); - if (IsHeroic()) - events.RescheduleEvent(EVENT_SPELL_CAUTERIZING_FLAMES, 3s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_FIREBOLT: - me->CastSpell(me->GetVictim(), SPELL_FIREBOLT, false); - events.Repeat(5s, 13s); - break; - case EVENT_SPELL_FLAME_BREATH: - me->CastSpell(me->GetVictim(), SPELL_FLAME_BREATH, false); - events.Repeat(10s, 15s); - break; - case EVENT_SPELL_LAVA_BURN: - me->CastSpell(me->GetVictim(), SPELL_LAVA_BURN, false); - events.Repeat(14s, 20s); - break; - case EVENT_SPELL_CAUTERIZING_FLAMES: - me->CastSpell((Unit*)nullptr, SPELL_FLAME_BREATH, false); - events.Repeat(10s, 16s); - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - if (pInstance) - pInstance->SetData(DATA_BOSS_DIED, 0); - } - - void MoveInLineOfSight(Unit* /*who*/) override {} - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - - if (pInstance) - pInstance->SetData(DATA_FAILED, 1); - } - }; + void EnterEvadeMode(EvadeReason why) override + { + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + _EnterEvadeMode(why); + } }; void AddSC_boss_lavanthor() { - new boss_lavanthor(); + RegisterVioletHoldCreatureAI(boss_lavanthor); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp index abfbc167c..598b55f99 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_moragg.cpp @@ -25,12 +25,7 @@ enum eSpells { SPELL_RAY_OF_SUFFERING = 54442, - //SPELL_RAY_OF_SUFFERING_TRIGGERED = 54417, - SPELL_RAY_OF_PAIN = 54438, - //SPELL_RAY_OF_PAIN_TRIGGERED_N = 54416, - //SPELL_RAY_OF_PAIN_TRIGGERED_H = 59525, - SPELL_CORROSIVE_SALIVA = 54527, SPELL_OPTIC_LINK = 54396, }; @@ -41,90 +36,46 @@ enum eEvents EVENT_SPELL_OPTIC_LINK, }; -class boss_moragg : public CreatureScript +struct boss_moragg : public BossAI { -public: - boss_moragg() : CreatureScript("boss_moragg") { } + boss_moragg(Creature* c) : BossAI(c, BOSS_MORAGG) { } - CreatureAI* GetAI(Creature* pCreature) const override + void JustEngagedWith(Unit* who) override { - return GetVioletHoldAI(pCreature); + BossAI::JustEngagedWith(who); + DoCastSelf(SPELL_RAY_OF_SUFFERING, true); + DoCastSelf(SPELL_RAY_OF_PAIN, true); + events.RescheduleEvent(EVENT_SPELL_CORROSIVE_SALIVA, 4s, 6s); + events.RescheduleEvent(EVENT_SPELL_OPTIC_LINK, 10s, 11s); } - struct boss_moraggAI : public ScriptedAI + void ExecuteEvent(uint32 eventId) override { - boss_moraggAI(Creature* c) : ScriptedAI(c) + switch (eventId) { - pInstance = c->GetInstanceScript(); + case EVENT_SPELL_CORROSIVE_SALIVA: + DoCastVictim(SPELL_CORROSIVE_SALIVA); + events.Repeat(8s, 10s); + break; + case EVENT_SPELL_OPTIC_LINK: + if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 40.0f, true)) + { + DoCast(target, SPELL_OPTIC_LINK); + events.Repeat(18s, 21s); + } + else + events.Repeat(5s); + break; } + } - InstanceScript* pInstance; - EventMap events; + void MoveInLineOfSight(Unit* /*who*/) override {} - void Reset() override - { - events.Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - DoZoneInCombat(); - me->CastSpell(me, SPELL_RAY_OF_SUFFERING, true); - me->CastSpell(me, SPELL_RAY_OF_PAIN, true); - events.Reset(); - events.RescheduleEvent(EVENT_SPELL_CORROSIVE_SALIVA, 4s, 6s); - events.RescheduleEvent(EVENT_SPELL_OPTIC_LINK, 10s, 11s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_CORROSIVE_SALIVA: - me->CastSpell(me->GetVictim(), SPELL_CORROSIVE_SALIVA, false); - events.Repeat(8s, 10s); - break; - case EVENT_SPELL_OPTIC_LINK: - if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 40.0f, true)) - { - me->CastSpell(target, SPELL_OPTIC_LINK, false); - events.Repeat(18s, 21s); - } - else - events.Repeat(5s); - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - if (pInstance) - pInstance->SetData(DATA_BOSS_DIED, 0); - } - - void MoveInLineOfSight(Unit* /*who*/) override {} - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - events.Reset(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (pInstance) - pInstance->SetData(DATA_FAILED, 1); - } - }; + void EnterEvadeMode(EvadeReason why) override + { + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + _EnterEvadeMode(why); + } }; class spell_optic_link_aura : public AuraScript @@ -147,6 +98,6 @@ class spell_optic_link_aura : public AuraScript void AddSC_boss_moragg() { - new boss_moragg(); + RegisterVioletHoldCreatureAI(boss_moragg); RegisterSpellScript(spell_optic_link_aura); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp index 28dbd974e..115b2acd9 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp @@ -39,8 +39,6 @@ enum eSpells SPELL_SUMMON_ETHEREAL_SPHERE_3 = 54138, SPELL_ARCANE_POWER = 54160 - //SPELL_SUMMON_PLAYERS = 54164, // not used - //SPELL_POWER_BALL_VISUAL = 54141, }; enum eEvents @@ -51,159 +49,114 @@ enum eEvents EVENT_CHECK_DISTANCE, }; -class boss_xevozz : public CreatureScript +struct boss_xevozz : public BossAI { -public: - boss_xevozz() : CreatureScript("boss_xevozz") { } + boss_xevozz(Creature* c) : BossAI(c, BOSS_XEVOZZ) { } - CreatureAI* GetAI(Creature* pCreature) const override + void JustEngagedWith(Unit* who) override { - return GetVioletHoldAI(pCreature); + Talk(SAY_AGGRO); + BossAI::JustEngagedWith(who); + events.RescheduleEvent(EVENT_SPELL_ARCANE_BARRAGE_VOLLEY, 16s, 20s); + events.RescheduleEvent(EVENT_SUMMON_SPHERES, 10s); } - struct boss_xevozzAI : public ScriptedAI + void ExecuteEvent(uint32 eventId) override { - boss_xevozzAI(Creature* c) : ScriptedAI(c), spheres(me) + switch (eventId) { - pInstance = c->GetInstanceScript(); - } - - InstanceScript* pInstance; - EventMap events; - SummonList spheres; - - void Reset() override - { - events.Reset(); - spheres.DespawnAll(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - DoZoneInCombat(); - events.Reset(); - events.RescheduleEvent(EVENT_SPELL_ARCANE_BARRAGE_VOLLEY, 16s, 20s); - events.RescheduleEvent(EVENT_SUMMON_SPHERES, 10s); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_ARCANE_BARRAGE_VOLLEY: - me->CastSpell((Unit*)nullptr, SPELL_ARCANE_BARRAGE_VOLLEY, false); - events.Repeat(20s); - break; - case EVENT_SPELL_ARCANE_BUFFET: - me->CastSpell(me->GetVictim(), SPELL_ARCANE_BUFFET, false); - break; - case EVENT_SUMMON_SPHERES: + case EVENT_SPELL_ARCANE_BARRAGE_VOLLEY: + DoCastAOE(SPELL_ARCANE_BARRAGE_VOLLEY); + events.Repeat(20s); + break; + case EVENT_SPELL_ARCANE_BUFFET: + DoCastVictim(SPELL_ARCANE_BUFFET); + break; + case EVENT_SUMMON_SPHERES: + { + Talk(SAY_SUMMON_ENERGY); + summons.DespawnAll(); + uint32 entry1 = RAND(SPELL_SUMMON_ETHEREAL_SPHERE_1, SPELL_SUMMON_ETHEREAL_SPHERE_2, SPELL_SUMMON_ETHEREAL_SPHERE_3); + DoCastAOE(entry1, true); + if (IsHeroic()) { - Talk(SAY_SUMMON_ENERGY); - spheres.DespawnAll(); - uint32 entry1 = RAND(SPELL_SUMMON_ETHEREAL_SPHERE_1, SPELL_SUMMON_ETHEREAL_SPHERE_2, SPELL_SUMMON_ETHEREAL_SPHERE_3); - me->CastSpell((Unit*)nullptr, entry1, true); - if (IsHeroic()) - { - uint32 entry2; - do { entry2 = RAND(SPELL_SUMMON_ETHEREAL_SPHERE_1, SPELL_SUMMON_ETHEREAL_SPHERE_2, SPELL_SUMMON_ETHEREAL_SPHERE_3); } - while (entry1 == entry2); - me->CastSpell((Unit*)nullptr, entry2, true); - } - events.Repeat(45s); - events.RescheduleEvent(EVENT_SPELL_ARCANE_BUFFET, 5s); - events.RescheduleEvent(EVENT_CHECK_DISTANCE, 6s); + uint32 entry2; + do { entry2 = RAND(SPELL_SUMMON_ETHEREAL_SPHERE_1, SPELL_SUMMON_ETHEREAL_SPHERE_2, SPELL_SUMMON_ETHEREAL_SPHERE_3); } + while (entry1 == entry2); + DoCastAOE(entry2, true); } - break; - case EVENT_CHECK_DISTANCE: + events.Repeat(45s); + events.RescheduleEvent(EVENT_SPELL_ARCANE_BUFFET, 5s); + events.RescheduleEvent(EVENT_CHECK_DISTANCE, 6s); + } + break; + case EVENT_CHECK_DISTANCE: + { + bool found = false; + for (ObjectGuid const& guid : summons) + if (Creature* sphere = instance->instance->GetCreature(guid)) + if (me->GetDistance(sphere) < 3.0f) + { + sphere->CastSpell(me, SPELL_ARCANE_POWER, false); + sphere->DespawnOrUnsummon(8s); + found = true; + } + if (found) { - bool found = false; - if (pInstance) - for (ObjectGuid const& guid : spheres) - if (Creature* c = pInstance->instance->GetCreature(guid)) - if (me->GetDistance(c) < 3.0f) - { - c->CastSpell(me, SPELL_ARCANE_POWER, false); - c->DespawnOrUnsummon(8s); - found = true; - } - if (found) - { - Talk(SAY_CHARGED); - events.Repeat(9s); - events.RescheduleEvent(EVENT_SUMMON_SPHERES, 10s); - } - else - events.Repeat(2s); + Talk(SAY_CHARGED); + events.Repeat(9s); + events.RescheduleEvent(EVENT_SUMMON_SPHERES, 10s); } - break; - } - - DoMeleeAttackIfReady(); + else + events.Repeat(2s); + } + break; } + } - void JustSummoned(Creature* pSummoned) override + void JustSummoned(Creature* summoned) override + { + if (summoned) { - if (pSummoned) - { - pSummoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f, MOTION_SLOT_ACTIVE, true, false); - spheres.Summon(pSummoned); - if (pInstance) - pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID()); - } + summoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f, MOTION_SLOT_ACTIVE, true, false); + BossAI::JustSummoned(summoned); + instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); } + } - void SummonedCreatureDespawn(Creature* pSummoned) override + void SummonedCreatureDespawn(Creature* summoned) override + { + if (summoned) { - if (pSummoned) - { - spheres.Despawn(pSummoned); - if (pInstance) - pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID()); - } + BossAI::SummonedCreatureDespawn(summoned); + instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID()); } + } - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - spheres.DespawnAll(); - if (pInstance) - pInstance->SetData(DATA_BOSS_DIED, 0); - } + void JustDied(Unit* killer) override + { + Talk(SAY_DEATH); + BossAI::JustDied(killer); + } - void KilledUnit(Unit* pVictim) override - { - if (pVictim && pVictim->GetGUID() == me->GetGUID()) - return; + void KilledUnit(Unit* victim) override + { + if (victim && victim->GetGUID() == me->GetGUID()) + return; - Talk(SAY_SLAY); - } + Talk(SAY_SLAY); + } - void MoveInLineOfSight(Unit* /*who*/) override {} + void MoveInLineOfSight(Unit* /*who*/) override {} - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - events.Reset(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (pInstance) - pInstance->SetData(DATA_FAILED, 1); - } - }; + void EnterEvadeMode(EvadeReason why) override + { + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + _EnterEvadeMode(why); + } }; void AddSC_boss_xevozz() { - new boss_xevozz(); + RegisterVioletHoldCreatureAI(boss_xevozz); } diff --git a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp index 78a273076..90c9e71da 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp @@ -36,12 +36,12 @@ enum eSpells SPELL_VOID_SHIFT = 54361, SPELL_SUMMON_VOID_SENTRY = 54369, SPELL_SUMMON_VOID_SENTRY_BALL = 58650, - - //SPELL_ZURAMAT_ADD_2_N = 54342, - //SPELL_ZURAMAT_ADD_2_H = 59747, }; -#define NPC_VOID_SENTRY_BALL 29365 +enum eCreatures +{ + NPC_VOID_SENTRY_BALL = 29365, +}; enum eEvents { @@ -50,208 +50,154 @@ enum eEvents EVENT_SPELL_SUMMON_VOID_SENTRY, }; -class boss_zuramat : public CreatureScript +enum eVoidSentryEvents { -public: - boss_zuramat() : CreatureScript("boss_zuramat") { } - - CreatureAI* GetAI(Creature* pCreature) const override - { - return GetVioletHoldAI(pCreature); - } - - struct boss_zuramatAI : public ScriptedAI - { - boss_zuramatAI(Creature* c) : ScriptedAI(c), summons(me) - { - pInstance = c->GetInstanceScript(); - } - - InstanceScript* pInstance; - EventMap events; - SummonList summons; - - void Reset() override - { - events.Reset(); - summons.DespawnAll(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - DoZoneInCombat(); - events.Reset(); - events.RescheduleEvent(EVENT_SPELL_SHROUD_OF_DARKNESS, 5s, 7s); - events.RescheduleEvent(EVENT_SPELL_VOID_SHIFT, 23s, 25s); - events.RescheduleEvent(EVENT_SPELL_SUMMON_VOID_SENTRY, 10s); - if (pInstance) - pInstance->SetData(DATA_ACHIEV, 1); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SPELL_SHROUD_OF_DARKNESS: - me->CastSpell(me, SPELL_SHROUD_OF_DARKNESS, false); - Talk(SAY_SHIELD); - events.Repeat(20s); - break; - case EVENT_SPELL_VOID_SHIFT: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true)) - { - me->CastSpell(target, SPELL_VOID_SHIFT, false); - me->Whisper("Gaze... into the void.", LANG_UNIVERSAL, target->ToPlayer()); - } - events.Repeat(18s, 22s); - break; - case EVENT_SPELL_SUMMON_VOID_SENTRY: - me->CastSpell((Unit*)nullptr, SPELL_SUMMON_VOID_SENTRY, false); - events.Repeat(12s); - break; - } - - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - summons.DespawnAll(); - Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(DATA_BOSS_DIED, 0); - } - - void KilledUnit(Unit* victim) override - { - if (victim && victim->GetGUID() == me->GetGUID()) - return; - - Talk(SAY_SLAY); - } - - void JustSummoned(Creature* pSummoned) override - { - if (pSummoned) - { - summons.Summon(pSummoned); - pSummoned->SetPhaseMask(16, true); - if (pInstance) - pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID()); - } - } - - void SummonedCreatureDespawn(Creature* pSummoned) override - { - if (pSummoned) - { - summons.Despawn(pSummoned); - if (pSummoned->IsAIEnabled) - pSummoned->AI()->DoAction(-1337); - if (pInstance) - pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID()); - } - } - - void MoveInLineOfSight(Unit* /*who*/) override {} - - void EnterEvadeMode(EvadeReason why) override - { - ScriptedAI::EnterEvadeMode(why); - events.Reset(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (pInstance) - pInstance->SetData(DATA_FAILED, 1); - } - }; + EVENT_CHECK_SUMMONER = 1, }; -class npc_vh_void_sentry : public CreatureScript +struct boss_zuramat : public BossAI { -public: - npc_vh_void_sentry() : CreatureScript("npc_vh_void_sentry") { } + boss_zuramat(Creature* c) : BossAI(c, BOSS_ZURAMAT) { } - CreatureAI* GetAI(Creature* pCreature) const override + void JustEngagedWith(Unit* who) override { - return GetVioletHoldAI(pCreature); + Talk(SAY_AGGRO); + BossAI::JustEngagedWith(who); + events.RescheduleEvent(EVENT_SPELL_SHROUD_OF_DARKNESS, 5s, 7s); + events.RescheduleEvent(EVENT_SPELL_VOID_SHIFT, 23s, 25s); + events.RescheduleEvent(EVENT_SPELL_SUMMON_VOID_SENTRY, 10s); + instance->SetData(DATA_ACHIEV, 1); } - struct npc_vh_void_sentryAI : public NullCreatureAI + void ExecuteEvent(uint32 eventId) override { - npc_vh_void_sentryAI(Creature* c) : NullCreatureAI(c) + switch (eventId) { - pInstance = c->GetInstanceScript(); - SummonedGUID.Clear(); - checkTimer = 5000; - //me->CastSpell(me, SPELL_SUMMON_VOID_SENTRY_BALL, true); - if (Creature* pSummoned = me->SummonCreature(NPC_VOID_SENTRY_BALL, *me, TEMPSUMMON_TIMED_DESPAWN, 300000)) - { - pSummoned->SetPhaseMask(1, true); - SummonedGUID = pSummoned->GetGUID(); - pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID()); - } + case EVENT_SPELL_SHROUD_OF_DARKNESS: + DoCastSelf(SPELL_SHROUD_OF_DARKNESS); + Talk(SAY_SHIELD); + events.Repeat(20s); + break; + case EVENT_SPELL_VOID_SHIFT: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true)) + { + DoCast(target, SPELL_VOID_SHIFT); + me->Whisper("Gaze... into the void.", LANG_UNIVERSAL, target->ToPlayer()); + } + events.Repeat(18s, 22s); + break; + case EVENT_SPELL_SUMMON_VOID_SENTRY: + DoCastAOE(SPELL_SUMMON_VOID_SENTRY); + events.Repeat(12s); + break; } + } - InstanceScript* pInstance; - ObjectGuid SummonedGUID; - uint16 checkTimer; + void JustDied(Unit* killer) override + { + Talk(SAY_DEATH); + BossAI::JustDied(killer); + } - void DoAction(int32 a) override + void KilledUnit(Unit* victim) override + { + if (victim && victim->GetGUID() == me->GetGUID()) + return; + + Talk(SAY_SLAY); + } + + void JustSummoned(Creature* summoned) override + { + if (summoned) { - if (a == -1337) - if (Creature* c = pInstance->instance->GetCreature(SummonedGUID)) - c->DespawnOrUnsummon(); + BossAI::JustSummoned(summoned); + summoned->SetPhaseMask(16, true); + instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); } + } - void JustDied(Unit* /*killer*/) override + void SummonedCreatureDespawn(Creature* summoned) override + { + if (summoned) { - if (pInstance) - { - pInstance->SetData(DATA_ACHIEV, 0); - if (Creature* c = pInstance->instance->GetCreature(SummonedGUID)) - c->DespawnOrUnsummon(); - } - me->DespawnOrUnsummon(5s); + BossAI::SummonedCreatureDespawn(summoned); + if (summoned->IsAIEnabled) + summoned->AI()->DoAction(-1337); + instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID()); } + } - void SummonedCreatureDespawn(Creature* pSummoned) override - { - if (pSummoned) - pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID()); - } + void MoveInLineOfSight(Unit* /*who*/) override {} - void UpdateAI(uint32 diff) override + void EnterEvadeMode(EvadeReason why) override + { + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + _EnterEvadeMode(why); + } +}; + +struct npc_vh_void_sentry : public NullCreatureAI +{ + npc_vh_void_sentry(Creature* c) : NullCreatureAI(c) + { + _instance = c->GetInstanceScript(); + _summonedGuid.Clear(); + _events.ScheduleEvent(EVENT_CHECK_SUMMONER, 5s); + if (Creature* summoned = me->SummonCreature(NPC_VOID_SENTRY_BALL, *me, TEMPSUMMON_TIMED_DESPAWN, 300000)) { - if (checkTimer <= diff) - { - checkTimer = 5000; - bool good = false; - if (me->IsSummon()) - if (Unit* s = me->ToTempSummon()->GetSummonerUnit()) - if (s->IsAlive()) - good = true; - if (!good) - me->KillSelf(); - } - else - checkTimer -= diff; + summoned->SetPhaseMask(1, true); + _summonedGuid = summoned->GetGUID(); + _instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); } - }; + } + + void DoAction(int32 action) override + { + if (action == -1337) + if (Creature* voidSentry = _instance->instance->GetCreature(_summonedGuid)) + voidSentry->DespawnOrUnsummon(); + } + + void JustDied(Unit* /*killer*/) override + { + _instance->SetData(DATA_ACHIEV, 0); + if (Creature* voidSentry = _instance->instance->GetCreature(_summonedGuid)) + voidSentry->DespawnOrUnsummon(); + me->DespawnOrUnsummon(5s); + } + + void SummonedCreatureDespawn(Creature* summoned) override + { + if (summoned) + _instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID()); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + if (_events.ExecuteEvent() == EVENT_CHECK_SUMMONER) + { + bool good = false; + if (me->IsSummon()) + if (Unit* s = me->ToTempSummon()->GetSummonerUnit()) + if (s->IsAlive()) + good = true; + if (!good) + me->KillSelf(); + _events.Repeat(5s); + } + } + +private: + InstanceScript* _instance; + ObjectGuid _summonedGuid; + EventMap _events; }; void AddSC_boss_zuramat() { - new boss_zuramat(); - new npc_vh_void_sentry(); + RegisterVioletHoldCreatureAI(boss_zuramat); + RegisterVioletHoldCreatureAI(npc_vh_void_sentry); } diff --git a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp index 021210d81..f88bc90d2 100644 --- a/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/instance_violet_hold.cpp @@ -22,7 +22,7 @@ #include "WorldStateDefines.h" #include "violet_hold.h" -enum vYells +enum VHYells { CYANIGOSA_SAY_SPAWN = 3, SAY_SINCLARI_LEAVING = 0, @@ -30,175 +30,163 @@ enum vYells SAY_SINCLARI_COMPLETE = 2, }; +ObjectData const creatureData[] = +{ + { NPC_MORAGG, BOSS_MORAGG }, + { NPC_EREKEM, BOSS_EREKEM }, + { NPC_ICHORON, BOSS_ICHORON }, + { NPC_LAVANTHOR, BOSS_LAVANTHOR }, + { NPC_XEVOZZ, BOSS_XEVOZZ }, + { NPC_ZURAMAT, BOSS_ZURAMAT }, + { NPC_CYANIGOSA, DATA_CYANIGOSA }, + { NPC_SINCLARI, DATA_SINCLARI }, + { NPC_PRISON_DOOR_SEAL, DATA_DOOR_SEAL }, + { NPC_TELEPORTATION_PORTAL, DATA_TELEPORTATION_PORTAL }, + { 0, 0 } +}; + +ObjectData const goData[] = +{ + { GO_MAIN_DOOR, DATA_MAIN_DOOR }, + { GO_MORAGG_DOOR, DATA_MORAGG_CELL }, + { GO_EREKEM_DOOR, DATA_EREKEM_CELL }, + { GO_EREKEM_GUARD_1_DOOR, DATA_EREKEM_GUARD_1_CELL }, + { GO_EREKEM_GUARD_2_DOOR, DATA_EREKEM_GUARD_2_CELL }, + { GO_ICHORON_DOOR, DATA_ICHORON_CELL }, + { GO_LAVANTHOR_DOOR, DATA_LAVANTHOR_CELL }, + { GO_XEVOZZ_DOOR, DATA_XEVOZZ_CELL }, + { GO_ZURAMAT_DOOR, DATA_ZURAMAT_CELL }, + { 0, 0 } +}; + class instance_violet_hold : public InstanceMapScript { public: instance_violet_hold() : InstanceMapScript("instance_violet_hold", MAP_VIOLET_HOLD) { } - InstanceScript* GetInstanceScript(InstanceMap* pMap) const override + InstanceScript* GetInstanceScript(InstanceMap* map) const override { - return new instance_violet_hold_InstanceMapScript(pMap); + return new instance_violet_hold_InstanceMapScript(map); } struct instance_violet_hold_InstanceMapScript : public InstanceScript { - instance_violet_hold_InstanceMapScript(Map* pMap) : InstanceScript(pMap) {} - - uint32 m_auiEncounter[MAX_ENCOUNTER]; - bool CLEANED; - uint8 EncounterStatus; - uint32 uiFirstBoss, uiSecondBoss; - std::string str_data; - EventMap events; - uint8 GateHealth; - uint8 WaveCount; - uint8 PortalLocation; - bool bAchiev; - bool bDefensesUsed; - - GuidVector GO_ActivationCrystalGUID; - ObjectGuid GO_MainGateGUID; - - ObjectGuid GO_MoraggCellGUID; - ObjectGuid GO_ErekemCellGUID; - ObjectGuid GO_ErekemRightGuardCellGUID; - ObjectGuid GO_ErekemLeftGuardCellGUID; - ObjectGuid GO_IchoronCellGUID; - ObjectGuid GO_LavanthorCellGUID; - ObjectGuid GO_XevozzCellGUID; - ObjectGuid GO_ZuramatCellGUID; - - GuidSet trashMobs; - ObjectGuid NPC_SinclariGUID; - ObjectGuid NPC_GuardGUID[4]; - ObjectGuid NPC_PortalGUID; - ObjectGuid NPC_DoorSealGUID; - - ObjectGuid NPC_MoraggGUID; - ObjectGuid NPC_ErekemGUID; - ObjectGuid NPC_ErekemGuardGUID[2]; - ObjectGuid NPC_IchoronGUID; - ObjectGuid NPC_LavanthorGUID; - ObjectGuid NPC_XevozzGUID; - ObjectGuid NPC_ZuramatGUID; - ObjectGuid NPC_CyanigosaGUID; + instance_violet_hold_InstanceMapScript(Map* map) : InstanceScript(map) + { + SetHeaders(DataHeader); + SetBossNumber(MAX_BOSS); + SetPersistentDataCount(PERSISTENT_DATA_COUNT); + LoadObjectData(creatureData, goData); + } void Initialize() override { - SetHeaders(DataHeader); - memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); - CLEANED = false; - EncounterStatus = NOT_STARTED; - uiFirstBoss = 0; - uiSecondBoss = 0; - events.Reset(); - events.RescheduleEvent(EVENT_CHECK_PLAYERS, 0ms); - GateHealth = 100; - WaveCount = 0; - PortalLocation = 0; - bDefensesUsed = false; + _cleaned = false; + _encounterStatus = NOT_STARTED; + _events.Reset(); + _events.RescheduleEvent(EVENT_CHECK_PLAYERS, 0ms); + _gateHealth = 100; + _waveCount = 0; + _portalLocation = 0; + _defensesUsed = false; - GO_ActivationCrystalGUID.clear(); + _activationCrystalGuidList.clear(); } bool IsEncounterInProgress() const override { - return false; + return _encounterStatus == IN_PROGRESS; } void OnCreatureCreate(Creature* creature) override { + InstanceScript::OnCreatureCreate(creature); + switch (creature->GetEntry()) { - case NPC_SINCLARI: - NPC_SinclariGUID = creature->GetGUID(); - break; case NPC_VIOLET_HOLD_GUARD: - for (uint8 i = 0; i < 4; ++i) - if (!NPC_GuardGUID[i]) + for (ObjectGuid& guid : _guardGuid) + if (!guid) { - NPC_GuardGUID[i] = creature->GetGUID(); + guid = creature->GetGUID(); break; } break; case NPC_DEFENSE_DUMMY_TARGET: creature->ApplySpellImmune(0, IMMUNITY_ID, SPELL_ARCANE_LIGHTNING, true); break; - case NPC_TELEPORTATION_PORTAL: - NPC_PortalGUID = creature->GetGUID(); - break; - case NPC_PRISON_DOOR_SEAL: - NPC_DoorSealGUID = creature->GetGUID(); - break; - // BOSSES BELOW: - case NPC_XEVOZZ: - NPC_XevozzGUID = creature->GetGUID(); - break; - case NPC_LAVANTHOR: - NPC_LavanthorGUID = creature->GetGUID(); - break; - case NPC_ICHORON: - NPC_IchoronGUID = creature->GetGUID(); - break; - case NPC_ZURAMAT: - NPC_ZuramatGUID = creature->GetGUID(); - break; - case NPC_EREKEM: - NPC_ErekemGUID = creature->GetGUID(); - break; case NPC_EREKEM_GUARD: - if (!NPC_ErekemGuardGUID[0]) - NPC_ErekemGuardGUID[0] = creature->GetGUID(); + if (!_erekemGuardGuid[0]) + _erekemGuardGuid[0] = creature->GetGUID(); else - NPC_ErekemGuardGUID[1] = creature->GetGUID(); - break; - case NPC_MORAGG: - NPC_MoraggGUID = creature->GetGUID(); - break; - case NPC_CYANIGOSA: - NPC_CyanigosaGUID = creature->GetGUID(); + _erekemGuardGuid[1] = creature->GetGUID(); break; } } void OnGameObjectCreate(GameObject* go) override { - switch (go->GetEntry()) + InstanceScript::OnGameObjectCreate(go); + + if (go->GetEntry() == GO_ACTIVATION_CRYSTAL) { - case GO_ACTIVATION_CRYSTAL: - HandleGameObject(ObjectGuid::Empty, false, go); // make go not used yet - go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); // not useable at the beginning - GO_ActivationCrystalGUID.push_back(go->GetGUID()); + HandleGameObject(ObjectGuid::Empty, false, go); + go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + _activationCrystalGuidList.push_back(go->GetGUID()); + } + } + + bool SetBossState(uint32 id, EncounterState state) override + { + if (!InstanceScript::SetBossState(id, state)) + return false; + + switch (id) + { + case BOSS_MORAGG: + case BOSS_EREKEM: + case BOSS_ICHORON: + case BOSS_LAVANTHOR: + case BOSS_XEVOZZ: + case BOSS_ZURAMAT: + if (state == DONE) + { + if (_waveCount == 6) + SetBossState(DATA_1ST_BOSS, DONE); + else if (_waveCount == 12) + SetBossState(DATA_2ND_BOSS, DONE); + _events.RescheduleEvent(EVENT_SUMMON_PORTAL, 35s); + } + else if (state == FAIL || state == NOT_STARTED) + { + _cleaned = false; + InstanceCleanup(); + } break; - case GO_MAIN_DOOR: - GO_MainGateGUID = go->GetGUID(); + case DATA_CYANIGOSA: + if (state == DONE) + { + _encounterStatus = DONE; + HandleGameObject(DATA_MAIN_DOOR, true); + DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 0); + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) + { + sinclari->AI()->Talk(SAY_SINCLARI_COMPLETE); + sinclari->DespawnOrUnsummon(); + sinclari->SetRespawnTime(3); + } + } + else if (state == FAIL || state == NOT_STARTED) + { + _cleaned = false; + InstanceCleanup(); + } break; - // BOSS GATES BELOW: - case GO_EREKEM_GUARD_1_DOOR: - GO_ErekemLeftGuardCellGUID = go->GetGUID(); - break; - case GO_EREKEM_GUARD_2_DOOR: - GO_ErekemRightGuardCellGUID = go->GetGUID(); - break; - case GO_EREKEM_DOOR: - GO_ErekemCellGUID = go->GetGUID(); - break; - case GO_ZURAMAT_DOOR: - GO_ZuramatCellGUID = go->GetGUID(); - break; - case GO_LAVANTHOR_DOOR: - GO_LavanthorCellGUID = go->GetGUID(); - break; - case GO_MORAGG_DOOR: - GO_MoraggCellGUID = go->GetGUID(); - break; - case GO_ICHORON_DOOR: - GO_IchoronCellGUID = go->GetGUID(); - break; - case GO_XEVOZZ_DOOR: - GO_XevozzCellGUID = go->GetGUID(); + default: break; } + + return true; } void SetData(uint32 type, uint32 data) override @@ -208,90 +196,57 @@ public: case DATA_ACTIVATE_DEFENSE_SYSTEM: { if (data) - bDefensesUsed = true; - const Position pos = {1919.09546f, 812.29724f, 86.2905f, M_PI}; + _defensesUsed = true; + Position const pos = {1919.09546f, 812.29724f, 86.2905f, M_PI}; instance->SummonCreature(NPC_DEFENSE_SYSTEM, pos, 0, 6499); } break; case DATA_START_INSTANCE: - if (EncounterStatus == NOT_STARTED) + if (_encounterStatus == NOT_STARTED) { - EncounterStatus = IN_PROGRESS; - if (Creature* c = instance->GetCreature(NPC_SinclariGUID)) + _encounterStatus = IN_PROGRESS; + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) { - c->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - c->AI()->Talk(SAY_SINCLARI_LEAVING); - /// @todo: Missing orientation for Sinclari's movement and "interaction" animation with the nearby crystal. + sinclari->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + sinclari->AI()->Talk(SAY_SINCLARI_LEAVING); } - events.RescheduleEvent(EVENT_GUARDS_FALL_BACK, 4s); + _events.RescheduleEvent(EVENT_GUARDS_FALL_BACK, 4s); } break; case DATA_PORTAL_DEFEATED: - events.RescheduleEvent(EVENT_SUMMON_PORTAL, 3s); + _events.RescheduleEvent(EVENT_SUMMON_PORTAL, 3s); break; case DATA_PORTAL_LOCATION: - PortalLocation = data; + _portalLocation = data; break; - case DATA_DECRASE_DOOR_HEALTH: - if (GateHealth > 0) - --GateHealth; - if (GateHealth == 0) + case DATA_DECREASE_DOOR_HEALTH: + if (_gateHealth > 0) + --_gateHealth; + if (_gateHealth == 0) { - CLEANED = false; + _cleaned = false; InstanceCleanup(); } - DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)GateHealth); + DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)_gateHealth); break; case DATA_RELEASE_BOSS: - if (WaveCount == 6) - StartBossEncounter(uiFirstBoss); + if (_waveCount == 6) + StartBossEncounter(GetPersistentData(PERSISTENT_DATA_FIRST_BOSS)); else - StartBossEncounter(uiSecondBoss); - break; - case DATA_BOSS_DIED: - if (WaveCount == 6) - m_auiEncounter[0] = DONE; - else if (WaveCount == 12) - m_auiEncounter[1] = DONE; - else if (WaveCount == 18) - { - m_auiEncounter[2] = DONE; - EncounterStatus = DONE; - HandleGameObject(GO_MainGateGUID, true); - DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 0); - if (Creature* c = instance->GetCreature(NPC_SinclariGUID)) - { - c->AI()->Talk(SAY_SINCLARI_COMPLETE); - c->DespawnOrUnsummon(); - c->SetRespawnTime(3); - } - } - SaveToDB(); - if (WaveCount < 18) - events.RescheduleEvent(EVENT_SUMMON_PORTAL, 35s); - break; - case DATA_FAILED: - CLEANED = false; - InstanceCleanup(); + StartBossEncounter(GetPersistentData(PERSISTENT_DATA_SECOND_BOSS)); break; case DATA_ACHIEV: - bAchiev = !!data; + _achievementCompleted = !!data; break; } } void SetGuidData(uint32 type, ObjectGuid data) override { - switch (type) - { - case DATA_ADD_TRASH_MOB: - trashMobs.insert(data); - break; - case DATA_DELETE_TRASH_MOB: - if (!CLEANED) - trashMobs.erase(data); - break; - } + if (type == DATA_ADD_TRASH_MOB) + _trashMobs.insert(data); + else if (type == DATA_DELETE_TRASH_MOB && !_cleaned) + _trashMobs.erase(data); } uint32 GetData(uint32 type) const override @@ -299,15 +254,11 @@ public: switch (type) { case DATA_ENCOUNTER_STATUS: - return (uint32)EncounterStatus; + return (uint32)_encounterStatus; case DATA_WAVE_COUNT: - return (uint32)WaveCount; + return (uint32)_waveCount; case DATA_PORTAL_LOCATION: - return PortalLocation; - case DATA_FIRST_BOSS_NUMBER: - return uiFirstBoss; - case DATA_SECOND_BOSS_NUMBER: - return uiSecondBoss; + return _portalLocation; } return 0; @@ -315,212 +266,180 @@ public: ObjectGuid GetGuidData(uint32 identifier) const override { - switch (identifier) - { - case DATA_TELEPORTATION_PORTAL_GUID: - return NPC_PortalGUID; - case DATA_DOOR_SEAL_GUID: - return NPC_DoorSealGUID; - case DATA_EREKEM_GUID: - return NPC_ErekemGUID; - case DATA_EREKEM_GUARD_1_GUID: - return NPC_ErekemGuardGUID[0]; - case DATA_EREKEM_GUARD_2_GUID: - return NPC_ErekemGuardGUID[1]; - case DATA_ICHORON_GUID: - return NPC_IchoronGUID; - } - + if (identifier == DATA_EREKEM_GUARD_1_GUID) + return _erekemGuardGuid[0]; + if (identifier == DATA_EREKEM_GUARD_2_GUID) + return _erekemGuardGuid[1]; return ObjectGuid::Empty; } - void StartBossEncounter(uint8 uiBoss) + void StartBossEncounter(uint32 bossId) { - Creature* pBoss = nullptr; - - switch (uiBoss) + switch (bossId) { case BOSS_MORAGG: - HandleGameObject(GO_MoraggCellGUID, true); - pBoss = instance->GetCreature(NPC_MoraggGUID); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove1); + HandleGameObject(DATA_MORAGG_CELL, true); break; case BOSS_EREKEM: - HandleGameObject(GO_ErekemCellGUID, true); - HandleGameObject(GO_ErekemRightGuardCellGUID, true); - HandleGameObject(GO_ErekemLeftGuardCellGUID, true); - pBoss = instance->GetCreature(NPC_ErekemGUID); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove2); - if (Creature* pGuard1 = instance->GetCreature(NPC_ErekemGuardGUID[0])) + HandleGameObject(DATA_EREKEM_CELL, true); + HandleGameObject(DATA_EREKEM_GUARD_2_CELL, true); + HandleGameObject(DATA_EREKEM_GUARD_1_CELL, true); + if (Creature* guard1 = instance->GetCreature(_erekemGuardGuid[0])) { - pGuard1->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - pGuard1->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - pGuard1->SetImmuneToNPC(false); - pGuard1->GetMotionMaster()->MovePoint(0, BossStartMove21); + guard1->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + guard1->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + guard1->SetImmuneToNPC(false); + guard1->GetMotionMaster()->MovePoint(0, BossStartMove21); } - if (Creature* pGuard2 = instance->GetCreature(NPC_ErekemGuardGUID[1])) + if (Creature* guard2 = instance->GetCreature(_erekemGuardGuid[1])) { - pGuard2->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - pGuard2->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - pGuard2->SetImmuneToNPC(false); - pGuard2->GetMotionMaster()->MovePoint(0, BossStartMove22); + guard2->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + guard2->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + guard2->SetImmuneToNPC(false); + guard2->GetMotionMaster()->MovePoint(0, BossStartMove22); } break; case BOSS_ICHORON: - HandleGameObject(GO_IchoronCellGUID, true); - pBoss = instance->GetCreature(NPC_IchoronGUID); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove3); + HandleGameObject(DATA_ICHORON_CELL, true); break; case BOSS_LAVANTHOR: - HandleGameObject(GO_LavanthorCellGUID, true); - pBoss = instance->GetCreature(NPC_LavanthorGUID); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove4); + HandleGameObject(DATA_LAVANTHOR_CELL, true); break; case BOSS_XEVOZZ: - HandleGameObject(GO_XevozzCellGUID, true); - pBoss = instance->GetCreature(NPC_XevozzGUID); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove5); + HandleGameObject(DATA_XEVOZZ_CELL, true); break; case BOSS_ZURAMAT: - HandleGameObject(GO_ZuramatCellGUID, true); - pBoss = instance->GetCreature(NPC_ZuramatGUID); - if (pBoss) - pBoss->GetMotionMaster()->MovePoint(0, BossStartMove6); + HandleGameObject(DATA_ZURAMAT_CELL, true); break; } - if (pBoss) + static Position const BossStartPositions[] = { - pBoss->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - pBoss->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - pBoss->SetImmuneToNPC(false); - pBoss->SetReactState(REACT_AGGRESSIVE); - if ((WaveCount == 6 && m_auiEncounter[0] == DONE) || (WaveCount == 12 && m_auiEncounter[1] == DONE)) - pBoss->SetLootMode(0); + BossStartMove1, // BOSS_MORAGG + BossStartMove2, // BOSS_EREKEM + BossStartMove3, // BOSS_ICHORON + BossStartMove4, // BOSS_LAVANTHOR + BossStartMove5, // BOSS_XEVOZZ + BossStartMove6, // BOSS_ZURAMAT + }; + + Creature* boss = GetCreature(bossId); + if (boss) + { + boss->GetMotionMaster()->MovePoint(0, BossStartPositions[bossId - BOSS_MORAGG]); + boss->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + boss->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + boss->SetImmuneToNPC(false); + boss->SetReactState(REACT_AGGRESSIVE); + if ((_waveCount == 6 && GetBossState(DATA_1ST_BOSS) == DONE) || (_waveCount == 12 && GetBossState(DATA_2ND_BOSS) == DONE)) + boss->SetLootMode(0); } } void Update(uint32 diff) override { - events.Update(diff); - switch (events.ExecuteEvent()) + _events.Update(diff); + switch (_events.ExecuteEvent()) { - case 0: - break; case EVENT_CHECK_PLAYERS: - { - if (DoNeedCleanup(false)) - InstanceCleanup(); - events.Repeat(5s); - } + if (DoNeedCleanup(false)) + InstanceCleanup(); + _events.Repeat(5s); break; case EVENT_GUARDS_FALL_BACK: - { - for (uint8 i = 0; i < 4; ++i) - if (Creature* c = instance->GetCreature(NPC_GuardGUID[i])) - { - c->SetReactState(REACT_PASSIVE); - c->CombatStop(); - c->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - c->GetMotionMaster()->MovePoint(0, guardMovePosition); - } - events.RescheduleEvent(EVENT_GUARDS_DISAPPEAR, 5s); - } + for (ObjectGuid const& guid : _guardGuid) + if (Creature* guard = instance->GetCreature(guid)) + { + guard->SetReactState(REACT_PASSIVE); + guard->CombatStop(); + guard->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + guard->GetMotionMaster()->MovePoint(0, guardMovePosition); + } + _events.RescheduleEvent(EVENT_GUARDS_DISAPPEAR, 5s); break; case EVENT_GUARDS_DISAPPEAR: - { - for (uint8 i = 0; i < 4; ++i) - if (Creature* c = instance->GetCreature(NPC_GuardGUID[i])) - c->SetVisible(false); - events.RescheduleEvent(EVENT_SINCLARI_FALL_BACK, 2s); - } + for (ObjectGuid const& guid : _guardGuid) + if (Creature* guard = instance->GetCreature(guid)) + guard->SetVisible(false); + _events.RescheduleEvent(EVENT_SINCLARI_FALL_BACK, 2s); break; case EVENT_SINCLARI_FALL_BACK: + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) { - if (Creature* c = instance->GetCreature(NPC_SinclariGUID)) - { - c->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - c->GetMotionMaster()->MovePoint(0, sinclariOutsidePosition); - } - SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 0); - events.RescheduleEvent(EVENT_START_ENCOUNTER, 4s); + sinclari->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + sinclari->GetMotionMaster()->MovePoint(0, sinclariOutsidePosition); } + SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 0); + _events.RescheduleEvent(EVENT_START_ENCOUNTER, 4s); break; case EVENT_START_ENCOUNTER: + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) { - if (Creature* c = instance->GetCreature(NPC_SinclariGUID)) - { - c->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - c->AI()->Talk(SAY_SINCLARI_DOOR_LOCK); - } - if (Creature* c = instance->GetCreature(NPC_DoorSealGUID)) - { - c->RemoveAllAuras(); // just to be sure... - } - GateHealth = 100; - HandleGameObject(GO_MainGateGUID, false); - DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 1); - DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)GateHealth); - DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)WaveCount); - - for (ObjectGuid const& guid : GO_ActivationCrystalGUID) - if (GameObject* go = instance->GetGameObject(guid)) - { - HandleGameObject(ObjectGuid::Empty, false, go); // not used yet - go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); // make it useable - } - events.RescheduleEvent(EVENT_SUMMON_PORTAL, 4s); + sinclari->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + sinclari->AI()->Talk(SAY_SINCLARI_DOOR_LOCK); } + if (Creature* doorSeal = GetCreature(DATA_DOOR_SEAL)) + doorSeal->RemoveAllAuras(); + _gateHealth = 100; + HandleGameObject(DATA_MAIN_DOOR, false); + DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 1); + DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)_gateHealth); + DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)_waveCount); + + for (ObjectGuid const& guid : _activationCrystalGuidList) + if (GameObject* go = instance->GetGameObject(guid)) + { + HandleGameObject(ObjectGuid::Empty, false, go); + go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + } + _events.RescheduleEvent(EVENT_SUMMON_PORTAL, 4s); break; case EVENT_SUMMON_PORTAL: - ++WaveCount; - DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)WaveCount); + ++_waveCount; + DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)_waveCount); SetData(DATA_PORTAL_LOCATION, (GetData(DATA_PORTAL_LOCATION) + urand(1, 5)) % 6); - if (Creature* c = instance->GetCreature(NPC_SinclariGUID)) + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) { - if (WaveCount % 6 != 0) - c->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocations[GetData(DATA_PORTAL_LOCATION)], TEMPSUMMON_CORPSE_DESPAWN); - else if (WaveCount == 6 || WaveCount == 12) // first or second boss + if (_waveCount % 6 != 0) + sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, PortalLocations[GetData(DATA_PORTAL_LOCATION)], TEMPSUMMON_CORPSE_DESPAWN); + else if (_waveCount == 6 || _waveCount == 12) { - if (!uiFirstBoss || !uiSecondBoss) + if (!GetPersistentData(PERSISTENT_DATA_FIRST_BOSS) || !GetPersistentData(PERSISTENT_DATA_SECOND_BOSS)) { - uiFirstBoss = urand(1, 6); - do { uiSecondBoss = urand(1, 6); } - while (uiFirstBoss == uiSecondBoss); - SaveToDB(); + uint32 firstBoss = urand(BOSS_MORAGG, BOSS_ZURAMAT); + uint32 secondBoss; + do { secondBoss = urand(BOSS_MORAGG, BOSS_ZURAMAT); } + while (firstBoss == secondBoss); + StorePersistentData(PERSISTENT_DATA_FIRST_BOSS, firstBoss); + StorePersistentData(PERSISTENT_DATA_SECOND_BOSS, secondBoss); } - c->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN); + sinclari->SummonCreature(NPC_TELEPORTATION_PORTAL, MiddleRoomPortalSaboLocation, TEMPSUMMON_CORPSE_DESPAWN); } - else // cyanigossa + else { - if (Creature* cyanigosa = c->SummonCreature(NPC_CYANIGOSA, CyanigosasSpawnLocation, TEMPSUMMON_DEAD_DESPAWN)) + if (Creature* cyanigosa = sinclari->SummonCreature(NPC_CYANIGOSA, CyanigosasSpawnLocation, TEMPSUMMON_DEAD_DESPAWN)) { cyanigosa->CastSpell(cyanigosa, SPELL_CYANIGOSA_BLUE_AURA, false); cyanigosa->AI()->Talk(CYANIGOSA_SAY_SPAWN); cyanigosa->GetMotionMaster()->MoveJump(MiddleRoomLocation.GetPositionX(), MiddleRoomLocation.GetPositionY(), MiddleRoomLocation.GetPositionZ(), 10.0f, 20.0f); } - events.RescheduleEvent(EVENT_CYANIGOSSA_TRANSFORM, 10s); + _events.RescheduleEvent(EVENT_CYANIGOSA_TRANSFORM, 10s); } } break; - case EVENT_CYANIGOSSA_TRANSFORM: - if (Creature* c = instance->GetCreature(NPC_CyanigosaGUID)) + case EVENT_CYANIGOSA_TRANSFORM: + if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA)) { - c->RemoveAurasDueToSpell(SPELL_CYANIGOSA_BLUE_AURA); - c->CastSpell(c, SPELL_CYANIGOSA_TRANSFORM, 0); - events.RescheduleEvent(EVENT_CYANIGOSA_ATTACK, 2500ms); + cyanigosa->RemoveAurasDueToSpell(SPELL_CYANIGOSA_BLUE_AURA); + cyanigosa->CastSpell(cyanigosa, SPELL_CYANIGOSA_TRANSFORM, 0); + _events.RescheduleEvent(EVENT_CYANIGOSA_ATTACK, 2500ms); } break; case EVENT_CYANIGOSA_ATTACK: - if (Creature* c = instance->GetCreature(NPC_CyanigosaGUID)) + if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA)) { - c->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - c->SetImmuneToNPC(false); + cyanigosa->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + cyanigosa->SetImmuneToNPC(false); } break; } @@ -531,121 +450,118 @@ public: if (DoNeedCleanup(plr->IsAlive())) InstanceCleanup(); - if (EncounterStatus == IN_PROGRESS) + if (_encounterStatus == IN_PROGRESS) { plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 1); - plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)GateHealth); - plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)WaveCount); + plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_PRISON_STATE, (uint32)_gateHealth); + plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_WAVE_COUNT, (uint32)_waveCount); } else plr->SendUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 0); - events.RescheduleEvent(EVENT_CHECK_PLAYERS, 5s); + _events.RescheduleEvent(EVENT_CHECK_PLAYERS, 5s); } bool DoNeedCleanup(bool enter) { - uint8 aliveCount = 0; - Map::PlayerList const& pl = instance->GetPlayers(); - for( Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr ) - if (Player* plr = itr->GetSource()) - if (plr->IsAlive() && !plr->IsGameMaster() && !plr->HasAura(27827)/*spirit of redemption aura*/ ) - ++aliveCount; - + uint32 aliveCount = instance->GetPlayersCountExceptGMs(true); bool need = enter ? aliveCount <= 1 : aliveCount == 0; - if (!need && CLEANED) - CLEANED = false; + if (!need && _cleaned) + _cleaned = false; return need; } void InstanceCleanup() { - if (CLEANED) + if (_cleaned) return; - CLEANED = true; + _cleaned = true; - // reset defense crystals - for (ObjectGuid const& guid : GO_ActivationCrystalGUID) + for (ObjectGuid const& guid : _activationCrystalGuidList) if (GameObject* go = instance->GetGameObject(guid)) { - HandleGameObject(ObjectGuid::Empty, false, go); // not used yet - go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); // not useable at the beginning + HandleGameObject(ObjectGuid::Empty, false, go); + go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); } - // reset positions of Sinclari and Guards - if (Creature* c = instance->GetCreature(NPC_SinclariGUID)) + if (Creature* sinclari = GetCreature(DATA_SINCLARI)) { - c->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); - c->DespawnOrUnsummon(); - c->SetRespawnTime(3); + sinclari->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); + sinclari->DespawnOrUnsummon(); + sinclari->SetRespawnTime(3); } - for (uint8 i = 0; i < 4; ++i) - if (Creature* c = instance->GetCreature(NPC_GuardGUID[i])) + for (ObjectGuid const& guid : _guardGuid) + if (Creature* guard = instance->GetCreature(guid)) { - c->DespawnOrUnsummon(); - c->SetRespawnTime(3); - if (m_auiEncounter[MAX_ENCOUNTER - 1] == DONE) - c->SetVisible(false); - else - c->SetVisible(true); - c->SetReactState(REACT_AGGRESSIVE); + guard->DespawnOrUnsummon(); + guard->SetRespawnTime(3); + guard->SetVisible(GetBossState(DATA_CYANIGOSA) != DONE); + guard->SetReactState(REACT_AGGRESSIVE); } - // remove portal if any - if (Creature* c = instance->GetCreature(NPC_PortalGUID)) - c->DespawnOrUnsummon(); - NPC_PortalGUID.Clear(); + if (Creature* portal = GetCreature(DATA_TELEPORTATION_PORTAL)) + portal->DespawnOrUnsummon(); - // remove trash - for (ObjectGuid const& guid : trashMobs) - if (Creature* c = instance->GetCreature(guid)) - c->DespawnOrUnsummon(); + for (ObjectGuid const& guid : _trashMobs) + if (Creature* trash = instance->GetCreature(guid)) + trash->DespawnOrUnsummon(); - trashMobs.clear(); + _trashMobs.clear(); - // clear door seal damaging auras: - if (Creature* c = instance->GetCreature(NPC_DoorSealGUID)) - c->RemoveAllAuras(); + if (Creature* doorSeal = GetCreature(DATA_DOOR_SEAL)) + doorSeal->RemoveAllAuras(); - // open main gate - HandleGameObject(GO_MainGateGUID, true); + HandleGameObject(DATA_MAIN_DOOR, true); - if (m_auiEncounter[MAX_ENCOUNTER - 1] != DONE) // instance not finished + if (GetBossState(DATA_CYANIGOSA) != DONE) { - // close all cells - HandleGameObject(GO_MoraggCellGUID, false); - HandleGameObject(GO_ErekemCellGUID, false); - HandleGameObject(GO_ErekemRightGuardCellGUID, false); - HandleGameObject(GO_ErekemLeftGuardCellGUID, false); - HandleGameObject(GO_IchoronCellGUID, false); - HandleGameObject(GO_LavanthorCellGUID, false); - HandleGameObject(GO_XevozzCellGUID, false); - HandleGameObject(GO_ZuramatCellGUID, false); + HandleGameObject(DATA_MORAGG_CELL, false); + HandleGameObject(DATA_EREKEM_CELL, false); + HandleGameObject(DATA_EREKEM_GUARD_2_CELL, false); + HandleGameObject(DATA_EREKEM_GUARD_1_CELL, false); + HandleGameObject(DATA_ICHORON_CELL, false); + HandleGameObject(DATA_LAVANTHOR_CELL, false); + HandleGameObject(DATA_XEVOZZ_CELL, false); + HandleGameObject(DATA_ZURAMAT_CELL, false); - // respawn bosses - if (Creature* c = instance->GetCreature(NPC_MoraggGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); } - if (Creature* c = instance->GetCreature(NPC_MoraggGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); } - if (Creature* c = instance->GetCreature(NPC_ErekemGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); } - if (Creature* c = instance->GetCreature(NPC_ErekemGuardGUID[0])) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); } - if (Creature* c = instance->GetCreature(NPC_ErekemGuardGUID[1])) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); } - if (Creature* c = instance->GetCreature(NPC_IchoronGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); } - if (Creature* c = instance->GetCreature(NPC_LavanthorGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); } - if (Creature* c = instance->GetCreature(NPC_XevozzGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); } - if (Creature* c = instance->GetCreature(NPC_ZuramatGUID)) { c->DespawnOrUnsummon(); c->SetRespawnTime(3); c->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); c->SetImmuneToNPC(true); } - if (Creature* c = instance->GetCreature(NPC_CyanigosaGUID)) { c->DespawnOrUnsummon(); } + for (uint32 id = BOSS_MORAGG; id <= BOSS_ZURAMAT; ++id) + { + if (Creature* boss = GetCreature(id)) + { + boss->DespawnOrUnsummon(); + boss->SetRespawnTime(3); + boss->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + boss->SetImmuneToNPC(true); + } + } + if (Creature* guard1 = instance->GetCreature(_erekemGuardGuid[0])) + { + guard1->DespawnOrUnsummon(); + guard1->SetRespawnTime(3); + guard1->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + guard1->SetImmuneToNPC(true); + } + if (Creature* guard2 = instance->GetCreature(_erekemGuardGuid[1])) + { + guard2->DespawnOrUnsummon(); + guard2->SetRespawnTime(3); + guard2->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + guard2->SetImmuneToNPC(true); + } + if (Creature* cyanigosa = GetCreature(DATA_CYANIGOSA)) + cyanigosa->DespawnOrUnsummon(); } - // reinitialize variables and events DoUpdateWorldState(WORLD_STATE_VIOLET_HOLD_SHOW, 0); - EncounterStatus = NOT_STARTED; - GateHealth = 100; - WaveCount = 0; - bDefensesUsed = false; - if (m_auiEncounter[MAX_ENCOUNTER - 1] == DONE) - EncounterStatus = DONE; - events.Reset(); - events.RescheduleEvent(EVENT_CHECK_PLAYERS, 5s); + _encounterStatus = NOT_STARTED; + _gateHealth = 100; + _waveCount = 0; + _defensesUsed = false; + if (GetBossState(DATA_CYANIGOSA) == DONE) + _encounterStatus = DONE; + _events.Reset(); + _events.RescheduleEvent(EVENT_CHECK_PLAYERS, 5s); } bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override @@ -653,35 +569,31 @@ public: switch (criteria_id) { case CRITERIA_DEFENSELESS: - return GateHealth == 100 && !bDefensesUsed; + return _gateHealth == 100 && !_defensesUsed; case CRITERIA_A_VOID_DANCE: case CRITERIA_DEHYDRATION: - return bAchiev; + return _achievementCompleted; } return false; } - void ReadSaveDataMore(std::istringstream& data) override - { - EncounterStatus = NOT_STARTED; - CLEANED = false; - events.Reset(); - events.RescheduleEvent(EVENT_CHECK_PLAYERS, 0ms); + private: + bool _cleaned{ false }; + uint8 _encounterStatus{ NOT_STARTED }; + EventMap _events; + uint8 _gateHealth{ 100 }; + uint8 _waveCount{ 0 }; + uint8 _portalLocation{ 0 }; + bool _achievementCompleted{ false }; + bool _defensesUsed{ false }; - data >> m_auiEncounter[0]; - data >> m_auiEncounter[1]; - data >> m_auiEncounter[2]; - data >> uiFirstBoss; - } + GuidVector _activationCrystalGuidList; - void WriteSaveDataMore(std::ostringstream& data) override - { - data << m_auiEncounter[0] << ' ' - << m_auiEncounter[1] << ' ' - << m_auiEncounter[2] << ' ' - << uiFirstBoss << ' ' - << uiSecondBoss << ' '; - } + // Multi-instance creature tracking (can't use ObjectData) + ObjectGuid _guardGuid[4]; + ObjectGuid _erekemGuardGuid[2]; + + GuidSet _trashMobs; }; }; diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp index c805c43f0..ccbe09fc9 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp @@ -51,9 +51,9 @@ public: bool OnGossipHello(Player* /*player*/, GameObject* go) override { - if (InstanceScript* pInstance = go->GetInstanceScript()) + if (InstanceScript* Instance = go->GetInstanceScript()) { - pInstance->SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 1); + Instance->SetData(DATA_ACTIVATE_DEFENSE_SYSTEM, 1); go->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); } @@ -72,8 +72,8 @@ public: bool OnGossipHello(Player* player, Creature* creature) override { - if (InstanceScript* pInstance = creature->GetInstanceScript()) - switch (pInstance->GetData(DATA_ENCOUNTER_STATUS)) + if (InstanceScript* Instance = creature->GetInstanceScript()) + switch (Instance->GetData(DATA_ENCOUNTER_STATUS)) { case NOT_STARTED: AddGossipItemFor(player, GOSSIP_MENU_START_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); @@ -83,17 +83,17 @@ public: AddGossipItemFor(player, GOSSIP_MENU_LATE_JOIN, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); SendGossipMenuFor(player, NPC_TEXT_SINCLARI_LATE_JOIN, creature->GetGUID()); break; - default: // DONE or invalid + default: SendGossipMenuFor(player, NPC_TEXT_SINCLARI_DONE, creature->GetGUID()); } return true; } - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*uiSender*/, uint32 uiAction) override + bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override { ClearGossipMenuFor(player); - switch (uiAction) + switch (action) { case GOSSIP_ACTION_INFO_DEF+1: AddGossipItemFor(player, GOSSIP_MENU_START_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); @@ -101,8 +101,8 @@ public: break; case GOSSIP_ACTION_INFO_DEF+2: CloseGossipMenuFor(player); - if (InstanceScript* pInstance = creature->GetInstanceScript()) - pInstance->SetData(DATA_START_INSTANCE, 1); + if (InstanceScript* Instance = creature->GetInstanceScript()) + Instance->SetData(DATA_START_INSTANCE, 1); break; case GOSSIP_ACTION_INFO_DEF+3: player->NearTeleportTo(playerTeleportPosition.GetPositionX(), playerTeleportPosition.GetPositionY(), playerTeleportPosition.GetPositionZ(), playerTeleportPosition.GetOrientation(), true); @@ -126,143 +126,122 @@ enum PortalEvents EVENT_CHECK_DEATHS, }; -class npc_vh_teleportation_portal : public CreatureScript +struct npc_vh_teleportation_portal : public NullCreatureAI { -public: - npc_vh_teleportation_portal() : CreatureScript("npc_vh_teleportation_portal") { } - - CreatureAI* GetAI(Creature* creature) const override + npc_vh_teleportation_portal(Creature* c) : NullCreatureAI(c), _listOfMobs(me) { - return GetVioletHoldAI(creature); + _instance = c->GetInstanceScript(); + _events.Reset(); + _wave = _instance->GetData(DATA_WAVE_COUNT); + _isKeeperOrGuardian = false; + _spawned = false; + + if (_wave < 12) + _addValue = 0; + else + _addValue = 1; + + if (_wave % 6 != 0) + _events.RescheduleEvent(RAND(EVENT_SUMMON_KEEPER_OR_GUARDIAN, EVENT_SUMMON_ELITES), 10s); + else + _events.RescheduleEvent(EVENT_SUMMON_SABOTEOUR, 3s); } - struct npc_vh_teleportation_portalAI : public NullCreatureAI + void UpdateAI(uint32 diff) override { - npc_vh_teleportation_portalAI(Creature* c) : NullCreatureAI(c), listOfMobs(me) + _events.Update(diff); + + switch (_events.ExecuteEvent()) { - pInstance = c->GetInstanceScript(); - events.Reset(); - if (pInstance) - { - wave = pInstance->GetData(DATA_WAVE_COUNT); - bKorG = false; - spawned = false; - - if (wave < 12) - addValue = 0; - else - addValue = 1; - - if (wave % 6 != 0) - events.RescheduleEvent(RAND(EVENT_SUMMON_KEEPER_OR_GUARDIAN, EVENT_SUMMON_ELITES), 10s); - else - events.RescheduleEvent(EVENT_SUMMON_SABOTEOUR, 3s); - } - } - - InstanceScript* pInstance; - SummonList listOfMobs; - EventMap events; - uint8 wave; - uint8 addValue; - bool bKorG; - bool spawned; - - void UpdateAI(uint32 diff) override - { - if (!pInstance) - return; - - events.Update(diff); - - switch (events.ExecuteEvent()) - { - case 0: - break; - case EVENT_SUMMON_KEEPER_OR_GUARDIAN: - bKorG = true; - spawned = true; - if (Creature* c = DoSummon(RAND(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER_1, NPC_PORTAL_KEEPER_2), me, 2.0f, 0, TEMPSUMMON_DEAD_DESPAWN)) - me->CastSpell(c, SPELL_PORTAL_CHANNEL, false); - events.RescheduleEvent(EVENT_SUMMON_KEEPER_TRASH, 20s); - break; - case EVENT_SUMMON_KEEPER_TRASH: - for (uint8 i = 0; i < 3 + addValue; ++i) - { - uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_INVADER_2, NPC_AZURE_SPELLBREAKER_1, NPC_AZURE_SPELLBREAKER_2, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_MAGE_SLAYER_2, NPC_AZURE_BINDER_1, NPC_AZURE_BINDER_2); - DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); - } - events.Repeat(20s); - break; - case EVENT_SUMMON_ELITES: - spawned = true; - for (uint8 i = 0; i < 2 + addValue; ++i) - { - uint32 entry = RAND(NPC_AZURE_CAPTAIN, NPC_AZURE_RAIDER, NPC_AZURE_STALKER, NPC_AZURE_SORCEROR); - DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); - } - me->SetVisible(false); - break; - case EVENT_SUMMON_SABOTEOUR: - DoSummon(NPC_SABOTEOUR, me, 2.0f, 0, TEMPSUMMON_CORPSE_DESPAWN); - me->DespawnOrUnsummon(3s); - break; - } - - if (!spawned) - return; - - if (bKorG) - { - if (!me->IsNonMeleeSpellCast(false)) // keeper/guardian died => channeled spell interrupted + case EVENT_SUMMON_KEEPER_OR_GUARDIAN: + _isKeeperOrGuardian = true; + _spawned = true; + if (Creature* c = DoSummon(RAND(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER_1, NPC_PORTAL_KEEPER_2), me, 2.0f, 0, TEMPSUMMON_DEAD_DESPAWN)) + DoCast(c, SPELL_PORTAL_CHANNEL); + _events.RescheduleEvent(EVENT_SUMMON_KEEPER_TRASH, 20s); + break; + case EVENT_SUMMON_KEEPER_TRASH: + for (uint8 i = 0; i < 3 + _addValue; ++i) { - // if keeper/guard lost all victims, in enterevademode linking aura is removed, restore it: - if (pInstance) - for (SummonList::iterator itr = listOfMobs.begin(); itr != listOfMobs.end(); ++itr) - if (Creature* c = pInstance->instance->GetCreature(*itr)) - if (c->IsAlive() && c->EntryEquals(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER_1, NPC_PORTAL_KEEPER_2)) - { - me->CastSpell(c, SPELL_PORTAL_CHANNEL, false); - return; - } - - Unit::Kill(me, me, false); + uint32 entry = RAND(NPC_AZURE_INVADER_1, NPC_AZURE_INVADER_2, NPC_AZURE_SPELLBREAKER_1, NPC_AZURE_SPELLBREAKER_2, NPC_AZURE_MAGE_SLAYER_1, NPC_AZURE_MAGE_SLAYER_2, NPC_AZURE_BINDER_1, NPC_AZURE_BINDER_2); + DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); } - } - else - { - if (listOfMobs.empty()) - Unit::Kill(me, me, false); - } + _events.Repeat(20s); + break; + case EVENT_SUMMON_ELITES: + _spawned = true; + for (uint8 i = 0; i < 2 + _addValue; ++i) + { + uint32 entry = RAND(NPC_AZURE_CAPTAIN, NPC_AZURE_RAIDER, NPC_AZURE_STALKER, NPC_AZURE_SORCEROR); + DoSummon(entry, me, 2.0f, 20000, TEMPSUMMON_DEAD_DESPAWN); + } + me->SetVisible(false); + break; + case EVENT_SUMMON_SABOTEOUR: + DoSummon(NPC_SABOTEOUR, me, 2.0f, 0, TEMPSUMMON_CORPSE_DESPAWN); + me->DespawnOrUnsummon(3s); + break; } - void JustDied(Unit* /*killer*/) override - { - events.Reset(); - if (wave % 6 == 0) // just to be sure, shouln't occur - return; - if (pInstance) - pInstance->SetData(DATA_PORTAL_DEFEATED, 0); - } + if (!_spawned) + return; - void JustSummoned(Creature* pSummoned) override + if (_isKeeperOrGuardian) { - if (pSummoned) + if (!me->IsNonMeleeSpellCast(false)) { - listOfMobs.Summon(pSummoned); - pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID()); + for (SummonList::iterator itr = _listOfMobs.begin(); itr != _listOfMobs.end(); ++itr) + if (Creature* c = _instance->instance->GetCreature(*itr)) + if (c->IsAlive() && c->EntryEquals(NPC_PORTAL_GUARDIAN, NPC_PORTAL_KEEPER_1, NPC_PORTAL_KEEPER_2)) + { + DoCast(c, SPELL_PORTAL_CHANNEL); + return; + } + + Unit::Kill(me, me, false); } } - - void SummonedMobDied(Creature* pSummoned) + else { - if (pSummoned) - { - listOfMobs.Despawn(pSummoned); - pInstance->SetGuidData(DATA_DELETE_TRASH_MOB, pSummoned->GetGUID()); - } + if (_listOfMobs.empty()) + Unit::Kill(me, me, false); } - }; + } + + void JustDied(Unit* /*killer*/) override + { + _events.Reset(); + if (_wave % 6 == 0) + return; + _instance->SetData(DATA_PORTAL_DEFEATED, 0); + } + + void JustSummoned(Creature* summoned) override + { + if (summoned) + { + _listOfMobs.Summon(summoned); + _instance->SetGuidData(DATA_ADD_TRASH_MOB, summoned->GetGUID()); + } + } + + void SummonedMobDied(Creature* summoned) + { + if (summoned) + { + _listOfMobs.Despawn(summoned); + _instance->SetGuidData(DATA_DELETE_TRASH_MOB, summoned->GetGUID()); + } + } + +private: + InstanceScript* _instance; + SummonList _listOfMobs; + EventMap _events; + uint8 _wave; + uint8 _addValue; + bool _isKeeperOrGuardian; + bool _spawned; }; /*********** @@ -273,23 +252,16 @@ struct violet_hold_trashAI : public npc_escortAI { violet_hold_trashAI(Creature* c) : npc_escortAI(c) { - pInstance = c->GetInstanceScript(); - if (pInstance) - PLoc = pInstance->GetData(DATA_PORTAL_LOCATION); - bAddedWP = false; - bAlt = false; + Instance = c->GetInstanceScript(); + PortalLoc = Instance->GetData(DATA_PORTAL_LOCATION); + AddedWaypoints = false; + UseAlternate = false; } - InstanceScript* pInstance; - bool bAddedWP; - uint32 PLoc; - bool bAlt; - void ClearDoorSealAura() { - if (pInstance) - if (Creature* c = pInstance->instance->GetCreature(pInstance->GetGuidData(DATA_DOOR_SEAL_GUID))) - c->RemoveAura(SPELL_DESTROY_DOOR_SEAL, me->GetGUID()); + if (Creature* c = Instance->GetCreature(DATA_DOOR_SEAL)) + c->RemoveAura(SPELL_DESTROY_DOOR_SEAL, me->GetGUID()); } void JustEngagedWith(Unit* who) override @@ -315,8 +287,8 @@ struct violet_hold_trashAI : public npc_escortAI using CreatureAI::WaypointReached; void WaypointReached(uint32 id) override { - if (PLoc < 6) - if (id == uint16(PLocWPCount[PLoc] - 1 - (bAlt ? 1 : 0))) + if (PortalLoc < 6) + if (id == uint16(PLocWPCount[PortalLoc] - 1 - (UseAlternate ? 1 : 0))) CreatureStartAttackDoor(); } @@ -327,48 +299,48 @@ struct violet_hold_trashAI : public npc_escortAI void UpdateAI(uint32 diff) override { - if (!bAddedWP) + if (!AddedWaypoints) { - bAddedWP = true; - switch (PLoc) + AddedWaypoints = true; + switch (PortalLoc) { case 0: - for(int i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) AddWaypoint(i, FirstPortalTrashWPs[i][0] + irand(-1, 1), FirstPortalTrashWPs[i][1] + irand(-1, 1), FirstPortalTrashWPs[i][2] + irand(-1, 1), 0); me->SetHomePosition(FirstPortalTrashWPs[5][0], FirstPortalTrashWPs[5][1], FirstPortalTrashWPs[5][2], 3.149439f); break; case 1: - bAlt = (bool)urand(0, 1); - if (!bAlt) + UseAlternate = (bool)urand(0, 1); + if (!UseAlternate) { - for(int i = 0; i < 9; i++) + for (int i = 0; i < 9; i++) AddWaypoint(i, SecondPortalTrashWPs1[i][0] + irand(-1, 1), SecondPortalTrashWPs1[i][1] + irand(-1, 1), SecondPortalTrashWPs1[i][2], 0); me->SetHomePosition(SecondPortalTrashWPs1[8][0] + irand(-1, 1), SecondPortalTrashWPs1[8][1] + irand(-1, 1), SecondPortalTrashWPs1[8][2] + irand(-1, 1), 3.149439f); } else { - for(int i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) AddWaypoint(i, SecondPortalTrashWPs2[i][0] + irand(-1, 1), SecondPortalTrashWPs2[i][1] + irand(-1, 1), SecondPortalTrashWPs2[i][2], 0); me->SetHomePosition(SecondPortalTrashWPs2[7][0], SecondPortalTrashWPs2[7][1], SecondPortalTrashWPs2[7][2], 3.149439f); } break; case 2: - for(int i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) AddWaypoint(i, ThirdPortalTrashWPs[i][0] + irand(-1, 1), ThirdPortalTrashWPs[i][1] + irand(-1, 1), ThirdPortalTrashWPs[i][2], 0); me->SetHomePosition(ThirdPortalTrashWPs[7][0], ThirdPortalTrashWPs[7][1], ThirdPortalTrashWPs[7][2], 3.149439f); break; case 3: - for(int i = 0; i < 9; i++) + for (int i = 0; i < 9; i++) AddWaypoint(i, FourthPortalTrashWPs[i][0] + irand(-1, 1), FourthPortalTrashWPs[i][1] + irand(-1, 1), FourthPortalTrashWPs[i][2], 0); me->SetHomePosition(FourthPortalTrashWPs[8][0], FourthPortalTrashWPs[8][1], FourthPortalTrashWPs[8][2], 3.149439f); break; case 4: - for(int i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) AddWaypoint(i, FifthPortalTrashWPs[i][0] + irand(-1, 1), FifthPortalTrashWPs[i][1] + irand(-1, 1), FifthPortalTrashWPs[i][2], 0); me->SetHomePosition(FifthPortalTrashWPs[5][0], FifthPortalTrashWPs[5][1], FifthPortalTrashWPs[5][2], 3.149439f); break; case 5: - for(int i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) AddWaypoint(i, SixthPoralTrashWPs[i][0] + irand(-1, 1), SixthPoralTrashWPs[i][1] + irand(-1, 1), SixthPoralTrashWPs[i][2], 0); me->SetHomePosition(SixthPoralTrashWPs[3][0], SixthPoralTrashWPs[3][1], SixthPoralTrashWPs[3][2], 3.149439f); break; @@ -382,16 +354,15 @@ struct violet_hold_trashAI : public npc_escortAI void JustDied(Unit* /*unit*/) override { - if (pInstance) - if (Creature* portal = ObjectAccessor::GetCreature((*me), pInstance->GetGuidData(DATA_TELEPORTATION_PORTAL_GUID))) - CAST_AI(npc_vh_teleportation_portal::npc_vh_teleportation_portalAI, portal->AI())->SummonedMobDied(me); + if (Creature* portal = Instance->GetCreature(DATA_TELEPORTATION_PORTAL)) + CAST_AI(npc_vh_teleportation_portal, portal->AI())->SummonedMobDied(me); } void CreatureStartAttackDoor() { RemoveEscortState(STATE_ESCORT_ESCORTING | STATE_ESCORT_RETURNING | STATE_ESCORT_PAUSED); me->SetImmuneToNPC(true); - me->CastSpell((Unit*)nullptr, SPELL_DESTROY_DOOR_SEAL, true); + DoCastAOE(SPELL_DESTROY_DOOR_SEAL, true); } void EnterEvadeMode(EvadeReason /*why*/) override @@ -416,6 +387,12 @@ struct violet_hold_trashAI : public npc_escortAI } me->ClearUnitState(UNIT_STATE_EVADE); } + +protected: + InstanceScript* Instance; + bool AddedWaypoints; + uint32 PortalLoc; + bool UseAlternate; }; /*********** @@ -477,509 +454,446 @@ enum AzureStalkerSpells }; /*********** -** TRASH +** TRASH — AZURE INVADER ***********/ -class npc_azure_invader : public CreatureScript +enum InvaderEvents { -public: - npc_azure_invader() : CreatureScript("npc_azure_invader") { } + EVENT_INVADER_CLEAVE = 1, + EVENT_INVADER_IMPALE, + EVENT_INVADER_BRUTAL_STRIKE, + EVENT_INVADER_SUNDER_ARMOR, +}; - CreatureAI* GetAI(Creature* creature) const override +struct npc_azure_invader : public violet_hold_trashAI +{ + npc_azure_invader(Creature* c) : violet_hold_trashAI(c) {} + + void Reset() override { - return GetVioletHoldAI(creature); + _events.Reset(); + _events.RescheduleEvent(EVENT_INVADER_CLEAVE, 5s); + _events.RescheduleEvent(EVENT_INVADER_IMPALE, 4s); + _events.RescheduleEvent(EVENT_INVADER_BRUTAL_STRIKE, 5s); + _events.RescheduleEvent(EVENT_INVADER_SUNDER_ARMOR, 4s); } - struct npc_azure_invaderAI : public violet_hold_trashAI + void UpdateAI(uint32 diff) override { - npc_azure_invaderAI(Creature* c) : violet_hold_trashAI(c) {} + violet_hold_trashAI::UpdateAI(diff); - uint32 uiCleaveTimer; - uint32 uiImpaleTimer; - uint32 uiBrutalStrikeTimer; - uint32 uiSunderArmorTimer; + if (!UpdateVictim()) + return; - void Reset() override + _events.Update(diff); + + switch (_events.ExecuteEvent()) { - uiCleaveTimer = 5000; - uiImpaleTimer = 4000; - uiBrutalStrikeTimer = 5000; - uiSunderArmorTimer = 4000; - } - - void UpdateAI(uint32 diff) override - { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; - - if (me->GetEntry() == NPC_AZURE_INVADER_1) - { - if (uiCleaveTimer <= diff) - { + case EVENT_INVADER_CLEAVE: + if (me->GetEntry() == NPC_AZURE_INVADER_1) DoCast(me->GetVictim(), SPELL_CLEAVE); - uiCleaveTimer = 5000; - } - else uiCleaveTimer -= diff; - - if (uiImpaleTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 5.0f, true); - if (pTarget) - DoCast(pTarget, SPELL_IMPALE); - uiImpaleTimer = 4000; - } - else uiImpaleTimer -= diff; - } - - if (me->GetEntry() == NPC_AZURE_INVADER_2) - { - if (uiBrutalStrikeTimer <= diff) - { + _events.Repeat(5s); + break; + case EVENT_INVADER_IMPALE: + if (me->GetEntry() == NPC_AZURE_INVADER_1) + DoCastRandomTarget(SPELL_IMPALE, 0, 5.0f); + _events.Repeat(4s); + break; + case EVENT_INVADER_BRUTAL_STRIKE: + if (me->GetEntry() == NPC_AZURE_INVADER_2) DoCast(me->GetVictim(), SPELL_BRUTAL_STRIKE); - uiBrutalStrikeTimer = 5000; - } - else uiBrutalStrikeTimer -= diff; - - if (uiSunderArmorTimer <= diff) - { + _events.Repeat(5s); + break; + case EVENT_INVADER_SUNDER_ARMOR: + if (me->GetEntry() == NPC_AZURE_INVADER_2) DoCast(me->GetVictim(), SPELL_SUNDER_ARMOR); - uiSunderArmorTimer = urand(8000, 10000); - } - else uiSunderArmorTimer -= diff; - } - - DoMeleeAttackIfReady(); + _events.Repeat(8s, 10s); + break; } - }; -}; -class npc_azure_binder : public CreatureScript -{ -public: - npc_azure_binder() : CreatureScript("npc_azure_binder") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetVioletHoldAI(creature); + DoMeleeAttackIfReady(); } - struct npc_azure_binderAI : public violet_hold_trashAI +private: + EventMap _events; +}; + +/*********** +** TRASH — AZURE BINDER +***********/ + +enum BinderEvents +{ + EVENT_BINDER_ARCANE_EXPLOSION = 1, + EVENT_BINDER_ARCANE_BARRAGE, + EVENT_BINDER_FROST_NOVA, + EVENT_BINDER_FROSTBOLT, +}; + +struct npc_azure_binder : public violet_hold_trashAI +{ + npc_azure_binder(Creature* c) : violet_hold_trashAI(c) {} + + void Reset() override { - npc_azure_binderAI(Creature* c) : violet_hold_trashAI(c) {} + _events.Reset(); + _events.RescheduleEvent(EVENT_BINDER_ARCANE_EXPLOSION, 5s); + _events.RescheduleEvent(EVENT_BINDER_ARCANE_BARRAGE, 4s); + _events.RescheduleEvent(EVENT_BINDER_FROST_NOVA, 5s); + _events.RescheduleEvent(EVENT_BINDER_FROSTBOLT, 4s); + } - uint32 uiArcaneExplosionTimer; - uint32 uiArcainBarrageTimer; - uint32 uiFrostNovaTimer; - uint32 uiFrostboltTimer; + void UpdateAI(uint32 diff) override + { + violet_hold_trashAI::UpdateAI(diff); - void Reset() override + if (!UpdateVictim()) + return; + + _events.Update(diff); + + switch (_events.ExecuteEvent()) { - uiArcaneExplosionTimer = 5000; - uiArcainBarrageTimer = 4000; - uiFrostNovaTimer = 5000; - uiFrostboltTimer = 4000; - } - - void UpdateAI(uint32 diff) override - { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; - - if (me->GetEntry() == NPC_AZURE_BINDER_1) - { - if (uiArcaneExplosionTimer <= diff) - { + case EVENT_BINDER_ARCANE_EXPLOSION: + if (me->GetEntry() == NPC_AZURE_BINDER_1) DoCast(SPELL_ARCANE_EXPLOSION); - uiArcaneExplosionTimer = 5000; - } - else uiArcaneExplosionTimer -= diff; - - if (uiArcainBarrageTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true); - if (pTarget) - DoCast(pTarget, SPELL_ARCANE_BARRAGE); - uiArcainBarrageTimer = 6000; - } - else uiArcainBarrageTimer -= diff; - } - - if (me->GetEntry() == NPC_AZURE_BINDER_2) - { - if (uiFrostNovaTimer <= diff) - { + _events.Repeat(5s); + break; + case EVENT_BINDER_ARCANE_BARRAGE: + if (me->GetEntry() == NPC_AZURE_BINDER_1) + DoCastRandomTarget(SPELL_ARCANE_BARRAGE, 0, 30.0f); + _events.Repeat(6s); + break; + case EVENT_BINDER_FROST_NOVA: + if (me->GetEntry() == NPC_AZURE_BINDER_2) DoCast(SPELL_FROST_NOVA); - uiFrostNovaTimer = 5000; - } - else uiFrostNovaTimer -= diff; - - if (uiFrostboltTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true); - if (pTarget) - DoCast(pTarget, SPELL_FROSTBOLT); - uiFrostboltTimer = 6000; - } - else uiFrostboltTimer -= diff; - } - - DoMeleeAttackIfReady(); + _events.Repeat(5s); + break; + case EVENT_BINDER_FROSTBOLT: + if (me->GetEntry() == NPC_AZURE_BINDER_2) + DoCastRandomTarget(SPELL_FROSTBOLT, 0, 40.0f); + _events.Repeat(6s); + break; } - }; -}; -class npc_azure_mage_slayer : public CreatureScript -{ -public: - npc_azure_mage_slayer() : CreatureScript("npc_azure_mage_slayer") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetVioletHoldAI(creature); + DoMeleeAttackIfReady(); } - struct npc_azure_mage_slayerAI : public violet_hold_trashAI - { - npc_azure_mage_slayerAI(Creature* c) : violet_hold_trashAI(c) {} - - uint32 uiArcaneEmpowermentTimer; - uint32 uiSpellLockTimer; - - void Reset() override - { - uiArcaneEmpowermentTimer = 5000; - uiSpellLockTimer = 5000; - } - - void UpdateAI(uint32 diff) override - { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; - - if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_1) - { - if (uiArcaneEmpowermentTimer <= diff) - { - DoCast(me, SPELL_ARCANE_EMPOWERMENT); - uiArcaneEmpowermentTimer = 14000; - } - else uiArcaneEmpowermentTimer -= diff; - } - - if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_2) - { - if (uiSpellLockTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true); - if (pTarget) - DoCast(pTarget, SPELL_SPELL_LOCK); - uiSpellLockTimer = 9000; - } - else uiSpellLockTimer -= diff; - } - - DoMeleeAttackIfReady(); - } - }; +private: + EventMap _events; }; -class npc_azure_raider : public CreatureScript -{ -public: - npc_azure_raider() : CreatureScript("npc_azure_raider") { } +/*********** +** TRASH — AZURE MAGE SLAYER +***********/ - CreatureAI* GetAI(Creature* creature) const override +enum MageSlayerEvents +{ + EVENT_MAGE_SLAYER_ARCANE_EMPOWERMENT = 1, + EVENT_MAGE_SLAYER_SPELL_LOCK, +}; + +struct npc_azure_mage_slayer : public violet_hold_trashAI +{ + npc_azure_mage_slayer(Creature* c) : violet_hold_trashAI(c) {} + + void Reset() override { - return GetVioletHoldAI (creature); + _events.Reset(); + _events.RescheduleEvent(EVENT_MAGE_SLAYER_ARCANE_EMPOWERMENT, 5s); + _events.RescheduleEvent(EVENT_MAGE_SLAYER_SPELL_LOCK, 5s); } - struct npc_azure_raiderAI : public violet_hold_trashAI + void UpdateAI(uint32 diff) override { - npc_azure_raiderAI(Creature* c) : violet_hold_trashAI(c) {} + violet_hold_trashAI::UpdateAI(diff); - uint32 uiConcussionBlowTimer; - uint32 uiMagicReflectionTimer; + if (!UpdateVictim()) + return; - void Reset() override + _events.Update(diff); + + switch (_events.ExecuteEvent()) { - uiConcussionBlowTimer = 5000; - uiMagicReflectionTimer = 8000; + case EVENT_MAGE_SLAYER_ARCANE_EMPOWERMENT: + if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_1) + DoCastSelf(SPELL_ARCANE_EMPOWERMENT); + _events.Repeat(14s); + break; + case EVENT_MAGE_SLAYER_SPELL_LOCK: + if (me->GetEntry() == NPC_AZURE_MAGE_SLAYER_2) + DoCastRandomTarget(SPELL_SPELL_LOCK, 0, 30.0f); + _events.Repeat(9s); + break; } - void UpdateAI(uint32 diff) override + DoMeleeAttackIfReady(); + } + +private: + EventMap _events; +}; + +/*********** +** TRASH — AZURE RAIDER +***********/ + +enum RaiderEvents +{ + EVENT_RAIDER_CONCUSSION_BLOW = 1, + EVENT_RAIDER_MAGIC_REFLECTION, +}; + +struct npc_azure_raider : public violet_hold_trashAI +{ + npc_azure_raider(Creature* c) : violet_hold_trashAI(c) {} + + void Reset() override + { + _events.Reset(); + _events.RescheduleEvent(EVENT_RAIDER_CONCUSSION_BLOW, 5s); + _events.RescheduleEvent(EVENT_RAIDER_MAGIC_REFLECTION, 8s); + } + + void UpdateAI(uint32 diff) override + { + violet_hold_trashAI::UpdateAI(diff); + + if (!UpdateVictim()) + return; + + _events.Update(diff); + + switch (_events.ExecuteEvent()) { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; - - if (uiConcussionBlowTimer <= diff) - { + case EVENT_RAIDER_CONCUSSION_BLOW: DoCast(me->GetVictim(), SPELL_CONCUSSION_BLOW); - uiConcussionBlowTimer = 5000; - } - else uiConcussionBlowTimer -= diff; - - if (uiMagicReflectionTimer <= diff) - { + _events.Repeat(5s); + break; + case EVENT_RAIDER_MAGIC_REFLECTION: DoCast(SPELL_MAGIC_REFLECTION); - uiMagicReflectionTimer = urand(10000, 15000); - } - else uiMagicReflectionTimer -= diff; - - DoMeleeAttackIfReady(); + _events.Repeat(10s, 15s); + break; } - }; -}; -class npc_azure_stalker : public CreatureScript -{ -public: - npc_azure_stalker() : CreatureScript("npc_azure_stalker") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetVioletHoldAI(creature); + DoMeleeAttackIfReady(); } - struct npc_azure_stalkerAI : public violet_hold_trashAI - { - npc_azure_stalkerAI(Creature* c) : violet_hold_trashAI(c) {} - - uint32 uiBackstabTimer; - uint32 uiTacticalBlinkTimer; - bool TacticalBlinkCasted; - - void Reset() override - { - uiBackstabTimer = 1300; - uiTacticalBlinkTimer = 8000; - TacticalBlinkCasted = false; - } - - void UpdateAI(uint32 diff) override - { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; - - /*if (!TacticalBlinkCasted) - { - if (uiTacticalBlinkTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true); - if (pTarget) - DoCast(pTarget, SPELL_TACTICAL_BLINK); - uiTacticalBlinkTimer = 10000; - TacticalBlinkCasted = true; - } else uiTacticalBlinkTimer -= diff; - } - - else*/ - { - if (uiBackstabTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::MaxDistance, 0, 5.0f, true); - if (pTarget && !pTarget->HasInArc(M_PI, me)) - DoCast(pTarget, SPELL_BACKSTAB); - TacticalBlinkCasted = false; - uiBackstabTimer = 4000; - } - else uiBackstabTimer -= diff; - } - - DoMeleeAttackIfReady(); - } - }; +private: + EventMap _events; }; -class npc_azure_spellbreaker : public CreatureScript -{ -public: - npc_azure_spellbreaker() : CreatureScript("npc_azure_spellbreaker") { } +/*********** +** TRASH — AZURE STALKER +***********/ - CreatureAI* GetAI(Creature* creature) const override +enum StalkerEvents +{ + EVENT_STALKER_BACKSTAB = 1, +}; + +struct npc_azure_stalker : public violet_hold_trashAI +{ + npc_azure_stalker(Creature* c) : violet_hold_trashAI(c) {} + + void Reset() override { - return GetVioletHoldAI(creature); + _events.Reset(); + _events.RescheduleEvent(EVENT_STALKER_BACKSTAB, 1300ms); } - struct npc_azure_spellbreakerAI : public violet_hold_trashAI + void UpdateAI(uint32 diff) override { - npc_azure_spellbreakerAI(Creature* c) : violet_hold_trashAI(c) {} + violet_hold_trashAI::UpdateAI(diff); - uint32 uiArcaneBlastTimer; - uint32 uiSlowTimer; - uint32 uiChainsOfIceTimer; - uint32 uiConeOfColdTimer; + if (!UpdateVictim()) + return; - void Reset() override + _events.Update(diff); + + if (_events.ExecuteEvent() == EVENT_STALKER_BACKSTAB) { - uiArcaneBlastTimer = 5000; - uiSlowTimer = 4000; - uiChainsOfIceTimer = 5000; - uiConeOfColdTimer = 4000; + if (Unit* target = SelectTarget(SelectTargetMethod::MaxDistance, 0, 5.0f, true)) + if (!target->HasInArc(M_PI, me)) + DoCast(target, SPELL_BACKSTAB); + _events.Repeat(4s); } - void UpdateAI(uint32 diff) override + DoMeleeAttackIfReady(); + } + +private: + EventMap _events; +}; + +/*********** +** TRASH — AZURE SPELLBREAKER +***********/ + +enum SpellbreakerEvents +{ + EVENT_SPELLBREAKER_ARCANE_BLAST = 1, + EVENT_SPELLBREAKER_SLOW, + EVENT_SPELLBREAKER_CHAINS_OF_ICE, + EVENT_SPELLBREAKER_CONE_OF_COLD, +}; + +struct npc_azure_spellbreaker : public violet_hold_trashAI +{ + npc_azure_spellbreaker(Creature* c) : violet_hold_trashAI(c) {} + + void Reset() override + { + _events.Reset(); + _events.RescheduleEvent(EVENT_SPELLBREAKER_ARCANE_BLAST, 5s); + _events.RescheduleEvent(EVENT_SPELLBREAKER_SLOW, 4s); + _events.RescheduleEvent(EVENT_SPELLBREAKER_CHAINS_OF_ICE, 5s); + _events.RescheduleEvent(EVENT_SPELLBREAKER_CONE_OF_COLD, 4s); + } + + void UpdateAI(uint32 diff) override + { + violet_hold_trashAI::UpdateAI(diff); + + if (!UpdateVictim()) + return; + + _events.Update(diff); + + switch (_events.ExecuteEvent()) { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; - - if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1) - { - if (uiArcaneBlastTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true); - if (pTarget) - DoCast(pTarget, SPELL_ARCANE_BLAST); - uiArcaneBlastTimer = 6000; - } - else uiArcaneBlastTimer -= diff; - - if (uiSlowTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true); - if (pTarget) - DoCast(pTarget, SPELL_SLOW); - uiSlowTimer = 5000; - } - else uiSlowTimer -= diff; - } - - if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2) - { - if (uiChainsOfIceTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true); - if (pTarget) - DoCast(pTarget, SPELL_CHAINS_OF_ICE); - uiChainsOfIceTimer = 7000; - } - else uiChainsOfIceTimer -= diff; - - if (uiConeOfColdTimer <= diff) - { + case EVENT_SPELLBREAKER_ARCANE_BLAST: + if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1) + DoCastRandomTarget(SPELL_ARCANE_BLAST, 0, 30.0f); + _events.Repeat(6s); + break; + case EVENT_SPELLBREAKER_SLOW: + if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_1) + DoCastRandomTarget(SPELL_SLOW, 0, 30.0f); + _events.Repeat(5s); + break; + case EVENT_SPELLBREAKER_CHAINS_OF_ICE: + if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2) + DoCastRandomTarget(SPELL_CHAINS_OF_ICE, 0, 30.0f); + _events.Repeat(7s); + break; + case EVENT_SPELLBREAKER_CONE_OF_COLD: + if (me->GetEntry() == NPC_AZURE_SPELLBREAKER_2) DoCast(SPELL_CONE_OF_COLD); - uiConeOfColdTimer = 5000; - } - else uiConeOfColdTimer -= diff; - } - - DoMeleeAttackIfReady(); + _events.Repeat(5s); + break; } - }; -}; -class npc_azure_captain : public CreatureScript -{ -public: - npc_azure_captain() : CreatureScript("npc_azure_captain") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetVioletHoldAI(creature); + DoMeleeAttackIfReady(); } - struct npc_azure_captainAI : public violet_hold_trashAI +private: + EventMap _events; +}; + +/*********** +** TRASH — AZURE CAPTAIN +***********/ + +enum CaptainEvents +{ + EVENT_CAPTAIN_MORTAL_STRIKE = 1, + EVENT_CAPTAIN_WHIRLWIND, +}; + +struct npc_azure_captain : public violet_hold_trashAI +{ + npc_azure_captain(Creature* c) : violet_hold_trashAI(c) {} + + void Reset() override { - npc_azure_captainAI(Creature* c) : violet_hold_trashAI(c) {} + _events.Reset(); + _events.RescheduleEvent(EVENT_CAPTAIN_MORTAL_STRIKE, 5s); + _events.RescheduleEvent(EVENT_CAPTAIN_WHIRLWIND, 8s); + } - uint32 uiMortalStrikeTimer; - uint32 uiWhirlwindTimer; + void UpdateAI(uint32 diff) override + { + violet_hold_trashAI::UpdateAI(diff); - void Reset() override + if (!UpdateVictim()) + return; + + _events.Update(diff); + + switch (_events.ExecuteEvent()) { - uiMortalStrikeTimer = 5000; - uiWhirlwindTimer = 8000; - } - - void UpdateAI(uint32 diff) override - { - violet_hold_trashAI::UpdateAI(diff); - - if (!UpdateVictim()) - return; - - if (uiMortalStrikeTimer <= diff) - { + case EVENT_CAPTAIN_MORTAL_STRIKE: DoCast(me->GetVictim(), SPELL_MORTAL_STRIKE); - uiMortalStrikeTimer = 5000; - } - else uiMortalStrikeTimer -= diff; - - if (uiWhirlwindTimer <= diff) - { + _events.Repeat(5s); + break; + case EVENT_CAPTAIN_WHIRLWIND: DoCastAOE(SPELL_WHIRLWIND_OF_STEEL); - uiWhirlwindTimer = 8000; - } - else uiWhirlwindTimer -= diff; - - DoMeleeAttackIfReady(); + _events.Repeat(8s); + break; } - }; -}; -class npc_azure_sorceror : public CreatureScript -{ -public: - npc_azure_sorceror() : CreatureScript("npc_azure_sorceror") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetVioletHoldAI(creature); + DoMeleeAttackIfReady(); } - struct npc_azure_sorcerorAI : public violet_hold_trashAI +private: + EventMap _events; +}; + +/*********** +** TRASH — AZURE SORCEROR +***********/ + +enum SorcerorEvents +{ + EVENT_SORCEROR_ARCANE_STREAM = 1, + EVENT_SORCEROR_MANA_DETONATION, +}; + +struct npc_azure_sorceror : public violet_hold_trashAI +{ + npc_azure_sorceror(Creature* c) : violet_hold_trashAI(c) {} + + void Reset() override { - npc_azure_sorcerorAI(Creature* c) : violet_hold_trashAI(c) {} + _events.Reset(); + _events.RescheduleEvent(EVENT_SORCEROR_ARCANE_STREAM, 4s); + _events.RescheduleEvent(EVENT_SORCEROR_MANA_DETONATION, 5s); + _arcaneStreamOnCooldown = false; + } - uint32 uiArcaneStreamTimer; - uint32 uiArcaneStreamTimerStartingValueHolder; - uint32 uiManaDetonationTimer; + void UpdateAI(uint32 diff) override + { + violet_hold_trashAI::UpdateAI(diff); - void Reset() override + if (!UpdateVictim()) + return; + + _events.Update(diff); + + switch (_events.ExecuteEvent()) { - uiArcaneStreamTimer = 4000; - uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer; - uiManaDetonationTimer = 5000; + case EVENT_SORCEROR_ARCANE_STREAM: + DoCastRandomTarget(SPELL_ARCANE_STREAM, 0, 35.0f); + _arcaneStreamOnCooldown = true; + _events.Repeat(5s, 10s); + break; + case EVENT_SORCEROR_MANA_DETONATION: + if (_arcaneStreamOnCooldown) + { + DoCastAOE(SPELL_MANA_DETONATION); + _arcaneStreamOnCooldown = false; + } + _events.Repeat(2s, 6s); + break; } - void UpdateAI(uint32 diff) override - { - violet_hold_trashAI::UpdateAI(diff); + DoMeleeAttackIfReady(); + } - if (!UpdateVictim()) - return; - - if (uiArcaneStreamTimer <= diff) - { - Unit* pTarget = SelectTarget(SelectTargetMethod::Random, 0, 35.0f, true); - if (pTarget) - DoCast(pTarget, SPELL_ARCANE_STREAM); - uiArcaneStreamTimer = urand(0, 5000) + 5000; - uiArcaneStreamTimerStartingValueHolder = uiArcaneStreamTimer; - } - else uiArcaneStreamTimer -= diff; - - if (uiManaDetonationTimer <= diff && uiArcaneStreamTimer >= 1500 && uiArcaneStreamTimer <= uiArcaneStreamTimerStartingValueHolder / 2) - { - DoCastAOE(SPELL_MANA_DETONATION); - uiManaDetonationTimer = urand(2000, 6000); - } - else uiManaDetonationTimer -= diff; - - DoMeleeAttackIfReady(); - } - }; +private: + EventMap _events; + bool _arcaneStreamOnCooldown; }; /*********** @@ -993,152 +907,143 @@ enum AzureSaboteurSpells SPELL_TELEPORT_VISUAL = 52096, }; -class npc_azure_saboteur : public CreatureScript +enum SaboteurEvents { -public: - npc_azure_saboteur() : CreatureScript("npc_azure_saboteur") { } + EVENT_SABOTEUR_SHIELD_DISRUPTION = 1, + EVENT_SABOTEUR_RELEASE_BOSS, + EVENT_SABOTEUR_DISAPPEAR, +}; - CreatureAI* GetAI(Creature* creature) const override +struct npc_azure_saboteur : public npc_escortAI +{ + npc_azure_saboteur(Creature* c) : npc_escortAI(c) { - return GetVioletHoldAI(creature); + _instance = c->GetInstanceScript(); + _boss = _instance->GetData(DATA_WAVE_COUNT) == 6 + ? _instance->GetPersistentData(PERSISTENT_DATA_FIRST_BOSS) + : _instance->GetPersistentData(PERSISTENT_DATA_SECOND_BOSS); + _addedWaypoints = false; + _isOpening = false; } - struct npc_azure_saboteurAI : public npc_escortAI + using CreatureAI::WaypointReached; + void WaypointReached(uint32 waypointId) override { - npc_azure_saboteurAI(Creature* c) : npc_escortAI(c) + switch (_boss) { - pInstance = c->GetInstanceScript(); - uiBoss = 0; - if (pInstance) - uiBoss = pInstance->GetData(DATA_WAVE_COUNT) == 6 ? pInstance->GetData(DATA_FIRST_BOSS_NUMBER) : pInstance->GetData(DATA_SECOND_BOSS_NUMBER); - bAddedWPs = false; - bOpening = false; + case BOSS_MORAGG: + if (waypointId == 2) + FinishPointReached(); + break; + case BOSS_EREKEM: + if (waypointId == 2) + FinishPointReached(); + break; + case BOSS_ICHORON: + if (waypointId == 1) + FinishPointReached(); + break; + case BOSS_LAVANTHOR: + if (waypointId == 0) + FinishPointReached(); + break; + case BOSS_XEVOZZ: + if (waypointId == 0) + FinishPointReached(); + break; + case BOSS_ZURAMAT: + if (waypointId == 4) + FinishPointReached(); + break; } + } - InstanceScript* pInstance; - bool bAddedWPs; - uint8 uiBoss; - bool bOpening; - uint32 timer; - uint8 count; + void UpdateAI(uint32 diff) override + { + npc_escortAI::UpdateAI(diff); - using CreatureAI::WaypointReached; - void WaypointReached(uint32 uiWPointId) override + if (!_addedWaypoints) { - if (!pInstance) - return; - - switch (uiBoss) + _addedWaypoints = true; + switch (_boss) { - case 1: - if (uiWPointId == 2) - FinishPointReached(); + case BOSS_MORAGG: + for (int i = 0; i < 3; i++) + AddWaypoint(i, SaboteurFinalPos1[i][0], SaboteurFinalPos1[i][1], SaboteurFinalPos1[i][2], 0); + me->SetHomePosition(SaboteurFinalPos1[2][0], SaboteurFinalPos1[2][1], SaboteurFinalPos1[2][2], 4.762346f); break; - case 2: - if (uiWPointId == 2) - FinishPointReached(); + case BOSS_EREKEM: + for (int i = 0; i < 3; i++) + AddWaypoint(i, SaboteurFinalPos2[i][0], SaboteurFinalPos2[i][1], SaboteurFinalPos2[i][2], 0); + me->SetHomePosition(SaboteurFinalPos2[2][0], SaboteurFinalPos2[2][1], SaboteurFinalPos2[2][2], 1.862674f); break; - case 3: - if (uiWPointId == 1) - FinishPointReached(); + case BOSS_ICHORON: + for (int i = 0; i < 2; i++) + AddWaypoint(i, SaboteurFinalPos3[i][0], SaboteurFinalPos3[i][1], SaboteurFinalPos3[i][2], 0); + me->SetHomePosition(SaboteurFinalPos3[1][0], SaboteurFinalPos3[1][1], SaboteurFinalPos3[1][2], 5.500638f); break; - case 4: - if (uiWPointId == 0) - FinishPointReached(); + case BOSS_LAVANTHOR: + AddWaypoint(0, SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 0); + me->SetHomePosition(SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 3.991108f); break; - case 5: - if (uiWPointId == 0) - FinishPointReached(); + case BOSS_XEVOZZ: + AddWaypoint(0, SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 0); + me->SetHomePosition(SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 1.100841f); break; - case 6: - if (uiWPointId == 4) - FinishPointReached(); + case BOSS_ZURAMAT: + for (int i = 0; i < 5; i++) + AddWaypoint(i, SaboteurFinalPos6[i][0], SaboteurFinalPos6[i][1], SaboteurFinalPos6[i][2], 0); + me->SetHomePosition(SaboteurFinalPos6[4][0], SaboteurFinalPos6[4][1], SaboteurFinalPos6[4][2], 0.983031f); break; } + SetDespawnAtEnd(false); + Start(true); } - void UpdateAI(uint32 diff) override + if (_isOpening) { - npc_escortAI::UpdateAI(diff); - - if (!bAddedWPs) + _events.Update(diff); + switch (_events.ExecuteEvent()) { - bAddedWPs = true; - switch (uiBoss) - { - case 1: - for(int i = 0; i < 3; i++) - AddWaypoint(i, SaboteurFinalPos1[i][0], SaboteurFinalPos1[i][1], SaboteurFinalPos1[i][2], 0); - me->SetHomePosition(SaboteurFinalPos1[2][0], SaboteurFinalPos1[2][1], SaboteurFinalPos1[2][2], 4.762346f); - break; - case 2: - for(int i = 0; i < 3; i++) - AddWaypoint(i, SaboteurFinalPos2[i][0], SaboteurFinalPos2[i][1], SaboteurFinalPos2[i][2], 0); - me->SetHomePosition(SaboteurFinalPos2[2][0], SaboteurFinalPos2[2][1], SaboteurFinalPos2[2][2], 1.862674f); - break; - case 3: - for(int i = 0; i < 2; i++) - AddWaypoint(i, SaboteurFinalPos3[i][0], SaboteurFinalPos3[i][1], SaboteurFinalPos3[i][2], 0); - me->SetHomePosition(SaboteurFinalPos3[1][0], SaboteurFinalPos3[1][1], SaboteurFinalPos3[1][2], 5.500638f); - break; - case 4: - AddWaypoint(0, SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 0); - me->SetHomePosition(SaboteurFinalPos4[0], SaboteurFinalPos4[1], SaboteurFinalPos4[2], 3.991108f); - break; - case 5: - AddWaypoint(0, SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 0); - me->SetHomePosition(SaboteurFinalPos5[0], SaboteurFinalPos5[1], SaboteurFinalPos5[2], 1.100841f); - break; - case 6: - for(int i = 0; i < 5; i++) - AddWaypoint(i, SaboteurFinalPos6[i][0], SaboteurFinalPos6[i][1], SaboteurFinalPos6[i][2], 0); - me->SetHomePosition(SaboteurFinalPos6[4][0], SaboteurFinalPos6[4][1], SaboteurFinalPos6[4][2], 0.983031f); - break; - } - SetDespawnAtEnd(false); - Start(true); - } - - if (bOpening) - { - if (timer <= diff) - { - if (count < 2) - { - me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false); - timer = 1000; - } - else if (count == 2) - { - me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false); - if (pInstance) - pInstance->SetData(DATA_RELEASE_BOSS, 0); - timer = 500; - } + case EVENT_SABOTEUR_SHIELD_DISRUPTION: + DoCastSelf(SABOTEUR_SHIELD_DISRUPTION); + ++_count; + if (_count < 3) + _events.RescheduleEvent(EVENT_SABOTEUR_SHIELD_DISRUPTION, 1s); else { - bOpening = false; - me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetDisplayId(11686); - me->CastSpell(me, SPELL_TELEPORT_VISUAL, true); - me->DespawnOrUnsummon(1s); + _instance->SetData(DATA_RELEASE_BOSS, 0); + _events.RescheduleEvent(EVENT_SABOTEUR_DISAPPEAR, 500ms); } - ++count; - } - else timer -= diff; + break; + case EVENT_SABOTEUR_DISAPPEAR: + _isOpening = false; + me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetDisplayId(11686); + DoCastSelf(SPELL_TELEPORT_VISUAL, true); + me->DespawnOrUnsummon(1s); + break; } } + } - void FinishPointReached() - { - bOpening = true; - timer = 1000; - count = 0; - me->CastSpell(me, SABOTEUR_SHIELD_DISRUPTION, false); - } + void FinishPointReached() + { + _isOpening = true; + _count = 1; + DoCastSelf(SABOTEUR_SHIELD_DISRUPTION); + _events.RescheduleEvent(EVENT_SABOTEUR_SHIELD_DISRUPTION, 1s); + } - void MoveInLineOfSight(Unit* /*who*/) override {} - }; + void MoveInLineOfSight(Unit* /*who*/) override {} + +private: + InstanceScript* _instance; + bool _addedWaypoints; + uint8 _boss; + bool _isOpening; + EventMap _events; + uint8 _count; }; /*********** @@ -1153,8 +1058,8 @@ class spell_destroy_door_seal_aura : public AuraScript { PreventDefaultAction(); if (Unit* target = GetTarget()) - if (InstanceScript* pInstance = target->GetInstanceScript()) - pInstance->SetData(DATA_DECRASE_DOOR_HEALTH, 0); + if (InstanceScript* Instance = target->GetInstanceScript()) + Instance->SetData(DATA_DECREASE_DOOR_HEALTH, 0); } void Register() override @@ -1197,17 +1102,18 @@ void AddSC_violet_hold() { new go_vh_activation_crystal(); new npc_vh_sinclari(); - new npc_vh_teleportation_portal(); - new npc_azure_saboteur(); - new npc_azure_invader(); - new npc_azure_spellbreaker(); - new npc_azure_binder(); - new npc_azure_mage_slayer(); - new npc_azure_captain(); - new npc_azure_sorceror(); - new npc_azure_raider(); - new npc_azure_stalker(); + RegisterVioletHoldCreatureAI(npc_vh_teleportation_portal); + RegisterVioletHoldCreatureAI(npc_azure_saboteur); + + RegisterVioletHoldCreatureAI(npc_azure_invader); + RegisterVioletHoldCreatureAI(npc_azure_spellbreaker); + RegisterVioletHoldCreatureAI(npc_azure_binder); + RegisterVioletHoldCreatureAI(npc_azure_mage_slayer); + RegisterVioletHoldCreatureAI(npc_azure_captain); + RegisterVioletHoldCreatureAI(npc_azure_sorceror); + RegisterVioletHoldCreatureAI(npc_azure_raider); + RegisterVioletHoldCreatureAI(npc_azure_stalker); RegisterSpellScript(spell_destroy_door_seal_aura); RegisterCreatureAI(npc_violet_hold_defense_system); diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.h b/src/server/scripts/Northrend/VioletHold/violet_hold.h index a21142afd..354785faf 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.h +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.h @@ -18,15 +18,57 @@ #ifndef DEF_VIOLET_HOLD_H #define DEF_VIOLET_HOLD_H -#define MAX_ENCOUNTER 3 - #include "CreatureAIImpl.h" #define DataHeader "VIO" #define VioletHoldScriptName "instance_violet_hold" -enum Creatures +enum VHData +{ + // Creature ObjectData IDs + // Boss creatures use VHBosses values directly (BOSS_MORAGG .. BOSS_ZURAMAT, DATA_CYANIGOSA) + DATA_SINCLARI = 10, + DATA_DOOR_SEAL, + DATA_TELEPORTATION_PORTAL, + + // GO ObjectData IDs + DATA_MAIN_DOOR = 20, + DATA_MORAGG_CELL, + DATA_EREKEM_CELL, + DATA_EREKEM_GUARD_1_CELL, + DATA_EREKEM_GUARD_2_CELL, + DATA_ICHORON_CELL, + DATA_LAVANTHOR_CELL, + DATA_XEVOZZ_CELL, + DATA_ZURAMAT_CELL, + + // Instance action/state IDs (used by SetData/GetData) + DATA_ENCOUNTER_STATUS = 30, + DATA_ACTIVATE_DEFENSE_SYSTEM, + DATA_START_INSTANCE, + DATA_ADD_TRASH_MOB, + DATA_DELETE_TRASH_MOB, + DATA_PORTAL_DEFEATED, + DATA_WAVE_COUNT, + DATA_PORTAL_LOCATION, + DATA_RELEASE_BOSS, + DATA_DECREASE_DOOR_HEALTH, + DATA_ACHIEV, + + // Manual GUID tracking (multi-instance entries) + DATA_EREKEM_GUARD_1_GUID, + DATA_EREKEM_GUARD_2_GUID, +}; + +enum VHPersistentData +{ + PERSISTENT_DATA_FIRST_BOSS, + PERSISTENT_DATA_SECOND_BOSS, + PERSISTENT_DATA_COUNT +}; + +enum VHCreatures { NPC_TELEPORTATION_PORTAL = 31011, NPC_DEFENSE_SYSTEM = 30837, @@ -63,7 +105,7 @@ enum Creatures NPC_AZURE_STALKER = 32191, }; -enum GameObjects +enum VHGameObjects { GO_MAIN_DOOR = 191723, GO_XEVOZZ_DOOR = 191556, @@ -78,19 +120,25 @@ enum GameObjects GO_ACTIVATION_CRYSTAL = 193611, }; -enum Bosses +enum VHBosses { - BOSS_NONE, - BOSS_MORAGG, + // Encounter slot IDs (used by SetBossState/GetBossState) + DATA_1ST_BOSS, + DATA_2ND_BOSS, + DATA_CYANIGOSA, + MAX_ENCOUNTER, + + // Individual boss IDs (used by ObjectData and BossAI) + BOSS_MORAGG = MAX_ENCOUNTER, BOSS_EREKEM, BOSS_ICHORON, BOSS_LAVANTHOR, BOSS_XEVOZZ, BOSS_ZURAMAT, - BOSS_CYANIGOSA + MAX_BOSS }; -enum Spells +enum VHSpells { SPELL_CONTROL_CRYSTAL_ACTIVATION = 57804, SPELL_DEFENSE_SYSTEM_SPAWN_EFFECT = 57886, @@ -104,7 +152,7 @@ enum Spells SPELL_CYANIGOSA_BLUE_AURA = 45870 }; -enum Events +enum VHInstanceEvents { EVENT_CHECK_PLAYERS = 1, EVENT_GUARDS_FALL_BACK, @@ -112,7 +160,7 @@ enum Events EVENT_SINCLARI_FALL_BACK, EVENT_START_ENCOUNTER, EVENT_SUMMON_PORTAL, - EVENT_CYANIGOSSA_TRANSFORM, + EVENT_CYANIGOSA_TRANSFORM, EVENT_CYANIGOSA_ATTACK, // Event defense system @@ -120,32 +168,7 @@ enum Events EVENT_ARCANE_LIGHTNING_INSTAKILL }; -enum Data -{ - DATA_ACTIVATE_DEFENSE_SYSTEM = 1, - DATA_ENCOUNTER_STATUS, - DATA_START_INSTANCE, - DATA_ADD_TRASH_MOB, - DATA_DELETE_TRASH_MOB, - DATA_PORTAL_DEFEATED, - DATA_WAVE_COUNT, - DATA_PORTAL_LOCATION, - DATA_TELEPORTATION_PORTAL_GUID, - DATA_DOOR_SEAL_GUID, - DATA_FIRST_BOSS_NUMBER, - DATA_SECOND_BOSS_NUMBER, - DATA_RELEASE_BOSS, - DATA_DECRASE_DOOR_HEALTH, - DATA_BOSS_DIED, - DATA_FAILED, - DATA_EREKEM_GUID, - DATA_EREKEM_GUARD_1_GUID, - DATA_EREKEM_GUARD_2_GUID, - DATA_ICHORON_GUID, - DATA_ACHIEV, -}; - -enum AchievCriteria +enum VHAchievCriteria { CRITERIA_DEFENSELESS = 6803, CRITERIA_A_VOID_DANCE = 7587, @@ -308,4 +331,6 @@ inline AI* GetVioletHoldAI(T* obj) return GetInstanceAI(obj, VioletHoldScriptName); } +#define RegisterVioletHoldCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetVioletHoldAI) + #endif