Illidian
The Kinslayer - 12-18-2009 15:30
Bite
Kod: /* ScriptData
SDName: Boss_Illidan_Stormrage
SD%Complete: 90
SDComment:
SDCategory: Black Temple
EndScriptData */
#include "precompiled.h"
#include "def_black_temple.h"
#include "WorldPacket.h"
/**** Creature Summon and Recognition IDs ****/
enum CreatureEntry
{
EMPTY = 0,
AKAMA = 22990,
ILLIDAN_STORMRAGE = 22917,
BLADE_OF_AZZINOTH = 22996,
FLAME_OF_AZZINOTH = 22997,
MAIEV_SHADOWSONG = 23197,
SHADOW_DEMON = 23375,
DEMON_FIRE = 23069,
FLAME_CRASH = 23336,
ILLIDAN_DOOR_TRIGGER = 23412,
SPIRIT_OF_OLUM = 23411,
SPIRIT_OF_UDALO = 23410,
ILLIDARI_ELITE = 23226,
PARASITIC_SHADOWFIEND = 23498,
CAGE_TRAP_TRIGGER = 23292,
};
/************* Quotes and Sounds ***********************/
// Gossip for when a player clicks Akama
#define GOSSIP_ITEM "We are ready to face Illidan"
#define SAY_CONVO_1 -1564097
#define SAY_CONVO_2 -1564098
#define SAY_CONVO_3 -1564099
#define SAY_CONVO_4 -1564100
#define SAY_CONVO_5 -1564101
#define SAY_CONVO_6 -1564102
#define SAY_CONVO_7 -1564103
#define SAY_CONVO_8 -1564104
#define SAY_CONVO_9 -1564105
#define SAY_CONVO_10 -1564106
#define SAY_CONVO_11 -1564107
#define SAY_CONVO_12 -1564108
#define SAY_CONVO_13 -1564109
#define SAY_CONVO_14 -1564110
#define SAY_CONVO_15 -1564111
#define SAY_TAUNT_1 -1564112
#define SAY_TAUNT_2 -1564113
#define SAY_TAUNT_3 -1564114
#define SAY_TAUNT_4 -1564115
#define SAY_MAIEV_TAUNT_1 -1564116
#define SAY_MAIEV_TAUNT_2 -1564117
#define SAY_MAIEV_TAUNT_3 -1564118
#define SAY_MAIEV_TAUNT_4 -1564119
//emote only defined if not related to textId (in database)
struct Yells
{
int32 textId;
uint32 creature, timer, emote;
bool Talk;
};
static Yells Conversation[]=
{
{SAY_CONVO_1, ILLIDAN_STORMRAGE, 8000, 0, true},
{0, ILLIDAN_STORMRAGE, 5000, 396, true},
{SAY_CONVO_2, AKAMA, 7000, 0, true},
{0, AKAMA, 5000, 66, true},
{SAY_CONVO_3, ILLIDAN_STORMRAGE, 8000, 0, true},
{SAY_CONVO_4, AKAMA, 3000, 0, true},
{0, AKAMA, 2000, 15, true},
{SAY_CONVO_5, ILLIDAN_STORMRAGE, 3000, 0, true},
{0, EMPTY, 1000, 0, true},
{0, EMPTY, 0, 0, false},
{SAY_CONVO_6, ILLIDAN_STORMRAGE, 8000, 0, true},
{SAY_CONVO_7, MAIEV_SHADOWSONG, 8000, 0, true},
{SAY_CONVO_8, ILLIDAN_STORMRAGE, 7000, 0, true},
{SAY_CONVO_9, MAIEV_SHADOWSONG, 8000, 0, true},
{SAY_CONVO_10, ILLIDAN_STORMRAGE, 1000, 0, false},
{SAY_CONVO_11, MAIEV_SHADOWSONG, 6000, 0, true},
// Emote dead for now. Kill him later
{SAY_CONVO_12, ILLIDAN_STORMRAGE, 22000, 0, true},
{SAY_CONVO_13, MAIEV_SHADOWSONG, 9000, 0, true},
{SAY_CONVO_14, MAIEV_SHADOWSONG, 0, true},
{SAY_CONVO_15, AKAMA, 8000, 0, true},
{0, EMPTY, 1000, 0, false}
};
static Yells RandomTaunts[]=
{
{SAY_TAUNT_1, ILLIDAN_STORMRAGE, 0, 0, false},
{SAY_TAUNT_2, ILLIDAN_STORMRAGE, 0, 0, false},
{SAY_TAUNT_3, ILLIDAN_STORMRAGE, 0, 0, false},
{SAY_TAUNT_4, ILLIDAN_STORMRAGE, 0, 0, false}
};
static Yells MaievTaunts[]=
{
{SAY_MAIEV_TAUNT_1, MAIEV_SHADOWSONG, 0, 0, false},
{SAY_MAIEV_TAUNT_2, MAIEV_SHADOWSONG, 0, 0, false},
{SAY_MAIEV_TAUNT_3, MAIEV_SHADOWSONG, 0, 0, false},
{SAY_MAIEV_TAUNT_4, MAIEV_SHADOWSONG, 0, 0, false}
};
// Yells for/by Akama
#define SAY_AKAMA_BEWARE -1564120
#define SAY_AKAMA_MINION -1564121
#define SAY_AKAMA_LEAVE -1564122
// Self explanatory
#define SAY_KILL1 -1564123
#define SAY_KILL2 -1564124
// I think I'll fly now and let my subordinates take you on
#define SAY_TAKEOFF -1564125
#define SAY_SUMMONFLAMES -1564126
// When casting Eye Blast. Demon Fire will be appear on places that he casts this
#define SAY_EYE_BLAST -1564127
// kk, I go big, dark and demon on you.
#define SAY_MORPH -1564128
// I KILL!
#define SAY_ENRAGE -1564129
/************** Spells *************/
// Normal Form
#define SPELL_SHEAR 41032 // Reduces Max. Health by 60% for 7 seconds. Can stack 19 times. 1.5 second cast
#define SPELL_FLAME_CRASH 40832 // Summons an invis/unselect passive mob that has an aura of flame in a circle around him.
#define SPELL_DRAW_SOUL 40904 // 5k Shadow Damage in front of him. Heals Illidan for 100k health (script effect)
#define SPELL_PARASITIC_SHADOWFIEND 41917 // DoT of 3k Shadow every 2 seconds. Lasts 10 seconds. (Script effect: Summon 2 parasites once the debuff has ticked off)
#define SPELL_SUMMON_PARASITICS 41915 // Summons 2 Parasitic Shadowfiends on the target. It's supposed to be cast as soon as the Parasitic Shadowfiend debuff is gone, but the spells aren't linked :(
#define SPELL_AGONIZING_FLAMES 40932 // 4k fire damage initial to target and anyone w/i 5 yards. PHASE 3 ONLY
#define SPELL_ENRAGE 40683 // Increases damage by 50% and attack speed by 30%. 20 seconds, PHASE 5 ONLY
// Flying (Phase 2)
#define SPELL_THROW_GLAIVE 39635 // Throws a glaive on the ground
#define SPELL_THROW_GLAIVE2 39849 // Animation for the above spell
#define SPELL_GLAIVE_RETURNS 39873 // Glaive flies back to Illidan
#define SPELL_FIREBALL 40598 // 2.5k-3.5k damage in 10 yard radius. 2 second cast time.
#define SPELL_DARK_BARRAGE 40585 // 10 second channeled spell, 3k shadow damage per second.
// Demon Form
#define SPELL_DEMON_TRANSFORM_1 40511 // First phase of animations for transforming into Dark Illidan (fall to ground)
#define SPELL_DEMON_TRANSFORM_2 40398 // Second phase of animations (kneel)
#define SPELL_DEMON_TRANSFORM_3 40510 // Final phase of animations (stand up and roar)
#define SPELL_DEMON_FORM 40506 // Transforms into Demon Illidan. Has an Aura of Dread on him.
#define SPELL_SHADOW_BLAST 41078 // 8k - 11k Shadow Damage. Targets highest threat. Has a splash effect, damaging anyone in 20 yards of the target.
#define SPELL_FLAME_BURST 41126 // Hurls fire xx entire raid for ~3.5k damage every 10 seconds. Resistable. (Does not work: Script effect)
#define SPELL_FLAME_BURST_EFFECT 41131 // The actual damage. Handled by core (41126 triggers 41131)
// Other Illidan spells
#define SPELL_KNEEL 39656 // Before beginning encounter, this is how he appears (talking to Wilson).
#define SPELL_SHADOW_PRISON 40647 // Illidan casts this spell to immobilize entire raid when he summons Maiev.
#define SPELL_DEATH 41220 // This spell doesn't do anything except stun Illidan and set him on his knees.
#define SPELL_BERSERK 45078 // Damage increased by 500%, attack speed by 150%
// Non-Illidan spells
#define SPELL_AKAMA_DOOR_CHANNEL 41268 // Akama's channel spell on the door before the Temple Summit
#define SPELL_DEATHSWORN_DOOR_CHANNEL 41269 // Olum and Udalo's channel spell on the door before the Temple Summit
#define SPELL_AKAMA_DOOR_FAIL 41271 // Not sure where this is really used...
#define SPELL_HEALING_POTION 40535 // Akama uses this to heal himself to full.
#define SPELL_AZZINOTH_CHANNEL 39857 // Glaives cast it on Flames. Not sure if this is the right spell.
#define SPELL_SHADOW_DEMON_PASSIVE 41079 // Adds the "shadowform" aura to Shadow Demons.
#define SPELL_CONSUME_SOUL 41080 // Once the Shadow Demons reach their target, they use this to kill them
#define SPELL_PARALYZE 41083 // Shadow Demons cast this on their target
#define SPELL_PURPLE_BEAM 39123 // Purple Beam connecting Shadow Demon to their target
#define SPELL_CAGE_TRAP_DUMMY 40761 // Put this in DB for cage trap GO.
#define SPELL_EYE_BLAST_TRIGGER 40017 // This summons Demon Form every few seconds and deals ~20k damage in its radius
#define SPELL_EYE_BLAST 39908 // This does the blue flamey animation.
#define SPELL_FLAME_CRASH_EFFECT 40836 // Firey blue ring of circle that the other flame crash summons
#define SPELL_BLAZE_EFFECT 40610 // Green flame on the ground, triggers damage (5k) every few seconds
#define SPELL_BLAZE_SUMMON 40637 // Summons the Blaze creature
#define SPELL_DEMON_FIRE 40029 // Blue fire trail left by Eye Blast. Deals 2k per second if players stand on it.
#define SPELL_CAGED 40695 // Caged Trap triggers will cast this on Illidan if he is within 3 yards
#define SPELL_CAGE_TRAP_SUMMON 40694 // Summons a Cage Trap GO (bugged) on the ground along with a Cage Trap Disturb Trigger mob (working)
#define SPELL_CAGE_TRAP_BEAM 40713 // 8 Triggers on the ground in an octagon cast spells like this on Illidan 'caging him'
#define SPELL_FLAME_BLAST 40631 // Flames of Azzinoth use this. Frontal cone AoE 7k-9k damage.
#define SPELL_CHARGE 40602 // Flames of Azzinoth charges whoever is too far from them. They enrage after this. For simplicity, we'll use the same enrage as Illidan.
#define SPELL_TELEPORT_VISUAL 41232 // Teleport visual for Maiev
#define SPELL_SHADOWFIEND_PASSIVE 41913 // Passive aura for shadowfiends
// Other defines
#define CENTER_X 676.740
#define CENTER_Y 305.297
#define CENTER_Z 353.192
/*** Phase Names ***/
enum Phase
{
PHASE_NORMAL = 1,
PHASE_FLIGHT = 2,
PHASE_NORMAL_2 = 3,
PHASE_DEMON = 4,
PHASE_NORMAL_MAIEV = 5,
PHASE_DEMON_SEQUENCE = 6,
};
struct Locations
{
float x, y, z;
uint32 id;
};
static Locations GlaivePosition[]=
{
{695.105, 305.303, 354.256},
{659.338, 305.303, 354.256},
{700.105, 305.303, 354.256},
{664.338, 305.303, 354.256}
};
static Locations EyeBlast[]=
{
{650.697, 320.128, 353.730},
{652.799, 275.091, 353.367},
{701.527, 273.815, 353.230},
{709.865, 325.654, 353.322}
};
static Locations AkamaWP[]=
{
{ 770.01, 304.50, 312.29 }, // Bottom of the first stairs, xx the doors
{ 780.66, 304.50, 319.74 }, // Top of the first stairs
{ 790.13, 319.68, 319.76 }, // Bottom of the second stairs (left from the entrance)
{ 787.17, 347.38, 341.42 }, // Top of the second stairs
{ 781.34, 350.31, 341.44 }, // Bottom of the third stairs
{ 762.60, 361.06, 353.60 }, // Top of the third stairs
{ 756.35, 360.52, 353.27 }, // Before the door-thingy
{ 743.82, 342.21, 353.00 }, // Somewhere further
{ 732.69, 305.13, 353.00 }, // In front of Illidan
{ 738.11, 365.44, 353.00 }, // in front of the door-thingy (the other one!)
{ 792.18, 366.62, 341.42 }, // Down the first flight of stairs
{ 796.84, 304.89, 319.76 }, // Down the second flight of stairs
{ 782.01, 304.55, 319.76 } // Final location - back xx the initial gates. This is where he will fight the minions!
};
// 755.762, 304.0747, 312.1769 -- This is where Akama should be spawned
static Locations SpiritSpawns[]=
{
{755.5426, 309.9156, 312.2129, SPIRIT_OF_UDALO},
{755.5426, 298.7923, 312.0834, SPIRIT_OF_OLUM}
};
struct WayPoints
{
WayPoints(uint32 _id, float _x, float _y, float _z)
{
id = _id;
x = _x;
y = _y;
z = _z;
}
uint32 id;
float x, y, z;
};
struct Animation // For the demon transformation
{
uint32 aura, unaura, timer, size, displayid, phase;
bool equip;
};
static Animation DemonTransformation[]=
{
{SPELL_DEMON_TRANSFORM_1, 0, 1300, 0, 0, 6, true},
{SPELL_DEMON_TRANSFORM_2, SPELL_DEMON_TRANSFORM_1, 4000, 0, 0, 6, true},
{SPELL_DEMON_FORM, 0, 3000, 1073741824, 21322, 6, false},
{SPELL_DEMON_TRANSFORM_3, SPELL_DEMON_TRANSFORM_2, 3500, 0, 0, 6, false},
{0, 0, 0, 0, 0, 4, false},
{SPELL_DEMON_TRANSFORM_1, 0, 1500, 0, 0, 6, false},
{SPELL_DEMON_TRANSFORM_2, SPELL_DEMON_TRANSFORM_1, 4000, 0, 0, 6, false},
{0, SPELL_DEMON_FORM, 3000, 1069547520, 21135, 6, false},
{SPELL_DEMON_TRANSFORM_3, SPELL_DEMON_TRANSFORM_2, 3500, 0, 0, 6, true},
{0, 0, 0, 0, 0, 8, true}
};
/**** Demon Fire will be used for Eye Blast. Illidan needs to have access to it's vars and functions, so we'll set it here ****/
struct MANGOS_DLL_DECL demonfireAI : public ScriptedAI
{
demonfireAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData();
Reset();
}
ScriptedInstance* m_pInstance;
uint64 IllidanGUID;
bool IsTrigger;
uint32 CheckTimer;
uint32 DemonFireTimer;
uint32 DespawnTimer;
void Reset()
{
IllidanGUID = 0;
IsTrigger = false;
CheckTimer = 2000;
DemonFireTimer = 0;
DespawnTimer = 45000;
}
void AttackStart(Unit* who) { }
void MoveInLineOfSight(Unit *who){ }
void UpdateAI(const uint32 diff)
{
if (IsTrigger)
return;
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
if (CheckTimer < diff)
{
if (!IllidanGUID && m_pInstance)
{
if (Creature* pIllidan = m_pInstance->instance->GetCreature(m_pInstance->GetData64(DATA_ILLIDANSTORMRAGE)))
{
IllidanGUID = m_pInstance->GetData64(DATA_ILLIDANSTORMRAGE);
if (!pIllidan->HasMonsterMoveFlag(MONSTER_MOVE_LEVITATING))
m_creature->setDeathState(JUST_DIED);
}
}
CheckTimer = 2000;
}else CheckTimer -= diff;
if (DemonFireTimer < diff)
{
DoCast(m_creature, SPELL_DEMON_FIRE);
DemonFireTimer = 30000;
}else DemonFireTimer -= diff;
if (DespawnTimer < diff)
m_creature->setDeathState(JUST_DIED);
else DespawnTimer -= diff;
DoMeleeAttackIfReady();
}
};
/******* Functions and vars for Akama's AI ******/
struct MANGOS_DLL_SPEC npc_akama_illidanAI : public ScriptedAI
{
npc_akama_illidanAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData();
WayPointList.clear();
Reset();
}
/* Instance Data */
ScriptedInstance* m_pInstance;
/* Timers */
uint32 ChannelTimer;
uint32 TalkTimer;
uint32 WalkTimer;
uint32 SummonMinionTimer;
/* GUIDs */
uint64 IllidanGUID;
uint64 PlayerGUID;
uint64 SpiritGUID[2];
uint64 ChannelGUID;
bool IsTalking;
bool StartChanneling;
bool DoorOpen;
bool FightMinions;
bool IsReturningToIllidan;
bool IsWalking;
uint32 TalkCount;
uint32 ChannelCount;
std::list<WayPoints> WayPointList;
std::list<WayPoints>::iterator WayPoint;
void BeginEvent(uint64 PlayerGUID);
void Reset()
{
if (m_pInstance)
{
m_pInstance->SetData(TYPE_ILLIDAN, NOT_STARTED);
GameObject* pGate = m_pInstance->instance->GetGameObject(m_pInstance->GetData64(DATA_GAMEOBJECT_ILLIDAN_GATE));
// close door if already open (when raid wipes or something)
if (pGate && !pGate->GetGoState())
pGate->SetGoState(GO_STATE_READY);
for(uint8 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L + 1; ++i)
{
if (GameObject* pDoor = m_pInstance->instance->GetGameObject(m_pInstance->GetData64(i)))
pDoor->SetGoState(GO_STATE_ACTIVE);
}
}
IllidanGUID = 0;
PlayerGUID = 0;
ChannelGUID = 0;
for(uint8 i = 0; i < 2; ++i) SpiritGUID[i] = 0;
ChannelTimer = 0;
ChannelCount = 0;
SummonMinionTimer = 2000;
WalkTimer = 0;
IsWalking = false;
TalkTimer = 0;
TalkCount = 0;
KillAllElites();
IsReturningToIllidan = false;
FightMinions = false;
IsTalking = false;
StartChanneling = false;
DoorOpen = false;
// Database sometimes has strange values..
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, 0);
m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetVisibility(VISIBILITY_ON);
}
// Do not call reset in Akama's evade mode, as this will stop him from summoning minions after he kills the first bit
void EnterEvadeMode()
{
m_creature->RemoveAllAuras();
m_creature->DeleteThreatList();
m_creature->CombatStop(true);
}
void KillAllElites()
{
std::list<HostilReference*>::iterator itr;
for(itr = m_creature->getThreatManager().getThreatList().begin(); itr != m_creature->getThreatManager().getThreatList().end(); ++itr)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
if (pUnit && (pUnit->GetTypeId() == TYPEID_UNIT) && (pUnit->GetEntry() == ILLIDARI_ELITE))
pUnit->setDeathState(JUST_DIED);
}
}
void ReturnToIllidan()
{
KillAllElites();
FightMinions = false;
IsReturningToIllidan = true;
WayPoint = WayPointList.begin();
m_creature->SetSpeed(MOVE_RUN, 2.0f);
m_creature->RemoveMonsterMoveFlag(MONSTER_MOVE_WALK);
IsWalking = true;
}
void AddWaypoint(uint32 id, float x, float y, float z)
{
WayPoints AWP(id, x, y, z);
WayPointList.push_back(AWP);
}
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage > m_creature->GetHealth() && (done_by->GetGUID() != m_creature->GetGUID()))
{
damage = 0;
DoCast(m_creature, SPELL_HEALING_POTION);
}
}
void BeginDoorEvent(Player* pPlayer)
{
if (!m_pInstance)
return;
debug_log("SD2: Akama - Door event initiated by player %s", pPlayer->GetName());
PlayerGUID = pPlayer->GetGUID();
if (GameObject* pGate = m_pInstance->instance->GetGameObject(m_pInstance->GetData64(DATA_GAMEOBJECT_ILLIDAN_GATE)))
{
float x,y,z;
pGate->GetPosition(x, y, z);
Creature* Channel = m_creature->SummonCreature(ILLIDAN_DOOR_TRIGGER, x, y, z+5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000);
if (Channel)
{
ChannelGUID = Channel->GetGUID();
// Invisible but spell visuals can still be seen.
Channel->SetDisplayId(11686);
Channel->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
float PosX, PosY, PosZ;
m_creature->GetPosition(PosX, PosY, PosZ);
for(uint8 i = 0; i < 2; ++i)
{
Creature* Spirit = m_creature->SummonCreature(SpiritSpawns[i].id, SpiritSpawns[i].x, SpiritSpawns[i].y, SpiritSpawns[i].z, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000);
if (Spirit)
{
Spirit->SetVisibility(VISIBILITY_OFF);
SpiritGUID[i] = Spirit->GetGUID();
}
}
StartChanneling = true;
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
DoCast(Channel, SPELL_AKAMA_DOOR_FAIL);
}
}
}
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE || !IsWalking)
return;
if (WayPoint->id != id)
return;
switch(id)
{
case 6:
if (!IsReturningToIllidan)
{
// open the doors that close the summit
for(uint32 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L+1; ++i)
{
if (GameObject* pDoor = m_pInstance->instance->GetGameObject(m_pInstance->GetData64(i)))
pDoor->SetGoState(GO_STATE_ACTIVE);
}
}
break;
case 7:
if (IsReturningToIllidan)
{
IsWalking = false;
if (IllidanGUID)
{
Unit* Illidan = Unit::GetUnit((*m_creature), IllidanGUID);
if (Illidan)
{
float dx = Illidan->GetPositionX() + rand()%15;
float dy = Illidan->GetPositionY() + rand()%15;
m_creature->GetMotionMaster()->MovePoint(13, dx, dy, Illidan->GetPositionZ());
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, IllidanGUID);
}
}
}
break;
case 8:
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
if (!IsReturningToIllidan)
{
IsWalking = false;
BeginEvent(PlayerGUID);
}
break;
case 12:
IsWalking = false;
FightMinions = true;
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
break;
}
++WayPoint;
WalkTimer = 200;
}
void DeleteFromThreatList()
{
// If we do not have Illidan's GUID, do not proceed
if (!IllidanGUID)
return;
// Create a pointer to Illidan
Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID));
// No use to continue if Illidan does not exist
if (!Illidan)
return;
std::list<HostilReference*>::iterator itr = Illidan->getThreatManager().getThreatList().begin();
for(; itr != Illidan->getThreatManager().getThreatList().end(); ++itr)
{
// Loop through threatlist till our GUID is found in it.
if ((*itr)->getUnitGuid() == m_creature->GetGUID())
{
(*itr)->removeReference(); // Delete ourself from his threatlist.
return; // No need to continue anymore.
}
}
// Now we delete our threatlist to prevent attacking anyone for now
m_creature->DeleteThreatList();
}
void UpdateAI(const uint32 diff)
{
if (IllidanGUID)
{
Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID));
if (Illidan)
{
if (Illidan->IsInEvadeMode() && !m_creature->IsInEvadeMode())
EnterEvadeMode();
if (((Illidan->GetHealth()*100 / Illidan->GetMaxHealth()) < 85) && m_creature->isInCombat() && !FightMinions)
{
if (TalkTimer < diff)
{
switch(TalkCount)
{
case 0:
DoScriptText(SAY_AKAMA_MINION, Illidan);
TalkTimer = 8000;
TalkCount = 1;
break;
case 1:
DoScriptText(SAY_AKAMA_LEAVE, m_creature);
TalkTimer = 3000;
TalkCount = 2;
break;
case 2:
IsTalking = true;
TalkTimer = 2000;
m_creature->RemoveAllAuras();
m_creature->CombatStop(true);
m_creature->AttackStop();
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
TalkCount = 3;
break;
case 3:
DeleteFromThreatList();
IsWalking = true;
WayPoint = WayPointList.begin();
std::advance(WayPoint, 9);
m_creature->RemoveMonsterMoveFlag(MONSTER_MOVE_WALK);
break;
}
}else TalkTimer -= diff;
}
if (((Illidan->GetHealth()*100 / Illidan->GetMaxHealth()) < 4) && !IsReturningToIllidan)
ReturnToIllidan();
}
}else
{
if (m_pInstance)
IllidanGUID = m_pInstance->GetData64(DATA_ILLIDANSTORMRAGE);
}
if (IsWalking && WalkTimer)
{
if (WalkTimer <= diff)
{
if (WayPoint == WayPointList.end())
return;
m_creature->GetMotionMaster()->MovePoint(WayPoint->id, WayPoint->x, WayPoint->y,WayPoint->z);
WalkTimer = 0;
}else WalkTimer -= diff;
}
if (StartChanneling)
{
if (ChannelTimer < diff)
{
switch(ChannelCount)
{
case 3:
if (!DoorOpen)
{
m_creature->InterruptNonMeleeSpells(true);
for(uint8 i = 0; i < 2; ++i)
{
if (SpiritGUID[i])
{
Unit* Spirit = Unit::GetUnit((*m_creature), SpiritGUID[i]);
if (Spirit)
Spirit->InterruptNonMeleeSpells(true);
}
}
if (GameObject* pGate = m_pInstance->instance->GetGameObject(m_pInstance->GetData64(DATA_GAMEOBJECT_ILLIDAN_GATE)))
pGate->SetGoState(GO_STATE_ACTIVE);
ChannelCount++;
ChannelTimer = 5000;
}
break;
case 4:
m_creature->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE);
ChannelTimer = 2000;
ChannelCount++;
break;
case 5:
DoScriptText(SAY_AKAMA_BEWARE, m_creature);
if (ChannelGUID)
{
Unit* ChannelTarget = Unit::GetUnit((*m_creature), ChannelGUID);
if (ChannelTarget)
ChannelTarget->setDeathState(JUST_DIED);
ChannelGUID = 0;
}
for(uint8 i = 0; i < 2; ++i)
{
if (SpiritGUID[i])
{
Unit* Spirit = Unit::GetUnit((*m_creature), SpiritGUID[i]);
if (Spirit)
Spirit->setDeathState(JUST_DIED);
}
}
ChannelTimer = 6000;
ChannelCount++;
break;
case 6:
StartChanneling = false;
if (WayPointList.empty())
{
error_log("SD2: Akama has no waypoints to start with!");
return;
}
WayPoint = WayPointList.begin();
m_creature->AddMonsterMoveFlag(MONSTER_MOVE_WALK);
m_creature->GetMotionMaster()->MovePoint(WayPoint->id, WayPoint->x, WayPoint->y, WayPoint->z);
IsWalking = true;
break;
default:
if (ChannelGUID)
{
Unit* Channel = Unit::GetUnit((*m_creature), ChannelGUID);
if (Channel)
{
m_creature->InterruptNonMeleeSpells(true);
for(uint8 i = 0; i < 2; ++i)
{
if (SpiritGUID[i])
{
Unit* Spirit = Unit::GetUnit((*m_creature), SpiritGUID[i]);
if (Spirit)
{
Spirit->InterruptNonMeleeSpells(true);
if (ChannelCount%2 == 0)
{
Spirit->CastSpell(Channel, SPELL_DEATHSWORN_DOOR_CHANNEL,false);
DoCast(Channel, SPELL_AKAMA_DOOR_CHANNEL);
}
else
{
if (Spirit->GetVisibility() == VISIBILITY_OFF)
Spirit->SetVisibility(VISIBILITY_ON);
}
}
}
}
if (ChannelCount < 3)
ChannelCount++;
ChannelTimer = 10000;
}
}
break;
}
}else ChannelTimer -= diff;
}
if (FightMinions)
{
if (SummonMinionTimer < diff)
{
if (IllidanGUID)
{
Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID));
if (!Illidan || Illidan->IsInEvadeMode())
{
Reset();
EnterEvadeMode();
return;
}
}
float x,y,z;
m_creature->GetPosition(x,y,z);
Creature* Elite = m_creature->SummonCreature(ILLIDARI_ELITE, x+rand()%10, y+rand()%10, z, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000);
if (Elite)
{
Elite->AI()->AttackStart(m_creature);
Elite->AddThreat(m_creature, 1000000.0f);
AttackStart(Elite);
}
SummonMinionTimer = 10000 + rand()%6000;
}else SummonMinionTimer -= diff;
}
// If we don't have a target, or is talking, or has run away, return
if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) return;
DoMeleeAttackIfReady();
}
};
/************************************** Illidan's AI ***************************************/
struct MANGOS_DLL_SPEC boss_illidan_stormrageAI : public ScriptedAI
{
boss_illidan_stormrageAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData();
for(uint8 i = 0; i < 2; i++)
{
FlameGUID[i] = 0;
GlaiveGUID[i] = 0;
}
AkamaGUID = 0;
MaievGUID = 0;
Reset();
}
/** Instance Data **/
ScriptedInstance* m_pInstance;
/** Generic **/
bool IsTalking;
bool HasSummoned;
bool RefaceVictim;
bool InformAkama;
uint32 Phase;
uint32 GlobalTimer;
uint32 TalkCount;
uint32 DemonFormSequence;
/** GUIDs **/
uint64 FlameGUID[2];
uint64 GlaiveGUID[2];
uint64 AkamaGUID;
uint64 MaievGUID;
/** Timers **/
uint32 ShearTimer;
uint32 DrawSoulTimer;
uint32 FlameCrashTimer;
uint32 ParasiticShadowFiendTimer;
uint32 FireballTimer;
uint32 EyeBlastTimer;
uint32 DarkBarrageTimer;
uint32 SummonBladesTimer; // Animate summoning the Blades of Azzinoth in Phase 2
uint32 SummonFlamesTimer; // Summon Flames of Azzinoth in Phase 2
uint32 CheckFlamesTimer; // This is used to check the status of the Flames to see if we should begin entering Phase 3 or not.
uint32 RetrieveBladesTimer; // Animate retrieving the Blades of Azzinoth in Phase 2 -> 3 transition
uint32 LandTimer; // This is used xx the end of phase 2 to signal Illidan landing after Flames are dead
uint32 AgonizingFlamesTimer;
uint32 ShadowBlastTimer;
uint32 FlameBurstTimer;
uint32 ShadowDemonTimer;
uint32 TalkTimer;
uint32 TransformTimer;
uint32 EnrageTimer;
uint32 CageTimer;
uint32 LayTrapTimer;
uint32 AnimationTimer;
uint32 TauntTimer; // This is used for his random yells
uint32 FaceVictimTimer;
uint32 BerserkTimer;
void Reset()
{
Phase = PHASE_NORMAL;
// Check if any flames/glaives are alive/existing. Kill if alive and set GUIDs to 0
for(uint8 i = 0; i < 2; ++i)
{
if (Unit* Flame = Unit::GetUnit((*m_creature), FlameGUID[i]))
{
if (Flame->isAlive())
Flame->setDeathState(JUST_DIED);
FlameGUID[i] = 0;
}
if (Unit* Glaive = Unit::GetUnit((*m_creature), GlaiveGUID[i]))
{
if (Glaive->isAlive())
Glaive->setDeathState(JUST_DIED);
GlaiveGUID[i] = 0;
}
}
if (Creature* Akama = ((Creature*)Unit::GetUnit((*m_creature), AkamaGUID)))
{
if (!Akama->isAlive())
Akama->Respawn();
((npc_akama_illidanAI*)Akama->AI())->Reset();
((npc_akama_illidanAI*)Akama->AI())->EnterEvadeMode();
Akama->GetMotionMaster()->MoveTargetedHome();
}
InformAkama = false;
RefaceVictim = false;
HasSummoned = false;
FaceVictimTimer = 1000;
BerserkTimer = 1500000;
GlobalTimer = 0;
DemonFormSequence = 0;
/** Normal Form **/
ShearTimer = 20000 + (rand()%11 * 1000); // 20 to 30 seconds
FlameCrashTimer = 30000; // 30 seconds
ParasiticShadowFiendTimer = 25000; // 25 seconds
DrawSoulTimer = 50000; // 50 seconds
/** Phase 2 **/
SummonBladesTimer = 10000;
SummonFlamesTimer = 20000; // Phase 2 timers may be incorrect
FireballTimer = 5000;
DarkBarrageTimer = 45000;
EyeBlastTimer = 30000;
CheckFlamesTimer = 5000;
RetrieveBladesTimer = 5000;
LandTimer = 0;
/** Phase 3+ **/
AgonizingFlamesTimer = 35000; // Phase 3+ timers may be incorrect
ShadowBlastTimer = 3000;
FlameBurstTimer = 10000;
ShadowDemonTimer = 30000;
TransformTimer = 90000;
EnrageTimer = 40000;
CageTimer = 30000;
LayTrapTimer = CageTimer + 2000;
AnimationTimer = 0;
TauntTimer = 30000; // This timer may be off.
m_creature->SetDisplayId(21135);
m_creature->InterruptNonMeleeSpells(false);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
// Unequip warglaives if needed
m_creature->RemoveMonsterMoveFlag(MONSTER_MOVE_LEVITATING);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0);
IsTalking = false;
TalkCount = 0;
TalkTimer = 0;
if (m_pInstance)
m_pInstance->SetData(TYPE_ILLIDAN, NOT_STARTED);
}
void Aggro(Unit* pWho)
{
m_creature->SetInCombatWithZone();
}
void AttackStart(Unit *who)
{
if (!who || IsTalking || Phase == 2 || Phase == 4 || Phase == 6 || m_creature->HasAura(SPELL_KNEEL, 0))
return;
if (who == m_creature)
return;
if (m_creature->Attack(who, true))
{
m_creature->AddThreat(who, 0.0f);
m_creature->SetInCombatWith(who);
who->SetInCombatWith(m_creature);
DoStartMovement(who);
}
}
void MoveInLineOfSight(Unit *who)
{
if (!who || m_creature->getVictim() || IsTalking || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who))
{
if (!m_creature->canFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
return;
float attackRadius = m_creature->GetAttackDistance(who);
if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who))
{
who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
AttackStart(who);
}
}
}
void JustDied(Unit *killer)
{
IsTalking = false;
TalkCount = 0;
TalkTimer = 0;
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
if (!m_pInstance)
return;
// Completed
m_pInstance->SetData(TYPE_ILLIDAN, DONE);
for(uint8 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L + 1; ++i)
{
// Open Doors
if (GameObject* pDoor = m_pInstance->instance->GetGameObject(m_pInstance->GetData64(i)))
pDoor->SetGoState(GO_STATE_ACTIVE);
}
}
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
switch(rand()%2)
{
case 0: DoScriptText(SAY_KILL1, m_creature, victim); break;
case 1: DoScriptText(SAY_KILL2, m_creature, victim); break;
}
}
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage > m_creature->GetHealth()) // Don't let ourselves be slain before we do our death speech
{
damage = 0;
m_creature->SetHealth(m_creature->GetMaxHealth()/100);
}
}
void Cast(Unit* victim, uint32 Spell, bool triggered = false)
{
if (!victim)
return;
RefaceVictim = true;
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID());
m_creature->CastSpell(victim, Spell, triggered);
}
/** This will handle the cast of eye blast **/
void CastEyeBlast()
{
m_creature->InterruptNonMeleeSpells(false);
DarkBarrageTimer += 10000;
DoScriptText(SAY_EYE_BLAST, m_creature);
uint32 initial = rand()%4;
uint32 final = 0;
if (initial < 3)
final = initial+1;
float initial_X = EyeBlast[initial].x;
float initial_Y = EyeBlast[initial].y;
float initial_Z = EyeBlast[initial].z;
float final_X = EyeBlast[final].x;
float final_Y = EyeBlast[final].y;
float final_Z = EyeBlast[final].z;
for(uint8 i = 0; i < 2; ++i)
{
Creature* Trigger = NULL;
Trigger = m_creature->SummonCreature(DEMON_FIRE, initial_X, initial_Y, initial_Z, 0, TEMPSUMMON_TIMED_DESPAWN, 20000);
if (Trigger)
{
((demonfireAI*)Trigger->AI())->IsTrigger = true;
Trigger->GetMotionMaster()->MovePoint(0, final_X, final_Y, final_Z);
if (!i)
Trigger->CastSpell(Trigger, SPELL_EYE_BLAST_TRIGGER, true);
else
{
Trigger->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, Trigger->GetGUID());
DoCast(Trigger, SPELL_EYE_BLAST);
}
}
}
}
// It's only cast on players that are greater than 15 yards away from Illidan.
//If no one is found, cast it on MT instead (since selecting someone in that 15 yard radius would cause the flames to hit the MT anyway).
void CastAgonizingFlames()
{
// We'll use a grid searcher that selects a player that is xx a distance >15 yards
if (Player* pPlayer = GetPlayerAtMinimumRange(15.0f))
DoCast(pPlayer, SPELL_AGONIZING_FLAMES);
else
DoCast(m_creature->getVictim(), SPELL_AGONIZING_FLAMES);
}
void Talk(uint32 count)
{
if (!m_creature->isAlive())
return;
int32 text = 0;
if (Conversation[count].textId)
text = Conversation[count].textId;
TalkTimer = Conversation[count].timer;
uint32 emote = Conversation[count].emote;
IsTalking = Conversation[count].Talk;
Creature* pCreature = NULL;
uint64 GUID = 0;
if (Conversation[count].creature == ILLIDAN_STORMRAGE)
pCreature = m_creature;
else if (Conversation[count].creature == AKAMA)
{
if (!AkamaGUID)
{
if (m_pInstance)
{
AkamaGUID = m_pInstance->GetData64(DATA_AKAMA);
if (!AkamaGUID)
return;
GUID = AkamaGUID;
}
}
else GUID = AkamaGUID;
}
else if (Conversation[count].creature == MAIEV_SHADOWSONG)
{
if (!MaievGUID)
return;
GUID = MaievGUID;
}
else if (Conversation[count].creature == EMPTY) // This is just for special cases without speech/sounds/emotes.
return;
if (GUID) // Now we check if we actually specified a GUID, if so:
// we grab a pointer to that creature
pCreature = ((Creature*)Unit::GetUnit((*m_creature), GUID));
if (pCreature)
{
if (emote)
pCreature->HandleEmoteCommand(emote); // Make the creature do some animation!
if (text)
DoScriptText(text, pCreature); // Have the creature yell out some text
}
}
void Move(float X, float Y, float Z, Creature* pCreature)
{
pCreature->GetMotionMaster()->MovePoint(0, X, Y, Z);
}
void HandleDemonTransformAnimation(uint32 count)
{
uint32 unaura = DemonTransformation[count].unaura;
uint32 aura = DemonTransformation[count].aura;
uint32 displayid = DemonTransformation[count].displayid;
AnimationTimer = DemonTransformation[count].timer;
uint32 size = DemonTransformation[count].size;
m_creature->InterruptNonMeleeSpells(false);
if (DemonTransformation[count].phase != 8)
{
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle();
}
if (unaura)
m_creature->RemoveAurasDueToSpell(unaura);
if (aura)
DoCast(m_creature, aura, true);
if (displayid)
// It's morphin time!
m_creature->SetDisplayId(displayid);
/*if (size)
m_creature->SetUInt32Value(OBJECT_FIELD_SCALE_X, size); // Let us grow! (or shrink)*/
if (DemonTransformation[count].equip)
{
// Requip warglaives if needed
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 45479);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 45481);
}
else
{
// Unequip warglaives if needed
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 0);
}
if (DemonTransformation[count].phase != 8)
Phase = DemonTransformation[count].phase; // Set phase properly
else
{
// Refollow and attack our old victim
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
// Depending on whether we summoned Maiev, we switch to either phase 5 or 3
if (MaievGUID) Phase = PHASE_NORMAL_MAIEV;
else Phase = PHASE_NORMAL_2;
}
if (count == 7)
{
DoResetThreat();
m_creature->RemoveAurasDueToSpell(SPELL_DEMON_FORM);
}
else if (count == 4)
{
DoResetThreat();
if (!m_creature->HasAura(SPELL_DEMON_FORM, 0))
DoCast(m_creature, SPELL_DEMON_FORM, true);
}
}
/** To reduce the amount of code in UpdateAI, we can seperate them into different functions and simply call them from UpdateAI **/
void EnterPhase2()
{
DoScriptText(SAY_TAKEOFF, m_creature);
SummonBladesTimer = 10000; // Summon Glaives when this decrements
SummonFlamesTimer = 20000; // Summon Flames when this decrements
GlobalTimer += 20000;
LandTimer = 0;
Phase = PHASE_FLIGHT;
m_creature->RemoveAllAuras();
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0);
// So players don't shoot us down
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
// We now hover!
m_creature->AddMonsterMoveFlag(MONSTER_MOVE_LEVITATING);
m_creature->GetMotionMaster()->MovePoint(0, CENTER_X, CENTER_Y, CENTER_Z);
for(uint8 i = 0; i < 2; ++i)
{
Creature* Glaive = m_creature->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
if (Glaive)
{
GlaiveGUID[i] = Glaive->GetGUID(); // We need this to remove them later on
Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
Glaive->SetVisibility(VISIBILITY_OFF);
Glaive->setFaction(m_creature->getFaction());
}
}
}
void SummonBladesOfAzzinoth()
{
m_creature->GetMotionMaster()->Clear(false);
LandTimer = 0;
RetrieveBladesTimer = 0;
// Make it look like we're throwing the glaives on the ground
DoCast(m_creature, SPELL_THROW_GLAIVE2);
// We no longer wear the glaives!
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0);
// since they are now channeling the flames (or will be)
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 0);
for(uint8 i = 0; i < 2; ++i)
{
Creature* Glaive = NULL;
Glaive = ((Creature*)Unit::GetUnit((*m_creature), GlaiveGUID[i]));
if (Glaive)
{
DoCast(Glaive, SPELL_THROW_GLAIVE, true);
Glaive->SetVisibility(VISIBILITY_ON);
}
}
}
void SummonFlamesOfAzzinoth()
{
DoScriptText(SAY_SUMMONFLAMES, m_creature);
for(uint8 i = 0; i < 2; ++i)
{
Creature* Flame = NULL;
Creature* Glaive = NULL;
Glaive = ((Creature*)Unit::GetUnit((*m_creature), GlaiveGUID[i]));
if (Glaive)
{
Flame = m_creature->SummonCreature(FLAME_OF_AZZINOTH, GlaivePosition[i+2].x, GlaivePosition[i+2].y, GlaivePosition[i+2].z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);
if (Flame)
{
// Just in case the database has it as a different faction
Flame->setFaction(m_creature->getFaction());
// Attack our target!
Flame->AI()->AttackStart(m_creature->getVictim());
// Record GUID in order to check if they're dead later on to move to the next phase
FlameGUID[i] = Flame->GetGUID();
// Glaives do some random Beam type channel on it.
Glaive->CastSpell(Flame, SPELL_AZZINOTH_CHANNEL, true);
if (m_creature->getVictim())
Flame->AI()->AttackStart(m_creature->getVictim());
}
else
{
error_log("SD2: Illidan Stormrage AI: Unable to summon Flame of Azzinoth (entry: 22997), please check your database");
EnterEvadeMode();
}
}
else
{
error_log("SD2: Illidan Stormrage AI: Unable to summon Blade of Azzinoth (entry: 22996), please check your database");
}
}
DoResetThreat(); // And now reset our threatlist
HasSummoned = true;
}
void SummonMaiev()
{
TauntTimer += 4000;
GlobalTimer += 4000;
m_creature->InterruptNonMeleeSpells(false); // Interrupt any of our spells
Creature* Maiev = NULL; // Summon Maiev near Illidan
Maiev = m_creature->SummonCreature(MAIEV_SHADOWSONG, m_creature->GetPositionX() + 10, m_creature->GetPositionY() + 5, m_creature->GetPositionZ()+2, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000);
if (Maiev)
{
m_creature->GetMotionMaster()->Clear(false); // Stop moving, it's rude to walk and talk!
m_creature->GetMotionMaster()->MoveIdle();
// Just in case someone is unaffected by Shadow Prison
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
DoCast(m_creature, SPELL_SHADOW_PRISON, true);
TalkCount = 10;
IsTalking = true; // We are now talking/
Maiev->SetVisibility(VISIBILITY_OFF); // Leave her invisible until she has to talk
Maiev->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
MaievGUID = Maiev->GetGUID();
}
else // If Maiev cannot be summoned, reset the encounter and post some errors to the console.
{
EnterEvadeMode();
debug_log("SD2: Unable to summon Maiev Shadowsong and enter Phase 4. Resetting Encounter.");
error_log("SD2: Unable to summon Maiev Shadowsong (entry: 23197). Check your database to see if you have the proper SQL for Maiev Shadowsong (entry: 23197)");
}
}
void InitializeDeath()
{
m_creature->RemoveAllAuras();
DoCast(m_creature, SPELL_DEATH); // Animate his kneeling + stun him
// Don't let the players interrupt our talk!
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->GetMotionMaster()->Clear(false); // No moving!
m_creature->GetMotionMaster()->MoveIdle();
if (MaievGUID)
{
if (Creature* Maiev = ((Creature*)Unit::GetUnit((*m_creature), MaievGUID)))
{
Maiev->CombatStop(true); // Maiev shouldn't do anything either. No point in her attacking us =]
Maiev->GetMotionMaster()->Clear(false); // Stop her from moving as well
Maiev->GetMotionMaster()->MoveIdle();
float distance = 10.0f;
float dx = m_creature->GetPositionX() + (distance*cos(m_creature->GetOrientation()));
float dy = m_creature->GetPositionY() + (distance*sin(m_creature->GetOrientation()));
Maiev->GetMap()->CreatureRelocation(m_creature, dx, dy, Maiev->GetPositionZ(), 0.0f);
Maiev->SendMonsterMove(dx, dy, Maiev->GetPositionZ(), 0, MONSTER_MOVE_NONE, 0);
Maiev->CastSpell(Maiev, SPELL_TELEPORT_VISUAL, true);
Maiev->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->GetGUID());
}
}
IsTalking = true;
TalkCount++;
}
void UpdateAI(const uint32 diff)
{
/*** This section will handle the conversations ***/
if (IsTalking) // Somewhat more efficient using a function rather than a long switch
{
if (TalkTimer < diff)
{
switch(TalkCount) // This is only for specialized cases
{
case 0:
// Time to stand up!
m_creature->RemoveAurasDueToSpell(SPELL_KNEEL);
break;
case 8:
// Equip our warglaives!
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 45479);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 45481);
// Hostile if we weren't before
m_creature->setFaction(14);
break;
case 9:
if (AkamaGUID)
{
Creature* Akama = ((Creature*)Unit::GetUnit((*m_creature), AkamaGUID));
if (Akama)
{
// Start attacking Akama
AttackStart(Akama);
// Akama stop talk and start attack illidan
((npc_akama_illidanAI*)Akama->AI())->IsTalking = false;
((npc_akama_illidanAI*)Akama->AI())->AttackStart(m_creature);
Akama->AddThreat(m_creature, 1000000.0f);
}
}
// We are now attackable!
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_8);
debug_log("SD2: Black Temple: Illidan intro complete, players can attack Illidan.");
break;
case 11:
if (MaievGUID)
{
Unit* Maiev = Unit::GetUnit((*m_creature), MaievGUID);
if (Maiev)
{
// Maiev is now visible
Maiev->SetVisibility(VISIBILITY_ON);
// onoz she looks like she teleported!
Maiev->CastSpell(Maiev, SPELL_TELEPORT_VISUAL, true);
// Have her face us
Maiev->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->GetGUID());
// Face her, so it's not rude =P
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, Maiev->GetGUID());
}
}
break;
case 14:
if (MaievGUID)
{
Creature* Maiev = ((Creature*)Unit::GetUnit((*m_creature), MaievGUID));
if (Maiev)
{
Maiev->GetMotionMaster()->Clear(false);
Maiev->GetMotionMaster()->MoveChase(m_creature);
// Have Maiev add a lot of threat on us so that players don't pull her off if they damage her via AOE
Maiev->AddThreat(m_creature, 10000000.0f);
// Force Maiev to attack us.
Maiev->AI()->AttackStart(m_creature);
Maiev->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
}
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
IsTalking = false;
FaceVictimTimer = 2000;
RefaceVictim = true;
break;
case 20:
// Kill ourself.
if (MaievGUID)
{
Creature* Maiev = ((Creature*)Unit::GetUnit((*m_creature), MaievGUID));
if (Maiev)
{
// Make Maiev leave
Maiev->CastSpell(Maiev, SPELL_TELEPORT_VISUAL, true);
Maiev->setDeathState(JUST_DIED);
}
}
IsTalking = false;
if (m_creature->getVictim())
m_creature->getVictim()->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE,SPELL_SCHOOL_MASK_NORMAL, NULL, false);
else
// Now we kill ourself
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
break;
}
// This function does most of the talking
Talk(TalkCount);
TalkCount++;
}else TalkTimer -= diff;
}
// If we don't have a target, return.
if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() || IsTalking)
return;
// If we are 'caged', then we shouldn't do anything such as cast spells or transform into Demon Form.
if (m_creature->HasAura(SPELL_CAGED, 0))
{
// Just so that he doesn't immediately enrage after he stops being caged.
EnrageTimer = 40000;
CageTimer = 30000;
return;
}
// Berserk Timer - flat 25 minutes
if (!m_creature->HasAura(SPELL_BERSERK, 0) && Phase != PHASE_DEMON_SEQUENCE)
{
if (BerserkTimer < diff)
{
DoScriptText(SAY_ENRAGE, m_creature);
DoCast(m_creature, SPELL_BERSERK, true);
}else BerserkTimer -= diff;
}
if (RefaceVictim)
{
if (FaceVictimTimer < diff)
{
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->getVictim()->GetGUID());
FaceVictimTimer = 1000;
RefaceVictim = false;
}else FaceVictimTimer -= diff;
}
/** Signal to change to phase 2 **/
if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 65) && (Phase == PHASE_NORMAL))
EnterPhase2();
/** Signal to summon Maiev **/
if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 30) && !MaievGUID &&
(Phase != PHASE_DEMON || Phase != PHASE_DEMON_SEQUENCE))
SummonMaiev();
/** Time for the death speech **/
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 1) && (!IsTalking) &&
(Phase != PHASE_DEMON || Phase != PHASE_DEMON_SEQUENCE))
InitializeDeath();
/***** Spells for Phase 1, 3 and 5 (Normal Form) ******/
if (Phase == PHASE_NORMAL || Phase == PHASE_NORMAL_2 || Phase == PHASE_NORMAL_MAIEV)
{
if (TauntTimer < diff) // His random taunt/yell timer.
{
uint32 random = rand()%4;
int32 yell = RandomTaunts[random].textId;
if (yell)
DoScriptText(yell, m_creature);
TauntTimer = 32000;
}else TauntTimer -= diff;
// Global Timer so that spells do not overlap.
if (GlobalTimer < diff)
{
if (ShearTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SHEAR);
ShearTimer = 25000 + (rand()%16 * 1000);
GlobalTimer += 2000;
}else ShearTimer -= diff;
if (FlameCrashTimer < diff)
{
//It spawns multiple flames sometimes. Therefore, we'll do this manually.
//DoCast(m_creature->getVictim(), SPELL_FLAME_CRASH);
m_creature->SummonCreature(FLAME_CRASH, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 40000);
FlameCrashTimer = 35000;
GlobalTimer += 2000;
}else FlameCrashTimer -= diff;
if (ParasiticShadowFiendTimer < diff)
{
Unit* target = NULL;
target = SelectUnit(SELECT_TARGET_RANDOM,1);
if (target && target->isAlive() && !target->HasAura(SPELL_PARASITIC_SHADOWFIEND, 0))
{
Cast(target, SPELL_PARASITIC_SHADOWFIEND);
ParasiticShadowFiendTimer = 40000;
}
}else ParasiticShadowFiendTimer -= diff;
if (DrawSoulTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_DRAW_SOUL);
DrawSoulTimer = 55000;
GlobalTimer += 3000;
}else DrawSoulTimer -= diff;
}else GlobalTimer -= diff;
if (!IsTalking)
DoMeleeAttackIfReady();
}
/*** Phase 2 ***/
if (Phase == PHASE_FLIGHT)
{
// Check if we have summoned or not.
if (!HasSummoned)
{
if (SummonBladesTimer)
if (SummonBladesTimer <= diff)
{
SummonBladesOfAzzinoth();
SummonBladesTimer = 0;
}else SummonBladesTimer -= diff;
if (SummonFlamesTimer < diff)
{
SummonFlamesOfAzzinoth();
}else SummonFlamesTimer -= diff;
}
if (!m_creature->GetMotionMaster()->empty() && (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE))
m_creature->GetMotionMaster()->Clear(false);
if (HasSummoned)
{
if (CheckFlamesTimer)
{
if (CheckFlamesTimer <= diff)
{
// Check if flames are dead or non-existant. If so, set GUID to 0.
for(uint8 i = 0; i < 2; i++)
{
if (FlameGUID[i])
{
Unit* Flame = NULL;
Flame = Unit::GetUnit((*m_creature), FlameGUID[i]);
// If the flame dies, or somehow the pointer becomes invalid, reset GUID to 0.
if (!Flame || !Flame->isAlive())
FlameGUID[i] = 0;
}
}
CheckFlamesTimer = 500;
}else CheckFlamesTimer -= diff;
}
// If both flames are dead/non-existant, kill glaives and change to phase 3.
if (!FlameGUID[0] && !FlameGUID[1] && CheckFlamesTimer)
{
RetrieveBladesTimer = 5000; // Prepare for re-equipin!
CheckFlamesTimer = 0;
}
if (RetrieveBladesTimer)
{
if (RetrieveBladesTimer <= diff) // Time to get back our glaives!
{
// Interrupt any spells we might be doing *cough* DArk Barrage *cough*
m_creature->InterruptNonMeleeSpells(false);
for(uint8 i = 0; i < 2; i++)
{
if (GlaiveGUID[i])
{
Unit* Glaive = NULL;
Glaive = Unit::GetUnit((*m_creature), GlaiveGUID[i]);
if (Glaive)
{
// Make it look like the Glaive flies back up to us
Glaive->CastSpell(m_creature, SPELL_GLAIVE_RETURNS, true);
// Despawn the Glaive
Glaive->setDeathState(JUST_DIED);
}
GlaiveGUID[i] = 0;
}
}
// Re-equip our warblades!
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 45479);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 45481);
// Prepare for landin'!
LandTimer = 5000;
RetrieveBladesTimer = 0;
}else RetrieveBladesTimer -= diff;
}
if (LandTimer)
{
// Time to land!
if (LandTimer <= diff)
{
DoResetThreat();
// anndddd touchdown!
m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
m_creature->RemoveMonsterMoveFlag(MONSTER_MOVE_LEVITATING);
Phase = PHASE_NORMAL_2;
// We should let the raid fight us =)
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->getVictim()->GetGUID());
// Chase our victim!
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
}else LandTimer -= diff;
return; // Do not continue past this point if LandTimer is not 0 and we are in phase 2.
}
}
if (GlobalTimer < diff)
{
if (FireballTimer < diff)
{
Cast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL);
FireballTimer = 5000;
}else FireballTimer -= diff;
if (DarkBarrageTimer < diff)
{
m_creature->InterruptNonMeleeSpells(false);
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DARK_BARRAGE);
DarkBarrageTimer = 35000;
GlobalTimer += 9000;
}else DarkBarrageTimer -= diff;
if (EyeBlastTimer < diff)
{
CastEyeBlast();
EyeBlastTimer = 30000;
}else EyeBlastTimer -= diff;
}else GlobalTimer -= diff;
}
/** Phase 3,5 spells only**/
if (Phase == PHASE_NORMAL_2 || Phase == PHASE_NORMAL_MAIEV)
{
if (GlobalTimer < diff)
{
if (AgonizingFlamesTimer < diff)
{
CastAgonizingFlames();
AgonizingFlamesTimer = 60000;
}else AgonizingFlamesTimer -= diff;
}else GlobalTimer -= diff;
if (TransformTimer < diff)
{
uint32 CurHealth = m_creature->GetHealth()*100 / m_creature->GetMaxHealth();
// Prevent Illidan from morphing if less than 32% or 5%, as this may cause issues with the phase transition or death speech
if ((CurHealth < 32 && !MaievGUID) || (CurHealth < 5))
return;
Phase = PHASE_DEMON_SEQUENCE; // Transform sequence
DemonFormSequence = 0;
AnimationTimer = 0;
DoScriptText(SAY_MORPH, m_creature);
TransformTimer = 60000;
FlameBurstTimer = 10000;
ShadowDemonTimer = 30000;
m_creature->GetMotionMaster()->Clear(false);// Stop moving
}else TransformTimer -= diff;
}
/** Phase 4 spells only (Demon Form) **/
if (Phase == PHASE_DEMON)
{
// Stop moving if we are by clearing movement generators.
if (!m_creature->GetMotionMaster()->empty())
m_creature->GetMotionMaster()->Clear(false);
if (TransformTimer < diff)
{
Phase = PHASE_DEMON_SEQUENCE;
DemonFormSequence = 5;
AnimationTimer = 100;
TransformTimer = 60000;
}else TransformTimer -= diff;
if (ShadowDemonTimer < diff)
{
m_creature->InterruptNonMeleeSpells(false);
Creature* ShadowDemon = NULL;
for(uint8 i = 0; i < 4; i++)
{
Unit* target = NULL;
target = SelectUnit(SELECT_TARGET_RANDOM,0);
// only on players.
if (target && target->GetTypeId() == TYPEID_PLAYER)
{
ShadowDemon = m_creature->SummonCreature(SHADOW_DEMON, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 25000);
if (ShadowDemon)
{
ShadowDemon->AddThreat(target, 5000000.0f);
ShadowDemon->AI()->AttackStart(target);
ShadowDemon->SetInCombatWithZone();
}
}
}
ShadowDemonTimer = 60000;
}else ShadowDemonTimer -= diff;
if (GlobalTimer < diff)
{
if (ShadowBlastTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
if (target && target->isAlive())
{
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, target->GetGUID());
DoCast(target, SPELL_SHADOW_BLAST);
ShadowBlastTimer = 4000;
GlobalTimer += 1500;
}
if (!m_creature->HasAura(SPELL_DEMON_FORM, 0))
DoCast(m_creature, SPELL_DEMON_FORM, true);
}else ShadowBlastTimer -= diff;
if (FlameBurstTimer < diff)
{
DoCast(m_creature, SPELL_FLAME_BURST);
FlameBurstTimer = 15000;
}else FlameBurstTimer -= diff;
}else GlobalTimer -= diff;
}
/** Phase 5 timers. Enrage spell **/
if (Phase == PHASE_NORMAL_MAIEV)
{
if (EnrageTimer < diff)
{
DoCast(m_creature, SPELL_ENRAGE);
EnrageTimer = 40000;
CageTimer = 30000;
TransformTimer += 10000;
}else EnrageTimer -= diff;
// We'll handle Cage Trap in Illidan's script for simplicity's sake
if (CageTimer < diff)
{
if (MaievGUID)
{
Unit* Maiev = Unit::GetUnit((*m_creature), MaievGUID);
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
if (!Maiev || !target || (target->GetTypeId() != TYPEID_PLAYER))
return;
float X, Y, Z;
target->GetPosition(X, Y, Z);
Maiev->GetMap()->CreatureRelocation(m_creature, X, Y, Z, Maiev->GetOrientation());
// Make it look like she 'teleported'
Maiev->CastSpell(Maiev, SPELL_TELEPORT_VISUAL, true);
// summon the trap!
Maiev->CastSpell(Maiev, SPELL_CAGE_TRAP_SUMMON, false);
}
CageTimer = 15000;
}else CageTimer -= diff;
}
if (Phase == PHASE_DEMON_SEQUENCE) // Demonic Transformation
{
if (AnimationTimer < diff)
{
HandleDemonTransformAnimation(DemonFormSequence);
DemonFormSequence++;
}else AnimationTimer -= diff;
}
}
};
/*********************** End of Illidan AI ******************************************/
void npc_akama_illidanAI::BeginEvent(uint64 PlayerGUID)
{
debug_log("SD2: Akama - Illidan Introduction started. Illidan event properly begun.");
if (m_pInstance)
{
IllidanGUID = m_pInstance->GetData64(DATA_ILLIDANSTORMRAGE);
m_pInstance->SetData(TYPE_ILLIDAN, IN_PROGRESS);
}
if (m_pInstance)
{
for(uint8 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L+1; ++i)
{
if (GameObject* pDoor = m_pInstance->instance->GetGameObject(m_pInstance->GetData64(i)))
pDoor->SetGoState(GO_STATE_READY);
}
}
if (IllidanGUID)
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID));
if (Illidan)
{
Illidan->RemoveAurasDueToSpell(SPELL_KNEEL); // Time for Illidan to stand up.
// First line of Akama-Illidan convo
((boss_illidan_stormrageAI*)Illidan->AI())->TalkCount = 0;
// Begin Talking
((boss_illidan_stormrageAI*)Illidan->AI())->IsTalking = true;
((boss_illidan_stormrageAI*)Illidan->AI())->AkamaGUID = m_creature->GetGUID();
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, Illidan->GetGUID());
Illidan->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->GetGUID());
IsTalking = true; // Prevent Akama from starting to attack him
// Prevent players from talking again
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
Illidan->GetMotionMaster()->Clear(false);
Illidan->GetMotionMaster()->MoveIdle();
m_creature->GetMotionMaster()->Clear(false);
m_creature->GetMotionMaster()->MoveIdle();
if (PlayerGUID)
{
Unit* pPlayer = Unit::GetUnit((*m_creature), PlayerGUID);
if (pPlayer)
Illidan->AddThreat(pPlayer, 100.0f);
}
}
}
}
bool GossipHello_npc_akama_xx_illidan(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
pPlayer->SEND_GOSSIP_MENU(10465, pCreature->GetGUID());
return true;
}
bool GossipSelect_npc_akama_xx_illidan(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action)
{
if (action == GOSSIP_ACTION_INFO_DEF) // Time to begin the event
{
pPlayer->CLOSE_GOSSIP_MENU();
((npc_akama_illidanAI*)pCreature->AI())->BeginDoorEvent(pPlayer);
}
return true;
}
struct MANGOS_DLL_SPEC boss_maievAI : public ScriptedAI
{
boss_maievAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData();
Reset();
};
uint32 TauntTimer;
uint64 IllidanGUID;
ScriptedInstance* m_pInstance;
void Reset()
{
TauntTimer = 12000;
IllidanGUID = 0;
}
void UpdateAI(const uint32 diff)
{
if (!IllidanGUID)
{
if (m_pInstance)
IllidanGUID = m_pInstance->GetData64(DATA_ILLIDANSTORMRAGE);
}else
{
Creature* Illidan = NULL;
Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID));
if (!Illidan || !Illidan->isAlive() || Illidan->IsInEvadeMode())
{
m_creature->SetVisibility(VISIBILITY_OFF);
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
else if (Illidan && ((Illidan->GetHealth()*100 / Illidan->GetMaxHealth()) < 2))
return;
}
// Return if we don't have a target
if (!m_creature->SelectHostilTarget() || !m_creature->getVictim())
return;
if (TauntTimer < diff)
{
uint32 random = rand()%4;
int32 text = MaievTaunts[random].textId;
DoScriptText(text, m_creature);
TauntTimer = 22000 + rand()%21 * 1000;
}else TauntTimer -= diff;
DoMeleeAttackIfReady();
}
};
struct MANGOS_DLL_DECL cage_trap_triggerAI : public ScriptedAI
{
cage_trap_triggerAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();}
uint64 IllidanGUID;
uint64 CageTrapGUID;
uint32 DespawnTimer;
bool Active;
bool SummonedBeams;
void Reset()
{
IllidanGUID = 0;
CageTrapGUID = 0;
Active = false;
SummonedBeams = false;
DespawnTimer = 0;
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
void MoveInLineOfSight(Unit *who)
{
if (!Active)
return;
if (who && (who->GetTypeId() != TYPEID_PLAYER))
{
if (who->GetEntry() == ILLIDAN_STORMRAGE) // Check if who is Illidan
{
if (!IllidanGUID && m_creature->IsWithinDistInMap(who, 3) && !who->HasAura(SPELL_CAGED, 0))
{
IllidanGUID = who->GetGUID();
who->CastSpell(who, SPELL_CAGED, true);
DespawnTimer = 5000;
// Dispel his enrage
if (who->HasAura(SPELL_ENRAGE, 0))
who->RemoveAurasDueToSpell(SPELL_ENRAGE);
if (GameObject* pCageTrap = m_creature->GetMap()->GetGameObject(CageTrapGUID))
pCageTrap->SetLootState(GO_JUST_DEACTIVATED);
}
}
}
}
void UpdateAI(const uint32 diff)
{
if (DespawnTimer)
{
if (DespawnTimer < diff)
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
else DespawnTimer -= diff;
}
//if (IllidanGUID && !SummonedBeams)
//{
// if (Unit* Illidan = Unit::GetUnit(*m_creature, IllidanGUID)
// {
// //TODO: Find proper spells and properly apply 'caged' Illidan effect
// }
//}
}
};
bool GOHello_cage_trap(Player* pPlayer, GameObject* pGo)
{
float x, y, z;
pPlayer->GetPosition(x, y, z);
// Grid search for nearest live creature of entry 23304 within 10 yards
Creature* pTrigger = GetClosestCreatureWithEntry(pGo, 23304, 10.0f);
if (!pTrigger)
{
error_log("SD2: Cage Trap- Unable to find trigger. This Cage Trap is now useless");
return false;
}
((cage_trap_triggerAI*)pTrigger->AI())->Active = true;
pGo->SetGoState(GO_STATE_ACTIVE);
return true;
}
struct MANGOS_DLL_DECL flame_of_azzinothAI : public ScriptedAI
{
flame_of_azzinothAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();}
uint32 FlameBlastTimer;
uint32 SummonBlazeTimer;
uint32 ChargeTimer;
void Reset()
{
FlameBlastTimer = 15000 + rand()%15000;
SummonBlazeTimer = 10000 + rand()%20000;
ChargeTimer = 5000;
}
void Charge()
{
// Get the Threat List
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
// He doesn't have anyone in his threatlist, useless to continue
if (!m_threatlist.size())
return;
std::list<Unit*> targets;
std::list<HostilReference *>::iterator itr = m_threatlist.begin();
//store the threat list in a different container
for(; itr!= m_threatlist.end(); ++itr)
{
Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid());
//only on alive players
if (target && target->isAlive() && target->GetTypeId() == TYPEID_PLAYER)
targets.push_back(target);
}
//Sort the list of players
targets.sort(ObjectDistanceOrderReversed(m_creature));
//Resize so we only get the furthest target
targets.resize(1);
Unit* target = (*targets.begin());
if (target && (!m_creature->IsWithinDistInMap(target, 40)))
{
DoCast(m_creature, SPELL_ENRAGE, true);
DoCast(target, SPELL_CHARGE);
}
}
void UpdateAI(const uint32 diff)
{
if (!m_creature->SelectHostilTarget() || !m_creature->getVictim())
return;
if (FlameBlastTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_FLAME_BLAST);
FlameBlastTimer = 30000;
}else FlameBlastTimer -= diff;
if (SummonBlazeTimer < diff)
{
DoCast(m_creature, SPELL_BLAZE_SUMMON);
SummonBlazeTimer = 30000 + rand()%20000;
}else SummonBlazeTimer -= diff;
if (ChargeTimer < diff)
{
Charge();
ChargeTimer = 5000;
}else ChargeTimer -= diff;
DoMeleeAttackIfReady();
}
};
struct MANGOS_DLL_DECL shadow_demonAI : public ScriptedAI
{
shadow_demonAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();}
uint64 TargetGUID;
void Reset() { TargetGUID = 0; }
void JustDied(Unit *killer)
{
if (TargetGUID)
{
Unit* target = Unit::GetUnit((*m_creature), TargetGUID);
if (target)
target->RemoveAurasDueToSpell(SPELL_PARALYZE);
}
}
void UpdateAI(const uint32 diff)
{
if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) return;
// Only cast the below on players.
if (m_creature->getVictim()->GetTypeId() != TYPEID_PLAYER) return;
if (!m_creature->getVictim()->HasAura(SPELL_PARALYZE, 0))
{
TargetGUID = m_creature->getVictim()->GetGUID();
m_creature->AddThreat(m_creature->getVictim(), 10000000.0f);
DoCast(m_creature, SPELL_SHADOW_DEMON_PASSIVE, true);
DoCast(m_creature->getVictim(), SPELL_PURPLE_BEAM, true);
DoCast(m_creature->getVictim(), SPELL_PARALYZE, true);
}
// Kill our target if we're very close.
if (m_creature->IsWithinDistInMap(m_creature->getVictim(), 3))
DoCast(m_creature->getVictim(), SPELL_CONSUME_SOUL);
}
};
struct MANGOS_DLL_DECL flamecrashAI : public ScriptedAI
{
flamecrashAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();}
uint32 FlameCrashTimer;
uint32 DespawnTimer;
void Reset()
{
FlameCrashTimer = 3000 +rand()%5000;
DespawnTimer = 60000;
}
void AttackStart(Unit *who) { }
void MoveInLineOfSight(Unit *who){ }
void UpdateAI(const uint32 diff)
{
if (FlameCrashTimer < diff)
{
DoCast(m_creature, SPELL_FLAME_CRASH_EFFECT);
FlameCrashTimer = 15000;
}else FlameCrashTimer -= diff;
if (DespawnTimer < diff)
{
// So that players don't see the sparkly effect when we die.
m_creature->SetVisibility(VISIBILITY_OFF);
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}else DespawnTimer -= diff;
}
};
// Shadowfiends interact with Illidan, setting more targets in Illidan's hashmap
struct MANGOS_DLL_SPEC mob_parasitic_shadowfiendAI : public ScriptedAI
{
mob_parasitic_shadowfiendAI(Creature* pCreature) : ScriptedAI(pCreature)
{
Reset();
}
void Reset() {}
void DoMeleeAttackIfReady()
{
//If we are within range melee the target
if (m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE))
{
//Make sure our attack is ready and we aren't currently casting
if (m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false))
{
if (!m_creature->getVictim()->HasAura(SPELL_PARASITIC_SHADOWFIEND, 0))
DoCast(m_creature->getVictim(), SPELL_PARASITIC_SHADOWFIEND, true);
m_creature->AttackerStateUpdate(m_creature->getVictim());
m_creature->resetAttackTimer();
}
}
}
};
struct MANGOS_DLL_DECL blazeAI : public ScriptedAI
{
blazeAI(Creature* pCreature) : ScriptedAI(pCreature) {Reset();}
uint32 BlazeTimer;
uint32 DespawnTimer;
void Reset()
{
BlazeTimer = 2000;
DespawnTimer = 15000;
}
void AttackStart(Unit* who) { }
void MoveInLineOfSight(Unit *who){ }
void UpdateAI(const uint32 diff)
{
if (BlazeTimer < diff)
{
DoCast(m_creature, SPELL_BLAZE_EFFECT);
BlazeTimer = 15000;
}else BlazeTimer -= diff;
if (DespawnTimer < diff)
{
m_creature->SetVisibility(VISIBILITY_OFF);
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}else DespawnTimer -= diff;
}
};
struct MANGOS_DLL_DECL blade_of_azzinothAI : public ScriptedAI
{
blade_of_azzinothAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); }
void Reset() {}
// Do-Nothing-But-Stand-There
void AttackStart(Unit* who) { }
void MoveInLineOfSight(Unit* who) { }
};
CreatureAI* GetAI_boss_illidan_stormrage(Creature* pCreature)
{
return new boss_illidan_stormrageAI(pCreature);
}
CreatureAI* GetAI_npc_akama_xx_illidan(Creature* pCreature)
{
npc_akama_illidanAI* Akama_AI = new npc_akama_illidanAI(pCreature);
for(uint8 i = 0; i < 13; ++i)
Akama_AI->AddWaypoint(i, AkamaWP[i].x, AkamaWP[i].y, AkamaWP[i].z);
return ((CreatureAI*)Akama_AI);
}
CreatureAI* GetAI_boss_maiev(Creature* pCreature)
{
return new boss_maievAI(pCreature);
}
CreatureAI* GetAI_mob_flame_of_azzinoth(Creature* pCreature)
{
return new flame_of_azzinothAI(pCreature);
}
CreatureAI* GetAI_cage_trap_trigger(Creature* pCreature)
{
return new cage_trap_triggerAI(pCreature);
}
CreatureAI* GetAI_shadow_demon(Creature* pCreature)
{
return new shadow_demonAI(pCreature);
}
CreatureAI* GetAI_flamecrash(Creature* pCreature)
{
return new flamecrashAI(pCreature);
}
CreatureAI* GetAI_demonfire(Creature* pCreature)
{
return new demonfireAI(pCreature);
}
CreatureAI* GetAI_blaze(Creature* pCreature)
{
return new blazeAI(pCreature);
}
CreatureAI* GetAI_blade_of_azzinoth(Creature* pCreature)
{
return new blade_of_azzinothAI(pCreature);
}
CreatureAI* GetAI_parasitic_shadowfiend(Creature* pCreature)
{
return new mob_parasitic_shadowfiendAI(pCreature);
}
void AddSC_boss_illidan()
{
Script* newscript;
newscript = new Script;
newscript->Name = "boss_illidan_stormrage";
newscript->GetAI = &GetAI_boss_illidan_stormrage;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "npc_akama_illidan";
newscript->GetAI = &GetAI_npc_akama_xx_illidan;
newscript->pGossipHello = &GossipHello_npc_akama_xx_illidan;
newscript->pGossipSelect = &GossipSelect_npc_akama_xx_illidan;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "boss_maiev_shadowsong";
newscript->GetAI = &GetAI_boss_maiev;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "mob_flame_of_azzinoth";
newscript->GetAI = &GetAI_mob_flame_of_azzinoth;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "mob_blade_of_azzinoth";
newscript->GetAI = &GetAI_blade_of_azzinoth;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "gameobject_cage_trap";
newscript->pGOHello = &GOHello_cage_trap;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "mob_cage_trap_trigger";
newscript->GetAI = &GetAI_cage_trap_trigger;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "mob_shadow_demon";
newscript->GetAI = &GetAI_shadow_demon;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "mob_flame_crash";
newscript->GetAI = &GetAI_flamecrash;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "mob_demon_fire";
newscript->GetAI = &GetAI_demonfire;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "mob_blaze";
newscript->GetAI = &GetAI_blaze;
newscript->RegisterSelf();
newscript = new Script;
newscript->Name = "mob_parasitic_shadowfiend";
newscript->GetAI = &GetAI_parasitic_shadowfiend;
newscript->RegisterSelf();
}
makwell - 06-21-2010 00:31
L O L
To jest zywcem wziete z Mangosa, a nie TC1.
I mowisz ze u ciebie na serwerze z TC to dziala tak..?
Strona 2 z 2 • Zostało znalezionych 10 wyników • 1, 2