437fe2d8fd
Port From (https://github.com/TrinityCore/TrinityCore/commit/605e5f94c0d71cad8e83fa5a07eaec4e6bed9cc3)
459 lines
17 KiB
C#
459 lines
17 KiB
C#
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
|
|
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
|
|
|
|
using Framework.Constants;
|
|
using Game.AI;
|
|
using Game.Entities;
|
|
using Game.Maps;
|
|
using Game.Scripting;
|
|
using Game.Spells;
|
|
using System;
|
|
|
|
namespace Scripts.EasternKingdoms.MagistersTerrace.FelbloodKaelthas
|
|
{
|
|
struct TextIds
|
|
{
|
|
// Kael'thas Sunstrider
|
|
public const uint SayIntro1 = 0;
|
|
public const uint SayIntro2 = 1;
|
|
public const uint SayGravityLapse1 = 2;
|
|
public const uint SayGravityLapse2 = 3;
|
|
public const uint SayPowerFeedback = 4;
|
|
public const uint SaySummonPhoenix = 5;
|
|
public const uint SayAnnouncePyroblast = 6;
|
|
public const uint SayFlameStrike = 7;
|
|
public const uint SayDeath = 8;
|
|
}
|
|
|
|
struct SpellIds
|
|
{
|
|
// Kael'thas Sunstrider
|
|
public const uint Fireball = 44189;
|
|
public const uint GravityLapse = 49887;
|
|
public const uint HGravityLapse = 44226;
|
|
public const uint GravityLapseCenterTeleport = 44218;
|
|
public const uint GravityLapseLeftTeleport = 44219;
|
|
public const uint GravityLapseFrontLeftTeleport = 44220;
|
|
public const uint GravityLapseFrontTeleport = 44221;
|
|
public const uint GravityLapseFrontRightTeleport = 44222;
|
|
public const uint GravityLapseRightTeleport = 44223;
|
|
public const uint GravityLapseInitial = 44224;
|
|
public const uint GravityLapseFly = 44227;
|
|
public const uint GravityLapseBeamVisualPeriodic = 44251;
|
|
public const uint SummonArcaneSphere = 44265;
|
|
public const uint FlameStrike = 46162;
|
|
public const uint ShockBarrier = 46165;
|
|
public const uint PowerFeedback = 44233;
|
|
public const uint HPowerFeedback = 47109;
|
|
public const uint Pyroblast = 36819;
|
|
public const uint Phoenix = 44194;
|
|
public const uint EmoteTalkExclamation = 48348;
|
|
public const uint EmotePoint = 48349;
|
|
public const uint EmoteRoar = 48350;
|
|
public const uint ClearFlight = 44232;
|
|
public const uint QuiteSuicide = 3617; // Serverside public const uint
|
|
|
|
// Flame Strike
|
|
public const uint FlameStrikeDummy = 44191;
|
|
public const uint FlameStrikeDamage = 44190;
|
|
|
|
// Phoenix
|
|
public const uint Rebirth = 44196;
|
|
public const uint Burn = 44197;
|
|
public const uint EmberBlast = 44199;
|
|
public const uint SummonPhoenixEgg = 44195; // Serverside public const uint
|
|
public const uint FullHeal = 17683;
|
|
}
|
|
|
|
enum Phase
|
|
{
|
|
Intro = 0,
|
|
One = 1,
|
|
Two = 2,
|
|
Outro = 3
|
|
}
|
|
|
|
struct MiscConst
|
|
{
|
|
public static uint[] GravityLapseTeleportSpells =
|
|
{
|
|
SpellIds.GravityLapseLeftTeleport,
|
|
SpellIds.GravityLapseFrontLeftTeleport,
|
|
SpellIds.GravityLapseFrontTeleport,
|
|
SpellIds.GravityLapseFrontRightTeleport,
|
|
SpellIds.GravityLapseRightTeleport
|
|
};
|
|
}
|
|
|
|
[Script]
|
|
class boss_felblood_kaelthas : BossAI
|
|
{
|
|
byte _gravityLapseTargetCount;
|
|
bool _firstGravityLapse;
|
|
|
|
Phase _phase;
|
|
|
|
static uint groupFireBall = 1;
|
|
|
|
public boss_felblood_kaelthas(Creature creature) : base(creature, DataTypes.KaelthasSunstrider)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
void Initialize()
|
|
{
|
|
_gravityLapseTargetCount = 0;
|
|
_firstGravityLapse = true;
|
|
}
|
|
|
|
public override void JustEngagedWith(Unit who)
|
|
{
|
|
base.JustEngagedWith(who);
|
|
_phase = Phase.One;
|
|
|
|
_scheduler.Schedule(TimeSpan.FromMilliseconds(1), groupFireBall, task =>
|
|
{
|
|
DoCastVictim(SpellIds.Fireball);
|
|
task.Repeat(TimeSpan.FromSeconds(2.5));
|
|
});
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(44), task =>
|
|
{
|
|
Talk(TextIds.SayFlameStrike);
|
|
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 40.0f, true);
|
|
if (target != null)
|
|
DoCast(target, SpellIds.FlameStrike);
|
|
task.Repeat();
|
|
});
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(12), task =>
|
|
{
|
|
Talk(TextIds.SaySummonPhoenix);
|
|
DoCastSelf(SpellIds.Phoenix);
|
|
task.Repeat(TimeSpan.FromSeconds(45));
|
|
});
|
|
|
|
if (IsHeroic())
|
|
{
|
|
_scheduler.Schedule(TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(1), task =>
|
|
{
|
|
Talk(TextIds.SayAnnouncePyroblast);
|
|
DoCastSelf(SpellIds.ShockBarrier);
|
|
task.RescheduleGroup(groupFireBall, TimeSpan.FromSeconds(2.5));
|
|
task.Schedule(TimeSpan.FromSeconds(2), pyroBlastTask =>
|
|
{
|
|
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 40.0f, true);
|
|
if (target != null)
|
|
DoCast(target, SpellIds.Pyroblast);
|
|
});
|
|
task.Repeat(TimeSpan.FromMinutes(1));
|
|
});
|
|
}
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
_Reset();
|
|
Initialize();
|
|
_phase = Phase.Intro;
|
|
}
|
|
|
|
public override void JustDied(Unit killer)
|
|
{
|
|
// No _JustDied() here because otherwise we would reset the events which will trigger the death sequence twice.
|
|
instance.SetBossState(DataTypes.KaelthasSunstrider, EncounterState.Done);
|
|
}
|
|
|
|
public override void EnterEvadeMode(EvadeReason why)
|
|
{
|
|
DoCastAOE(SpellIds.ClearFlight, new CastSpellExtraArgs(true));
|
|
_EnterEvadeMode();
|
|
summons.DespawnAll();
|
|
_DespawnAtEvade();
|
|
}
|
|
|
|
public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null)
|
|
{
|
|
// Checking for lethal damage first so we trigger the outro phase without triggering phase two in case of oneshot attacks
|
|
if (damage >= me.GetHealth() && _phase != Phase.Outro)
|
|
{
|
|
me.AttackStop();
|
|
me.SetReactState(ReactStates.Passive);
|
|
me.InterruptNonMeleeSpells(true);
|
|
me.RemoveAurasDueToSpell(DungeonMode(SpellIds.PowerFeedback, SpellIds.HPowerFeedback));
|
|
summons.DespawnAll();
|
|
DoCastAOE(SpellIds.ClearFlight);
|
|
Talk(TextIds.SayDeath);
|
|
|
|
_phase = Phase.Outro;
|
|
_scheduler.CancelAll();
|
|
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(1), task =>
|
|
{
|
|
DoCastSelf(SpellIds.EmoteTalkExclamation);
|
|
});
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(3.8), task =>
|
|
{
|
|
DoCastSelf(SpellIds.EmotePoint);
|
|
});
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(7.4), task =>
|
|
{
|
|
DoCastSelf(SpellIds.EmoteRoar);
|
|
});
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(10), task =>
|
|
{
|
|
DoCastSelf(SpellIds.EmoteRoar);
|
|
});
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(11), task =>
|
|
{
|
|
DoCastSelf(SpellIds.QuiteSuicide);
|
|
});
|
|
}
|
|
|
|
// Phase two checks. Skip phase two if we are in the outro already
|
|
if (me.HealthBelowPctDamaged(50, damage) && _phase != Phase.Two && _phase != Phase.Outro)
|
|
{
|
|
_phase = Phase.Two;
|
|
_scheduler.CancelAll();
|
|
_scheduler.Schedule(TimeSpan.FromMilliseconds(1), task =>
|
|
{
|
|
Talk(_firstGravityLapse ? TextIds.SayGravityLapse1 : TextIds.SayGravityLapse2);
|
|
_firstGravityLapse = false;
|
|
me.SetReactState(ReactStates.Passive);
|
|
me.AttackStop();
|
|
me.GetMotionMaster().Clear();
|
|
task.Schedule(TimeSpan.FromSeconds(1), _ =>
|
|
{
|
|
DoCastSelf(SpellIds.GravityLapseCenterTeleport);
|
|
task.Schedule(TimeSpan.FromSeconds(1), _ =>
|
|
{
|
|
_gravityLapseTargetCount = 0;
|
|
DoCastAOE(SpellIds.GravityLapseInitial);
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(4), _ =>
|
|
{
|
|
for (byte i = 0; i < 3; i++)
|
|
DoCastSelf(SpellIds.SummonArcaneSphere, new CastSpellExtraArgs(true));
|
|
});
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(5), _ =>
|
|
{
|
|
DoCastAOE(SpellIds.GravityLapseBeamVisualPeriodic);
|
|
});
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(35), _ =>
|
|
{
|
|
Talk(TextIds.SayPowerFeedback);
|
|
DoCastAOE(SpellIds.ClearFlight);
|
|
DoCastSelf(DungeonMode(SpellIds.PowerFeedback, SpellIds.HPowerFeedback));
|
|
summons.DespawnEntry(CreatureIds.ArcaneSphere);
|
|
task.Repeat(TimeSpan.FromSeconds(11));
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// Kael'thas may only kill himself via Quite Suicide
|
|
if (damage >= me.GetHealth() && attacker != me)
|
|
damage = (uint)(me.GetHealth() - 1);
|
|
}
|
|
|
|
public override void SetData(uint type, uint data)
|
|
{
|
|
if (type == DataTypes.KaelthasIntro)
|
|
{
|
|
// skip the intro if Kael'thas is engaged already
|
|
if (_phase != Phase.Intro)
|
|
return;
|
|
|
|
me.SetImmuneToPC(true);
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(6), task =>
|
|
{
|
|
Talk(TextIds.SayIntro1);
|
|
me.SetEmoteState(Emote.StateTalk);
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(20.6), _ =>
|
|
{
|
|
Talk(TextIds.SayIntro2);
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(15) + TimeSpan.FromMilliseconds(500), _ =>
|
|
{
|
|
me.SetEmoteState(Emote.OneshotNone);
|
|
me.SetImmuneToPC(false);
|
|
});
|
|
});
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(15.6), _ => me.HandleEmoteCommand(Emote.OneshotLaughNoSheathe));
|
|
});
|
|
}
|
|
}
|
|
|
|
public override void SpellHitTarget(WorldObject target, SpellInfo spellInfo)
|
|
{
|
|
Unit unitTarget = target.ToUnit();
|
|
if (unitTarget == null)
|
|
return;
|
|
|
|
switch (spellInfo.Id)
|
|
{
|
|
case SpellIds.GravityLapseInitial:
|
|
{
|
|
DoCast(unitTarget, MiscConst.GravityLapseTeleportSpells[_gravityLapseTargetCount], new CastSpellExtraArgs(true));
|
|
target.m_Events.AddEventAtOffset(() =>
|
|
{
|
|
target.CastSpell(target, DungeonMode(SpellIds.GravityLapse, SpellIds.HGravityLapse));
|
|
target.CastSpell(target, SpellIds.GravityLapseFly);
|
|
|
|
}, TimeSpan.FromMilliseconds(400));
|
|
_gravityLapseTargetCount++;
|
|
break;
|
|
}
|
|
case SpellIds.ClearFlight:
|
|
unitTarget.RemoveAurasDueToSpell(SpellIds.GravityLapseFly);
|
|
unitTarget.RemoveAurasDueToSpell(DungeonMode(SpellIds.GravityLapse, SpellIds.HGravityLapse));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override void JustSummoned(Creature summon)
|
|
{
|
|
summons.Summon(summon);
|
|
|
|
switch (summon.GetEntry())
|
|
{
|
|
case CreatureIds.ArcaneSphere:
|
|
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 70.0f, true);
|
|
if (target != null)
|
|
summon.GetMotionMaster().MoveFollow(target, 0.0f, 0.0f);
|
|
break;
|
|
case CreatureIds.FlameStrike:
|
|
summon.CastSpell(summon, SpellIds.FlameStrikeDummy);
|
|
summon.DespawnOrUnsummon(TimeSpan.FromSeconds(15));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override void UpdateAI(uint diff)
|
|
{
|
|
if (!UpdateVictim() && _phase != Phase.Intro)
|
|
return;
|
|
|
|
_scheduler.Update(diff);
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class npc_felblood_kaelthas_phoenix : ScriptedAI
|
|
{
|
|
InstanceScript _instance;
|
|
|
|
bool _isInEgg;
|
|
ObjectGuid _eggGUID;
|
|
|
|
public npc_felblood_kaelthas_phoenix(Creature creature) : base(creature)
|
|
{
|
|
_instance = creature.GetInstanceScript();
|
|
Initialize();
|
|
}
|
|
|
|
void Initialize()
|
|
{
|
|
me.SetReactState(ReactStates.Passive);
|
|
_isInEgg = false;
|
|
}
|
|
|
|
public override void IsSummonedBy(WorldObject summoner)
|
|
{
|
|
DoZoneInCombat();
|
|
DoCastSelf(SpellIds.Burn);
|
|
DoCastSelf(SpellIds.Rebirth);
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(2), task => me.SetReactState(ReactStates.Aggressive));
|
|
}
|
|
|
|
public override void JustEngagedWith(Unit who) { }
|
|
|
|
public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null)
|
|
{
|
|
if (damage >= me.GetHealth())
|
|
{
|
|
if (!_isInEgg)
|
|
{
|
|
me.AttackStop();
|
|
me.SetReactState(ReactStates.Passive);
|
|
me.RemoveAllAuras();
|
|
me.SetUninteractible(true);
|
|
DoCastSelf(SpellIds.EmberBlast);
|
|
// DoCastSelf(SpellSummonPhoenixEgg); -- We do a manual summon for now. Feel free to move it to spelleffect_dbc
|
|
Creature egg = DoSummon(CreatureIds.PhoenixEgg, me.GetPosition(), TimeSpan.FromSeconds(0));
|
|
if (egg != null)
|
|
{
|
|
Creature kaelthas = _instance.GetCreature(DataTypes.KaelthasSunstrider);
|
|
if (kaelthas != null)
|
|
{
|
|
kaelthas.GetAI().JustSummoned(egg);
|
|
_eggGUID = egg.GetGUID();
|
|
}
|
|
}
|
|
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(15), task =>
|
|
{
|
|
Creature egg = ObjectAccessor.GetCreature(me, _eggGUID);
|
|
if (egg != null)
|
|
egg.DespawnOrUnsummon();
|
|
|
|
me.RemoveAllAuras();
|
|
task.Schedule(TimeSpan.FromSeconds(2), rebirthTask =>
|
|
{
|
|
DoCastSelf(SpellIds.Rebirth);
|
|
rebirthTask.Schedule(TimeSpan.FromSeconds(2), engageTask =>
|
|
{
|
|
_isInEgg = false;
|
|
DoCastSelf(SpellIds.FullHeal);
|
|
DoCastSelf(SpellIds.Burn);
|
|
me.SetUninteractible(false);
|
|
engageTask.Schedule(TimeSpan.FromSeconds(2), task => me.SetReactState(ReactStates.Aggressive));
|
|
});
|
|
});
|
|
});
|
|
_isInEgg = true;
|
|
}
|
|
damage = (uint)(me.GetHealth() - 1);
|
|
}
|
|
|
|
}
|
|
|
|
public override void SummonedCreatureDies(Creature summon, Unit killer)
|
|
{
|
|
// Egg has been destroyed within 15 seconds so we lose the phoenix.
|
|
me.DespawnOrUnsummon();
|
|
}
|
|
|
|
public override void UpdateAI(uint diff)
|
|
{
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
_scheduler.Update(diff);
|
|
}
|
|
}
|
|
|
|
[Script] // 44191 - Flame Strike
|
|
class spell_felblood_kaelthas_flame_strike : AuraScript
|
|
{
|
|
public override bool Validate(SpellInfo spellInfo)
|
|
{
|
|
return ValidateSpellInfo(SpellIds.FlameStrikeDamage);
|
|
}
|
|
|
|
void AfterRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
|
|
{
|
|
Unit target = GetTarget();
|
|
if (target != null)
|
|
target.CastSpell(target, SpellIds.FlameStrikeDamage);
|
|
}
|
|
|
|
public override void Register()
|
|
{
|
|
AfterEffectRemove.Add(new EffectApplyHandler(AfterRemove, 0, AuraType.Dummy, AuraEffectHandleModes.Real));
|
|
}
|
|
}
|
|
}
|
|
|