diff options
Diffstat (limited to 'src')
3 files changed, 697 insertions, 837 deletions
diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 93521f3b235..c837edcdaf1 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -25,115 +25,235 @@ EndScriptData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "Spell.h" #include "the_eye.h" -#include "WorldPacket.h" -#include "Opcodes.h" enum Yells { // Kael'thas Speech - SAY_INTRO = 0, - SAY_INTRO_CAPERNIAN = 1, - SAY_INTRO_TELONICUS = 2, - SAY_INTRO_THALADRED = 3, - SAY_INTRO_SANGUINAR = 4, - SAY_PHASE2_WEAPON = 5, - SAY_PHASE3_ADVANCE = 6, - SAY_PHASE4_INTRO2 = 7, - SAY_PHASE5_NUTS = 8, - SAY_SLAY = 9, - SAY_MINDCONTROL = 10, - SAY_GRAVITYLAPSE = 11, - SAY_SUMMON_PHOENIX = 12, - SAY_DEATH = 13, + SAY_INTRO = 0, + SAY_INTRO_CAPERNIAN = 1, + SAY_INTRO_TELONICUS = 2, + SAY_INTRO_THALADRED = 3, + SAY_INTRO_SANGUINAR = 4, + SAY_PHASE2_WEAPON = 5, + SAY_PHASE3_ADVANCE = 6, + SAY_PHASE4_INTRO2 = 7, + SAY_PHASE5_NUTS = 8, + SAY_SLAY = 9, + SAY_MIND_CONTROL = 10, + SAY_GRAVITY_LAPSE = 11, + SAY_SUMMON_PHOENIX = 12, + SAY_DEATH = 13, + EMOTE_PYROBLAST = 14, // Thaladred the Darkener speech - SAY_THALADRED_AGGRO = 0, - SAY_THALADRED_DEATH = 1, - EMOTE_THALADRED_GAZE = 2, + SAY_THALADRED_AGGRO = 0, + SAY_THALADRED_DEATH = 1, + EMOTE_THALADRED_GAZE = 2, //Lord Sanguinar speech - SAY_SANGUINAR_AGGRO = 0, - SAY_SANGUINAR_DEATH = 1, + SAY_SANGUINAR_AGGRO = 0, + SAY_SANGUINAR_DEATH = 1, // Grand Astromancer Capernian speech - SAY_CAPERNIAN_AGGRO = 0, - SAY_CAPERNIAN_DEATH = 1, + SAY_CAPERNIAN_AGGRO = 0, + SAY_CAPERNIAN_DEATH = 1, // Master Engineer Telonicus speech - SAY_TELONICUS_AGGRO = 0, - SAY_TELONICUS_DEATH = 1 + SAY_TELONICUS_AGGRO = 0, + SAY_TELONICUS_DEATH = 1 }; enum Spells { // Phase 2 spells - SPELL_SUMMON_WEAPONS = 36976, - SPELL_SUMMON_WEAPONA = 36958, - SPELL_SUMMON_WEAPONB = 36959, - SPELL_SUMMON_WEAPONC = 36960, - SPELL_SUMMON_WEAPOND = 36961, - SPELL_SUMMON_WEAPONE = 36962, - SPELL_SUMMON_WEAPONF = 36963, - SPELL_SUMMON_WEAPONG = 36964, - SPELL_RES_VISUAL = 24171, + SPELL_SUMMON_WEAPONS = 36976, + SPELL_SUMMON_WEAPONA = 36958, + SPELL_SUMMON_WEAPONB = 36959, + SPELL_SUMMON_WEAPONC = 36960, + SPELL_SUMMON_WEAPOND = 36961, + SPELL_SUMMON_WEAPONE = 36962, + SPELL_SUMMON_WEAPONF = 36963, + SPELL_SUMMON_WEAPONG = 36964, + SPELL_RESSURECTION = 36450, // Phase 4 spells - SPELL_FIREBALL = 22088, //wrong but works with CastCustomSpell - SPELL_PYROBLAST = 36819, - SPELL_FLAME_STRIKE = 36735, - SPELL_FLAME_STRIKE_VIS = 36730, - SPELL_FLAME_STRIKE_DMG = 36731, - SPELL_ARCANE_DISRUPTION = 36834, - SPELL_SHOCK_BARRIER = 36815, - SPELL_PHOENIX_ANIMATION = 36723, - SPELL_MIND_CONTROL = 32830, + SPELL_FIREBALL = 36805, + SPELL_PYROBLAST = 36819, + SPELL_FLAME_STRIKE = 36735, + SPELL_FLAME_STRIKE_VIS = 36730, + SPELL_FLAME_STRIKE_DMG = 36731, + SPELL_ARCANE_DISRUPTION = 36834, + SPELL_SHOCK_BARRIER = 36815, + SPELL_PHOENIX_ANIMATION = 36723, + //SPELL_MIND_CONTROL = 32830, + SPELL_MIND_CONTROL = 36797, + SPELL_BANISH = 40370, // Cast on Phoenix // Phase 5 spells - SPELL_EXPLODE = 36092, - SPELL_FULLPOWER = 36187, - SPELL_KNOCKBACK = 11027, - SPELL_GRAVITY_LAPSE = 34480, - SPELL_GRAVITY_LAPSE_AURA = 39432, - SPELL_NETHER_BEAM = 35873, + SPELL_KAEL_GAINING_POWER = 36091, + SPELL_KAEL_EXPLODES = 36373, + SPELL_KAEL_EXPLODES2 = 36375, + SPELL_KAEL_EXPLODES3 = 36092, + SPELL_KAEL_EXPLODES4 = 36354, + SPELL_KAEL_STUNNED = 36185, + SPELL_FULLPOWER = 36187, + SPELL_NETHER_BEAM = 35873, + SPELL_PURE_NETHER_BEAM = 36196, + SPELL_SUMMON_NETHER_VAPOR = 35865, + + // Visual, phase transition spells + SPELL_NETHER_BEAM_VISUAL = 36089, // Channeled by trigger on Kael'thas. + SPELL_NETHER_BEAM_VISUAL2 = 36090, // Channeled by trigger on Kael'thas. + SPELL_NETHER_BEAM_VISUAL3 = 36364, // Cast by Kael'thas on himself, purple glowing effect. + + // Gravity Lapse spells + SPELL_GRAVITY_LAPSE = 35941, + SPELL_GRAVITY_LAPSE_PERIODIC = 34480, + SPELL_GRAVITY_LAPSE_FLIGHT_AURA = 39432, // Cast by players on themselves, allows flight + + // 25 teleport spells, one for each raid member... + SPELL_GRAVITY_LAPSE_TELE_FRONT = 35966, + SPELL_GRAVITY_LAPSE_TELE_FRONT_RIGHT = 35967, + SPELL_GRAVITY_LAPSE_TELE_FRONT_LEFT = 35968, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_RIGHT = 35969, + SPELL_GRAVITY_LAPSE_TELE_BACK = 35970, + SPELL_GRAVITY_LAPSE_TELE_TO_CASTER = 35971, + SPELL_GRAVITY_LAPSE_TELE_BACK_LEFT = 35972, + SPELL_GRAVITY_LAPSE_TELE_FRONT_LEFT2 = 35973, + SPELL_GRAVITY_LAPSE_TELE_CASTER_LEFT = 35974, + SPELL_GRAVITY_LAPSE_TELE_CASTER_LEFT2 = 35975, + SPELL_GRAVITY_LAPSE_TELE_FRONT_LEFT3 = 35976, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_LEFT = 35977, + SPELL_GRAVITY_LAPSE_TELE_CASTER_FRONT = 35978, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK = 35979, + SPELL_GRAVITY_LAPSE_TELE_FRONT_RIGHT2 = 35980, + SPELL_GRAVITY_LAPSE_TELE_CASTER_RIGHT = 35981, + SPELL_GRAVITY_LAPSE_TELE_CASTER_FRONT_RIGHT = 35982, + SPELL_GRAVITY_LAPSE_TELE_CASTER_FRONT2 = 35983, + SPELL_GRAVITY_LAPSE_TELE_CASTER_FRONT_LEFT = 35984, + SPELL_GRAVITY_LAPSE_TELE_CASTER_LEFT3 = 35985, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_LEFT2 = 35986, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK2 = 35987, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_RIGHT2 = 35988, + SPELL_GRAVITY_LAPSE_TELE_CASTER_RIGHT2 = 35989, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_RIGHT3 = 35990, // Thaladred the Darkener spells - SPELL_PSYCHIC_BLOW = 10689, - SPELL_SILENCE = 30225, + SPELL_PSYCHIC_BLOW = 10689, + SPELL_SILENCE = 30225, // Lord Sanguinar spells - SPELL_BELLOWING_ROAR = 40636, + SPELL_BELLOWING_ROAR = 40636, // Grand Astromancer Capernian spells - SPELL_CAPERNIAN_FIREBALL = 36971, - SPELL_CONFLAGRATION = 37018, - SPELL_ARCANE_EXPLOSION = 36970, + SPELL_CAPERNIAN_FIREBALL = 36971, + SPELL_CONFLAGRATION = 37018, + SPELL_ARCANE_EXPLOSION = 36970, //Master Engineer Telonicus spells - SPELL_BOMB = 37036, - SPELL_REMOTE_TOY = 37027, + SPELL_BOMB = 37036, + SPELL_REMOTE_TOY = 37027, //Nether Vapor spell - SPELL_NETHER_VAPOR = 35859, + SPELL_NETHER_VAPOR = 35859, //Phoenix spell - SPELL_BURN = 36720, - SPELL_EMBER_BLAST = 34341, - SPELL_REBIRTH = 41587 + SPELL_BURN = 36720, + SPELL_EMBER_BLAST = 34341, + SPELL_REBIRTH = 41587 }; enum Creatures { - NPC_PHOENIX = 21362, - NPC_PHOENIX_EGG = 21364 + NPC_PHOENIX = 21362, + NPC_PHOENIX_EGG = 21364 }; enum Models { //Phoenix egg and phoenix model - MODEL_ID_PHOENIX = 19682, - MODEL_ID_PHOENIX_EGG = 20245 + MODEL_ID_PHOENIX = 19682, + MODEL_ID_PHOENIX_EGG = 20245 }; -enum Misc +enum Actions { - MAX_ADVISORS = 4 + ACTION_START_ENCOUNTER, + ACTION_REVIVE_ADVISORS, + ACTION_PREPARE_ADVISORS, + ACTION_ACTIVE_ADVISOR, + ACTION_SCHEDULE_COMBAT_EVENTS +}; + +enum Advisors +{ + ADVISOR_THALADRED, + ADVISOR_SANGUINAR, + ADVISOR_CAPERNIAN, + ADVISOR_TELONICUS, + MAX_ADVISORS = 4, + + MAX_DEFEATED_ADVISORS = 4, + MAX_KILLED_ADVISORS = 8 +}; + +enum Events +{ + EVENT_START_ENCOUNTER = 1, + EVENT_ACTIVE_ADVISOR, + EVENT_SUMMON_WEAPONS, + EVENT_REVIVE_ADVISORS, + EVENT_ENGAGE_COMBAT, + EVENT_FULL_POWER, + EVENT_FIREBALL, + EVENT_ARCANE_DISRUPTION, + EVENT_FLAMESTRIKE, + EVENT_MIND_CONTROL, + EVENT_SUMMON_PHOENIX, + EVENT_SHOCK_BARRIER, + EVENT_PYROBLAST, + EVENT_PYROBLAST_CAST, + EVENT_GAINING_POWER, + EVENT_END_TRANSITION, + EVENT_GRAVITY_LAPSE, + EVENT_NETHER_BEAM, + + // Movement updates + EVENT_TRANSITION_1, + EVENT_TRANSITION_2, + EVENT_TRANSITION_3, + EVENT_TRANSITION_4, + EVENT_TRANSITION_5, + EVENT_TRANSITION_6, + + // Phase transition + EVENT_SIZE_INCREASE, + EVENT_EXPLODE, + EVENT_RESUME_COMBAT, + + // Advisors + EVENT_DELAYED_RESSURECTION, + + // Event groups + EVENT_GROUP_COMBAT = 1, // Default abilities + EVENT_GROUP_SPECIAL = 2 // Special abilities (Pyroblast, Nether Beam, Shock Barrier) +}; + +enum Phases +{ + PHASE_NONE, + PHASE_INTRO, + PHASE_REVIVED_ADVISORS, + PHASE_COMBAT, + PHASE_TRANSITION +}; + +enum MovementPoints +{ + POINT_START_TRANSITION = 1, + POINT_TRANSITION_CENTER_ASCENDING = 2, + POINT_TRANSITION_HALFWAY_ASCENDING = 3, + POINT_TRANSITION_TOP = 4, + POINT_TRANSITION_HALFWAY_DESCENDING = 5, + POINT_END_TRANSITION = 6 }; uint32 m_auiSpellSummonWeapon[]= @@ -142,61 +262,83 @@ uint32 m_auiSpellSummonWeapon[]= SPELL_SUMMON_WEAPONE, SPELL_SUMMON_WEAPONF, SPELL_SUMMON_WEAPONG }; +uint32 GravityLapseSpells[] = +{ + SPELL_GRAVITY_LAPSE_TELE_FRONT, + SPELL_GRAVITY_LAPSE_TELE_FRONT_RIGHT, + SPELL_GRAVITY_LAPSE_TELE_FRONT_LEFT, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_RIGHT, + SPELL_GRAVITY_LAPSE_TELE_BACK, + SPELL_GRAVITY_LAPSE_TELE_TO_CASTER, + SPELL_GRAVITY_LAPSE_TELE_BACK_LEFT, + SPELL_GRAVITY_LAPSE_TELE_FRONT_LEFT2, + SPELL_GRAVITY_LAPSE_TELE_CASTER_LEFT, + SPELL_GRAVITY_LAPSE_TELE_CASTER_LEFT2, + SPELL_GRAVITY_LAPSE_TELE_FRONT_LEFT3, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_LEFT, + SPELL_GRAVITY_LAPSE_TELE_CASTER_FRONT, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK, + SPELL_GRAVITY_LAPSE_TELE_FRONT_RIGHT2, + SPELL_GRAVITY_LAPSE_TELE_CASTER_RIGHT, + SPELL_GRAVITY_LAPSE_TELE_CASTER_FRONT_RIGHT, + SPELL_GRAVITY_LAPSE_TELE_CASTER_FRONT2, + SPELL_GRAVITY_LAPSE_TELE_CASTER_FRONT_LEFT, + SPELL_GRAVITY_LAPSE_TELE_CASTER_LEFT3, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_LEFT2, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK2, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_RIGHT2, + SPELL_GRAVITY_LAPSE_TELE_CASTER_RIGHT2, + SPELL_GRAVITY_LAPSE_TELE_CASTER_BACK_RIGHT3 +}; + const float CAPERNIAN_DISTANCE = 20.0f; //she casts away from the target //const float KAEL_VISIBLE_RANGE = 50.0f; -const float afGravityPos[3] = {795.0f, 0.0f, 70.0f}; +Position const afGravityPos = {795.0f, 0.0f, 70.0f}; -#define TIME_PHASE_2_3 120000 -#define TIME_PHASE_3_4 180000 +Position const TransitionPos[6] = +{ + // First two values are not static, they seem to differ on each sniff. + { 794.0522f, -0.96732f, 48.97848f, 0.0f }, + { 796.641f, -0.5888171f, 48.72847f, 3.176499f }, + { 795.007f, -0.471827f, 75.0f, 0.0f }, + { 795.007f, -0.471827f, 75.0f, 3.133458f }, + { 792.419f, -0.504778f, 50.0505f, 0.0f }, + { 792.419f, -0.504778f, 50.0505f, 3.130386f } +}; -//Base AI for Advisors struct advisorbase_ai : public ScriptedAI { advisorbase_ai(Creature* creature) : ScriptedAI(creature) { Initialize(); instance = creature->GetInstanceScript(); - m_bDoubled_Health = false; } void Initialize() { - FakeDeath = false; - DelayRes_Timer = 0; + _hasRessurrected = false; + _inFakeDeath = false; DelayRes_Target.Clear(); } - InstanceScript* instance; - bool FakeDeath; - bool m_bDoubled_Health; - uint32 DelayRes_Timer; - ObjectGuid DelayRes_Target; - void Reset() override { - if (m_bDoubled_Health) - { - me->SetMaxHealth(me->GetMaxHealth() / 2); - m_bDoubled_Health = false; - } - Initialize(); me->SetStandState(UNIT_STAND_STATE_STAND); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); //reset encounter - if (instance->GetData(DATA_KAELTHASEVENT) == 1 || instance->GetData(DATA_KAELTHASEVENT) == 3) + if (instance->GetBossState(DATA_KAELTHAS) == IN_PROGRESS) if (Creature* Kaelthas = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KAELTHAS))) Kaelthas->AI()->EnterEvadeMode(); } void MoveInLineOfSight(Unit* who) override - { - if (!who || FakeDeath || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + if (!who || _inFakeDeath || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::MoveInLineOfSight(who); @@ -204,69 +346,61 @@ struct advisorbase_ai : public ScriptedAI void AttackStart(Unit* who) override { - if (!who || FakeDeath || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + if (!who || _inFakeDeath || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::AttackStart(who); } - void Revive(Unit* /*Target*/) + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - // double health for phase 3 - me->SetMaxHealth(me->GetMaxHealth() * 2); - m_bDoubled_Health = true; - me->SetFullHealth(); - me->SetStandState(UNIT_STAND_STATE_STAND); - - DoCast(me, SPELL_RES_VISUAL, false); - DelayRes_Timer = 2000; + if (spell->Id == SPELL_RESSURECTION) + { + _hasRessurrected = true; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); + me->SetStandState(UNIT_STAND_STATE_STAND); + events.ScheduleEvent(EVENT_DELAYED_RESSURECTION, 2000); + } } void DamageTaken(Unit* killer, uint32 &damage) override { - if (damage < me->GetHealth()) - return; - - //Prevent glitch if in fake death - if (FakeDeath && instance->GetData(DATA_KAELTHASEVENT) != 0) - { - damage = 0; - return; - } - - //Don't really die in phase 1 & 3, only die after that - if (instance->GetData(DATA_KAELTHASEVENT) != 0) + if (damage >= me->GetHealth() && !_inFakeDeath && !_hasRessurrected) { //prevent death damage = 0; - FakeDeath = true; + _inFakeDeath = true; me->InterruptNonMeleeSpells(false); me->SetHealth(0); - me->StopMoving(); me->ClearComboPointHolders(); me->RemoveAllAurasOnDeath(); me->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); me->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->ClearAllReactives(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); me->SetTarget(ObjectGuid::Empty); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); me->SetStandState(UNIT_STAND_STATE_DEAD); + me->GetMotionMaster()->Clear(); JustDied(killer); } } + void JustDied(Unit* /*killer*/) override + { + if (Creature* kael = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KAELTHAS))) + kael->AI()->DoAction(ACTION_ACTIVE_ADVISOR); + } + void UpdateAI(uint32 diff) override { - if (DelayRes_Timer) + if (_hasRessurrected) + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - if (DelayRes_Timer <= diff) + if (eventId == EVENT_DELAYED_RESSURECTION) { - DelayRes_Timer = 0; - FakeDeath = false; + _inFakeDeath = false; Unit* Target = ObjectAccessor::GetUnit(*me, DelayRes_Target); if (!Target) @@ -277,9 +411,15 @@ struct advisorbase_ai : public ScriptedAI me->GetMotionMaster()->Clear(); me->GetMotionMaster()->MoveChase(Target); me->AddThreat(Target, 0.0f); - } else DelayRes_Timer -= diff; + } } } + public: + EventMap events; + InstanceScript* instance; + bool _hasRessurrected; + bool _inFakeDeath; + ObjectGuid DelayRes_Target; }; class boss_kaelthas : public CreatureScript @@ -293,150 +433,199 @@ class boss_kaelthas : public CreatureScript boss_kaelthasAI(Creature* creature) : BossAI(creature, DATA_KAELTHAS) { Initialize(); - PhaseSubphase = 0; - Phase_Timer = 0; } void Initialize() { - Fireball_Timer = 5000 + rand32() % 10000; - ArcaneDisruption_Timer = 45000; - MindControl_Timer = 40000; - Phoenix_Timer = 50000; - ShockBarrier_Timer = 60000; - FlameStrike_Timer = 30000; - GravityLapse_Timer = 20000; - GravityLapse_Phase = 0; - NetherBeam_Timer = 8000; - NetherVapor_Timer = 10000; - PyrosCast = 0; - Phase = 0; - InGravityLapse = false; - IsCastingFireball = false; - ChainPyros = false; + _advisorCounter = 0; + _pyrosCast = 0; + _netherbeamsCast = 0; + _phase = PHASE_NONE; + _scaleStage = 0; + _hasFullPower = false; } - uint32 Fireball_Timer; - uint32 ArcaneDisruption_Timer; - uint32 Phoenix_Timer; - uint32 ShockBarrier_Timer; - uint32 GravityLapse_Timer; - uint32 GravityLapse_Phase; - uint32 NetherBeam_Timer; - uint32 NetherVapor_Timer; - uint32 FlameStrike_Timer; - uint32 MindControl_Timer; - uint32 Phase; - uint32 PhaseSubphase; //generic - uint32 Phase_Timer; //generic timer - uint32 PyrosCast; - - bool InGravityLapse; - bool IsCastingFireball; - bool ChainPyros; - - ObjectGuid m_auiAdvisorGuid[MAX_ADVISORS]; - void Reset() override { Initialize(); + DoAction(ACTION_PREPARE_ADVISORS); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + me->SetDisableGravity(false); + me->SetTarget(ObjectGuid::Empty); + me->SetObjectScale(1.0f); + BossAI::Reset(); + } - if (me->IsInCombat()) - PrepareAdvisors(); + void JustReachedHome() override + { + BossAI::JustReachedHome(); - _Reset(); + // Rebuild the surrounding environment. + if (GameObject* statue = instance->GetGameObject(DATA_KAEL_STATUE_LEFT)) + statue->ResetDoorOrButton(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + if (GameObject* statue = instance->GetGameObject(DATA_KAEL_STATUE_RIGHT)) + statue->ResetDoorOrButton(); - instance->SetData(DATA_KAELTHASEVENT, 0); + if (GameObject* window = instance->GetGameObject(DATA_TEMPEST_BRIDGE_WINDOW)) + window->ResetDoorOrButton(); } - void PrepareAdvisors() + void DoAction(int32 action) override { - for (uint8 i = 0; i < MAX_ADVISORS; ++i) + switch (action) { - if (Creature* creature = ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[i])) - { - creature->Respawn(); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - creature->setFaction(me->getFaction()); - creature->AI()->EnterEvadeMode(); - } + case ACTION_START_ENCOUNTER: + Talk(SAY_INTRO); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + _advisorGuid[ADVISOR_THALADRED] = instance->GetGuidData(DATA_THALADREDTHEDARKENER); + _advisorGuid[ADVISOR_SANGUINAR] = instance->GetGuidData(DATA_LORDSANGUINAR); + _advisorGuid[ADVISOR_CAPERNIAN] = instance->GetGuidData(DATA_GRANDASTROMANCERCAPERNIAN); + _advisorGuid[ADVISOR_TELONICUS] = instance->GetGuidData(DATA_MASTERENGINEERTELONICUS); + + _phase = PHASE_INTRO; + instance->SetBossState(DATA_KAELTHAS, IN_PROGRESS); + events.ScheduleEvent(EVENT_START_ENCOUNTER, 23000); + break; + case ACTION_PREPARE_ADVISORS: + for (uint8 i = 0; i < MAX_ADVISORS; ++i) + { + if (Creature* creature = ObjectAccessor::GetCreature(*me, _advisorGuid[i])) + { + creature->Respawn(true); + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + creature->AI()->EnterEvadeMode(); + } + } + break; + case ACTION_ACTIVE_ADVISOR: + // They have already been active, so we are not handling new ones, just counting their death. + if (_phase == PHASE_REVIVED_ADVISORS) + ++_advisorCounter; + + switch (_advisorCounter) + { + case ADVISOR_THALADRED: + Talk(SAY_INTRO_THALADRED); + events.ScheduleEvent(EVENT_ACTIVE_ADVISOR, 7000); + break; + case ADVISOR_SANGUINAR: + Talk(SAY_INTRO_SANGUINAR); + events.ScheduleEvent(EVENT_ACTIVE_ADVISOR, 12500); + break; + case ADVISOR_CAPERNIAN: + Talk(SAY_INTRO_CAPERNIAN); + events.ScheduleEvent(EVENT_ACTIVE_ADVISOR, 7000); + break; + case ADVISOR_TELONICUS: + Talk(SAY_INTRO_TELONICUS); + events.ScheduleEvent(EVENT_ACTIVE_ADVISOR, 8400); + break; + case MAX_DEFEATED_ADVISORS: + // Every advisor defeated - Phase 2 starts. + Talk(SAY_PHASE2_WEAPON); + events.ScheduleEvent(EVENT_SUMMON_WEAPONS, 3500); + break; + case MAX_KILLED_ADVISORS: + // Every advisor killed - Phase 3 starts. + events.ScheduleEvent(EVENT_ENGAGE_COMBAT, 5000); + break; + default: + break; + } + break; + case ACTION_SCHEDULE_COMBAT_EVENTS: + _phase = PHASE_COMBAT; + events.SetPhase(PHASE_COMBAT); + events.ScheduleEvent(EVENT_FIREBALL, 1000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + events.ScheduleEvent(EVENT_ARCANE_DISRUPTION, 45000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + events.ScheduleEvent(EVENT_FLAMESTRIKE, 30000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + events.ScheduleEvent(EVENT_MIND_CONTROL, 40000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + events.ScheduleEvent(EVENT_SUMMON_PHOENIX, 50000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + break; + default: + break; } } - void StartEvent() + void MoveInLineOfSight(Unit* who) override { - m_auiAdvisorGuid[0] = instance->GetGuidData(DATA_THALADREDTHEDARKENER); - m_auiAdvisorGuid[1] = instance->GetGuidData(DATA_LORDSANGUINAR); - m_auiAdvisorGuid[2] = instance->GetGuidData(DATA_GRANDASTROMANCERCAPERNIAN); - m_auiAdvisorGuid[3] = instance->GetGuidData(DATA_MASTERENGINEERTELONICUS); - - if (!m_auiAdvisorGuid[0] || !m_auiAdvisorGuid[1] || !m_auiAdvisorGuid[2] || !m_auiAdvisorGuid[3]) - { - TC_LOG_ERROR("scripts", "Kael'Thas One or more advisors missing, Skipping Phases 1-3"); - - Talk(SAY_PHASE4_INTRO2); - - Phase = 4; - - instance->SetData(DATA_KAELTHASEVENT, 4); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - AttackStart(target); - } - else + if (_phase == PHASE_NONE && me->IsValidAttackTarget(who) && me->IsWithinDistInMap(who, 30.0f)) { - PrepareAdvisors(); - - Talk(SAY_INTRO); - - instance->SetData(DATA_KAELTHASEVENT, 1); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - - PhaseSubphase = 0; - Phase_Timer = 23000; - Phase = 1; + DoAction(ACTION_START_ENCOUNTER); + who->SetInCombatWith(me); + me->AddThreat(who, 0.0f); + me->SetTarget(who->GetGUID()); } } - void MoveInLineOfSight(Unit* who) override + void DamageTaken(Unit* attacker, uint32& damage) override { - if (!me->HasUnitState(UNIT_STATE_STUNNED) && me->CanCreatureAttack(who)) + if (_phase == PHASE_NONE) { - if (!me->CanFly() && me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE) - return; - - float attackRadius = me->GetAttackDistance(who); - if (me->IsWithinDistInMap(who, attackRadius) && me->IsWithinLOSInMap(who)) - { - if (!me->GetVictim() && Phase >= 4) - { - who->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); - AttackStart(who); - } - else - { - if (!instance->GetData(DATA_KAELTHASEVENT) && !Phase) - StartEvent(); + DoAction(ACTION_START_ENCOUNTER); + me->SetTarget(attacker->GetGUID()); + } - who->SetInCombatWith(me); - me->AddThreat(who, 0.0f); - } - } + if (!_hasFullPower && me->HealthBelowPctDamaged(50, damage)) + { + _hasFullPower = true; + me->AttackStop(); + me->InterruptNonMeleeSpells(false); + events.CancelEventGroup(EVENT_GROUP_COMBAT); + events.CancelEventGroup(EVENT_GROUP_SPECIAL); + events.SetPhase(PHASE_TRANSITION); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->MovePoint(POINT_START_TRANSITION, TransitionPos[0]); } } - void EnterCombat(Unit* /*who*/) override + void MovementInform(uint32 type, uint32 point) override { - if (!instance->GetData(DATA_KAELTHASEVENT) && !Phase) - StartEvent(); + if (type != POINT_MOTION_TYPE) + return; + + switch (point) + { + case POINT_START_TRANSITION: + events.ScheduleEvent(EVENT_TRANSITION_1, 1000); + break; + case POINT_TRANSITION_CENTER_ASCENDING: + me->SetFacingTo(float(M_PI)); + Talk(SAY_PHASE5_NUTS); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetDisableGravity(true); + //me->SetHover(true); -- Set in sniffs, but breaks his visual. + events.ScheduleEvent(EVENT_TRANSITION_2, 2000); + events.ScheduleEvent(EVENT_SIZE_INCREASE, 5000); + break; + case POINT_TRANSITION_HALFWAY_ASCENDING: + DoCast(me, SPELL_NETHER_BEAM_VISUAL3, true); + events.ScheduleEvent(EVENT_TRANSITION_3, 1000); + break; + case POINT_TRANSITION_TOP: + events.ScheduleEvent(EVENT_EXPLODE, 10000); + break; + case POINT_TRANSITION_HALFWAY_DESCENDING: + events.ScheduleEvent(EVENT_TRANSITION_5, 2000); + break; + case POINT_END_TRANSITION: + me->SetReactState(REACT_AGGRESSIVE); + me->InterruptNonMeleeSpells(false); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAurasDueToSpell(SPELL_FULLPOWER); - instance->SetBossState(DATA_KAELTHAS, IN_PROGRESS); + if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0)) + AttackStart(target); + + DoAction(ACTION_SCHEDULE_COMBAT_EVENTS); + events.ScheduleEvent(EVENT_GRAVITY_LAPSE, 10000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + break; + default: + break; + } } void KilledUnit(Unit* /*victim*/) override @@ -456,556 +645,224 @@ class boss_kaelthas : public CreatureScript } } - void JustDied(Unit* /*killer*/) override + void JustDied(Unit* killer) override { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - Talk(SAY_DEATH); - - instance->SetData(DATA_KAELTHASEVENT, 0); - - for (uint8 i = 0; i < MAX_ADVISORS; ++i) - { - if (Unit* pAdvisor = ObjectAccessor::GetUnit(*me, m_auiAdvisorGuid[i])) - pAdvisor->Kill(pAdvisor); - } - _JustDied(); + BossAI::JustDied(killer); } void UpdateAI(uint32 diff) override { - //Phase 1 - switch (Phase) - { - case 1: - { - Unit* target = NULL; - Creature* Advisor = NULL; - - //Subphase switch - switch (PhaseSubphase) - { - //Subphase 1 - Start - case 0: - if (Phase_Timer <= diff) - { - Talk(SAY_INTRO_THALADRED); - - //start advisor within 7 seconds - Phase_Timer = 7000; - ++PhaseSubphase; - } else Phase_Timer -= diff; - break; - - //Subphase 1 - Unlock advisor - case 1: - if (Phase_Timer <= diff) - { - Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[0])); - - if (Advisor) - { - Advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - Advisor->setFaction(me->getFaction()); - - target = SelectTarget(SELECT_TARGET_RANDOM, 0); - if (target) - Advisor->AI()->AttackStart(target); - } - - ++PhaseSubphase; - } else Phase_Timer -= diff; - break; - - //Subphase 2 - Start - case 2: - Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[0])); - - if (Advisor && (Advisor->GetStandState() == UNIT_STAND_STATE_DEAD)) - { - Talk(SAY_INTRO_SANGUINAR); - - //start advisor within 12.5 seconds - Phase_Timer = 12500; - ++PhaseSubphase; - } - break; - - //Subphase 2 - Unlock advisor - case 3: - if (Phase_Timer <= diff) - { - Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[1])); - - if (Advisor) - { - Advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - Advisor->setFaction(me->getFaction()); - - target = SelectTarget(SELECT_TARGET_RANDOM, 0); - if (target) - Advisor->AI()->AttackStart(target); - } - - ++PhaseSubphase; - } else Phase_Timer -= diff; - break; - - //Subphase 3 - Start - case 4: - Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[1])); - - if (Advisor && (Advisor->GetStandState() == UNIT_STAND_STATE_DEAD)) - { - Talk(SAY_INTRO_CAPERNIAN); - - //start advisor within 7 seconds - Phase_Timer = 7000; - ++PhaseSubphase; - } - break; - - //Subphase 3 - Unlock advisor - case 5: - if (Phase_Timer <= diff) - { - Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[2])); - - if (Advisor) - { - Advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - Advisor->setFaction(me->getFaction()); - - target = SelectTarget(SELECT_TARGET_RANDOM, 0); - if (target) - Advisor->AI()->AttackStart(target); - } - - ++PhaseSubphase; - } else Phase_Timer -= diff; - break; - - //Subphase 4 - Start - case 6: - Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[2])); - - if (Advisor && (Advisor->GetStandState() == UNIT_STAND_STATE_DEAD)) - { - Talk(SAY_INTRO_TELONICUS); - - //start advisor within 8.4 seconds - Phase_Timer = 8400; - ++PhaseSubphase; - } - break; - - //Subphase 4 - Unlock advisor - case 7: - if (Phase_Timer <= diff) - { - Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[3])); - - if (Advisor) - { - Advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - Advisor->setFaction(me->getFaction()); - - target = SelectTarget(SELECT_TARGET_RANDOM, 0); - if (target) - Advisor->AI()->AttackStart(target); - } - - Phase_Timer = 3000; - ++PhaseSubphase; - } else Phase_Timer -= diff; - break; - - //End of phase 1 - case 8: - Advisor = (ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[3])); - - if (Advisor && (Advisor->GetStandState() == UNIT_STAND_STATE_DEAD)) - { - Phase = 2; - instance->SetData(DATA_KAELTHASEVENT, 2); + if (_phase == PHASE_COMBAT) + if (!UpdateVictim()) + return; - Talk(SAY_PHASE2_WEAPON); + events.Update(diff); - PhaseSubphase = 0; - Phase_Timer = 3500; - DoCast(me, SPELL_SUMMON_WEAPONS); - } - break; - } - } - break; + // SPELL_KAEL_GAINING_POWER and SPELL_KAEL_STUNNED are channeling spells that need to be interrupted during his transition. + if (me->HasUnitState(UNIT_STATE_CASTING) && !me->FindCurrentSpellBySpellId(SPELL_KAEL_GAINING_POWER) && !me->FindCurrentSpellBySpellId(SPELL_KAEL_STUNNED)) + return; - case 2: + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - if (PhaseSubphase == 0) - { - if (Phase_Timer <= diff) + case EVENT_START_ENCOUNTER: + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED); + DoAction(ACTION_ACTIVE_ADVISOR); + break; + case EVENT_ACTIVE_ADVISOR: + if (Creature* advisor = ObjectAccessor::GetCreature(*me, _advisorGuid[_advisorCounter])) { - PhaseSubphase = 1; - } - else - Phase_Timer -= diff; - } + advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - //Spawn weapons - if (PhaseSubphase == 1) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + advisor->AI()->AttackStart(target); + } + ++_advisorCounter; + break; + case EVENT_SUMMON_WEAPONS: { DoCast(me, SPELL_SUMMON_WEAPONS, false); - uint8 uiMaxWeapon = sizeof(m_auiSpellSummonWeapon)/sizeof(uint32); + uint8 uiMaxWeapon = sizeof(m_auiSpellSummonWeapon) / sizeof(uint32); for (uint32 i = 0; i < uiMaxWeapon; ++i) DoCast(me, m_auiSpellSummonWeapon[i], true); - PhaseSubphase = 2; - Phase_Timer = TIME_PHASE_2_3; - } - - if (PhaseSubphase == 2) - { - if (Phase_Timer <= diff) - { - Talk(SAY_PHASE3_ADVANCE); - instance->SetData(DATA_KAELTHASEVENT, 3); - Phase = 3; - PhaseSubphase = 0; - } - else - Phase_Timer -= diff; - } - } - break; - - case 3: - { - if (PhaseSubphase == 0) - { - //Respawn advisors - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0); - - for (uint8 i = 0; i < MAX_ADVISORS; ++i) - { - Creature* Advisor = ObjectAccessor::GetCreature(*me, m_auiAdvisorGuid[i]); - - if (!Advisor) - TC_LOG_ERROR("scripts", "SD2: Kael'Thas Advisor %u does not exist. Possibly despawned? Incorrectly Killed?", i); - else - ENSURE_AI(advisorbase_ai, Advisor->AI())->Revive(target); - } - - PhaseSubphase = 1; - Phase_Timer = TIME_PHASE_3_4; + events.ScheduleEvent(EVENT_REVIVE_ADVISORS, 120000); + break; } - - if (Phase_Timer <= diff) - { + case EVENT_REVIVE_ADVISORS: + _phase = PHASE_REVIVED_ADVISORS; + Talk(SAY_PHASE3_ADVANCE); + DoCast(me, SPELL_RESSURECTION); + break; + case EVENT_ENGAGE_COMBAT: Talk(SAY_PHASE4_INTRO2); - Phase = 4; - - instance->SetData(DATA_KAELTHASEVENT, 4); // Sometimes people can collect Aggro in Phase 1-3. Reset threat before releasing Kael. DoResetThreat(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) AttackStart(target); - Phase_Timer = 30000; - } - else - Phase_Timer -= diff; - } - break; - - case 4: - case 5: - case 6: - { - //Return since we have no target - if (!UpdateVictim()) - return; - - //Fireball_Timer - if (!InGravityLapse && !ChainPyros && Phase != 5) - { - if (Fireball_Timer <= diff) - { - if (!IsCastingFireball) - { - if (!me->IsNonMeleeSpellCast(false)) - { - //interruptable - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, false); - int32 dmg = 20000 + rand32() % 5000; - me->CastCustomSpell(me->GetVictim(), SPELL_FIREBALL, &dmg, 0, 0, false); - IsCastingFireball = true; - Fireball_Timer = 2500; - } - } - else - { - //apply resistance - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, true); - IsCastingFireball = false; - Fireball_Timer = 5000 + rand32() % 10000; - } - } - else - Fireball_Timer -= diff; + DoAction(ACTION_SCHEDULE_COMBAT_EVENTS); + events.ScheduleEvent(EVENT_PYROBLAST, 60000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + break; + case EVENT_FIREBALL: + DoCastVictim(SPELL_FIREBALL); + events.ScheduleEvent(EVENT_FIREBALL, 2500, EVENT_GROUP_COMBAT, PHASE_COMBAT); + break; + case EVENT_ARCANE_DISRUPTION: + DoCastVictim(SPELL_ARCANE_DISRUPTION, true); + events.ScheduleEvent(EVENT_ARCANE_DISRUPTION, 60000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + break; + case EVENT_FLAMESTRIKE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_FLAME_STRIKE); - //ArcaneDisruption_Timer - if (ArcaneDisruption_Timer <= diff) + events.ScheduleEvent(EVENT_FLAMESTRIKE, 30000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + break; + case EVENT_MIND_CONTROL: + Talk(SAY_MIND_CONTROL); + DoCastAOE(SPELL_MIND_CONTROL, true); + events.ScheduleEvent(EVENT_MIND_CONTROL, 60000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + break; + case EVENT_SUMMON_PHOENIX: + DoCast(me, SPELL_PHOENIX_ANIMATION); + Talk(SAY_SUMMON_PHOENIX); + events.ScheduleEvent(EVENT_SUMMON_PHOENIX, urand(45000, 60000), EVENT_GROUP_COMBAT, PHASE_COMBAT); + break; + case EVENT_END_TRANSITION: + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + DoCast(SPELL_FULLPOWER); + events.ScheduleEvent(EVENT_TRANSITION_4, 2000); + break; + case EVENT_PYROBLAST: + _pyrosCast = 0; + Talk(EMOTE_PYROBLAST); + DoCast(me, SPELL_SHOCK_BARRIER); + events.DelayEvents(10000, EVENT_GROUP_COMBAT); + events.ScheduleEvent(EVENT_PYROBLAST_CAST, 1000, EVENT_GROUP_SPECIAL, PHASE_COMBAT); + break; + case EVENT_PYROBLAST_CAST: + if (_pyrosCast < 3) { - DoCastVictim(SPELL_ARCANE_DISRUPTION, true); - ArcaneDisruption_Timer = 60000; + DoCastVictim(SPELL_PYROBLAST); + events.ScheduleEvent(EVENT_PYROBLAST_CAST, 3000); + _pyrosCast++; } else - ArcaneDisruption_Timer -= diff; - - if (FlameStrike_Timer <= diff) + events.ScheduleEvent(EVENT_PYROBLAST, 60000, EVENT_GROUP_COMBAT, PHASE_COMBAT); + break; + case EVENT_GRAVITY_LAPSE: + Talk(SAY_GRAVITY_LAPSE); + DoCastAOE(SPELL_GRAVITY_LAPSE); + DoCast(me, SPELL_NETHER_VAPOR); + events.DelayEvents(24000, EVENT_GROUP_COMBAT); + events.ScheduleEvent(EVENT_NETHER_BEAM, 3000, EVENT_GROUP_SPECIAL, PHASE_COMBAT); + events.ScheduleEvent(EVENT_SHOCK_BARRIER, 1000, EVENT_GROUP_SPECIAL, PHASE_COMBAT); + events.ScheduleEvent(EVENT_GRAVITY_LAPSE, 30000, EVENT_GROUP_SPECIAL, PHASE_COMBAT); + break; + case EVENT_NETHER_BEAM: + if (_netherbeamsCast <= 8) { if (Unit* unit = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(unit, SPELL_FLAME_STRIKE); + DoCast(unit, SPELL_NETHER_BEAM); - FlameStrike_Timer = 30000; + _netherbeamsCast++; + events.ScheduleEvent(EVENT_NETHER_BEAM, 3000); } else - FlameStrike_Timer -= diff; - - if (MindControl_Timer <= diff) - { - if (me->getThreatManager().getThreatList().size() >= 2) - for (uint32 i = 0; i < 3; ++i) - { - TC_LOG_DEBUG("scripts", "Kael'Thas mind control not supported."); - //DoCast(unit, SPELL_MIND_CONTROL); - } - - MindControl_Timer = 60000; - } - else - MindControl_Timer -= diff; - } - - //Phoenix_Timer - if (Phoenix_Timer <= diff) - { - DoCast(me, SPELL_PHOENIX_ANIMATION); - Talk(SAY_SUMMON_PHOENIX); - - Phoenix_Timer = 60000; - } - else - Phoenix_Timer -= diff; - - //Phase 4 specific spells - if (Phase == 4) - { - if (HealthBelowPct(50)) - { - instance->SetData(DATA_KAELTHASEVENT, 4); - Phase = 5; - Phase_Timer = 10000; - - Talk(SAY_PHASE5_NUTS); - - me->StopMoving(); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); - me->SetPosition(afGravityPos[0], afGravityPos[1], afGravityPos[2], 0); - me->MonsterMoveWithSpeed(afGravityPos[0], afGravityPos[1], afGravityPos[2], 1); - - me->InterruptNonMeleeSpells(false); - DoCast(me, SPELL_FULLPOWER); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - } - - //ShockBarrier_Timer - if (ShockBarrier_Timer <= diff) - { - DoCast(me, SPELL_SHOCK_BARRIER); - ChainPyros = true; - PyrosCast = 0; - ShockBarrier_Timer = 60000; - } - else - ShockBarrier_Timer -= diff; - - //Chain Pyros (3 of them max) - if (ChainPyros && !me->IsNonMeleeSpellCast(false)) - { - if (PyrosCast < 3) - { - DoCastVictim(SPELL_PYROBLAST); - ++PyrosCast; - } - else - { - ChainPyros = false; - Fireball_Timer = 2500; - ArcaneDisruption_Timer = 60000; - } - } - } - - if (Phase == 5) - { - if (Phase_Timer <= diff) - { - me->InterruptNonMeleeSpells(false); - me->RemoveAurasDueToSpell(SPELL_FULLPOWER); + _netherbeamsCast = 0; + break; + case EVENT_TRANSITION_1: + me->GetMotionMaster()->MovePoint(POINT_TRANSITION_CENTER_ASCENDING, TransitionPos[1]); + break; + case EVENT_TRANSITION_2: + DoCast(me, SPELL_KAEL_GAINING_POWER); + me->GetMotionMaster()->Clear(); + me->RemoveUnitMovementFlag(MOVEMENTFLAG_ROOT); + me->GetMotionMaster()->MovePoint(POINT_TRANSITION_HALFWAY_ASCENDING, TransitionPos[2], false); + break; + case EVENT_TRANSITION_3: + me->GetMotionMaster()->MovePoint(POINT_TRANSITION_TOP, TransitionPos[3], false); + break; + case EVENT_TRANSITION_4: + me->GetMotionMaster()->MovePoint(POINT_TRANSITION_HALFWAY_DESCENDING, TransitionPos[4], false); + break; + case EVENT_TRANSITION_5: + me->GetMotionMaster()->MovePoint(POINT_END_TRANSITION, TransitionPos[5], false); + break; + case EVENT_EXPLODE: + me->InterruptNonMeleeSpells(false); + me->RemoveAurasDueToSpell(SPELL_NETHER_BEAM_VISUAL3); + DoCast(me, SPELL_KAEL_EXPLODES3, true); + DoCast(me, SPELL_KAEL_STUNNED); // Core doesn't handle the emote properly while flying. + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_DROWNED); - DoCast(me, SPELL_EXPLODE); + // Destroy the surrounding environment. + if (GameObject* statue = instance->GetGameObject(DATA_KAEL_STATUE_LEFT)) + statue->UseDoorOrButton(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - Phase = 6; - AttackStart(me->GetVictim()); - } - else - Phase_Timer -= diff; - } + if (GameObject* statue = instance->GetGameObject(DATA_KAEL_STATUE_RIGHT)) + statue->UseDoorOrButton(); - //Phase 5 - if (Phase == 6) - { - //GravityLapse_Timer - if (GravityLapse_Timer <= diff) - { - ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList(); - ThreatContainer::StorageType::const_iterator i = threatlist.begin(); - - switch (GravityLapse_Phase) - { - case 0: - me->StopMoving(); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); - me->SetPosition(afGravityPos[0], afGravityPos[1], afGravityPos[2], 0); - me->MonsterMoveWithSpeed(afGravityPos[0], afGravityPos[1], afGravityPos[2], 0); - - // 1) Kael'thas will portal the whole raid right into his body - for (i = threatlist.begin(); i != threatlist.end(); ++i) - { - Unit* unit = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()); - if (unit && (unit->GetTypeId() == TYPEID_PLAYER)) - { - //Use work around packet to prevent player from being dropped from combat - DoTeleportPlayer(unit, afGravityPos[0], afGravityPos[1], afGravityPos[2], unit->GetOrientation()); - } - } - - GravityLapse_Timer = 500; - ++GravityLapse_Phase; - InGravityLapse = true; - ShockBarrier_Timer = 1000; - NetherBeam_Timer = 5000; - break; - - case 1: - Talk(SAY_GRAVITYLAPSE); - - // 2) At that point he will put a Gravity Lapse debuff on everyone - for (i = threatlist.begin(); i != threatlist.end(); ++i) - { - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid())) - { - DoCast(unit, SPELL_KNOCKBACK, true); - //Gravity lapse - needs an exception in Spell system to work - - unit->CastSpell(unit, SPELL_GRAVITY_LAPSE, true, 0, 0, me->GetGUID()); - unit->CastSpell(unit, SPELL_GRAVITY_LAPSE_AURA, true, 0, 0, me->GetGUID()); - - //Using packet workaround - WorldPacket data(SMSG_MOVE_SET_CAN_FLY, 12); - data << unit->GetPackGUID(); - data << uint32(0); - unit->SendMessageToSet(&data, true); - } - } - GravityLapse_Timer = 10000; - ++GravityLapse_Phase; - break; - - case 2: - //Cast nether vapor aura on self - me->InterruptNonMeleeSpells(false); - DoCast(me, SPELL_NETHER_VAPOR); - - GravityLapse_Timer = 20000; - ++GravityLapse_Phase; - break; - - case 3: - //Remove flight - for (i = threatlist.begin(); i != threatlist.end(); ++i) - { - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid())) - { - //Using packet workaround - WorldPacket data(SMSG_MOVE_UNSET_CAN_FLY, 12); - data << unit->GetPackGUID(); - data << uint32(0); - unit->SendMessageToSet(&data, true); - } - } - - me->RemoveAurasDueToSpell(SPELL_NETHER_VAPOR); - InGravityLapse = false; - GravityLapse_Timer = 60000; - GravityLapse_Phase = 0; - AttackStart(me->GetVictim()); - break; - } - } - else - GravityLapse_Timer -= diff; + if (GameObject* window = instance->GetGameObject(DATA_TEMPEST_BRIDGE_WINDOW)) + window->UseDoorOrButton(); - if (InGravityLapse) + events.ScheduleEvent(EVENT_END_TRANSITION, 10000); + break; + case EVENT_SIZE_INCREASE: + switch (_scaleStage) { - //ShockBarrier_Timer - if (ShockBarrier_Timer <= diff) - { - DoCast(me, SPELL_SHOCK_BARRIER); - ShockBarrier_Timer = 20000; - } - else - ShockBarrier_Timer -= diff; - - //NetherBeam_Timer - if (NetherBeam_Timer <= diff) - { - if (Unit* unit = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(unit, SPELL_NETHER_BEAM); - - NetherBeam_Timer = 4000; - } - else - NetherBeam_Timer -= diff; + case 0: + me->SetObjectScale(1.4f); + events.ScheduleEvent(EVENT_SIZE_INCREASE, 5000); + break; + case 1: + me->SetObjectScale(1.8f); + events.ScheduleEvent(EVENT_SIZE_INCREASE, 3000); + break; + case 2: + me->SetObjectScale(2.0f); + events.ScheduleEvent(EVENT_SIZE_INCREASE, 1000); + break; + case 3: + me->SetObjectScale(2.2f); + break; + default: + break; } - } - - if (!InGravityLapse) - DoMeleeAttackIfReady(); + ++_scaleStage; + break; + default: + break; } } + + if (events.IsInPhase(PHASE_COMBAT)) + DoMeleeAttackIfReady(); } + private: + uint8 _advisorCounter; + uint8 _phase; + uint8 _pyrosCast; + uint8 _scaleStage; + uint8 _netherbeamsCast; + bool _hasFullPower; + ObjectGuid _advisorGuid[MAX_ADVISORS]; }; + CreatureAI* GetAI(Creature* creature) const override { return GetInstanceAI<boss_kaelthasAI>(creature); } }; -//Thaladred the Darkener AI class boss_thaladred_the_darkener : public CreatureScript { public: @@ -1039,32 +896,23 @@ class boss_thaladred_the_darkener : public CreatureScript void EnterCombat(Unit* who) override { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; - - if (!who || FakeDeath) - return; - Talk(SAY_THALADRED_AGGRO); me->AddThreat(who, 5000000.0f); } - void JustDied(Unit* /*killer*/) override + void JustDied(Unit* killer) override { - if (instance->GetData(DATA_KAELTHASEVENT) == 3) + if (_hasRessurrected) Talk(SAY_THALADRED_DEATH); + + advisorbase_ai::JustDied(killer); } void UpdateAI(uint32 diff) override { advisorbase_ai::UpdateAI(diff); - //Faking death, don't do anything - if (FakeDeath) - return; - - //Return since we have no target - if (!UpdateVictim()) + if (!UpdateVictim() || _inFakeDeath) return; //Gaze_Timer @@ -1109,7 +957,6 @@ class boss_thaladred_the_darkener : public CreatureScript } }; -//Lord Sanguinar AI class boss_lord_sanguinar : public CreatureScript { public: @@ -1136,33 +983,24 @@ class boss_lord_sanguinar : public CreatureScript advisorbase_ai::Reset(); } - void EnterCombat(Unit* who) override + void EnterCombat(Unit* /*who*/) override { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; - - if (!who || FakeDeath) - return; - Talk(SAY_SANGUINAR_AGGRO); } - void JustDied(Unit* /*killer*/) override + void JustDied(Unit* killer) override { - if (instance->GetData(DATA_KAELTHASEVENT) == 3) + if (_hasRessurrected) Talk(SAY_SANGUINAR_DEATH); + + advisorbase_ai::JustDied(killer); } void UpdateAI(uint32 diff) override { advisorbase_ai::UpdateAI(diff); - //Faking death, don't do anything - if (FakeDeath) - return; - - //Return since we have no target - if (!UpdateVictim()) + if (!UpdateVictim() || _inFakeDeath) return; //Fear_Timer @@ -1182,7 +1020,7 @@ class boss_lord_sanguinar : public CreatureScript return GetInstanceAI<boss_lord_sanguinarAI>(creature); } }; -//Grand Astromancer Capernian AI + class boss_grand_astromancer_capernian : public CreatureScript { public: @@ -1218,15 +1056,17 @@ class boss_grand_astromancer_capernian : public CreatureScript advisorbase_ai::Reset(); } - void JustDied(Unit* /*killer*/) override + void JustDied(Unit* killer) override { - if (instance->GetData(DATA_KAELTHASEVENT) == 3) + if (_hasRessurrected) Talk(SAY_CAPERNIAN_DEATH); + + advisorbase_ai::JustDied(killer); } void AttackStart(Unit* who) override { - if (!who || FakeDeath || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + if (!who || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) return; if (me->Attack(who, true)) @@ -1239,39 +1079,18 @@ class boss_grand_astromancer_capernian : public CreatureScript } } - void EnterCombat(Unit* who) override + void EnterCombat(Unit* /*who*/) override { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; - - if (!who || FakeDeath) - return; + Talk(SAY_CAPERNIAN_AGGRO); } void UpdateAI(uint32 diff) override { advisorbase_ai::UpdateAI(diff); - //Faking Death, don't do anything - if (FakeDeath) + if (!UpdateVictim() || _inFakeDeath) return; - //Return since we have no target - if (!UpdateVictim()) - return; - - //Yell_Timer - if (!Yell) - { - if (Yell_Timer <= diff) - { - Talk(SAY_CAPERNIAN_AGGRO); - Yell = true; - } - else - Yell_Timer -= diff; - } - //Fireball_Timer if (Fireball_Timer <= diff) { @@ -1333,7 +1152,6 @@ class boss_grand_astromancer_capernian : public CreatureScript } }; -//Master Engineer Telonicus AI class boss_master_engineer_telonicus : public CreatureScript { public: @@ -1363,20 +1181,16 @@ class boss_master_engineer_telonicus : public CreatureScript advisorbase_ai::Reset(); } - void JustDied(Unit* /*killer*/) override + void JustDied(Unit* killer) override { - if (instance->GetData(DATA_KAELTHASEVENT) == 3) + if (_hasRessurrected) Talk(SAY_TELONICUS_DEATH); + + advisorbase_ai::JustDied(killer); } - void EnterCombat(Unit* who) override + void EnterCombat(Unit* /*who*/) override { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - return; - - if (!who || FakeDeath) - return; - Talk(SAY_TELONICUS_AGGRO); } @@ -1384,12 +1198,7 @@ class boss_master_engineer_telonicus : public CreatureScript { advisorbase_ai::UpdateAI(diff); - //Faking Death, do nothing - if (FakeDeath) - return; - - //Return since we have no target - if (!UpdateVictim()) + if (!UpdateVictim() || _inFakeDeath) return; //Bomb_Timer @@ -1422,7 +1231,6 @@ class boss_master_engineer_telonicus : public CreatureScript } }; -//Flame Strike AI class npc_kael_flamestrike : public CreatureScript { public: @@ -1493,7 +1301,6 @@ class npc_kael_flamestrike : public CreatureScript } }; -//Phoenix AI class npc_phoenix_tk : public CreatureScript { public: @@ -1553,7 +1360,6 @@ class npc_phoenix_tk : public CreatureScript } }; -//Phoenix Egg AI class npc_phoenix_egg_tk : public CreatureScript { public: @@ -1620,6 +1426,54 @@ class npc_phoenix_egg_tk : public CreatureScript } }; +// 35941 - Gravity Lapse +class spell_kael_gravity_lapse : public SpellScriptLoader +{ + public: + spell_kael_gravity_lapse() : SpellScriptLoader("spell_kael_gravity_lapse") { } + + class spell_kael_gravity_lapse_SpellScript : public SpellScript + { + PrepareSpellScript(spell_kael_gravity_lapse_SpellScript); + + public: + spell_kael_gravity_lapse_SpellScript() + { + _targetCount = 0; + } + + bool Validate(SpellInfo const* /*spell*/) override + { + for (uint8 i = 0; i < 25; ++i) + if (!sSpellMgr->GetSpellInfo(GravityLapseSpells[i])) + return false; + + return true; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), GravityLapseSpells[_targetCount], true); + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_GRAVITY_LAPSE_PERIODIC, true); + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_GRAVITY_LAPSE_FLIGHT_AURA, true); + _targetCount++; + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_kael_gravity_lapse_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + + private: + uint8 _targetCount; + }; + + SpellScript* GetSpellScript() const override + { + return new spell_kael_gravity_lapse_SpellScript(); + } +}; + void AddSC_boss_kaelthas() { new boss_kaelthas(); @@ -1630,4 +1484,5 @@ void AddSC_boss_kaelthas() new npc_kael_flamestrike(); new npc_phoenix_tk(); new npc_phoenix_egg_tk(); + new spell_kael_gravity_lapse(); } diff --git a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp index c421b9974ce..173e0596bea 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp @@ -34,6 +34,21 @@ EndScriptData */ 3 - Void Reaver event */ +DoorData const doorData[] = +{ + { GO_ARCANE_DOOR_LEFT, DATA_KAELTHAS, DOOR_TYPE_ROOM, BOUNDARY_SW }, + { GO_ARCANE_DOOR_RIGHT, DATA_KAELTHAS, DOOR_TYPE_ROOM, BOUNDARY_SE }, + { 0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE } // END +}; + +ObjectData const gameObjectData[] = +{ + { GO_KAEL_STATUE_RIGHT, DATA_KAEL_STATUE_RIGHT }, + { GO_KAEL_STATUE_LEFT, DATA_KAEL_STATUE_LEFT }, + { GO_TEMPEST_BRIDDGE_WINDOW, DATA_TEMPEST_BRIDGE_WINDOW }, + { 0, 0 } // END +}; + class instance_the_eye : public InstanceMapScript { public: @@ -45,8 +60,8 @@ class instance_the_eye : public InstanceMapScript { SetHeaders(DataHeader); SetBossNumber(EncounterCount); - - KaelthasEventPhase = 0; + LoadDoorData(doorData); + LoadObjectData(nullptr, gameObjectData); } ObjectGuid ThaladredTheDarkener; @@ -56,7 +71,6 @@ class instance_the_eye : public InstanceMapScript ObjectGuid Kaelthas; ObjectGuid Astromancer; ObjectGuid Alar; - uint8 KaelthasEventPhase; void OnCreatureCreate(Creature* creature) override { @@ -102,28 +116,6 @@ class instance_the_eye : public InstanceMapScript } return ObjectGuid::Empty; } - - void SetData(uint32 type, uint32 data) override - { - switch (type) - { - case DATA_KAELTHASEVENT: - KaelthasEventPhase = data; - break; - default: - break; - } - } - - uint32 GetData(uint32 type) const override - { - switch (type) - { - case DATA_KAELTHASEVENT: - return KaelthasEventPhase; - } - return 0; - } }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h b/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h index c46fe408274..ca60e14e923 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h +++ b/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h @@ -33,10 +33,14 @@ enum DataTypes DATA_ASTROMANCER = 4, DATA_GRANDASTROMANCERCAPERNIAN = 5, - DATA_KAELTHASEVENT = 6, - DATA_LORDSANGUINAR = 7, - DATA_MASTERENGINEERTELONICUS = 8, - DATA_THALADREDTHEDARKENER = 9 + DATA_LORDSANGUINAR = 6, + DATA_MASTERENGINEERTELONICUS = 7, + DATA_THALADREDTHEDARKENER = 8, + + // Additional Data + DATA_KAEL_STATUE_LEFT = 9, + DATA_KAEL_STATUE_RIGHT = 10, + DATA_TEMPEST_BRIDGE_WINDOW = 11 }; enum CreatureIds @@ -50,4 +54,13 @@ enum CreatureIds NPC_ALAR = 19514 }; +enum GameObjectIds +{ + GO_TEMPEST_BRIDDGE_WINDOW = 184069, + GO_KAEL_STATUE_RIGHT = 184596, + GO_KAEL_STATUE_LEFT = 184597, + GO_ARCANE_DOOR_LEFT = 184324, + GO_ARCANE_DOOR_RIGHT = 184325 +}; + #endif |