/*
* 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 .
*/
/* ScriptData
SDName: Boss_Kiljaeden
SD%Complete: 80
SDComment: Sinister Reflection Model, Armageddon Visual, SAY_KJ_SHADOWSPIKE3, Emote, End Sequence
SDCategory: Sunwell_Plateau
EndScriptData */
/// @todo rewrite Armageddon
#include "ScriptMgr.h"
#include "GameObject.h"
#include "GameObjectAI.h"
#include "InstanceScript.h"
#include "Log.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "sunwell_plateau.h"
#include "TemporarySummon.h"
#include
/*** Speech and sounds***/
enum Yells
{
SAY_KJ_OFFCOMBAT = 0,
SAY_KALECGOS_ENCOURAGE = 0,
SAY_KALECGOS_READY1 = 1,
SAY_KALECGOS_READY2 = 2,
SAY_KALECGOS_READY3 = 3,
SAY_KALECGOS_READY4 = 4,
SAY_KALECGOS_AWAKEN = 5,
SAY_KALECGOS_LETGO = 6,
SAY_KALECGOS_FOCUS = 7,
SAY_KALECGOS_FATE = 8,
SAY_KALECGOS_GOODBYE = 9,
SAY_KALECGOS_JOIN = 10,
SAY_KJ_DEATH = 0,
SAY_KJ_SLAY = 1,
SAY_KJ_REFLECTION = 2,
SAY_KJ_EMERGE = 3,
SAY_KJ_DARKNESS = 4,
SAY_KJ_PHASE3 = 5,
SAY_KJ_PHASE4 = 6,
SAY_KJ_PHASE5 = 7,
EMOTE_KJ_DARKNESS = 8,
SAY_ANVEENA_IMPRISONED = 0,
SAY_ANVEENA_LOST = 1,
SAY_ANVEENA_KALEC = 2,
SAY_ANVEENA_GOODBYE = 3
};
/*** Spells used during the encounter ***/
enum Spells
{
/* Hand of the Deceiver's spells and cosmetics */
SPELL_SHADOW_BOLT_VOLLEY = 45770, // ~30 yard range Shadow Bolt Volley for ~2k(?) damage
SPELL_SHADOW_INFUSION = 45772, // They gain this at 20% - Immunity to Stun/Silence and makes them look angry!
SPELL_FELFIRE_PORTAL = 46875, // Creates a portal that spawns Felfire Fiends (LIVE FOR THE SWARM!1 FOR THE OVERMIND!)
SPELL_SHADOW_CHANNELING = 46757, // Channeling animation out of combat
/* Volatile Felfire Fiend's spells */
SPELL_FELFIRE_FISSION = 45779, // Felfire Fiends explode when they die or get close to target.
/* Kil'Jaeden's spells and cosmetics */
SPELL_TRANS = 23188, // Surprisingly, this seems to be the right spell.. (Where is it used?)
SPELL_REBIRTH = 44200, // Emerge from the Sunwell
SPELL_SOUL_FLAY = 45442, // 9k Shadow damage over 3 seconds. Spammed throughout all the fight.
SPELL_SOUL_FLAY_SLOW = 47106,
SPELL_LEGION_LIGHTNING = 45664, // Chain Lightning, 4 targets, ~3k Shadow damage, 1.5fk mana burn
SPELL_FIRE_BLOOM = 45641, // Places a debuff on 5 raid members, which causes them to deal 2k Fire damage to nearby allies and selves. MIGHT NOT WORK
SPELL_DESTROY_ALL_DRAKES = 46707, // when he use it?
SPELL_SINISTER_REFLECTION = 45785, // Summon shadow copies of 5 raid members that fight against KJ's enemies//dont work
// 45892 // right one for SPELL_SINISTER_REFLECTION but no EffectScriptEffect
SPELL_COPY_WEAPON = 41055, // }
SPELL_COPY_WEAPON2 = 41054, // }
SPELL_COPY_OFFHAND = 45206, // }- Spells used in Sinister Reflection creation
SPELL_COPY_OFFHAND_WEAPON = 45205, // }
SPELL_SHADOW_SPIKE = 46680, // Bombard random raid members with Shadow Spikes (Very similar to Void Reaver orbs)
SPELL_FLAME_DART = 45737, // Bombards the raid with flames every 3(?) seconds
SPELL_DARKNESS_OF_A_THOUSAND_SOULS = 46605, // Begins a 8-second channeling, after which he will deal 50'000 damage to the raid
SPELL_DARKNESS_OF_A_THOUSAND_SOULS_DAMAGE = 45657,
/* Armageddon spells wrong visual */
SPELL_ARMAGEDDON_TRIGGER = 45909, // Meteor spell trigger missile should cast Creature on himself
SPELL_ARMAGEDDON_VISUAL = 45911, // Does the hellfire visual to indicate where the meteor missle lands
SPELL_ARMAGEDDON_VISUAL2 = 45914, // Does the light visual to indicate where the meteor missle lands
SPELL_ARMAGEDDON_VISUAL3 = 24207, // This shouldn't correct but same as seen on the movie
SPELL_ARMAGEDDON_SUMMON_TRIGGER = 45921, // Summons the triggers that cast the spells on himself need random target select
SPELL_ARMAGEDDON_DAMAGE = 45915, // This does the area damage
/* Shield Orb Spells*/
SPELL_SHADOW_BOLT = 45680, //45679 would be correct but triggers to often /// @todo fix console error
/* Anveena's spells and cosmetics (Or, generally, everything that has "Anveena" in name) */
SPELL_ANVEENA_PRISON = 46367, // She hovers locked within a bubble
SPELL_ANVEENA_ENERGY_DRAIN = 46410, // Sunwell energy glow animation (Control mob uses this)
SPELL_SACRIFICE_OF_ANVEENA = 46474, // This is cast on Kil'Jaeden when Anveena sacrifices herself into the Sunwell
/* Sinister Reflection Spells */
SPELL_SR_CURSE_OF_AGONY = 46190,
SPELL_SR_SHADOW_BOLT = 47076,
SPELL_SR_EARTH_SHOCK = 47071,
SPELL_SR_FIREBALL = 47074,
SPELL_SR_HEMORRHAGE = 45897,
SPELL_SR_HOLY_SHOCK = 38921,
SPELL_SR_HAMMER_OF_JUSTICE = 37369,
SPELL_SR_HOLY_SMITE = 47077,
SPELL_SR_RENEW = 47079,
SPELL_SR_SHOOT = 16496,
SPELL_SR_MULTI_SHOT = 48098,
SPELL_SR_WING_CLIP = 40652,
SPELL_SR_WHIRLWIND = 17207,
SPELL_SR_MOONFIRE = 47072,
//SPELL_SR_PLAGU STRIKE = 58843, Dk Spell!
/*** Other Spells (used by players, etc) ***/
SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT = 45839, // Possess the blue dragon from the orb to help the raid.
SPELL_ENTROPIUS_BODY = 46819, // Visual for Entropius at the Epilogue
SPELL_RING_OF_BLUE_FLAMES = 45825 //Cast this spell when the go is activated
};
/*** Others ***/
#define SHIELD_ORB_Z 45.000f
enum Phase
{
PHASE_DECEIVERS = 1, // Fight 3 adds
PHASE_NORMAL = 2, // Kil'Jaeden emerges from the sunwell
PHASE_DARKNESS = 3, // At 85%, he gains few abilities; Kalecgos joins the fight
PHASE_ARMAGEDDON = 4, // At 55%, he gains even more abilities
PHASE_SACRIFICE = 5 // At 25%, Anveena sacrifices herself into the Sunwell; at this point he becomes enraged and has *significally* shorter cooldowns.
};
//Timers
enum KilJaedenTimers
{
TIMER_SPEECH = 0,
//Phase 2 Timer
TIMER_SOUL_FLAY = 1,
TIMER_LEGION_LIGHTNING = 2,
TIMER_FIRE_BLOOM = 3,
TIMER_SUMMON_SHILEDORB = 4,
//Phase 3 Timer
TIMER_SHADOW_SPIKE = 5,
TIMER_FLAME_DART = 6,
TIMER_DARKNESS = 7,
TIMER_ORBS_EMPOWER = 8,
//Phase 4 Timer
TIMER_ARMAGEDDON = 9
};
// Locations of the Hand of Deceiver adds
Position DeceiverLocations[3]=
{
{1682.949951f, 637.75000f, 28.0231f, 5.717090f},
{1684.699951f, 614.41998f, 28.0580f, 0.698392f},
{1707.609985f, 612.15002f, 28.0946f, 1.990370f}
};
// Locations, where Shield Orbs will spawn
float ShieldOrbLocations[4][2]=
{
{1698.900f, 627.870f}, // middle pont of Sunwell
{12, 3.14f}, // First one spawns northeast of KJ
{12, 3.14f/0.7f}, // Second one spawns southeast
{12, 3.14f*3.8f} // Third one spawns (?)
};
struct Speech
{
int32 textid;
uint32 creature, timer;
};
// Timers
static Speech Speeches[]=
{
//Kil Phase 1 -> Phase 2
{SAY_KJ_EMERGE, DATA_KILJAEDEN, 0},
{SAY_KALECGOS_JOIN, DATA_KALECGOS_KJ, 26000},
//Kil Phase 2 -> Phase 3
{SAY_KALECGOS_AWAKEN, DATA_KALECGOS_KJ, 10000},
{SAY_ANVEENA_IMPRISONED, DATA_ANVEENA, 5000},
{SAY_KJ_PHASE3, DATA_KILJAEDEN, 5000},
//Kil Phase 3 -> Phase 4
{SAY_KALECGOS_LETGO, DATA_KALECGOS_KJ, 10000},
{SAY_ANVEENA_LOST, DATA_ANVEENA, 8000},
{SAY_KJ_PHASE4, DATA_KILJAEDEN, 7000},
//Kil Phase 4 -> Phase 5
{SAY_KALECGOS_FOCUS, DATA_KALECGOS_KJ, 4000},
{SAY_ANVEENA_KALEC, DATA_ANVEENA, 11000},
{SAY_KALECGOS_FATE, DATA_KALECGOS_KJ, 2000},
{SAY_ANVEENA_GOODBYE, DATA_ANVEENA, 6000},
{SAY_KJ_PHASE5, DATA_KILJAEDEN, 5500},
// use in End sequence?
{SAY_KALECGOS_GOODBYE, DATA_KALECGOS_KJ, 12000}
};
//AI for Kalecgos
class boss_kalecgos_kj : public CreatureScript
{
public:
boss_kalecgos_kj() : CreatureScript("boss_kalecgos_kj") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI(creature);
}
struct boss_kalecgos_kjAI : public ScriptedAI
{
boss_kalecgos_kjAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
instance = creature->GetInstanceScript();
}
void Initialize()
{
OrbsEmpowered = 0;
EmpowerCount = 0;
}
InstanceScript* instance;
uint8 OrbsEmpowered;
uint8 EmpowerCount;
void Reset() override
{
Initialize();
me->SetDisableGravity(true);
me->SetUninteractible(true);
me->setActive(true);
me->SetFarVisible(true);
for (uint8 i = 0; i < 4; ++i)
if (GameObject* pOrb = GetOrb(i))
pOrb->SetGoType(GAMEOBJECT_TYPE_BUTTON);
}
GameObject* GetOrb(int32 index)
{
switch (index)
{
case 0:
return ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_1));
case 1:
return ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_2));
case 2:
return ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_3));
case 3:
return ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_4));
}
return nullptr;
}
void ResetOrbs()
{
me->RemoveDynObject(SPELL_RING_OF_BLUE_FLAMES);
for (uint8 i = 0; i < 4; ++i)
if (GameObject* pOrb = GetOrb(i))
pOrb->SetFaction(FACTION_NONE);
}
void EmpowerOrb(bool all)
{
GameObject* pOrbEmpowered = GetOrb(OrbsEmpowered);
if (!pOrbEmpowered)
return;
if (all)
{
me->RemoveDynObject(SPELL_RING_OF_BLUE_FLAMES);
for (uint8 i = 0; i < 4; ++i)
{
if (GameObject* pOrb = GetOrb(i))
{
pOrb->CastSpell(me, SPELL_RING_OF_BLUE_FLAMES);
pOrb->SetFaction(FACTION_FRIENDLY);
pOrb->setActive(true);
pOrb->SetFarVisible(true);
pOrb->Refresh();
}
}
Talk(SAY_KALECGOS_ENCOURAGE);
}
else
{
if (GameObject* pOrb = GetOrb(urand(0, 3)))
{
pOrb->CastSpell(me, SPELL_RING_OF_BLUE_FLAMES);
pOrb->SetFaction(FACTION_FRIENDLY);
pOrb->setActive(true);
pOrb->SetFarVisible(true);
pOrb->Refresh();
OrbsEmpowered = (OrbsEmpowered+1)%4;
++EmpowerCount;
switch (EmpowerCount)
{
case 1: Talk(SAY_KALECGOS_READY1); break;
case 2: Talk(SAY_KALECGOS_READY2); break;
case 3: Talk(SAY_KALECGOS_READY3); break;
case 4: Talk(SAY_KALECGOS_READY4); break;
}
}
}
}
void UpdateAI(uint32 /*diff*/) override
{
}
void SetRingOfBlueFlames()
{
me->RemoveDynObject(SPELL_RING_OF_BLUE_FLAMES);
for (uint8 i = 0; i < 4; ++i)
{
if (GameObject* pOrb = GetOrb(i))
{
if (pOrb->GetFaction() == FACTION_FRIENDLY)
{
pOrb->CastSpell(me, SPELL_RING_OF_BLUE_FLAMES);
pOrb->setActive(true);
pOrb->SetFarVisible(true);
pOrb->Refresh();
}
}
}
}
};
};
class go_orb_of_the_blue_flight : public GameObjectScript
{
public:
go_orb_of_the_blue_flight() : GameObjectScript("go_orb_of_the_blue_flight") { }
struct go_orb_of_the_blue_flightAI : public GameObjectAI
{
go_orb_of_the_blue_flightAI(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { }
InstanceScript* instance;
bool OnGossipHello(Player* player) override
{
if (me->GetFaction() == FACTION_FRIENDLY)
{
player->SummonCreature(NPC_POWER_OF_THE_BLUE_DRAGONFLIGHT, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 121s);
player->CastSpell(player, SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT, false);
me->SetFaction(FACTION_NONE);
if (Creature* pKalec = instance->GetCreature(DATA_KALECGOS_KJ))
ENSURE_AI(boss_kalecgos_kj::boss_kalecgos_kjAI, pKalec->AI())->SetRingOfBlueFlames();
me->Refresh();
}
return true;
}
};
GameObjectAI* GetAI(GameObject* go) const override
{
return GetSunwellPlateauAI(go);
}
};
//AI for Kil'jaeden Event Controller
class npc_kiljaeden_controller : public CreatureScript
{
public:
npc_kiljaeden_controller() : CreatureScript("npc_kiljaeden_controller") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI(creature);
}
struct npc_kiljaeden_controllerAI : public ScriptedAI
{
npc_kiljaeden_controllerAI(Creature* creature) : ScriptedAI(creature), summons(me)
{
Initialize();
instance = creature->GetInstanceScript();
SetCombatMovement(false);
}
void Initialize()
{
phase = PHASE_DECEIVERS;
deceiverDeathCount = 0;
bSummonedDeceivers = false;
bKiljaedenDeath = false;
uiRandomSayTimer = 30000;
}
InstanceScript* instance;
SummonList summons;
bool bSummonedDeceivers;
bool bKiljaedenDeath;
uint32 uiRandomSayTimer;
uint32 phase;
uint8 deceiverDeathCount;
void InitializeAI() override
{
me->SetUninteractible(true);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->AddUnitState(UNIT_STATE_STUNNED);
ScriptedAI::InitializeAI();
}
void Reset() override
{
Initialize();
if (Creature* pKalecKJ = instance->GetCreature(DATA_KALECGOS_KJ))
ENSURE_AI(boss_kalecgos_kj::boss_kalecgos_kjAI, pKalecKJ->AI())->ResetOrbs();
summons.DespawnAll();
}
void JustSummoned(Creature* summoned) override
{
switch (summoned->GetEntry())
{
case NPC_HAND_OF_THE_DECEIVER:
summoned->CastSpell(summoned, SPELL_SHADOW_CHANNELING, false);
break;
case NPC_ANVEENA:
summoned->CastSpell(summoned, SPELL_ANVEENA_PRISON, true);
break;
case NPC_KILJAEDEN:
summoned->CastSpell(summoned, SPELL_REBIRTH, false);
AddThreat(me->GetVictim(), 1.0f, summoned);
break;
}
summons.Summon(summoned);
}
void UpdateAI(uint32 diff) override
{
if (uiRandomSayTimer < diff)
{
if (instance->GetBossState(DATA_MURU) != DONE && instance->GetBossState(DATA_KILJAEDEN) == NOT_STARTED)
Talk(SAY_KJ_OFFCOMBAT);
uiRandomSayTimer = 30000;
} else uiRandomSayTimer -= diff;
if (!bSummonedDeceivers)
{
for (uint8 i = 0; i < 3; ++i)
me->SummonCreature(NPC_HAND_OF_THE_DECEIVER, DeceiverLocations[i], TEMPSUMMON_DEAD_DESPAWN);
DoSpawnCreature(NPC_ANVEENA, 0, 0, 40, 0, TEMPSUMMON_DEAD_DESPAWN, 0s);
DoCast(me, SPELL_ANVEENA_ENERGY_DRAIN);
bSummonedDeceivers = true;
}
if (deceiverDeathCount > 2 && phase == PHASE_DECEIVERS)
{
me->RemoveAurasDueToSpell(SPELL_ANVEENA_ENERGY_DRAIN);
phase = PHASE_NORMAL;
DoSpawnCreature(NPC_KILJAEDEN, 0, 0, 0, 0, TEMPSUMMON_MANUAL_DESPAWN, 0s);
}
}
};
};
//AI for Kil'jaeden
class boss_kiljaeden : public CreatureScript
{
public:
boss_kiljaeden() : CreatureScript("boss_kiljaeden") { }
struct boss_kiljaedenAI : public ScriptedAI
{
boss_kiljaedenAI(Creature* creature) : ScriptedAI(creature), summons(me)
{
Initialize();
instance = creature->GetInstanceScript();
speechPhaseEnd = 0;
SetCombatMovement(false);
}
void Initialize()
{
TimerIsDeactivated[TIMER_SPEECH] = false;
Timer[TIMER_SPEECH] = 0;
//Phase 2 Timer
Timer[TIMER_SOUL_FLAY] = 11000;
Timer[TIMER_LEGION_LIGHTNING] = 30000;
Timer[TIMER_FIRE_BLOOM] = 20000;
Timer[TIMER_SUMMON_SHILEDORB] = 35000;
//Phase 3 Timer
Timer[TIMER_SHADOW_SPIKE] = 4000;
Timer[TIMER_FLAME_DART] = 3000;
Timer[TIMER_DARKNESS] = 45000;
Timer[TIMER_ORBS_EMPOWER] = 35000;
//Phase 4 Timer
Timer[TIMER_ARMAGEDDON] = 2000;
ActiveTimers = 5;
WaitTimer = 0;
speechCount = 0;
SpeechTimer = 0;
Phase = PHASE_NORMAL;
IsInDarkness = false;
IsWaiting = false;
OrbActivated = false;
SpeechBegins = true;
ChangeTimers(false, 0);
}
InstanceScript* instance;
SummonList summons;
uint8 Phase;
uint8 ActiveTimers;
uint32 SpeechTimer;
uint32 Timer[10];
uint32 WaitTimer;
uint8 speechCount;
uint8 speechPhaseEnd;
/* Boolean */
bool IsInDarkness;
bool TimerIsDeactivated[10];
bool IsWaiting;
bool OrbActivated;
bool SpeechBegins;
void InitializeAI() override
{
// Scripted_NoMovementAI::InitializeAI();
}
void Reset() override
{
Initialize();
if (Creature* pKalec = instance->GetCreature(DATA_KALECGOS_KJ))
pKalec->RemoveDynObject(SPELL_RING_OF_BLUE_FLAMES);
me->SetCombatReach(12.0f);
summons.DespawnAll();
}
void ChangeTimers(bool status, uint32 WTimer)
{
for (uint8 i = 1; i < ActiveTimers; ++i)
TimerIsDeactivated[i] = status;
if (WTimer > 0)
{
IsWaiting = true;
WaitTimer = WTimer;
}
if (OrbActivated)
TimerIsDeactivated[TIMER_ORBS_EMPOWER] = true;
if (Timer[TIMER_SHADOW_SPIKE] == 0)
TimerIsDeactivated[TIMER_SHADOW_SPIKE] = true;
if (Phase == PHASE_SACRIFICE)
TimerIsDeactivated[TIMER_SUMMON_SHILEDORB] = true;
}
void JustSummoned(Creature* summoned) override
{
if (summoned->GetEntry() == NPC_ARMAGEDDON_TARGET)
{
summoned->SetUninteractible(true);
summoned->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
// summoned->SetVisibility(VISIBILITY_OFF); //with this we cant see the armageddon visuals
}
else
summoned->SetLevel(me->GetLevel());
summoned->SetFaction(me->GetFaction());
summons.Summon(summoned);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_KJ_DEATH);
summons.DespawnAll();
instance->SetBossState(DATA_KILJAEDEN, DONE);
}
void KilledUnit(Unit* /*victim*/) override
{
Talk(SAY_KJ_SLAY);
}
void EnterEvadeMode(EvadeReason why) override
{
ScriptedAI::EnterEvadeMode(why);
summons.DespawnAll();
// Reset the controller
if (Creature* pControl = instance->GetCreature(DATA_KILJAEDEN_CONTROLLER))
ENSURE_AI(npc_kiljaeden_controller::npc_kiljaeden_controllerAI, pControl->AI())->Reset();
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
}
void EnterNextPhase()
{
SpeechBegins = true;
OrbActivated = false;
ChangeTimers(true, 0);//stop every cast Shadow spike will reactivate em all
TimerIsDeactivated[TIMER_SHADOW_SPIKE] = false;
Timer[TIMER_SHADOW_SPIKE] = 100;
// empowered orbs before darkness
Timer[TIMER_DARKNESS] = (Phase == PHASE_SACRIFICE) ? 15000 : urand(10000, 40000);
Timer[TIMER_ORBS_EMPOWER] = (Phase == PHASE_SACRIFICE) ? 10000 : 5000;
}
void CastSinisterReflection()
{
Talk(SAY_KJ_REFLECTION);
for (uint8 i = 0; i < 4; ++i)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true, true, -SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT))
{
float x, y, z;
target->GetPosition(x, y, z);
if (Creature* pSinisterReflection = me->SummonCreature(NPC_SINISTER_REFLECTION, x, y, z, 0, TEMPSUMMON_CORPSE_DESPAWN))
{
pSinisterReflection->SetDisplayId(target->GetDisplayId());
pSinisterReflection->AI()->AttackStart(target);
}
}
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() || Phase < PHASE_NORMAL)
return;
if (IsWaiting)
{
if (WaitTimer <= diff)
{
IsWaiting = false;
ChangeTimers(false, 0);
} else WaitTimer -= diff;
}
for (uint8 t = 0; t < ActiveTimers; ++t)
{
if (Timer[t] < diff && !TimerIsDeactivated[t])
{
switch (t)
{
case TIMER_SPEECH:
if (SpeechBegins)
{
SpeechBegins = false;
switch (Phase)
{
case PHASE_NORMAL:
speechPhaseEnd=1;
break;
case PHASE_DARKNESS:
speechPhaseEnd=4;
break;
case PHASE_ARMAGEDDON:
speechPhaseEnd=7;
break;
case PHASE_SACRIFICE:
speechPhaseEnd=12;
break;
}
}
if (Speeches[speechCount].timer < SpeechTimer)
{
SpeechTimer = 0;
if (Creature* speechCreature = instance->GetCreature(Speeches[speechCount].creature))
speechCreature->AI()->Talk(Speeches[speechCount].textid);
if (speechCount == 12)
if (Creature* pAnveena = instance->GetCreature(DATA_ANVEENA))
pAnveena->CastSpell(me, SPELL_SACRIFICE_OF_ANVEENA, false);
// ChangeTimers(true, 10000); // Kil should do an emote while screaming without attacking for 10 seconds
if (speechCount == speechPhaseEnd)
TimerIsDeactivated[TIMER_SPEECH]=true;
speechCount++;
}
SpeechTimer += diff;
break;
case TIMER_SOUL_FLAY:
if (!me->IsNonMeleeSpellCast(false))
{
DoCastVictim(SPELL_SOUL_FLAY_SLOW, false);
DoCastVictim(SPELL_SOUL_FLAY, false);
Timer[TIMER_SOUL_FLAY] = 3500;
}
break;
case TIMER_LEGION_LIGHTNING:
if (!me->IsNonMeleeSpellCast(false))
{
Unit* pRandomPlayer = nullptr;
me->RemoveAurasDueToSpell(SPELL_SOUL_FLAY);
for (uint8 z = 0; z < 6; ++z)
{
pRandomPlayer = SelectTarget(SelectTargetMethod::Random, 0, 100, true);
if (!pRandomPlayer || !pRandomPlayer->HasAura(SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT))
break;
}
if (pRandomPlayer)
DoCast(pRandomPlayer, SPELL_LEGION_LIGHTNING, false);
else
TC_LOG_ERROR("scripts", "try to cast SPELL_LEGION_LIGHTNING on invalid target");
Timer[TIMER_LEGION_LIGHTNING] = (Phase == PHASE_SACRIFICE) ? 18000 : 30000; // 18 seconds in PHASE_SACRIFICE
Timer[TIMER_SOUL_FLAY] = 2500;
}
break;
case TIMER_FIRE_BLOOM:
if (!me->IsNonMeleeSpellCast(false))
{
me->RemoveAurasDueToSpell(SPELL_SOUL_FLAY);
DoCastAOE(SPELL_FIRE_BLOOM, false);
Timer[TIMER_FIRE_BLOOM] = (Phase == PHASE_SACRIFICE) ? 25000 : 40000; // 25 seconds in PHASE_SACRIFICE
Timer[TIMER_SOUL_FLAY] = 1000;
}
break;
case TIMER_SUMMON_SHILEDORB:
for (uint8 i = 1; i < Phase; ++i)
{
float sx, sy;
sx = ShieldOrbLocations[0][0] + std::sin(ShieldOrbLocations[i][0]);
sy = ShieldOrbLocations[0][1] + std::sin(ShieldOrbLocations[i][1]);
me->SummonCreature(NPC_SHIELD_ORB, sx, sy, SHIELD_ORB_Z, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45s);
}
Timer[TIMER_SUMMON_SHILEDORB] = urand(30000, 60000); // 30-60seconds cooldown
Timer[TIMER_SOUL_FLAY] = 2000;
break;
case TIMER_SHADOW_SPIKE: //Phase 3
if (!me->IsNonMeleeSpellCast(false))
{
CastSinisterReflection();
DoCastAOE(SPELL_SHADOW_SPIKE, false);
ChangeTimers(true, 30000);
Timer[TIMER_SHADOW_SPIKE] = 0;
TimerIsDeactivated[TIMER_SPEECH] = false;
}
break;
case TIMER_FLAME_DART: //Phase 3
DoCastAOE(SPELL_FLAME_DART, false);
Timer[TIMER_FLAME_DART] = 3000; /// @todo Timer
break;
case TIMER_DARKNESS: //Phase 3
if (!me->IsNonMeleeSpellCast(false))
{
// Begins to channel for 8 seconds, then deals 50'000 damage to all raid members.
if (!IsInDarkness)
{
Talk(EMOTE_KJ_DARKNESS);
DoCastAOE(SPELL_DARKNESS_OF_A_THOUSAND_SOULS, false);
ChangeTimers(true, 9000);
Timer[TIMER_DARKNESS] = 8750;
TimerIsDeactivated[TIMER_DARKNESS] = false;
if (Phase == PHASE_SACRIFICE)
TimerIsDeactivated[TIMER_ARMAGEDDON] = false;
IsInDarkness = true;
}
else
{
Timer[TIMER_DARKNESS] = (Phase == PHASE_SACRIFICE) ? 15000 : urand(40000, 70000);
IsInDarkness = false;
DoCastAOE(SPELL_DARKNESS_OF_A_THOUSAND_SOULS_DAMAGE);
Talk(SAY_KJ_DARKNESS);
}
Timer[TIMER_SOUL_FLAY] = 9000;
}
break;
case TIMER_ORBS_EMPOWER: //Phase 3
if (Creature* pKalec = instance->GetCreature(DATA_KALECGOS_KJ))
{
switch (Phase)
{
case PHASE_SACRIFICE:
ENSURE_AI(boss_kalecgos_kj::boss_kalecgos_kjAI, pKalec->AI())->EmpowerOrb(true);
break;
default:
ENSURE_AI(boss_kalecgos_kj::boss_kalecgos_kjAI, pKalec->AI())->EmpowerOrb(false);
break;
}
}
OrbActivated = true;
TimerIsDeactivated[TIMER_ORBS_EMPOWER] = true;
break;
case TIMER_ARMAGEDDON: //Phase 4
Unit* target = nullptr;
for (uint8 z = 0; z < 6; ++z)
{
target = SelectTarget(SelectTargetMethod::Random, 0, 100, true);
if (!target || !target->HasAura(SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT)) break;
}
if (target)
{
float x, y, z;
target->GetPosition(x, y, z);
me->SummonCreature(NPC_ARMAGEDDON_TARGET, x, y, z, 0, TEMPSUMMON_TIMED_DESPAWN, 15s);
}
Timer[TIMER_ARMAGEDDON] = 2000; // No, I'm not kidding
break;
}
}
}
//Time runs over!
for (uint8 i = 0; i < ActiveTimers; ++i)
if (!TimerIsDeactivated[i])
{
Timer[i] -= diff;
if (((int32)Timer[i]) < 0) Timer[i] = 0;
}
//Phase 3
if (Phase <= PHASE_NORMAL && !IsInDarkness)
{
if (Phase == PHASE_NORMAL && HealthBelowPct(85))
{
Phase = PHASE_DARKNESS;
ActiveTimers = 9;
EnterNextPhase();
}
else return;
}
//Phase 4
if (Phase <= PHASE_DARKNESS && !IsInDarkness)
{
if (Phase == PHASE_DARKNESS && HealthBelowPct(55))
{
Phase = PHASE_ARMAGEDDON;
ActiveTimers = 10;
EnterNextPhase();
}
else return;
}
//Phase 5 specific spells all we can
if (Phase <= PHASE_ARMAGEDDON && !IsInDarkness)
{
if (Phase == PHASE_ARMAGEDDON && HealthBelowPct(25))
{
Phase = PHASE_SACRIFICE;
EnterNextPhase();
}
else return;
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI(creature);
}
};
//AI for Hand of the Deceiver
class npc_hand_of_the_deceiver : public CreatureScript
{
public:
npc_hand_of_the_deceiver() : CreatureScript("npc_hand_of_the_deceiver") { }
struct npc_hand_of_the_deceiverAI : public ScriptedAI
{
npc_hand_of_the_deceiverAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
instance = creature->GetInstanceScript();
}
void Initialize()
{
/// @todo Timers!
ShadowBoltVolleyTimer = urand(8000, 14000); // So they don't all cast it in the same moment.
FelfirePortalTimer = 20000;
}
InstanceScript* instance;
uint32 ShadowBoltVolleyTimer;
uint32 FelfirePortalTimer;
void Reset() override
{
Initialize();
instance->SetBossState(DATA_KILJAEDEN, NOT_STARTED);
}
void JustSummoned(Creature* summoned) override
{
summoned->SetFaction(me->GetFaction());
summoned->SetLevel(me->GetLevel());
}
void JustEngagedWith(Unit* who) override
{
instance->SetBossState(DATA_KILJAEDEN, IN_PROGRESS);
if (Creature* pControl = instance->GetCreature(DATA_KILJAEDEN_CONTROLLER))
AddThreat(who, 1.0f, pControl);
me->InterruptNonMeleeSpells(true);
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* pControl = instance->GetCreature(DATA_KILJAEDEN_CONTROLLER))
++(ENSURE_AI(npc_kiljaeden_controller::npc_kiljaeden_controllerAI, pControl->AI())->deceiverDeathCount);
}
void UpdateAI(uint32 diff) override
{
if (!me->IsInCombat())
DoCast(me, SPELL_SHADOW_CHANNELING);
if (!UpdateVictim())
return;
// Gain Shadow Infusion at 20% health
if (HealthBelowPct(20) && !me->HasAura(SPELL_SHADOW_INFUSION))
DoCast(me, SPELL_SHADOW_INFUSION, true);
// Shadow Bolt Volley - Shoots Shadow Bolts at all enemies within 30 yards, for ~2k Shadow damage.
if (ShadowBoltVolleyTimer <= diff)
{
DoCastVictim(SPELL_SHADOW_BOLT_VOLLEY);
ShadowBoltVolleyTimer = 12000;
}
else
ShadowBoltVolleyTimer -= diff;
// Felfire Portal - Creatres a portal, that spawns Volatile Felfire Fiends, which do suicide bombing.
if (FelfirePortalTimer <= diff)
{
if (Creature* pPortal = DoSpawnCreature(NPC_FELFIRE_PORTAL, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 20s))
for (ThreatReference const* ref : me->GetThreatManager().GetUnsortedThreatList())
AddThreat(ref->GetVictim(), 1.0f, pPortal);
FelfirePortalTimer = 20000;
} else FelfirePortalTimer -= diff;
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI(creature);
}
};
//AI for Felfire Portal
class npc_felfire_portal : public CreatureScript
{
public:
npc_felfire_portal() : CreatureScript("npc_felfire_portal") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI(creature);
}
struct npc_felfire_portalAI : public ScriptedAI
{
npc_felfire_portalAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
SetCombatMovement(false);
}
void Initialize()
{
uiSpawnFiendTimer = 5000;
}
uint32 uiSpawnFiendTimer;
void Reset() override
{
Initialize();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetUninteractible(true);
}
void JustSummoned(Creature* summoned) override
{
summoned->SetFaction(me->GetFaction());
summoned->SetLevel(me->GetLevel());
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (uiSpawnFiendTimer <= diff)
{
if (Creature* pFiend = DoSpawnCreature(NPC_VOLATILE_FELFIRE_FIEND, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 20s))
AddThreat(SelectTarget(SelectTargetMethod::Random, 0), 100000.0f, pFiend);
uiSpawnFiendTimer = urand(4000, 8000);
} else uiSpawnFiendTimer -= diff;
}
};
};
//AI for Felfire Fiend
class npc_volatile_felfire_fiend : public CreatureScript
{
public:
npc_volatile_felfire_fiend() : CreatureScript("npc_volatile_felfire_fiend") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI(creature);
}
struct npc_volatile_felfire_fiendAI : public ScriptedAI
{
npc_volatile_felfire_fiendAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
}
void Initialize()
{
uiExplodeTimer = 2000;
bLockedTarget = false;
}
uint32 uiExplodeTimer;
bool bLockedTarget;
void Reset() override
{
Initialize();
}
void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (damage > me->GetHealth())
DoCast(me, SPELL_FELFIRE_FISSION, true);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (!bLockedTarget)
{
AddThreat(me->GetVictim(), 10000000.0f);
bLockedTarget = true;
}
if (uiExplodeTimer)
{
if (uiExplodeTimer <= diff)
uiExplodeTimer = 0;
else uiExplodeTimer -= diff;
}
else if (me->IsWithinDistInMap(me->GetVictim(), 3)) // Explode if it's close enough to it's target
{
DoCastVictim(SPELL_FELFIRE_FISSION);
me->KillSelf();
}
}
};
};
//AI for Armageddon target
class npc_armageddon : public CreatureScript
{
public:
npc_armageddon() : CreatureScript("npc_armageddon") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI(creature);
}
struct npc_armageddonAI : public ScriptedAI
{
npc_armageddonAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
SetCombatMovement(false);
}
void Initialize()
{
spell = 0;
uiTimer = 0;
}
uint8 spell;
uint32 uiTimer;
void Reset() override
{
Initialize();
}
void UpdateAI(uint32 diff) override
{
if (uiTimer <= diff)
{
switch (spell)
{
case 0:
DoCast(me, SPELL_ARMAGEDDON_VISUAL, true);
++spell;
break;
case 1:
DoCast(me, SPELL_ARMAGEDDON_VISUAL2, true);
uiTimer = 9000;
++spell;
break;
case 2:
DoCast(me, SPELL_ARMAGEDDON_TRIGGER, true);
++spell;
uiTimer = 5000;
break;
case 3:
me->DespawnOrUnsummon();
break;
}
} else uiTimer -=diff;
}
};
};
//AI for Shield Orbs
class npc_shield_orb : public CreatureScript
{
public:
npc_shield_orb() : CreatureScript("npc_shield_orb") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI(creature);
}
struct npc_shield_orbAI : public ScriptedAI
{
npc_shield_orbAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
instance = creature->GetInstanceScript();
x = 0.f;
y = 0.f;
}
void Initialize()
{
bPointReached = true;
uiTimer = urand(500, 1000);
uiCheckTimer = 1000;
r = 17;
c = 0;
mx = ShieldOrbLocations[0][0];
my = ShieldOrbLocations[0][1];
bClockwise = roll_chance_i(50);
}
InstanceScript* instance;
bool bPointReached;
bool bClockwise;
uint32 uiTimer;
uint32 uiCheckTimer;
float x, y, r, c, mx, my;
void Reset() override
{
me->SetDisableGravity(true);
Initialize();
}
void UpdateAI(uint32 diff) override
{
if (bPointReached)
{
if (bClockwise)
{
y = my - r * std::sin(c);
x = mx - r * std::cos(c);
}
else
{
y = my + r * std::sin(c);
x = mx + r * std::cos(c);
}
bPointReached = false;
uiCheckTimer = 1000;
me->GetMotionMaster()->MovePoint(1, x, y, SHIELD_ORB_Z);
c += float(M_PI)/32;
if (c >= 2 * float(M_PI)) c = 0;
}
else
{
if (uiCheckTimer <= diff)
{
DoTeleportTo(x, y, SHIELD_ORB_Z);
bPointReached = true;
}
else uiCheckTimer -= diff;
}
if (uiTimer <= diff)
{
if (Unit* random = ObjectAccessor::GetPlayer(*me, instance->GetGuidData(DATA_PLAYER_GUID)))
DoCast(random, SPELL_SHADOW_BOLT, false);
uiTimer = urand(500, 1000);
} else uiTimer -= diff;
}
void MovementInform(uint32 type, uint32 /*id*/) override
{
if (type != POINT_MOTION_TYPE)
return;
bPointReached = true;
}
};
};
//AI for Sinister Reflection
class npc_sinster_reflection : public CreatureScript
{
public:
npc_sinster_reflection() : CreatureScript("npc_sinster_reflection") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI(creature);
}
struct npc_sinster_reflectionAI : public ScriptedAI
{
npc_sinster_reflectionAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
}
void Initialize()
{
uiTimer[0] = 0;
uiTimer[1] = 0;
uiTimer[2] = 0;
victimClass = 0;
}
uint8 victimClass;
uint32 uiTimer[3];
void Reset() override
{
Initialize();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if ((victimClass == 0) && me->GetVictim())
{
victimClass = me->EnsureVictim()->GetClass();
switch (victimClass)
{
case CLASS_DRUID:
break;
case CLASS_HUNTER:
break;
case CLASS_MAGE:
break;
case CLASS_WARLOCK:
break;
case CLASS_WARRIOR:
me->SetCanDualWield(true);
break;
case CLASS_PALADIN:
break;
case CLASS_PRIEST:
break;
case CLASS_SHAMAN:
me->SetCanDualWield(true);
break;
case CLASS_ROGUE:
me->SetCanDualWield(true);
break;
}
}
switch (victimClass)
{
case CLASS_DRUID:
if (uiTimer[1] <= diff)
{
DoCastVictim(SPELL_SR_MOONFIRE, false);
uiTimer[1] = urand(2000, 4000);
}
break;
case CLASS_HUNTER:
if (uiTimer[1] <= diff)
{
DoCastVictim(SPELL_SR_MULTI_SHOT, false);
uiTimer[1] = urand(8000, 10000);
}
if (uiTimer[2] <= diff)
{
DoCastVictim(SPELL_SR_SHOOT, false);
uiTimer[2] = urand(4000, 6000);
}
if (me->IsWithinMeleeRange(me->GetVictim()))
{
if (uiTimer[0] <= diff)
{
DoCastVictim(SPELL_SR_MULTI_SHOT, false);
uiTimer[0] = urand(6000, 8000);
}
}
break;
case CLASS_MAGE:
if (uiTimer[1] <= diff)
{
DoCastVictim(SPELL_SR_FIREBALL, false);
uiTimer[1] = urand(2000, 4000);
}
break;
case CLASS_WARLOCK:
if (uiTimer[1] <= diff)
{
DoCastVictim(SPELL_SR_SHADOW_BOLT, false);
uiTimer[1] = urand(3000, 5000);
}
if (uiTimer[2] <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 0, 100, true), SPELL_SR_CURSE_OF_AGONY, true);
uiTimer[2] = urand(2000, 4000);
}
break;
case CLASS_WARRIOR:
if (uiTimer[1] <= diff)
{
DoCastVictim(SPELL_SR_WHIRLWIND, false);
uiTimer[1] = urand(9000, 11000);
}
break;
case CLASS_PALADIN:
if (uiTimer[1] <= diff)
{
DoCastVictim(SPELL_SR_HAMMER_OF_JUSTICE, false);
uiTimer[1] = urand(6000, 8000);
}
if (uiTimer[2] <= diff)
{
DoCastVictim(SPELL_SR_HOLY_SHOCK, false);
uiTimer[2] = urand(2000, 4000);
}
break;
case CLASS_PRIEST:
if (uiTimer[1] <= diff)
{
DoCastVictim(SPELL_SR_HOLY_SMITE, false);
uiTimer[1] = urand(4000, 6000);
}
if (uiTimer[2] <= diff)
{
DoCast(me, SPELL_SR_RENEW, false);
uiTimer[2] = urand(6000, 8000);
}
break;
case CLASS_SHAMAN:
if (uiTimer[1] <= diff)
{
DoCastVictim(SPELL_SR_EARTH_SHOCK, false);
uiTimer[1] = urand(4000, 6000);
}
break;
case CLASS_ROGUE:
if (uiTimer[1] <= diff)
{
DoCastVictim(SPELL_SR_HEMORRHAGE, true);
uiTimer[1] = urand(4000, 6000);
}
break;
}
TC_LOG_DEBUG("scripts", "Sinister-Timer");
for (uint8 i = 0; i < 3; ++i)
uiTimer[i] -= diff;
}
};
};
void AddSC_boss_kiljaeden()
{
new go_orb_of_the_blue_flight();
new boss_kalecgos_kj();
new boss_kiljaeden();
new npc_kiljaeden_controller();
new npc_hand_of_the_deceiver();
new npc_felfire_portal();
new npc_volatile_felfire_fiend();
new npc_armageddon();
new npc_shield_orb();
new npc_sinster_reflection();
}