diff options
author | Keader <keader.android@gmail.com> | 2017-02-05 12:45:29 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-05 12:45:29 -0300 |
commit | 8731975187694d1989af93f9569b591d4ebd767b (patch) | |
tree | 34cae866e3686a9a927cea462a4ad03c9a1d817b /src | |
parent | 7c7b877c3fc0292f33f54f4e8b514be3bf8514a6 (diff) |
Core/Scripts: Illidan Stormrage Rewrite (#18963)
Closes #5018
Diffstat (limited to 'src')
3 files changed, 2101 insertions, 1729 deletions
diff --git a/src/server/scripts/Outland/BlackTemple/black_temple.h b/src/server/scripts/Outland/BlackTemple/black_temple.h index cd3ee42e3b8..8a2cb69d603 100644 --- a/src/server/scripts/Outland/BlackTemple/black_temple.h +++ b/src/server/scripts/Outland/BlackTemple/black_temple.h @@ -38,22 +38,23 @@ enum DataTypes // Additional Data DATA_AKAMA_SHADE = 9, - DATA_AKAMA = 10, - DATA_GATHIOS_THE_SHATTERER = 11, - DATA_HIGH_NETHERMANCER_ZEREVOR = 12, - DATA_LADY_MALANDE = 13, - DATA_VERAS_DARKSHADOW = 14, - DATA_BLOOD_ELF_COUNCIL_VOICE = 15, + DATA_AKAMA = 10, + DATA_MAIEV = 11, + DATA_GO_ILLIDAN_GATE = 12, + DATA_BLACK_TEMPLE_TRIGGER = 13, - DATA_GO_ILLIDAN_GATE = 16, + DATA_GATHIOS_THE_SHATTERER = 14, + DATA_HIGH_NETHERMANCER_ZEREVOR = 15, + DATA_LADY_MALANDE = 16, + DATA_VERAS_DARKSHADOW = 17, + DATA_BLOOD_ELF_COUNCIL_VOICE = 18, - DATA_BLACK_TEMPLE_TRIGGER = 17, - DATA_GO_DEN_OF_MORTAL_DOOR = 18, + DATA_GO_DEN_OF_MORTAL_DOOR = 19, - DATA_ESSENCE_OF_SUFFERING = 19, - DATA_ESSENCE_OF_DESIRE = 20, - DATA_ESSENCE_OF_ANGER = 21 + DATA_ESSENCE_OF_SUFFERING = 20, + DATA_ESSENCE_OF_DESIRE = 21, + DATA_ESSENCE_OF_ANGER = 22 }; enum TriggerEmotes @@ -93,7 +94,17 @@ enum CreatureIds NPC_ASHTONGUE_PRIMALIST = 22847, NPC_ASHTONGUE_STORMCALLER = 22846, NPC_ASHTONGUE_FERAL_SPIRIT = 22849, - NPC_STORM_FURY = 22848 + NPC_STORM_FURY = 22848, + NPC_SPIRIT_OF_UDALO = 23410, + NPC_SPIRIT_OF_OLUM = 23411, + NPC_FLAME_OF_AZZINOTH = 22997, + NPC_BLADE_OF_AZZINOTH = 22996, + NPC_MAIEV_SHADOWSONG = 23197, + NPC_ILLIDAN_DB_TARGET = 23070, + NPC_ILLIDARI_ELITE = 23226, + NPC_GLAIVE_TARGET = 23448, + NPC_GLAIVE_WORLD_TRIGGER = 22515, + NPC_DEMON_FIRE = 23069 }; enum GameObjectIds @@ -111,13 +122,18 @@ enum GameObjectIds GO_COUNCIL_DOOR_2 = 186152, GO_ILLIDAN_GATE = 185905, GO_ILLIDAN_DOOR_R = 186261, - GO_ILLIDAN_DOOR_L = 186262 + GO_ILLIDAN_DOOR_L = 186262, + GO_ILLIDAN_CAGE_TRAP = 185916 }; -enum BlackTempleFactions +enum BlackTempleMisc { ASHTONGUE_FACTION_FRIEND = 1820, - AKAMA_FACTION_COMBAT = 1868 + AKAMA_FACTION_COMBAT = 1868, + AKAMA_INTRO = 1, + AKAMA_FIGHT = 2, + ACTION_ACTIVE_AKAMA_INTRO = 3, + ACTION_OPEN_DOOR = 4 }; template<class AI> diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index 71d0e12911d..39fc20eea28 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,13 +15,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: boss_illidan_stormrage -SD%Complete: 90 -SDComment: Somewhat of a workaround for Parasitic Shadowfiend, unable to summon GOs for Cage Trap. -SDCategory: Black Temple -EndScriptData */ - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" @@ -30,17 +22,12 @@ EndScriptData */ #include "black_temple.h" #include "Player.h" #include "SpellInfo.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" +#include "GridNotifiersImpl.h" -// Other defines -#define CENTER_X 676.740f -#define CENTER_Y 305.297f -#define CENTER_Z 353.192f - -enum Say +enum IllidanSay { - // Akama - SAY_AKAMA_BEWARE = 0, - SAY_AKAMA_LEAVE = 1, // Illidan SAY_ILLIDAN_MINION = 0, SAY_ILLIDAN_KILL = 1, @@ -50,424 +37,404 @@ enum Say SAY_ILLIDAN_MORPH = 5, SAY_ILLIDAN_ENRAGE = 6, SAY_ILLIDAN_TAUNT = 7, + SAY_ILLIDAN_DUPLICITY = 8, + SAY_ILLIDAN_UNCONVINCED = 9, + SAY_ILLIDAN_PREPARED = 10, + SAY_ILLIDAN_SHADOW_PRISON = 11, + SAY_ILLIDAN_CONFRONT_MAIEV = 12, + SAY_ILLIDAN_FRENZY = 13, + SAY_ILLIDAN_DEFEATED = 14, + // Maiev Shadowsong SAY_MAIEV_SHADOWSONG_TAUNT = 0, + SAY_MAIEV_SHADOWSONG_APPEAR = 1, + SAY_MAIEV_SHADOWSONG_JUSTICE = 2, + SAY_MAIEV_SHADOWSONG_TRAP = 3, + SAY_MAIEV_SHADOWSONG_DOWN = 4, + SAY_MAIEV_SHADOWSONG_FINISHED = 5, + SAY_MAIEV_SHADOWSONG_OUTRO = 6, + SAY_MAIEV_SHADOWSONG_FAREWELL = 7, + // Flame of Azzinoth - EMOTE_AZZINOTH_GAZE = 0 -}; + EMOTE_AZZINOTH_GAZE = 0, -enum Spells -// Normal Form -{ - SPELL_SHEAR = 37335, // 41032 is bugged, cannot be block/dodge/parry// Reduces Max. Health by 60% for 7 seconds. Can stack 19 times. 1.5f second cast - SPELL_FLAME_CRASH = 40832, - SPELL_DRAW_SOUL = 40904, - SPELL_PARASITIC_SHADOWFIEND = 41917, - SPELL_PARASITIC_SHADOWFIEND2 = 41914, - SPELL_SUMMON_PARASITICS = 41915, - SPELL_AGONIZING_FLAMES = 40932, - SPELL_ENRAGE = 40683, -// Flying (Phase 2) - SPELL_THROW_GLAIVE = 39635, - SPELL_THROW_GLAIVE2 = 39849, // Animation for the spell above - SPELL_GLAIVE_RETURNS = 39873, - SPELL_FIREBALL = 40598, - SPELL_DARK_BARRAGE = 40585, -// Demon Form - SPELL_DEMON_TRANSFORM_1 = 40511, - SPELL_DEMON_TRANSFORM_2 = 40398, - SPELL_DEMON_TRANSFORM_3 = 40510, - SPELL_DEMON_FORM = 40506, - SPELL_SHADOW_BLAST = 41078, - SPELL_FLAME_BURST = 41126, - SPELL_FLAME_BURST_EFFECT = 41131, // The actual damage. Have each player cast it on itself (workaround) -// Other Illidan spells - SPELL_KNEEL = 39656, // Before beginning encounter, this is how he appears (talking to skully). - SPELL_SHADOW_PRISON = 40647, - SPELL_DEATH = 41220, - SPELL_BERSERK = 45078, - SPELL_DUAL_WIELD = 42459, - SPELL_SUMMON_MAIEV = 40403, -// Phase Normal spells - SPELL_FLAME_CRASH_EFFECT = 40836, - SPELL_SUMMON_SHADOWDEMON = 41117, - SPELL_SHADOWFIEND_PASSIVE = 41913, - SPELL_SHADOW_DEMON_PASSIVE = 41079, - SPELL_CONSUME_SOUL = 41080, - SPELL_PARALYZE = 41083, - SPELL_PURPLE_BEAM = 39123, -// Phase Flight spells - SPELL_AZZINOTH_CHANNEL = 39857, // Glaives cast it on Flames. Not sure if this is the right spell. - SPELL_EYE_BLAST_TRIGGER = 40017, - SPELL_EYE_BLAST = 39908, - SPELL_BLAZE_EFFECT = 40610, - SPELL_BLAZE_SUMMON = 40637, - SPELL_DEMON_FIRE = 40029, - SPELL_FLAME_BLAST = 40631, - SPELL_CHARGE = 41581, - SPELL_FLAME_ENRAGE = 45078, -// Akama spells - SPELL_AKAMA_DOOR_CHANNEL = 41268, - SPELL_DEATHSWORN_DOOR_CHANNEL = 41269, - SPELL_AKAMA_DOOR_FAIL = 41271, // Not sure where this is really used... - SPELL_HEALING_POTION = 40535, - SPELL_CHAIN_LIGHTNING = 40536, -// Maiev spells - SPELL_CAGE_TRAP_DUMMY = 40761, // Put this in DB for cage trap GO. - SPELL_CAGED = 40695, - SPELL_CAGE_TRAP_SUMMON = 40694, // Summons a Cage Trap GO (bugged) on the ground along with a Cage Trap Disturb Trigger mob (working) - SPELL_CAGE_TRAP_BEAM = 40713, - SPELL_TELEPORT_VISUAL = 41232, - SPELL_SHADOW_STRIKE = 40685, - SPELL_THROW_DAGGER = 41152, - SPELL_FAN_BLADES = 39954 // bugged visual + // Akama + SAY_AKAMA_DOOR = 0, + SAY_AKAMA_ALONE = 1, + SAY_AKAMA_SALUTE = 2, + SAY_AKAMA_BETRAYER = 3, + SAY_AKAMA_FREE = 4, + SAY_AKAMA_TIME_HAS_COME = 5, + SAY_AKAMA_MINIONS = 6, + SAY_AKAMA_LIGHT = 7, + SAY_AKAMA_FINISH = 8, + + // Spirits + SAY_SPIRIT_ALONE = 0, + + // Direct Sounds + ILLIDAN_TAKEOFF_SOUND_ID = 11479, + ILLIDAN_WARGLAIVE_SOUND_ID = 11480, + WARGLAIVE_SPAWN_SOUND_ID = 11689 }; -enum Misc +enum IllidanSpells { - FLAME_ENRAGE_DISTANCE = 30, - FLAME_CHARGE_DISTANCE = 50, + // Akama + SPELL_AKAMA_DOOR_CHANNEL = 41268, + SPELL_AKAMA_DOOR_FAIL = 41271, + SPELL_HEALING_POTION = 40535, + SPELL_CHAIN_LIGHTNING = 40536, + SPELL_AKAMA_TELEPORT = 41077, + SPELL_AKAMA_DESPAWN = 41242, - EQUIP_ID_MAIN_HAND = 32837, - EQUIP_ID_OFF_HAND = 32838, - EQUIP_ID_MAIN_HAND_MAIEV = 44850, + // Spirits + SPELL_DEATHSWORN_DOOR_CHANNEL = 41269, - MODEL_INVISIBLE = 11686, - MODEL_ILLIDAN = 21135, - MODEL_BLADE = 21431 -}; + // Door Trigger + SPELL_ARCANE_EXPLOSION = 35426, -/**** Creature Summon and Recognition IDs ****/ -enum CreatureEntry -{ - EMPTY = 0, - AKAMA = 22990, - ILLIDAN_STORMRAGE = 22917, - BLADE_OF_AZZINOTH = 22996, - FLAME_OF_AZZINOTH = 22997, - MAIEV_SHADOWSONG = 23197, - SHADOW_DEMON = 23375, - DEMON_FIRE = 23069, - FLAME_CRASH = 23336, - ILLIDAN_DOOR_TRIGGER = 23412, - SPIRIT_OF_OLUM = 23411, - SPIRIT_OF_UDALO = 23410, - ILLIDARI_ELITE = 23226, - PARASITIC_SHADOWFIEND = 23498, - CAGE_TRAP_TRIGGER = 23292 + // Blade of Azzinoth + SPELL_BIRTH = 40031, + SPELL_SUMMON_TEAR_OF_AZZINOTH = 39855, + SPELL_AZZINOTH_CHANNEL = 39857, + SPELL_GLAIVE_RETURNS = 39873, + + // Flame of Azzinoth + SPELL_FLAME_TEAR_OF_AZZINOTH = 39856, + SPELL_CHARGE = 42003, + SPELL_FLAME_BLAST = 40631, + SPELL_UNCAGED_WRATH = 39869, + + // Maiev + SPELL_TELEPORT_VISUAL = 41236, + SPELL_CAGE_TRAP_SUMMON = 40694, + SPELL_SHADOW_STRIKE = 40685, + SPELL_THROW_DAGGER = 41152, + SPELL_MAIEV_DOWN = 40409, + + // Cage Trap Disturb Trigger + SPELL_CAGE_TRAP_PERIODIC = 40761, + + // Shadow Demon + SPELL_SHADOW_DEMON_PASSIVE = 41079, + SPELL_FIND_TARGET = 41081, + SPELL_PARALYZE = 41083, + SPELL_CONSUME_SOUL = 41080, + + // Player + SPELL_SUMMON_PARASITIC_SHADOWFIENDS = 41915, + SPELL_BLAZE_SUMMON = 40637, + + // Illidan DB Target + SPELL_EYE_BLAST_TRIGGER = 40017, + + // Cage Trap Summon Spells + SPELL_SUMMON_CAGE_TRAP_1 = 40696, + SPELL_SUMMON_CAGE_TRAP_2 = 40697, + SPELL_SUMMON_CAGE_TRAP_3 = 40698, + SPELL_SUMMON_CAGE_TRAP_4 = 40699, + SPELL_SUMMON_CAGE_TRAP_5 = 40700, + SPELL_SUMMON_CAGE_TRAP_6 = 40701, + SPELL_SUMMON_CAGE_TRAP_7 = 40702, + SPELL_SUMMON_CAGE_TRAP_8 = 40703, + + // Glaive Target + SPELL_RANGE_MARKER = 41997, + SPELL_SUMMON_GLAIVE = 41466, + + // Illidan + SPELL_FLAME_CRASH = 40832, + SPELL_SHEAR = 41032, + SPELL_DRAW_SOUL = 40904, + SPELL_DRAW_SOUL_HEAL = 40903, + SPELL_PARASITIC_SHADOWFIEND = 41917, + SPELL_AGONIZING_FLAMES = 40932, + SPELL_AGONIZING_FLAMES_SELECTOR = 40834, + SPELL_FRENZY = 40683, + SPELL_THROW_GLAIVE = 39849, + SPELL_THROW_GLAIVE2 = 39635, + SPELL_FIREBALL = 40598, + SPELL_DARK_BARRAGE = 40585, + SPELL_DEMON_TRANSFORM_1 = 40511, + SPELL_DEMON_TRANSFORM_2 = 40398, + SPELL_DEMON_TRANSFORM_3 = 40510, + SPELL_DEMON_FORM = 40506, + SPELL_AURA_OF_DREAD = 41142, + SPELL_SHADOW_BLAST = 41078, + SPELL_FLAME_BURST = 41126, + SPELL_FLAME_BURST_EFFECT = 41131, + SPELL_KNEEL = 39656, + SPELL_SHADOW_PRISON = 40647, + SPELL_EMOTE_TALK_QUESTION = 41616, + SPELL_BERSERK = 45078, + SPELL_SUMMON_MAIEV = 40403, + SPELL_TELEPORT_MAIEV = 41221, + SPELL_CLEAR_ALL_DEBUFFS = 34098, + SPELL_DEATH = 41218, + SPELL_QUIET_SUICIDE = 3617, + SPELL_SUMMON_SHADOWDEMON = 41117, + SPELL_CAGED_TRAP_TELEPORT = 40693, + SPELL_CAGE_TRAP = 40760, + SPELL_CAGED_DEBUFF = 40695, + SPELL_EYE_BLAST = 39908 }; -/*** Phase Names ***/ -enum PhaseIllidan -{ - PHASE_ILLIDAN_NULL = 0, - PHASE_NORMAL = 1, - PHASE_FLIGHT = 2, - PHASE_NORMAL_2 = 3, - PHASE_DEMON = 4, - PHASE_NORMAL_MAIEV = 5, - PHASE_TALK_SEQUENCE = 6, - PHASE_FLIGHT_SEQUENCE = 7, - PHASE_TRANSFORM_SEQUENCE = 8, - PHASE_ILLIDAN_MAX = 9 -}; // Maiev uses the same phase - -enum PhaseAkama +enum IllidanMisc { - PHASE_AKAMA_NULL = 0, - PHASE_CHANNEL = 1, - PHASE_WALK = 2, - PHASE_TALK = 3, - PHASE_FIGHT_ILLIDAN = 4, - PHASE_FIGHT_MINIONS = 5, - PHASE_RETURN = 6 + GOSSIP_START_INTRO = 0, + GOSSIP_START_FIGHT = 1, + SUMMON_GROUP = 1, + DATA_AKAMA_TELEPORT_POSITION = 0, + MAX_MINIONS_NUMBER = 10, + SPELL_GLAIVE_VISUAL_KIT = 7668 }; -enum EventIllidan +enum IllidanActions { - EVENT_NULL = 0, - EVENT_BERSERK = 1, - // normal phase - EVENT_TAUNT = 2, - EVENT_SHEAR = 3, - EVENT_FLAME_CRASH = 4, - EVENT_PARASITIC_SHADOWFIEND = 5, - EVENT_PARASITE_CHECK = 6, - EVENT_DRAW_SOUL = 7, - EVENT_AGONIZING_FLAMES = 8, - EVENT_TRANSFORM_NORMAL = 9, - EVENT_ENRAGE = 10, - // flight phase - EVENT_FIREBALL = 2, - EVENT_DARK_BARRAGE = 3, - EVENT_EYE_BLAST = 4, - EVENT_MOVE_POINT = 5, - // demon phase - EVENT_SHADOW_BLAST = 2, - EVENT_FLAME_BURST = 3, - EVENT_SHADOWDEMON = 4, - EVENT_TRANSFORM_DEMON = 5, - // sequence phase - EVENT_TALK_SEQUENCE = 2, - EVENT_FLIGHT_SEQUENCE = 2, - EVENT_TRANSFORM_SEQUENCE = 2 + ACTION_START_ENCOUNTER = 5, + ACTION_FREE, + ACTION_INTRO_DONE, + ACTION_START_MINIONS, + ACTION_START_MINIONS_WEAVE, + ACTION_START_PHASE_2, + ACTION_FLAME_DEAD, + ACTION_FINALIZE_AIR_PHASE, + ACTION_START_PHASE_4, + ACTION_ILLIDAN_CAGED, + ACTION_START_OUTRO, + ACTION_MAIEV_DOWN_FADE }; -enum EventMaiev +enum IllidanPhases { - EVENT_MAIEV_NULL = 0, - EVENT_MAIEV_STEALTH = 1, - EVENT_MAIEV_TAUNT = 2, - EVENT_MAIEV_SHADOW_STRIKE = 3, - EVENT_MAIEV_THROW_DAGGER = 4, - EVENT_MAIEV_TRAP = 4 + PHASE_INTRO = 1, + PHASE_1, + PHASE_MINIONS, + PHASE_2, + PHASE_3, + PHASE_4 }; -static const EventIllidan MaxTimer[9] = +enum IllidanSplineMovement { - EVENT_NULL, - EVENT_DRAW_SOUL, - EVENT_MOVE_POINT, - EVENT_TRANSFORM_NORMAL, - EVENT_TRANSFORM_DEMON, - EVENT_ENRAGE, - EVENT_TALK_SEQUENCE, - EVENT_FLIGHT_SEQUENCE, - EVENT_TRANSFORM_SEQUENCE + SPLINE_ILLIDARI_COUNCIL = 1, + SPLINE_STAIRS = 2, + SPLINE_ILLIDAN_ROOM = 3, + SPLINE_FACE_ILLIDAN = 4, + SPLINE_TELEPORT = 5, + SPLINE_MINIONS = 6, + SPLINE_MOVE_BACK = 7 }; - -/* ################## TO DO CONVERT THIS UGLINESS TO CREATURE TEXT ################## - -SET @AKAMA := 23089; -SET @ILLIDAN := 22917; -SET @MAIEV := 23197; -DELETE FROM `creature_text` WHERE `entry`=@AKAMA AND `groupid` IN (2,3,4); -DELETE FROM `creature_text` WHERE `entry`=@ILLIDAN AND `groupid` IN (8,9,10,11,12,13,14); -DELETE FROM `creature_text` WHERE `entry`=@MAIEV AND `groupid` IN (1,2,3,4); -INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`comment`) VALUES -(@ILLIDAN,8,0, "Akama... your duplicity is hardly surprising. I should have slaughtered you and your malformed brethren long ago.",14,0,100,0,0,11463, 'Illidan SAY_XXXXXXXXXXX'), -(@AKAMA,2,0, "We've come to end your reign, Illidan. My people and all of Outland shall be free!",14,0,100,25,0,11389, 'Akama SAY_XXXXXXXXXXX'), -(@ILLIDAN,9,0, "Boldly said. But I remain unconvinced.",14,0,100,396,0,11464, 'Illidan SAY_XXXXXXXXXXX'), -(@AKAMA,3,0, "The time has come! The moment is at hand!",14,0,100,22,0,11380, 'Akama SAY_XXXXXXXXXXX'), -(@ILLIDAN,10,0, "You are not prepared!",14,0,100,406,0,11466, 'Illidan SAY_XXXXXXXXXXX'), -(@ILLIDAN,11,0, "Is this it, mortals? Is this all the fury you can muster?",14,0,100,0,0,11476, 'Illidan SAY_XXXXXXXXXXX'), -(@MAIEV,1,0, "Their fury pales before mine, Illidan. We have some unsettled business between us.",14,0,100,5,0,11491, 'Maiev Shadowsong SAY_XXXXXXXXXXX'), -(@ILLIDAN,12,0, "Maiev... How is this even possible?",14,0,100,1,0,11477, 'Illidan SAY_XXXXXXXXXXX'), -(@MAIEV,2,0, "Ah... my long hunt is finally over. Today, Justice will be done!",14,0,100,15,0,11492, 'Maiev Shadowsong SAY_XXXXXXXXXXX'), -(@ILLIDAN,13,0, "Feel the hatred of ten thousand years!",14,0,100,396,0,11470, 'Illidan SAY_XXXXXXXXXXX'), -(@MAIEV,3,0, "Ahh... It is finished. You are beaten.",14,0,100,0,0,11496, 'Maiev Shadowsong SAY_XXXXXXXXXXX'), -(@ILLIDAN,14,0, "You have won... Maiev...but the huntress... is nothing...without the hunt... you... are nothing... without me..",14,0,100,65,0,11478, 'Illidan SAY_XXXXXXXXXXX'), -(@MAIEV,4,0, "He is right. I feel nothing... I am nothing... Farewell, champions.",14,0,100,0,0,11497, 'Maiev Shadowsong SAY_XXXXXXXXXXX'), -(@AKAMA,4,0, "The Light will fill these dismal halls once again. I swear it.",14,0,100,0,0,11387, 'Akama SAY_XXXXXXXXXXX'); - -*/ - -struct Yells +enum IllidanPoints { - uint32 sound; - std::string text; - uint32 creature, timer, emote; - bool Talk; + POINT_ILLIDARI_COUNCIL = 1, + POINT_STAIRS, + POINT_ILLIDAN_ROOM, + POINT_FACE_ILLIDAN, + POINT_TELEPORT, + POINT_MINIONS, + POINT_THROW_GLAIVE, + POINT_RANDOM_PILLAR, + POINT_DB_TARGET, + POINT_ILLIDAN_MIDDLE, + POINT_MOVE_BACK, + POINT_ILLIDAN }; -static const Yells Conversation[22] = +enum IllidanEventGroup { - {11463, "Akama... your duplicity is hardly surprising. I should have slaughtered you and your malformed brethren long ago.", ILLIDAN_STORMRAGE, 8000, 0, true}, - {0, "", ILLIDAN_STORMRAGE, 5000, 396, true}, - {11389, "We've come to end your reign, Illidan. My people and all of Outland shall be free!", AKAMA, 7000, 25, true}, - {0, "", AKAMA, 5000, 66, true}, - {11464, "Boldly said. But I remain unconvinced.", ILLIDAN_STORMRAGE, 8000, 396, true}, - {11380, "The time has come! The moment is at hand!", AKAMA, 3000, 22, true}, - {0, "", AKAMA, 2000, 15, true}, - {11466, "You are not prepared!", ILLIDAN_STORMRAGE, 3000, 406, true}, - {0, "", EMPTY, 1000, 0, true}, - {0, "", EMPTY, 0, 0, false}, // 9 - {11476, "Is this it, mortals? Is this all the fury you can muster?", ILLIDAN_STORMRAGE, 8000, 0, true}, - {11491, "Their fury pales before mine, Illidan. We have some unsettled business between us.", MAIEV_SHADOWSONG, 8000, 5, true}, - {11477, "Maiev... How is this even possible?", ILLIDAN_STORMRAGE, 5000, 1, true}, - {11492, "Ah... my long hunt is finally over. Today, Justice will be done!", MAIEV_SHADOWSONG, 8000, 15, true}, - {11470, "Feel the hatred of ten thousand years!", ILLIDAN_STORMRAGE, 1000, 0, false}, // 14 - {11496, "Ahh... It is finished. You are beaten.", MAIEV_SHADOWSONG, 6000, 0, true}, // 15 - - {11478, "You have won... Maiev...but the huntress... is nothing...without the hunt... you... are nothing... without me..", ILLIDAN_STORMRAGE, 30000, 65, true}, // Emote dead for now. Kill him later - {11497, "He is right. I feel nothing... I am nothing... Farewell, champions.", MAIEV_SHADOWSONG, 9000, 0, true}, - {11498, "", MAIEV_SHADOWSONG, 5000, 0, true}, - {11498, "", EMPTY, 1000, 0, true}, // 19 Maiev disappear - {11387, "The Light will fill these dismal halls once again. I swear it.", AKAMA, 8000, 0, true}, - {0, "", EMPTY, 1000, 0, false} // 21 + GROUP_PHASE_ALL = 0, + GROUP_PHASE_1, + GROUP_PHASE_2, + GROUP_PHASE_3, + GROUP_PHASE_DEMON, + GROUP_PHASE_4 }; -G3D::Vector3 const HoverPosition[4]= +enum IllidanEvents { - {657.0f, 340.0f, 355.0f}, - {657.0f, 275.0f, 355.0f}, - {705.0f, 275.0f, 355.0f}, - {705.0f, 340.0f, 355.0f} + // Akama + EVENT_TELEPORT = 1, + EVENT_MOVE_TO_ILLIDARI_ROOM, + EVENT_AKAMA_SAY_DOOR, + EVENT_AKAMA_DOOR_FAIL, + EVENT_AKAMA_SAY_ALONE, + EVENT_SUMMON_SPIRITS, + EVENT_SPIRIT_SAY_1, + EVENT_SPIRIT_SAY_2, + EVENT_AKAMA_DOOR_SUCCESS, + EVENT_AKAMA_THANKS, + EVENT_SPIRIT_SALUTE, + EVENT_RUN_FROM_ILLIDAN_ROOM, + EVENT_START_ILLIDAN, + EVENT_FREE, + EVENT_TIME_HAS_COME, + EVENT_ROAR, + EVENT_CHANGE_ORIENTATION, + EVENT_HEALING_POTION, + EVENT_AKAMA_MINIONS, + EVENT_AKAMA_MINIONS_EMOTE, + EVENT_AKAMA_MINIONS_MOVE, + EVENT_AKAMA_MINIONS_MOVE_2, + EVENT_CHAIN_LIGHTNING, + EVENT_AKAMA_MOVE_BACK, + EVENT_AKAMA_MOVE_TO_ILLIDAN, + EVENT_AKAMA_LIGHT_TEXT, + EVENT_FINAL_SALUTE, + EVENT_AKAMA_DESPAWN, + + // Illidan Stormrage + EVENT_START_INTRO, + EVENT_UNCONVINCED, + EVENT_PREPARED, + EVENT_ENCOUNTER_START, + EVENT_EVADE_CHECK, + EVENT_FLAME_CRASH, + EVENT_DRAW_SOUL, + EVENT_SHEAR, + EVENT_BERSERK, + EVENT_PARASITIC_SHADOWFIEND, + EVENT_MINIONS_WEAVE, + EVENT_MOVE_TO_WARGLAIVE_POINT, + EVENT_FACE_MIDDLE, + EVENT_FLY, + EVENT_THROW_WARGLAIVE, + EVENT_THROW_WARGLAIVE_2, + EVENT_FLY_TO_RANDOM_PILLAR, + EVENT_FIREBALL, + EVENT_EYE_BLAST, + EVENT_DARK_BARRAGE, + EVENT_GLAIVE_EMOTE, + EVENT_RESUME_COMBAT, + EVENT_AGONIZING_FLAMES, + EVENT_DEMON, + EVENT_DEMON_TEXT, + EVENT_CANCEL_DEMON_FORM, + EVENT_RESUME_COMBAT_DEMON, + EVENT_FLAME_BURST, + EVENT_SHADOW_DEMON, + EVENT_SCHEDULE_DEMON_SPELLS, + EVENT_SHADOW_BLAST, + EVENT_PHASE_4_DELAYED, + EVENT_SHADOW_PRISON_TEXT, + EVENT_SUMMON_MAIEV, + EVENT_CONFRONT_MAIEV_TEXT, + EVENT_RESUME_COMBAT_PHASE_4, + EVENT_FRENZY, + EVENT_TAUNT, + EVENT_DEFEATED_TEXT, + EVENT_QUIET_SUICIDE, + + // Flame of Azzinoth + EVENT_ENGAGE, + EVENT_FLAME_CHARGE, + EVENT_FLAME_BLAST, + + // Maiev + EVENT_MAIEV_APPEAR, + EVENT_MAIEV_JUSTICE_TEXT, + EVENT_MAIEV_EXCLAMATION, + EVENT_MAIEV_YES, + EVENT_MAIEV_ROAR, + EVENT_MAIEV_COMBAT, + EVENT_CAGE_TRAP, + EVENT_SHADOW_STRIKE, + EVENT_THROW_DAGGER, + EVENT_MAIEV_OUTRO_TEXT, + EVENT_MAIEV_FAREWELL_TEXT, + EVENT_MAIEV_TELEPORT_DESPAWN }; -G3D::Vector3 const GlaivePosition[4]= +Position const AkamaTeleportPositions[2] = { - {695.105f, 305.303f, 354.256f}, - {659.338f, 305.303f, 354.256f}, // the distance between two glaives is 36 - {700.105f, 305.303f, 354.256f}, - {664.338f, 305.303f, 354.256f} + { 609.7720f, 308.4560f, 271.826f }, // Illidari Council Position + { 752.2771f, 369.9401f, 353.1584f } // Minions Position }; -G3D::Vector3 const EyeBlast[2]= +Position const MinionsSpawnPositions[10] = { - {677.0f, 350.0f, 354.0f}, // start point, pass through glaive point - {677.0f, 260.0f, 354.0f} + { 745.2552f, 322.1574f, 310.4596f, 6.038839f }, + { 747.0576f, 326.4268f, 309.0688f, 0.0f }, + { 743.9686f, 289.6447f, 311.1807f, 6.056293f }, + { 748.8422f, 288.0620f, 310.9782f, 1.884956f }, + { 751.0878f, 327.6505f, 309.4576f, 6.178465f }, + { 750.0472f, 282.3274f, 309.4353f, 3.071779f }, + { 754.0332f, 325.8136f, 310.3195f, 2.9147f }, + { 753.8425f, 286.562f, 310.9353f, 1.029744f }, + { 745.3237f, 283.986f, 309.2765f, 0.6283185f }, + { 750.0322f, 323.6064f, 310.2757f, 5.497787f } }; -G3D::Vector3 const AkamaWP[13]= +Position const IllidanPhase2Positions[4] = { - {770.01f, 304.50f, 312.29f}, // Bottom of the first stairs, at the doors - {780.66f, 304.50f, 319.74f}, // Top of the first stairs - {790.13f, 319.68f, 319.76f}, // Bottom of the second stairs (left from the entrance) - {787.17f, 347.38f, 341.42f}, // Top of the second stairs - {781.34f, 350.31f, 341.44f}, // Bottom of the third stairs - {762.60f, 361.06f, 353.60f}, // Top of the third stairs - {756.35f, 360.52f, 353.27f}, // Before the door-thingy - {743.82f, 342.21f, 353.00f}, // Somewhere further - {732.69f, 305.13f, 353.00f}, // In front of Illidan - (8) - {738.11f, 365.44f, 353.00f}, // in front of the door-thingy (the other one!) - {792.18f, 366.62f, 341.42f}, // Down the first flight of stairs - {796.84f, 304.89f, 319.76f}, // Down the second flight of stairs - {782.01f, 304.55f, 319.76f} // Final location - back at the initial gates. This is where he will fight the minions! (12) + { 705.921997f, 337.145996f, 370.083008f, 3.961900f }, + { 706.226990f, 273.264008f, 370.083008f, 2.251072f }, + { 658.830017f, 265.098999f, 370.083008f, 0.850345f }, + { 656.859009f, 344.071991f, 370.083008f, 5.235990f } }; -// 755.762f, 304.0747f, 312.1769f -- This is where Akama should be spawned -G3D::Vector3 const SpiritSpawns[2]= + +Position const IllidanMiddlePoint = { 676.6479f, 304.7606f, 354.1909f , 6.230825f }; + +Position const IllidanDBTargetSpawnPositions[4] = { - {755.5426f, 309.9156f, 312.2129f}, - {755.5426f, 298.7923f, 312.0834f} + { 710.8815f, 306.4028f, 353.5962f, 2.391101f }, + { 652.105f, 259.5127f, 353.0794f, 0.122173f }, + { 642.7164f, 305.2436f, 353.5596f, 3.438299f }, + { 710.8815f, 306.4028f, 353.5962f, 2.391101f } }; -struct Animation // For the demon transformation +Position const IllidanDBTargetPoints[4] = { - uint32 aura, unaura, timer, size, displayid, phase; - bool equip; + { 660.3492f, 345.5749f, 353.2961f }, + { 701.6755f, 297.3358f, 354.041f }, + { 706.7507f, 269.4593f, 353.2778f }, + { 660.3492f, 345.5749f, 353.2961f } }; -static const Animation DemonTransformation[10]= +uint32 const SummonCageTrapSpells[8] = { - {SPELL_DEMON_TRANSFORM_1, 0, 1000, 0, 0, 6, true}, - {SPELL_DEMON_TRANSFORM_2, SPELL_DEMON_TRANSFORM_1, 4000, 0, 0, 6, true}, - {0, 0, 3000, 1073741824, 21322, 6, false}, // stunned, cannot cast demon form - {SPELL_DEMON_TRANSFORM_3, SPELL_DEMON_TRANSFORM_2, 3500, 0, 0, 6, false}, - {SPELL_DEMON_FORM, SPELL_DEMON_TRANSFORM_3, 0, 0, 0, 4, false}, - {SPELL_DEMON_TRANSFORM_1, 0, 1000, 0, 0, 6, false}, - {SPELL_DEMON_TRANSFORM_2, SPELL_DEMON_TRANSFORM_1, 4000, 0, 0, 6, false}, - {0, SPELL_DEMON_FORM, 3000, 1069547520, 21135, 6, false}, - {SPELL_DEMON_TRANSFORM_3, SPELL_DEMON_TRANSFORM_2, 3500, 0, 0, 6, true}, - {0, SPELL_DEMON_TRANSFORM_3, 0, 0, 0, 8, true} + SPELL_SUMMON_CAGE_TRAP_1, + SPELL_SUMMON_CAGE_TRAP_2, + SPELL_SUMMON_CAGE_TRAP_3, + SPELL_SUMMON_CAGE_TRAP_4, + SPELL_SUMMON_CAGE_TRAP_5, + SPELL_SUMMON_CAGE_TRAP_6, + SPELL_SUMMON_CAGE_TRAP_7, + SPELL_SUMMON_CAGE_TRAP_8 }; -class npc_flame_of_azzinoth : public CreatureScript +class SummonWarglaiveEvent : public BasicEvent { public: - npc_flame_of_azzinoth() : CreatureScript("npc_flame_of_azzinoth") { } + SummonWarglaiveEvent(Unit* owner) : BasicEvent(), _owner(owner) { } - struct flame_of_azzinothAI : public ScriptedAI + bool Execute(uint64 /*eventTime*/, uint32 /*diff*/) override { - flame_of_azzinothAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - FlameBlastTimer = 15000; - CheckTimer = 5000; - GlaiveGUID.Clear(); - } - - void Reset() override - { - Initialize(); - } - - void EnterCombat(Unit* /*who*/) override - { - DoZoneInCombat(); - } - - void ChargeCheck() - { - Unit* target = SelectTarget(SELECT_TARGET_FARTHEST, 0, 200, false); - if (target && (!me->IsWithinCombatRange(target, FLAME_CHARGE_DISTANCE))) - { - me->AddThreat(target, 5000000.0f); - AttackStart(target); - DoCast(target, SPELL_CHARGE); - Talk(EMOTE_AZZINOTH_GAZE); - } - } - - void EnrageCheck() - { - if (Creature* glaive = ObjectAccessor::GetCreature(*me, GlaiveGUID)) - { - if (!me->IsWithinDistInMap(glaive, FLAME_ENRAGE_DISTANCE)) - { - glaive->InterruptNonMeleeSpells(true); - DoCast(me, SPELL_FLAME_ENRAGE, true); - DoResetThreat(); - if (SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - me->AddThreat(me->GetVictim(), 5000000.0f); - AttackStart(me->GetVictim()); - } - } - else if (!me->HasAura(SPELL_AZZINOTH_CHANNEL)) - { - glaive->CastSpell(me, SPELL_AZZINOTH_CHANNEL, false); - me->RemoveAurasDueToSpell(SPELL_FLAME_ENRAGE); - } - } - } - - void SetGlaiveGUID(ObjectGuid guid) - { - GlaiveGUID = guid; - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (FlameBlastTimer <= diff) - { - DoCastVictim(SPELL_BLAZE_SUMMON, true); // appear at victim - DoCastVictim(SPELL_FLAME_BLAST); - FlameBlastTimer = 15000; // 10000 is official-like? - DoZoneInCombat(); // in case someone is revived - } else FlameBlastTimer -= diff; - - if (CheckTimer <= diff) - { - ChargeCheck(); - EnrageCheck(); - CheckTimer = 1000; - } else CheckTimer -= diff; + _owner->CastSpell(_owner, SPELL_RANGE_MARKER, true); + _owner->CastSpell(_owner, SPELL_SUMMON_GLAIVE, true); + return true; + } - DoMeleeAttackIfReady(); - } +private: + Unit* _owner; +}; - private: - uint32 FlameBlastTimer; - uint32 CheckTimer; - ObjectGuid GlaiveGUID; - }; +class ChargeTargetSelector : public std::unary_function<Unit*, bool> +{ +public: + ChargeTargetSelector(Unit const* unit) : _me(unit) { } - CreatureAI* GetAI(Creature* creature) const override + bool operator()(Unit* unit) const { - return new flame_of_azzinothAI(creature); + return _me->GetDistance2d(unit) > 25.0f; } + +private: + Unit const* _me; }; -/************************************** Illidan's AI* **************************************/ class boss_illidan_stormrage : public CreatureScript { public: @@ -475,1755 +442,2111 @@ public: struct boss_illidan_stormrageAI : public BossAI { - boss_illidan_stormrageAI(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE) + boss_illidan_stormrageAI(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE), + _intro(true), _minionsCount(0), _flameCount(0), _orientation(0.0f), _pillarIndex(0), _phase(0), _dead(false), _isDemon(false) { } + + void Reset() override { - Initialize(); - DoCast(me, SPELL_DUAL_WIELD, true); + _Reset(); + specialEvents.Reset(); + me->SummonCreatureGroup(SUMMON_GROUP); + me->LoadEquipment(1, true); + me->SetSheath(SHEATH_STATE_UNARMED); + me->SetControlled(false, UNIT_STATE_ROOT); + me->SetDisableGravity(false); + _dead = false; + _minionsCount = 0; + _flameCount = 0; + _phase = PHASE_1; + _isDemon = false; + if (_intro && instance->GetBossState(DATA_ILLIDARI_COUNCIL) == DONE) + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_ACTIVE_AKAMA_INTRO); } - void Initialize() + void EnterCombat(Unit* /*who*/) override { - MaievGUID.Clear(); - for (uint8 i = 0; i < 2; ++i) - { - FlameGUID[i].Clear(); - GlaiveGUID[i].Clear(); - } - - Phase = PHASE_ILLIDAN_NULL; - Event = EVENT_NULL; - Timer[EVENT_BERSERK] = 1500000; - - HoverPoint = 0; - TalkCount = 0; - FlightCount = 0; - TransformCount = 0; + _EnterCombat(); + me->SetCanDualWield(true); + specialEvents.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(10)); + specialEvents.ScheduleEvent(EVENT_BERSERK, Minutes(25)); + ScheduleEvents(GROUP_PHASE_1, GROUP_PHASE_1); + events.ScheduleEvent(EVENT_TAUNT, Seconds(30), Seconds(60), GROUP_PHASE_ALL); } - void Reset() override; + void ChangeOrientation(float orientation) + { + _orientation = orientation; + events.ScheduleEvent(EVENT_CHANGE_ORIENTATION, Milliseconds(1), GROUP_PHASE_ALL); + } - void JustSummoned(Creature* summon) override; + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_ILLIDAN_KILL); + } - void SummonedCreatureDespawn(Creature* summon) override + void ScheduleEvents(uint8 phase, uint8 group) { - if (summon->GetCreatureTemplate()->Entry == FLAME_OF_AZZINOTH) + switch (phase) { - for (uint8 i = 0; i < 2; ++i) - if (summon->GetGUID() == FlameGUID[i]) - FlameGUID[i].Clear(); - - if (!FlameGUID[0] && !FlameGUID[1] && Phase != PHASE_ILLIDAN_NULL) - { - me->InterruptNonMeleeSpells(true); - EnterPhase(PHASE_FLIGHT_SEQUENCE); - } + case GROUP_PHASE_1: + events.ScheduleEvent(EVENT_FLAME_CRASH, Seconds(30), group); + events.ScheduleEvent(EVENT_DRAW_SOUL, Seconds(34), group); + events.ScheduleEvent(EVENT_SHEAR, Seconds(10), group); + events.ScheduleEvent(EVENT_PARASITIC_SHADOWFIEND, Seconds(26), group); + break; + case GROUP_PHASE_2: + events.ScheduleEvent(EVENT_FIREBALL, Seconds(1), Seconds(8), group); + events.ScheduleEvent(EVENT_EYE_BLAST, Seconds(1), Seconds(30), group); + if (roll_chance_i(50)) + events.ScheduleEvent(EVENT_DARK_BARRAGE, Seconds(1), Seconds(20), group); + break; + case GROUP_PHASE_3: + ScheduleEvents(GROUP_PHASE_1, group); + events.ScheduleEvent(EVENT_AGONIZING_FLAMES, Seconds(21), group); + events.ScheduleEvent(EVENT_DEMON, Seconds(60), group); + break; + case GROUP_PHASE_DEMON: + events.ScheduleEvent(EVENT_SHADOW_BLAST, Seconds(1), group); + events.ScheduleEvent(EVENT_FLAME_BURST, Seconds(6), group); + events.ScheduleEvent(EVENT_SHADOW_DEMON, Seconds(18), Seconds(30), group); + case GROUP_PHASE_4: + ScheduleEvents(GROUP_PHASE_3, group); + events.ScheduleEvent(EVENT_FRENZY, Seconds(40), group); + break; + default: + break; } - summons.Despawn(summon); } - void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) override + void JustSummoned(Creature* summon) override { - if (FlightCount == 7) // change hover point - { - if (me->GetVictim()) + BossAI::JustSummoned(summon); + if (summon->GetEntry() == NPC_ILLIDARI_ELITE) + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) { - me->SetInFront(me->GetVictim()); - me->StopMoving(); + summon->CombatStart(akama); + summon->AddThreat(akama, 1000.0f); } - EnterPhase(PHASE_FLIGHT); - } - else // handle flight sequence - Timer[EVENT_FLIGHT_SEQUENCE] = 1000; } - void EnterCombat(Unit* /*who*/) override + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - _EnterCombat(); + if (summon->GetEntry() == NPC_ILLIDARI_ELITE) + _minionsCount--; } - void AttackStart(Unit* who) override + void EnterEvadeMode(EvadeReason /*why*/) override { - if (!who || Phase >= PHASE_TALK_SEQUENCE) - return; - - if (Phase == PHASE_FLIGHT || Phase == PHASE_DEMON) - AttackStartNoMove(who); - else - ScriptedAI::AttackStart(who); + summons.DespawnAll(); + specialEvents.Reset(); + _DespawnAtEvade(); } - void MoveInLineOfSight(Unit*) override { } - + void DoAction(int32 actionId) override + { + switch (actionId) + { + case ACTION_START_ENCOUNTER: + events.SetPhase(PHASE_INTRO); + me->RemoveAurasDueToSpell(SPELL_KNEEL); + events.ScheduleEvent(EVENT_START_INTRO, Seconds(2), GROUP_PHASE_ALL); + events.ScheduleEvent(EVENT_UNCONVINCED, Seconds(24), GROUP_PHASE_ALL); + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_FREE); + break; + case ACTION_INTRO_DONE: + _intro = false; + break; + case ACTION_START_MINIONS: + Talk(SAY_ILLIDAN_MINION); + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_START_MINIONS); + break; + case ACTION_START_MINIONS_WEAVE: + events.ScheduleEvent(EVENT_MINIONS_WEAVE, Milliseconds(1), GROUP_PHASE_ALL); + break; + case ACTION_START_PHASE_2: + { + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + me->SetDisableGravity(true); + DoPlaySoundToSet(me, ILLIDAN_TAKEOFF_SOUND_ID); + events.ScheduleEvent(EVENT_FLY, Seconds(1), GROUP_PHASE_ALL); + events.CancelEventGroup(GROUP_PHASE_1); + break; + } + case ACTION_FLAME_DEAD: + _flameCount++; + if (_flameCount == 2) + { + _flameCount = 0; + DoAction(ACTION_FINALIZE_AIR_PHASE); + } + break; + case ACTION_FINALIZE_AIR_PHASE: + me->InterruptNonMeleeSpells(false); + me->GetMotionMaster()->Clear(); + events.CancelEventGroup(GROUP_PHASE_2); + _phase = PHASE_3; + events.CancelEvent(EVENT_FLY_TO_RANDOM_PILLAR); + me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_MIDDLE, IllidanMiddlePoint); + break; + case ACTION_START_PHASE_4: + events.CancelEventGroup(GROUP_PHASE_3); + DoCastSelf(SPELL_SHADOW_PRISON, true); + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + events.ScheduleEvent(EVENT_SHADOW_PRISON_TEXT, Milliseconds(500), GROUP_PHASE_ALL); + break; + case ACTION_ILLIDAN_CAGED: + for (uint32 summonSpell : SummonCageTrapSpells) + DoCastSelf(summonSpell, true); + DoCastSelf(SPELL_CAGE_TRAP, true); + break; + case ACTION_START_OUTRO: + me->AttackStop(); + events.Reset(); + specialEvents.Reset(); + DoCastSelf(SPELL_DEATH, true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + events.ScheduleEvent(EVENT_DEFEATED_TEXT, Seconds(4)); + break; + default: + break; + } + } void JustDied(Unit* /*killer*/) override { me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - - _JustDied(); + instance->SetBossState(DATA_ILLIDAN_STORMRAGE, DONE); + events.Reset(); } - void KilledUnit(Unit* victim) override + void MovementInform(uint32 type, uint32 pointId) override { - if (victim->GetTypeId() != TYPEID_PLAYER) + if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE) return; - Talk(SAY_ILLIDAN_KILL); - } - - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (damage >= me->GetHealth() && done_by != me) - damage = 0; - if (done_by->GetGUID() == MaievGUID) - done_by->AddThreat(me, -(3*(float)damage)/4); // do not let maiev tank him - } - - void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override - { - if (spell->Id == SPELL_GLAIVE_RETURNS) // Re-equip our warglaives! + switch (pointId) { - if (!me->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID)) - SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); - else - SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_ID_OFF_HAND, EQUIP_NO_CHANGE); - me->SetSheath(SHEATH_STATE_MELEE); - } - } - - void DeleteFromThreatList(ObjectGuid TargetGUID) - { - ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) - { - if ((*itr)->getUnitGuid() == TargetGUID) + case POINT_THROW_GLAIVE: + DoPlaySoundToSet(me, ILLIDAN_WARGLAIVE_SOUND_ID); + events.ScheduleEvent(EVENT_THROW_WARGLAIVE, Seconds(2), GROUP_PHASE_ALL); + events.ScheduleEvent(EVENT_FACE_MIDDLE, Milliseconds(1), GROUP_PHASE_ALL); + break; + case POINT_RANDOM_PILLAR: { - (*itr)->removeReference(); + float orientation = IllidanPhase2Positions[_pillarIndex].GetOrientation(); + ChangeOrientation(orientation); + ScheduleEvents(GROUP_PHASE_2, GROUP_PHASE_2); break; } + case POINT_ILLIDAN_MIDDLE: + { + float orientation = IllidanMiddlePoint.GetOrientation(); + ChangeOrientation(orientation); + + std::list<Creature*> triggers; + GetCreatureListWithEntryInGrid(triggers, me, NPC_BLADE_OF_AZZINOTH, 150.0f); + for (Creature* trigger : triggers) + trigger->CastSpell(trigger, SPELL_GLAIVE_RETURNS, true); + + events.ScheduleEvent(EVENT_GLAIVE_EMOTE, Seconds(3), GROUP_PHASE_ALL); + break; + } + default: + break; } } - void Talk(uint32 count) + void EnterEvadeModeIfNeeded() { - Timer[EVENT_TALK_SEQUENCE] = Conversation[count].timer; + Map::PlayerList const &players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + if (Player* player = i->GetSource()) + if (player->IsAlive() && !player->IsGameMaster() && CheckBoundary(player)) + return; - Creature* creature = NULL; - if (Conversation[count].creature == ILLIDAN_STORMRAGE) - creature = me; - else if (Conversation[count].creature == AKAMA) - creature = (ObjectAccessor::GetCreature((*me), AkamaGUID)); - else if (Conversation[count].creature == MAIEV_SHADOWSONG) - creature = (ObjectAccessor::GetCreature((*me), MaievGUID)); + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } - if (creature) + void SummonMinions() + { + uint8 needSummon = MAX_MINIONS_NUMBER - _minionsCount; + for (uint8 i = 0; i < needSummon; ++i) { - if (Conversation[count].emote) - creature->HandleEmoteCommand(Conversation[count].emote); // Make the Creature do some animation! - if (Conversation[count].text.size()) - creature->Yell(Conversation[count].text.c_str(), LANG_UNIVERSAL); // Have the Creature yell out some text - if (Conversation[count].sound) - DoPlaySoundToSet(creature, Conversation[count].sound); // Play some sound on the creature + _minionsCount++; + me->SummonCreature(NPC_ILLIDARI_ELITE, MinionsSpawnPositions[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); } } - void EnterPhase(PhaseIllidan NextPhase) + void DamageTaken(Unit* who, uint32 &damage) override { - DoZoneInCombat(); - switch (NextPhase) + if (damage >= me->GetHealth() && who->GetGUID() != me->GetGUID()) { - case PHASE_NORMAL: - case PHASE_NORMAL_2: - case PHASE_NORMAL_MAIEV: - AttackStart(me->GetVictim()); - Timer[EVENT_TAUNT] = 32000; - Timer[EVENT_SHEAR] = urand(10, 25) * 1000; - Timer[EVENT_FLAME_CRASH] = 20000; - Timer[EVENT_PARASITIC_SHADOWFIEND] = 25000; - Timer[EVENT_PARASITE_CHECK] = 0; - Timer[EVENT_DRAW_SOUL] = 30000; - if (NextPhase == PHASE_NORMAL) - break; - Timer[EVENT_AGONIZING_FLAMES] = 35000; - Timer[EVENT_TRANSFORM_NORMAL] = 60000; - if (NextPhase == PHASE_NORMAL_2) - break; - Timer[EVENT_ENRAGE] = urand(30, 40) * 1000; - break; - case PHASE_FLIGHT: - Timer[EVENT_FIREBALL] = 1000; - if (!(rand32() % 4)) - Timer[EVENT_DARK_BARRAGE] = 10000; - Timer[EVENT_EYE_BLAST] = urand(10, 25) * 1000; - Timer[EVENT_MOVE_POINT] = urand(20, 40) * 1000; - break; - case PHASE_DEMON: - Timer[EVENT_SHADOW_BLAST] = 1000; - Timer[EVENT_FLAME_BURST] = 10000; - Timer[EVENT_SHADOWDEMON] = 30000; - Timer[EVENT_TRANSFORM_DEMON] = 60000; - AttackStart(me->GetVictim()); - break; - case PHASE_TALK_SEQUENCE: - Timer[EVENT_TALK_SEQUENCE] = 100; - me->RemoveAllAuras(); - me->InterruptNonMeleeSpells(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->GetMotionMaster()->Clear(false); - me->AttackStop(); - break; - case PHASE_FLIGHT_SEQUENCE: - if (Phase == PHASE_NORMAL) // lift off - { - FlightCount = 1; - Timer[EVENT_FLIGHT_SEQUENCE] = 1; - me->RemoveAllAuras(); - me->InterruptNonMeleeSpells(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->GetMotionMaster()->Clear(false); - me->AttackStop(); - } - else // land - Timer[EVENT_FLIGHT_SEQUENCE] = 2000; - break; - case PHASE_TRANSFORM_SEQUENCE: - if (Phase == PHASE_DEMON) - Timer[EVENT_TRANSFORM_SEQUENCE] = 500; - else + damage = me->GetHealth() - 1; + if (!_dead) { - TransformCount = 0; - Timer[EVENT_TRANSFORM_SEQUENCE] = 500; - Talk(SAY_ILLIDAN_MORPH); + if (_isDemon) + { + events.Reset(); + specialEvents.Reset(); + DoCastSelf(SPELL_DEMON_TRANSFORM_1); + return; + } + _dead = true; + DoAction(ACTION_START_OUTRO); + if (Creature* maiev = instance->GetCreature(DATA_MAIEV)) + maiev->AI()->DoAction(ACTION_START_OUTRO); } - me->GetMotionMaster()->Clear(); - me->AttackStop(); - break; - default: - break; } - if (MaievGUID) + else if (me->HealthBelowPct(90) && _phase < PHASE_MINIONS) { - if (Creature* maiev = ObjectAccessor::GetCreature(*me, MaievGUID)) - if (maiev->IsAlive()) - maiev->AI()->DoAction(NextPhase); + _phase = PHASE_MINIONS; + DoAction(ACTION_START_MINIONS); } - Phase = NextPhase; - Event = EVENT_NULL; - } - - void CastEyeBlast() - { - me->InterruptNonMeleeSpells(false); - - Talk(SAY_ILLIDAN_EYE_BLAST); - - float distx, disty, dist[2]; - for (uint8 i = 0; i < 2; ++i) + else if (me->HealthBelowPct(65) && _phase < PHASE_2) { - distx = EyeBlast[i].x - HoverPosition[HoverPoint].x; - disty = EyeBlast[i].y - HoverPosition[HoverPoint].y; - dist[i] = distx * distx + disty * disty; + _phase = PHASE_2; + DoAction(ACTION_START_PHASE_2); } - G3D::Vector3 initial = EyeBlast[dist[0] < dist[1] ? 0 : 1]; - for (uint8 i = 0; i < 2; ++i) + else if (me->HealthBelowPct(30) && _phase < PHASE_4) { - distx = GlaivePosition[i].x - HoverPosition[HoverPoint].x; - disty = GlaivePosition[i].y - HoverPosition[HoverPoint].y; - dist[i] = distx * distx + disty * disty; - } - G3D::Vector3 final = GlaivePosition[dist[0] < dist[1] ? 0 : 1]; - - final.x = 2 * final.x - initial.x; - final.y = 2 * final.y - initial.y; - - Creature* Trigger = me->SummonCreature(23069, initial.x, initial.y, initial.z, 0, TEMPSUMMON_TIMED_DESPAWN, 13000); - if (!Trigger) - return; - - Trigger->SetSpeedRate(MOVE_WALK, 3); - Trigger->SetWalk(true); - Trigger->GetMotionMaster()->MovePoint(0, final.x, final.y, final.z); + _phase = PHASE_4; - // Trigger->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetTarget(Trigger->GetGUID()); - DoCast(Trigger, SPELL_EYE_BLAST); + if (_isDemon) + { + _isDemon = false; + me->SetControlled(false, UNIT_STATE_ROOT); + events.CancelEventGroup(GROUP_PHASE_DEMON); + me->InterruptNonMeleeSpells(false); + DoCastSelf(SPELL_DEMON_TRANSFORM_1, true); + events.ScheduleEvent(EVENT_PHASE_4_DELAYED, Seconds(12), GROUP_PHASE_ALL); + } + else + DoAction(ACTION_START_PHASE_4); + } } - void SummonFlamesOfAzzinoth() + void ExecuteSpecialEvents() { - Talk(SAY_ILLIDAN_SUMMONFLAMES); - - for (uint8 i = 0; i < 2; ++i) + while (uint32 eventId = specialEvents.ExecuteEvent()) { - if (Creature* glaive = ObjectAccessor::GetCreature(*me, GlaiveGUID[i])) + switch (eventId) { - if (Creature* flame = me->SummonCreature(FLAME_OF_AZZINOTH, GlaivePosition[i+2].x, GlaivePosition[i+2].y, GlaivePosition[i+2].z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000)) - { - flame->setFaction(me->getFaction()); // Just in case the database has it as a different faction - flame->SetMeleeDamageSchool(SPELL_SCHOOL_FIRE); - FlameGUID[i] = flame->GetGUID(); // Record GUID in order to check if they're dead later on to move to the next phase - ENSURE_AI(npc_flame_of_azzinoth::flame_of_azzinothAI, flame->AI())->SetGlaiveGUID(GlaiveGUID[i]); - glaive->CastSpell(flame, SPELL_AZZINOTH_CHANNEL, false); // Glaives do some random Beam type channel on it. - } + case EVENT_BERSERK: + Talk(SAY_ILLIDAN_ENRAGE); + DoCastSelf(SPELL_BERSERK, true); + break; + case EVENT_CANCEL_DEMON_FORM: + me->InterruptNonMeleeSpells(false); + me->SetControlled(false, UNIT_STATE_ROOT); + events.CancelEventGroup(GROUP_PHASE_DEMON); + DoCastSelf(SPELL_DEMON_TRANSFORM_1, true); + events.ScheduleEvent(EVENT_RESUME_COMBAT_DEMON, Seconds(12), GROUP_PHASE_ALL); + _isDemon = false; + break; + case EVENT_EVADE_CHECK: + EnterEvadeModeIfNeeded(); + specialEvents.Repeat(Seconds(10)); + break; + default: + break; } } } - void SummonMaiev() + void UpdateAI(uint32 diff) override { - DoCast(me, SPELL_SHADOW_PRISON, true); - DoCast(me, SPELL_SUMMON_MAIEV, true); - if (!MaievGUID) // If Maiev cannot be summoned, reset the encounter and post some errors to the console. - { - EnterEvadeMode(); - TC_LOG_ERROR("scripts", "SD2 ERROR: Unable to summon Maiev Shadowsong (entry: 23197). Check your database to see if you have the proper SQL for Maiev Shadowsong (entry: 23197)"); - } - } + if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) + return; - void HandleTalkSequence(); + specialEvents.Update(diff); - void HandleFlightSequence() - { - switch (FlightCount) + ExecuteSpecialEvents(); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - case 1: // lift off - me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); - me->SetDisableGravity(true); - me->StopMoving(); - Talk(SAY_ILLIDAN_TAKEOFF); - Timer[EVENT_FLIGHT_SEQUENCE] = 3000; - break; - case 2: // move to center - me->GetMotionMaster()->MovePoint(0, CENTER_X + 5, CENTER_Y, CENTER_Z); // +5, for SPELL_THROW_GLAIVE bug - Timer[EVENT_FLIGHT_SEQUENCE] = 0; - break; - case 3: // throw one glaive + switch (eventId) + { + case EVENT_START_INTRO: + Talk(SAY_ILLIDAN_DUPLICITY); + break; + case EVENT_UNCONVINCED: + Talk(SAY_ILLIDAN_UNCONVINCED); + events.ScheduleEvent(EVENT_PREPARED, Seconds(14), GROUP_PHASE_ALL); + break; + case EVENT_PREPARED: + Talk(SAY_ILLIDAN_PREPARED); + me->SetSheath(SHEATH_STATE_MELEE); + events.ScheduleEvent(EVENT_ENCOUNTER_START, Seconds(3), GROUP_PHASE_ALL); + break; + case EVENT_ENCOUNTER_START: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + DoZoneInCombat(); + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_START_ENCOUNTER); + break; + case EVENT_FLAME_CRASH: + DoCastVictim(SPELL_FLAME_CRASH); + events.Repeat(Seconds(30)); + break; + case EVENT_DRAW_SOUL: + DoCastAOE(SPELL_DRAW_SOUL); + events.Repeat(Seconds(34)); + break; + case EVENT_SHEAR: + DoCastVictim(SPELL_SHEAR); + events.Repeat(Seconds(12)); + break; + case EVENT_PARASITIC_SHADOWFIEND: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) + DoCast(target, SPELL_PARASITIC_SHADOWFIEND); + events.Repeat(Seconds(30)); + break; + case EVENT_MINIONS_WEAVE: + SummonMinions(); + events.Repeat(Seconds(30)); + break; + case EVENT_MOVE_TO_WARGLAIVE_POINT: { - uint8 i=1; - Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); - if (Glaive) + Position pos; + std::list<Creature*> triggers; + GetCreatureListWithEntryInGrid(triggers, me, NPC_GLAIVE_WORLD_TRIGGER, 150.0f); + triggers.remove_if([](WorldObject* unit) { - GlaiveGUID[i] = Glaive->GetGUID(); - Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - Glaive->SetDisplayId(MODEL_INVISIBLE); - Glaive->setFaction(me->getFaction()); - DoCast(Glaive, SPELL_THROW_GLAIVE2); - } + return unit->GetPositionZ() < 355.0f || unit->GetPositionZ() > 365.0f; + }); + + if (triggers.empty()) + break; + + triggers.sort(Trinity::ObjectDistanceOrderPred(me)); + pos.Relocate(triggers.front()); + pos.SetOrientation(0.0f); + me->GetMotionMaster()->MovePoint(POINT_THROW_GLAIVE, pos); + break; } - Timer[EVENT_FLIGHT_SEQUENCE] = 700; - break; - case 4: // throw another - SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); + case EVENT_THROW_WARGLAIVE: + DoCastAOE(SPELL_THROW_GLAIVE); + events.ScheduleEvent(EVENT_THROW_WARGLAIVE_2, Seconds(1), GROUP_PHASE_ALL); + break; + case EVENT_THROW_WARGLAIVE_2: + DoCastAOE(SPELL_THROW_GLAIVE2); + me->SetSheath(SHEATH_STATE_UNARMED); + events.ScheduleEvent(EVENT_FLY_TO_RANDOM_PILLAR, Seconds(2), GROUP_PHASE_ALL); + break; + case EVENT_CHANGE_ORIENTATION: + me->SetFacingTo(_orientation, true); + break; + case EVENT_FLY: + ChangeOrientation(3.137039f); + events.ScheduleEvent(EVENT_MOVE_TO_WARGLAIVE_POINT, Seconds(6), GROUP_PHASE_ALL); + break; + case EVENT_FLY_TO_RANDOM_PILLAR: { - uint8 i=0; - Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); - if (Glaive) - { - GlaiveGUID[i] = Glaive->GetGUID(); - Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - Glaive->SetDisplayId(MODEL_INVISIBLE); - Glaive->setFaction(me->getFaction()); - DoCast(Glaive, SPELL_THROW_GLAIVE, true); - } + events.CancelEventGroup(GROUP_PHASE_2); + _pillarIndex = urand(0, 3); + me->GetMotionMaster()->MovePoint(POINT_RANDOM_PILLAR, IllidanPhase2Positions[_pillarIndex]); + events.Repeat(Seconds(30)); + break; } - Timer[EVENT_FLIGHT_SEQUENCE] = 5000; - break; - case 5: // summon flames - SummonFlamesOfAzzinoth(); - Timer[EVENT_FLIGHT_SEQUENCE] = 3000; - break; - case 6: // fly to hover point - me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z); - Timer[EVENT_FLIGHT_SEQUENCE] = 0; - break; - case 7: // return to center - me->GetMotionMaster()->MovePoint(0, CENTER_X, CENTER_Y, CENTER_Z); - Timer[EVENT_FLIGHT_SEQUENCE] = 0; - break; - case 8: // glaive return - for (uint8 i = 0; i < 2; ++i) + case EVENT_FACE_MIDDLE: + { + float angle = me->GetAngle(IllidanMiddlePoint); + me->SetFacingTo(angle, true); + break; + } + case EVENT_EYE_BLAST: { - if (GlaiveGUID[i]) + events.CancelEvent(EVENT_DARK_BARRAGE); + Position pos = IllidanDBTargetSpawnPositions[_pillarIndex]; + if (TempSummon* dbTarget = me->SummonCreature(NPC_ILLIDAN_DB_TARGET, pos, TEMPSUMMON_MANUAL_DESPAWN)) { - Unit* Glaive = ObjectAccessor::GetUnit(*me, GlaiveGUID[i]); - if (Glaive) - { - Glaive->CastSpell(me, SPELL_GLAIVE_RETURNS, false); // Make it look like the Glaive flies back up to us - Glaive->SetDisplayId(MODEL_INVISIBLE); // disappear but not die for now - } + Talk(SAY_ILLIDAN_EYE_BLAST); + DoCast(dbTarget, SPELL_EYE_BLAST); + dbTarget->GetMotionMaster()->MovePoint(POINT_DB_TARGET, IllidanDBTargetPoints[_pillarIndex]); } + break; } - Timer[EVENT_FLIGHT_SEQUENCE] = 2000; - break; - case 9: // land - me->SetDisableGravity(false); - me->StopMoving(); - me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); - for (uint8 i = 0; i < 2; ++i) + case EVENT_DARK_BARRAGE: { - if (GlaiveGUID[i]) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 150.0f, true)) + DoCast(target, SPELL_DARK_BARRAGE); + events.RescheduleEvent(EVENT_EYE_BLAST, Seconds(5), GROUP_PHASE_2); + uint32 currentTime = events.GetNextEventTime(EVENT_FLY_TO_RANDOM_PILLAR); + events.RescheduleEvent(EVENT_FLY_TO_RANDOM_PILLAR, Seconds(currentTime) + Seconds(30), GROUP_PHASE_2); + break; + } + case EVENT_FIREBALL: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 150.0f, true)) + DoCast(target, SPELL_FIREBALL); + events.Repeat(Seconds(2), Seconds(4)); + break; + case EVENT_GLAIVE_EMOTE: + me->SetDisableGravity(false); + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + me->SetSheath(SHEATH_STATE_MELEE); + events.ScheduleEvent(EVENT_RESUME_COMBAT, Seconds(3), GROUP_PHASE_ALL); + break; + case EVENT_RESUME_COMBAT: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + ScheduleEvents(GROUP_PHASE_3, GROUP_PHASE_3); + break; + case EVENT_AGONIZING_FLAMES: + DoCastSelf(SPELL_AGONIZING_FLAMES_SELECTOR); + events.Repeat(Seconds(53)); + break; + case EVENT_DEMON: + me->SetControlled(true, UNIT_STATE_ROOT); + _isDemon = true; + events.CancelEventGroup(_phase == PHASE_3 ? GROUP_PHASE_3 : GROUP_PHASE_4); + me->LoadEquipment(0, true); + DoCastSelf(SPELL_DEMON_TRANSFORM_1, true); + events.ScheduleEvent(EVENT_DEMON_TEXT, Seconds(2), GROUP_PHASE_ALL); + specialEvents.ScheduleEvent(EVENT_CANCEL_DEMON_FORM, Minutes(1) + Seconds(12)); + events.ScheduleEvent(EVENT_SCHEDULE_DEMON_SPELLS, Seconds(15)); + break; + case EVENT_SCHEDULE_DEMON_SPELLS: + DoResetThreat(); + ScheduleEvents(GROUP_PHASE_DEMON, GROUP_PHASE_DEMON); + break; + case EVENT_DEMON_TEXT: + Talk(SAY_ILLIDAN_MORPH); + break; + case EVENT_RESUME_COMBAT_DEMON: + { + uint8 group = _phase == PHASE_3 ? GROUP_PHASE_3 : GROUP_PHASE_4; + DoResetThreat(); + ScheduleEvents(group, group); + me->LoadEquipment(1, true); + break; + } + case EVENT_FLAME_BURST: + DoCastSelf(SPELL_FLAME_BURST); + events.Repeat(Seconds(22)); + break; + case EVENT_SHADOW_DEMON: + DoCastSelf(SPELL_SUMMON_SHADOWDEMON); + break; + case EVENT_SHADOW_BLAST: + DoCastVictim(SPELL_SHADOW_BLAST); + events.Repeat(Seconds(2)); + break; + case EVENT_PHASE_4_DELAYED: + DoAction(ACTION_START_PHASE_4); + break; + case EVENT_SHADOW_PRISON_TEXT: + Talk(SAY_ILLIDAN_SHADOW_PRISON); + events.ScheduleEvent(EVENT_SUMMON_MAIEV, Seconds(9), GROUP_PHASE_ALL); + break; + case EVENT_SUMMON_MAIEV: + DoCastSelf(SPELL_SUMMON_MAIEV); + if (Creature* maiev = instance->GetCreature(DATA_MAIEV)) + me->SetFacingToObject(maiev); + events.ScheduleEvent(EVENT_CONFRONT_MAIEV_TEXT, Seconds(9), GROUP_PHASE_ALL); + break; + case EVENT_CONFRONT_MAIEV_TEXT: + Talk(SAY_ILLIDAN_CONFRONT_MAIEV); + events.ScheduleEvent(EVENT_RESUME_COMBAT_PHASE_4, Seconds(13), GROUP_PHASE_ALL); + break; + case EVENT_RESUME_COMBAT_PHASE_4: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + ScheduleEvents(GROUP_PHASE_4, GROUP_PHASE_4); + break; + case EVENT_FRENZY: + DoCastSelf(SPELL_FRENZY); + Talk(SAY_ILLIDAN_FRENZY); + events.Repeat(Seconds(40)); + break; + case EVENT_TAUNT: + Talk(SAY_ILLIDAN_TAUNT); + events.Repeat(Seconds(30), Seconds(60)); + break; + case EVENT_DEFEATED_TEXT: + Talk(SAY_ILLIDAN_DEFEATED); + events.ScheduleEvent(EVENT_QUIET_SUICIDE, Seconds(18)); + break; + case EVENT_QUIET_SUICIDE: + { + DoCastSelf(SPELL_QUIET_SUICIDE, true); + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_START_OUTRO); + ObjectGuid _akamaGUID = instance->GetGuidData(DATA_AKAMA); + ObjectGuid _maievGUID = instance->GetGuidData(DATA_MAIEV); + summons.DespawnIf([_akamaGUID, _maievGUID](ObjectGuid unitGuid) { - if (Creature* glaive = ObjectAccessor::GetCreature(*me, GlaiveGUID[i])) - glaive->DespawnOrUnsummon(); - - GlaiveGUID[i].Clear(); - } + return unitGuid != _akamaGUID && unitGuid != _maievGUID; + }); + break; } - Timer[EVENT_FLIGHT_SEQUENCE] = 2000; - break; - case 10: // attack - DoResetThreat(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - me->SetSheath(SHEATH_STATE_MELEE); - EnterPhase(PHASE_NORMAL_2); - break; - default: - break; + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); + } + + private: + bool _intro; + uint8 _minionsCount; + uint8 _flameCount; + float _orientation; + uint8 _pillarIndex; + uint8 _phase; + bool _dead; + bool _isDemon; + EventMap specialEvents; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetBlackTempleAI<boss_illidan_stormrageAI>(creature); + } +}; + +class npc_akama : public CreatureScript +{ +public: + npc_akama() : CreatureScript("npc_akama_illidan") { } + + struct npc_akamaAI : public ScriptedAI + { + npc_akamaAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _orientation(0.0f), _isTeleportToMinions(false) { } + + void Reset() override + { + _events.Reset(); + _spiritOfUdaloGUID.Clear(); + _spiritOfOlumGUID.Clear(); + _isTeleportToMinions = false; + } + + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + { + if (gossipListId == GOSSIP_START_INTRO) + { + _instance->SetData(DATA_AKAMA, AKAMA_FIGHT); + me->GetMotionMaster()->MoveAlongSplineChain(POINT_STAIRS, SPLINE_STAIRS, false); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->DoAction(ACTION_INTRO_DONE); + CloseGossipMenuFor(player); + + } + else if (gossipListId == GOSSIP_START_FIGHT) + { + _events.SetPhase(PHASE_INTRO); + me->GetMotionMaster()->MoveAlongSplineChain(POINT_FACE_ILLIDAN, SPLINE_FACE_ILLIDAN, false); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + CloseGossipMenuFor(player); } - ++FlightCount; } - void HandleTransformSequence() + bool CanAIAttack(Unit const* who) const override { - if (DemonTransformation[TransformCount].unaura) - me->RemoveAurasDueToSpell(DemonTransformation[TransformCount].unaura); + if (_events.IsInPhase(PHASE_MINIONS) && who->GetEntry() == NPC_ILLIDAN_STORMRAGE) + return false; + return ScriptedAI::CanAIAttack(who); + } - if (DemonTransformation[TransformCount].aura) - DoCast(me, DemonTransformation[TransformCount].aura, true); + uint32 GetData(uint32 /*data*/) const override + { + return _isTeleportToMinions ? 1 : 0; + } - if (DemonTransformation[TransformCount].displayid) - me->SetDisplayId(DemonTransformation[TransformCount].displayid); // It's morphin time! + void EnterEvadeMode(EvadeReason /*why*/) override { } - if (DemonTransformation[TransformCount].equip) + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_SPIRIT_OF_UDALO) { - // Requip warglaives if needed - SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_ID_OFF_HAND, EQUIP_NO_CHANGE); - me->SetSheath(SHEATH_STATE_MELEE); + _spiritOfUdaloGUID = summon->GetGUID(); + summon->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } - else + else if (summon->GetEntry() == NPC_SPIRIT_OF_OLUM) { - // Unequip warglaives if needed - SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); + _spiritOfOlumGUID = summon->GetGUID(); + summon->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } + } - switch (TransformCount) + void DoAction(int32 actionId) override + { + switch (actionId) { - case 2: - DoResetThreat(); + case ACTION_ACTIVE_AKAMA_INTRO: + _events.SetPhase(PHASE_INTRO); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + _events.SetPhase(PHASE_INTRO); + _events.ScheduleEvent(EVENT_TELEPORT, Seconds(1)); + _events.ScheduleEvent(EVENT_MOVE_TO_ILLIDARI_ROOM, Seconds(1) + Milliseconds(500)); break; - case 4: - EnterPhase(PHASE_DEMON); + case ACTION_OPEN_DOOR: + _instance->SetData(ACTION_OPEN_DOOR, 0); + _events.ScheduleEvent(EVENT_AKAMA_THANKS, Seconds(2)); break; - case 7: - DoResetThreat(); + case ACTION_FREE: + _events.ScheduleEvent(EVENT_FREE, Seconds(14)); break; - case 9: - if (MaievGUID) - EnterPhase(PHASE_NORMAL_MAIEV); // Depending on whether we summoned Maiev, we switch to either phase 5 or 3 - else - EnterPhase(PHASE_NORMAL_2); + case ACTION_START_ENCOUNTER: + DoZoneInCombat(); + _events.ScheduleEvent(EVENT_HEALING_POTION, Seconds(1)); + break; + case ACTION_START_MINIONS: + _events.ScheduleEvent(EVENT_AKAMA_MINIONS, Seconds(8)); + break; + case ACTION_START_OUTRO: + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + _events.Reset(); + _events.ScheduleEvent(EVENT_AKAMA_MOVE_BACK, Seconds(2)); break; default: break; } - if (Phase == PHASE_TRANSFORM_SEQUENCE) - Timer[EVENT_TRANSFORM_SEQUENCE] = DemonTransformation[TransformCount].timer; - ++TransformCount; } - void UpdateAI(uint32 diff) override + void ChangeOrientation(float orientation) { - if ((!UpdateVictim()) && Phase < PHASE_TALK_SEQUENCE) - return; + _orientation = orientation; + _events.ScheduleEvent(EVENT_CHANGE_ORIENTATION, Milliseconds(1)); + } - Event = EVENT_NULL; - for (int32 i = 1; i <= MaxTimer[Phase]; ++i) - { - if (Timer[i]) // Event is enabled - { - if (Timer[i] <= diff) - { - if (!Event) // No event with higher priority - Event = (EventIllidan)i; - } - else Timer[i] -= diff; - } - } + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != POINT_MOTION_TYPE && type != SPLINE_CHAIN_MOTION_TYPE) + return; - switch (Phase) + switch (pointId) { - case PHASE_NORMAL: - if (HealthBelowPct(65)) - EnterPhase(PHASE_FLIGHT_SEQUENCE); + case POINT_ILLIDARI_COUNCIL: + Talk(SAY_AKAMA_FINISH); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); break; - - case PHASE_NORMAL_2: - if (HealthBelowPct(30)) - EnterPhase(PHASE_TALK_SEQUENCE); + case POINT_STAIRS: + ChangeOrientation(6.265732f); + _events.ScheduleEvent(EVENT_AKAMA_SAY_DOOR, Seconds(5)); break; - - case PHASE_NORMAL_MAIEV: - if (HealthBelowPct(1)) - EnterPhase(PHASE_TALK_SEQUENCE); + case POINT_ILLIDAN_ROOM: + ChangeOrientation(2.129302f); + Talk(SAY_AKAMA_BETRAYER); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); break; - - case PHASE_TALK_SEQUENCE: - if (Event == EVENT_TALK_SEQUENCE) - HandleTalkSequence(); + case POINT_FACE_ILLIDAN: + ChangeOrientation(3.140537f); + _events.ScheduleEvent(EVENT_START_ILLIDAN, Seconds(2)); break; - - case PHASE_FLIGHT_SEQUENCE: - if (Event == EVENT_FLIGHT_SEQUENCE) - HandleFlightSequence(); + case POINT_TELEPORT: + DoCastSelf(SPELL_AKAMA_TELEPORT); + _events.ScheduleEvent(EVENT_AKAMA_MINIONS_MOVE_2, Milliseconds(500)); break; - - case PHASE_TRANSFORM_SEQUENCE: - if (Event == EVENT_TRANSFORM_SEQUENCE) - HandleTransformSequence(); + case POINT_MINIONS: + _events.SetPhase(PHASE_MINIONS); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + me->SetReactState(REACT_AGGRESSIVE); + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->DoAction(ACTION_START_MINIONS_WEAVE); + _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, Seconds(2)); + break; + case POINT_MOVE_BACK: + _events.ScheduleEvent(EVENT_AKAMA_MOVE_TO_ILLIDAN, Milliseconds(1)); + break; + case POINT_ILLIDAN: + _events.ScheduleEvent(EVENT_AKAMA_LIGHT_TEXT, Seconds(1)); break; default: break; } + } + + void DamageTaken(Unit* /*who*/, uint32 &damage) override + { + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; + } - if (me->IsNonMeleeSpellCast(false)) + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO)) return; - if (Phase == PHASE_NORMAL || Phase == PHASE_NORMAL_2 || (Phase == PHASE_NORMAL_MAIEV && !me->HasAura(SPELL_CAGED))) + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) { - switch (Event) + switch (eventId) { - // PHASE_NORMAL - case EVENT_BERSERK: - Talk(SAY_ILLIDAN_ENRAGE); - DoCast(me, SPELL_BERSERK, true); - Timer[EVENT_BERSERK] = 5000; // The buff actually lasts forever. + case EVENT_TELEPORT: + DoCastSelf(SPELL_AKAMA_TELEPORT, true); break; - - case EVENT_TAUNT: - Talk(SAY_ILLIDAN_TAUNT); - Timer[EVENT_TAUNT] = urand(25000, 35000); + case EVENT_MOVE_TO_ILLIDARI_ROOM: + me->GetMotionMaster()->MoveAlongSplineChain(POINT_ILLIDARI_COUNCIL, SPLINE_ILLIDARI_COUNCIL, false); break; - - case EVENT_SHEAR: - // no longer exists in 3.0f.2 - // DoCastVictim(SPELL_SHEAR); - Timer[EVENT_SHEAR] = 25000 + (rand32() % 16 * 1000); + case EVENT_AKAMA_SAY_DOOR: + Talk(SAY_AKAMA_DOOR); + _events.ScheduleEvent(EVENT_AKAMA_DOOR_FAIL, Seconds(4)); break; - - case EVENT_FLAME_CRASH: - DoCastVictim(SPELL_FLAME_CRASH); - Timer[EVENT_FLAME_CRASH] = urand(30000, 40000); + case EVENT_AKAMA_DOOR_FAIL: + DoCastSelf(SPELL_AKAMA_DOOR_FAIL); + _events.ScheduleEvent(EVENT_AKAMA_SAY_ALONE, Seconds(10)); break; - - case EVENT_PARASITIC_SHADOWFIEND: + case EVENT_AKAMA_SAY_ALONE: + Talk(SAY_AKAMA_ALONE); + _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, Seconds(7)); + break; + case EVENT_SUMMON_SPIRITS: + me->SummonCreatureGroup(SUMMON_GROUP); + _events.ScheduleEvent(EVENT_SPIRIT_SAY_1, Seconds(1)); + break; + case EVENT_SPIRIT_SAY_1: + if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) + undalo->AI()->Talk(SAY_SPIRIT_ALONE); + _events.ScheduleEvent(EVENT_SPIRIT_SAY_2, Seconds(6)); + break; + case EVENT_SPIRIT_SAY_2: + if (Creature* olum = ObjectAccessor::GetCreature(*me, _spiritOfOlumGUID)) + olum->AI()->Talk(SAY_SPIRIT_ALONE); + _events.ScheduleEvent(EVENT_AKAMA_DOOR_SUCCESS, Seconds(6)); + break; + case EVENT_AKAMA_DOOR_SUCCESS: + DoCastSelf(SPELL_AKAMA_DOOR_CHANNEL); + if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) + undalo->CastSpell((Unit*) nullptr, SPELL_DEATHSWORN_DOOR_CHANNEL); + if (Creature* olum = ObjectAccessor::GetCreature(*me, _spiritOfOlumGUID)) + olum->CastSpell((Unit*) nullptr, SPELL_DEATHSWORN_DOOR_CHANNEL); + break; + case EVENT_AKAMA_THANKS: + Talk(SAY_AKAMA_SALUTE); + _events.ScheduleEvent(EVENT_SPIRIT_SALUTE, Seconds(3)); + _events.ScheduleEvent(EVENT_RUN_FROM_ILLIDAN_ROOM, Seconds(7)); + break; + case EVENT_SPIRIT_SALUTE: + if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 200, true)) - DoCast(target, SPELL_PARASITIC_SHADOWFIEND, true); - Timer[EVENT_PARASITIC_SHADOWFIEND] = urand(35000, 45000); + undalo->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + undalo->DespawnOrUnsummon(Seconds(7)); + } + if (Creature* olum = ObjectAccessor::GetCreature(*me, _spiritOfOlumGUID)) + { + olum->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + olum->DespawnOrUnsummon(Seconds(7)); } break; - - case EVENT_PARASITE_CHECK: - Timer[EVENT_PARASITE_CHECK] = 0; + case EVENT_RUN_FROM_ILLIDAN_ROOM: + me->GetMotionMaster()->MoveAlongSplineChain(POINT_ILLIDAN_ROOM, SPLINE_ILLIDAN_ROOM, false); break; - - case EVENT_DRAW_SOUL: - DoCastVictim(SPELL_DRAW_SOUL); - Timer[EVENT_DRAW_SOUL] = urand(50000, 60000); + case EVENT_START_ILLIDAN: + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->DoAction(ACTION_START_ENCOUNTER); break; - - // PHASE_NORMAL_2 - case EVENT_AGONIZING_FLAMES: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_AGONIZING_FLAMES); - Timer[EVENT_AGONIZING_FLAMES] = 0; + case EVENT_FREE: + Talk(SAY_AKAMA_FREE); + _events.ScheduleEvent(EVENT_TIME_HAS_COME, Seconds(18)); break; - - case EVENT_TRANSFORM_NORMAL: - EnterPhase(PHASE_TRANSFORM_SEQUENCE); + case EVENT_TIME_HAS_COME: + Talk(SAY_AKAMA_TIME_HAS_COME); + _events.ScheduleEvent(EVENT_ROAR, Seconds(2)); break; - - // PHASE_NORMAL_MAIEV - case EVENT_ENRAGE: - DoCast(me, SPELL_ENRAGE); - Timer[EVENT_ENRAGE] = 0; + case EVENT_ROAR: + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H); break; - - default: + case EVENT_CHANGE_ORIENTATION: + me->SetFacingTo(_orientation, true); break; - } - DoMeleeAttackIfReady(); - } - - if (Phase == PHASE_FLIGHT) - { - switch (Event) - { - case EVENT_FIREBALL: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL); - Timer[EVENT_FIREBALL] = 3000; + case EVENT_HEALING_POTION: + if (me->HealthBelowPct(20)) + DoCastSelf(SPELL_HEALING_POTION); + _events.Repeat(Seconds(1)); break; - - case EVENT_DARK_BARRAGE: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_DARK_BARRAGE); - Timer[EVENT_DARK_BARRAGE] = 0; + case EVENT_AKAMA_MINIONS: + Talk(SAY_AKAMA_MINIONS); + _events.ScheduleEvent(EVENT_AKAMA_MINIONS_EMOTE, Seconds(2)); break; - - case EVENT_EYE_BLAST: - CastEyeBlast(); - Timer[EVENT_EYE_BLAST] = 0; + case EVENT_AKAMA_MINIONS_EMOTE: + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + _events.ScheduleEvent(EVENT_AKAMA_MINIONS_MOVE, Seconds(4)); break; - - case EVENT_MOVE_POINT: - Phase = PHASE_FLIGHT_SEQUENCE; - Timer[EVENT_FLIGHT_SEQUENCE] = 0; // do not start Event when changing hover point - HoverPoint += (rand32() % 3 + 1); - if (HoverPoint > 3) - HoverPoint -= 4; - me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z); + case EVENT_AKAMA_MINIONS_MOVE: + _isTeleportToMinions = true; + me->GetMotionMaster()->MoveAlongSplineChain(POINT_TELEPORT, SPLINE_TELEPORT, false); break; - - default: + case EVENT_AKAMA_MINIONS_MOVE_2: + me->GetMotionMaster()->MoveAlongSplineChain(POINT_MINIONS, SPLINE_MINIONS, false); break; - } - } - - if (Phase == PHASE_DEMON) - { - switch (Event) - { - case EVENT_SHADOW_BLAST: - me->GetMotionMaster()->Clear(false); - if (me->GetVictim() && (!me->IsWithinDistInMap(me->GetVictim(), 50) || !me->IsWithinLOSInMap(me->GetVictim()))) - me->GetMotionMaster()->MoveChase(me->GetVictim(), 30); - else - me->GetMotionMaster()->MoveIdle(); - DoCastVictim(SPELL_SHADOW_BLAST); - Timer[EVENT_SHADOW_BLAST] = 4000; + case EVENT_CHAIN_LIGHTNING: + DoCastVictim(SPELL_CHAIN_LIGHTNING); + _events.Repeat(Seconds(8) + Milliseconds(500)); break; - case EVENT_SHADOWDEMON: - DoCast(me, SPELL_SUMMON_SHADOWDEMON); - Timer[EVENT_SHADOWDEMON] = 0; - Timer[EVENT_FLAME_BURST] += 10000; + case EVENT_AKAMA_MOVE_BACK: + me->GetMotionMaster()->MoveAlongSplineChain(POINT_MOVE_BACK, SPLINE_MOVE_BACK, false); break; - case EVENT_FLAME_BURST: - DoCast(me, SPELL_FLAME_BURST); - Timer[EVENT_FLAME_BURST] = 15000; + case EVENT_AKAMA_MOVE_TO_ILLIDAN: + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + me->GetMotionMaster()->MoveCloserAndStop(POINT_ILLIDAN, illidan, 5.0f); break; - case EVENT_TRANSFORM_DEMON: - EnterPhase(PHASE_TRANSFORM_SEQUENCE); + case EVENT_AKAMA_LIGHT_TEXT: + Talk(SAY_AKAMA_LIGHT); + _events.ScheduleEvent(EVENT_FINAL_SALUTE, Seconds(4)); + break; + case EVENT_FINAL_SALUTE: + me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + _events.ScheduleEvent(EVENT_AKAMA_DESPAWN, Seconds(5)); + break; + case EVENT_AKAMA_DESPAWN: + DoCastSelf(SPELL_AKAMA_DESPAWN, true); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } + + DoMeleeAttackIfReady(); } - public: - ObjectGuid AkamaGUID; - uint32 Timer[EVENT_ENRAGE + 1]; - PhaseIllidan Phase; private: - EventIllidan Event; - uint32 TalkCount; - uint32 TransformCount; - uint32 FlightCount; - uint32 HoverPoint; - ObjectGuid MaievGUID; - ObjectGuid FlameGUID[2]; - ObjectGuid GlaiveGUID[2]; + InstanceScript* _instance; + EventMap _events; + ObjectGuid _spiritOfUdaloGUID; + ObjectGuid _spiritOfOlumGUID; + float _orientation; + bool _isTeleportToMinions; }; CreatureAI* GetAI(Creature* creature) const override { - return GetInstanceAI<boss_illidan_stormrageAI>(creature); + return GetBlackTempleAI<npc_akamaAI>(creature); } }; -/********************************** End of Illidan AI* *****************************************/ - -/******* Functions and vars for Maiev's AI* *****/ -class boss_maiev_shadowsong : public CreatureScript +class npc_parasitic_shadowfiend : public CreatureScript { public: - boss_maiev_shadowsong() : CreatureScript("boss_maiev_shadowsong") { } + npc_parasitic_shadowfiend() : CreatureScript("npc_parasitic_shadowfiend") { } - struct boss_maievAI : public ScriptedAI + struct npc_parasitic_shadowfiendAI : public ScriptedAI { - boss_maievAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - MaxTimer = 0; - Phase = PHASE_NORMAL_MAIEV; - IllidanGUID.Clear(); - Timer[EVENT_MAIEV_STEALTH] = 0; - Timer[EVENT_MAIEV_TAUNT] = urand(22, 43) * 1000; - Timer[EVENT_MAIEV_SHADOW_STRIKE] = 30000; - } + npc_parasitic_shadowfiendAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } void Reset() override { - Initialize(); - SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND_MAIEV, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); - me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, 45738); + if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) + { + me->DespawnOrUnsummon(); + return; + } + + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->JustSummoned(me); + me->SetReactState(REACT_DEFENSIVE); + _scheduler.Schedule(Seconds(2), [this](TaskContext /*context*/) + { + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + }); } - void EnterCombat(Unit* /*who*/) override { } - void MoveInLineOfSight(Unit* /*who*/) override { } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void EnterEvadeMode(EvadeReason /*why*/) override { } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void GetIllidanGUID(ObjectGuid guid) - { - IllidanGUID = guid; + _scheduler.Update(diff, [this] + { + DoMeleeAttackIfReady(); + }); } - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (done_by->GetGUID() != IllidanGUID) - damage = 0; - else - { - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) - if (illidan->GetVictim() == me) - damage = me->CountPctFromMaxHealth(10); + private: + InstanceScript* _instance; + TaskScheduler _scheduler; + }; - if (damage >= me->GetHealth()) - damage = 0; - } - } + CreatureAI* GetAI(Creature* creature) const override + { + return GetBlackTempleAI<npc_parasitic_shadowfiendAI>(creature); + } +}; - void AttackStart(Unit* who) override +class npc_blade_of_azzinoth : public CreatureScript +{ +public: + npc_blade_of_azzinoth() : CreatureScript("npc_blade_of_azzinoth") { } + + struct npc_blade_of_azzinothAI : public NullCreatureAI + { + npc_blade_of_azzinothAI(Creature* creature) : NullCreatureAI(creature), _instance(creature->GetInstanceScript()) { } + + void Reset() override { - if (!who || Timer[EVENT_MAIEV_STEALTH]) + if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) + { + me->DespawnOrUnsummon(); return; + } - if (Phase == PHASE_TALK_SEQUENCE) - AttackStartNoMove(who); - else if (Phase == PHASE_DEMON || Phase == PHASE_TRANSFORM_SEQUENCE) + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->JustSummoned(me); + _flameGuid.Clear(); + me->PlayDirectSound(WARGLAIVE_SPAWN_SOUND_ID); + DoCastSelf(SPELL_BIRTH, true); + _scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) { - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) - if (me->IsWithinDistInMap(illidan, 25)) - BlinkToPlayer(); // Do not let dread aura hurt her. - AttackStartNoMove(who); - } - else - ScriptedAI::AttackStart(who); + DoCastSelf(SPELL_SUMMON_TEAR_OF_AZZINOTH); + _scheduler.Schedule(Milliseconds(500), [this](TaskContext /*context*/) + { + if (Creature* flame = ObjectAccessor::GetCreature(*me, _flameGuid)) + DoCast(flame, SPELL_AZZINOTH_CHANNEL); + }); + }); } - void DoAction(int32 param) override + void JustSummoned(Creature* summon) override { - if (param > PHASE_ILLIDAN_NULL && param < PHASE_ILLIDAN_MAX) - EnterPhase(PhaseIllidan(param)); + if (summon->GetEntry() == NPC_FLAME_OF_AZZINOTH) + _flameGuid = summon->GetGUID(); } - void EnterPhase(PhaseIllidan NextPhase) // This is in fact Illidan's phase. + void UpdateAI(uint32 diff) override { - switch (NextPhase) - { - case PHASE_TALK_SEQUENCE: - if (Timer[EVENT_MAIEV_STEALTH]) - { - me->SetFullHealth(); - me->SetVisible(true); - Timer[EVENT_MAIEV_STEALTH] = 0; - } - me->InterruptNonMeleeSpells(false); - me->GetMotionMaster()->Clear(false); - me->AttackStop(); - me->SetTarget(IllidanGUID); - MaxTimer = 0; - break; - case PHASE_TRANSFORM_SEQUENCE: - MaxTimer = 4; - Timer[EVENT_MAIEV_TAUNT] += 10000; - Timer[EVENT_MAIEV_THROW_DAGGER] = 2000; - break; - case PHASE_DEMON: - break; - case PHASE_NORMAL_MAIEV: - MaxTimer = 4; - Timer[EVENT_MAIEV_TAUNT] += 10000; - Timer[EVENT_MAIEV_TRAP] = 22000; - break; - default: - break; - } - if (Timer[EVENT_MAIEV_STEALTH]) - MaxTimer = 1; - Phase = NextPhase; + _scheduler.Update(diff); } - void BlinkTo(float x, float y, float z) + private: + InstanceScript* _instance; + TaskScheduler _scheduler; + ObjectGuid _flameGuid; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetBlackTempleAI<npc_blade_of_azzinothAI>(creature); + } +}; + +class npc_flame_of_azzinoth : public CreatureScript +{ +public: + npc_flame_of_azzinoth() : CreatureScript("npc_flame_of_azzinoth") { } + + struct npc_flame_of_azzinothAI : public ScriptedAI + { + npc_flame_of_azzinothAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { - me->AttackStop(); - me->InterruptNonMeleeSpells(false); - me->GetMotionMaster()->Clear(false); - DoTeleportTo(x, y, z); - DoCast(me, SPELL_TELEPORT_VISUAL, true); + SetBoundary(_instance->GetBossBoundary(DATA_ILLIDAN_STORMRAGE)); } - void BlinkToPlayer() + void Reset() override { - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) + if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) { - Unit* target = illidan->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0); - - if (!target || !me->IsWithinDistInMap(target, 80) || illidan->IsWithinDistInMap(target, 20)) - { - uint8 pos = rand32() % 4; - BlinkTo(HoverPosition[pos].x, HoverPosition[pos].y, HoverPosition[pos].z); - } - else - { - float x, y, z; - target->GetPosition(x, y, z); - BlinkTo(x, y, z); - } + me->DespawnOrUnsummon(); + return; } + + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->JustSummoned(me); + DoCastSelf(SPELL_FLAME_TEAR_OF_AZZINOTH, true); // Idk what this spell should do + me->SetReactState(REACT_PASSIVE); + _events.ScheduleEvent(EVENT_ENGAGE, Seconds(3)); + _events.ScheduleEvent(EVENT_FLAME_BLAST, Seconds(11)); } void UpdateAI(uint32 diff) override { - if ((!UpdateVictim()) - && !Timer[EVENT_MAIEV_STEALTH]) + if (!UpdateVictim()) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - EventMaiev Event = EVENT_MAIEV_NULL; - for (uint8 i = 1; i <= MaxTimer; ++i) + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) { - if (Timer[i]) + switch (eventId) { - if (Timer[i] <= diff) - Event = (EventMaiev)i; - else Timer[i] -= diff; + case EVENT_ENGAGE: + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + _events.ScheduleEvent(EVENT_FLAME_CHARGE, Seconds(5)); + break; + case EVENT_FLAME_CHARGE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, ChargeTargetSelector(me))) + { + DoCast(target, SPELL_CHARGE); + _events.Repeat(Seconds(5)); + } + else + _events.Repeat(Seconds(1)); + break; + case EVENT_FLAME_BLAST: + DoCastAOE(SPELL_FLAME_BLAST); + _events.Repeat(Seconds(12)); + break; + default: + break; } - } - switch (Event) - { - case EVENT_MAIEV_STEALTH: - { - me->SetFullHealth(); - me->SetVisible(true); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - Timer[EVENT_MAIEV_STEALTH] = 0; - BlinkToPlayer(); - EnterPhase(Phase); - } - break; - case EVENT_MAIEV_TAUNT: - Talk(SAY_MAIEV_SHADOWSONG_TAUNT); - Timer[EVENT_MAIEV_TAUNT] = urand(22, 43) * 1000; - break; - case EVENT_MAIEV_SHADOW_STRIKE: - DoCastVictim(SPELL_SHADOW_STRIKE); - Timer[EVENT_MAIEV_SHADOW_STRIKE] = 60000; - break; - case EVENT_MAIEV_TRAP: - if (Phase == PHASE_NORMAL_MAIEV) - { - BlinkToPlayer(); - DoCast(me, SPELL_CAGE_TRAP_SUMMON); - Timer[EVENT_MAIEV_TRAP] = 22000; - } - else - { - if (!me->IsWithinDistInMap(me->GetVictim(), 40)) - me->GetMotionMaster()->MoveChase(me->GetVictim(), 30); - DoCastVictim(SPELL_THROW_DAGGER); - Timer[EVENT_MAIEV_THROW_DAGGER] = 2000; - } - break; - default: - break; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - if (HealthBelowPct(50)) - { - me->SetVisible(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) - ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->DeleteFromThreatList(me->GetGUID()); - me->AttackStop(); - Timer[EVENT_MAIEV_STEALTH] = 60000; // reappear after 1 minute - MaxTimer = 1; - } + DoMeleeAttackIfReady(); + } - if (Phase == PHASE_NORMAL_MAIEV) - DoMeleeAttackIfReady(); + void JustDied(Unit* /*killer*/) override + { + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->DoAction(ACTION_FLAME_DEAD); } private: - ObjectGuid IllidanGUID; - PhaseIllidan Phase; - uint32 Timer[5]; - uint32 MaxTimer; + InstanceScript* _instance; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override { - return new boss_maievAI(creature); + return GetBlackTempleAI<npc_flame_of_azzinothAI>(creature); } }; -/******* Functions and vars for Akama's AI* *****/ -class npc_akama_illidan : public CreatureScript +class npc_illidan_db_target : public CreatureScript { public: - npc_akama_illidan() : CreatureScript("npc_akama_illidan") { } + npc_illidan_db_target() : CreatureScript("npc_illidan_db_target") { } - struct npc_akama_illidanAI : public ScriptedAI + struct npc_illidan_db_targetAI : public NullCreatureAI { - npc_akama_illidanAI(Creature* creature) : ScriptedAI(creature) + npc_illidan_db_targetAI(Creature* creature) : NullCreatureAI(creature) { } + + void Reset() override { - Initialize(); - instance = creature->GetInstanceScript(); - JustCreated = true; + DoCastSelf(SPELL_EYE_BLAST_TRIGGER); } - void Initialize() + void JustSummoned(Creature* summon) override { - ChannelGUID.Clear(); - SpiritGUID[0].Clear(); - SpiritGUID[1].Clear(); - - Phase = PHASE_AKAMA_NULL; - Timer = 0; - - ChannelCount = 0; - TalkCount = 0; - Check_Timer = 5000; - WalkCount = 0; + if (summon->GetEntry() == NPC_DEMON_FIRE) + summon->SetReactState(REACT_PASSIVE); } - void Reset() override + void MovementInform(uint32 type, uint32 pointId) override { - Initialize(); - instance->SetBossState(DATA_ILLIDAN_STORMRAGE, NOT_STARTED); + if (type == POINT_MOTION_TYPE && pointId == POINT_DB_TARGET) + { + me->RemoveAurasDueToSpell(SPELL_EYE_BLAST_TRIGGER); + me->RemoveAurasDueToSpell(SPELL_EYE_BLAST); + } + } + }; - IllidanGUID = instance->GetGuidData(DATA_ILLIDAN_STORMRAGE); - GateGUID = instance->GetGuidData(DATA_GO_ILLIDAN_GATE); + CreatureAI* GetAI(Creature* creature) const override + { + return GetBlackTempleAI<npc_illidan_db_targetAI>(creature); + } +}; - if (JustCreated) // close all doors at create - instance->HandleGameObject(GateGUID, false); - else // open all doors, raid wiped +class npc_illidan_shadow_demon : public CreatureScript +{ +public: + npc_illidan_shadow_demon() : CreatureScript("npc_shadow_demon") { } + + struct npc_illidan_shadow_demonAI : public PassiveAI + { + npc_illidan_shadow_demonAI(Creature* creature) : PassiveAI(creature), _instance(creature->GetInstanceScript()) { } + + void Reset() override + { + if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) { - instance->HandleGameObject(GateGUID, true); - WalkCount = 1; // skip first wp + me->DespawnOrUnsummon(); + return; } - KillAllElites(); + DoCastSelf(SPELL_SHADOW_DEMON_PASSIVE); + DoCastSelf(SPELL_FIND_TARGET); + _scheduler.Schedule(Seconds(1), [this](TaskContext checkTarget) + { + if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) + { + if (!target->IsAlive()) + DoCastSelf(SPELL_FIND_TARGET); + else if (me->IsWithinMeleeRange(target)) + { + me->InterruptNonMeleeSpells(false); + DoCast(target, SPELL_CONSUME_SOUL, true); + } + } + checkTarget.Repeat(); + }); + } - me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); // Database sometimes has strange values.. - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - me->setActive(false); - me->SetVisible(false); + void SetGUID(ObjectGuid guid, int32 /*id*/) override + { + _targetGUID = guid; + if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) + me->GetMotionMaster()->MoveChase(target); } - // Do not call reset in Akama's evade mode, as this will stop him from summoning minions after he kills the first bit - void EnterEvadeMode(EvadeReason /*why*/) override + void UpdateAI(uint32 diff) override { - me->RemoveAllAuras(); - me->DeleteThreatList(); - me->CombatStop(true); + _scheduler.Update(diff); } - void EnterCombat(Unit* /*who*/) override { } - void MoveInLineOfSight(Unit* /*who*/) override { } + private: + InstanceScript* _instance; + TaskScheduler _scheduler; + ObjectGuid _targetGUID; + }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetBlackTempleAI<npc_illidan_shadow_demonAI>(creature); + } +}; - void MovementInform(uint32 MovementType, uint32 /*Data*/) override +class npc_maiev : public CreatureScript +{ +public: + npc_maiev() : CreatureScript("npc_maiev") { } + + struct npc_maievAI : public ScriptedAI + { + npc_maievAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _canDown(true) { } + + void Reset() override { - if (MovementType == POINT_MOTION_TYPE) - Timer = 1; + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + me->SetFacingToObject(illidan); + me->SetReactState(REACT_PASSIVE); + _events.SetPhase(PHASE_INTRO); + _events.ScheduleEvent(EVENT_MAIEV_APPEAR, Seconds(1)); + _events.ScheduleEvent(EVENT_MAIEV_EXCLAMATION, Seconds(2)); + _events.ScheduleEvent(EVENT_MAIEV_JUSTICE_TEXT, Seconds(14)); + _events.ScheduleEvent(EVENT_TAUNT, Seconds(20), Seconds(60)); + _canDown = true; } - void DamageTaken(Unit* done_by, uint32 &damage) override + void EnterCombat(Unit* /*who*/) override { - if (damage > me->GetHealth() || done_by->GetGUID() != IllidanGUID) - damage = 0; + _events.SetPhase(PHASE_1); + _events.ScheduleEvent(EVENT_CAGE_TRAP, Seconds(30)); + _events.ScheduleEvent(EVENT_SHADOW_STRIKE, Seconds(50)); + _events.ScheduleEvent(EVENT_THROW_DAGGER, Seconds(1)); } - void KillAllElites() + void DoAction(int32 actionId) override { - ThreatContainer::StorageType const &threatList = me->getThreatManager().getThreatList(); - std::vector<Unit*> eliteList; - for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) + if (actionId == ACTION_START_OUTRO) { - Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); - if (unit && unit->GetEntry() == ILLIDARI_ELITE) - eliteList.push_back(unit); + _events.Reset(); + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + me->SetFacingToObject(illidan); + Talk(SAY_MAIEV_SHADOWSONG_FINISHED); + _events.ScheduleEvent(EVENT_MAIEV_OUTRO_TEXT, Seconds(28)); } - for (std::vector<Unit*>::const_iterator itr = eliteList.begin(); itr != eliteList.end(); ++itr) - (*itr)->setDeathState(JUST_DIED); - EnterEvadeMode(EVADE_REASON_OTHER); + else if (actionId == ACTION_MAIEV_DOWN_FADE) + _canDown = true; } - void BeginTalk() + void DamageTaken(Unit* /*who*/, uint32 &damage) override { - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) + if (damage >= me->GetHealth() && _canDown) { - illidan->RemoveAurasDueToSpell(SPELL_KNEEL); - me->SetInFront(illidan); - illidan->SetInFront(me); - me->GetMotionMaster()->MoveIdle(); - illidan->GetMotionMaster()->MoveIdle(); - ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->AkamaGUID = me->GetGUID(); - ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->EnterPhase(PHASE_TALK_SEQUENCE); + damage = me->GetHealth() - 1; + _canDown = false; + DoCastSelf(SPELL_MAIEV_DOWN, true); + Talk(SAY_MAIEV_SHADOWSONG_DOWN, me); } } - void BeginChannel() + void UpdateAI(uint32 diff) override { - me->setActive(true); - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - if (!JustCreated) + if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO)) return; - float x, y, z; - if (GameObject* gate = ObjectAccessor::GetGameObject(*me, GateGUID)) - gate->GetPosition(x, y, z); - else - return; // if door not spawned, don't crash server - if (Creature* Channel = me->SummonCreature(ILLIDAN_DOOR_TRIGGER, x, y, z+5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000)) - { - ChannelGUID = Channel->GetGUID(); - Channel->SetDisplayId(MODEL_INVISIBLE); // Invisible but spell visuals can still be seen. - DoCast(Channel, SPELL_AKAMA_DOOR_FAIL); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - for (uint8 i = 0; i < 2; ++i) - if (Creature* Spirit = me->SummonCreature(i ? SPIRIT_OF_OLUM : SPIRIT_OF_UDALO, SpiritSpawns[i].x, SpiritSpawns[i].y, SpiritSpawns[i].z, 0, TEMPSUMMON_TIMED_DESPAWN, 20000)) + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - Spirit->SetVisible(false); - SpiritGUID[i] = Spirit->GetGUID(); + case EVENT_MAIEV_APPEAR: + Talk(SAY_MAIEV_SHADOWSONG_APPEAR); + break; + case EVENT_MAIEV_EXCLAMATION: + me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + break; + case EVENT_MAIEV_JUSTICE_TEXT: + Talk(SAY_MAIEV_SHADOWSONG_JUSTICE); + _events.ScheduleEvent(EVENT_MAIEV_YES, Seconds(2)); + break; + case EVENT_MAIEV_YES: + me->HandleEmoteCommand(EMOTE_ONESHOT_YES); + _events.ScheduleEvent(EVENT_MAIEV_ROAR, Seconds(3)); + break; + case EVENT_MAIEV_ROAR: + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + _events.ScheduleEvent(EVENT_MAIEV_COMBAT, Seconds(3)); + break; + case EVENT_MAIEV_COMBAT: + me->SetReactState(REACT_AGGRESSIVE); + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + AttackStart(illidan); + break; + case EVENT_CAGE_TRAP: + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->CastSpell(illidan, SPELL_CAGED_TRAP_TELEPORT, true); + DoCastSelf(SPELL_CAGE_TRAP_SUMMON); + Talk(SAY_MAIEV_SHADOWSONG_TRAP); + _events.Repeat(Seconds(30)); + break; + case EVENT_SHADOW_STRIKE: + DoCastVictim(SPELL_SHADOW_STRIKE); + _events.Repeat(Seconds(50)); + break; + case EVENT_THROW_DAGGER: + if (Unit* target = me->GetVictim()) + if (!me->IsWithinMeleeRange(target)) + { + DoCastVictim(SPELL_THROW_DAGGER); + _events.Repeat(Seconds(5)); + break; + } + _events.Repeat(Seconds(1)); + break; + case EVENT_TAUNT: + Talk(SAY_MAIEV_SHADOWSONG_TAUNT); + _events.Repeat(Seconds(30), Seconds(60)); + break; + case EVENT_MAIEV_OUTRO_TEXT: + Talk(SAY_MAIEV_SHADOWSONG_OUTRO); + _events.ScheduleEvent(EVENT_MAIEV_FAREWELL_TEXT, Seconds(11)); + break; + case EVENT_MAIEV_FAREWELL_TEXT: + Talk(SAY_MAIEV_SHADOWSONG_FAREWELL); + _events.ScheduleEvent(EVENT_MAIEV_TELEPORT_DESPAWN, Seconds(3)); + break; + case EVENT_MAIEV_TELEPORT_DESPAWN: + DoCastSelf(SPELL_TELEPORT_VISUAL); + me->DespawnOrUnsummon(Seconds(1)); + break; + default: + break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); + } + + private: + EventMap _events; + InstanceScript* _instance; + bool _canDown; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetBlackTempleAI<npc_maievAI>(creature); + } +}; + +class npc_cage_trap_trigger : public CreatureScript +{ +public: + npc_cage_trap_trigger() : CreatureScript("npc_cage_trap_trigger") { } + + struct npc_cage_trap_triggerAI : public PassiveAI + { + npc_cage_trap_triggerAI(Creature* creature) : PassiveAI(creature) { } + + void Reset() override + { + _scheduler.Schedule(Seconds(1), [this](TaskContext checkTarget) + { + DoCastSelf(SPELL_CAGE_TRAP_PERIODIC); + checkTarget.Repeat(); + }); } - void BeginWalk() + void UpdateAI(uint32 diff) override { - me->SetWalk(false); - me->SetSpeedRate(MOVE_RUN, 1.0f); - me->GetMotionMaster()->MovePoint(0, AkamaWP[WalkCount].x, AkamaWP[WalkCount].y, AkamaWP[WalkCount].z); + _scheduler.Update(diff); } - void EnterPhase(PhaseAkama NextPhase) + private: + TaskScheduler _scheduler; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetBlackTempleAI<npc_cage_trap_triggerAI>(creature); + } +}; + +// 41077 - Akama Teleport +class spell_illidan_akama_teleport : public SpellScriptLoader +{ + public: + spell_illidan_akama_teleport() : SpellScriptLoader("spell_illidan_akama_teleport") { } + + class spell_illidan_akama_teleport_SpellScript : public SpellScript { - switch (NextPhase) + PrepareSpellScript(spell_illidan_akama_teleport_SpellScript); + + void SetDest(SpellDestination& dest) { - case PHASE_CHANNEL: - BeginChannel(); - Timer = 5000; - ChannelCount = 0; - break; - case PHASE_WALK: - if (Phase == PHASE_CHANNEL) - WalkCount = 0; - else if (Phase == PHASE_TALK) - { - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) - ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->DeleteFromThreatList(me->GetGUID()); - EnterEvadeMode(EVADE_REASON_OTHER); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - ++WalkCount; - } - JustCreated = false; - BeginWalk(); - Timer = 0; - break; - case PHASE_TALK: - if (Phase == PHASE_WALK) - { - BeginTalk(); - Timer = 0; - } - else if (Phase == PHASE_FIGHT_ILLIDAN) - { - Timer = 1; - TalkCount = 0; - } - break; - case PHASE_FIGHT_ILLIDAN: - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) + if (Creature* caster = GetCaster()->ToCreature()) { - me->AddThreat(illidan, 10000000.0f); - me->GetMotionMaster()->MoveChase(illidan); + uint32 destination = caster->AI()->GetData(DATA_AKAMA_TELEPORT_POSITION); + dest.Relocate(AkamaTeleportPositions[destination]); } - Timer = 30000; // chain lightning - break; - case PHASE_FIGHT_MINIONS: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - Timer = urand(10000, 16000); // summon minion - break; - case PHASE_RETURN: - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - KillAllElites(); - WalkCount = 0; - BeginWalk(); - Timer = 1; - break; - default: - break; - } - Phase = NextPhase; - } - - void HandleTalkSequence() - { - switch (TalkCount) - { - case 0: - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) - { - ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->Timer[EVENT_TAUNT] += 30000; - illidan->AI()->Talk(SAY_ILLIDAN_MINION); - } - Timer = 8000; - break; - case 1: - Talk(SAY_AKAMA_LEAVE); - Timer = 3000; - break; - case 2: - EnterPhase(PHASE_WALK); - break; } - ++TalkCount; + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_illidan_akama_teleport_SpellScript::SetDest, EFFECT_0, TARGET_DEST_NEARBY_ENTRY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_illidan_akama_teleport_SpellScript(); } +}; - void HandleChannelSequence() +// 41268 - Quest - Black Temple - Akama - Door Open +class spell_illidan_akama_door_channel : public SpellScriptLoader +{ + public: + spell_illidan_akama_door_channel() : SpellScriptLoader("spell_illidan_akama_door_channel") { } + + class spell_illidan_akama_door_channel_AuraScript : public AuraScript { - Unit* Channel = NULL; - Unit* Spirit[2] = { NULL, NULL }; - if (ChannelCount <= 5) + PrepareAuraScript(spell_illidan_akama_door_channel_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override { - Channel = ObjectAccessor::GetUnit(*me, ChannelGUID); - Spirit[0] = ObjectAccessor::GetUnit(*me, SpiritGUID[0]); - Spirit[1] = ObjectAccessor::GetUnit(*me, SpiritGUID[1]); - if (!Channel || !Spirit[0] || !Spirit[1]) - return; + if (!sSpellMgr->GetSpellInfo(SPELL_ARCANE_EXPLOSION)) + return false; + return true; } - switch (ChannelCount) - { - case 0: // channel failed - me->InterruptNonMeleeSpells(true); - Timer = 2000; - break; - case 1: // spirit appear - Spirit[0]->SetVisible(true); - Spirit[1]->SetVisible(true); - Timer = 2000; - break; - case 2: // spirit help - DoCast(Channel, SPELL_AKAMA_DOOR_CHANNEL); - Spirit[0]->CastSpell(Channel, SPELL_DEATHSWORN_DOOR_CHANNEL, false); - Spirit[1]->CastSpell(Channel, SPELL_DEATHSWORN_DOOR_CHANNEL, false); - Timer = 5000; - break; - case 3: // open the gate - me->InterruptNonMeleeSpells(true); - Spirit[0]->InterruptNonMeleeSpells(true); - Spirit[1]->InterruptNonMeleeSpells(true); - instance->HandleGameObject(GateGUID, true); - Timer = 2000; - break; - case 4: - me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); - Timer = 2000; - break; - case 5: - Talk(SAY_AKAMA_BEWARE); - Channel->setDeathState(JUST_DIED); - Spirit[0]->SetVisible(false); - Spirit[1]->SetVisible(false); - Timer = 3000; - break; - case 6: - EnterPhase(PHASE_WALK); - break; - default: - break; - } - ++ChannelCount; - } - - void HandleWalkSequence() - { - switch (WalkCount) - { - case 8: - if (Phase == PHASE_WALK) - EnterPhase(PHASE_TALK); - else - EnterPhase(PHASE_FIGHT_ILLIDAN); - break; - case 12: - EnterPhase(PHASE_FIGHT_MINIONS); - break; + void OnRemoveDummy(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_ARCANE_EXPLOSION, true); + + if (InstanceScript* instance = target->GetInstanceScript()) + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_OPEN_DOOR); } - if (Phase == PHASE_WALK) + void Register() override { - Timer = 0; - ++WalkCount; - me->GetMotionMaster()->MovePoint(WalkCount, AkamaWP[WalkCount].x, AkamaWP[WalkCount].y, AkamaWP[WalkCount].z); + AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_akama_door_channel_AuraScript::OnRemoveDummy, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_illidan_akama_door_channel_AuraScript(); } +}; - void UpdateAI(uint32 diff) override +// 40904 - Draw Soul +class spell_illidan_draw_soul : public SpellScriptLoader +{ + public: + spell_illidan_draw_soul() : SpellScriptLoader("spell_illidan_draw_soul") { } + + class spell_illidan_draw_soul_SpellScript : public SpellScript { - if (!me->IsVisible()) + PrepareSpellScript(spell_illidan_draw_soul_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override { - if (Check_Timer <= diff) - { - if (instance->GetBossState(DATA_ILLIDARI_COUNCIL) == DONE) - me->SetVisible(true); + if (!sSpellMgr->GetSpellInfo(SPELL_DRAW_SOUL_HEAL)) + return false; + return true; + } - Check_Timer = 5000; - } else Check_Timer -= diff; + void HandleScriptEffect(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetHitUnit()->CastSpell(GetCaster(), SPELL_DRAW_SOUL_HEAL, true); } - bool Event = false; - if (Timer) + + void Register() override { - if (Timer <= diff) - Event = true; - else Timer -= diff; + OnEffectHitTarget += SpellEffectFn(spell_illidan_draw_soul_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_illidan_draw_soul_SpellScript(); + } +}; + +/* 41917 - Parasitic Shadowfiend + 41914 - Parasitic Shadowfiend */ +class spell_illidan_parasitic_shadowfiend : public SpellScriptLoader +{ + public: + spell_illidan_parasitic_shadowfiend() : SpellScriptLoader("spell_illidan_parasitic_shadowfiend") { } + + class spell_illidan_parasitic_shadowfiend_AuraScript : public AuraScript + { + PrepareAuraScript(spell_illidan_parasitic_shadowfiend_AuraScript); - if (Event) + bool Validate(SpellInfo const* /*spellInfo*/) override { - switch (Phase) - { - case PHASE_CHANNEL: - if (JustCreated) - HandleChannelSequence(); - else{ - EnterPhase(PHASE_WALK); - } - break; - case PHASE_TALK: - HandleTalkSequence(); - break; - case PHASE_WALK: - case PHASE_RETURN: - HandleWalkSequence(); - break; - case PHASE_FIGHT_ILLIDAN: - { - Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID); - if (illidan && illidan->HealthBelowPct(90)) - EnterPhase(PHASE_TALK); - else - { - DoCastVictim(SPELL_CHAIN_LIGHTNING); - Timer = 30000; - } - } - break; - case PHASE_FIGHT_MINIONS: - { - float x, y, z; - me->GetPosition(x, y, z); - Creature* Elite = me->SummonCreature(ILLIDARI_ELITE, x + rand32() % 10, y + rand32() % 10, z, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000); - // Creature* Elite = me->SummonCreature(ILLIDARI_ELITE, x, y, z, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000); - if (Elite) - { - Elite->AI()->AttackStart(me); - Elite->AddThreat(me, 1000000.0f); - AttackStart(Elite); - me->AddThreat(Elite, 1000000.0f); - } - Timer = urand(10000, 16000); - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) - if (illidan->HealthBelowPct(10)) - EnterPhase(PHASE_RETURN); - } - break; - default: - break; - } + if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_PARASITIC_SHADOWFIENDS)) + return false; + return true; } - if (!UpdateVictim()) - return; + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_SUMMON_PARASITIC_SHADOWFIENDS, true); + } - if (HealthBelowPct(20)) - DoCast(me, SPELL_HEALING_POTION); + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_parasitic_shadowfiend_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } + }; - DoMeleeAttackIfReady(); + AuraScript* GetAuraScript() const override + { + return new spell_illidan_parasitic_shadowfiend_AuraScript(); } +}; - void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override +/* 39635 - Throw Glaive + 39849 - Throw Glaive */ +class spell_illidan_throw_warglaive : public SpellScriptLoader +{ + public: + spell_illidan_throw_warglaive() : SpellScriptLoader("spell_illidan_throw_warglaive") { } + + class spell_illidan_throw_warglaive_SpellScript : public SpellScript { - CloseGossipMenuFor(player); - EnterPhase(PHASE_CHANNEL); - } + PrepareSpellScript(spell_illidan_throw_warglaive_SpellScript); - private: - bool JustCreated; - InstanceScript* instance; - PhaseAkama Phase; - uint32 Timer; - ObjectGuid IllidanGUID; - ObjectGuid ChannelGUID; - ObjectGuid SpiritGUID[2]; - ObjectGuid GateGUID; - uint32 ChannelCount; - uint32 WalkCount; - uint32 TalkCount; - uint32 Check_Timer; - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + target->m_Events.AddEvent(new SummonWarglaiveEvent(target), target->m_Events.CalculateTime(1000)); + } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_akama_illidanAI>(creature); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_throw_warglaive_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_illidan_throw_warglaive_SpellScript(); + } }; -void boss_illidan_stormrage::boss_illidan_stormrageAI::Reset() +// 39857 - Tear of Azzinoth Summon Channel +class spell_illidan_tear_of_azzinoth_channel : public SpellScriptLoader { - _Reset(); + public: + spell_illidan_tear_of_azzinoth_channel() : SpellScriptLoader("spell_illidan_tear_of_azzinoth_channel") { } - if (Creature* akama = ObjectAccessor::GetCreature(*me, AkamaGUID)) - { - if (!akama->IsAlive()) - akama->Respawn(); - else - akama->AI()->EnterEvadeMode(); - } + class spell_illidan_tear_of_azzinoth_channel_AuraScript : public AuraScript + { + PrepareAuraScript(spell_illidan_tear_of_azzinoth_channel_AuraScript); - Initialize(); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_UNCAGED_WRATH)) + return false; + return true; + } - me->SetDisplayId(MODEL_ILLIDAN); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); - me->SetDisableGravity(false); - me->setActive(false); -} + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + if (Unit* caster = GetCaster()) + { + Unit* target = GetTarget(); + if (caster->GetDistance2d(target) > 25.0f) + { + target->CastSpell(target, SPELL_UNCAGED_WRATH, true); + Remove(); + } + } + } -void boss_illidan_stormrage::boss_illidan_stormrageAI::JustSummoned(Creature* summon) -{ - summons.Summon(summon); - switch (summon->GetEntry()) - { - case PARASITIC_SHADOWFIEND: - { - if (Phase == PHASE_TALK_SEQUENCE) + void Register() override { - summon->SetVisible(false); - summon->setDeathState(JUST_DIED); - return; + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_tear_of_azzinoth_channel_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); } - Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0, 999, true); - if (!target || target->HasAura(SPELL_PARASITIC_SHADOWFIEND) - || target->HasAura(SPELL_PARASITIC_SHADOWFIEND2)) - target = SelectTarget(SELECT_TARGET_RANDOM, 0, 999, true); - if (target) - summon->AI()->AttackStart(target); - } - break; - case SHADOW_DEMON: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 999, true)) // only on players. + }; + + AuraScript* GetAuraScript() const override { - summon->AddThreat(target, 5000000.0f); - summon->AI()->AttackStart(target); + return new spell_illidan_tear_of_azzinoth_channel_AuraScript(); } - break; - case MAIEV_SHADOWSONG: +}; + +// 40631 - Flame Blast +class spell_illidan_flame_blast : public SpellScriptLoader +{ + public: + spell_illidan_flame_blast() : SpellScriptLoader("spell_illidan_flame_blast") { } + + class spell_illidan_flame_blast_SpellScript : public SpellScript { - summon->SetVisible(false); // Leave her invisible until she has to talk - summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - MaievGUID = summon->GetGUID(); - ENSURE_AI(boss_maiev_shadowsong::boss_maievAI, summon->AI())->GetIllidanGUID(me->GetGUID()); - summon->AI()->DoAction(PHASE_TALK_SEQUENCE); - } - break; - case FLAME_OF_AZZINOTH: + PrepareSpellScript(spell_illidan_flame_blast_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_BLAZE_SUMMON)) + return false; + return true; + } + + void HandleBlaze(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + target->CastSpell(target, SPELL_BLAZE_SUMMON, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_blast_SpellScript::HandleBlaze, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } + }; + + SpellScript* GetSpellScript() const override { - summon->AI()->AttackStart(summon->SelectNearestTarget(999)); + return new spell_illidan_flame_blast_SpellScript(); } - break; - default: - break; - } -} +}; -void boss_illidan_stormrage::boss_illidan_stormrageAI::HandleTalkSequence() +// 39873 - Glaive Returns +class spell_illidan_return_glaives : public SpellScriptLoader { - switch (TalkCount) - { - case 0: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - break; - case 8: - // Equip our warglaives! - SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_ID_OFF_HAND, EQUIP_NO_CHANGE); - me->SetSheath(SHEATH_STATE_MELEE); - me->SetWalk(false); - break; - case 9: - if (Creature* akama = ObjectAccessor::GetCreature(*me, AkamaGUID)) - { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - me->AddThreat(akama, 100.0f); - ENSURE_AI(npc_akama_illidan::npc_akama_illidanAI, akama->AI())->EnterPhase(PHASE_FIGHT_ILLIDAN); - EnterPhase(PHASE_NORMAL); - } - break; - case 10: - SummonMaiev(); - break; - case 11: - if (Creature* maiev = ObjectAccessor::GetCreature(*me, MaievGUID)) - { - maiev->SetVisible(true); // Maiev is now visible - maiev->CastSpell(maiev, SPELL_TELEPORT_VISUAL, true); // onoz she looks like she teleported! - maiev->SetInFront(me); // Have her face us - me->SetInFront(maiev); // Face her, so it's not rude =P - maiev->GetMotionMaster()->MoveIdle(); - me->GetMotionMaster()->MoveIdle(); - } - break; - case 14: - if (Creature* maiev = ObjectAccessor::GetCreature(*me, MaievGUID)) - { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - maiev->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - maiev->AddThreat(me, 10000000.0f); // Have Maiev add a lot of threat on us so that players don't pull her off if they damage her via AOE - maiev->AI()->AttackStart(me); // Force Maiev to attack us. - EnterPhase(PHASE_NORMAL_MAIEV); - } - break; - case 15: - DoCast(me, SPELL_DEATH); // Animate his kneeling + stun him - summons.DespawnAll(); - break; - case 17: - if (Creature* akama = ObjectAccessor::GetCreature(*me, AkamaGUID)) - { - if (!me->IsWithinDistInMap(akama, 15)) - { - float x, y, z; - me->GetPosition(x, y, z); - x += 10; y += 10; - akama->GetMotionMaster()->Clear(false); - // Akama->GetMotionMaster()->MoveIdle(); - akama->SetPosition(x, y, z, 0.0f); - akama->MonsterMoveWithSpeed(x, y, z, 0); // Illidan must not die until Akama arrives. - akama->GetMotionMaster()->MoveChase(me); - } - } - break; - case 19: // Make Maiev leave - if (Creature* maiev = ObjectAccessor::GetCreature(*me, MaievGUID)) - { - maiev->CastSpell(maiev, SPELL_TELEPORT_VISUAL, true); - maiev->setDeathState(JUST_DIED); - me->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_DEAD); - } - break; - case 21: // Kill ourself. - me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - break; - default: - break; - } - if (Phase == PHASE_TALK_SEQUENCE) - Talk(TalkCount); // This function does most of the talking - ++TalkCount; -} + public: spell_illidan_return_glaives() : SpellScriptLoader("spell_illidan_return_glaives") { } -class npc_cage_trap_trigger : public CreatureScript -{ -public: - npc_cage_trap_trigger() : CreatureScript("npc_cage_trap_trigger") { } + class spell_illidan_return_glaives_SpellScript : public SpellScript + { + PrepareSpellScript(spell_illidan_return_glaives_SpellScript); - struct cage_trap_triggerAI : public ScriptedAI - { - cage_trap_triggerAI(Creature* creature) : ScriptedAI(creature) + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->SendPlaySpellVisual(SPELL_GLAIVE_VISUAL_KIT); + if (Creature* caster = GetCaster()->ToCreature()) + caster->DespawnOrUnsummon(); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_return_glaives_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override { - Initialize(); + return new spell_illidan_return_glaives_SpellScript(); } +}; + +// 40834 - Agonizing Flames +class spell_illidan_agonizing_flames : public SpellScriptLoader +{ + public: + spell_illidan_agonizing_flames() : SpellScriptLoader("spell_illidan_agonizing_flames") { } - void Initialize() + class spell_illidan_agonizing_flames_SpellScript : public SpellScript { - IllidanGUID.Clear(); + PrepareSpellScript(spell_illidan_agonizing_flames_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_AGONIZING_FLAMES)) + return false; + return true; + } - Active = false; - SummonedBeams = false; + void FilterTargets(std::list<WorldObject*>& targets) + { + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } - DespawnTimer = 0; + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_AGONIZING_FLAMES, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_agonizing_flames_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_illidan_agonizing_flames_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_illidan_agonizing_flames_SpellScript(); } +}; - void Reset() override +// 40511 - Demon Transform 1 +class spell_illidan_demon_transform1 : public SpellScriptLoader +{ + public: + spell_illidan_demon_transform1() : SpellScriptLoader("spell_illidan_demon_transform1") { } + + class spell_illidan_demon_transform1_AuraScript : public AuraScript { - Initialize(); + PrepareAuraScript(spell_illidan_demon_transform1_AuraScript); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DEMON_TRANSFORM_2)) + return false; + return true; + } - void EnterCombat(Unit* /*who*/) override { } + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_DEMON_TRANSFORM_2, true); + Remove(); + } - void MoveInLineOfSight(Unit* who) override + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform1_AuraScript::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + AuraScript* GetAuraScript() const override { - if (!Active) - return; + return new spell_illidan_demon_transform1_AuraScript(); + } +}; - if (who && (who->GetTypeId() != TYPEID_PLAYER)) +// 40398 - Demon Transform 2 +class spell_illidan_demon_transform2 : public SpellScriptLoader +{ + public: + spell_illidan_demon_transform2() : SpellScriptLoader("spell_illidan_demon_transform2") { } + + class spell_illidan_demon_transform2_AuraScript : public AuraScript + { + PrepareAuraScript(spell_illidan_demon_transform2_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override { - if (who->GetEntry() == ILLIDAN_STORMRAGE) // Check if who is Illidan + if (!sSpellMgr->GetSpellInfo(SPELL_DEMON_FORM) + || !sSpellMgr->GetSpellInfo(SPELL_DEMON_TRANSFORM_3)) + return false; + return true; + } + + void OnPeriodic(AuraEffect const* aurEff) + { + PreventDefaultAction(); + Unit* target = GetTarget(); + + if (aurEff->GetTickNumber() == 1) { - if (!IllidanGUID && me->IsWithinDistInMap(who, 3) && (!who->HasAura(SPELL_CAGED))) - { - IllidanGUID = who->GetGUID(); - who->CastSpell(who, SPELL_CAGED, true); - DespawnTimer = 5000; - if (who->HasAura(SPELL_ENRAGE)) - who->RemoveAurasDueToSpell(SPELL_ENRAGE); // Dispel his enrage - // if (GameObject* CageTrap = instance->instance->GetGameObject(instance->GetGuidData(CageTrapGUID))) - - // CageTrap->SetLootState(GO_JUST_DEACTIVATED); - } + if (target->GetDisplayId() == target->GetNativeDisplayId()) + target->CastSpell(target, SPELL_DEMON_FORM, true); + else + target->RemoveAurasDueToSpell(SPELL_DEMON_FORM); + } + else if (aurEff->GetTickNumber() == 2) + { + target->CastSpell(target, SPELL_DEMON_TRANSFORM_3, true); + if (Aura* aura = GetUnitOwner()->GetAura(SPELL_DEMON_TRANSFORM_3)) + aura->SetDuration(4300); + Remove(); } } - } - void UpdateAI(uint32 diff) override - { - if (DespawnTimer) + void Register() override { - if (DespawnTimer <= diff) - me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - else DespawnTimer -= diff; + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform2_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); } + }; - // if (IllidanGUID && !SummonedBeams) - // { - // if (Unit* Illidan = ObjectAccessor::GetUnit(*me, IllidanGUID) - // { - // /// @todo Find proper spells and properly apply 'caged' Illidan effect - // } - // } + AuraScript* GetAuraScript() const override + { + return new spell_illidan_demon_transform2_AuraScript(); } +}; +// 41126 - Flame Burst +class spell_illidan_flame_burst : public SpellScriptLoader +{ public: - bool Active; - private: - ObjectGuid IllidanGUID; - uint32 DespawnTimer; - bool SummonedBeams; - }; + spell_illidan_flame_burst() : SpellScriptLoader("spell_illidan_flame_burst") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new cage_trap_triggerAI(creature); - } -}; + class spell_illidan_flame_burst_SpellScript : public SpellScript + { + PrepareSpellScript(spell_illidan_flame_burst_SpellScript); -class gameobject_cage_trap : public GameObjectScript -{ -public: - gameobject_cage_trap() : GameObjectScript("gameobject_cage_trap") { } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_FLAME_BURST_EFFECT)) + return false; + return true; + } - bool OnGossipHello(Player* player, GameObject* go) override - { - float x, y, z; - player->GetPosition(x, y, z); + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_FLAME_BURST_EFFECT, true); + } - // Grid search for nearest live Creature of entry 23304 within 10 yards - if (Creature* pTrigger = go->FindNearestCreature(23304, 10.0f)) - ENSURE_AI(npc_cage_trap_trigger::cage_trap_triggerAI, pTrigger->AI())->Active = true; - go->SetGoState(GO_STATE_ACTIVE); - return true; - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_burst_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_illidan_flame_burst_SpellScript(); + } }; -class npc_shadow_demon : public CreatureScript +// 41081 - Find Target +class spell_illidan_find_target : public SpellScriptLoader { -public: - npc_shadow_demon() : CreatureScript("npc_shadow_demon") { } + public: + spell_illidan_find_target() : SpellScriptLoader("spell_illidan_find_target") { } - struct shadow_demonAI : public ScriptedAI - { - shadow_demonAI(Creature* creature) : ScriptedAI(creature) { } + class spell_illidan_find_target_SpellScript : public SpellScript + { + PrepareSpellScript(spell_illidan_find_target_SpellScript); - void EnterCombat(Unit* /*who*/) override + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PARALYZE)) + return false; + return true; + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_PARALYZE)); + + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + if (Creature* caster = GetCaster()->ToCreature()) + { + caster->CastSpell(target, SPELL_PARALYZE, true); + caster->ClearUnitState(UNIT_STATE_CASTING); + caster->AI()->SetGUID(target->GetGUID(), 0); + } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_find_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_illidan_find_target_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override { - DoZoneInCombat(); + return new spell_illidan_find_target_SpellScript(); } +}; - void Reset() override +// 39908 - Eye Blast +class spell_illidan_eye_blast : public SpellScriptLoader +{ + public: + spell_illidan_eye_blast() : SpellScriptLoader("spell_illidan_eye_blast") { } + + class spell_illidan_eye_blast_AuraScript : public AuraScript { - TargetGUID.Clear(); - DoCast(me, SPELL_SHADOW_DEMON_PASSIVE, true); - } + PrepareAuraScript(spell_illidan_eye_blast_AuraScript); - void JustDied(Unit* /*killer*/) override + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + target->DespawnOrUnsummon(); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_eye_blast_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override { - if (Unit* target = ObjectAccessor::GetUnit(*me, TargetGUID)) - target->RemoveAurasDueToSpell(SPELL_PARALYZE); + return new spell_illidan_eye_blast_AuraScript(); } +}; - void UpdateAI(uint32 /*diff*/) override - { - if (!UpdateVictim() || !me->GetVictim()) - return; +// 40761 - Cage Trap +class spell_illidan_cage_trap : public SpellScriptLoader +{ + public: + spell_illidan_cage_trap() : SpellScriptLoader("spell_illidan_cage_trap") { } - if (me->EnsureVictim()->GetTypeId() != TYPEID_PLAYER) - return; // Only cast the below on players. + class spell_illidan_cage_trap_SpellScript : public SpellScript + { + PrepareSpellScript(spell_illidan_cage_trap_SpellScript); - if (!me->EnsureVictim()->HasAura(SPELL_PARALYZE)) + void HandleScriptEffect(SpellEffIndex /*effIndex*/) { - TargetGUID = me->EnsureVictim()->GetGUID(); - me->AddThreat(me->GetVictim(), 10000000.0f); - DoCastVictim(SPELL_PURPLE_BEAM, true); - DoCastVictim(SPELL_PARALYZE, true); + Creature* target = GetHitCreature(); + Creature* caster = GetCaster()->ToCreature(); + + if (!target || !caster) + return; + + if (caster->GetDistance2d(target) < 4.0f) + { + target->AI()->DoAction(ACTION_ILLIDAN_CAGED); + caster->DespawnOrUnsummon(); + if (GameObject* trap = target->FindNearestGameObject(GO_ILLIDAN_CAGE_TRAP, 10.0f)) + trap->UseDoorOrButton(); + } } - // Kill our target if we're very close. - if (me->IsWithinDistInMap(me->GetVictim(), 3)) - DoCastVictim(SPELL_CONSUME_SOUL); - } - private: - ObjectGuid TargetGUID; - }; + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_cage_trap_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; - CreatureAI* GetAI(Creature* creature) const override - { - return new shadow_demonAI(creature); - } + SpellScript* GetSpellScript() const override + { + return new spell_illidan_cage_trap_SpellScript(); + } }; -class npc_blade_of_azzinoth : public CreatureScript +// 40760 - Cage Trap +class spell_illidan_caged : public SpellScriptLoader { -public: - npc_blade_of_azzinoth() : CreatureScript("npc_blade_of_azzinoth") { } + public: + spell_illidan_caged() : SpellScriptLoader("spell_illidan_caged") { } - struct blade_of_azzinothAI : public NullCreatureAI - { - blade_of_azzinothAI(Creature* creature) : NullCreatureAI(creature) { } + class spell_illidan_caged_AuraScript : public AuraScript + { + PrepareAuraScript(spell_illidan_caged_AuraScript); - void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_CAGED_DEBUFF)) + return false; + return true; + } + + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_CAGED_DEBUFF, true); + Remove(); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_caged_AuraScript::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override { - if (spell->Id == SPELL_THROW_GLAIVE2 || spell->Id == SPELL_THROW_GLAIVE) - me->SetDisplayId(MODEL_BLADE);// appear when hit by Illidan's glaive + return new spell_illidan_caged_AuraScript(); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new blade_of_azzinothAI(creature); - } }; -class npc_parasitic_shadowfiend : public CreatureScript +// 40409 - Maiev Down +class spell_maiev_down : public SpellScriptLoader { public: - npc_parasitic_shadowfiend() : CreatureScript("npc_parasitic_shadowfiend") { } + spell_maiev_down() : SpellScriptLoader("spell_maiev_down") { } - // Shadowfiends interact with Illidan, setting more targets in Illidan's hashmap - struct npc_parasitic_shadowfiendAI : public ScriptedAI + class spell_maiev_down_AuraScript : public AuraScript { - npc_parasitic_shadowfiendAI(Creature* creature) : ScriptedAI(creature) + PrepareAuraScript(spell_maiev_down_AuraScript); + + bool Load() override { - Initialize(); - instance = creature->GetInstanceScript(); + return GetUnitOwner()->GetTypeId() == TYPEID_UNIT; } - void Initialize() + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - CheckTimer = 5000; + GetTarget()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } - void Reset() override + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - IllidanGUID = instance->GetGuidData(DATA_ILLIDAN_STORMRAGE); - - Initialize(); - DoCast(me, SPELL_SHADOWFIEND_PASSIVE, true); + GetTarget()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + GetTarget()->GetAI()->DoAction(ACTION_MAIEV_DOWN_FADE); } - void EnterCombat(Unit* /*who*/) override + void Register() override { - DoZoneInCombat(); + OnEffectApply += AuraEffectApplyFn(spell_maiev_down_AuraScript::HandleEffectApply, EFFECT_1, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_maiev_down_AuraScript::HandleEffectRemove, EFFECT_1, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_maiev_down_AuraScript(); + } +}; - void DoMeleeAttackIfReady() +// 40693 - Cage Trap +class spell_illidan_cage_teleport : public SpellScriptLoader +{ + public: + spell_illidan_cage_teleport() : SpellScriptLoader("spell_illidan_cage_teleport") { } + + class spell_illidan_cage_teleport_SpellScript : public SpellScript { - if (me->isAttackReady() && me->IsWithinMeleeRange(me->GetVictim())) + PrepareSpellScript(spell_illidan_cage_teleport_SpellScript); + + void SetDest(SpellDestination& dest) { - if (!me->EnsureVictim()->HasAura(SPELL_PARASITIC_SHADOWFIEND) - && !me->EnsureVictim()->HasAura(SPELL_PARASITIC_SHADOWFIEND2)) - { - if (Creature* illidan = ObjectAccessor::GetCreature((*me), IllidanGUID))// summon only in 1. phase - if (ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->Phase == PHASE_NORMAL) - me->CastSpell(me->GetVictim(), SPELL_PARASITIC_SHADOWFIEND2, true, 0, 0, IllidanGUID); // do not stack - } - me->AttackerStateUpdate(me->GetVictim()); - me->resetAttackTimer(); + Position offset = { 0.0f, 0.0f, GetCaster()->GetPositionZ(), 0.0f }; + dest.RelocateOffset(offset); + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_illidan_cage_teleport_SpellScript::SetDest, EFFECT_0, TARGET_DEST_CASTER_RADIUS); } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_illidan_cage_teleport_SpellScript(); } +}; - void UpdateAI(uint32 diff) override +// 41242 - Akama Despawn +class spell_illidan_despawn_akama : public SpellScriptLoader +{ + public: + spell_illidan_despawn_akama() : SpellScriptLoader("spell_illidan_despawn_akama") { } + + class spell_illidan_despawn_akama_SpellScript : public SpellScript { - if (!me->GetVictim()) + PrepareSpellScript(spell_illidan_despawn_akama_SpellScript); + + void HandleDummy(SpellEffIndex /*effIndex*/) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 999, true)) - AttackStart(target); - else - { - me->DespawnOrUnsummon(); - return; - } + if (Creature* target = GetHitCreature()) + target->DespawnOrUnsummon(Seconds(1)); } - if (CheckTimer <= diff) + void Register() override { - Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID); - if (!illidan || illidan->IsInEvadeMode()) - { - me->DespawnOrUnsummon(); - return; - } - else - CheckTimer = 5000; + OnEffectHitTarget += SpellEffectFn(spell_illidan_despawn_akama_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } - else - CheckTimer -= diff; + }; - DoMeleeAttackIfReady(); + SpellScript* GetSpellScript() const override + { + return new spell_illidan_despawn_akama_SpellScript(); } - - private: - InstanceScript* instance; - ObjectGuid IllidanGUID; - uint32 CheckTimer; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_parasitic_shadowfiendAI>(creature); - } }; void AddSC_boss_illidan() { new boss_illidan_stormrage(); - new npc_akama_illidan(); - new boss_maiev_shadowsong(); - new npc_flame_of_azzinoth(); + new npc_akama(); + new npc_parasitic_shadowfiend(); new npc_blade_of_azzinoth(); - new gameobject_cage_trap(); + new npc_flame_of_azzinoth(); + new npc_illidan_db_target(); + new npc_maiev(); + new npc_illidan_shadow_demon(); new npc_cage_trap_trigger(); - new npc_shadow_demon(); - new npc_parasitic_shadowfiend(); + new spell_illidan_akama_teleport(); + new spell_illidan_akama_door_channel(); + new spell_illidan_draw_soul(); + new spell_illidan_parasitic_shadowfiend(); + new spell_illidan_throw_warglaive(); + new spell_illidan_tear_of_azzinoth_channel(); + new spell_illidan_flame_blast(); + new spell_illidan_return_glaives(); + new spell_illidan_agonizing_flames(); + new spell_illidan_demon_transform1(); + new spell_illidan_demon_transform2(); + new spell_illidan_flame_burst(); + new spell_illidan_find_target(); + new spell_illidan_eye_blast(); + new spell_illidan_cage_trap(); + new spell_illidan_caged(); + new spell_maiev_down(); + new spell_illidan_cage_teleport(); + new spell_illidan_despawn_akama(); } diff --git a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp index b8c11a06092..35b80e4a47a 100644 --- a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp +++ b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp @@ -48,7 +48,7 @@ BossBoundaryData const boundaries = { DATA_RELIQUARY_OF_SOULS, new ZRangeBoundary(81.8f, 148.0f) }, { DATA_MOTHER_SHAHRAZ, new RectangleBoundary(903.4f, 982.1f, 92.4f, 313.2f) }, { DATA_ILLIDARI_COUNCIL, new EllipseBoundary(Position(696.6f, 305.0f), 70.0 , 85.0) }, - { DATA_ILLIDAN_STORMRAGE, new EllipseBoundary(Position(694.8f, 309.0f), 70.0 , 85.0) } + { DATA_ILLIDAN_STORMRAGE, new EllipseBoundary(Position(694.8f, 309.0f), 80.0 , 95.0) } }; ObjectData const creatureData[] = @@ -70,6 +70,7 @@ ObjectData const creatureData[] = { NPC_VERAS_DARKSHADOW, DATA_VERAS_DARKSHADOW }, { NPC_BLOOD_ELF_COUNCIL_VOICE, DATA_BLOOD_ELF_COUNCIL_VOICE }, { NPC_BLACK_TEMPLE_TRIGGER, DATA_BLACK_TEMPLE_TRIGGER }, + { NPC_MAIEV_SHADOWSONG, DATA_MAIEV }, { 0, 0 } // END }; @@ -94,6 +95,7 @@ class instance_black_temple : public InstanceMapScript LoadDoorData(doorData); LoadObjectData(creatureData, gameObjectData); LoadBossBoundaries(boundaries); + akamaState = AKAMA_INTRO; } void OnGameObjectCreate(GameObject* go) override @@ -127,6 +129,30 @@ class instance_black_temple : public InstanceMapScript } } + uint32 GetData(uint32 data) const override + { + if (data == DATA_AKAMA) + return akamaState; + + return 0; + } + + void SetData(uint32 data, uint32 value) override + { + switch (data) + { + case DATA_AKAMA: + akamaState = value; + break; + case ACTION_OPEN_DOOR: + if (GameObject* illidanGate = GetGameObject(DATA_GO_ILLIDAN_GATE)) + HandleGameObject(ObjectGuid::Empty, true, illidanGate); + break; + default: + break; + } + } + bool SetBossState(uint32 type, EncounterState state) override { if (!InstanceScript::SetBossState(type, state)) @@ -157,6 +183,11 @@ class instance_black_temple : public InstanceMapScript HandleGameObject(ObjectGuid::Empty, true, door); } break; + case DATA_ILLIDARI_COUNCIL: + if (state == DONE) + if (Creature* akama = GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_ACTIVE_AKAMA_INTRO); + break; default: break; } @@ -171,8 +202,10 @@ class instance_black_temple : public InstanceMapScript return false; return true; } + protected: GuidVector AshtongueGUIDs; + uint8 akamaState; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override |