diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp | 1211 |
1 files changed, 651 insertions, 560 deletions
diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp index 1a9074c2464..c9e08176ad6 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp @@ -15,45 +15,45 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* -Name: Boss_Shade_of_Akama -%Complete: 80 -Comment: WIP A few more adds to script, ending script, and bugs. -Category: Black Temple -*/ - #include "ObjectMgr.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "PassiveAI.h" #include "ScriptedGossip.h" #include "GridNotifiers.h" #include "black_temple.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" enum Says { - // Akama Ending cinematic text + // Akama SAY_BROKEN_FREE_0 = 0, SAY_BROKEN_FREE_1 = 1, - SAY_BROKEN_FREE_2 = 2 + SAY_BROKEN_FREE_2 = 2, + SAY_LOW_HEALTH = 3, + SAY_DEAD = 4, + // Ashtongue Broken + SAY_BROKEN_SPECIAL = 0, + SAY_BROKEN_HAIL = 1 }; enum Spells { // Akama - SPELL_STEALTH = 34189, // On Spawn - SPELL_AKAMA_SOUL_CHANNEL = 40447, // Cast on self hits Shade - SPELL_FIXATE = 40607, // Cast on self hits Shade - SPELL_CHAIN_LIGHTNING = 39945, // Combat - SPELL_DESTRUCTIVE_POISON = 40874, // Combat + SPELL_STEALTH = 34189, + SPELL_AKAMA_SOUL_CHANNEL = 40447, + SPELL_FIXATE = 40607, + SPELL_CHAIN_LIGHTNING = 39945, + SPELL_DESTRUCTIVE_POISON = 40874, + SPELL_AKAMA_SOUL_EXPEL = 40902, // Shade - SPELL_THREAT = 41602, // self cast hits Akama - SPELL_SHADE_OF_AKAMA_TRIGGER = 40955, // Cast on death - SPELL_AKAMA_SOUL_EXPEL_CHANNEL = 40927, // must hit shade - SPELL_AKAMA_SOUL_EXPEL = 40902, // the one he cast + SPELL_THREAT = 41602, + SPELL_SHADE_OF_AKAMA_TRIGGER = 40955, + SPELL_AKAMA_SOUL_EXPEL_CHANNEL = 40927, // Ashtongue Channeler SPELL_SHADE_SOUL_CHANNEL = 40401, SPELL_SHADE_SOUL_CHANNEL_2 = 40520, - SPELL_SHADOWFORM = 40973, // Cast on Shade // Creature Spawner SPELL_ASHTONGUE_WAVE_B = 42035, SPELL_SUMMON_ASHTONGUE_SORCERER = 40476, @@ -78,11 +78,6 @@ enum Spells enum Creatures { NPC_ASHTONGUE_CHANNELER = 23421, - NPC_ASHTONGUE_SORCERER = 23215, - NPC_ASHTONGUE_DEFENDER = 23216, - NPC_ASHTONGUE_ELEMENTALIST = 23523, - NPC_ASHTONGUE_ROGUE = 23318, - NPC_ASHTONGUE_SPIRITBINDER = 23524, NPC_ASHTONGUE_BROKEN = 23319, NPC_CREATURE_SPAWNER_AKAMA = 23210 }; @@ -95,60 +90,114 @@ enum Factions enum Actions { - ACTION_CHANNELER_DIED = 1, - ACTION_START_SPAWNING = 2, - ACTION_STOP_SPAWNING = 3, - ACTION_DESPAWN_ALL_SPAWNS = 4, + ACTION_START_SPAWNING = 0, + ACTION_STOP_SPAWNING = 1, + ACTION_DESPAWN_ALL_SPAWNS = 2, + ACTION_SHADE_OF_AKAMA_DEAD = 3, + ACTION_BROKEN_SPECIAL = 4, + ACTION_BROKEN_EMOTE = 5, + ACTION_BROKEN_HAIL = 6 }; enum Events { // Akama - EVENT_SHADE_START = 1, - EVENT_SHADE_CHANNEL = 2, - EVENT_FIXATE = 3, - EVENT_CHAIN_LIGHTNING = 4, - EVENT_DESTRUCTIVE_POISON = 5, - // Shade - EVENT_RESET_ENCOUNTER = 6, - EVENT_FIND_CHANNELERS_SPAWNERS = 7, - EVENT_SET_CHANNELERS_SPAWNERS = 8, - EVENT_START_ATTACK_AKAMA = 9, - EVENT_ADD_THREAT = 10, + EVENT_SHADE_START = 1, + EVENT_SHADE_CHANNEL = 2, + EVENT_FIXATE = 3, + EVENT_CHAIN_LIGHTNING = 4, + EVENT_DESTRUCTIVE_POISON = 5, + EVENT_START_BROKEN_FREE = 6, + EVENT_START_SOUL_EXPEL = 7, + EVENT_EVADE_CHECK = 8, + EVENT_BROKEN_FREE_1 = 9, + EVENT_BROKEN_FREE_2 = 10, + EVENT_BROKEN_FREE_3 = 11, + EVENT_BROKEN_FREE_4 = 12, + // Shade of Akama + EVENT_INITIALIZE_SPAWNERS = 13, + EVENT_START_CHANNELERS_AND_SPAWNERS = 14, + EVENT_ADD_THREAT = 15, // Creature spawner - EVENT_SPAWN_WAVE_B = 11, - EVENT_SUMMON_ASHTONGUE_SORCERER = 12, - EVENT_SUMMON_ASHTONGUE_DEFENDER = 13, - // Channeler - EVENT_CHANNEL = 14, - // Ashtongue Sorcerer - EVENT_SORCERER_CHANNEL = 15, + EVENT_SPAWN_WAVE_B = 16, + EVENT_SUMMON_ASHTONGUE_SORCERER = 17, + EVENT_SUMMON_ASHTONGUE_DEFENDER = 18, // Ashtongue Defender - EVENT_DEBILITATING_STRIKE = 16, - EVENT_HEROIC_STRIKE = 17, - EVENT_SHIELD_BASH = 18, - EVENT_WINDFURY = 29, + EVENT_DEBILITATING_STRIKE = 19, + EVENT_HEROIC_STRIKE = 20, + EVENT_SHIELD_BASH = 21, + EVENT_WINDFURY = 22, // Ashtongue Rogue - EVENT_DEBILITATING_POISON = 20, - EVENT_EVISCERATE = 21, + EVENT_DEBILITATING_POISON = 23, + EVENT_EVISCERATE = 24, // Ashtongue Elementalist - EVENT_RAIN_OF_FIRE = 22, - EVENT_LIGHTNING_BOLT = 23, + EVENT_RAIN_OF_FIRE = 25, + EVENT_LIGHTNING_BOLT = 26, // Ashtongue Spiritbinder - EVENT_SPIRIT_HEAL = 24 + EVENT_SPIRIT_HEAL = 27, + EVENT_SPIRIT_MEND_RESET = 28, + EVENT_CHAIN_HEAL_RESET = 29 }; -G3D::Vector3 const ShadeWP = { 512.4877f, 400.7993f, 112.7837f }; +enum Misc +{ + AKAMA_CHANNEL_WAYPOINT = 0, + AKAMA_INTRO_WAYPOINT = 1, + + SUMMON_GROUP_RESET = 1 +}; -G3D::Vector3 const AkamaWP[] = +Position const AkamaWP[2] = { { 517.4877f, 400.7993f, 112.7837f }, { 468.4435f, 401.1062f, 118.5379f } }; -// ######################################################## -// Shade of Akama -// ######################################################## +Position const BrokenPos[18] = +{ + { 495.5628f, 462.7089f, 112.8169f, 4.1808090f }, + { 498.3421f, 463.8384f, 112.8673f, 4.5634810f }, + { 501.6708f, 463.8806f, 112.8673f, 3.7157850f }, + { 532.4264f, 448.4718f, 112.8563f, 3.9813020f }, + { 532.9113f, 451.6227f, 112.8671f, 4.6479530f }, + { 532.8243f, 453.9475f, 112.8671f, 4.7032810f }, + { 521.5317f, 402.3790f, 112.8671f, 3.1138120f }, + { 521.9184f, 404.6848f, 112.8671f, 4.0787760f }, + { 522.4290f, 406.5160f, 112.8671f, 3.3869470f }, + { 521.0833f, 393.1852f, 112.8611f, 3.0750830f }, + { 521.9014f, 395.6381f, 112.8671f, 4.0157140f }, + { 522.2610f, 397.7423f, 112.8671f, 3.4417790f }, + { 532.4565f, 345.3987f, 112.8585f, 1.7232640f }, + { 532.5565f, 346.8792f, 112.8671f, 1.8325960f }, + { 532.5491f, 348.6840f, 112.8671f, 0.2054047f }, + { 501.4669f, 338.5967f, 112.8504f, 1.7038430f }, + { 499.0937f, 337.9894f, 112.8673f, 1.8586250f }, + { 496.8722f, 338.0152f, 112.8673f, 0.5428222f } +}; + +Position const BrokenWP[18] = +{ + { 479.1884f, 434.8635f, 112.7838f }, + { 479.7349f, 435.9843f, 112.7838f }, + { 480.5328f, 436.8310f, 112.7838f }, + { 493.1714f, 420.1136f, 112.7838f }, + { 494.7830f, 417.4830f, 112.7838f }, + { 492.9280f, 423.1891f, 112.7838f }, + { 491.8618f, 403.2035f, 112.7838f }, + { 491.7784f, 400.2046f, 112.7838f }, + { 491.9451f, 406.2023f, 112.7838f }, + { 488.3535f, 395.3652f, 112.7838f }, + { 488.8324f, 392.3267f, 112.7838f }, + { 489.2300f, 398.3135f, 112.7838f }, + { 491.9286f, 383.0433f, 112.7838f }, + { 491.1526f, 380.0966f, 112.7839f }, + { 493.6747f, 385.5407f, 112.7838f }, + { 476.2499f, 369.0865f, 112.7839f }, + { 473.7637f, 367.8766f, 112.7839f }, + { 478.8986f, 370.1895f, 112.7839f } +}; + +static float const MIDDLE_OF_ROOM = 400.0f; class boss_shade_of_akama : public CreatureScript { @@ -160,233 +209,144 @@ public: boss_shade_of_akamaAI(Creature* creature) : BossAI(creature, DATA_SHADE_OF_AKAMA) { Initialize(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } void Initialize() { - combatStarted = false; - akamaReached = false; - HasKilledAkama = false; - HasKilledAkamaAndReseting = false; + _spawners.clear(); + _isInPhaseOne = true; } void Reset() override { - _Reset(); - if (!HasKilledAkamaAndReseting) - { - for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) - if (Creature* Channeler = ObjectAccessor::GetCreature(*me, *itr)) - Channeler->DespawnOrUnsummon(); - - for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) - if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); - - events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 3000); - events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 5000); - } - + Initialize(); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); me->SetWalk(true); - Initialize(); + events.ScheduleEvent(EVENT_INITIALIZE_SPAWNERS, Seconds(1)); + me->SummonCreatureGroup(SUMMON_GROUP_RESET); } - void EnterCombat(Unit* /*who*/) override { } - - void AttackStart(Unit* who) override + void EnterEvadeMode(EvadeReason /*why*/) override { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - { - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - if (Akama->IsAlive()) - ScriptedAI::AttackStart(Akama); - } - else - ScriptedAI::AttackStart(who); - } + _Reset(); - void DoAction(int32 actionId) override - { - if (actionId == ACTION_CHANNELER_DIED) - me->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); + for (ObjectGuid const& spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); - UpdateSpeed(); + _DespawnAtEvade(); } void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { if (spell->Id == SPELL_AKAMA_SOUL_CHANNEL) { - combatStarted = true; - events.ScheduleEvent(EVENT_START_ATTACK_AKAMA, 500); - events.ScheduleEvent(EVENT_SET_CHANNELERS_SPAWNERS, 1000); + events.ScheduleEvent(EVENT_START_CHANNELERS_AND_SPAWNERS, Seconds(1)); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - me->AddThreat(Akama, 10000000.0f); + events.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(10)); + if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } - else if (spell->Id == SPELL_SHADE_SOUL_CHANNEL_2) - UpdateSpeed(); + + if (spell->Id == SPELL_AKAMA_SOUL_EXPEL) + DoCastSelf(SPELL_AKAMA_SOUL_EXPEL_CHANNEL); } - void UpdateSpeed() + void MovementInform(uint32 motionType, uint32 /*pointId*/) override { - float moveSpeed = 0.2f; - - if (me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2) <= 3) + if (_isInPhaseOne && motionType == CHASE_MOTION_TYPE) { - moveSpeed = (2.0f - (0.6f * me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2))); - me->SetSpeedRate(MOVE_WALK, moveSpeed / 2.5f); - me->SetSpeedRate(MOVE_RUN, (moveSpeed * 2) / 7); - me->ClearUnitState(UNIT_STATE_ROOT); + _isInPhaseOne = false; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetWalk(false); + events.ScheduleEvent(EVENT_ADD_THREAT, Milliseconds(100)); + + for (ObjectGuid const& spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_STOP_SPAWNING); } - else - me->AddUnitState(UNIT_STATE_ROOT); } - void UpdateAI(uint32 diff) override + void JustDied(Unit* /*killer*/) override { - if (HasKilledAkamaAndReseting) - return; + DoCastSelf(SPELL_SHADE_OF_AKAMA_TRIGGER); + + if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) + akama->AI()->DoAction(ACTION_SHADE_OF_AKAMA_DEAD); + for (ObjectGuid const& spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); + + events.Reset(); + summons.DespawnEntry(NPC_ASHTONGUE_CHANNELER); + instance->SetBossState(DATA_SHADE_OF_AKAMA, DONE); + } + + void EnterEvadeModeIfNeeded() + { + Map::PlayerList const &players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + if (Player* player = i->GetSource()) + if (player->IsAlive() && !player->IsGameMaster() && CheckBoundary(player)) + return; + + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } + + void UpdateAI(uint32 diff) override + { events.Update(diff); - if (!combatStarted) - { - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_RESET_ENCOUNTER: - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - if (!Akama->IsAlive()) - Akama->Respawn(); - break; - case EVENT_FIND_CHANNELERS_SPAWNERS: - { - std::list<Creature*> ChannelerList; - me->GetCreatureListWithEntryInGrid(ChannelerList, NPC_ASHTONGUE_CHANNELER, 15.0f); - - if (!ChannelerList.empty()) - for (std::list<Creature*>::const_iterator itr = ChannelerList.begin(); itr != ChannelerList.end(); ++itr) - { - Channelers.push_back((*itr)->GetGUID()); - if ((*itr)->isDead()) - (*itr)->Respawn(); - } - - std::list<Creature*> SpawnerList; - me->GetCreatureListWithEntryInGrid(SpawnerList, NPC_CREATURE_SPAWNER_AKAMA, 90.0f); - - if (!SpawnerList.empty()) - for (std::list<Creature*>::const_iterator itr = SpawnerList.begin(); itr != SpawnerList.end(); ++itr) - Spawners.push_back((*itr)->GetGUID()); - - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); - break; - } - default: - break; - } - } - } - else + if (!UpdateVictim()) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - while (uint32 eventId = events.ExecuteEvent()) + switch (eventId) { - switch (eventId) + case EVENT_INITIALIZE_SPAWNERS: { - case EVENT_SET_CHANNELERS_SPAWNERS: - { - for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) - if (Creature* Channeler = ObjectAccessor::GetCreature(*me, *itr)) - Channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - - for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) - if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->DoAction(ACTION_START_SPAWNING); - break; - } - case EVENT_START_ATTACK_AKAMA: - me->GetMotionMaster()->MovePoint(0, ShadeWP.x, ShadeWP.y, ShadeWP.z, false); - events.ScheduleEvent(EVENT_START_ATTACK_AKAMA, 1000); - break; - case EVENT_ADD_THREAT: - DoCast(SPELL_THREAT); - events.ScheduleEvent(EVENT_ADD_THREAT, 3500); - break; - default: - break; - } - } + std::list<Creature*> SpawnerList; + me->GetCreatureListWithEntryInGrid(SpawnerList, NPC_CREATURE_SPAWNER_AKAMA); + for (Creature* spawner : SpawnerList) + _spawners.push_back(spawner->GetGUID()); - if (HasKilledAkama) - { - if (!HasKilledAkamaAndReseting) - { - HasKilledAkamaAndReseting = true; - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - instance->SetBossState(DATA_SHADE_OF_AKAMA, NOT_STARTED); - me->RemoveAllAurasExceptType(SPELL_AURA_DUMMY); - me->DeleteThreatList(); - me->CombatStop(); - me->GetMotionMaster()->MoveTargetedHome(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - combatStarted = false; - - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - Akama->DespawnOrUnsummon(); - - for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) - if (Creature* Channeler = ObjectAccessor::GetCreature(*me, *itr)) - Channeler->DespawnOrUnsummon(); - - for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) - if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); - - events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 10000); - events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 20000); + break; } - } - - if (!akamaReached) - { - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) + case EVENT_START_CHANNELERS_AND_SPAWNERS: { - if (me->IsWithinDist(Akama, 2.0f, false)) - { - akamaReached = true; - me->GetMotionMaster()->Clear(true); - me->GetMotionMaster()->MoveIdle(); - me->SetWalk(false); + for (ObjectGuid const& summonGuid : summons) + if (Creature* channeler = ObjectAccessor::GetCreature(*me, summonGuid)) + channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + for (ObjectGuid const& spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_START_SPAWNING); - events.CancelEvent(EVENT_START_ATTACK_AKAMA); - events.ScheduleEvent(EVENT_ADD_THREAT, 100); - - for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) - if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->DoAction(ACTION_STOP_SPAWNING); - } + break; } + case EVENT_ADD_THREAT: + DoCast(SPELL_THREAT); + events.Repeat(Seconds(3) + Milliseconds(500)); + break; + case EVENT_EVADE_CHECK: + EnterEvadeModeIfNeeded(); + events.Repeat(Seconds(10)); + break; + default: + break; } - else - DoMeleeAttackIfReady(); } + + DoMeleeAttackIfReady(); } - public: - bool HasKilledAkama; private: - GuidList Channelers; - GuidList Spawners; - bool akamaReached; - bool combatStarted; - bool HasKilledAkamaAndReseting; + GuidVector _spawners; + bool _isInPhaseOne; }; CreatureAI* GetAI(Creature* creature) const override @@ -395,10 +355,6 @@ public: } }; -// ######################################################## -// Akama -// ######################################################## - class npc_akama_shade : public CreatureScript { public: @@ -406,117 +362,188 @@ public: struct npc_akamaAI : public ScriptedAI { - npc_akamaAI(Creature* creature) : ScriptedAI(creature) + npc_akamaAI(Creature* creature) : ScriptedAI(creature), _summons(me) { Initialize(); - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Initialize() { - StartChannel = false; - StartCombat = false; - HasYelledOnce = false; - ShadeHasDied = false; + _isInCombat = false; + _hasYelledOnce = false; + _chosen.Clear(); + _summons.DespawnAll(); + _events.Reset(); } void Reset() override { - me->setFaction(FACTION_FRIENDLY); - DoCast(me, SPELL_STEALTH); Initialize(); + me->setFaction(FACTION_FRIENDLY); + DoCastSelf(SPELL_STEALTH); - if (instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE) + if (_instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE) me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } - void JustDied(Unit* /*killer*/) override + void JustSummoned(Creature* summon) override { - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - if (Shade->IsAlive()) - ENSURE_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->HasKilledAkama = true; - me->GetMotionMaster()->Clear(true); - me->GetMotionMaster()->MoveIdle(); + _summons.Summon(summon); } + void EnterEvadeMode(EvadeReason /*why*/) override { } + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { - if (spell->Id == SPELL_THREAT && !StartCombat) + if (spell->Id == SPELL_THREAT && !_isInCombat) { - me->ClearUnitState(UNIT_STATE_ROOT); - me->RemoveAura(SPELL_AKAMA_SOUL_CHANNEL); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->RemoveAura(SPELL_AKAMA_SOUL_CHANNEL); - StartCombat = true; + _isInCombat = true; + me->SetWalk(false); + me->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + { + shade->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); + AttackStart(shade); + _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, Seconds(2)); + _events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, Seconds(5)); + } } } - void EnterCombat(Unit* /*who*/) override + void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override { - events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 2000); - events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, 5000); + if (me->HealthBelowPct(20) && !_hasYelledOnce) + { + _hasYelledOnce = true; + Talk(SAY_LOW_HEALTH); + } } - void UpdateAI(uint32 diff) override + void DoAction(int32 actionId) override { - if (StartChannel) + if (actionId == ACTION_SHADE_OF_AKAMA_DEAD) { - events.Update(diff); + _isInCombat = false; + me->CombatStop(true); + me->setFaction(FACTION_FRIENDLY); + me->SetWalk(true); + _events.Reset(); + me->GetMotionMaster()->MovePoint(AKAMA_INTRO_WAYPOINT, AkamaWP[1]); + } + } - while (uint32 eventId = events.ExecuteEvent()) + void MovementInform(uint32 motionType, uint32 pointId) override + { + if (motionType != POINT_MOTION_TYPE) + return; + + if (pointId == AKAMA_CHANNEL_WAYPOINT) + _events.ScheduleEvent(EVENT_SHADE_CHANNEL, Seconds(1)); + + else if (pointId == AKAMA_INTRO_WAYPOINT) + { + me->SetWalk(false); + me->SetFacingTo(0.08726646f); + _events.ScheduleEvent(EVENT_START_SOUL_EXPEL, Seconds(1)); + } + } + + void SummonBrokens() + { + for (uint8 i = 0; i < 18; i++) + { + if (TempSummon* summoned = me->SummonCreature(NPC_ASHTONGUE_BROKEN, BrokenPos[i])) { - switch (eventId) - { - case EVENT_SHADE_START: - instance->SetBossState(DATA_SHADE_OF_AKAMA, IN_PROGRESS); - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - me->RemoveAura(SPELL_STEALTH); - me->SetWalk(true); - me->GetMotionMaster()->MovePoint(0, AkamaWP[0].x, AkamaWP[0].y, AkamaWP[0].z, false); - events.ScheduleEvent(EVENT_SHADE_CHANNEL, 10000); - break; - case EVENT_SHADE_CHANNEL: - me->AddUnitState(UNIT_STATE_ROOT); - me->SetFacingTo(3.118662f); - DoCast(me, SPELL_AKAMA_SOUL_CHANNEL); - me->setFaction(FACTION_COMBAT); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - events.ScheduleEvent(EVENT_FIXATE, 5000); - break; - case EVENT_FIXATE: - DoCast(SPELL_FIXATE); - StartChannel = false; - break; - default: - break; - } + summoned->SetWalk(true); + summoned->GetMotionMaster()->MovePoint(0, BrokenWP[i]); + if (i == 9) //On Sniffs, npc that Yell "Special" is the tenth to be created + _chosen = summoned->GetGUID(); } } + } - if (!UpdateVictim()) - return; - - events.Update(diff); + void UpdateAI(uint32 diff) override + { + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { + case EVENT_SHADE_START: + _instance->SetBossState(DATA_SHADE_OF_AKAMA, IN_PROGRESS); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + me->RemoveAurasDueToSpell(SPELL_STEALTH); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(AKAMA_CHANNEL_WAYPOINT, AkamaWP[0], false); + break; + case EVENT_SHADE_CHANNEL: + me->SetFacingTo(3.118662f); + DoCastSelf(SPELL_AKAMA_SOUL_CHANNEL); + me->setFaction(FACTION_COMBAT); + _events.ScheduleEvent(EVENT_FIXATE, Seconds(5)); + break; + case EVENT_FIXATE: + DoCast(SPELL_FIXATE); + break; case EVENT_CHAIN_LIGHTNING: DoCastVictim(SPELL_CHAIN_LIGHTNING); - events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, urand(10000, 15000)); + _events.Repeat(randtime(Seconds(8), Seconds(15))); break; case EVENT_DESTRUCTIVE_POISON: - DoCast(me, SPELL_DESTRUCTIVE_POISON); - events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, urand(4000, 5000)); + DoCastSelf(SPELL_DESTRUCTIVE_POISON); + _events.Repeat(randtime(Seconds(3), Seconds(7))); + break; + case EVENT_START_SOUL_EXPEL: + DoCast(SPELL_AKAMA_SOUL_EXPEL); + _events.ScheduleEvent(EVENT_START_BROKEN_FREE, Seconds(15)); + break; + case EVENT_START_BROKEN_FREE: + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + Talk(SAY_BROKEN_FREE_0); + SummonBrokens(); + _events.ScheduleEvent(EVENT_BROKEN_FREE_1, Seconds(10)); + break; + case EVENT_BROKEN_FREE_1: + Talk(SAY_BROKEN_FREE_1); + _events.ScheduleEvent(EVENT_BROKEN_FREE_2, Seconds(12)); + break; + case EVENT_BROKEN_FREE_2: + Talk(SAY_BROKEN_FREE_2); + _events.ScheduleEvent(EVENT_BROKEN_FREE_3, Seconds(15)); + break; + case EVENT_BROKEN_FREE_3: + if (Creature* special = ObjectAccessor::GetCreature(*me, _chosen)) + special->AI()->Talk(SAY_BROKEN_SPECIAL); + + _summons.DoAction(ACTION_BROKEN_EMOTE, _pred); + _events.ScheduleEvent(EVENT_BROKEN_FREE_4, Seconds(5)); + break; + case EVENT_BROKEN_FREE_4: + _summons.DoAction(ACTION_BROKEN_HAIL, _pred); break; default: break; } } - DoMeleeAttackIfReady(); + if (me->getFaction() == FACTION_COMBAT) + { + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + } + + void JustDied(Unit* /*killer*/) override + { + _summons.DespawnAll(); + Talk(SAY_DEAD); + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (shade->IsAlive()) + shade->AI()->EnterEvadeMode(EVADE_REASON_OTHER); } void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override @@ -524,19 +551,18 @@ public: if (gossipListId == 0) { player->CLOSE_GOSSIP_MENU(); - StartChannel = true; - events.ScheduleEvent(EVENT_SHADE_START, 500); + _events.ScheduleEvent(EVENT_SHADE_START, Milliseconds(500)); } } private: - InstanceScript* instance; - EventMap events; - bool StartChannel; - bool ShadeHasDied; - bool StartCombat; - bool HasYelledOnce; - + InstanceScript* _instance; + EventMap _events; + SummonList _summons; + DummyEntryCheckPredicate _pred; + ObjectGuid _chosen; //Creature that should yell the speech special. + bool _isInCombat; + bool _hasYelledOnce; }; CreatureAI* GetAI(Creature* creature) const override @@ -545,70 +571,44 @@ public: } }; -// ######################################################## -// Ashtongue Channeler -// ######################################################## - class npc_ashtongue_channeler : public CreatureScript { public: npc_ashtongue_channeler() : CreatureScript("npc_ashtongue_channeler") { } - struct npc_ashtongue_channelerAI : public ScriptedAI + struct npc_ashtongue_channelerAI : public PassiveAI { - npc_ashtongue_channelerAI(Creature* creature) : ScriptedAI(creature) + npc_ashtongue_channelerAI(Creature* creature) : PassiveAI(creature) { - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Reset() override { - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); + _scheduler.Schedule(Seconds(2), [this](TaskContext channel) + { + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + { + if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + DoCastSelf(SPELL_SHADE_SOUL_CHANNEL); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_CHANNEL, 2000); - } + else + me->DespawnOrUnsummon(Seconds(3)); + } - void JustDied(Unit* /*killer*/) override - { - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->AI()->DoAction(ACTION_CHANNELER_DIED); + channel.Repeat(Seconds(2)); + }); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } - void EnterCombat(Unit* /*who*/) override { } - void AttackStart(Unit* /*who*/) override { } - void UpdateAI(uint32 diff) override { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_CHANNEL: - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - { - if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - DoCast(me, SPELL_SHADE_SOUL_CHANNEL); - else - { - me->InterruptSpell(CURRENT_CHANNELED_SPELL); - Shade->AI()->DoAction(ACTION_CHANNELER_DIED); - } - } - events.ScheduleEvent(EVENT_CHANNEL, 2000); - break; - default: - break; - } - } + _scheduler.Update(diff); } private: - InstanceScript* instance; - EventMap events; + InstanceScript* _instance; + TaskScheduler _scheduler; }; CreatureAI* GetAI(Creature* creature) const override @@ -617,10 +617,6 @@ public: } }; -// ######################################################## -// Creature Generator Akama -// ######################################################## - class npc_creature_generator_akama : public CreatureScript { public: @@ -628,57 +624,53 @@ public: struct npc_creature_generator_akamaAI : public ScriptedAI { - npc_creature_generator_akamaAI(Creature* creature) : ScriptedAI(creature), Summons(me) + npc_creature_generator_akamaAI(Creature* creature) : ScriptedAI(creature), _summons(me) { Initialize(); - instance = creature->GetInstanceScript(); } void Initialize() { - doSpawning = false; - leftSide = false; + _leftSide = false; + _events.Reset(); + _summons.DespawnAll(); } void Reset() override { - Summons.DespawnAll(); - Initialize(); - if (me->GetPositionY() < 400.0f) - leftSide = true; + if (me->GetPositionY() < MIDDLE_OF_ROOM) + _leftSide = true; } void JustSummoned(Creature* summon) override { - Summons.Summon(summon); + _summons.Summon(summon); } void DoAction(int32 actionId) override { - doSpawning = true; - switch (actionId) { case ACTION_START_SPAWNING: - if (leftSide) + if (_leftSide) { - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 100); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(2000, 5000)); + _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Milliseconds(100)); + _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, randtime(Seconds(2), Seconds(5))); } else { - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 10000); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(2000, 5000)); + _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Seconds(10)); + _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, randtime(Seconds(2), Seconds(5))); } break; case ACTION_STOP_SPAWNING: - doSpawning = false; + _events.Reset(); break; case ACTION_DESPAWN_ALL_SPAWNS: - doSpawning = false; - Summons.DespawnAll(); + _events.Reset(); + _summons.DespawnAll(); break; default: break; @@ -687,39 +679,34 @@ public: void UpdateAI(uint32 diff) override { - if (doSpawning) - { - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_SPAWN_WAVE_B: - DoCast(me, SPELL_ASHTONGUE_WAVE_B); - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, urand(45000, 50000)); - break; - case EVENT_SUMMON_ASHTONGUE_SORCERER: // left - DoCast(me, SPELL_SUMMON_ASHTONGUE_SORCERER); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(30000, 35000)); - break; - case EVENT_SUMMON_ASHTONGUE_DEFENDER: // right - DoCast(me, SPELL_SUMMON_ASHTONGUE_DEFENDER); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(30000, 35000)); - break; - default: - break; - } + case EVENT_SPAWN_WAVE_B: + DoCastSelf(SPELL_ASHTONGUE_WAVE_B); + _events.Repeat(randtime(Seconds(50), Seconds(60))); + break; + case EVENT_SUMMON_ASHTONGUE_SORCERER: // left + DoCastSelf(SPELL_SUMMON_ASHTONGUE_SORCERER); + _events.Repeat(randtime(Seconds(30), Seconds(35))); + break; + case EVENT_SUMMON_ASHTONGUE_DEFENDER: // right + DoCastSelf(SPELL_SUMMON_ASHTONGUE_DEFENDER); + _events.Repeat(randtime(Seconds(30), Seconds(40))); + break; + default: + break; } } } private: - InstanceScript* instance; - EventMap events; - SummonList Summons; - bool leftSide; - bool doSpawning; + EventMap _events; + SummonList _summons; + bool _leftSide; }; CreatureAI* GetAI(Creature* creature) const override @@ -728,10 +715,6 @@ public: } }; -// ######################################################## -// Ashtongue Sorcerer -// ######################################################## - class npc_ashtongue_sorcerer : public CreatureScript { public: @@ -742,103 +725,96 @@ public: npc_ashtongue_sorcererAI(Creature* creature) : ScriptedAI(creature) { Initialize(); - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Initialize() { - startedBanishing = false; - switchToCombat = false; + _switchToCombat = false; + _inBanish = false; } void Reset() override { - if (!startedBanishing) + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) { - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + me->GetMotionMaster()->MovePoint(0, shade->GetPosition()); + + else { - if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - me->GetMotionMaster()->MovePoint(0, Shade->GetPositionX(), Shade->GetPositionY(), Shade->GetPositionZ(), false); - else - { - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); - } + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } } - Initialize(); } void JustDied(Unit* /*killer*/) override { - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->AI()->DoAction(ACTION_CHANNELER_DIED); - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } + void EnterEvadeMode(EvadeReason /*why*/) override { } void EnterCombat(Unit* /*who*/) override { } void AttackStart(Unit* who) override { - if (!switchToCombat) + if (!_switchToCombat) return; + ScriptedAI::AttackStart(who); } - void UpdateAI(uint32 diff) override + void MoveInLineOfSight(Unit* who) override { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) + if (!_inBanish && who->GetGUID() == _instance->GetGuidData(DATA_SHADE_OF_AKAMA) && me->IsWithinDist(who, 20.0f, false)) { - switch (eventId) + _inBanish = true; + me->StopMoving(); + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MovePoint(1, me->GetPositionX() + frand(-8.0f, 8.0f), me->GetPositionY() + frand(-8.0f, 8.0f), me->GetPositionZ()); + + _scheduler.Schedule(Seconds(1) + Milliseconds(500), [this](TaskContext sorcer_channel) { - case EVENT_SORCERER_CHANNEL: - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + { + if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) { - if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - { - me->SetFacingToObject(Shade); - DoCast(me, SPELL_SHADE_SOUL_CHANNEL); - events.ScheduleEvent(EVENT_SORCERER_CHANNEL, 2000); - } - else - { - me->InterruptSpell(CURRENT_CHANNELED_SPELL); - Shade->AI()->DoAction(ACTION_CHANNELER_DIED); - switchToCombat = true; - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); - } + me->SetFacingToObject(shade); + DoCastSelf(SPELL_SHADE_SOUL_CHANNEL); + sorcer_channel.Repeat(Seconds(2)); } - break; - default: - break; - } + else + { + me->InterruptSpell(CURRENT_CHANNELED_SPELL); + _switchToCombat = true; + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); + } + } + }); } + } - if (!startedBanishing) - { - Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA)); - if (me->IsWithinDist(Shade, 20.0f, false)) - { - me->StopMoving(); - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MovePoint(1, me->GetPositionX() + frand (-8.0f, 8.0f), me->GetPositionY() + frand (-8.0f, 8.0f), me->GetPositionZ(), false); - events.ScheduleEvent(EVENT_SORCERER_CHANNEL, 1500); - startedBanishing = true; - } - } + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (!UpdateVictim()) + return; DoMeleeAttackIfReady(); } private: - InstanceScript* instance; - EventMap events; - bool startedBanishing; - bool switchToCombat; + InstanceScript* _instance; + TaskScheduler _scheduler; + bool _switchToCombat; + bool _inBanish; }; CreatureAI* GetAI(Creature* creature) const override @@ -847,10 +823,6 @@ public: } }; -// ######################################################## -// Ashtongue Defender -// ######################################################## - class npc_ashtongue_defender : public CreatureScript { public: @@ -860,54 +832,55 @@ public: { npc_ashtongue_defenderAI(Creature* creature) : ScriptedAI(creature) { - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Reset() override { - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } void JustDied(Unit* /*killer*/) override { - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } void EnterCombat(Unit* /*who*/) override { - events.ScheduleEvent(EVENT_HEROIC_STRIKE, 5000); - events.ScheduleEvent(EVENT_SHIELD_BASH, urand(10000, 16000)); - events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, urand(10000, 16000)); - events.ScheduleEvent(EVENT_WINDFURY, urand(8000, 12000)); + _events.ScheduleEvent(EVENT_HEROIC_STRIKE, Seconds(5)); + _events.ScheduleEvent(EVENT_SHIELD_BASH, randtime(Seconds(10), Seconds(16))); + _events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, randtime(Seconds(10), Seconds(16))); + _events.ScheduleEvent(EVENT_WINDFURY, randtime(Seconds(8), Seconds(12))); } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_DEBILITATING_STRIKE: DoCastVictim(SPELL_DEBILITATING_STRIKE); - events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, urand(8000, 16000)); + _events.Repeat(randtime(Seconds(20), Seconds(25))); break; case EVENT_HEROIC_STRIKE: - DoCast(me, SPELL_HEROIC_STRIKE); - events.ScheduleEvent(EVENT_HEROIC_STRIKE, urand(50000, 60000)); + DoCastSelf(SPELL_HEROIC_STRIKE); + _events.Repeat(randtime(Seconds(5), Seconds(15))); break; case EVENT_SHIELD_BASH: DoCastVictim(SPELL_SHIELD_BASH); - events.ScheduleEvent(EVENT_SHIELD_BASH, urand(8000, 16000)); + _events.Repeat(randtime(Seconds(10), Seconds(20))); break; case EVENT_WINDFURY: DoCastVictim(SPELL_WINDFURY); - events.ScheduleEvent(EVENT_WINDFURY, urand(6000 , 8000)); + _events.Repeat(randtime(Seconds(6), Seconds(8))); break; default: break; @@ -918,8 +891,8 @@ public: } private: - InstanceScript* instance; - EventMap events; + InstanceScript* _instance; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override @@ -928,10 +901,6 @@ public: } }; -// ######################################################## -// Ashtongue Rogue -// ######################################################## - class npc_ashtongue_rogue : public CreatureScript { public: @@ -941,44 +910,46 @@ public: { npc_ashtongue_rogueAI(Creature* creature) : ScriptedAI(creature) { - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Reset() override { - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } void JustDied(Unit* /*killer*/) override { - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } void EnterCombat(Unit* /*who*/) override { - events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand(500, 2000)); - events.ScheduleEvent(EVENT_EVISCERATE, urand(2000, 5000)); + _events.ScheduleEvent(EVENT_DEBILITATING_POISON, randtime(Milliseconds(500), Seconds(2))); + _events.ScheduleEvent(EVENT_EVISCERATE, randtime(Seconds(2), Seconds(5))); } + void EnterEvadeMode(EvadeReason /*why*/) override { } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_DEBILITATING_POISON: DoCastVictim(SPELL_DEBILITATING_POISON); - events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand(14000, 18000)); + _events.Repeat(randtime(Seconds(15), Seconds(20))); break; case EVENT_EVISCERATE: DoCastVictim(SPELL_EVISCERATE); - events.ScheduleEvent(EVENT_EVISCERATE, urand(12000, 16000)); + _events.Repeat(randtime(Seconds(12), Seconds(20))); break; default: break; @@ -989,8 +960,8 @@ public: } private: - InstanceScript* instance; - EventMap events; + InstanceScript* _instance; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override @@ -999,10 +970,6 @@ public: } }; -// ######################################################## -// Ashtongue Elementalist -// ######################################################## - class npc_ashtongue_elementalist : public CreatureScript { public: @@ -1012,44 +979,46 @@ public: { npc_ashtongue_elementalistAI(Creature* creature) : ScriptedAI(creature) { - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Reset() override { - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } void JustDied(Unit* /*killer*/) override { - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } void EnterCombat(Unit* /*who*/) override { - events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 18000); - events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 6000); + _events.ScheduleEvent(EVENT_RAIN_OF_FIRE, Seconds(18)); + _events.ScheduleEvent(EVENT_LIGHTNING_BOLT, Seconds(6)); } + void EnterEvadeMode(EvadeReason /*why*/) override { } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_RAIN_OF_FIRE: DoCastVictim(SPELL_RAIN_OF_FIRE); - events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 20000); + _events.Repeat(randtime(Seconds(15), Seconds(20))); break; case EVENT_LIGHTNING_BOLT: DoCastVictim(SPELL_LIGHTNING_BOLT); - events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 15000); + _events.Repeat(randtime(Seconds(8), Seconds(15))); break; default: break; @@ -1060,8 +1029,8 @@ public: } private: - InstanceScript* instance; - EventMap events; + InstanceScript* _instance; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override @@ -1070,10 +1039,6 @@ public: } }; -// ######################################################## -// Ashtongue Spiritbinder -// ######################################################## - class npc_ashtongue_spiritbinder : public CreatureScript { public: @@ -1084,44 +1049,72 @@ public: npc_ashtongue_spiritbinderAI(Creature* creature) : ScriptedAI(creature) { Initialize(); - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Initialize() { - spiritMend = false; - chainHeal = false; + _spiritMend = false; + _chainHeal = false; } void Reset() override { Initialize(); - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } void JustDied(Unit* /*killer*/) override { - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } void EnterCombat(Unit* /*who*/) override { - events.ScheduleEvent(EVENT_SPIRIT_HEAL, urand (5000, 6000)); + _events.ScheduleEvent(EVENT_SPIRIT_HEAL, randtime(Seconds(5), Seconds(6))); } + void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override + { + if (!_spiritMend) + if (HealthBelowPct(30)) + { + DoCastSelf(SPELL_SPIRIT_MEND); + _spiritMend = true; + _events.ScheduleEvent(EVENT_SPIRIT_MEND_RESET, randtime(Seconds(10),Seconds(15))); + } + + if (!_chainHeal) + if (HealthBelowPct(50)) + { + DoCastSelf(SPELL_CHAIN_HEAL); + _chainHeal = true; + _events.ScheduleEvent(EVENT_CHAIN_HEAL_RESET, randtime(Seconds(10), Seconds(15))); + } + + } + + void EnterEvadeMode(EvadeReason /*why*/) override { } + void UpdateAI(uint32 diff) override { - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_SPIRIT_HEAL: - DoCast(me, SPELL_SPIRITBINDER_SPIRIT_HEAL); - events.ScheduleEvent(EVENT_SPIRIT_HEAL, urand (13000, 16000)); + DoCastSelf(SPELL_SPIRITBINDER_SPIRIT_HEAL); + _events.Repeat(randtime(Seconds(13), Seconds(16))); + break; + case EVENT_SPIRIT_MEND_RESET: + _spiritMend = false; + break; + case EVENT_CHAIN_HEAL_RESET: + _chainHeal = false; break; default: break; @@ -1131,32 +1124,14 @@ public: if (!UpdateVictim()) return; - if (!spiritMend) - { - if (HealthBelowPct(25)) - { - DoCast(me, SPELL_SPIRIT_MEND); - spiritMend = true; - } - } - - if (!chainHeal) - { - if (HealthBelowPct(40)) - { - DoCast(me, SPELL_CHAIN_HEAL); - chainHeal = true; - } - } - DoMeleeAttackIfReady(); } private: - InstanceScript* instance; - EventMap events; - bool spiritMend; - bool chainHeal; + InstanceScript* _instance; + EventMap _events; + bool _spiritMend; + bool _chainHeal; }; CreatureAI* GetAI(Creature* creature) const override @@ -1165,6 +1140,119 @@ public: } }; +class npc_ashtongue_broken : public CreatureScript +{ +public: + npc_ashtongue_broken() : CreatureScript("npc_ashtongue_broken") { } + + struct npc_ashtongue_brokenAI : public ScriptedAI + { + npc_ashtongue_brokenAI(Creature* creature) : ScriptedAI(creature) + { + _instance = me->GetInstanceScript(); + } + + void MovementInform(uint32 motionType, uint32 /*pointId*/) override + { + if (motionType != POINT_MOTION_TYPE) + return; + + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + me->SetFacingToObject(akama); + } + + void DoAction(int32 actionId) override + { + switch (actionId) + { + case ACTION_BROKEN_SPECIAL: + Talk(SAY_BROKEN_SPECIAL); + break; + case ACTION_BROKEN_HAIL: + me->setFaction(FACTION_FRIENDLY); + Talk(SAY_BROKEN_HAIL); + break; + case ACTION_BROKEN_EMOTE: + me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_KNEEL); + break; + default: + break; + } + } + + private: + InstanceScript* _instance; + }; + + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_ashtongue_brokenAI>(creature); + } +}; + +class spell_shade_soul_channel_serverside : public SpellScriptLoader +{ +public: + spell_shade_soul_channel_serverside() : SpellScriptLoader("spell_shade_soul_channel_serverside") { } + + class spell_shade_soul_channel_serverside_AuraScript : public AuraScript + { + PrepareAuraScript(spell_shade_soul_channel_serverside_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHADE_SOUL_CHANNEL_2)) + return false; + return true; + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_shade_soul_channel_serverside_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_shade_soul_channel_serverside_AuraScript(); + } +}; + +class spell_shade_soul_channel : public SpellScriptLoader +{ +public: + spell_shade_soul_channel() : SpellScriptLoader("spell_shade_soul_channel") { } + + class spell_shade_soul_channel_AuraScript : public AuraScript + { + PrepareAuraScript(spell_shade_soul_channel_AuraScript); + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + int32 const maxSlowEff = -99; + if (aurEff->GetAmount() < maxSlowEff) + if (AuraEffect* slowEff = GetEffect(EFFECT_0)) + slowEff->ChangeAmount(maxSlowEff); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_shade_soul_channel_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_shade_soul_channel_AuraScript(); + } +}; + void AddSC_boss_shade_of_akama() { new boss_shade_of_akama(); @@ -1176,4 +1264,7 @@ void AddSC_boss_shade_of_akama() new npc_ashtongue_rogue(); new npc_ashtongue_elementalist(); new npc_ashtongue_spiritbinder(); + new npc_ashtongue_broken(); + new spell_shade_soul_channel_serverside(); + new spell_shade_soul_channel(); } |