/* Copyright (C) 2006,2007 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 Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* ScriptData
SDName: boss_illidan_stormrage
SD%Complete: 80
SDComment: Need to do Eye Blast, Cage Traps, Enrage, Transform cosmetics and adjust timers.
EndScriptData */
/***************************************/
/* This script is still in development */
/* by Wolfdragon and Moriquendu */
/***************************************/
/******** Includes ************/
#include "../../sc_defines.h"
#include "../../../../../game/TargetedMovementGenerator.h"
#include "../../../../../shared/WorldPacket.h"
#include "../../../../../game/SpellAuras.h"
#include "../../../../../game/Player.h"
#include "../../../../../game/GossipDef.h"
/************* Quotes and Sounds ***********************/
// Gossip for when a player clicks Akama
#define GOSSIP_ITEM "We are ready to face Illidan"
// Intro speech with Akama
#define SAY_INTRO_ILL1 "Akama... your duplicity is hardly surprising. I should have slaughtered you and your malformed brethren long ago."
#define SOUND_INTRO_ILL1 11463 // ~13 seconds
#define SAY_INTRO_AKA1 "We've come to end your reign, Illidan. My people and all of Outland shall be free!"
#define SOUND_INTRO_AKA1 11389 // ~12 seconds
#define SAY_INTRO_ILL2 "Boldly said. But I remain unconvinced."
#define SOUND_INTRO_ILL2 11464 // ~8 seconds
#define SAY_INTRO_AKA2 "The time has come! The moment is at hand!"
#define SOUND_INTRO_AKA2 11380 // ~5 seconds
#define SAY_INTRO_ILL3 "You are not prepared!" // He conjectures that your level of preparation could perhaps be improved somewhat
#define SOUND_INTRO_ILL3 11466
// Signal for Akama to leave
#define SAY_AKAMA_MINION "Come, my minions. Deal with this traitor as he deserves!"
#define SOUND_AKAMA_MINION 11465
#define SAY_AKAMA_LEAVE "I'll deal with these mongrels. Strike now, friends! Strike at the betrayer!"
#define SOUND_AKAMA_LEAVE 11390
// Taunts?
#define SAY_HATRED "I can feel your hatred"
#define SOUND_HATRED 11467
#define SAY_FEAR "Give in to your fear!"
#define SOUND_FEAR 11468
#define SAY_POWER "You know nothing of power!"
#define SOUND_POWER 11469
#define SAY_ARROGANCE "Such..arrogance!"
#define SOUND_ARROGANCE 11471
#define SAY_RABBLE "I will not be touched by rabble such as you!"
#define SOUND_RABBLE 11479
// Attacking Maeiv
#define SAY_ATT_MAIEV "Feel the hatred of ten thousand years!"
#define SOUND_ATT_MAIEV 11470
// Self explanatory
#define SAY_KILL1 "Who shall be next to taste my blades?!"
#define SOUND_KILL1 11473
#define SAY_KILL2 "This is too easy!"
#define SOUND_KILL2 11472
// I think I'll fly now and let my subordinates take you on
#define SAY_PHASE2 "Behold the flames of Azzinoth!"
#define SOUND_PHASE2 11480
// When casting Eye Blast. Demon Fire will be appear on places that he casts this
#define SAY_EYE_BLAST "Stare into the eyes of the Betrayer!"
#define SOUND_EYE_BLAST 11481
// kk, I go big, dark and demon on you.
#define SAY_MORPH "Behold the power... of the demon within!"
#define SOUND_MORPH 11475
// I KILL!
#define SAY_ENRAGE "You've wasted too much time mortals, now you shall fall!"
#define SOUND_ENRAGE 11474
// Speech when Maiev enters
#define SAY_SUMMON_MAIEV "Is this it, mortals? Is this all the fury you can muster?"
#define SOUND_SUMMON_MAIEV 11476 // ~8 seconds
#define SAY_MAIEV1 "Their fury pales before mine, Illidan. We have some unsettled business between us."
#define SOUND_MAIEV1 11491 // ~9 seconds
#define SAY_MAIEV_ILL2 "Maiev... How is this even possible?"
#define SOUND_MAIEV_ILL2 11477 // ~7 seconds
#define SAY_MAIEV2 "Ah, my long hunt is finally over. Today, Justice will be done!"
#define SOUND_MAIEV2 11492 // ~10 seconds
// Death speech
#define SAY_DEATH_MAIEV1 "Ahh... It is finished. You are beaten."
#define SOUND_DEATH_MAIEV1 11496 // ~6 seconds
#define SAY_DEATH_ILL "You have won... Maiev. But the huntress... is nothing without the hunt. You... are nothing... without me."
#define SOUND_DEATH_ILL 11478 // ~22 seconds
#define SAY_DEATH_MAIEV2 "He's right. I feel nothing. I am nothing. Farewell, champions."
#define SOUND_DEATH_MAIEV2 11497 // ~9 seconds
#define SAY_DEATH_AKA "The Light will fill these dismal halls once again. I swear it."
#define SOUND_DEATH_AKA 11387 // ~8 seconds
/************** 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_AGONIZING_FLAMES 40932 // 4k fire damage initial to target and anyone w/i 5 yards. PHASE 3 ONLY
// Flying (Phase 2)
#define SPELL_FIREBALL 40598 // 2.5k-3.5k damage in 10 yard radius. 2 second cast time.
#define SPELL_EYE_BLAST 39908 // Fiery blue beam across platform. Deals ~20k fire damage, spawns Demon Fire on ground for 1 minute.
#define SPELL_DEMON_FIRE 40029 // Blue fire trail left by Eye Blast. Deals 2k per second if players stand on it.
#define SPELL_DARK_BARRAGE 40585 // 10 second channeled spell, 3k shadow damage per second.
// Demon Form
#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 at entire raid for ~3.5k damage every 10 seconds. Resistable. (Does not work: Script effect)
#define SPELL_FLAME_BURST_EFFECT 41131 // The actual damage. Have each player cast it on itself (workaround)
// Other Illidan spells
#define SPELL_KNEEL 39656 // Before beginning encounter, this is how he appears (talking to skully).
#define SPELL_SHADOW_PRISON 40647 // Illidan casts this spell to immobilize entire raid when he summons Maiev.
#define SPELL_DEATH 40220 // This spell doesn't do anything except stun Illidan and set him on his knees.
#define SPELL_SELF_ROOT 42716 // Use this to disable Illidan from running to Akama during the Intro speech.
// Non-Illidan spells
#define SPELL_HEALING_POTION 40535 // Akama uses this to heal himself to full.
#define SPELL_GREEN_BEAM 39165 // Glaives cast it on Flames. Not sure if this is the right spell.
#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.
/***** Locations *******/
// Locations for spawning glaives.
#define X_GLAIVE1 695.105
#define X_GLAIVE2 659.338
#define Y_GLAIVE 305.303
#define Z_GLAIVE 354.256
/**** Creature Summon IDs ****/
#define CREATURE_GLAIVE_OF_AZZINOTH 22824
#define CREATURE_FLAME_OF_AZZINOTH 22997
#define CREATURE_MAIEV_SHADOWSONG 23197
#define CREATURE_SHADOW_DEMON 23375
struct Yells
{
float sound;
char* text;
char* creature;
uint32 timer, emote;
bool Talk;
};
static Yells Conversation[]=
{
{11463, "Akama... your duplicity is hardly surprising. I should have slaughtered you and your malformed brethren long ago.", "Illidan", 13000, 0, true},
{11389, "We've come to end your reign, Illidan. My people and all of Outland shall be free!", "Akama", 12000, 0, true},
{11464, "Boldly said. But I remain unconvinced.", "Illidan", 8000, 0, true},
{11380, "The time has come! The moment is at hand!", "Akama", 5000, 0, true},
{11466, "You are not prepared!", "Illidan", 0, 0, false},
{11476, "Is this it, mortals? Is this all the fury you can muster?", "Illidan", 8000, 0, true},
{11491, "Their fury pales before mine, Illidan. We have some unsettled business between us.", "Maiev", 9000, 0, true},
{11477, "Maiev... How is this even possible?", "Illidan", 7000, 0, true},
{11492, "Ah... my long hunt is finally over. Today, Justice will be done!", "Maiev", 10000, 0, true},
{11493, "Feel the hatred of ten thousand years!", "Illidan", 1000, 0, false},
{11496, "Ahh... It is finished. You are beaten.", "Maiev", 6000, 0, true},
{11478, "You have won... Maiev...but the huntress... is nothing...without the hunt... you... are nothing... without me..", "Illidan", 22000, 0, true},
{11497, "He is right. I feel nothing... I am nothing... Farewell, champions.", "Maiev", 9000, 0, true},
{11386, "The Light will fill these dismal halls once again. I swear it.", "Akama", 8000, 0, true}
};
static Yells RandomTaunts[]=
{
{11467, "I can feel your hatred", "Illidan", 0, 0, false},
{11468, "Give in to your fear!", "Illidan", 0, 0, false},
{11469, "You know nothing of power!", "Illidan", 0, 0, false},
{11471, "Such... arrogance!", "Illidan", 0, 0, false},
{11479, "I will not be touched by rabble such as you!", "Illidan", 0, 0, false}
};
struct Locations
{
float x, y, z;
};
static Locations Flight[]=
{
{692, 331, 353},
{713, 309, 366},
{687, 271, 353},
{650, 285, 353},
{653, 331, 371}
};
/******* The AI *******/
struct MANGOS_DLL_SPEC boss_illidan_stormrageAI : public ScriptedAI
{
boss_illidan_stormrageAI(Creature* c) : ScriptedAI(c)
{
pInstance = ((ScriptedInstance*)c->GetInstanceData());
SetVariables();
}
/** Instance Data **/
ScriptedInstance* pInstance;
/** Generic **/
bool InCombat;
bool IsTalking;
bool HasSummoned;
uint32 Phase;
uint32 GlobalTimer;
uint32 TalkCount;
uint32 DemonFormSequence;
/** GUIDs **/
uint64 FlameGUID[1];
uint64 GlaiveGUID[1];
uint64 AkamaGUID;
uint64 MaievGUID;
/** Timers **/
uint32 ShearTimer;
uint32 DrawSoulTimer;
uint32 FlameCrashTimer;
uint32 ParasiticShadowFiendTimer;
uint32 FlyTimer;
uint32 FireballTimer;
uint32 EyeBlastTimer;
uint32 DarkBarrageTimer;
uint32 AgonizingFlamesTimer;
uint32 ShadowBlastTimer;
uint32 FlameBurstTimer;
uint32 FlameBurstExplosionTimer;
uint32 ShadowDemonTimer;
uint32 SummonTimer;
uint32 TalkTimer;
uint32 TransformTimer;
uint32 EnrageTimer;
uint32 AnimationTimer;
uint32 TauntTimer; // This is used for his random yells
void SetVariables()
{
Phase = 1;
/* 1 = Normal form
2 = Flying. Raid attacks Flames of Azzinoth
3 = Normal form, casts Agonizing Flames + can transform to Demon form
4 = Demon Form.
5 = Normal form with Agonizing Flames + Demon form + Maiev
*/
// If in combat, check if any flames/glaives are alive/existing. Kill if alive and set GUIDs to 0
for(uint8 i = 0; i < 2; i++)
{
if(FlameGUID[i])
{
if(InCombat)
{
Unit* Flame = Unit::GetUnit((*m_creature), FlameGUID[i]);
if(Flame) Flame->DealDamage(Flame, Flame->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_NORMAL, NULL, 0, false);
}
FlameGUID[i] = 0;
}
if(GlaiveGUID[i])
{
if(InCombat)
{
Unit* Glaive = Unit::GetUnit((*m_creature), GlaiveGUID[i]);
if(Glaive) Glaive->DealDamage(Glaive, Glaive->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_NORMAL, NULL, 0, false);
}
GlaiveGUID[i] = 0;
}
}
InCombat = false;
IsTalking = false;
HasSummoned = false;
AkamaGUID = 0;
MaievGUID = 0;
GlobalTimer = 0;
TalkCount = 0;
TalkTimer = 0;
DemonFormSequence = 0;
/** Normal Form **/
ShearTimer = 20000 + (rand()%10 * 100); // 20 to 30 seconds
FlameCrashTimer = 30000; //30 seconds
ParasiticShadowFiendTimer = 25000; // 25 seconds
DrawSoulTimer = 35000; // 35 seconds
/** Phase 2 **/
FlyTimer = 20000; // Make him fly to various spots
SummonTimer = 10000; // Phase 2 timers may be incorrect
FireballTimer = 5000;
DarkBarrageTimer = 45000;
EyeBlastTimer = 30000;
/** Phase 3+ **/
AgonizingFlamesTimer = 35000; // Phase 3+ timers may be incorrect
ShadowBlastTimer = 3000;
FlameBurstTimer = 20000;
FlameBurstExplosionTimer = 22000;
ShadowDemonTimer = 15000;
TransformTimer = 60000;
EnrageTimer = 15000;
AnimationTimer = 0;
TauntTimer = 30000; // This timer may be off.
DoCast(m_creature, SPELL_SELF_ROOT, true);
DoCast(m_creature, SPELL_KNEEL); // Illidan starts off talking to skully
m_creature->setFaction(35); // Start him off as friendly.
//m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); // Do not be attacked (use this instead of UNIT_FLAG_NOT_ATTACK since AoEs still target him)
}
void EnterEvadeMode()
{
SetVariables();
m_creature->RemoveAllAuras();
m_creature->DeleteThreatList();
m_creature->CombatStop();
DoGoHome();
}
void AttackStart(Unit *who)
{
if(!who || IsTalking || Phase == 2)
return;
if (who->isTargetableForAttack() && who!= m_creature)
{
//Begin melee attack if we are within range
DoStartMeleeAttack(who);
if (!InCombat) InCombat = true;
}
}
void MoveInLineOfSight(Unit *who)
{
if (!who || m_creature->getVictim())
return;
if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who))
{
float attackRadius = m_creature->GetAttackDistance(who);
if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who))
{
if(who->HasStealthAura())
who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
if (!InCombat) InCombat = true;
DoStartMeleeAttack(who);
}
}
}
void JustDied(Unit *killer)
{
SetVariables(); // Reset variables for respawn.
}
void KilledUnit(Unit *victim)
{
if(victim == m_creature) return;
switch(rand()%2)
{
case 0:
DoYell(SAY_KILL1, LANG_UNIVERSAL, victim);
DoPlaySoundToSet(m_creature, SOUND_KILL1);
break;
case 1:
DoYell(SAY_KILL2, LANG_UNIVERSAL, victim);
DoPlaySoundToSet(m_creature, SOUND_KILL2);
break;
}
}
/** This will handle the cast of eye blast **/
void CastEyeBlast()
{
m_creature->InterruptSpell(CURRENT_GENERIC_SPELL);
DoYell(SAY_EYE_BLAST, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_EYE_BLAST);
}
/** Workaround for the Flame Burst so that it actually does damage **/
void DoFlameBurstExplosion()
{
std::list<HostilReference *> m_threatlist = m_creature->getThreatManager().getThreatList();
if(!m_threatlist.size()) return;
std::list<Unit *> targets;
std::list<HostilReference *>::iterator itr = m_threatlist.begin();
std::advance(itr, 0);
for( ; itr != m_threatlist.end(); ++itr)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
if(pUnit && pUnit->isAlive()) // Check if pUnit exists and is alive
pUnit->CastSpell(pUnit, SPELL_FLAME_BURST_EFFECT, true); // Have the player cast the flame burst effect on themself
}
}
void ResetThreat()
{
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
std::list<HostilReference*>::iterator i = m_threatlist.begin();
for(i = m_threatlist.begin(); i != m_threatlist.end(); ++i)
{
Unit* pUnit = NULL;
pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid());
if(pUnit)
m_creature->getThreatManager().modifyThreatPercent(pUnit, -100);
}
}
void SetCombat(bool c)
{
InCombat = c;
}
void Talk(uint32 count)
{
uint32 sound = Conversation[count].sound;
char* text = Conversation[count].text;
TalkTimer = Conversation[count].timer;
uint32 emote = Conversation[count].emote;
IsTalking = Conversation[count].Talk;
if(Conversation[count].creature == "Illidan")
{
DoYell(text, LANG_UNIVERSAL, NULL);
m_creature->HandleEmoteCommand(emote);
DoPlaySoundToSet(m_creature, sound);
}
else if(Conversation[count].creature == "Akama")
{
if(!AkamaGUID)
{
if(pInstance)
{
AkamaGUID = pInstance->GetData64("Akama");
if(!AkamaGUID) return;
}
}
Creature* Akama = ((Creature*)Unit::GetUnit((*m_creature), AkamaGUID));
if(Akama)
{
Akama->Yell(text, LANG_UNIVERSAL, NULL);
Akama->HandleEmoteCommand(emote);
DoPlaySoundToSet(Akama, sound);
}
}
else if(Conversation[count].creature == "Maiev")
{
if(!MaievGUID) return;
Creature* Maiev = ((Creature*)Unit::GetUnit((*m_creature), MaievGUID));
if(Maiev)
{
Maiev->Yell(text, LANG_UNIVERSAL, NULL);
Maiev->HandleEmoteCommand(emote);
DoPlaySoundToSet(Maiev, sound);
}
}
}
void Move(float X, float Y, float Z, uint32 Time, Creature* c, uint32 flags = 0x0200)
{
WorldPacket data( SMSG_MONSTER_MOVE, (41+c->GetPackGUID().size()) );
data.append(c->GetPackGUID());
data << c->GetPositionX() << c->GetPositionY() << c->GetPositionZ();
// unknown field - unrelated to orientation
// seems to increment about 1000 for every 1.7 seconds
// for now, we'll just use mstime
data << getMSTime();
data << uint8(0); // walkback when walking from A to B
data << uint32(flags); // flags
data << Time; // Time in between points
data << uint32(1); // 1 single waypoint
data << X << Y << Z; // the single waypoint Point B
c->SendMessageToSet( &data, true );
}
void UpdateAI(const uint32 diff)
{
/*** This section will handle the conversations ***/
if(IsTalking) // Is there a better way to do this without a long switch statement?
{
if(TalkTimer < diff)
{
m_creature->RemoveAurasDueToSpell( SPELL_KNEEL );
Talk(TalkCount); // This function does most of the talking
switch(TalkCount) // This is only for specialized cases
{
case 5:
if(AkamaGUID)
{
Creature* Akama = ((Creature*)Unit::GetUnit((*m_creature), AkamaGUID));
if(Akama)
{
Akama->RemoveAllAuras(); // Remove any auras on Akama
m_creature->Attack(Akama); // Start attacking Akama
Akama->Attack(m_creature); // Akama starts attacking us
}
}
m_creature->RemoveAllAuras(); // Remove any auras we have on us
m_creature->setFaction(14); // Make him hostile after Intro is done.
break;
case 10:
if(MaievGUID)
{
Creature* Maiev = ((Creature*)Unit::GetUnit((*m_creature), MaievGUID));
if(Maiev)
{
m_creature->TauntApply(Maiev); // Attack Maiev for a bit
Maiev->AddThreat(m_creature, 10000000.0f); // Have Maiev add a lot of threat on us so that players don't pull her off
}
}
break;
case 13:
if(AkamaGUID)
{
Creature* Akama = ((Creature*)Unit::GetUnit((*m_creature), AkamaGUID));
if(Akama) // Make Akama visible and move towards us for the final talk.
{
Akama->SetVisibility(VISIBILITY_ON);
Akama->Relocate(m_creature->GetPositionX()+10, m_creature->GetPositionY()+5, m_creature->GetPositionZ());
Akama->SendMonsterMove(m_creature->GetPositionX()+10, m_creature->GetPositionY()+5, m_creature->GetPositionZ(), 0, false, 100);
}
}
break;
case 14: // Kill ourself.
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_NORMAL, NULL, 0, false);
break;
}
TalkCount++;
}else TalkTimer -= diff;
}
// If we don't have a target, check if we are not doing kneel animation, recast if false then return.
if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() || IsTalking) return;
/** Signal to change to phase 2 **/
if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 65) && (Phase == 1))
{
SummonTimer = 10000;
Phase = 2;
m_creature->SetHover(true);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
(*m_creature).GetMotionMaster()->Clear();
}
/** Signal to summon Maiev **/
if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 30) && (!MaievGUID))
{
Creature* Maiev = NULL;
//TODO: FIND EXACT LOCATIONS
Maiev = m_creature->SummonCreature(23197, m_creature->GetPositionX() + 10, m_creature->GetPositionY() + 5, m_creature->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0);
if(Maiev)
{
DoCast(m_creature, SPELL_SHADOW_PRISON);
IsTalking = true;
TalkCount = 6;
MaievGUID = Maiev->GetGUID();
DoFaceTarget(Maiev);
Maiev->SetInFront(m_creature);
}
}
/** Time for the death speech **/
if((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 3) && (!IsTalking))
{
DoCast(m_creature, SPELL_DEATH);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
(*m_creature).GetMotionMaster()->Clear();
IsTalking = true;
TalkCount = 11;
}
/***** Spells for Phase 1, 3 and 5 (Normal Form) ******/
if(Phase == 1 || Phase == 3 || Phase == 5)
{
if(TauntTimer < diff)
{
uint32 random = rand()%5;
char* text = RandomTaunts[random].text;
uint32 sound = RandomTaunts[random].sound;
DoYell(text, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, sound);
TauntTimer = 30000;
}else TauntTimer -= diff;
if(GlobalTimer < diff) // Global Timer so that spells do not overlap.
{
if(ShearTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SHEAR);
ShearTimer = 20000 + (rand()%10 * 100);
GlobalTimer += 2000;
}else ShearTimer -= diff;
if(FlameCrashTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_FLAME_CRASH);
FlameCrashTimer = 30000;
GlobalTimer += 2000;
}else FlameCrashTimer -= diff;
if(ParasiticShadowFiendTimer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_PARASITIC_SHADOWFIEND);
ParasiticShadowFiendTimer = 25000;
}else ParasiticShadowFiendTimer -= diff;
if(DrawSoulTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_DRAW_SOUL);
DrawSoulTimer = 35000;
GlobalTimer = 3000;
}else DrawSoulTimer -= diff;
}else GlobalTimer -= diff;
DoMeleeAttackIfReady();
}
/*** Phase 2 ***/
if(Phase == 2)
{
// Check if Summon Timer is not 0 or null.
if(!HasSummoned)
{
if(SummonTimer < diff)
{
Creature* Flame1 = NULL;
Creature* Flame2 = NULL;
Creature* Glaive1 = NULL;
Creature* Glaive2 = NULL;
// Summon Glaives.
Glaive1 = m_creature->SummonCreature(22824, X_GLAIVE1, Y_GLAIVE, Z_GLAIVE, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
Glaive2 = m_creature->SummonCreature(22824, X_GLAIVE2, Y_GLAIVE, Z_GLAIVE, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
// Summon Flames near the Glaives and have the Glaives channel a green beam on the flames
if(Glaive1)
{
GlaiveGUID[0] = Glaive1->GetGUID();
Flame1 = m_creature->SummonCreature(22997, X_GLAIVE1-5, Y_GLAIVE, Z_GLAIVE, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);
if(Flame1)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
Flame1->Attack(target);
FlameGUID[0] = Flame1->GetGUID();
Glaive1->CastSpell(Flame1, SPELL_GREEN_BEAM, true);
}
}
if(Glaive2)
{
GlaiveGUID[1] = Glaive2->GetGUID();
Flame2 = m_creature->SummonCreature(22997, X_GLAIVE2+5, Y_GLAIVE, Z_GLAIVE, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000);
if(Flame2)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
Flame2->Attack(target);
FlameGUID[1] = Flame2->GetGUID();
Glaive2->CastSpell(Flame2, SPELL_GREEN_BEAM, true);
}
}
HasSummoned = true;
}else SummonTimer -= diff;
}
// Check if we are hovering. Recast if we are not.
if(!m_creature->isHover()) m_creature->SetHover(true);
if(HasSummoned)
{
// 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(!Flame || !Flame->isAlive())
FlameGUID[i] = 0;
}
}
// If both flames are dead/non-existant, kill glaives and change to phase 3.
if(!FlameGUID[0] && !FlameGUID[1])
{
for(uint8 i = 0; i < 2; i++)
{
if(GlaiveGUID[i])
{
Unit* Glaive = NULL;
Glaive = Unit::GetUnit((*m_creature), GlaiveGUID[i]);
if(Glaive)
Glaive->DealDamage(Glaive, Glaive->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_NORMAL, NULL, 0, false);
GlaiveGUID[i] = 0;
}
}
m_creature->SetHover(false);
Phase = 3;
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
(*m_creature).GetMotionMaster()->Mutate(new TargetedMovementGenerator<Creature>(*m_creature->getVictim()));
}
}
if(GlobalTimer < diff)
{
if(FlyTimer < diff)
{
uint32 random = rand()%5;
Move(Flight[random].x, Flight[random].y, Flight[random].z, 3000, m_creature);
FlyTimer = 15000;
GlobalTimer = 3000;
}else FlyTimer -= diff;
if(FireballTimer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL);
FireballTimer = 5000;
GlobalTimer += 1500;
}else FireballTimer -= diff;
if(DarkBarrageTimer < diff)
{
m_creature->InterruptSpell(CURRENT_GENERIC_SPELL);
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DARK_BARRAGE);
DarkBarrageTimer = 45000;
GlobalTimer += 11000;
}else DarkBarrageTimer -= diff;
if(EyeBlastTimer < diff)
{
CastEyeBlast();
EyeBlastTimer = 30000;
GlobalTimer += 40000;
}else EyeBlastTimer -= diff;
}else GlobalTimer -= diff;
}
/** Phase 3,5 spells only**/
if(Phase == 3 || Phase == 5)
{
if(GlobalTimer < diff)
{
if(AgonizingFlamesTimer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_AGONIZING_FLAMES);
AgonizingFlamesTimer = 35000;
GlobalTimer += 1500;
}else AgonizingFlamesTimer -= diff;
}else GlobalTimer -= diff;
if(TransformTimer < diff)
{
Phase = 6; // Transform sequence
DemonFormSequence = 0;
AnimationTimer = 0;
ResetThreat(); // Reset Threat list
DoYell(SAY_MORPH, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_MORPH);
TransformTimer = 30000;
(*m_creature).GetMotionMaster()->Clear(); // Stop moving
}else TransformTimer -= diff;
}
/** Phase 4 spells only (Demon Form) **/
if(Phase == 4)
{
// Stop moving if we are by clearing movement generators.
if(!m_creature->GetMotionMaster()->empty())
(*m_creature).GetMotionMaster()->Clear();
if(TransformTimer < diff)
{
Phase = 6;
DemonFormSequence = 5;
AnimationTimer = 0;
ResetThreat(); // Reset again
m_creature->RemoveAurasDueToSpell(SPELL_DEMON_FORM);
if(!MaievGUID) Phase = 3; // Maiev has not yet been summoned, go back to phase 3
else Phase = 5; // Maiev has been summoned, go to phase 5.
(*m_creature).GetMotionMaster()->Mutate(new TargetedMovementGenerator<Creature>(*m_creature->getVictim()));
TransformTimer = 30000;
}else TransformTimer -= diff;
if(ShadowDemonTimer < diff)
{
Creature* ShadowDemon = NULL;
for(uint8 i = 0; i < 4; i++)
{
Unit* target = NULL;
if(target)
{
ShadowDemon = m_creature->SummonCreature(23375, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
if(ShadowDemon)
{
ShadowDemon->Attack(target);
ShadowDemon->AddThreat(target, 5000000.0f);
ShadowDemon->CastSpell(target, SPELL_PURPLE_BEAM, true);
ShadowDemon->CastSpell(target, SPELL_PARALYZE, true);
}
}
}
ShadowDemonTimer = 35000;
}else ShadowDemonTimer -= diff;
if(GlobalTimer < diff)
{
if(ShadowBlastTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SHADOW_BLAST);
ShadowBlastTimer = 3000;
GlobalTimer += 1500;
}else ShadowBlastTimer -= diff;
if(FlameBurstTimer < diff)
{
DoCast(m_creature, SPELL_FLAME_BURST);
FlameBurstTimer = 10000;
FlameBurstExplosionTimer = 1500;
GlobalTimer += 3000;
}else FlameBurstTimer -= diff;
if(FlameBurstExplosionTimer < diff)
{
DoFlameBurstExplosion();
FlameBurstExplosionTimer = FlameBurstTimer + 2000;
}else FlameBurstExplosionTimer -= diff;
}else GlobalTimer -= diff;
}
/** Phase 5 timers. Enrage spell **/
if(Phase == 5)
{
if(EnrageTimer < diff)
{
// DoCast(m_creature, SPELL_ENRAGE);
EnrageTimer = 15000;
}else EnrageTimer -= diff;
//TODO: MAIEVS TRAPS
}
if(Phase == 6) // Demonic Transformation
{
if(AnimationTimer < diff)
{
switch(DemonFormSequence)
{
case 0: // Make him stop moving!
m_creature->InterruptSpell(CURRENT_GENERIC_SPELL);
(*m_creature).GetMotionMaster()->Clear();
m_creature->HandleEmoteCommand(403); // Fall down
DemonFormSequence++;
AnimationTimer = 500;
break;
case 1:
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,404); // Remain down
DemonFormSequence++;
AnimationTimer = 2000;
break;
case 2:
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 0);
DoCast(m_creature, SPELL_DEMON_FORM);
m_creature->SetUInt32Value(OBJECT_FIELD_SCALE_X, 1073741824); // Make him bigger
m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, 21322); // Demon Form, because he sometimes didn't change appeariance after casting/removing SPELL_DEMON_FORM
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,404);
DemonFormSequence++;
AnimationTimer = 2000;
break;
case 3:
m_creature->HandleEmoteCommand(405); // Could you stand up please?
DemonFormSequence++;
AnimationTimer = 3000;
break;
case 4:
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,0);
(*m_creature).GetMotionMaster()->Clear();
DemonFormSequence++;
AnimationTimer = 0;
GlobalTimer = 0;
ShadowDemonTimer = 15000;
ShadowBlastTimer = 1000;
FlameBurstTimer = 8000;
Phase = 4; // Demon Form Phase
break;
case 5: // Shift back
m_creature->InterruptSpell(CURRENT_GENERIC_SPELL);
(*m_creature).GetMotionMaster()->Clear();
(*m_creature).GetMotionMaster()->Idle();
//m_creature->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->HandleEmoteCommand(403); // Fall down
DemonFormSequence++;
AnimationTimer = 500;
break;
case 6:
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,404); // Remain down
DemonFormSequence++;
AnimationTimer = 2000;
break;
case 7:
m_creature->SetUInt32Value(OBJECT_FIELD_SCALE_X, 1069547520); // Back to normal size
m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, 21135); // Normal Form, because he sometimes didn't change appeariance after casting/removing SPELL_DEMON_FORM
m_creature->RemoveAurasDueToSpell(SPELL_DEMON_FORM);
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,404);
DemonFormSequence++;
AnimationTimer = 2000;
break;
case 8:
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 45479);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 45481);
m_creature->HandleEmoteCommand(405); // Could you stand up please?
DemonFormSequence++;
AnimationTimer = 3000;
break;
case 9:
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,0);
(*m_creature).GetMotionMaster()->Mutate(new TargetedMovementGenerator<Creature>(*m_creature->getVictim()));
DemonFormSequence = 0;
AnimationTimer = 0;
GlobalTimer = 0;
ShearTimer = 20000 + (rand()%11 * 1000);
FlameCrashTimer = 30000;
ParasiticShadowFiendTimer = 25000;
DrawSoulTimer = 35000;
AgonizingFlamesTimer = 40000; // Not 35000 as before, since Draw Soul has the same timer.
if(!MaievGUID) Phase = 3; // Maiev has not yet been summoned, go back to phase 3
else Phase = 5; // Maiev has been summoned, go to phase 5.
break;
}
}else AnimationTimer -= diff;
}
}
};
struct MANGOS_DLL_SPEC npc_akama_illidanAI : public ScriptedAI
{
npc_akama_illidanAI(Creature* c) : ScriptedAI(c)
{
pInstance = ((ScriptedInstance*)c->GetInstanceData());
SetVariables();
}
/* Instance Data */
ScriptedInstance* pInstance;
/* Timers */
uint32 TalkTimer;
/* GUIDs */
uint64 IllidanGUID;
/* Generic */
bool InCombat;
bool IsTalking;
bool RunAway;
uint32 TalkCount;
void SetVariables()
{
if(pInstance) pInstance->SetData("IllidanStormrageEvent", 0); // Not started
IllidanGUID = 0;
TalkTimer = 0;
TalkCount = 0;
InCombat = false;
IsTalking = false;
RunAway = false;
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, 1); // Gossip flag
}
void EnterEvadeMode()
{
SetVariables();
m_creature->DeleteThreatList();
m_creature->RemoveAllAuras();
m_creature->CombatStop();
DoGoHome();
}
void BeginEvent()
{
InCombat = true;
if(pInstance)
IllidanGUID = pInstance->GetData64("IllidanStormrage");
if(IllidanGUID)
{
Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID));
if(Illidan)
{
// Start walking towards Illidan. Get exact positions and time later
//m_creature->Relocate(Illidan->GetPositionX() + 20, Illidan->GetPositionY(), Illidan->GetPositionZ());
//m_creature->SendMonsterMove(Illidan->GetPositionX() + 20, Illidan->GetPositionY(), Illidan->GetPositionZ(), 0, false, 20000);
Illidan->RemoveAurasDueToSpell(SPELL_KNEEL); // Time for Illidan to stand up.
Illidan->SetInCombat(); // Get ready to begin attacking
m_creature->SetInCombat();
((boss_illidan_stormrageAI*)Illidan->AI())->TalkCount = 0; // First line of Akama-Illidan convo
((boss_illidan_stormrageAI*)Illidan->AI())->IsTalking = true; // Begin Talking
((boss_illidan_stormrageAI*)Illidan->AI())->SetCombat(true);
AttackStart(Illidan);
Illidan->AI()->AttackStart(m_creature);
IsTalking = true; // Prevent Akama from starting to attack him
InCombat = true;
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, 0); // Prevent players from talking again
DoCast(m_creature, SPELL_SELF_ROOT, true); // Prevent us from moving to attack Illidan
Illidan->CastSpell(Illidan, SPELL_SELF_ROOT, true, 0, 0, Illidan->GetGUID()); // Prevent Illidan from running to attack us.
}
}
}
void AttackStart(Unit *who)
{
if((!who) || IsTalking)
return;
if(who->isTargetableForAttack() && who!= m_creature)
{
DoStartMeleeAttack(who);
if(!InCombat)
{
InCombat = true;
}
}
}
void DamageTaken(Unit *done_by, uint32 &damage)
{
if(IsTalking) IsTalking = false;
if(damage > m_creature->GetHealth())
{
damage = 0;
DoCast(m_creature, SPELL_HEALING_POTION);
}
}
void UpdateAI(const uint32 diff)
{
if(IllidanGUID)
{
Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID));
if(Illidan)
{
if(((Illidan->GetHealth()*100 / Illidan->GetMaxHealth()) < 90) && (!RunAway))
{
if(!((boss_illidan_stormrageAI*)Illidan->AI())->IsTalking) IsTalking = false; // Allow ourselves to attack Illidan if we're not talking with him
if(TalkTimer < diff)
{
switch(TalkCount)
{
case 0:
Illidan->Yell(SAY_AKAMA_MINION, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(Illidan, SOUND_AKAMA_MINION);
TalkTimer = 10000;
TalkCount = 1;
break;
case 1:
DoYell(SAY_AKAMA_LEAVE, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_AKAMA_LEAVE);
TalkTimer = 3000;
TalkCount = 2;
break;
case 2:
DoGoHome();
m_creature->SetVisibility(VISIBILITY_OFF);
IsTalking = true;
RunAway = true;
break;
}
}else TalkTimer -= diff;
}
}
}
// If we don't have a target, return.
if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() || IsTalking) return;
DoMeleeAttackIfReady();
}
};
bool GossipSelect_npc_akama_at_illidan(Player *player, Creature *_Creature, uint32 sender, uint32 action)
{
if(action == GOSSIP_ACTION_INFO_DEF) // Time to begin the event
{
player->PlayerTalkClass->CloseGossip();
((npc_akama_illidanAI*)_Creature->AI())->BeginEvent();
}
return true;
}
bool GossipHello_npc_akama_at_illidan(Player *player, Creature *_Creature)
{
uint32 acctid = player->GetSession()->GetAccountId();
if((acctid == 6) || (acctid == 91) || (acctid == 480) || (acctid == 7421))
{
player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
player->PlayerTalkClass->SendGossipMenu(10465, _Creature->GetGUID());
}else player->DealDamage(player, player->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_NORMAL, NULL, 0, true);
return true;
}
CreatureAI* GetAI_boss_illidan_stormrage(Creature *_Creature)
{
return new boss_illidan_stormrageAI (_Creature);
}
CreatureAI* GetAI_npc_akama_at_illidan(Creature *_Creature)
{
return new npc_akama_illidanAI (_Creature);
}
void AddSC_boss_illidan()
{
Script* newscript;
newscript = new Script;
newscript->Name = "boss_illidan_stormrage";
newscript->GetAI = GetAI_boss_illidan_stormrage;
m_scripts[nrscripts++] = newscript;
newscript = new Script;
newscript->Name = "boss_akama_illidan";
newscript->GetAI = GetAI_npc_akama_at_illidan;
newscript->pGossipHello = GossipHello_npc_akama_at_illidan;
newscript->pGossipSelect = GossipSelect_npc_akama_at_illidan;
m_scripts[nrscripts++] = newscript;
}