/* Copyright (C) 2006,2007 ScriptDev2
* 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)
// 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;
bool Talk;
};
static Yells Intro[]=
{
{11463, "Akama... your duplicity is hardly surprising. I should have slaughtered you and your malformed brethren long ago.", "Illidan", 13000, true},
{11389, "We've come to end your reign, Illidan. My people and all of Outland shall be free!", "Akama", 12000, true},
{11464, "Boldly said. But I remain unconvinced.", "Illidan", 8000, true},
{11380, "The time has come! The moment is at hand!", "Akama", 5000, true},
{11466, "You are not prepared!", "Illidan", 0, false}
};
static Yells SummonMaiev[]=
{
{11476, "Is this it, mortals? Is this all the fury you can muster?", "Illidan", 8000, true},
{11491, "Their fury pales before mine, Illidan. We have some unsettled business between us.", "Maiev", 9000, true},
{11477, "Maiev... How is this even possible?", "Illidan", 7000, true},
{11492, "Ah... my long hunt is finally over. Today, Justice will be done!", "Maiev", 10000, false}
};
static Yells Death[]=
{
{11496, "Ahh... It is finished. You are beaten.", "Maiev", 6000, true},
{11478, "You have won... Maiev...but the huntress... is nothing...without the hunt... you... are nothing... without me..", "Illidan", 22000, true},
{11497, "He is right. I feel nothing... I am nothing... Farewell, champions.", "Maiev", 9000, true},
{11386, "The Light will fill these dismal halls once again. I swear it.", "Akama", 8000, true}
};
static Yells RandomTaunts[]=
{
{11467, "I can feel your hatred", "Illidan"},
{11468, "Give in to your fear!", "Illidan"},
{11469, "You know nothing of power!", "Illidan"},
{11471, "Such... arrogance!", "Illidan"},
{11479, "I will not be touched by rabble such as you!", "Illidan"}
};
struct Locations
{
float x, y, z;
};
static Locations Coords[]=
{
{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 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 **/
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_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()
{
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()
{
}
void ResetThreat()
{
std::list& m_threatlist = m_creature->getThreatManager().getThreatList();
std::list::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 = Intro[count].sound;
char* text = Intro[count].text;
TalkTimer = Intro[count].timer;
IsTalking = Intro[count].Talk;
if(Intro[count].creature == "Illidan")
DoYell(text, LANG_UNIVERSAL, NULL);
else if(Intro[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);
}
}
else if(Intro[count].creature == "Maiev")
{
if(!MaievGUID) return;
Creature* Maiev = ((Creature*)Unit::GetUnit((*m_creature), MaievGUID));
if(Maiev)
{
Maiev->Yell(text, LANG_UNIVERSAL, NULL);
}
}
DoPlaySoundToSet(m_creature, sound);
}
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);
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(*m_creature->getVictim()));
}
}
if(GlobalTimer < diff)
{
if(FireballTimer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL);
FireballTimer = 5000;
GlobalTimer += 1500;
}else FireballTimer -= diff;
if(DarkBarrageTimer < diff)
{
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(*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 = 2000;
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(*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
}
}
}
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;
}