/* 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) #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 m_threatlist = m_creature->getThreatManager().getThreatList(); if(!m_threatlist.size()) return; std::list targets; std::list::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& 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 = 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(*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(*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(*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; }