/*
* This file is part of the AzerothCore 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 "CreatureScript.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "SpellScriptLoader.h"
#include "black_temple.h"
#include "Player.h"
#include "ScriptedGossip.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
enum Says
{
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,
EMOTE_AZZINOTH_GAZE = 0
};
enum Spells
{
SPELL_ILLIDAN_KNEEL_INTRO = 39656, // Aura removal does not play the full animation, using StandState instead
SPELL_DUAL_WIELD = 42459,
SPELL_BERSERK = 45078,
SPELL_EMOTE_TALK_QUESTION = 41616,
SPELL_CLEAR_ALL_DEBUFFS = 34098,
SPELL_HIT_CHANCE = 43689,
// Phase 1
SPELL_FLAME_CRASH = 40832,
SPELL_DRAW_SOUL = 40904,
SPELL_DRAW_SOUL_HEAL = 40903,
SPELL_PARASITIC_SHADOWFIEND = 41917,
SPELL_PARASITIC_SHADOWFIEND_TRIGGER = 41914,
SPELL_SUMMON_PARASITIC_SHADOWFIENDS = 41915,
// Phase 2
SPELL_THROW_GLAIVE = 39635,
SPELL_THROW_GLAIVE2 = 39849,
SPELL_GLAIVE_RETURNS = 39873,
SPELL_SUMMON_GLAIVE = 41466,
SPELL_FIREBALL = 40598,
SPELL_DARK_BARRAGE = 40585,
SPELL_EYE_BLAST = 39908,
// Phase 3
SPELL_AGONIZING_FLAMES = 40932,
SPELL_SUMMON_MAIEV = 40403,
SPELL_SHADOW_PRISON = 40647,
// Phase 4
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,
SPELL_SUMMON_SHADOW_DEMON = 41117,
SPELL_CONSUME_SOUL = 41080,
SPELL_FIND_TARGET = 41081,
// Phase 5
SPELL_FRENZY = 40683,
SPELL_TELEPORT_MAIEV = 41221,
SPELL_DEATH = 41218,
// Cage
SPELL_CAGE_TRAP = 40693,
SPELL_CAGE_TRAP_PERIODIC = 40760,
SPELL_CAGE_TRAP_DUMMY = 40761,
SPELL_CAGED_DEBUFF = 40695,
SPELL_CAGED_SUMMON1 = 40696,
SPELL_CAGED_SUMMON8 = 40703
};
enum Misc
{
EQUIPMENT_UNARMED = 0,
EQUIPMENT_GLAIVES = 1,
// Illidan
ACTION_START_EVENT = 1,
ACTION_ILLIDAN_LIFTOFF = 2,
ACTION_ILLIDAN_CAGED = 3,
ACTION_SHADOW_PRISON = 4,
ACTION_ILLIDAN_DIE = 5,
ACTION_ILLIDAN_DEMON_TRANSFORM = 6,
ACTION_ILLIDAN_DEMON_TRANSFORM_BACK = 7,
// Akama
ACTION_ILLIDARI_COUNCIL_DONE = 0,
ACTION_AKAMA_MINIONS = 1,
ACTION_AKAMA_ENDING = 2,
ACTION_AKAMA_MAIEV_DESPAWN = 3,
// Summons
ACTION_MAIEV_ENDING = 1,
ACTION_RETURN_BLADE = 2, // Sent to 22996 (Blade of Azzinoth)
MAX_EYE_BEAM_POS = 4,
POINT_ILLIDAN_TAKEOFF = 1,
POINT_ILLIDAN_HOVER = 2,
POINT_ILLIDAN_LAND = 3,
GROUP_BERSERK = 1,
GROUP_PHASE_FLYING = 2,
GROUP_DEMON_FORM = 3,
NPC_WORLD_TRIGGER = 22515,
NPC_ILLIDAN_DB_TARGET = 23070,
NPC_MAIEV_SHADOWSONG = 23197,
GO_CAGE_TRAP = 185916,
PHASE_INITIAL = 1,
PHASE_FLYING = 2,
PHASE_LANDING = 3,
PHASE_DEMON = 4,
PHASE_MAIEV = 5
};
const Position eyeBeamPos[MAX_EYE_BEAM_POS * 2] =
{
{639.97f, 301.63f, 354.0f, 0.0f},
{658.83f, 265.10f, 354.0f, 0.0f},
{656.86f, 344.07f, 354.0f, 0.0f},
{640.70f, 310.47f, 354.0f, 0.0f},
{706.22f, 273.26f, 354.0f, 0.0f},
{717.55f, 328.33f, 354.0f, 0.0f},
{718.06f, 286.08f, 354.0f, 0.0f},
{705.92f, 337.14f, 354.0f, 0.0f}
};
const Position airHoverPos[MAX_EYE_BEAM_POS] =
{
{658.83f, 265.10f, 356.0f, 0.0f},
{706.22f, 273.26f, 356.0f, 0.0f},
{705.92f, 337.14f, 356.0f, 0.0f},
{656.86f, 344.07f, 356.0f, 0.0f}
};
Position illidanTakeoffPoint = { 727.6356f, 305.62753, 359.1486f };
Position illidanLand = { 676.648f, 304.76074f, 354.18906f, 6.230825424194335937f };
Position roomCenter = { 676.021f, 305.455f, 353.582f, 3.82227f };
Position const BladesPositions[2] =
{
{ 676.226013f, 325.230988f },
{ 678.059998f, 285.220001f }
};
class ChargeTargetSelector
{
public:
ChargeTargetSelector() { }
bool operator()(Unit* unit) const
{
return unit->IsPlayer()
&& 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), _canTalk(true), _dying(false), _inCutscene(false), beamPosId(0) { }
void Reset() override
{
BossAI::Reset();
me->m_Events.CancelEventGroup(GROUP_BERSERK);
me->m_Events.CancelEventGroup(GROUP_PHASE_FLYING);
me->m_Events.CancelEventGroup(GROUP_DEMON_FORM);
_canTalk = true;
_dying = false;
_inCutscene = false;
beamPosId = urand(0, MAX_EYE_BEAM_POS - 1);
me->ReplaceAllUnitFlags(UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
me->SetDisableGravity(false);
me->SetHover(false);
DoCastSelf(SPELL_DUAL_WIELD, true);
me->LoadEquipment(EQUIPMENT_GLAIVES, true);
me->SetStandState(UNIT_STAND_STATE_KNEEL);
me->SetSheath(SHEATH_STATE_UNARMED);
me->SetControlled(false, UNIT_STATE_ROOT);
me->SetCombatMovement(true);
ScheduleHealthCheckEvent(90, [&] {
// Call for minions
Talk(SAY_ILLIDAN_MINION);
if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN))
akama->AI()->DoAction(ACTION_AKAMA_MINIONS);
});
ScheduleHealthCheckEvent(65, [&] {
// Phase 2
scheduler.CancelAll();
DoAction(ACTION_ILLIDAN_LIFTOFF);
});
ScheduleHealthCheckEvent(30, [&] {
// Maiev Spawn Scene
scheduler.CancelAll();
if (me->HasAura(SPELL_DEMON_FORM))
DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM_BACK);
me->m_Events.CancelEventGroup(GROUP_DEMON_FORM);
DoAction(ACTION_SHADOW_PRISON);
});
}
void DoAction(int32 param) override
{
scheduler.CancelAll();
switch (param)
{
case ACTION_START_EVENT:
{
me->SetStandState(UNIT_STAND_STATE_STAND);
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_ILLIDAN_DUPLICITY);
}, 2210ms);
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION);
}, 5670ms); // 3460ms
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION);
}, 8100ms); // 2430ms
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION);
}, 11750ms); // 3650ms
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_ILLIDAN_UNCONVINCED);
}, 26950ms); // 15200ms
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_QUESTION);
}, 29980ms); // 3030ms
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_ILLIDAN_PREPARED);
}, 39710ms); // 9730ms
me->m_Events.AddEventAtOffset([&] {
me->SetSheath(SHEATH_STATE_MELEE);
}, 40920ms); // 1210ms
me->m_Events.AddEventAtOffset([&] {
me->RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
me->SetInCombatWithZone();
}, 43370ms); // 2450ms
}
break;
case ACTION_ILLIDAN_LIFTOFF:
{
me->SetReactState(REACT_PASSIVE);
DoStopAttack();
me->GetMotionMaster()->Clear();
me->StopMovingOnCurrentPos();
DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS, true);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
me->SetHover(true);
me->SetOrientation(2.837961435317993164f);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_ILLIDAN_TAKEOFF);
}, 3640ms);
me->m_Events.AddEventAtOffset([&] {
me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_TAKEOFF, illidanTakeoffPoint);
}, 7290ms); // 3650ms
}
break;
case ACTION_SHADOW_PRISON:
{
scheduler.CancelAll();
_inCutscene = true;
DoCastSelf(SPELL_SHADOW_PRISON, true);
DoStopAttack();
me->SetReactState(REACT_PASSIVE);
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_EMOTE_TALK_QUESTION);
}, 1200ms);
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_ILLIDAN_SHADOW_PRISON);
}, 1430ms); // 230ms
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_SUMMON_MAIEV);
}, 10940ms); // 9510ms
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_ILLIDAN_CONFRONT_MAIEV);
}, 19490ms); // 8550ms
me->m_Events.AddEventAtOffset([&] {
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H);
}, 23080ms); // 3590ms
me->m_Events.AddEventAtOffset([&] {
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_AGGRESSIVE);
ScheduleAbilities(PHASE_MAIEV);
_inCutscene = false;
}, 32830ms); // 9750ms
}
break;
case ACTION_ILLIDAN_DEMON_TRANSFORM:
{
scheduler.CancelAll();
me->SetReactState(REACT_PASSIVE);
DoStopAttack();
me->SetControlled(true, UNIT_STATE_ROOT);
me->SetCombatMovement(false);
DoResetThreatList();
DoCastSelf(SPELL_DEMON_TRANSFORM_1, true);
Talk(SAY_ILLIDAN_MORPH, 2630ms);
me->m_Events.AddEventAtOffset([&] {
// me->SetControlled(false, UNIT_STATE_ROOT);
me->SetReactState(REACT_AGGRESSIVE);
ScheduleAbilities(PHASE_DEMON);
}, 12230ms, GROUP_DEMON_FORM);
}
break;
case ACTION_ILLIDAN_DEMON_TRANSFORM_BACK:
{
scheduler.CancelAll();
me->SetReactState(REACT_AGGRESSIVE);
me->SetCombatMovement(true);
me->SetControlled(false, UNIT_STATE_ROOT);
me->RemoveAurasDueToSpell(SPELL_DEMON_TRANSFORM_1);
me->RemoveAurasDueToSpell(SPELL_DEMON_TRANSFORM_2);
me->RemoveAurasDueToSpell(SPELL_DEMON_TRANSFORM_3);
me->RemoveAurasDueToSpell(SPELL_DEMON_FORM);
me->LoadEquipment(EQUIPMENT_GLAIVES, true);
}
break;
case ACTION_ILLIDAN_CAGED:
{
scheduler.CancelAll();
me->RemoveAurasDueToSpell(SPELL_FRENZY);
DoCastSelf(SPELL_CAGE_TRAP_PERIODIC, true);
me->m_Events.AddEventAtOffset([&] {
DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM);
}, 15s, GROUP_DEMON_FORM);
}
break;
case ACTION_ILLIDAN_DIE:
{
me->m_Events.CancelEventGroup(GROUP_DEMON_FORM);
scheduler.CancelAll();
if (me->HasAura(SPELL_DEMON_FORM))
DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM_BACK);
_dying = true;
if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
maiev->AI()->DoAction(ACTION_MAIEV_ENDING);
if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN))
akama->AI()->DoAction(ACTION_AKAMA_ENDING);
me->m_Events.AddEventAtOffset([&] {
if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN))
akama->AI()->DoAction(ACTION_AKAMA_ENDING);
}, 1s);
me->SetControlled(true, UNIT_STATE_ROOT);
DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS, true);
DoCastSelf(SPELL_DEATH, true);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_ILLIDAN_DEFEATED);
}, 7490ms);
me->m_Events.AddEventAtOffset([&] {
Unit::Kill(nullptr, me);
}, 25530ms); // 18040ms
}
break;
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type == POINT_MOTION_TYPE)
{
switch (id)
{
case POINT_ILLIDAN_TAKEOFF:
{
me->SetFacingTo(me->GetAngle(&roomCenter));
Talk(SAY_ILLIDAN_SUMMONFLAMES);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_THROW_GLAIVE2);
}, 1210ms);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_THROW_GLAIVE);
me->LoadEquipment(EQUIPMENT_UNARMED, true);
}, 2430ms); // 1220ms
me->m_Events.AddEventAtOffset([&] {
ScheduleAbilities(PHASE_FLYING);
}, 3090ms); // 660ms
}
break;
case POINT_ILLIDAN_HOVER:
{
me->SetControlled(true, UNIT_STATE_ROOT);
scheduler.CancelAll();
ScheduleAbilities(PHASE_FLYING);
}
break;
case POINT_ILLIDAN_LAND:
{
EntryCheckPredicate pred(NPC_BLADE_OF_AZZINOTH);
summons.DoAction(ACTION_RETURN_BLADE, pred);
me->m_Events.AddEventAtOffset([&] {
me->LoadEquipment(EQUIPMENT_GLAIVES);
}, 1215ms);
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
me->SetHover(false);
}, 3665ms); // 2450ms
me->m_Events.AddEventAtOffset([&] {
DoResetThreatList();
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_AGGRESSIVE);
me->SetControlled(false, UNIT_STATE_ROOT);
ScheduleAbilities(PHASE_LANDING);
}, 6095ms); // 2430ms
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_HIT_CHANCE, true);
}, 7305ms); // 1210ms
}
break;
}
}
}
void ScheduleAbilities(uint8 phase)
{
switch (phase)
{
case PHASE_INITIAL:
{
ScheduleTimedEvent(25s, 30s, [&] {
DoCastVictim(SPELL_FLAME_CRASH);
}, 26s, 35s);
ScheduleTimedEvent(32s, [&] {
DoCastVictim(SPELL_DRAW_SOUL);
}, 32s);
ScheduleTimedEvent(25s, 30s, [&] {
DoCastRandomTarget(SPELL_PARASITIC_SHADOWFIEND, 0U, 100.f);
}, 25s, 30s);
// Custom from SunwellCore?
ScheduleTimedEvent(30s, 60s, [&] {
Talk(SAY_ILLIDAN_TAUNT);
}, 30s, 60s);
}
break;
case PHASE_FLYING:
{
me->SetFacingTo(me->GetAngle(&roomCenter));
scheduler.Schedule(0s, [this](TaskContext context)
{
// Do not repeat if interrupted (Eye Beam is cast)
if (DoCastRandomTarget(SPELL_FIREBALL, 0U, 50000.f, true, false, true) == SPELL_CAST_OK)
context.Repeat(2400ms);
}).Schedule(25s, 45s, [this](TaskContext /*context*/)
{
// Eye Blast
me->InterruptNonMeleeSpells(false);
Talk(SAY_ILLIDAN_EYE_BLAST);
me->SummonCreature(NPC_ILLIDAN_DB_TARGET, eyeBeamPos[beamPosId], TEMPSUMMON_TIMED_DESPAWN, 30000);
if (Creature* trigger = summons.GetCreatureWithEntry(NPC_ILLIDAN_DB_TARGET))
trigger->GetMotionMaster()->MovePoint(0, eyeBeamPos[beamPosId + MAX_EYE_BEAM_POS], FORCED_MOVEMENT_NONE, 0.f, false, true);
// Reposition
me->m_Events.AddEventAtOffset([&] {
scheduler.CancelAll();
me->InterruptNonMeleeSpells(false);
me->SetControlled(false, UNIT_STATE_ROOT);
CycleBeamPos(beamPosId);
me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_HOVER, airHoverPos[beamPosId], FORCED_MOVEMENT_NONE, 0.f, false, true);
}, 20s, GROUP_PHASE_FLYING);
});
// Check for Phase Transition
scheduler.Schedule(5s, [this](TaskContext context) {
if (!SelectTargetFromPlayerList(150.0f))
{
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
return;
}
summons.RemoveNotExisting();
if (!summons.HasEntry(NPC_FLAME_OF_AZZINOTH))
{
me->InterruptNonMeleeSpells(false);
me->SetControlled(false, UNIT_STATE_ROOT);
me->m_Events.CancelEventGroup(GROUP_PHASE_FLYING);
me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_LAND, illidanLand);
scheduler.CancelAll();
}
else
context.Repeat(3s);
});
}
break;
case PHASE_LANDING:
{
scheduler.CancelAll();
ScheduleTimedEvent(25s, 30s, [&] {
DoCastVictim(SPELL_FLAME_CRASH);
}, 26s, 35s);
ScheduleTimedEvent(32s, [&] {
DoCastVictim(SPELL_DRAW_SOUL);
}, 32s);
ScheduleTimedEvent(25s, 30s, [&] {
DoCastRandomTarget(SPELL_PARASITIC_SHADOWFIEND, 0U, 100.f);
}, 25s, 30s);
ScheduleTimedEvent(25s, [&] {
DoCastSelf(SPELL_AGONIZING_FLAMES);
}, 24s);
ScheduleTimedEvent(60s, [&] {
if (!_inCutscene)
DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM);
}, 60s);
}
break;
case PHASE_DEMON:
{
scheduler.CancelAll();
ScheduleTimedEvent(30s, [&] {
DoCastSelf(SPELL_SUMMON_SHADOW_DEMON, true);
}, 100s);
ScheduleTimedEvent(1s, 2500ms, [&] {
DoCastVictim(SPELL_SHADOW_BLAST);
}, 2500ms);
ScheduleTimedEvent(7s, [&] {
DoCastSelf(SPELL_FLAME_BURST);
}, 19500ms);
me->m_Events.AddEventAtOffset([&] {
DoAction(ACTION_ILLIDAN_DEMON_TRANSFORM_BACK);
if (summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
ScheduleAbilities(PHASE_MAIEV);
else
ScheduleAbilities(PHASE_LANDING);
DoResetThreatList();
}, 60s);
}
break;
case PHASE_MAIEV:
{
ScheduleAbilities(PHASE_LANDING);
ScheduleTimedEvent(40s, [&] {
Talk(SAY_ILLIDAN_FRENZY);
DoCastSelf(SPELL_FRENZY, true);
}, 100s);
ScheduleTimedEvent(30s, [&] {
if (Creature* maiev = summons.GetCreatureWithEntry(NPC_MAIEV_SHADOWSONG))
DoCast(maiev, SPELL_CAGE_TRAP, true);
}, 45s);
}
break;
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
ScheduleAbilities(PHASE_INITIAL);
if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN))
akama->AI()->AttackStart(me);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_BERSERK, true);
}, 25min, GROUP_BERSERK);
}
void EnterEvadeMode(EvadeReason why) override
{
if (_inCutscene)
return;
if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN))
akama->DespawnOnEvade();
BossAI::EnterEvadeMode(why);
me->DespawnOnEvade();
me->m_Events.CancelEventGroup(GROUP_BERSERK);
me->m_Events.CancelEventGroup(GROUP_PHASE_FLYING);
me->m_Events.CancelEventGroup(GROUP_DEMON_FORM);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
if (summon->GetEntry() == NPC_ILLIDAN_DB_TARGET)
DoCast(summon, SPELL_EYE_BLAST);
else if (summon->GetEntry() == NPC_MAIEV_SHADOWSONG)
{
me->SetTarget(summon->GetGUID());
me->SetFacingToObject(summon);
}
}
void KilledUnit(Unit* /*victim*/) override
{
if (_canTalk)
{
Talk(SAY_ILLIDAN_KILL);
_canTalk = false;
me->m_Events.AddEventAtOffset([&] {
_canTalk = true;
}, 6s); // 3590ms
}
}
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override
{
if (damage >= me->GetHealth())
{
damage = me->GetHealth() - 1;
if (!_dying)
DoAction(ACTION_ILLIDAN_DIE);
}
if (!_dying)
BossAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask);
}
void JustDied(Unit* killer) override
{
summons.clear();
BossAI::JustDied(killer);
}
bool CanAIAttack(Unit const* target) const override
{
return target->GetEntry() != NPC_AKAMA_ILLIDAN && target->GetEntry() != NPC_MAIEV_SHADOWSONG;
}
private:
bool _canTalk;
bool _dying;
bool _inCutscene;
uint8 beamPosId;
void CycleBeamPos(uint8 &beamPosId)
{
uint8 newPos;
do {
newPos = urand(0, MAX_EYE_BEAM_POS - 1);
} while (newPos == beamPosId);
beamPosId = newPos;
}
};
enum Akama
{
POINT_FACE_ILLIDAN = 1,
POINT_ILLIDAN_DEFEATED_1 = 2,
POINT_ILLIDAN_DEFEATED_2 = 3,
SPELL_AKAMA_DOOR_OPEN = 41268,
SPELL_AKAMA_DOOR_FAIL = 41271,
SPELL_DEATHSWORN_DOOR_OPEN = 41269,
SPELL_ARCANE_EXPLOSION_VIS = 35426,
SPELL_HEALING_POTION = 40535,
SPELL_CHAIN_LIGHTNING = 40536,
SPELL_REDUCED_THREAT = 41000,
SPELL_AKAMA_TELEPORT = 41077,
SPELL_AKAMA_DESPAWN = 41242,
NPC_ILLIDAN_DOOR_TRIGGER = 23412,
NPC_SPIRIT_OF_OLUM = 23411,
NPC_SPIRIT_OF_UDALO = 23410,
NPC_ILLIDARI_ELITE = 23226,
PATH_AKAMA_ILLIDARI_COUNCIL_1 = 230891,
PATH_AKAMA_ILLIDARI_COUNCIL_2 = 230892,
PATH_AKAMA_ILLIDARI_COUNCIL_3 = 230893,
PATH_AKAMA_MINIONS = 230894,
SAY_UDALO = 0,
SAY_OLUM = 0,
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_COUNCIL_1 = 8,
SAY_AKAMA_COUNCIL_2 = 9
};
Position AkamaIllidariCouncilTeleport = { 609.772f, 308.456f, 271.826f, 6.1972566f };
Position SpiritUdaloPos = { 751.4565f, 311.01065f, 312.18997f, 0.f };
Position SpiritOlumPos = { 751.6437f, 297.2233f, 312.20825f, 6.038839340209960937f };
Position FaceIllidan = { 745.225f, 304.946f, 352.98593f };
Position IllidanDefeated = { 753.04553f, 369.30273f, 353.1165f };
Position IllidariMinionPos[10] =
{
{ 750.0472f , 282.32742f, 309.4353f , 3.071779489517211914f },
{ 747.0576f , 326.42682f, 309.06885f, 0.0f },
{ 754.0332f , 325.81363f, 310.31952f, 2.914699792861938476f },
{ 745.25525f, 322.15738f, 310.45963f, 6.038839340209960937f },
{ 748.8422f , 288.06195f, 310.9782f , 1.884955525398254394f },
{ 745.3237f , 283.986f , 309.2765f , 0.628318548202514648f },
{ 743.9686f , 289.64468f, 311.18066f, 6.056292533874511718f },
{ 751.08777f, 327.6505f , 309.45758f, 6.17846536636352539f },
{ 750.03217f, 323.60635f, 310.27567f, 5.497786998748779296f },
{ 753.8425f , 286.56195f, 310.9353f , 1.029744267463684082f }
};
struct npc_akama_illidan : public ScriptedAI
{
npc_akama_illidan(Creature* creature) : ScriptedAI(creature), summons(me)
{
instance = creature->GetInstanceScript();
}
void Reset() override
{
scheduler.CancelAll();
me->m_Events.KillAllEvents(false);
me->SetReactState(REACT_AGGRESSIVE);
if (instance->GetBossState(DATA_ILLIDAN_STORMRAGE) == DONE)
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
else
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
me->setActive(false);
summons.DespawnAll();
DoCastSelf(SPELL_REDUCED_THREAT, true);
me->SetControlled(false, UNIT_STATE_ROOT);
}
void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
{
CloseGossipMenuFor(player);
me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE);
me->setActive(true);
if (instance->GetBossState(DATA_AKAMA_ILLIDAN) != DONE)
{
me->GetMotionMaster()->MoveWaypoint(PATH_AKAMA_ILLIDARI_COUNCIL_2, false);
}
else
{
me->SetSheath(SHEATH_STATE_UNARMED);
me->GetMotionMaster()->MovePoint(POINT_FACE_ILLIDAN, FaceIllidan);
}
}
void DoAction(int32 param) override
{
switch (param)
{
case ACTION_ILLIDARI_COUNCIL_DONE:
{
me->NearTeleportTo(AkamaIllidariCouncilTeleport);
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
me->GetMotionMaster()->MoveWaypoint(PATH_AKAMA_ILLIDARI_COUNCIL_1, false);
}
break;
case ACTION_AKAMA_MINIONS:
{
EnterEvadeMode(EVADE_REASON_OTHER);
}
break;
case ACTION_AKAMA_ENDING:
{
if (me->IsEngaged())
{
summons.DespawnAll();
me->SetReactState(REACT_PASSIVE);
me->SetControlled(false, UNIT_STATE_ROOT);
}
else
{
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_DEFEATED_1, IllidanDefeated);
}
}
break;
case ACTION_AKAMA_MAIEV_DESPAWN:
{
Talk(SAY_AKAMA_LIGHT);
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE);
}, 3490ms);
me->m_Events.AddEventAtOffset([&] {
me->SetRespawnDelay(WEEK);
DoCastSelf(SPELL_AKAMA_DESPAWN);
}, 8340ms); // 4850ms
me->m_Events.AddEventAtOffset([&] {
me->DespawnOnEvade();
}, 8740ms);
}
break;
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type == POINT_MOTION_TYPE)
{
switch (id)
{
case POINT_FACE_ILLIDAN:
{
if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE))
{
me->SetFacingToObject(illidan);
illidan->AI()->DoAction(ACTION_START_EVENT);
me->SetHomePosition(me->GetPosition());
}
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_AKAMA_FREE);
}, 15400ms);
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_TALK);
}, 19440ms); // 4040ms
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE);
}, 23080ms); // 3640ms
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_AKAMA_TIME_HAS_COME);
}, 33840ms); // 10760ms
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
me->SetSheath(SHEATH_STATE_MELEE);
}, 35210ms); // 1370ms
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H);
}, 37640ms); // 2430ms
}
break;
case POINT_ILLIDAN_DEFEATED_1:
{
me->SetWalk(true);
if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE))
{
float x, y, z;
me->GetNearPoint(illidan, x, y, z, 15.f, 0, me->GetAngle(illidan));
me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_DEFEATED_2, x, y, z); // Maiev starts Akama's Ending scene
}
}
break;
}
}
else if (type == WAYPOINT_MOTION_TYPE)
{
if (me->GetCurrentWaypointID() == PATH_AKAMA_MINIONS)
if (id == 3)
DoCastSelf(SPELL_AKAMA_TELEPORT);
}
}
void PathEndReached(uint32 pathId) override
{
switch (pathId)
{
// Talk to Open Door
case PATH_AKAMA_ILLIDARI_COUNCIL_1:
{
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_AKAMA_COUNCIL_1);
}, 200ms);
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_AKAMA_COUNCIL_2);
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
}, 8s); // 7800ms
}
break;
// Reached Door
case PATH_AKAMA_ILLIDARI_COUNCIL_2:
{
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_AKAMA_DOOR);
}, 4600ms);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_AKAMA_DOOR_FAIL);
}, 8120ms); // 3520ms
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_AKAMA_ALONE);
}, 17860ms); // 9740ms
me->m_Events.AddEventAtOffset([&] {
me->SummonCreature(NPC_SPIRIT_OF_UDALO, SpiritUdaloPos, TEMPSUMMON_TIMED_DESPAWN, 60000);
me->SummonCreature(NPC_SPIRIT_OF_OLUM, SpiritOlumPos, TEMPSUMMON_TIMED_DESPAWN, 60000);
}, 23930ms); // 6070ms
me->m_Events.AddEventAtOffset([&] {
if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f))
udalo->AI()->Talk(SAY_UDALO);
}, 25190ms); // 1260ms
me->m_Events.AddEventAtOffset([&] {
if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f))
olum->AI()->Talk(SAY_OLUM);
}, 31370ms); // 6180ms
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_AKAMA_DOOR_OPEN);
if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f))
udalo->AI()->DoCastSelf(SPELL_DEATHSWORN_DOOR_OPEN);
if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f))
olum->AI()->DoCastSelf(SPELL_DEATHSWORN_DOOR_OPEN);
}, 39710ms); // 8340ms
me->m_Events.AddEventAtOffset([&] {
if (Creature* door = me->FindNearestCreature(NPC_ILLIDAN_DOOR_TRIGGER, 15.0f))
door->AI()->DoCastSelf(SPELL_ARCANE_EXPLOSION_VIS);
}, 50660ms); // 10950ms
me->m_Events.AddEventAtOffset([&] {
me->InterruptNonMeleeSpells(false);
if (Creature* udalo = me->FindNearestCreature(NPC_SPIRIT_OF_UDALO, 15.0f))
udalo->InterruptNonMeleeSpells(false);
if (Creature* olum = me->FindNearestCreature(NPC_SPIRIT_OF_OLUM, 15.0f))
olum->InterruptNonMeleeSpells(false);
instance->SetBossState(DATA_AKAMA_ILLIDAN, NOT_STARTED);
instance->SetBossState(DATA_AKAMA_ILLIDAN, DONE);
}, 50680ms); // 20ms
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_AKAMA_SALUTE);
}, 56955ms); // 6275ms
me->m_Events.AddEventAtOffset([&] {
me->GetMotionMaster()->MoveWaypoint(PATH_AKAMA_ILLIDARI_COUNCIL_3, false);
}, 64030ms); // 7075ms
}
break;
// Talk to Initiate Fight
case PATH_AKAMA_ILLIDARI_COUNCIL_3:
{
Talk(SAY_AKAMA_BETRAYER);
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
}
break;
case PATH_AKAMA_MINIONS:
{
me->SetControlled(true, UNIT_STATE_ROOT);
me->SetReactState(REACT_AGGRESSIVE);
for (int i = 0; i < 10; ++i)
me->SummonCreature(NPC_ILLIDARI_ELITE, IllidariMinionPos[i], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1200);
}
break;
}
}
void JustReachedHome() override
{
// Minions Event
if (instance->GetBossState(DATA_ILLIDAN_STORMRAGE) == IN_PROGRESS && !instance->GetCreature(DATA_ILLIDAN_STORMRAGE)->HasAura(SPELL_DEATH))
{
me->SetReactState(REACT_PASSIVE);
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H);
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_AKAMA_MINIONS);
}, 6700ms);
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION);
}, 9530ms); // 2830ms
me->m_Events.AddEventAtOffset([&] {
me->GetMotionMaster()->MoveWaypoint(PATH_AKAMA_MINIONS, false);
}, 14400ms); // 4870ms
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE);
if (summon->GetEntry() == NPC_ILLIDARI_ELITE)
{
me->AddThreat(summon, 1000000.0f);
summon->AddThreat(me, 1000000.0f);
summon->AI()->AttackStart(me);
AttackStart(summon);
}
}
void SummonedCreatureDies(Creature* summon, Unit*) override
{
summons.Despawn(summon);
}
void KilledUnit(Unit* victim) override
{
if (victim->GetEntry() == NPC_ILLIDARI_ELITE)
me->SummonCreature(NPC_ILLIDARI_ELITE, IllidariMinionPos[urand(0, 9)], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1200);
}
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(12s, 18s, [&] {
DoCastVictim(SPELL_CHAIN_LIGHTNING);
}, 16s, 24s);
ScheduleTimedEvent(5s, 10s, [&] {
if (me->HealthBelowPct(20))
DoCastSelf(SPELL_HEALING_POTION);
}, 5s, 10s);
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= me->GetHealth())
damage = me->GetHealth() - 1;
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
private:
InstanceScript* instance;
SummonList summons;
};
enum Maiev
{
SPELL_MAIEV_DOWN = 40409,
SPELL_THROW_DAGGER = 41152,
SPELL_SHADOW_STRIKE = 40685,
SPELL_CAGE_TRAP_SUMMON = 40694,
SPELL_TELEPORT_VISUAL = 41236,
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
};
struct npc_maiev_illidan : public ScriptedAI
{
npc_maiev_illidan(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
}
void Reset() override
{
scheduler.CancelAll();
me->m_Events.KillAllEvents(true);
}
void DoAction(int32 param) override
{
if (param == ACTION_MAIEV_ENDING)
{
scheduler.CancelAll();
me->SetReactState(REACT_PASSIVE);
DoStopAttack();
me->CombatStop();
me->StopMovingOnCurrentPos();
me->RemoveAurasDueToSpell(SPELL_MAIEV_DOWN);
me->SetWalk(true);
if (Creature* illidan = instance->GetCreature(DATA_ILLIDAN_STORMRAGE))
{
if (!me->IsWithinDist(illidan, 15.f))
{
float x, y, z;
me->GetNearPoint(illidan, x, y, z, 15.f, 0, me->GetAngle(illidan));
me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_DEFEATED_2, x, y, z);
}
}
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_MAIEV_SHADOWSONG_FINISHED);
}, 1420ms);
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_MAIEV_SHADOWSONG_OUTRO);
}, 28550ms); // 27130ms
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_MAIEV_SHADOWSONG_FAREWELL);
}, 39510ms); // 10960ms
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_TELEPORT_VISUAL);
}, 41700ms); // 2190ms
me->m_Events.AddEventAtOffset([&] {
if (Creature* akama = instance->GetCreature(DATA_AKAMA_ILLIDAN))
akama->AI()->DoAction(ACTION_AKAMA_MAIEV_DESPAWN);
me->DespawnOnEvade();
}, 43320ms); // 1620ms
}
}
void IsSummonedBy(WorldObject* summoner) override
{
me->SetReactState(REACT_PASSIVE);
me->SetFacingToObject(summoner);
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_MAIEV_SHADOWSONG_APPEAR);
}, 25ms);
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION);
}, 2415ms); // 2390ms
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_MAIEV_SHADOWSONG_JUSTICE);
}, 14815ms); // 12400ms
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_YES);
}, 17015ms); // 2200ms
me->m_Events.AddEventAtOffset([&] {
me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
}, 19445ms); // 2430ms
me->m_Events.AddEventAtOffset([&] {
me->SetReactState(REACT_AGGRESSIVE);
}, 21920ms); // 2475ms
me->m_Events.AddEventAtOffset([&] {
if (Creature* illidan = me->FindNearestCreature(NPC_ILLIDAN_STORMRAGE, 15.0f))
AttackStart(illidan);
}, 23095ms); // 1175ms
}
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(7s, [&] {
DoCastVictim(SPELL_THROW_DAGGER);
}, 30s);
ScheduleTimedEvent(22s, [&] {
DoCastVictim(SPELL_SHADOW_STRIKE);
if (roll_chance_i(50))
Talk(SAY_MAIEV_SHADOWSONG_TAUNT);
}, 30s);
ScheduleTimedEvent(1200ms, [&] {
if (me->HealthBelowPct(20))
{
Talk(SAY_MAIEV_SHADOWSONG_DOWN);
DoCastSelf(SPELL_MAIEV_DOWN);
}
}, 1200ms);
}
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_CAGE_TRAP)
{
DoCastSelf(SPELL_CAGE_TRAP_SUMMON, true);
Talk(SAY_MAIEV_SHADOWSONG_TRAP);
}
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= me->GetHealth())
damage = me->GetHealth() - 1;
}
void UpdateAI(uint32 diff) override
{
if (!me->HasUnitState(UNIT_STATE_STUNNED))
scheduler.Update(diff);
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
private:
InstanceScript* instance;
};
struct npc_parasitic_shadowfiend : public ScriptedAI
{
npc_parasitic_shadowfiend(Creature* creature) : ScriptedAI(creature) { }
bool CanAIAttack(Unit const* who) const override
{
return !who->HasAura(SPELL_PARASITIC_SHADOWFIEND) && !who->HasAura(SPELL_PARASITIC_SHADOWFIEND_TRIGGER) && who->IsPlayer();
}
void EnterEvadeMode(EvadeReason /*why*/) override
{
me->DespawnOrUnsummon();
}
void IsSummonedBy(WorldObject* /*summoner*/) override
{
// Simulate blizz-like AI delay to avoid extreme overpopulation of adds
me->SetReactState(REACT_DEFENSIVE);
scheduler.Schedule(2400ms, [this](TaskContext context)
{
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
context.Repeat();
});
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
};
enum WarbladeTear
{
SOUND_WARBLADE_SPAWN = 11689,
SPELL_SUMMON_TEAR = 39855,
SPELL_TEAR_CHANNEL = 39857,
MODEL_INVISIBLE = 11686
};
struct npc_blade_of_azzinoth : public ScriptedAI
{
npc_blade_of_azzinoth(Creature* creature) : ScriptedAI(creature)
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
}
void IsSummonedBy(WorldObject* /*summoner*/) override
{
me->SetReactState(REACT_PASSIVE);
me->PlayRadiusSound(SOUND_WARBLADE_SPAWN, 150.f);
me->SetInCombatWithZone();
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_SUMMON_TEAR);
}, 2700ms);
}
void JustSummoned(Creature* summon) override
{
DoCast(summon, SPELL_TEAR_CHANNEL, true);
}
void DoAction(int32 param) override
{
if (param == ACTION_RETURN_BLADE)
{
if (Creature* illidan = me->FindNearestCreature(NPC_ILLIDAN_STORMRAGE, 100.0f))
DoCast(illidan, SPELL_GLAIVE_RETURNS, true);
me->m_Events.AddEventAtOffset([&] {
me->SetDisplayId(MODEL_INVISIBLE);
}, 10ms);
me->m_Events.AddEventAtOffset([&] {
me->DespawnOrUnsummon();
}, 2020ms);
}
}
};
enum FlameAzzinoth
{
NPC_BLAZE = 23259,
SPELL_BLAZE_EFFECT = 40610,
SPELL_FLAME_BLAST = 40631,
SPELL_CHARGE = 42003,
SPELL_UNCAGED_WRATH = 39869,
SPELL_BLAZE = 40637
};
struct npc_flame_of_azzinoth : public ScriptedAI
{
npc_flame_of_azzinoth(Creature* creature) : ScriptedAI(creature) { }
void IsSummonedBy(WorldObject* /*summoner*/) override
{
// Flame is set to be Illidan's summon, so we check for nearest blade
if (Creature* _blade = me->FindNearestCreature(NPC_BLADE_OF_AZZINOTH, 15.0f))
_bladeGUID = _blade->GetGUID();
me->SetCorpseDelay(2);
me->SetReactState(REACT_DEFENSIVE);
me->m_Events.AddEventAtOffset([&] {
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
}, 2020ms);
}
void JustSummoned(Creature* summon) override
{
if (summon->GetEntry() == NPC_BLAZE)
{
summon->SetReactState(REACT_PASSIVE);
summon->AI()->DoCastSelf(SPELL_BLAZE_EFFECT, true);
summon->SetCombatMovement(false);
}
}
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(10s, [&] {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, ChargeTargetSelector()))
DoCast(target, SPELL_CHARGE);
}, 5s, 20s);
ScheduleTimedEvent(10s, 20s, [&] {
DoCastVictim(SPELL_FLAME_BLAST);
me->m_Events.AddEventAtOffset([&] {
if (Unit* victim = me->GetVictim())
victim->CastSpell(victim, SPELL_BLAZE, true);
}, 1s);
}, 15s, 20s);
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
private:
ObjectGuid _bladeGUID;
};
class spell_illidan_draw_soul : public SpellScript
{
PrepareSpellScript(spell_illidan_draw_soul);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_DRAW_SOUL_HEAL });
}
void HandleScriptEffect(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
if (Unit* target = GetHitUnit())
target->CastSpell(GetCaster(), SPELL_DRAW_SOUL_HEAL, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidan_draw_soul::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_illidan_parasitic_shadowfiend_aura : public AuraScript
{
PrepareAuraScript(spell_illidan_parasitic_shadowfiend_aura);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SUMMON_PARASITIC_SHADOWFIENDS });
}
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (!GetTarget()->HasAura(SPELL_SHADOW_PRISON) && GetTarget()->GetInstanceScript() && GetTarget()->GetInstanceScript()->IsEncounterInProgress())
GetTarget()->CastSpell(GetTarget(), SPELL_SUMMON_PARASITIC_SHADOWFIENDS, true);
}
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_parasitic_shadowfiend_aura::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
}
};
class spell_illidan_parasitic_shadowfiend_trigger : public SpellScript
{
PrepareSpellScript(spell_illidan_parasitic_shadowfiend_trigger);
void HandleScriptEffect(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
if (Creature* target = GetHitCreature())
target->DespawnOrUnsummon(1ms);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidan_parasitic_shadowfiend_trigger::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_illidan_parasitic_shadowfiend_trigger_aura : public AuraScript
{
PrepareAuraScript(spell_illidan_parasitic_shadowfiend_trigger_aura);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SUMMON_PARASITIC_SHADOWFIENDS });
}
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (!GetTarget()->HasAura(SPELL_SHADOW_PRISON) && GetTarget()->GetInstanceScript() && GetTarget()->GetInstanceScript()->IsEncounterInProgress())
GetTarget()->CastSpell(GetTarget(), SPELL_SUMMON_PARASITIC_SHADOWFIENDS, true);
}
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_parasitic_shadowfiend_trigger_aura::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
}
};
class spell_illidan_glaive_throw : public SpellScript
{
PrepareSpellScript(spell_illidan_glaive_throw);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SUMMON_GLAIVE });
}
void HandleDummy(SpellEffIndex effIndex)
{
PreventHitEffect(effIndex);
if (Unit* target = GetHitUnit())
target->CastSpell(target, SPELL_SUMMON_GLAIVE, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidan_glaive_throw::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
class spell_illidan_tear_of_azzinoth_summon_channel_aura : public AuraScript
{
PrepareAuraScript(spell_illidan_tear_of_azzinoth_summon_channel_aura);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_UNCAGED_WRATH, SPELL_CHARGE });
}
void OnPeriodic(AuraEffect const* /*aurEff*/)
{
PreventDefaultAction();
if (Unit* caster = GetCaster())
{
if (GetTarget()->GetDistance2d(caster) > 30.0f)
{
SetDuration(0);
GetTarget()->CastSpell(GetTarget(), SPELL_UNCAGED_WRATH, true);
}
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_tear_of_azzinoth_summon_channel_aura::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
class spell_illidan_shadow_prison : public SpellScript
{
PrepareSpellScript(spell_illidan_shadow_prison);
void FilterTargets(std::list& targets)
{
targets.remove_if(PlayerOrPetCheck());
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_shadow_prison::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
class spell_illidan_shadow_prison_aura : public AuraScript
{
PrepareAuraScript(spell_illidan_shadow_prison_aura);
void HandleOnEffectApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
{
GetTarget()->ApplySpellImmune(GetId(), IMMUNITY_SCHOOL, aurEff->GetMiscValue(), true);
}
void HandleOnEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
{
GetTarget()->ApplySpellImmune(GetId(), IMMUNITY_SCHOOL, aurEff->GetMiscValue(), false);
}
void Register() override
{
OnEffectApply += AuraEffectApplyFn(spell_illidan_shadow_prison_aura::HandleOnEffectApply, EFFECT_1, SPELL_AURA_DAMAGE_IMMUNITY, AURA_EFFECT_HANDLE_REAL);
OnEffectRemove += AuraEffectRemoveFn(spell_illidan_shadow_prison_aura::HandleOnEffectRemove, EFFECT_1, SPELL_AURA_DAMAGE_IMMUNITY, AURA_EFFECT_HANDLE_REAL);
}
};
class spell_illidan_demon_transform1_aura : public AuraScript
{
PrepareAuraScript(spell_illidan_demon_transform1_aura);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_DEMON_TRANSFORM_2 });
}
bool Load() override
{
return GetUnitOwner()->IsCreature();
}
void OnPeriodic(AuraEffect const* /*aurEff*/)
{
PreventDefaultAction();
SetDuration(0);
GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DEMON_TRANSFORM_2, true);
GetUnitOwner()->ToCreature()->LoadEquipment(0, true);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform1_aura::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
class spell_illidan_demon_transform2_aura : public AuraScript
{
PrepareAuraScript(spell_illidan_demon_transform2_aura);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_DEMON_FORM, SPELL_DEMON_TRANSFORM_3 });
}
bool Load() override
{
return GetUnitOwner()->IsCreature();
}
void OnPeriodic(AuraEffect const* aurEff)
{
PreventDefaultAction();
if (aurEff->GetTickNumber() == 1)
{
if (GetUnitOwner()->GetDisplayId() == GetUnitOwner()->GetNativeDisplayId())
GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DEMON_FORM, true);
else
GetUnitOwner()->RemoveAurasDueToSpell(SPELL_DEMON_FORM);
}
else if (aurEff->GetTickNumber() == 2)
{
SetDuration(0);
GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_DEMON_TRANSFORM_3, true);
if (Aura* aura = GetUnitOwner()->GetAura(SPELL_DEMON_TRANSFORM_3))
aura->SetDuration(4500);
if (!GetUnitOwner()->HasAura(SPELL_DEMON_FORM))
GetUnitOwner()->ToCreature()->LoadEquipment(1, true);
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform2_aura::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
class spell_illidan_flame_burst : public SpellScript
{
PrepareSpellScript(spell_illidan_flame_burst);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_FLAME_BURST_EFFECT });
}
void HandleScriptEffect(SpellEffIndex effIndex)
{
PreventHitEffect(effIndex);
if (Unit* target = GetHitUnit())
target->CastSpell(target, SPELL_FLAME_BURST_EFFECT, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_burst::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_illidan_found_target : public SpellScript
{
PrepareSpellScript(spell_illidan_found_target);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_CONSUME_SOUL, SPELL_FIND_TARGET });
}
void HandleDummy(SpellEffIndex effIndex)
{
PreventHitEffect(effIndex);
if (Unit* target = GetHitUnit())
if (GetCaster()->GetDistance(target) < 2.0f)
{
GetCaster()->CastSpell(target, SPELL_CONSUME_SOUL, true);
GetCaster()->CastSpell(GetCaster(), SPELL_FIND_TARGET, true);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidan_found_target::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
class spell_illidan_cage_trap : public SpellScript
{
PrepareSpellScript(spell_illidan_cage_trap);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_CAGE_TRAP });
}
bool Load() override
{
return GetCaster()->IsCreature();
}
void HandleScriptEffect(SpellEffIndex effIndex)
{
PreventHitEffect(effIndex);
if (Creature* target = GetHitCreature())
if (GetCaster()->GetExactDist2d(target) < 4.0f)
{
target->AI()->DoAction(ACTION_ILLIDAN_CAGED);
GetCaster()->ToCreature()->DespawnOrUnsummon(1ms);
if (GameObject* gobject = GetCaster()->FindNearestGameObject(GO_CAGE_TRAP, 10.0f))
gobject->SetLootState(GO_JUST_DEACTIVATED);
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_illidan_cage_trap::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_illidan_cage_trap_stun_aura : public AuraScript
{
PrepareAuraScript(spell_illidan_cage_trap_stun_aura);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_CAGED_DEBUFF, SPELL_CAGED_SUMMON1, SPELL_CAGED_SUMMON1+1, SPELL_CAGED_SUMMON1+2, SPELL_CAGED_SUMMON1+3, SPELL_CAGED_SUMMON1+4, SPELL_CAGED_SUMMON1+5, SPELL_CAGED_SUMMON1+6, SPELL_CAGED_SUMMON8 });
}
void OnPeriodic(AuraEffect const* /*aurEff*/)
{
PreventDefaultAction();
SetDuration(0);
for (uint32 i = SPELL_CAGED_SUMMON1; i <= SPELL_CAGED_SUMMON8; ++i)
GetTarget()->CastSpell(GetTarget(), i, true);
GetTarget()->CastSpell(GetTarget(), SPELL_CAGED_DEBUFF, true);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_cage_trap_stun_aura::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
void AddSC_boss_illidan()
{
RegisterBlackTempleCreatureAI(boss_illidan_stormrage);
RegisterBlackTempleCreatureAI(npc_maiev_illidan);
RegisterBlackTempleCreatureAI(npc_akama_illidan);
RegisterBlackTempleCreatureAI(npc_parasitic_shadowfiend);
RegisterBlackTempleCreatureAI(npc_blade_of_azzinoth);
RegisterBlackTempleCreatureAI(npc_flame_of_azzinoth);
RegisterSpellScript(spell_illidan_draw_soul);
RegisterSpellScript(spell_illidan_parasitic_shadowfiend_aura);
RegisterSpellAndAuraScriptPair(spell_illidan_parasitic_shadowfiend_trigger, spell_illidan_parasitic_shadowfiend_trigger_aura);
RegisterSpellScript(spell_illidan_glaive_throw);
RegisterSpellScript(spell_illidan_tear_of_azzinoth_summon_channel_aura);
RegisterSpellAndAuraScriptPair(spell_illidan_shadow_prison, spell_illidan_shadow_prison_aura);
RegisterSpellScript(spell_illidan_demon_transform1_aura);
RegisterSpellScript(spell_illidan_demon_transform2_aura);
RegisterSpellScript(spell_illidan_flame_burst);
RegisterSpellScript(spell_illidan_found_target);
RegisterSpellScript(spell_illidan_cage_trap);
RegisterSpellScript(spell_illidan_cage_trap_stun_aura);
}