diff options
Diffstat (limited to 'src')
3 files changed, 830 insertions, 713 deletions
diff --git a/src/server/scripts/Outland/BlackTemple/black_temple.h b/src/server/scripts/Outland/BlackTemple/black_temple.h index 2d26fe745c2..971cc36d7ba 100644 --- a/src/server/scripts/Outland/BlackTemple/black_temple.h +++ b/src/server/scripts/Outland/BlackTemple/black_temple.h @@ -19,6 +19,8 @@ #ifndef BLACK_TEMPLE_H_ #define BLACK_TEMPLE_H_ +uint32 const EncounterCount = 9; + enum DataTypes { DATA_AKAMA = 1, @@ -49,4 +51,4 @@ enum DataTypes DATA_BLOOD_ELF_COUNCIL_VOICE = 26 }; -#endif // BLACK_TEMPLE_H_ +#endif 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 04e08a517e7..1f8e57498a5 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,904 +15,1022 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_Shade_of_Akama -SD%Complete: 90 -SDComment: Seems to be complete. -SDCategory: Black Temple -EndScriptData */ +/* +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 "ScriptedGossip.h" +#include "GridNotifiers.h" #include "black_temple.h" -#include "Player.h" -enum ShadeOfAkama +enum Says { - SAY_DEATH = 0, - SAY_LOW_HEALTH = 1, - // Ending cinematic text - SAY_FREE = 2, - SAY_BROKEN_FREE_01 = 0, - SAY_BROKEN_FREE_02 = 1 + // Akama Ending cinematic text + SAY_BROKEN_FREE_0 = 0, + SAY_BROKEN_FREE_1 = 1, + SAY_BROKEN_FREE_2 = 2 }; -#define GOSSIP_ITEM "We are ready to fight alongside you, Akama" - -struct Location +enum Spells { - float x, y, o, z; + // 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 + // 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 + // 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, + SPELL_SUMMON_ASHTONGUE_DEFENDER = 40474, + // Ashtongue Defender + SPELL_DEBILITATING_STRIKE = 41178, + SPELL_HEROIC_STRIKE = 41975, + SPELL_SHIELD_BASH = 41180, + SPELL_WINDFURY = 38229, + // Ashtongue Rogue + SPELL_DEBILITATING_POISON = 41978, + SPELL_EVISCERATE = 41177, }; -/* Not used -static Location ChannelerLocations[]= +enum Creatures { - {463.161285f, 401.219757f, 3.141592f, 0.0f}, - {457.377625f, 391.227661f, 2.106461f, 0.0f}, - {446.012421f, 391.227661f, 1.071904f, 0.0f}, - {439.533783f, 401.219757f, 0.000000f, 0.0f}, - {446.012421f, 411.211853f, 5.210546f, 0.0f}, - {457.377625f, 411.211853f, 4.177494f, 0.0f} + 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 }; -*/ -static Location SpawnLocations[]= +enum Factions { - {498.652740f, 461.728119f, 0.0f, 0.0f}, - {498.505003f, 339.619324f, 0.0f, 0.0f} + FACTION_FRIENDLY = 1820, + FACTION_COMBAT = 1868 }; -static Location AkamaWP[]= +enum SetData { - {482.352448f, 401.162720f, 0.0f, 112.783928f}, - {469.597443f, 402.264404f, 0.0f, 118.537910f} + SETDATA_DATA = 1, + SETDATA_RESET = 1, + SETDATA_CHANNELER_DIED = 2, + SETDATA_START_SPAWNING = 3, + SETDATA_STOP_SPAWNING = 4, + SETDATA_DESPAWN_ALL_SPAWNS = 5, + SETDATA_START_ATTACK_AKAMA = 6 }; -static Location BrokenCoords[]= +enum Events { - {541.375916f, 401.439575f, M_PI, 112.783997f}, // The place where Akama channels - {534.130005f, 352.394531f, 2.164150f, 112.783737f}, // Behind a 'pillar' which is behind the east alcove - {499.621185f, 341.534729f, 1.652856f, 112.783730f}, // East Alcove - {499.151093f, 461.036438f, 4.770888f, 112.78370f}, // West Alcove + // 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, + // Creature spawner + EVENT_SPAWN_WAVE_B = 11, + EVENT_SUMMON_ASHTONGUE_SORCERER = 12, + EVENT_SUMMON_ASHTONGUE_DEFENDER = 13, + // Channeler + EVENT_GET_SHADE_GUID = 14, + EVENT_CHANNEL = 15, + // Ashtongue Sorcerer + EVENT_SORCERER_CHANNEL = 16, + // Ashtongue Defender + EVENT_DEBILITATING_STRIKE = 17, + EVENT_HEROIC_STRIKE = 18, + EVENT_SHIELD_BASH = 19, + EVENT_WINDFURY = 20, + // Ashtongue Rogue + EVENT_DEBILITATING_POISON = 21, + EVENT_EVISCERATE = 22, }; -static Location BrokenWP[]= +struct Location { - {492.491638f, 400.744690f, 3.122336f, 112.783737f}, - {494.335724f, 382.221771f, 2.676230f, 112.783737f}, - {489.555939f, 373.507202f, 2.416263f, 112.783737f}, - {491.136353f, 427.868774f, 3.519748f, 112.783737f}, + float x, y, z; }; -// Locations -#define Z1 118.543144f -#define Z2 120.783768f -#define Z_SPAWN 113.537949f -#define AGGRO_X 482.793182f -#define AGGRO_Y 401.270172f -#define AGGRO_Z 112.783928f -#define AKAMA_X 514.583984f -#define AKAMA_Y 400.601013f -#define AKAMA_Z 112.783997f - - -enum Spells -{ - SPELL_VERTEX_SHADE_BLACK = 39833, - SPELL_SHADE_SOUL_CHANNEL = 40401, - SPELL_DESTRUCTIVE_POISON = 40874, - SPELL_LIGHTNING_BOLT = 42024, - SPELL_AKAMA_SOUL_CHANNEL = 40447, - SPELL_AKAMA_SOUL_RETRIEVE = 40902, - SPELL_AKAMA_SOUL_EXPEL = 40855, - SPELL_SHADE_SOUL_CHANNEL_2 = 40520 -}; +static Location ShadeWP= { 512.4877f, 400.7993f, 112.7837f }; -enum Creatures +static Location AkamaWP[]= { - NPC_CHANNELER = 23421, - NPC_SORCERER = 23215, - NPC_DEFENDER = 23216, - NPC_BROKEN = 23319 + { 517.4877f, 400.7993f, 112.7837f }, + { 468.4435f, 401.1062f, 118.5379f } }; -const uint32 spawnEntries[4]= { 23523, 23318, 23524 }; +// ######################################################## +// Shade of Akama +// ######################################################## -class npc_ashtongue_channeler : public CreatureScript +class boss_shade_of_akama : public CreatureScript { public: - npc_ashtongue_channeler() : CreatureScript("npc_ashtongue_channeler") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_ashtongue_channelerAI (creature); - } + boss_shade_of_akama() : CreatureScript("boss_shade_of_akama") { } - struct npc_ashtongue_channelerAI : public ScriptedAI + struct boss_shade_of_akamaAI : public ScriptedAI { - npc_ashtongue_channelerAI(Creature* creature) : ScriptedAI(creature) + boss_shade_of_akamaAI(Creature* creature) : ScriptedAI(creature) { - ShadeGUID = 0; + instance = creature->GetInstanceScript(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } - uint64 ShadeGUID; + void Reset() + { + if (!HasKilledAkamaAndReseting) + { + for (std::list<uint64>::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) + if (Creature* Channeler = (Unit::GetCreature(*me, *itr))) + Channeler->DespawnOrUnsummon(); - void Reset() OVERRIDE {} - void JustDied(Unit* /*killer*/) OVERRIDE; - void EnterCombat(Unit* /*who*/) OVERRIDE {} - void AttackStart(Unit* /*who*/) OVERRIDE {} - void MoveInLineOfSight(Unit* /*who*/) OVERRIDE {} + for (std::list<uint64>::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) + if (Creature* Spawner = (Unit::GetCreature(*me, *itr))) + Spawner->AI()->SetData(SETDATA_DATA, SETDATA_DESPAWN_ALL_SPAWNS); - void UpdateAI(uint32 /*diff*/) OVERRIDE {} - }; + events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 3000); + events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 5000); + } -}; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); -class npc_ashtongue_sorcerer : public CreatureScript -{ -public: - npc_ashtongue_sorcerer() : CreatureScript("npc_ashtongue_sorcerer") { } + me->SetWalk(true); + combatStarted = false; + akamaReached = false; + HasKilledAkama = false; + HasKilledAkamaAndReseting = false; + } - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_ashtongue_sorcererAI (creature); - } + void JustDied(Unit* /*killer*/) + { + if (instance) + instance->SetData(DATA_SHADEOFAKAMAEVENT, DONE); + } - struct npc_ashtongue_sorcererAI : public ScriptedAI - { - npc_ashtongue_sorcererAI(Creature* creature) : ScriptedAI(creature) + void EnterCombat(Unit* /*who*/) {} + + void AttackStart(Unit* who) { - ShadeGUID = 0; + if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + { + if (Creature* Akama = Unit::GetCreature((*me), akamaGUID)) + if (Akama->IsAlive()) + ScriptedAI::AttackStart(Akama); + } + else + ScriptedAI::AttackStart(who); } - uint64 ShadeGUID; - uint32 CheckTimer; - bool StartBanishing; + void SetData(uint32 data, uint32 value) + { + if (data == SETDATA_DATA && value == SETDATA_CHANNELER_DIED) + me->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); + + UpdateSpeed(); + } - void Reset() OVERRIDE + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) { - StartBanishing = false; - CheckTimer = 5000; + if (spell->Id == SPELL_AKAMA_SOUL_CHANNEL) + { + combatStarted = true; + events.ScheduleEvent(EVENT_START_ATTACK_AKAMA, 500); + events.ScheduleEvent(EVENT_SET_CHANNELERS_SPAWNERS, 1000); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); + if (Creature* Akama = Unit::GetCreature((*me), akamaGUID)) + me->AddThreat(Akama, 10000000.0f); + } + else if (spell->Id == SPELL_SHADE_SOUL_CHANNEL_2) + UpdateSpeed(); } - void JustDied(Unit* /*killer*/) OVERRIDE; - void EnterCombat(Unit* /*who*/) OVERRIDE {} - void AttackStart(Unit* /*who*/) OVERRIDE {} - void MoveInLineOfSight(Unit* /*who*/) OVERRIDE {} + void UpdateSpeed() + { + float moveSpeed = 0.2f; - void UpdateAI(uint32 diff) OVERRIDE + if (me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2) <= 3) + { + moveSpeed = (2.0 - (0.6 * me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2))); + me->SetSpeed(MOVE_WALK, moveSpeed / 2.5); + me->SetSpeed(MOVE_RUN, (moveSpeed * 2) / 7); + me->ClearUnitState(UNIT_STATE_ROOT); + } + else + me->AddUnitState(UNIT_STATE_ROOT); + } + + void UpdateAI(uint32 diff) { - if (StartBanishing) + if (HasKilledAkamaAndReseting) return; - if (CheckTimer <= diff) + events.Update(diff); + + if (!combatStarted) { - Creature* Shade = Unit::GetCreature((*me), ShadeGUID); - if (Shade && Shade->IsAlive() && me->IsAlive()) + while (uint32 eventId = events.ExecuteEvent()) { - if (me->IsWithinDist(Shade, 20, false)) + switch (eventId) { - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MoveIdle(); - DoCast(Shade, SPELL_SHADE_SOUL_CHANNEL, true); - DoCast(Shade, SPELL_SHADE_SOUL_CHANNEL_2, true); + case EVENT_RESET_ENCOUNTER: + if (Creature* Akama = Unit::GetCreature((*me), akamaGUID)) + 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); + akamaGUID = instance->GetData64(DATA_AKAMA_SHADE); + break; + } + default: + break; + } + } + } + else + { + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SET_CHANNELERS_SPAWNERS: + { + for (std::list<uint64>::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) + { + if (Creature* Channeler = (Unit::GetCreature(*me, *itr))) + Channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + for (std::list<uint64>::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) + { + if (Creature* Spawner = (Unit::GetCreature(*me, *itr))) + Spawner->AI()->SetData(SETDATA_DATA, SETDATA_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; + } + } + + if (HasKilledAkama) + { + if (!HasKilledAkamaAndReseting) + { + HasKilledAkamaAndReseting = true; + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + instance->SetData(DATA_SHADEOFAKAMAEVENT, 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 = Unit::GetCreature((*me), akamaGUID)) + Akama->DespawnOrUnsummon(); + + for (std::list<uint64>::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) + if (Creature* Channeler = (Unit::GetCreature(*me, *itr))) + Channeler->DespawnOrUnsummon(); + + for (std::list<uint64>::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) + if (Creature* Spawner = (Unit::GetCreature(*me, *itr))) + Spawner->AI()->SetData(SETDATA_DATA, SETDATA_DESPAWN_ALL_SPAWNS); + + events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 10000); + events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 20000); + } + } + + if (!akamaReached) + { + if (Creature* Akama = Unit::GetCreature((*me), akamaGUID)) + { + if (me->IsWithinDist(Akama, 2.0f, false)) + { + akamaReached = true; + me->GetMotionMaster()->Clear(true); + me->GetMotionMaster()->MoveIdle(); + me->SetWalk(false); + + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + events.CancelEvent(EVENT_START_ATTACK_AKAMA); + events.ScheduleEvent(EVENT_ADD_THREAT, 100); - StartBanishing = true; + for (std::list<uint64>::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) + if (Creature* Spawner = (Unit::GetCreature(*me, *itr))) + Spawner->AI()->SetData(SETDATA_DATA, SETDATA_STOP_SPAWNING); + } } } - CheckTimer = 2000; - } else CheckTimer -= diff; + else + DoMeleeAttackIfReady(); + } } + + public: + bool HasKilledAkama; + private: + InstanceScript* instance; + EventMap events; + std::list<uint64> Channelers; + std::list<uint64> Spawners; + uint64 akamaGUID; + uint64 ShadeGUID; + bool akamaReached; + bool combatStarted; + bool HasKilledAkamaAndReseting; }; + CreatureAI* GetAI(Creature* creature) const + { + return new boss_shade_of_akamaAI (creature); + } }; -class boss_shade_of_akama : public CreatureScript +// ######################################################## +// Akama +// ######################################################## + +class npc_akama_shade : public CreatureScript { public: - boss_shade_of_akama() : CreatureScript("boss_shade_of_akama") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new boss_shade_of_akamaAI (creature); - } + npc_akama_shade() : CreatureScript("npc_akama_shade") { } - struct boss_shade_of_akamaAI : public ScriptedAI + struct npc_akamaAI : public ScriptedAI { - boss_shade_of_akamaAI(Creature* creature) : ScriptedAI(creature), summons(me) + npc_akamaAI(Creature* creature) : ScriptedAI(creature) { instance = creature->GetInstanceScript(); - AkamaGUID = instance ? instance->GetData64(DATA_AKAMA_SHADE) : 0; - me->setActive(true);//if view distance is too low - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); - } - InstanceScript* instance; + } - std::list<uint64> Channelers; - std::list<uint64> Sorcerers; - uint64 AkamaGUID; + void Reset() + { + me->setFaction(FACTION_FRIENDLY); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + DoCast(me, SPELL_STEALTH); + StartChannel = false; + StartCombat = false; + HasYelledOnce = false; + ShadeHasDied = false; + } - uint32 SorcererCount; - uint32 DeathCount; + void JustDied(Unit* /*killer*/) + { + if (Creature* Shade = Unit::GetCreature((*me), ShadeGUID)) + if (Shade->IsAlive()) + CAST_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->HasKilledAkama = true; + me->GetMotionMaster()->Clear(true); + me->GetMotionMaster()->MoveIdle(); + } - uint32 ReduceHealthTimer; - uint32 SummonTimer; - uint32 ResetTimer; - uint32 DefenderTimer; // They are on a flat 15 second timer, independant of the other summon Creature timer. + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) + { + if (!StartCombat) + { + if (spell->Id == SPELL_THREAT) + { + me->ClearUnitState(UNIT_STATE_ROOT); + me->RemoveAura(SPELL_AKAMA_SOUL_CHANNEL); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); + if (Creature* Shade = Unit::GetCreature((*me), ShadeGUID)) + Shade->RemoveAura(SPELL_AKAMA_SOUL_CHANNEL); + StartCombat = true; + } + } + } - bool IsBanished; - bool HasKilledAkama; - bool reseting; - bool GridSearcherSucceeded; - bool HasKilledAkamaAndReseting; - SummonList summons; + void EnterCombat(Unit* /*who*/) + { + events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 2000); + events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, 5000); + } - void Reset() OVERRIDE + void UpdateAI(uint32 diff) { - reseting = true; - HasKilledAkamaAndReseting = false; + if (StartChannel) + { + events.Update(diff); - GridSearcherSucceeded = false; + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SHADE_START: + if (instance) + { + ShadeGUID = instance->GetData64(DATA_SHADEOFAKAMA); + instance->SetData(DATA_SHADEOFAKAMAEVENT, 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; + } + } + } - Sorcerers.clear(); - summons.DespawnAll();//despawn all adds + if (!UpdateVictim()) + return; + + events.Update(diff); - if (Creature* Akama = Unit::GetCreature(*me, AkamaGUID)) + while (uint32 eventId = events.ExecuteEvent()) { - Akama->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);//turn gossip on so players can restart the event - if (Akama->isDead()) + switch (eventId) { - Akama->Respawn();//respawn akama if dead - Akama->AI()->EnterEvadeMode(); + case EVENT_CHAIN_LIGHTNING: + DoCastVictim(SPELL_CHAIN_LIGHTNING); + events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, urand(10000, 15000)); + break; + case EVENT_DESTRUCTIVE_POISON: + DoCast(me, SPELL_DESTRUCTIVE_POISON); + events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, urand(4000, 5000)); + break; + default: + break; } } - SorcererCount = 0; - DeathCount = 0; + DoMeleeAttackIfReady(); + } - SummonTimer = 10000; - ReduceHealthTimer = 0; - ResetTimer = 60000; - DefenderTimer = 15000; + void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) + { + if (action == 0) + { + player->CLOSE_GOSSIP_MENU(); + StartChannel = true; + events.ScheduleEvent(EVENT_SHADE_START, 500); + } + } - IsBanished = true; - HasKilledAkama = false; + private: + InstanceScript* instance; + EventMap events; + uint64 ShadeGUID; + bool StartChannel; + bool ShadeHasDied; + bool StartCombat; + bool HasYelledOnce; - me->SetVisible(true); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - //me->GetMotionMaster()->Clear(); - //me->GetMotionMaster()->MoveIdle(); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); + }; - if (instance) - instance->SetData(DATA_SHADEOFAKAMAEVENT, NOT_STARTED); + CreatureAI* GetAI(Creature* creature) const + { + return new npc_akamaAI (creature); + } +}; - reseting = false; - } +// ######################################################## +// Ashtongue Channeler +// ######################################################## - void JustDied(Unit* /*killer*/) OVERRIDE +class mob_ashtongue_channeler : public CreatureScript +{ +public: + mob_ashtongue_channeler() : CreatureScript("mob_ashtongue_channeler") { } + + struct mob_ashtongue_channelerAI : public ScriptedAI + { + mob_ashtongue_channelerAI(Creature* creature) : ScriptedAI(creature) { - summons.DespawnAll(); + instance = creature->GetInstanceScript(); } - void JustSummoned(Creature* summon) OVERRIDE + void Reset() { - if (summon->GetEntry() == NPC_DEFENDER || summon->GetEntry() == 23523 || summon->GetEntry() == 23318 || summon->GetEntry() == 23524) - summons.Summon(summon); + me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); + + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + events.ScheduleEvent(EVENT_CHANNEL, 2000); + events.ScheduleEvent(EVENT_GET_SHADE_GUID, 1000); } - void SummonedCreatureDespawn(Creature* summon) OVERRIDE + void JustDied(Unit* /*killer*/) { - if (summon->GetEntry() == NPC_DEFENDER || summon->GetEntry() == 23523 || summon->GetEntry() == 23318 || summon->GetEntry() == 23524) - summons.Despawn(summon); + if (Creature* Shade = (Unit::GetCreature((*me), ShadeGUID))) + Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); } - void MoveInLineOfSight(Unit* /*who*/) OVERRIDE + void EnterCombat(Unit* /*who*/) {} + void AttackStart(Unit* /*who*/) {} + void UpdateAI(uint32 diff) { - if (!GridSearcherSucceeded) - { - FindChannelers(); + events.Update(diff); - if (!Channelers.empty()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - for (std::list<uint64>::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) - { - Creature* Channeler = (Unit::GetCreature(*me, *itr)); - if (Channeler) + case EVENT_CHANNEL: + if (Creature* Shade = (Unit::GetCreature((*me), ShadeGUID))) { - if (Channeler->isDead()) + if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + DoCast(me, SPELL_SHADE_SOUL_CHANNEL); + else { - Channeler->RemoveCorpse(); - Channeler->Respawn(); + me->InterruptSpell(CURRENT_CHANNELED_SPELL); + Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); } - - Channeler->CastSpell(me, SPELL_SHADE_SOUL_CHANNEL, true); - Channeler->CastSpell(me, SPELL_SHADE_SOUL_CHANNEL_2, true); - Channeler->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - GridSearcherSucceeded = true; } - } + events.ScheduleEvent(EVENT_CHANNEL, 2000); + break; + case EVENT_GET_SHADE_GUID: + if (instance) + ShadeGUID = instance->GetData64(DATA_SHADEOFAKAMA); + break; + default: + break; } - else - TC_LOG_ERROR(LOG_FILTER_TSCR, "SD2 ERROR: No Channelers are stored in the list. This encounter will not work properly"); } } - void AttackStart(Unit* who) OVERRIDE - { - if (!who || IsBanished) - return; + private: + InstanceScript* instance; + EventMap events; + uint64 ShadeGUID; + }; - if (who->isTargetableForAttack() && who != me) - DoStartMovement(who); + CreatureAI* GetAI(Creature* creature) const + { + return new mob_ashtongue_channelerAI(creature); + } +}; + +// ######################################################## +// Creature Generator Akama +// ######################################################## + +class npc_creature_generator_akama : public CreatureScript +{ +public: + npc_creature_generator_akama() : CreatureScript("npc_creature_generator_akama") { } + + struct npc_creature_generator_akamaAI : public ScriptedAI + { + npc_creature_generator_akamaAI(Creature* creature) : ScriptedAI(creature), Summons(me) + { + instance = creature->GetInstanceScript(); } - void IncrementDeathCount(uint64 guid = 0) // If guid is set, will remove it from list of sorcerer + void Reset() { - if (reseting) - return; + Summons.DespawnAll(); - TC_LOG_DEBUG(LOG_FILTER_TSCR, "Increasing Death Count for Shade of Akama encounter"); - ++DeathCount; - me->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); - if (guid) - { - if (Sorcerers.empty()) - TC_LOG_ERROR(LOG_FILTER_TSCR, "SD2 ERROR: Shade of Akama - attempt to remove guid " UI64FMTD " from Sorcerers list but list is already empty", guid); - else Sorcerers.remove(guid); - } + doSpawning = false; + leftSide = false; + + if (me->GetPositionY() < 400.0f) + leftSide = true; } - void SummonCreature() + void JustSummoned(Creature* summon) { - uint32 random = rand()%2; - float X = SpawnLocations[random].x; - float Y = SpawnLocations[random].y; - // max of 6 sorcerers can be summoned - if ((rand()%3 == 0) && (DeathCount > 0) && (SorcererCount < 7)) + Summons.Summon(summon); + } + + void SetData(uint32 data, uint32 value) + { + if (data == SETDATA_DATA) { - Creature* Sorcerer = me->SummonCreature(NPC_SORCERER, X, Y, Z_SPAWN, 0, TEMPSUMMON_DEAD_DESPAWN, 0); - if (Sorcerer) + doSpawning = true; + + switch (value) { - CAST_AI(npc_ashtongue_sorcerer::npc_ashtongue_sorcererAI, Sorcerer->AI())->ShadeGUID = me->GetGUID(); - Sorcerer->SetWalk(false); - Sorcerer->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); - Sorcerer->SetTarget(me->GetGUID()); - Sorcerers.push_back(Sorcerer->GetGUID()); - --DeathCount; - ++SorcererCount; + case SETDATA_START_SPAWNING: + if (leftSide) + { + events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 100); + events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(2000, 5000)); + } + else + { + events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 10000); + events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(2000, 5000)); + } + break; + case SETDATA_STOP_SPAWNING: + doSpawning = false; + break; + case SETDATA_DESPAWN_ALL_SPAWNS: + doSpawning = false; + Summons.DespawnAll(); + break; + default: + break; } } - else + } + + void UpdateAI(uint32 diff) + { + if (doSpawning) { - for (uint8 i = 0; i < 3; ++i) + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - Creature* Spawn = me->SummonCreature(spawnEntries[i], X, Y, Z_SPAWN, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 25000); - if (Spawn) + switch (eventId) { - Spawn->SetWalk(false); - Spawn->GetMotionMaster()->MovePoint(0, AGGRO_X, AGGRO_Y, AGGRO_Z); - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1); - Spawn->AI()->AttackStart(target); + 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; } } } } - void FindChannelers() - { - std::list<Creature*> ChannelerList; - me->GetCreatureListWithEntryInGrid(ChannelerList, NPC_CHANNELER, 50.0f); + private: + InstanceScript* instance; + EventMap events; + SummonList Summons; + bool leftSide; + bool doSpawning; + }; - if (!ChannelerList.empty()) + CreatureAI* GetAI(Creature* creature) const + { + return new npc_creature_generator_akamaAI(creature); + } +}; + +// ######################################################## +// Ashtongue Sorcerer +// ######################################################## + +class npc_ashtongue_sorcerer : public CreatureScript +{ +public: + npc_ashtongue_sorcerer() : CreatureScript("npc_ashtongue_sorcerer") { } + + struct npc_ashtongue_sorcererAI : public ScriptedAI + { + npc_ashtongue_sorcererAI(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + if (instance) { - for (std::list<Creature*>::const_iterator itr = ChannelerList.begin(); itr != ChannelerList.end(); ++itr) - { - CAST_AI(npc_ashtongue_channeler::npc_ashtongue_channelerAI, (*itr)->AI())->ShadeGUID = me->GetGUID(); - Channelers.push_back((*itr)->GetGUID()); - TC_LOG_DEBUG(LOG_FILTER_TSCR, "Shade of Akama Grid Search found channeler " UI64FMTD ". Adding to list", (*itr)->GetGUID()); - } + akamaGUID = instance->GetData64(DATA_AKAMA_SHADE); + shadeGUID = instance->GetData64(DATA_SHADEOFAKAMA); } - else TC_LOG_ERROR(LOG_FILTER_TSCR, "SD2 ERROR: Grid Search was unable to find any channelers. Shade of Akama encounter will be buggy"); } - void SetSelectableChannelers() + void Reset() { - if (Channelers.empty()) - { - TC_LOG_ERROR(LOG_FILTER_TSCR, "SD2 ERROR: Channeler List is empty, Shade of Akama encounter will be buggy"); - return; - } + if(!startedBanishing) + if (Creature* Shade = (Unit::GetCreature((*me), shadeGUID))) + 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 = me->GetCreature(*me, akamaGUID)) + AttackStart(target); - for (std::list<uint64>::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) - if (Creature* Channeler = (Unit::GetCreature(*me, *itr))) - Channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + startedBanishing = false; + switchToCombat = false; } - void SetAkamaGUID(uint64 guid) { AkamaGUID = guid; } + void JustDied(Unit* /*killer*/) + { + if (Creature* Shade = (Unit::GetCreature((*me), shadeGUID))) + Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + me->DespawnOrUnsummon(5000); + } - void UpdateAI(uint32 diff) OVERRIDE + void IsSummonedBy(Unit* /*summoner*/) { - if (!me->IsInCombat()) - return; + if (Creature* summoner = (Unit::GetCreature((*me), summonerGuid))) + CAST_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); + } - if (IsBanished) - { - // Akama is set in the threatlist so when we reset, we make sure that he is not included in our check - if (me->getThreatManager().getThreatList().size() < 2) - { - EnterEvadeMode(); - return; - } + void EnterCombat(Unit* /*who*/) {} - if (DefenderTimer <= diff) - { - uint32 ran = rand()%2; - Creature* Defender = me->SummonCreature(NPC_DEFENDER, SpawnLocations[ran].x, SpawnLocations[ran].y, Z_SPAWN, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 25000); - if (Defender) - { - Defender->SetWalk(false); - bool move = true; - if (AkamaGUID) - { - if (Creature* Akama = Unit::GetCreature(*me, AkamaGUID)) - { - float x, y, z; - Akama->GetPosition(x, y, z); - // They move towards AKama - Defender->GetMotionMaster()->MovePoint(0, x, y, z); - Defender->AI()->AttackStart(Akama); - } else move = false; - } else move = false; - if (!move) - Defender->GetMotionMaster()->MovePoint(0, AKAMA_X, AKAMA_Y, AKAMA_Z); - } - DefenderTimer = 15000; - } else DefenderTimer -= diff; + void AttackStart(Unit* who) + { + if (!switchToCombat) + return; + ScriptedAI::AttackStart(who); + } - if (SummonTimer <= diff) - { - SummonCreature(); - SummonTimer = 35000; - } else SummonTimer -= diff; + void UpdateAI(uint32 diff) + { + events.Update(diff); - if (DeathCount >= 6) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - if (AkamaGUID) - { - Creature* Akama = Unit::GetCreature((*me), AkamaGUID); - if (Akama && Akama->IsAlive()) + case EVENT_SORCERER_CHANNEL: + if (Creature* Shade = (Unit::GetCreature((*me), shadeGUID))) { - IsBanished = false; - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MoveChase(Akama); - Akama->GetMotionMaster()->Clear(); - // Shade should move to Akama, not the other way around - Akama->GetMotionMaster()->MoveIdle(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - // Crazy amount of threat - me->AddThreat(Akama, 10000000.0f); - Akama->AddThreat(me, 10000000.0f); - me->Attack(Akama, true); - Akama->Attack(me, true); + 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()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + switchToCombat = true; + if (Unit* target = me->GetCreature(*me, akamaGUID)) + AttackStart(target); + } } - } + break; + default: + break; } } - else // No longer banished, let's fight Akama now - { - if (ReduceHealthTimer <= diff) - { - if (AkamaGUID) - { - Creature* Akama = Unit::GetCreature((*me), AkamaGUID); - if (Akama && Akama->IsAlive()) - { - //10 % less health every few seconds. - me->DealDamage(Akama, Akama->GetMaxHealth()/10, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - ReduceHealthTimer = 12000; - } - } - } else ReduceHealthTimer -= diff; - if (HasKilledAkama) + if (!startedBanishing) + { + Creature* Shade = Unit::GetCreature((*me), shadeGUID); + if (me->IsWithinDist(Shade, 20.0f, false)) { - if (!HasKilledAkamaAndReseting)//do not let players kill Shade if Akama is dead and Shade is waiting for ResetTimer!! event would bug - { - HasKilledAkamaAndReseting = true; - me->RemoveAllAuras(); - me->DeleteThreatList(); - me->CombatStop(); - //me->SetFullHealth(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->GetMotionMaster()->MoveTargetedHome(); - } - if (ResetTimer <= diff) - { - EnterEvadeMode();// Reset a little while after killing Akama, evade and respawn Akama - return; - } else ResetTimer -= diff; + 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; } - - DoMeleeAttackIfReady(); } + + DoMeleeAttackIfReady(); } + + private: + InstanceScript* instance; + EventMap events; + uint64 akamaGUID; + uint64 shadeGUID; + uint64 summonerGuid; + float distanceToShade; + bool startedBanishing; + bool switchToCombat; }; + CreatureAI* GetAI(Creature* creature) const + { + return new npc_ashtongue_sorcererAI (creature); + } }; -void npc_ashtongue_channeler::npc_ashtongue_channelerAI::JustDied(Unit* /*killer*/) -{ - Creature* Shade = (Unit::GetCreature((*me), ShadeGUID)); - if (Shade && Shade->IsAlive()) - CAST_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->IncrementDeathCount(); - else TC_LOG_ERROR(LOG_FILTER_TSCR, "SD2 ERROR: Channeler dead but unable to increment DeathCount for Shade of Akama."); -} +// ######################################################## +// Ashtongue Defender +// ######################################################## -void npc_ashtongue_sorcerer::npc_ashtongue_sorcererAI::JustDied(Unit* /*killer*/) -{ - Creature* Shade = (Unit::GetCreature((*me), ShadeGUID)); - if (Shade && Shade->IsAlive()) - CAST_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->IncrementDeathCount(me->GetGUID()); - else TC_LOG_ERROR(LOG_FILTER_TSCR, "SD2 ERROR: Sorcerer dead but unable to increment DeathCount for Shade of Akama."); -} - -class npc_akama_shade : public CreatureScript +class npc_ashtongue_defender : public CreatureScript { public: - npc_akama_shade() : CreatureScript("npc_akama_shade") { } + npc_ashtongue_defender() : CreatureScript("npc_ashtongue_defender") { } - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) OVERRIDE + struct npc_ashtongue_defenderAI : public ScriptedAI { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF + 1) //Fight time + npc_ashtongue_defenderAI(Creature* creature) : ScriptedAI(creature) { - player->CLOSE_GOSSIP_MENU(); - CAST_AI(npc_akama_shade::npc_akamaAI, creature->AI())->BeginEvent(player); + instance = creature->GetInstanceScript(); + if (instance) + akamaGUID = instance->GetData64(DATA_AKAMA_SHADE); } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) OVERRIDE - { - if (player->IsAlive()) + void Reset() { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - player->SEND_GOSSIP_MENU(907, creature->GetGUID()); + if (Unit* target = me->GetCreature(*me, akamaGUID)) + AttackStart(target); } - return true; - } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_akamaAI (creature); - } - - struct npc_akamaAI : public ScriptedAI - { - npc_akamaAI(Creature* creature) : ScriptedAI(creature), summons(me) + void JustDied(Unit* /*killer*/) { - ShadeHasDied = false; - StartCombat = false; - instance = creature->GetInstanceScript(); - if (instance) - ShadeGUID = instance->GetData64(DATA_SHADEOFAKAMA); - else - ShadeGUID = NOT_STARTED; - me->setActive(true); - EventBegun = false; - CastSoulRetrieveTimer = 0; - SoulRetrieveTimer = 0; - SummonBrokenTimer = 0; - EndingTalkCount = 0; - WayPointId = 0; - BrokenSummonIndex = 0; - BrokenList.clear(); - HasYelledOnce = false; + me->DespawnOrUnsummon(5000); } - InstanceScript* instance; - - uint64 ShadeGUID; - - uint32 DestructivePoisonTimer; - uint32 LightningBoltTimer; - uint32 CheckTimer; - uint32 CastSoulRetrieveTimer; - uint32 SoulRetrieveTimer; - uint32 SummonBrokenTimer; - uint32 EndingTalkCount; - uint32 WayPointId; - uint32 BrokenSummonIndex; - - std::list<uint64> BrokenList; + void IsSummonedBy(Unit* /*summoner*/) + { + if (Creature* summoner = (Unit::GetCreature((*me), summonerGuid))) + CAST_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); + } - bool EventBegun; - bool ShadeHasDied; - bool StartCombat; - bool HasYelledOnce; - SummonList summons; + void EnterCombat(Unit* /*who*/) + { + 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)); + } - void Reset() OVERRIDE + void UpdateAI(uint32 diff) { - DestructivePoisonTimer = 15000; - LightningBoltTimer = 10000; - CheckTimer = 2000; + events.Update(diff); - if (!EventBegun) + while (uint32 eventId = events.ExecuteEvent()) { - me->SetUInt32Value(UNIT_NPC_FLAGS, 0); // Database sometimes has very very strange values - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + switch (eventId) + { + case EVENT_DEBILITATING_STRIKE: + DoCastVictim(SPELL_DEBILITATING_STRIKE); + events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, urand (8000, 16000)); + break; + case EVENT_HEROIC_STRIKE: + DoCast(me, SPELL_HEROIC_STRIKE); + events.ScheduleEvent(EVENT_HEROIC_STRIKE, urand(50000, 60000)); + break; + case EVENT_SHIELD_BASH: + DoCastVictim(SPELL_SHIELD_BASH); + events.ScheduleEvent(EVENT_SHIELD_BASH, urand (8000, 16000)); + break; + case EVENT_WINDFURY: + DoCastVictim(SPELL_WINDFURY); + events.ScheduleEvent(EVENT_WINDFURY, urand (6000 , 8000)); + break; + default: + break; + } } - summons.DespawnAll(); + DoMeleeAttackIfReady(); } - void JustSummoned(Creature* summon) OVERRIDE + private: + InstanceScript* instance; + EventMap events; + uint64 akamaGUID; + uint64 summonerGuid; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_ashtongue_defenderAI (creature); + } +}; + +// ######################################################## +// Ashtongue Rogue +// ######################################################## + +class npc_ashtongue_rogue : public CreatureScript +{ +public: + npc_ashtongue_rogue() : CreatureScript("npc_ashtongue_rogue") { } + + struct npc_ashtongue_rogueAI : public ScriptedAI + { + npc_ashtongue_rogueAI(Creature* creature) : ScriptedAI(creature) { - if (summon->GetEntry() == NPC_BROKEN) - summons.Summon(summon); + instance = creature->GetInstanceScript(); + if (instance) + akamaGUID = instance->GetData64(DATA_AKAMA_SHADE); } - void SummonedCreatureDespawn(Creature* summon) OVERRIDE + void Reset() { - if (summon->GetEntry() == NPC_BROKEN) - summons.Despawn(summon); + if (Unit* target = me->GetCreature(*me, akamaGUID)) + AttackStart(target); } - void EnterCombat(Unit* /*who*/) OVERRIDE {} - - void BeginEvent(Player* player) + void JustDied(Unit* /*killer*/) { - if (!instance) - return; - - ShadeGUID = instance->GetData64(DATA_SHADEOFAKAMA); - if (!ShadeGUID) - return; - - Creature* Shade = (Unit::GetCreature((*me), ShadeGUID)); - if (Shade) - { - instance->SetData(DATA_SHADEOFAKAMAEVENT, IN_PROGRESS); - // Prevent players from trying to restart event - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - CAST_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->SetAkamaGUID(me->GetGUID()); - CAST_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->SetSelectableChannelers(); - Shade->AddThreat(me, 1000000.0f); - me->CombatStart(Shade); - Shade->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); - Shade->SetTarget(me->GetGUID()); - if (player) - Shade->AddThreat(player, 1.0f); - DoZoneInCombat(Shade); - EventBegun = true; - } + me->DespawnOrUnsummon(5000); } - void MovementInform(uint32 type, uint32 id) OVERRIDE + void IsSummonedBy(Unit* /*summoner*/) { - if (type != POINT_MOTION_TYPE) - return; - - switch (id) - { - case 0: - ++WayPointId; - break; - - case 1: - if (Creature* Shade = Unit::GetCreature(*me, ShadeGUID)) - { - me->SetTarget(ShadeGUID); - DoCast(Shade, SPELL_AKAMA_SOUL_RETRIEVE); - EndingTalkCount = 0; - SoulRetrieveTimer = 16000; - } - break; - } + if (Creature* summoner = (Unit::GetCreature((*me), summonerGuid))) + CAST_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); } - void JustDied(Unit* /*killer*/) OVERRIDE + void EnterCombat(Unit* /*who*/) { - Talk(SAY_DEATH); - EventBegun = false; - ShadeHasDied = false; - StartCombat = false; - CastSoulRetrieveTimer = 0; - SoulRetrieveTimer = 0; - SummonBrokenTimer = 0; - EndingTalkCount = 0; - WayPointId = 0; - BrokenSummonIndex = 0; - BrokenList.clear(); - HasYelledOnce = false; - Creature* Shade = Unit::GetCreature((*me), ShadeGUID); - if (Shade && Shade->IsAlive()) - CAST_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->HasKilledAkama = true; - summons.DespawnAll(); + events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand (500, 2000)); + events.ScheduleEvent(SPELL_EVISCERATE, urand (2000, 5000)); } - void UpdateAI(uint32 diff) OVERRIDE + void UpdateAI(uint32 diff) { - if (!EventBegun) - return; - - if (HealthBelowPct(15) && !HasYelledOnce) - { - Talk(SAY_LOW_HEALTH); - HasYelledOnce = true; - } + events.Update(diff); - if (ShadeGUID && !StartCombat) + while (uint32 eventId = events.ExecuteEvent()) { - Creature* Shade = (Unit::GetCreature((*me), ShadeGUID)); - if (Shade && Shade->IsAlive()) + switch (eventId) { - if (CAST_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->IsBanished) - { - if (CastSoulRetrieveTimer <= diff) - { - DoCast(Shade, SPELL_AKAMA_SOUL_CHANNEL); - CastSoulRetrieveTimer = 500; - } else CastSoulRetrieveTimer -= diff; - } - else - { - me->InterruptNonMeleeSpells(false); - StartCombat = true; - } - } - } - - if (ShadeHasDied && (WayPointId == 1)) - { - if (instance) - instance->SetData(DATA_SHADEOFAKAMAEVENT, DONE); - me->GetMotionMaster()->MovePoint(WayPointId, AkamaWP[1].x, AkamaWP[1].y, AkamaWP[1].z); - ++WayPointId; - } - - if (!ShadeHasDied && StartCombat) - { - if (CheckTimer <= diff) - { - if (ShadeGUID) - { - Creature* Shade = Unit::GetCreature((*me), ShadeGUID); - if (Shade && !Shade->IsAlive()) - { - ShadeHasDied = true; - WayPointId = 0; - me->SetWalk(true); - me->GetMotionMaster()->MovePoint(WayPointId, AkamaWP[0].x, AkamaWP[0].y, AkamaWP[0].z); - } - if (Shade && Shade->IsAlive()) - { - if (Shade->getThreatManager().getThreatList().size() < 2) - Shade->AI()->EnterEvadeMode(); - } - } - CheckTimer = 5000; - } else CheckTimer -= diff; - } - - if (SummonBrokenTimer && BrokenSummonIndex < 4) - { - if (SummonBrokenTimer <= diff) - { - for (uint8 i = 0; i < 4; ++i) - { - float x = BrokenCoords[BrokenSummonIndex].x + (i*5); - float y = BrokenCoords[BrokenSummonIndex].y + (1*5); - float z = BrokenCoords[BrokenSummonIndex].z; - float o = BrokenCoords[BrokenSummonIndex].o; - Creature* Broken = me->SummonCreature(NPC_BROKEN, x, y, z, o, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000); - if (Broken) - { - float wx = BrokenWP[BrokenSummonIndex].x + (i*5); - float wy = BrokenWP[BrokenSummonIndex].y + (i*5); - float wz = BrokenWP[BrokenSummonIndex].z; - Broken->GetMotionMaster()->MovePoint(0, wx, wy, wz); - Broken->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - BrokenList.push_back(Broken->GetGUID()); - } - } - ++BrokenSummonIndex; - SummonBrokenTimer = 1000; - } else SummonBrokenTimer -= diff; - } - - if (SoulRetrieveTimer) - { - if (SoulRetrieveTimer <= diff) - { - switch (EndingTalkCount) - { - case 0: - me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); - ++EndingTalkCount; - SoulRetrieveTimer = 2000; - SummonBrokenTimer = 1; + case EVENT_DEBILITATING_POISON: + DoCastVictim(SPELL_DEBILITATING_POISON); + events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand (14000, 18000)); break; - case 1: - Talk(SAY_FREE); - ++EndingTalkCount; - SoulRetrieveTimer = 25000; + case EVENT_EVISCERATE: + DoCastVictim(SPELL_EVISCERATE); + events.ScheduleEvent(EVENT_EVISCERATE, urand(12000, 16000)); break; - case 2: - if (!BrokenList.empty()) - { - bool Yelled = false; - for (std::list<uint64>::const_iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr) - if (Creature* unit = Unit::GetCreature(*me, *itr)) - { - if (!Yelled) - { - unit->AI()->Talk(SAY_BROKEN_FREE_01); - Yelled = true; - } - unit->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL); - } - } - ++EndingTalkCount; - SoulRetrieveTimer = 1500; - break; - case 3: - if (!BrokenList.empty()) - { - for (std::list<uint64>::const_iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr) - if (Creature* unit = Unit::GetCreature(*me, *itr)) - // This is the incorrect spell, but can't seem to find the right one. - unit->CastSpell(unit, 39656, true); - } - ++EndingTalkCount; - SoulRetrieveTimer = 5000; + default: break; - case 4: - if (!BrokenList.empty()) - { - for (std::list<uint64>::const_iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr) - if (Creature* unit = Unit::GetCreature((*me), *itr)) - unit->AI()->Talk(SAY_BROKEN_FREE_02); - } - SoulRetrieveTimer = 0; - break; - } - } else SoulRetrieveTimer -= diff; + } } - - if (!UpdateVictim()) - return; - - if (DestructivePoisonTimer <= diff) - { - Creature* Shade = Unit::GetCreature((*me), ShadeGUID); - if (Shade && Shade->IsAlive()) - DoCast(Shade, SPELL_DESTRUCTIVE_POISON); - DestructivePoisonTimer = 15000; - } else DestructivePoisonTimer -= diff; - - if (LightningBoltTimer <= diff) - { - DoCastVictim(SPELL_LIGHTNING_BOLT); - LightningBoltTimer = 10000; - } else LightningBoltTimer -= diff; - DoMeleeAttackIfReady(); } + + private: + InstanceScript* instance; + EventMap events; + uint64 akamaGUID; + uint64 summonerGuid; }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_ashtongue_rogueAI (creature); + } }; void AddSC_boss_shade_of_akama() { new boss_shade_of_akama(); - new npc_ashtongue_channeler(); - new npc_ashtongue_sorcerer(); new npc_akama_shade(); + new mob_ashtongue_channeler(); + new npc_creature_generator_akama(); + new npc_ashtongue_sorcerer(); + new npc_ashtongue_defender(); + new npc_ashtongue_rogue(); } diff --git a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp index 60092689cc0..2a82810a3c1 100644 --- a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp +++ b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp @@ -28,8 +28,6 @@ EndScriptData */ #include "black_temple.h" #include "Player.h" -#define MAX_ENCOUNTER 9 - /* Black Temple encounters: 0 - High Warlord Naj'entus event 1 - Supremus Event @@ -56,7 +54,7 @@ public: { instance_black_temple_InstanceMapScript(Map* map) : InstanceScript(map) {} - uint32 m_auiEncounter[MAX_ENCOUNTER]; + uint32 m_auiEncounter[EncounterCount]; std::string str_data; uint64 Najentus; @@ -89,37 +87,37 @@ public: { memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); - Najentus = 0; - Akama = 0; - Akama_Shade = 0; - ShadeOfAkama = 0; - Supremus = 0; - LadyMalande = 0; - GathiosTheShatterer = 0; + Najentus = 0; + Akama = 0; + Akama_Shade = 0; + ShadeOfAkama = 0; + Supremus = 0; + LadyMalande = 0; + GathiosTheShatterer = 0; HighNethermancerZerevor = 0; - VerasDarkshadow = 0; - IllidariCouncil = 0; - BloodElfCouncilVoice = 0; - IllidanStormrage = 0; - - NajentusGate = 0; - MainTempleDoors = 0; - ShadeOfAkamaDoor= 0; - CommonDoor = 0;//teron + VerasDarkshadow = 0; + IllidariCouncil = 0; + BloodElfCouncilVoice = 0; + IllidanStormrage = 0; + + NajentusGate = 0; + MainTempleDoors = 0; + ShadeOfAkamaDoor = 0; + CommonDoor = 0; // teron TeronDoor = 0; GuurtogDoor = 0; MotherDoor = 0; TempleDoor = 0; - SimpleDoor = 0;//Bycouncil + SimpleDoor = 0; // Bycouncil CouncilDoor = 0; - IllidanGate = 0; - IllidanDoor[0] = 0; - IllidanDoor[1] = 0; + IllidanGate = 0; + IllidanDoor[0] = 0; + IllidanDoor[1] = 0; } bool IsEncounterInProgress() const { - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + for (uint8 i = 0; i < EncounterCount; ++i) if (m_auiEncounter[i] == IN_PROGRESS) return true; @@ -149,7 +147,7 @@ public: { case 22887: Najentus = creature->GetGUID(); break; case 23089: Akama = creature->GetGUID(); break; - case 22990: Akama_Shade = creature->GetGUID(); break; + case 23191: Akama_Shade = creature->GetGUID(); break; case 22841: ShadeOfAkama = creature->GetGUID(); break; case 22898: Supremus = creature->GetGUID(); break; case 22917: IllidanStormrage = creature->GetGUID(); break; @@ -167,13 +165,13 @@ public: switch (go->GetEntry()) { case 185483: - NajentusGate = go->GetGUID();// Gate past Naj'entus (at the entrance to Supermoose's courtyards) + NajentusGate = go->GetGUID(); // Gate past Naj'entus (at the entrance to Supermoose's courtyards) if (m_auiEncounter[0] == DONE) HandleGameObject(0, true, go); break; case 185882: - MainTempleDoors = go->GetGUID();// Main Temple Doors - right past Supermoose (Supremus) + MainTempleDoors = go->GetGUID(); // Main Temple Doors - right past Supermoose (Supremus) if (m_auiEncounter[1] == DONE) HandleGameObject(0, true, go); break; @@ -386,7 +384,7 @@ public: >> m_auiEncounter[3] >> m_auiEncounter[4] >> m_auiEncounter[5] >> m_auiEncounter[6] >> m_auiEncounter[7] >> m_auiEncounter[8]; - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) + for (uint8 i = 0; i < EncounterCount; ++i) if (m_auiEncounter[i] == IN_PROGRESS) m_auiEncounter[i] = NOT_STARTED; |