diff options
Diffstat (limited to 'src/server')
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; | 
