aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMalcrom <malcromdev@gmail.com>2013-07-08 12:39:32 -0230
committerMalcrom <malcromdev@gmail.com>2013-07-08 12:39:32 -0230
commit92eec6831a5f0bc32b641f85515ce4c9d243e24d (patch)
treeefed4b71bb05d9a9c5dbbebdaae2a28f16e89da8 /src
parentbebae5d3fadfaffc3ab9292511df6406b7abe767 (diff)
Core: Scripting: Shade of Akama WIP
Diffstat (limited to 'src')
-rw-r--r--src/server/scripts/Outland/BlackTemple/black_temple.h4
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp1485
-rw-r--r--src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp54
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;