/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "ScriptMgr.h" #include "black_temple.h" #include "Containers.h" #include "GridNotifiersImpl.h" #include "InstanceScript.h" #include "Map.h" #include "MotionMaster.h" #include "ObjectAccessor.h" #include "PassiveAI.h" #include "Player.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" #include "SpellAuraEffects.h" #include "SpellInfo.h" #include "SpellScript.h" #include "TemporarySummon.h" enum IllidanSay { // Illidan SAY_ILLIDAN_MINION = 0, SAY_ILLIDAN_KILL = 1, SAY_ILLIDAN_TAKEOFF = 2, SAY_ILLIDAN_SUMMONFLAMES = 3, SAY_ILLIDAN_EYE_BLAST = 4, 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, // 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, EVENT_BT_SUMMIT_WALK_SOUND_ID = 11717, EVENT_BT_SUMMIT_WALK_3_SOUND_ID = 11725, EVENT_BT_STORM_WALK_HERO_2_SOUND_ID = 11727, EVENT_BT_STORM_WALK_UNI_3_SOUND_ID = 11729, EVENT_BT_ARRIVAL_WALK_HERO_1_SOUND_ID = 11728 }; enum IllidanSpells { // 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, // Spirits SPELL_DEATHSWORN_DOOR_CHANNEL = 41269, // Door Trigger SPELL_ARCANE_EXPLOSION = 35426, // 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_PARASITIC_SHADOWFIEND_2 = 41914, SPELL_REMOVE_PARASITIC_SHADOWFIEND = 41923, 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, // Blaze SPELL_BLAZE = 40610, // Demon Fire SPELL_DEMON_FIRE = 40029, // Flame Crash SPELL_FLAME_CRASH_GROUND = 40836 }; enum IllidanMisc { 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 IllidanActions { 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_RESUME_COMBAT, ACTION_ILLIDAN_CAGED, ACTION_START_OUTRO, ACTION_MAIEV_DOWN_FADE }; enum IllidanPhases { PHASE_INTRO = 1, PHASE_1, PHASE_MINIONS, PHASE_2, PHASE_3, PHASE_4, PHASE_OUTRO }; enum IllidanSplineMovement { 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 }; enum IllidanPoints { 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 }; enum IllidanEventGroup { GROUP_PHASE_ALL = 0, GROUP_PHASE_1, GROUP_PHASE_2, GROUP_PHASE_3, GROUP_PHASE_DEMON, GROUP_PHASE_4 }; enum IllidanEvents { // 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, EVENT_AKAMA_START_SOUND, // 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 }; Position const AkamaTeleportPositions[2] = { { 609.7720f, 308.4560f, 271.826f }, // Illidari Council Position { 752.2771f, 369.9401f, 353.1584f } // Minions Position }; Position const MinionsSpawnPositions[10] = { { 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 } }; Position const IllidanPhase2Positions[4] = { { 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 } }; Position const IllidanMiddlePoint = { 676.6479f, 304.7606f, 354.1909f , 6.230825f }; Position const IllidanDBTargetSpawnPositions[4] = { { 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 } }; Position const IllidanDBTargetPoints[4] = { { 660.3492f, 345.5749f, 353.2961f }, { 701.6755f, 297.3358f, 354.041f }, { 706.7507f, 269.4593f, 353.2778f }, { 660.3492f, 345.5749f, 353.2961f } }; Position const BladesPositions[2] = { { 676.226013f, 325.230988f }, { 678.059998f, 285.220001f } }; uint32 const SummonCageTrapSpells[8] = { 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 SummonWarglaiveEvent : public BasicEvent { public: SummonWarglaiveEvent(Unit* owner) : BasicEvent(), _owner(owner) { } bool Execute(uint64 /*eventTime*/, uint32 /*diff*/) override { _owner->CastSpell(_owner, SPELL_RANGE_MARKER, true); _owner->CastSpell(_owner, SPELL_SUMMON_GLAIVE, true); return true; } private: Unit* _owner; }; class ChargeTargetSelector { public: ChargeTargetSelector() { } bool operator()(Unit* unit) const { return unit->GetTypeId() == TYPEID_PLAYER && unit->GetDistance2d(BladesPositions[0].GetPositionX(), BladesPositions[0].GetPositionY()) > 25.0f && unit->GetDistance2d(BladesPositions[1].GetPositionX(), BladesPositions[1].GetPositionY()) > 25.0f; } }; struct boss_illidan_stormrage : public BossAI { boss_illidan_stormrage(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE), _minionsCount(0), _flameCount(0), _orientation(0.0f), _pillarIndex(0), _phase(0), _dead(false), _isDemon(false) { } void Reset() override { _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 (instance->GetData(DATA_AKAMA_ILLIDAN_INTRO) && instance->GetBossState(DATA_ILLIDARI_COUNCIL) == DONE) if (Creature* akama = instance->GetCreature(DATA_AKAMA)) akama->AI()->DoAction(ACTION_ACTIVE_AKAMA_INTRO); } void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); me->SetCanDualWield(true); if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) musicController->PlayDirectMusic(EVENT_BT_SUMMIT_WALK_3_SOUND_ID); specialEvents.ScheduleEvent(EVENT_EVADE_CHECK, 10s); specialEvents.ScheduleEvent(EVENT_BERSERK, 25min); ScheduleEvents(GROUP_PHASE_1, GROUP_PHASE_1); events.ScheduleEvent(EVENT_TAUNT, Seconds(30), Seconds(60), GROUP_PHASE_ALL); } void JustSummoned(Creature* summon) override { if (summon->GetEntry() == NPC_PARASITIC_SHADOWFIEND) summons.Summon(summon); else BossAI::JustSummoned(summon); } void ChangeOrientation(float orientation) { _orientation = orientation; events.ScheduleEvent(EVENT_CHANGE_ORIENTATION, Milliseconds(1), GROUP_PHASE_ALL); } void KilledUnit(Unit* victim) override { if (victim->GetTypeId() == TYPEID_PLAYER) Talk(SAY_ILLIDAN_KILL); } void ScheduleEvents(uint8 phase, uint8 group) { switch (phase) { 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); break; case GROUP_PHASE_4: ScheduleEvents(GROUP_PHASE_3, group); events.ScheduleEvent(EVENT_FRENZY, Seconds(40), group); break; default: break; } } void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override { if (summon->GetEntry() == NPC_ILLIDARI_ELITE) _minionsCount--; } void EnterEvadeMode(EvadeReason /*why*/) override { summons.DespawnAll(); specialEvents.Reset(); _DespawnAtEvade(); } 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: instance->SetData(DATA_AKAMA_ILLIDAN_INTRO, 0); 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->SetUninteractible(true); 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); summons.DoAction(ACTION_START_PHASE_4, EntryCheckPredicate(NPC_PARASITIC_SHADOWFIEND)); me->SetReactState(REACT_PASSIVE); me->AttackStop(); me->SetUninteractible(true); 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->SetUninteractible(true); events.ScheduleEvent(EVENT_DEFEATED_TEXT, 4s); break; default: break; } } void JustDied(Unit* /*killer*/) override { me->SetUninteractible(false); instance->SetBossState(DATA_ILLIDAN_STORMRAGE, DONE); events.Reset(); } void MovementInform(uint32 type, uint32 pointId) override { if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE) return; switch (pointId) { 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: { 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 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 EnterEvadeModeIfNeeded() { 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() && IsInBoundary(player)) return; EnterEvadeMode(EvadeReason::NoHostiles); } void SummonMinions() { uint8 needSummon = MAX_MINIONS_NUMBER - _minionsCount; for (uint8 i = 0; i < needSummon; ++i) { _minionsCount++; me->SummonCreature(NPC_ILLIDARI_ELITE, MinionsSpawnPositions[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6s); } } void DamageTaken(Unit* who, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override { if (damage >= me->GetHealth() && (!who || who->GetGUID() != me->GetGUID())) { damage = me->GetHealth() - 1; if (!_dead) { if (_isDemon) { events.Reset(); specialEvents.Reset(); DoCastSelf(SPELL_DEMON_TRANSFORM_1); return; } _dead = true; summons.DespawnEntry(NPC_PARASITIC_SHADOWFIEND); DoCastSelf(SPELL_REMOVE_PARASITIC_SHADOWFIEND, true); DoAction(ACTION_START_OUTRO); if (Creature* maiev = instance->GetCreature(DATA_MAIEV)) maiev->AI()->DoAction(ACTION_START_OUTRO); } } else if (me->HealthBelowPct(90) && _phase < PHASE_MINIONS) { _phase = PHASE_MINIONS; DoAction(ACTION_START_MINIONS); } else if (me->HealthBelowPct(65) && _phase < PHASE_2) { _phase = PHASE_2; DoAction(ACTION_START_PHASE_2); } else if (me->HealthBelowPct(30) && _phase < PHASE_4) { _phase = PHASE_4; 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 ExecuteSpecialEvents() { while (uint32 eventId = specialEvents.ExecuteEvent()) { switch (eventId) { 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 UpdateAI(uint32 diff) override { if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) return; specialEvents.Update(diff); ExecuteSpecialEvents(); if (me->HasUnitState(UNIT_STATE_CASTING)) return; events.Update(diff); while (uint32 eventId = events.ExecuteEvent()) { 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->SetImmuneToAll(false); 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(SelectTargetMethod::Random, 0, 0.0f, true)) DoCast(target, SPELL_PARASITIC_SHADOWFIEND); events.Repeat(Seconds(30)); break; case EVENT_MINIONS_WEAVE: if (_dead) return; SummonMinions(); events.Repeat(Seconds(30)); break; case EVENT_MOVE_TO_WARGLAIVE_POINT: { Position pos; std::list triggers; GetCreatureListWithEntryInGrid(triggers, me, NPC_GLAIVE_WORLD_TRIGGER, 150.0f); triggers.remove_if([](WorldObject* unit) { 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); if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) musicController->PlayDirectMusic(EVENT_BT_STORM_WALK_HERO_2_SOUND_ID); break; } 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); 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: { events.CancelEventGroup(GROUP_PHASE_2); _pillarIndex = urand(0, 3); me->GetMotionMaster()->MovePoint(POINT_RANDOM_PILLAR, IllidanPhase2Positions[_pillarIndex]); events.Repeat(Seconds(30)); break; } case EVENT_FACE_MIDDLE: { float angle = me->GetAbsoluteAngle(IllidanMiddlePoint); me->SetFacingTo(angle); break; } case EVENT_EYE_BLAST: { events.CancelEvent(EVENT_DARK_BARRAGE); Position pos = IllidanDBTargetSpawnPositions[_pillarIndex]; if (TempSummon* dbTarget = me->SummonCreature(NPC_ILLIDAN_DB_TARGET, pos, TEMPSUMMON_MANUAL_DESPAWN)) { Talk(SAY_ILLIDAN_EYE_BLAST); DoCast(dbTarget, SPELL_EYE_BLAST); dbTarget->GetMotionMaster()->MovePoint(POINT_DB_TARGET, IllidanDBTargetPoints[_pillarIndex]); } break; } case EVENT_DARK_BARRAGE: { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 150.0f, true)) DoCast(target, SPELL_DARK_BARRAGE); events.RescheduleEvent(EVENT_EYE_BLAST, Seconds(5), GROUP_PHASE_2); Milliseconds currentTime = events.GetTimeUntilEvent(EVENT_FLY_TO_RANDOM_PILLAR); events.RescheduleEvent(EVENT_FLY_TO_RANDOM_PILLAR, currentTime + 30s, GROUP_PHASE_2); break; } case EVENT_FIREBALL: if (Unit* target = SelectTarget(SelectTargetMethod::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->SetUninteractible(false); me->SetReactState(REACT_AGGRESSIVE); ScheduleEvents(GROUP_PHASE_3, GROUP_PHASE_3); if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) musicController->PlayDirectMusic(EVENT_BT_STORM_WALK_UNI_3_SOUND_ID); 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, 15s); break; case EVENT_SCHEDULE_DEMON_SPELLS: ResetThreatList(); 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; ResetThreatList(); 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->SetUninteractible(false); me->SetReactState(REACT_AGGRESSIVE); ScheduleEvents(GROUP_PHASE_4, GROUP_PHASE_4); summons.DoAction(ACTION_RESUME_COMBAT, EntryCheckPredicate(NPC_PARASITIC_SHADOWFIEND)); 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); if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) musicController->PlayDirectMusic(EVENT_BT_ARRIVAL_WALK_HERO_1_SOUND_ID); events.ScheduleEvent(EVENT_QUIET_SUICIDE, 18s); 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) { return unitGuid != _akamaGUID && unitGuid != _maievGUID; }); break; } default: break; } if (me->HasUnitState(UNIT_STATE_CASTING)) return; } } private: uint8 _minionsCount; uint8 _flameCount; float _orientation; uint8 _pillarIndex; uint8 _phase; bool _dead; bool _isDemon; EventMap specialEvents; }; struct npc_akama_illidan : public ScriptedAI { npc_akama_illidan(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _orientation(0.0f), _isTeleportToMinions(false) { } void Reset() override { _events.Reset(); _spiritOfUdaloGUID.Clear(); _spiritOfOlumGUID.Clear(); _isTeleportToMinions = false; } bool OnGossipSelect(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->RemoveNpcFlag(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->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); CloseGossipMenuFor(player); } return false; } bool CanAIAttack(Unit const* who) const override { if (_events.IsInPhase(PHASE_MINIONS) && who->GetEntry() == NPC_ILLIDAN_STORMRAGE) return false; return ScriptedAI::CanAIAttack(who); } uint32 GetData(uint32 /*data*/) const override { return _isTeleportToMinions ? 1 : 0; } void EnterEvadeMode(EvadeReason /*why*/) override { } void JustSummoned(Creature* summon) override { if (summon->GetEntry() == NPC_SPIRIT_OF_UDALO) { _spiritOfUdaloGUID = summon->GetGUID(); summon->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); } else if (summon->GetEntry() == NPC_SPIRIT_OF_OLUM) { _spiritOfOlumGUID = summon->GetGUID(); summon->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); } } void DoAction(int32 actionId) override { switch (actionId) { case ACTION_ACTIVE_AKAMA_INTRO: _events.SetPhase(PHASE_INTRO); me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); _events.SetPhase(PHASE_INTRO); _events.ScheduleEvent(EVENT_TELEPORT, 1s); _events.ScheduleEvent(EVENT_MOVE_TO_ILLIDARI_ROOM, Seconds(1) + Milliseconds(500)); break; case ACTION_OPEN_DOOR: _instance->SetData(ACTION_OPEN_DOOR, 0); _events.ScheduleEvent(EVENT_AKAMA_THANKS, 2s); break; case ACTION_FREE: _events.ScheduleEvent(EVENT_FREE, 14s); break; case ACTION_START_ENCOUNTER: DoZoneInCombat(); _events.ScheduleEvent(EVENT_HEALING_POTION, 1s); break; case ACTION_START_MINIONS: _events.ScheduleEvent(EVENT_AKAMA_MINIONS, 8s); break; case ACTION_START_OUTRO: me->SetReactState(REACT_PASSIVE); me->AttackStop(); _events.Reset(); _events.ScheduleEvent(EVENT_AKAMA_MOVE_BACK, 2s); break; default: break; } } void ChangeOrientation(float orientation) { _orientation = orientation; _events.ScheduleEvent(EVENT_CHANGE_ORIENTATION, Milliseconds(1)); } void MovementInform(uint32 type, uint32 pointId) override { if (type != POINT_MOTION_TYPE && type != SPLINE_CHAIN_MOTION_TYPE) return; switch (pointId) { case POINT_ILLIDARI_COUNCIL: Talk(SAY_AKAMA_FINISH); me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); break; case POINT_STAIRS: ChangeOrientation(6.265732f); _events.ScheduleEvent(EVENT_AKAMA_SAY_DOOR, 5s); break; case POINT_ILLIDAN_ROOM: ChangeOrientation(2.129302f); Talk(SAY_AKAMA_BETRAYER); me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP); break; case POINT_FACE_ILLIDAN: ChangeOrientation(3.140537f); _events.ScheduleEvent(EVENT_START_ILLIDAN, 2s); break; case POINT_TELEPORT: DoCastSelf(SPELL_AKAMA_TELEPORT); _events.ScheduleEvent(EVENT_AKAMA_MINIONS_MOVE_2, Milliseconds(500)); break; case POINT_MINIONS: _events.SetPhase(PHASE_MINIONS); me->SetImmuneToNPC(false); me->SetReactState(REACT_AGGRESSIVE); if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) illidan->AI()->DoAction(ACTION_START_MINIONS_WEAVE); _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 2s); break; case POINT_MOVE_BACK: _events.ScheduleEvent(EVENT_AKAMA_MOVE_TO_ILLIDAN, Milliseconds(1)); break; case POINT_ILLIDAN: _events.ScheduleEvent(EVENT_AKAMA_LIGHT_TEXT, 1s); break; default: break; } } void DamageTaken(Unit* /*who*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override { if (damage >= me->GetHealth()) damage = me->GetHealth() - 1; } void UpdateAI(uint32 diff) override { if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO)) return; _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_TELEPORT: DoCastSelf(SPELL_AKAMA_TELEPORT, true); break; case EVENT_MOVE_TO_ILLIDARI_ROOM: me->GetMotionMaster()->MoveAlongSplineChain(POINT_ILLIDARI_COUNCIL, SPLINE_ILLIDARI_COUNCIL, false); break; case EVENT_AKAMA_SAY_DOOR: Talk(SAY_AKAMA_DOOR); _events.ScheduleEvent(EVENT_AKAMA_DOOR_FAIL, 4s); break; case EVENT_AKAMA_DOOR_FAIL: DoCastSelf(SPELL_AKAMA_DOOR_FAIL); _events.ScheduleEvent(EVENT_AKAMA_SAY_ALONE, 10s); break; case EVENT_AKAMA_SAY_ALONE: Talk(SAY_AKAMA_ALONE); _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, 7s); 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, 6s); 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); _events.ScheduleEvent(EVENT_AKAMA_START_SOUND, 5s); break; case EVENT_AKAMA_START_SOUND: if (GameObject* musicController = _instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) musicController->PlayDirectMusic(EVENT_BT_SUMMIT_WALK_SOUND_ID); break; case EVENT_AKAMA_THANKS: Talk(SAY_AKAMA_SALUTE); _events.ScheduleEvent(EVENT_SPIRIT_SALUTE, 3s); _events.ScheduleEvent(EVENT_RUN_FROM_ILLIDAN_ROOM, 7s); break; case EVENT_SPIRIT_SALUTE: if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) { 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_RUN_FROM_ILLIDAN_ROOM: me->GetMotionMaster()->MoveAlongSplineChain(POINT_ILLIDAN_ROOM, SPLINE_ILLIDAN_ROOM, false); break; case EVENT_START_ILLIDAN: if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) illidan->AI()->DoAction(ACTION_START_ENCOUNTER); break; case EVENT_FREE: Talk(SAY_AKAMA_FREE); _events.ScheduleEvent(EVENT_TIME_HAS_COME, 18s); break; case EVENT_TIME_HAS_COME: Talk(SAY_AKAMA_TIME_HAS_COME); _events.ScheduleEvent(EVENT_ROAR, 2s); break; case EVENT_ROAR: me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); me->SetEmoteState(EMOTE_STATE_READY1H); break; case EVENT_CHANGE_ORIENTATION: me->SetFacingTo(_orientation); break; case EVENT_HEALING_POTION: if (me->HealthBelowPct(20)) DoCastSelf(SPELL_HEALING_POTION); _events.Repeat(Seconds(1)); break; case EVENT_AKAMA_MINIONS: Talk(SAY_AKAMA_MINIONS); _events.ScheduleEvent(EVENT_AKAMA_MINIONS_EMOTE, 2s); break; case EVENT_AKAMA_MINIONS_EMOTE: me->SetReactState(REACT_PASSIVE); me->AttackStop(); me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); me->SetImmuneToNPC(true); _events.ScheduleEvent(EVENT_AKAMA_MINIONS_MOVE, 4s); break; case EVENT_AKAMA_MINIONS_MOVE: _isTeleportToMinions = true; me->GetMotionMaster()->MoveAlongSplineChain(POINT_TELEPORT, SPLINE_TELEPORT, false); break; case EVENT_AKAMA_MINIONS_MOVE_2: me->GetMotionMaster()->MoveAlongSplineChain(POINT_MINIONS, SPLINE_MINIONS, false); break; case EVENT_CHAIN_LIGHTNING: DoCastVictim(SPELL_CHAIN_LIGHTNING); _events.Repeat(Seconds(8) + Milliseconds(500)); break; case EVENT_AKAMA_MOVE_BACK: me->GetMotionMaster()->MoveAlongSplineChain(POINT_MOVE_BACK, SPLINE_MOVE_BACK, false); break; 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_AKAMA_LIGHT_TEXT: Talk(SAY_AKAMA_LIGHT); _events.ScheduleEvent(EVENT_FINAL_SALUTE, 4s); break; case EVENT_FINAL_SALUTE: me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); _events.ScheduleEvent(EVENT_AKAMA_DESPAWN, 5s); break; case EVENT_AKAMA_DESPAWN: DoCastSelf(SPELL_AKAMA_DESPAWN, true); break; default: break; } if (me->HasUnitState(UNIT_STATE_CASTING)) return; } } private: InstanceScript* _instance; EventMap _events; ObjectGuid _spiritOfUdaloGUID; ObjectGuid _spiritOfOlumGUID; float _orientation; bool _isTeleportToMinions; }; struct npc_parasitic_shadowfiend : public ScriptedAI { npc_parasitic_shadowfiend(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } void Reset() override { 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_PASSIVE); _scheduler.Schedule(Seconds(2), [this](TaskContext /*context*/) { me->SetReactState(REACT_AGGRESSIVE); DoZoneInCombat(); }); } void DoAction(int32 action) override { if (action == ACTION_START_PHASE_4) { me->SetReactState(REACT_PASSIVE); me->AttackStop(); } else if (action == ACTION_RESUME_COMBAT) _scheduler.Schedule(Seconds(2), [this](TaskContext /*context*/) { me->SetReactState(REACT_AGGRESSIVE); DoZoneInCombat(); }); } void UpdateAI(uint32 diff) override { _scheduler.Update(diff); if (!UpdateVictim()) return; } private: InstanceScript* _instance; TaskScheduler _scheduler; }; struct npc_blade_of_azzinoth : public NullCreatureAI { npc_blade_of_azzinoth(Creature* creature) : NullCreatureAI(creature), _instance(creature->GetInstanceScript()) { } void Reset() override { if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) { me->DespawnOrUnsummon(); return; } 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*/) { 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 JustSummoned(Creature* summon) override { if (summon->GetEntry() == NPC_FLAME_OF_AZZINOTH) _flameGuid = summon->GetGUID(); } void UpdateAI(uint32 diff) override { _scheduler.Update(diff); } private: InstanceScript* _instance; TaskScheduler _scheduler; ObjectGuid _flameGuid; }; struct npc_flame_of_azzinoth : public ScriptedAI { npc_flame_of_azzinoth(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { SetBoundary(_instance->GetBossBoundary(DATA_ILLIDAN_STORMRAGE)); } void Reset() override { if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) { 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, 3s); _events.ScheduleEvent(EVENT_FLAME_BLAST, 11s); } void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; if (me->HasUnitState(UNIT_STATE_CASTING)) return; _events.Update(diff); while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_ENGAGE: me->SetReactState(REACT_AGGRESSIVE); DoZoneInCombat(); _events.ScheduleEvent(EVENT_FLAME_CHARGE, 5s); break; case EVENT_FLAME_CHARGE: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, ChargeTargetSelector())) { 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; } if (me->HasUnitState(UNIT_STATE_CASTING)) return; } } void JustDied(Unit* /*killer*/) override { if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) illidan->AI()->DoAction(ACTION_FLAME_DEAD); } private: InstanceScript* _instance; EventMap _events; }; struct npc_illidan_db_target : public NullCreatureAI { npc_illidan_db_target(Creature* creature) : NullCreatureAI(creature) { } void Reset() override { DoCastSelf(SPELL_EYE_BLAST_TRIGGER); } void JustSummoned(Creature* summon) override { if (summon->GetEntry() == NPC_DEMON_FIRE) summon->SetReactState(REACT_PASSIVE); } void MovementInform(uint32 type, uint32 pointId) override { if (type == POINT_MOTION_TYPE && pointId == POINT_DB_TARGET) { me->RemoveAurasDueToSpell(SPELL_EYE_BLAST_TRIGGER); me->RemoveAurasDueToSpell(SPELL_EYE_BLAST); } } }; struct npc_shadow_demon : public PassiveAI { npc_shadow_demon(Creature* creature) : PassiveAI(creature), _instance(creature->GetInstanceScript()) { } void Reset() override { if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) { me->DespawnOrUnsummon(); return; } 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(); }); } void SetGUID(ObjectGuid const& guid, int32 /*id*/) override { _targetGUID = guid; if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) me->GetMotionMaster()->MoveChase(target); } void UpdateAI(uint32 diff) override { _scheduler.Update(diff); } private: InstanceScript* _instance; TaskScheduler _scheduler; ObjectGuid _targetGUID; }; struct npc_maiev : public ScriptedAI { npc_maiev(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _canDown(true) { } void JustAppeared() override { if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) me->SetFacingToObject(illidan); me->SetReactState(REACT_PASSIVE); _events.SetPhase(PHASE_OUTRO); _events.ScheduleEvent(EVENT_MAIEV_APPEAR, 1s); _events.ScheduleEvent(EVENT_MAIEV_EXCLAMATION, 2s); _events.ScheduleEvent(EVENT_MAIEV_JUSTICE_TEXT, 14s); _events.ScheduleEvent(EVENT_TAUNT, 20s, 1min); _canDown = true; } void JustEngagedWith(Unit* /*who*/) override { _events.SetPhase(PHASE_1); _events.ScheduleEvent(EVENT_CAGE_TRAP, 30s); _events.ScheduleEvent(EVENT_SHADOW_STRIKE, 50s); _events.ScheduleEvent(EVENT_THROW_DAGGER, 1s); } void DoAction(int32 actionId) override { if (actionId == ACTION_START_OUTRO) { _events.Reset(); _events.SetPhase(PHASE_OUTRO); 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, 28s); } else if (actionId == ACTION_MAIEV_DOWN_FADE) _canDown = true; } void DamageTaken(Unit* /*who*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override { if (damage >= me->GetHealth() && _canDown) { damage = me->GetHealth() - 1; _canDown = false; DoCastSelf(SPELL_MAIEV_DOWN, true); Talk(SAY_MAIEV_SHADOWSONG_DOWN, me); } } void UpdateAI(uint32 diff) override { if (!_events.IsInPhase(PHASE_OUTRO) && !UpdateVictim()) return; if (me->HasUnitState(UNIT_STATE_CASTING)) return; _events.Update(diff); while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { 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, 2s); break; case EVENT_MAIEV_YES: me->HandleEmoteCommand(EMOTE_ONESHOT_YES); _events.ScheduleEvent(EVENT_MAIEV_ROAR, 3s); break; case EVENT_MAIEV_ROAR: me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); _events.ScheduleEvent(EVENT_MAIEV_COMBAT, 3s); 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, 11s); break; case EVENT_MAIEV_FAREWELL_TEXT: Talk(SAY_MAIEV_SHADOWSONG_FAREWELL); _events.ScheduleEvent(EVENT_MAIEV_TELEPORT_DESPAWN, 3s); break; case EVENT_MAIEV_TELEPORT_DESPAWN: DoCastSelf(SPELL_TELEPORT_VISUAL); me->DespawnOrUnsummon(Seconds(1)); break; default: break; } if (me->HasUnitState(UNIT_STATE_CASTING)) return; } } private: EventMap _events; InstanceScript* _instance; bool _canDown; }; struct npc_cage_trap_trigger : public PassiveAI { npc_cage_trap_trigger(Creature* creature) : PassiveAI(creature) { } void Reset() override { _scheduler.Schedule(Seconds(1), [this](TaskContext checkTarget) { DoCastSelf(SPELL_CAGE_TRAP_PERIODIC); checkTarget.Repeat(); }); } void UpdateAI(uint32 diff) override { _scheduler.Update(diff); } private: TaskScheduler _scheduler; }; struct npc_illidari_elite : public ScriptedAI { npc_illidari_elite(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } void Reset() override { if (Creature* akama = _instance->GetCreature(DATA_AKAMA)) { AttackStart(akama); AddThreat(akama, 1000.0f); } } bool CanAIAttack(Unit const* who) const override { if (who->GetEntry() == NPC_AKAMA) return true; return false; } private: InstanceScript* _instance; }; struct npc_illidan_generic_fire : public ScriptedAI { npc_illidan_generic_fire(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { SetCombatMovement(false); } void Reset() override { if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) illidan->AI()->JustSummoned(me); me->SetReactState(REACT_PASSIVE); switch (me->GetEntry()) { case NPC_DEMON_FIRE: DoCastSelf(SPELL_DEMON_FIRE, true); break; case NPC_BLAZE: DoCastSelf(SPELL_BLAZE, true); DoCastSelf(SPELL_BIRTH, true); break; case NPC_FLAME_CRASH: DoCastSelf(SPELL_FLAME_CRASH_GROUND, true); break; default: break; } } void UpdateAI(uint32 /*diff*/) override { } private: InstanceScript* _instance; }; // 41077 - Akama Teleport class spell_illidan_akama_teleport : public SpellScript { void SetDest(SpellDestination& dest) { if (Creature* caster = GetCaster()->ToCreature()) { uint32 destination = caster->AI()->GetData(DATA_AKAMA_TELEPORT_POSITION); dest.Relocate(AkamaTeleportPositions[destination]); } } void Register() override { OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_illidan_akama_teleport::SetDest, EFFECT_0, TARGET_DEST_NEARBY_ENTRY); } }; // 41268 - Quest - Black Temple - Akama - Door Open class spell_illidan_akama_door_channel : public AuraScript { bool Validate(SpellInfo const* /*spell*/) override { return ValidateSpellInfo({ SPELL_ARCANE_EXPLOSION }); } 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); } void Register() override { AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_akama_door_channel::OnRemoveDummy, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); } }; // 40904 - Draw Soul class spell_illidan_draw_soul : public SpellScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_DRAW_SOUL_HEAL }); } void HandleScriptEffect(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); GetHitUnit()->CastSpell(GetCaster(), SPELL_DRAW_SOUL_HEAL, true); } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_illidan_draw_soul::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); } }; /* 41917 - Parasitic Shadowfiend 41914 - Parasitic Shadowfiend */ class spell_illidan_parasitic_shadowfiend : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_SUMMON_PARASITIC_SHADOWFIENDS }); } void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { AuraRemoveMode removeMode = GetTargetApplication()->GetRemoveMode(); if (removeMode != AURA_REMOVE_BY_EXPIRE && removeMode != AURA_REMOVE_BY_DEATH) return; Unit* target = GetTarget(); target->CastSpell(target, SPELL_SUMMON_PARASITIC_SHADOWFIENDS, true); } void Register() override { AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_parasitic_shadowfiend::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); } }; // 41913 - Parasitic Shadowfiend Passive class spell_illidan_parasitic_shadowfiend_proc : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_PARASITIC_SHADOWFIEND, SPELL_PARASITIC_SHADOWFIEND_2 }); } bool CheckProc(ProcEventInfo& eventInfo) { Unit* target = eventInfo.GetProcTarget(); return target && !target->HasAura(SPELL_PARASITIC_SHADOWFIEND) && !target->HasAura(SPELL_PARASITIC_SHADOWFIEND_2); } void Register() override { DoCheckProc += AuraCheckProcFn(spell_illidan_parasitic_shadowfiend_proc::CheckProc); } }; // 41923 - Remove Parasitic Shadowfiends (SERVERSIDE) class spell_illidan_remove_parasitic_shadowfiend : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_PARASITIC_SHADOWFIEND, SPELL_PARASITIC_SHADOWFIEND_2 }); } void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { GetTarget()->RemoveAurasDueToSpell(SPELL_PARASITIC_SHADOWFIEND); GetTarget()->RemoveAurasDueToSpell(SPELL_PARASITIC_SHADOWFIEND_2); } void Register() override { AfterEffectApply += AuraEffectApplyFn(spell_illidan_remove_parasitic_shadowfiend::HandleApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); } }; /* 39635 - Throw Glaive 39849 - Throw Glaive */ class spell_illidan_throw_warglaive : public SpellScript { void HandleDummy(SpellEffIndex /*effIndex*/) { Unit* target = GetHitUnit(); target->m_Events.AddEvent(new SummonWarglaiveEvent(target), target->m_Events.CalculateTime(1s)); } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_illidan_throw_warglaive::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; // 39857 - Tear of Azzinoth Summon Channel class spell_illidan_tear_of_azzinoth_channel : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_UNCAGED_WRATH }); } 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 Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_tear_of_azzinoth_channel::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); } }; // 40631 - Flame Blast class spell_illidan_flame_blast : public SpellScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_BLAZE_SUMMON }); } void HandleBlaze(SpellEffIndex /*effIndex*/) { Unit* target = GetHitUnit(); if (target->GetTypeId() == TYPEID_PLAYER) target->CastSpell(target, SPELL_BLAZE_SUMMON, true); } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_blast::HandleBlaze, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); } }; // 39873 - Glaive Returns class spell_illidan_return_glaives : public SpellScript { void HandleScriptEffect(SpellEffIndex /*effIndex*/) { GetHitUnit()->SendPlaySpellVisualKit(SPELL_GLAIVE_VISUAL_KIT, 0, 0); if (Creature* caster = GetCaster()->ToCreature()) caster->DespawnOrUnsummon(); } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_illidan_return_glaives::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; // 40834 - Agonizing Flames class spell_illidan_agonizing_flames : public SpellScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_AGONIZING_FLAMES }); } void FilterTargets(std::list& targets) { if (targets.empty()) return; WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); targets.clear(); targets.push_back(target); } void HandleScript(SpellEffIndex /*effIndex*/) { GetCaster()->CastSpell(GetHitUnit(), SPELL_AGONIZING_FLAMES, true); } void Register() override { OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_agonizing_flames::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); OnEffectHitTarget += SpellEffectFn(spell_illidan_agonizing_flames::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); } }; // 40511 - Demon Transform 1 class spell_illidan_demon_transform1 : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_DEMON_TRANSFORM_2 }); } void OnPeriodic(AuraEffect const* /*aurEff*/) { PreventDefaultAction(); GetTarget()->CastSpell(GetTarget(), SPELL_DEMON_TRANSFORM_2, true); Remove(); } void Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform1::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); } }; // 40398 - Demon Transform 2 class spell_illidan_demon_transform2 : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_DEMON_FORM, SPELL_DEMON_TRANSFORM_3 }); } void OnPeriodic(AuraEffect const* aurEff) { PreventDefaultAction(); Unit* target = GetTarget(); if (aurEff->GetTickNumber() == 1) { 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 Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform2::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); } }; // 41126 - Flame Burst class spell_illidan_flame_burst : public SpellScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_FLAME_BURST_EFFECT }); } void HandleScriptEffect(SpellEffIndex /*effIndex*/) { GetHitUnit()->CastSpell(GetHitUnit(), SPELL_FLAME_BURST_EFFECT, true); } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_burst::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; // 41081 - Find Target class spell_illidan_find_target : public SpellScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_PARALYZE }); } void FilterTargets(std::list& 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->AI()->SetGUID(target->GetGUID(), 0); } } void Register() override { OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_find_target::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); OnEffectHitTarget += SpellEffectFn(spell_illidan_find_target::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); } }; // 39908 - Eye Blast class spell_illidan_eye_blast : public AuraScript { void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { if (Creature* target = GetTarget()->ToCreature()) target->DespawnOrUnsummon(); } void Register() override { AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_eye_blast::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); } }; // 40761 - Cage Trap class spell_illidan_cage_trap : public SpellScript { void HandleScriptEffect(SpellEffIndex /*effIndex*/) { 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(); } } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_illidan_cage_trap::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; // 40760 - Cage Trap class spell_illidan_caged : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_CAGED_DEBUFF }); } 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::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); } }; // 40409 - Maiev Down class spell_maiev_down : public AuraScript { bool Load() override { return GetUnitOwner()->GetTypeId() == TYPEID_UNIT; } void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { GetTarget()->SetUninteractible(true); } void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { GetTarget()->SetUninteractible(false); GetTarget()->GetAI()->DoAction(ACTION_MAIEV_DOWN_FADE); } void Register() override { OnEffectApply += AuraEffectApplyFn(spell_maiev_down::HandleEffectApply, EFFECT_1, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); AfterEffectRemove += AuraEffectRemoveFn(spell_maiev_down::HandleEffectRemove, EFFECT_1, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); } }; // 40693 - Cage Trap class spell_illidan_cage_teleport : public SpellScript { void SetDest(SpellDestination& dest) { Position offset = { 0.0f, 0.0f, GetCaster()->GetPositionZ(), 0.0f }; dest.RelocateOffset(offset); } void Register() override { OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_illidan_cage_teleport::SetDest, EFFECT_0, TARGET_DEST_CASTER_RADIUS); } }; // 41242 - Akama Despawn class spell_illidan_despawn_akama : public SpellScript { void HandleDummy(SpellEffIndex /*effIndex*/) { if (Creature* target = GetHitCreature()) target->DespawnOrUnsummon(Seconds(1)); } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_illidan_despawn_akama::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; void AddSC_boss_illidan() { RegisterBlackTempleCreatureAI(boss_illidan_stormrage); RegisterBlackTempleCreatureAI(npc_akama_illidan); RegisterBlackTempleCreatureAI(npc_parasitic_shadowfiend); RegisterBlackTempleCreatureAI(npc_blade_of_azzinoth); RegisterBlackTempleCreatureAI(npc_flame_of_azzinoth); RegisterBlackTempleCreatureAI(npc_illidan_db_target); RegisterBlackTempleCreatureAI(npc_maiev); RegisterBlackTempleCreatureAI(npc_shadow_demon); RegisterBlackTempleCreatureAI(npc_cage_trap_trigger); RegisterBlackTempleCreatureAI(npc_illidari_elite); RegisterBlackTempleCreatureAI(npc_illidan_generic_fire); RegisterSpellScript(spell_illidan_akama_teleport); RegisterSpellScript(spell_illidan_akama_door_channel); RegisterSpellScript(spell_illidan_draw_soul); RegisterSpellScript(spell_illidan_parasitic_shadowfiend); RegisterSpellScript(spell_illidan_parasitic_shadowfiend_proc); RegisterSpellScript(spell_illidan_remove_parasitic_shadowfiend); RegisterSpellScript(spell_illidan_throw_warglaive); RegisterSpellScript(spell_illidan_tear_of_azzinoth_channel); RegisterSpellScript(spell_illidan_flame_blast); RegisterSpellScript(spell_illidan_return_glaives); RegisterSpellScript(spell_illidan_agonizing_flames); RegisterSpellScript(spell_illidan_demon_transform1); RegisterSpellScript(spell_illidan_demon_transform2); RegisterSpellScript(spell_illidan_flame_burst); RegisterSpellScript(spell_illidan_find_target); RegisterSpellScript(spell_illidan_eye_blast); RegisterSpellScript(spell_illidan_cage_trap); RegisterSpellScript(spell_illidan_caged); RegisterSpellScript(spell_maiev_down); RegisterSpellScript(spell_illidan_cage_teleport); RegisterSpellScript(spell_illidan_despawn_akama); }