885 lines
28 KiB
C#
885 lines
28 KiB
C#
/*
|
|
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
|
|
*
|
|
* 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 3 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using Framework.Constants;
|
|
using Framework.Dynamic;
|
|
using Game.AI;
|
|
using Game.Entities;
|
|
using Game.Maps;
|
|
using Game.Scripting;
|
|
using Game.Spells;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Scripts.Northrend.Ulduar.Xt002
|
|
{
|
|
struct SpellIds
|
|
{
|
|
public const uint TympanicTantrum = 62776;
|
|
public const uint SearingLight = 63018;
|
|
|
|
public const uint SummonLifeSpark = 64210;
|
|
public const uint SummonVoidZone = 64203;
|
|
|
|
public const uint GravityBomb = 63024;
|
|
|
|
public const uint Heartbreak = 65737;
|
|
|
|
// Cast By 33337 At Heartbreak:
|
|
public const uint RechargePummeler = 62831; // Summons 33344
|
|
public const uint RechargeScrapbot = 62828; // Summons 33343
|
|
public const uint RechargeBoombot = 62835; // Summons 33346
|
|
|
|
// Cast By 33329 On 33337 (Visual?)
|
|
public const uint EnergyOrb = 62790; // Triggers 62826 - Needs Spellscript For Periodic Tick To Cast One Of The Random Spells Above
|
|
|
|
public const uint HeartHealToFull = 17683;
|
|
public const uint HeartOverload = 62789;
|
|
|
|
public const uint HeartLightningTether = 64799; // Cast On Self?
|
|
public const uint Enrage = 26662;
|
|
public const uint Stand = 37752;
|
|
public const uint Submerge = 37751;
|
|
|
|
//------------------Void Zone--------------------
|
|
public const uint VoidZone = 64203;
|
|
public const uint Consumption = 64208;
|
|
|
|
// Life Spark
|
|
public const uint ArcanePowerState = 49411;
|
|
public const uint StaticCharged = 64227;
|
|
public const uint Shock = 64230;
|
|
|
|
//----------------Xt-002 Heart-------------------
|
|
public const uint ExposedHeart = 63849;
|
|
public const uint HeartRideVehicle = 63852;
|
|
public const uint RideVehicleExposed = 63313; //Heart Exposed
|
|
|
|
//---------------Xm-024 Pummeller----------------
|
|
public const uint ArcingSmash = 8374;
|
|
public const uint Trample = 5568;
|
|
public const uint Uppercut = 10966;
|
|
|
|
// Scrabot:
|
|
public const uint ScrapbotRideVehicle = 47020;
|
|
public const uint ScrapRepair = 62832;
|
|
public const uint Suicide = 7;
|
|
|
|
//------------------Boombot-----------------------
|
|
public const uint AuraBoombot = 65032;
|
|
public const uint Boom = 62834;
|
|
|
|
// Achievement-Related Spells
|
|
public const uint AchievementCreditNerfScrapbots = 65037;
|
|
}
|
|
|
|
struct XT002Data
|
|
{
|
|
public const uint TransferedHealth = 0;
|
|
public const uint HardMode = 1;
|
|
public const uint HealthRecovered = 2;
|
|
public const uint GravityBombCasualty = 3;
|
|
}
|
|
|
|
struct Texts
|
|
{
|
|
public const uint Aggro = 0;
|
|
public const uint HeartOpened = 1;
|
|
public const uint HeartClosed = 2;
|
|
public const uint TympanicTantrum = 3;
|
|
public const uint Slay = 4;
|
|
public const uint Berserk = 5;
|
|
public const uint Death = 6;
|
|
public const uint Summon = 7;
|
|
public const uint EmoteHeartOpened = 8;
|
|
public const uint EmoteHeartClosed = 9;
|
|
public const uint EmoteTympanicTantrum = 10;
|
|
public const uint EmoteScrapbot = 11;
|
|
}
|
|
|
|
struct Misc
|
|
{
|
|
public const uint PhaseOneGroup = 1;
|
|
public const int ActionHardMode = 1;
|
|
public const uint AchievMustDeconstructFaster = 21027;
|
|
|
|
public const sbyte SeatHeartNormal = 0;
|
|
public const sbyte SeatHeartExposed = 1;
|
|
}
|
|
|
|
[Script]
|
|
class boss_xt002_ : BossAI
|
|
{
|
|
public boss_xt002_(Creature creature) : base(creature, BossIds.Xt002)
|
|
{
|
|
Initialize();
|
|
_transferHealth = 0;
|
|
}
|
|
|
|
void Initialize()
|
|
{
|
|
_healthRecovered = false;
|
|
_gravityBombCasualty = false;
|
|
_hardMode = false;
|
|
|
|
_heartExposed = 0;
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
_Reset();
|
|
|
|
me.RemoveFlag(UnitFields.Flags, UnitFlags.NotSelectable);
|
|
me.SetReactState(ReactStates.Aggressive);
|
|
DoCastSelf(SpellIds.Stand);
|
|
|
|
Initialize();
|
|
|
|
instance.DoStopCriteriaTimer(CriteriaTimedTypes.Event, Misc.AchievMustDeconstructFaster);
|
|
}
|
|
|
|
public override void EnterCombat(Unit who)
|
|
{
|
|
Talk(Texts.Aggro);
|
|
_EnterCombat();
|
|
|
|
//Enrage
|
|
_scheduler.Schedule(TimeSpan.FromMinutes(10), task =>
|
|
{
|
|
Talk(Texts.Berserk);
|
|
DoCastSelf(SpellIds.Enrage);
|
|
});
|
|
|
|
//Gavity Bomb
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(20), Misc.PhaseOneGroup, task =>
|
|
{
|
|
Unit target = SelectTarget(SelectAggroTarget.Random, 0);
|
|
if (target)
|
|
DoCast(target, SpellIds.GravityBomb);
|
|
|
|
task.Repeat(TimeSpan.FromSeconds(20));
|
|
});
|
|
|
|
//Searing Light
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(20), Misc.PhaseOneGroup, task =>
|
|
{
|
|
Unit target = SelectTarget(SelectAggroTarget.Random, 0);
|
|
if (target)
|
|
DoCast(target, SpellIds.SearingLight);
|
|
|
|
task.Repeat(TimeSpan.FromSeconds(20));
|
|
});
|
|
|
|
//Tympanic Tantrum
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(30), Misc.PhaseOneGroup, task =>
|
|
{
|
|
Talk(Texts.TympanicTantrum);
|
|
Talk(Texts.EmoteTympanicTantrum);
|
|
DoCast(SpellIds.TympanicTantrum);
|
|
task.Repeat(TimeSpan.FromSeconds(30));
|
|
});
|
|
|
|
instance.DoStartCriteriaTimer(CriteriaTimedTypes.Event, Misc.AchievMustDeconstructFaster);
|
|
}
|
|
|
|
public override void DoAction(int action)
|
|
{
|
|
switch (action)
|
|
{
|
|
case Misc.ActionHardMode:
|
|
_scheduler.Schedule(TimeSpan.FromMilliseconds(1), task =>
|
|
{
|
|
SetPhaseOne(true);
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override void KilledUnit(Unit who)
|
|
{
|
|
if (who.GetTypeId() == TypeId.Player)
|
|
Talk(Texts.Slay);
|
|
}
|
|
|
|
public override void JustDied(Unit killer)
|
|
{
|
|
Talk(Texts.Death);
|
|
_JustDied();
|
|
me.RemoveFlag(UnitFields.Flags, UnitFlags.NotSelectable);
|
|
}
|
|
|
|
public override void DamageTaken(Unit attacker, ref uint damage)
|
|
{
|
|
if (!_hardMode && !me.HasReactState(ReactStates.Passive) && !HealthAbovePct(100 - 25 * (_heartExposed + 1)))
|
|
ExposeHeart();
|
|
}
|
|
|
|
public override void UpdateAI(uint diff)
|
|
{
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
_scheduler.Update(diff);
|
|
|
|
if (!me.HasReactState(ReactStates.Passive))
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
|
|
public override void PassengerBoarded(Unit who, sbyte seatId, bool apply)
|
|
{
|
|
if (apply && who.GetEntry() == InstanceCreatureIds.XS013Scrapbot)
|
|
{
|
|
// Need this so we can properly determine when to expose heart again in damagetaken hook
|
|
if (me.GetHealthPct() > (25 * (4 - _heartExposed)))
|
|
++_heartExposed;
|
|
|
|
Talk(Texts.EmoteScrapbot);
|
|
DoCast(who, SpellIds.ScrapRepair, true);
|
|
_healthRecovered = true;
|
|
}
|
|
|
|
if (apply && seatId == Misc.SeatHeartExposed)
|
|
who.CastSpell(who, SpellIds.ExposedHeart); // Channeled
|
|
}
|
|
|
|
public override uint GetData(uint type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case XT002Data.HardMode:
|
|
return _hardMode ? 1 : 0u;
|
|
case XT002Data.HealthRecovered:
|
|
return _healthRecovered ? 1 : 0u;
|
|
case XT002Data.GravityBombCasualty:
|
|
return _gravityBombCasualty ? 1 : 0u;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public override void SetData(uint type, uint data)
|
|
{
|
|
switch (type)
|
|
{
|
|
case XT002Data.TransferedHealth:
|
|
_transferHealth = data;
|
|
break;
|
|
case XT002Data.GravityBombCasualty:
|
|
_gravityBombCasualty = (data > 0) ? true : false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ExposeHeart()
|
|
{
|
|
Talk(Texts.HeartOpened);
|
|
Talk(Texts.EmoteHeartOpened);
|
|
|
|
DoCastSelf(SpellIds.Submerge); // WIll make creature untargetable
|
|
me.AttackStop();
|
|
me.SetReactState(ReactStates.Passive);
|
|
|
|
Unit heart = me.GetVehicleKit() ? me.GetVehicleKit().GetPassenger(Misc.SeatHeartNormal) : null;
|
|
if (heart)
|
|
{
|
|
heart.CastSpell(heart, SpellIds.HeartOverload);
|
|
heart.CastSpell(me, SpellIds.HeartLightningTether);
|
|
heart.CastSpell(heart, SpellIds.HeartHealToFull, true);
|
|
heart.CastSpell(me, SpellIds.RideVehicleExposed, true);
|
|
heart.RemoveFlag(UnitFields.Flags, UnitFlags.NotSelectable);
|
|
heart.SetFlag(UnitFields.Flags, UnitFlags.Unk29);
|
|
}
|
|
_scheduler.DelayGroup(Misc.PhaseOneGroup, TimeSpan.FromSeconds(30));
|
|
|
|
// Start "end of phase 2 timer"
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(30), task => { SetPhaseOne(false); });
|
|
|
|
_heartExposed++;
|
|
}
|
|
|
|
void SetPhaseOne(bool isHardMode)
|
|
{
|
|
if (isHardMode)
|
|
{
|
|
me.SetFullHealth();
|
|
DoCastSelf(SpellIds.Heartbreak, true);
|
|
me.AddLootMode(LootModes.HardMode1);
|
|
_hardMode = true;
|
|
}
|
|
|
|
Talk(Texts.HeartClosed);
|
|
Talk(Texts.EmoteHeartClosed);
|
|
|
|
me.RemoveFlag(UnitFields.Flags, UnitFlags.NotSelectable);
|
|
me.SetReactState(ReactStates.Aggressive);
|
|
DoCastSelf(SpellIds.Stand);
|
|
|
|
//_events.RescheduleEvent(EVENT_SEARING_LIGHT, TIMER_SEARING_LIGHT / 2);
|
|
//_events.RescheduleEvent(EVENT_GRAVITY_BOMB, TIMER_GRAVITY_BOMB);
|
|
//_events.RescheduleEvent(EVENT_TYMPANIC_TANTRUM, RandomHelper.URand(TIMER_TYMPANIC_TANTRUM_MIN, TIMER_TYMPANIC_TANTRUM_MAX));
|
|
|
|
Unit heart = me.GetVehicleKit() ? me.GetVehicleKit().GetPassenger(Misc.SeatHeartExposed) : null;
|
|
if (!heart)
|
|
return;
|
|
|
|
heart.CastSpell(me, SpellIds.HeartRideVehicle, true);
|
|
heart.SetFlag(UnitFields.Flags, UnitFlags.NotSelectable);
|
|
heart.RemoveFlag(UnitFields.Flags, UnitFlags.Unk29);
|
|
heart.RemoveAurasDueToSpell(SpellIds.ExposedHeart);
|
|
|
|
if (!_hardMode)
|
|
{
|
|
if (_transferHealth == 0)
|
|
_transferHealth = (uint)(heart.GetMaxHealth() - heart.GetHealth());
|
|
|
|
if (_transferHealth >= me.GetHealth())
|
|
_transferHealth = (uint)me.GetHealth() - 1;
|
|
|
|
me.ModifyHealth(-(int)_transferHealth);
|
|
me.LowerPlayerDamageReq(_transferHealth);
|
|
}
|
|
}
|
|
|
|
// Achievement related
|
|
bool _healthRecovered; // Did a scrapbot recover XT-002's health during the encounter?
|
|
bool _hardMode; // Are we in hard mode? Or: was the heart killed during phase 2?
|
|
bool _gravityBombCasualty; // Did someone die because of Gravity Bomb damage?
|
|
|
|
byte _heartExposed;
|
|
uint _transferHealth;
|
|
}
|
|
|
|
[Script]
|
|
class npc_xt002_heart : ScriptedAI
|
|
{
|
|
public npc_xt002_heart(Creature creature) : base(creature)
|
|
{
|
|
_instance = creature.GetInstanceScript();
|
|
|
|
SetCombatMovement(false);
|
|
}
|
|
|
|
public override void UpdateAI(uint diff) { }
|
|
|
|
public override void JustDied(Unit killer)
|
|
{
|
|
Creature xt002 = _instance != null ? ObjectAccessor.GetCreature(me, _instance.GetGuidData(BossIds.Xt002)) : null;
|
|
if (!xt002 || xt002.GetAI() == null)
|
|
return;
|
|
|
|
xt002.GetAI().SetData(XT002Data.TransferedHealth, (uint)me.GetHealth());
|
|
xt002.GetAI().DoAction(Misc.ActionHardMode);
|
|
}
|
|
|
|
InstanceScript _instance;
|
|
}
|
|
|
|
[Script]
|
|
class npc_scrapbot : ScriptedAI
|
|
{
|
|
public npc_scrapbot(Creature creature) : base(creature)
|
|
{
|
|
_instance = me.GetInstanceScript();
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
me.SetReactState(ReactStates.Passive);
|
|
Creature pXT002 = ObjectAccessor.GetCreature(me, _instance.GetGuidData(BossIds.Xt002));
|
|
if (pXT002)
|
|
me.GetMotionMaster().MoveFollow(pXT002, 0.0f, 0.0f);
|
|
}
|
|
|
|
public override void MovementInform(MovementGeneratorType type, uint id)
|
|
{
|
|
ObjectGuid guid = _instance.GetGuidData(BossIds.Xt002);
|
|
if (type == MovementGeneratorType.Follow && id == guid.GetCounter())
|
|
{
|
|
Creature xt002 = ObjectAccessor.GetCreature(me, guid);
|
|
if (xt002)
|
|
{
|
|
if (me.IsWithinMeleeRange(xt002))
|
|
{
|
|
DoCast(xt002, SpellIds.ScrapbotRideVehicle);
|
|
// Unapply vehicle aura again
|
|
xt002.RemoveAurasDueToSpell(SpellIds.ScrapbotRideVehicle);
|
|
me.DespawnOrUnsummon();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
InstanceScript _instance;
|
|
}
|
|
|
|
[Script]
|
|
class npc_pummeller : ScriptedAI
|
|
{
|
|
public npc_pummeller(Creature creature) : base(creature)
|
|
{
|
|
Initialize();
|
|
_instance = creature.GetInstanceScript();
|
|
}
|
|
|
|
void Initialize()
|
|
{
|
|
_scheduler.SetValidator(() => me.IsWithinMeleeRange(me.GetVictim()));
|
|
|
|
//Arcing Smash
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(27), task =>
|
|
{
|
|
DoCastVictim(SpellIds.ArcingSmash);
|
|
task.Repeat(TimeSpan.FromSeconds(27));
|
|
});
|
|
|
|
//Trample
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(22), task =>
|
|
{
|
|
DoCastVictim(SpellIds.Trample);
|
|
task.Repeat(TimeSpan.FromSeconds(22));
|
|
});
|
|
|
|
//Uppercut
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(17), task =>
|
|
{
|
|
DoCastVictim(SpellIds.Uppercut);
|
|
task.Repeat(TimeSpan.FromSeconds(17));
|
|
});
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
Initialize();
|
|
Creature xt002 = ObjectAccessor.GetCreature(me, _instance.GetGuidData(BossIds.Xt002));
|
|
if (xt002)
|
|
{
|
|
Position pos = xt002.GetPosition();
|
|
me.GetMotionMaster().MovePoint(0, pos);
|
|
}
|
|
}
|
|
|
|
public override void UpdateAI(uint diff)
|
|
{
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
_scheduler.Update(diff);
|
|
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
|
|
InstanceScript _instance;
|
|
}
|
|
|
|
class BoomEvent : BasicEvent
|
|
{
|
|
public BoomEvent(Creature me)
|
|
{
|
|
_me = me;
|
|
}
|
|
|
|
public override bool Execute(ulong time, uint diff)
|
|
{
|
|
// This hack is here because we suspect our implementation of spell effect execution on targets
|
|
// is done in the wrong order. We suspect that 0 needs to be applied on all targets,
|
|
// then 1, etc - instead of applying each effect on target1, then target2, etc.
|
|
// The above situation causes the visual for this spell to be bugged, so we remove the instakill
|
|
// effect and implement a script hack for that.
|
|
|
|
_me.CastSpell(_me, SpellIds.Boom, false);
|
|
return true;
|
|
}
|
|
|
|
Creature _me;
|
|
}
|
|
|
|
[Script]
|
|
class npc_boombot : ScriptedAI
|
|
{
|
|
public npc_boombot(Creature creature) : base(creature)
|
|
{
|
|
Initialize();
|
|
_instance = creature.GetInstanceScript();
|
|
}
|
|
|
|
void Initialize()
|
|
{
|
|
_boomed = false;
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
Initialize();
|
|
|
|
DoCast(SpellIds.AuraBoombot); // For achievement
|
|
|
|
// HACK/workaround:
|
|
// these values aren't confirmed - lack of data - and the values in DB are incorrect
|
|
// these values are needed for correct damage of Boom spell
|
|
me.SetFloatValue(UnitFields.MinDamage, 15000.0f);
|
|
me.SetFloatValue(UnitFields.MaxDamage, 18000.0f);
|
|
|
|
// @todo proper waypoints?
|
|
Creature pXT002 = ObjectAccessor.GetCreature(me, _instance.GetGuidData(BossIds.Xt002));
|
|
if (pXT002)
|
|
me.GetMotionMaster().MoveFollow(pXT002, 0.0f, 0.0f);
|
|
}
|
|
|
|
public override void DamageTaken(Unit who, ref uint damage)
|
|
{
|
|
if (damage >= (me.GetHealth() - me.GetMaxHealth() * 0.5f) && !_boomed)
|
|
{
|
|
_boomed = true; // Prevent recursive calls
|
|
|
|
//me.SendSpellInstakillLog(Spells.Boom, me);
|
|
|
|
//me.DealDamage(me, me.GetHealth(), null, DamageEffectType.NoDamage, SpellSchoolMask.Normal, null, false);
|
|
|
|
damage = 0;
|
|
|
|
me.CastSpell(me, SpellIds.Boom, false);
|
|
|
|
// Visual only seems to work if the instant kill event is delayed or the spell itself is delayed
|
|
// Casting done from player and caster source has the same targetinfo flags,
|
|
// so that can't be the issue
|
|
// See BoomEvent class
|
|
// Schedule 1s delayed
|
|
//me.m_Events.AddEvent(new BoomEvent(me), me.m_Events.CalculateTime(1 * Time.InMilliseconds));
|
|
}
|
|
}
|
|
|
|
public override void UpdateAI(uint diff)
|
|
{
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
// No melee attack
|
|
}
|
|
|
|
InstanceScript _instance;
|
|
bool _boomed;
|
|
}
|
|
|
|
[Script]
|
|
class npc_life_spark : ScriptedAI
|
|
{
|
|
public npc_life_spark(Creature creature) : base(creature) { }
|
|
|
|
void Initialize()
|
|
{
|
|
_scheduler.Schedule(TimeSpan.FromMilliseconds(1), task =>
|
|
{
|
|
DoCastVictim(SpellIds.Shock);
|
|
task.Repeat(TimeSpan.FromSeconds(12));
|
|
});
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
DoCastSelf(SpellIds.ArcanePowerState);
|
|
_scheduler.CancelAll();
|
|
}
|
|
|
|
public override void EnterCombat(Unit victim)
|
|
{
|
|
DoCastSelf(SpellIds.StaticCharged);
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(12), task =>
|
|
{
|
|
DoCastVictim(SpellIds.Shock);
|
|
task.Repeat();
|
|
});
|
|
}
|
|
|
|
public override void UpdateAI(uint diff)
|
|
{
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
if (me.HasUnitState(UnitState.Casting))
|
|
return;
|
|
|
|
_scheduler.Update(diff, DoMeleeAttackIfReady);
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class npc_xt_void_zone : PassiveAI
|
|
{
|
|
public npc_xt_void_zone(Creature creature) : base(creature) { }
|
|
|
|
public override void Reset()
|
|
{
|
|
_scheduler.Schedule(TimeSpan.FromSeconds(1), consumption =>
|
|
{
|
|
DoCastSelf(SpellIds.Consumption);
|
|
consumption.Repeat();
|
|
});
|
|
}
|
|
|
|
public override void UpdateAI(uint diff)
|
|
{
|
|
_scheduler.Update(diff);
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class spell_xt002_searing_light_spawn_life_spark : AuraScript
|
|
{
|
|
public override bool Validate(SpellInfo spell)
|
|
{
|
|
return ValidateSpellInfo(SpellIds.SummonLifeSpark);
|
|
}
|
|
|
|
void OnRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
|
|
{
|
|
Player player = GetOwner().ToPlayer();
|
|
if (player)
|
|
{
|
|
Unit xt002 = GetCaster();
|
|
if (xt002)
|
|
if (xt002.HasAura((uint)aurEff.GetAmount())) // Heartbreak aura indicating hard mode
|
|
xt002.CastSpell(player, SpellIds.SummonLifeSpark, true);
|
|
}
|
|
}
|
|
|
|
public override void Register()
|
|
{
|
|
AfterEffectRemove.Add(new EffectApplyHandler(OnRemove, 0, AuraType.PeriodicTriggerSpell, AuraEffectHandleModes.Real));
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class spell_xt002_gravity_bomb_aura : AuraScript
|
|
{
|
|
public override bool Validate(SpellInfo spell)
|
|
{
|
|
return ValidateSpellInfo(SpellIds.SummonVoidZone);
|
|
}
|
|
|
|
void OnRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
|
|
{
|
|
Player player = GetOwner().ToPlayer();
|
|
if (player)
|
|
{
|
|
Unit xt002 = GetCaster();
|
|
if (xt002)
|
|
if (xt002.HasAura((uint)aurEff.GetAmount())) // Heartbreak aura indicating hard mode
|
|
xt002.CastSpell(player, SpellIds.SummonVoidZone, true);
|
|
}
|
|
}
|
|
|
|
void OnPeriodic(AuraEffect aurEff)
|
|
{
|
|
Unit xt002 = GetCaster();
|
|
if (!xt002)
|
|
return;
|
|
|
|
Unit owner = GetOwner().ToUnit();
|
|
if (!owner)
|
|
return;
|
|
|
|
if ((uint)aurEff.GetAmount() >= owner.GetHealth())
|
|
if (xt002.GetAI() != null)
|
|
xt002.GetAI().SetData(XT002Data.GravityBombCasualty, 1);
|
|
}
|
|
|
|
public override void Register()
|
|
{
|
|
OnEffectPeriodic.Add(new EffectPeriodicHandler(OnPeriodic, 2, AuraType.PeriodicDamage));
|
|
AfterEffectRemove.Add(new EffectApplyHandler(OnRemove, 0, AuraType.PeriodicTriggerSpell, AuraEffectHandleModes.Real));
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class spell_xt002_gravity_bomb_damage : SpellScript
|
|
{
|
|
void HandleScript(uint eff)
|
|
{
|
|
Unit caster = GetCaster();
|
|
if (!caster)
|
|
return;
|
|
|
|
if ((uint)GetHitDamage() >= GetHitUnit().GetHealth())
|
|
if (caster.GetAI() != null)
|
|
caster.GetAI().SetData(XT002Data.GravityBombCasualty, 1);
|
|
}
|
|
|
|
public override void Register()
|
|
{
|
|
OnEffectHitTarget.Add(new EffectHandler(HandleScript, 0, SpellEffectName.SchoolDamage));
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class spell_xt002_heart_overload_periodic : SpellScript
|
|
{
|
|
public override bool Validate(SpellInfo spell)
|
|
{
|
|
return ValidateSpellInfo(SpellIds.EnergyOrb, SpellIds.RechargeBoombot, SpellIds.RechargePummeler, SpellIds.RechargeScrapbot);
|
|
}
|
|
|
|
void HandleScript(uint effIndex)
|
|
{
|
|
Unit caster = GetCaster();
|
|
if (caster)
|
|
{
|
|
InstanceScript instance = caster.GetInstanceScript();
|
|
if (instance != null)
|
|
{
|
|
Unit toyPile = Global.ObjAccessor.GetUnit(caster, instance.GetGuidData(InstanceData.ToyPile0 + RandomHelper.URand(0, 3)));
|
|
if (toyPile)
|
|
{
|
|
caster.CastSpell(toyPile, SpellIds.EnergyOrb, true);
|
|
|
|
// This should probably be incorporated in a dummy effect handler, but I've had trouble getting the correct target
|
|
// Weighed randomization (approximation)
|
|
uint[] spells = { SpellIds.RechargeScrapbot, SpellIds.RechargeScrapbot, SpellIds.RechargeScrapbot, SpellIds.RechargePummeler, SpellIds.RechargeBoombot };
|
|
|
|
for (byte i = 0; i < 5; ++i)
|
|
{
|
|
uint spellId = spells[RandomHelper.IRand(0, 4)];
|
|
toyPile.CastSpell(toyPile, spellId, true, null, null, instance.GetGuidData(BossIds.Xt002));
|
|
}
|
|
}
|
|
}
|
|
|
|
Creature creatureBase = caster.GetVehicleCreatureBase();
|
|
if (creatureBase)
|
|
creatureBase.GetAI().Talk(Texts.Summon);
|
|
}
|
|
}
|
|
|
|
public override void Register()
|
|
{
|
|
OnEffectHit.Add(new EffectHandler(HandleScript, 0, SpellEffectName.Dummy));
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class spell_xt002_tympanic_tantrum : SpellScript
|
|
{
|
|
void FilterTargets(List<WorldObject> targets)
|
|
{
|
|
targets.RemoveAll(new PlayerOrPetCheck());
|
|
}
|
|
|
|
void RecalculateDamage()
|
|
{
|
|
SetHitDamage((int)GetHitUnit().CountPctFromMaxHealth(GetHitDamage()));
|
|
}
|
|
|
|
public override void Register()
|
|
{
|
|
OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 0, Targets.UnitSrcAreaEnemy));
|
|
OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 1, Targets.UnitSrcAreaEnemy));
|
|
OnHit.Add(new HitHandler(RecalculateDamage));
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class spell_xt002_submerged : SpellScript
|
|
{
|
|
void HandleScript(uint eff)
|
|
{
|
|
Creature target = GetHitCreature();
|
|
if (!target)
|
|
return;
|
|
|
|
target.SetFlag(UnitFields.Flags, UnitFlags.NotSelectable);
|
|
target.SetStandState(UnitStandStateType.Submerged);
|
|
}
|
|
|
|
public override void Register()
|
|
{
|
|
OnEffectHitTarget.Add(new EffectHandler(HandleScript, 0, SpellEffectName.ScriptEffect));
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class spell_xt002_321_boombot_aura : AuraScript
|
|
{
|
|
public override bool Validate(SpellInfo spellInfo)
|
|
{
|
|
return ValidateSpellInfo(SpellIds.AchievementCreditNerfScrapbots);
|
|
}
|
|
|
|
bool CheckProc(ProcEventInfo eventInfo)
|
|
{
|
|
if (eventInfo.GetActionTarget().GetEntry() != InstanceCreatureIds.XS013Scrapbot)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo)
|
|
{
|
|
InstanceScript instance = eventInfo.GetActor().GetInstanceScript();
|
|
if (instance == null)
|
|
return;
|
|
|
|
instance.DoCastSpellOnPlayers(SpellIds.AchievementCreditNerfScrapbots);
|
|
}
|
|
|
|
public override void Register()
|
|
{
|
|
DoCheckProc.Add(new CheckProcHandler(CheckProc));
|
|
OnEffectProc.Add(new EffectProcHandler(HandleProc, 0, AuraType.Dummy));
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class achievement_nerf_engineering : AchievementCriteriaScript
|
|
{
|
|
public achievement_nerf_engineering() : base("achievement_nerf_engineering") { }
|
|
|
|
public override bool OnCheck(Player source, Unit target)
|
|
{
|
|
if (!target || target.GetAI() == null)
|
|
return false;
|
|
|
|
return target.GetAI().GetData(XT002Data.HealthRecovered) == 0;
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class achievement_heartbreaker : AchievementCriteriaScript
|
|
{
|
|
public achievement_heartbreaker() : base("achievement_heartbreaker") { }
|
|
|
|
public override bool OnCheck(Player source, Unit target)
|
|
{
|
|
if (!target || target.GetAI() == null)
|
|
return false;
|
|
|
|
return target.GetAI().GetData(XT002Data.HardMode) != 0;
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class achievement_nerf_gravity_bombs : AchievementCriteriaScript
|
|
{
|
|
public achievement_nerf_gravity_bombs() : base("achievement_nerf_gravity_bombs") { }
|
|
|
|
public override bool OnCheck(Player source, Unit target)
|
|
{
|
|
if (!target || target.GetAI() == null)
|
|
return false;
|
|
|
|
return target.GetAI().GetData(XT002Data.GravityBombCasualty) == 0;
|
|
}
|
|
}
|
|
}
|