437fe2d8fd
Port From (https://github.com/TrinityCore/TrinityCore/commit/605e5f94c0d71cad8e83fa5a07eaec4e6bed9cc3)
554 lines
18 KiB
C#
554 lines
18 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;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Scripts.EasternKingdoms.Karazhan.ShadeOfAran
|
|
{
|
|
struct SpellIds
|
|
{
|
|
public const uint Frostbolt = 29954;
|
|
public const uint Fireball = 29953;
|
|
public const uint Arcmissle = 29955;
|
|
public const uint Chainsofice = 29991;
|
|
public const uint Dragonsbreath = 29964;
|
|
public const uint Massslow = 30035;
|
|
public const uint FlameWreath = 29946;
|
|
public const uint AoeCs = 29961;
|
|
public const uint Playerpull = 32265;
|
|
public const uint Aexplosion = 29973;
|
|
public const uint MassPoly = 29963;
|
|
public const uint BlinkCenter = 29967;
|
|
public const uint Elementals = 29962;
|
|
public const uint Conjure = 29975;
|
|
public const uint Drink = 30024;
|
|
public const uint Potion = 32453;
|
|
public const uint AoePyroblast = 29978;
|
|
|
|
public const uint CircularBlizzard = 29951;
|
|
public const uint Waterbolt = 31012;
|
|
public const uint ShadowPyro = 29978;
|
|
}
|
|
|
|
struct CreatureIds
|
|
{
|
|
public const uint WaterElemental = 17167;
|
|
public const uint ShadowOfAran = 18254;
|
|
public const uint AranBlizzard = 17161;
|
|
}
|
|
|
|
struct TextIds
|
|
{
|
|
public const uint SayAggro = 0;
|
|
public const uint SayFlamewreath = 1;
|
|
public const uint SayBlizzard = 2;
|
|
public const uint SayExplosion = 3;
|
|
public const uint SayDrink = 4;
|
|
public const uint SayElementals = 5;
|
|
public const uint SayKill = 6;
|
|
public const uint SayTimeover = 7;
|
|
public const uint SayDeath = 8;
|
|
public const uint SayAtiesh = 9;
|
|
}
|
|
|
|
enum SuperSpell
|
|
{
|
|
Flame = 0,
|
|
Blizzard,
|
|
Ae,
|
|
}
|
|
|
|
[Script]
|
|
class boss_aran : ScriptedAI
|
|
{
|
|
static uint[] AtieshStaves =
|
|
{
|
|
22589, //ItemAtieshMage,
|
|
22630, //ItemAtieshWarlock,
|
|
22631, //ItemAtieshPriest,
|
|
22632 //ItemAtieshDruid,
|
|
};
|
|
|
|
InstanceScript instance;
|
|
|
|
uint SecondarySpellTimer;
|
|
uint NormalCastTimer;
|
|
uint SuperCastTimer;
|
|
uint BerserkTimer;
|
|
uint CloseDoorTimer; // Don't close the door right on aggro in case some people are still entering.
|
|
|
|
SuperSpell LastSuperSpell;
|
|
|
|
uint FlameWreathTimer;
|
|
uint FlameWreathCheckTime;
|
|
ObjectGuid[] FlameWreathTarget = new ObjectGuid[3];
|
|
float[] FWTargPosX = new float[3];
|
|
float[] FWTargPosY = new float[3];
|
|
|
|
uint CurrentNormalSpell;
|
|
uint ArcaneCooldown;
|
|
uint FireCooldown;
|
|
uint FrostCooldown;
|
|
|
|
uint DrinkInterruptTimer;
|
|
|
|
bool ElementalsSpawned;
|
|
bool Drinking;
|
|
bool DrinkInturrupted;
|
|
bool SeenAtiesh;
|
|
|
|
public boss_aran(Creature creature) : base(creature)
|
|
{
|
|
Initialize();
|
|
instance = creature.GetInstanceScript();
|
|
}
|
|
|
|
void Initialize()
|
|
{
|
|
SecondarySpellTimer = 5000;
|
|
NormalCastTimer = 0;
|
|
SuperCastTimer = 35000;
|
|
BerserkTimer = 720000;
|
|
CloseDoorTimer = 15000;
|
|
|
|
LastSuperSpell = (SuperSpell)(RandomHelper.Rand32() % 3);
|
|
|
|
FlameWreathTimer = 0;
|
|
FlameWreathCheckTime = 0;
|
|
|
|
CurrentNormalSpell = 0;
|
|
ArcaneCooldown = 0;
|
|
FireCooldown = 0;
|
|
FrostCooldown = 0;
|
|
|
|
DrinkInterruptTimer = 10000;
|
|
|
|
ElementalsSpawned = false;
|
|
Drinking = false;
|
|
DrinkInturrupted = false;
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
Initialize();
|
|
me.SetCanMelee(true);
|
|
|
|
// Not in progress
|
|
instance.SetBossState(DataTypes.Aran, EncounterState.NotStarted);
|
|
instance.HandleGameObject(instance.GetGuidData(DataTypes.GoLibraryDoor), true);
|
|
}
|
|
|
|
public override void KilledUnit(Unit victim)
|
|
{
|
|
Talk(TextIds.SayKill);
|
|
}
|
|
|
|
public override void JustDied(Unit killer)
|
|
{
|
|
Talk(TextIds.SayDeath);
|
|
|
|
instance.SetBossState(DataTypes.Aran, EncounterState.Done);
|
|
instance.HandleGameObject(instance.GetGuidData(DataTypes.GoLibraryDoor), true);
|
|
}
|
|
|
|
public override void JustEngagedWith(Unit who)
|
|
{
|
|
Talk(TextIds.SayAggro);
|
|
|
|
instance.SetBossState(DataTypes.Aran, EncounterState.InProgress);
|
|
instance.HandleGameObject(instance.GetGuidData(DataTypes.GoLibraryDoor), false);
|
|
}
|
|
|
|
void FlameWreathEffect()
|
|
{
|
|
List<Unit> targets = new();
|
|
//store the threat list in a different container
|
|
foreach (var refe in me.GetThreatManager().GetSortedThreatList())
|
|
{
|
|
Unit target = refe.GetVictim();
|
|
if (refe.GetVictim().IsPlayer() && refe.GetVictim().IsAlive())
|
|
targets.Add(target);
|
|
}
|
|
|
|
//cut down to size if we have more than 3 targets
|
|
targets.RandomResize(3);
|
|
|
|
uint i = 0;
|
|
foreach (var unit in targets)
|
|
{
|
|
if (unit != null)
|
|
{
|
|
FlameWreathTarget[i] = unit.GetGUID();
|
|
FWTargPosX[i] = unit.GetPositionX();
|
|
FWTargPosY[i] = unit.GetPositionY();
|
|
DoCast(unit, SpellIds.FlameWreath, new CastSpellExtraArgs(true));
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void UpdateAI(uint diff)
|
|
{
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
if (CloseDoorTimer != 0)
|
|
{
|
|
if (CloseDoorTimer <= diff)
|
|
{
|
|
instance.HandleGameObject(instance.GetGuidData(DataTypes.GoLibraryDoor), false);
|
|
CloseDoorTimer = 0;
|
|
}
|
|
else CloseDoorTimer -= diff;
|
|
}
|
|
|
|
//Cooldowns for casts
|
|
if (ArcaneCooldown != 0)
|
|
{
|
|
if (ArcaneCooldown >= diff)
|
|
ArcaneCooldown -= diff;
|
|
else ArcaneCooldown = 0;
|
|
}
|
|
|
|
if (FireCooldown != 0)
|
|
{
|
|
if (FireCooldown >= diff)
|
|
FireCooldown -= diff;
|
|
else FireCooldown = 0;
|
|
}
|
|
|
|
if (FrostCooldown != 0)
|
|
{
|
|
if (FrostCooldown >= diff)
|
|
FrostCooldown -= diff;
|
|
else FrostCooldown = 0;
|
|
}
|
|
|
|
if (!Drinking && me.GetMaxPower(PowerType.Mana) != 0 && me.GetPowerPct(PowerType.Mana) < 20.0f)
|
|
{
|
|
Drinking = true;
|
|
me.InterruptNonMeleeSpells(false);
|
|
|
|
Talk(TextIds.SayDrink);
|
|
|
|
if (!DrinkInturrupted)
|
|
{
|
|
DoCast(me, SpellIds.MassPoly, new CastSpellExtraArgs(true));
|
|
DoCast(me, SpellIds.Conjure, new CastSpellExtraArgs(false));
|
|
DoCast(me, SpellIds.Drink, new CastSpellExtraArgs(false));
|
|
me.SetStandState(UnitStandStateType.Sit);
|
|
DrinkInterruptTimer = 10000;
|
|
}
|
|
}
|
|
|
|
//Drink Interrupt
|
|
if (Drinking && DrinkInturrupted)
|
|
{
|
|
Drinking = false;
|
|
me.RemoveAurasDueToSpell(SpellIds.Drink);
|
|
me.SetStandState(UnitStandStateType.Stand);
|
|
me.SetPower(PowerType.Mana, me.GetMaxPower(PowerType.Mana) - 32000);
|
|
DoCast(me, SpellIds.Potion, new CastSpellExtraArgs(false));
|
|
}
|
|
|
|
//Drink Interrupt Timer
|
|
if (Drinking && !DrinkInturrupted)
|
|
{
|
|
if (DrinkInterruptTimer >= diff)
|
|
DrinkInterruptTimer -= diff;
|
|
else
|
|
{
|
|
me.SetStandState(UnitStandStateType.Stand);
|
|
DoCast(me, SpellIds.Potion, new CastSpellExtraArgs(true));
|
|
DoCast(me, SpellIds.AoePyroblast, new CastSpellExtraArgs(false));
|
|
DrinkInturrupted = true;
|
|
Drinking = false;
|
|
}
|
|
}
|
|
|
|
//Don't execute any more code if we are drinking
|
|
if (Drinking)
|
|
return;
|
|
|
|
//Normal casts
|
|
if (NormalCastTimer <= diff)
|
|
{
|
|
if (!me.IsNonMeleeSpellCast(false))
|
|
{
|
|
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 100, true);
|
|
if (target == null)
|
|
return;
|
|
|
|
uint[] Spells = new uint[3];
|
|
byte AvailableSpells = 0;
|
|
|
|
//Check for what spells are not on cooldown
|
|
if (ArcaneCooldown == 0)
|
|
{
|
|
Spells[AvailableSpells] = SpellIds.Arcmissle;
|
|
++AvailableSpells;
|
|
}
|
|
if (FireCooldown == 0)
|
|
{
|
|
Spells[AvailableSpells] = SpellIds.Fireball;
|
|
++AvailableSpells;
|
|
}
|
|
if (FrostCooldown == 0)
|
|
{
|
|
Spells[AvailableSpells] = SpellIds.Frostbolt;
|
|
++AvailableSpells;
|
|
}
|
|
|
|
//If no available spells wait 1 second and try again
|
|
if (AvailableSpells != 0)
|
|
{
|
|
CurrentNormalSpell = Spells[RandomHelper.Rand32() % AvailableSpells];
|
|
DoCast(target, CurrentNormalSpell);
|
|
}
|
|
}
|
|
NormalCastTimer = 1000;
|
|
}
|
|
else NormalCastTimer -= diff;
|
|
|
|
if (SecondarySpellTimer <= diff)
|
|
{
|
|
switch (RandomHelper.URand(0, 1))
|
|
{
|
|
case 0:
|
|
DoCast(me, SpellIds.AoeCs);
|
|
break;
|
|
case 1:
|
|
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 100, true);
|
|
if (target != null)
|
|
DoCast(target, SpellIds.Chainsofice);
|
|
break;
|
|
}
|
|
SecondarySpellTimer = RandomHelper.URand(5000, 20000);
|
|
}
|
|
else SecondarySpellTimer -= diff;
|
|
|
|
if (SuperCastTimer <= diff)
|
|
{
|
|
SuperSpell[] Available = new SuperSpell[2];
|
|
|
|
switch (LastSuperSpell)
|
|
{
|
|
case SuperSpell.Ae:
|
|
Available[0] = SuperSpell.Flame;
|
|
Available[1] = SuperSpell.Blizzard;
|
|
break;
|
|
case SuperSpell.Flame:
|
|
Available[0] = SuperSpell.Ae;
|
|
Available[1] = SuperSpell.Blizzard;
|
|
break;
|
|
case SuperSpell.Blizzard:
|
|
Available[0] = SuperSpell.Flame;
|
|
Available[1] = SuperSpell.Ae;
|
|
break;
|
|
default:
|
|
Available[0] = 0;
|
|
Available[1] = 0;
|
|
break;
|
|
}
|
|
|
|
LastSuperSpell = Available[RandomHelper.URand(0, 1)];
|
|
|
|
switch (LastSuperSpell)
|
|
{
|
|
case SuperSpell.Ae:
|
|
Talk(TextIds.SayExplosion);
|
|
|
|
DoCast(me, SpellIds.BlinkCenter, new CastSpellExtraArgs(true));
|
|
DoCast(me, SpellIds.Playerpull, new CastSpellExtraArgs(true));
|
|
DoCast(me, SpellIds.Massslow, new CastSpellExtraArgs(true));
|
|
DoCast(me, SpellIds.Aexplosion, new CastSpellExtraArgs(false));
|
|
break;
|
|
|
|
case SuperSpell.Flame:
|
|
Talk(TextIds.SayFlamewreath);
|
|
|
|
FlameWreathTimer = 20000;
|
|
FlameWreathCheckTime = 500;
|
|
|
|
FlameWreathTarget[0].Clear();
|
|
FlameWreathTarget[1].Clear();
|
|
FlameWreathTarget[2].Clear();
|
|
|
|
FlameWreathEffect();
|
|
break;
|
|
|
|
case SuperSpell.Blizzard:
|
|
Talk(TextIds.SayBlizzard);
|
|
|
|
Creature pSpawn = me.SummonCreature(CreatureIds.AranBlizzard, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawn, TimeSpan.FromSeconds(25));
|
|
if (pSpawn != null)
|
|
{
|
|
pSpawn.SetFaction(me.GetFaction());
|
|
pSpawn.CastSpell(pSpawn, SpellIds.CircularBlizzard, false);
|
|
}
|
|
break;
|
|
}
|
|
|
|
SuperCastTimer = RandomHelper.URand(35000, 40000);
|
|
}
|
|
else SuperCastTimer -= diff;
|
|
|
|
if (!ElementalsSpawned && HealthBelowPct(40))
|
|
{
|
|
ElementalsSpawned = true;
|
|
|
|
for (uint i = 0; i < 4; ++i)
|
|
{
|
|
Creature unit = me.SummonCreature(CreatureIds.WaterElemental, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawn, TimeSpan.FromSeconds(90));
|
|
if (unit != null)
|
|
{
|
|
unit.Attack(me.GetVictim(), true);
|
|
unit.SetFaction(me.GetFaction());
|
|
}
|
|
}
|
|
|
|
Talk(TextIds.SayElementals);
|
|
}
|
|
|
|
if (BerserkTimer <= diff)
|
|
{
|
|
for (uint i = 0; i < 5; ++i)
|
|
{
|
|
Creature unit = me.SummonCreature(CreatureIds.ShadowOfAran, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(5));
|
|
if (unit != null)
|
|
{
|
|
unit.Attack(me.GetVictim(), true);
|
|
unit.SetFaction(me.GetFaction());
|
|
}
|
|
}
|
|
|
|
Talk(TextIds.SayTimeover);
|
|
|
|
BerserkTimer = 60000;
|
|
}
|
|
else BerserkTimer -= diff;
|
|
|
|
//Flame Wreath check
|
|
if (FlameWreathTimer != 0)
|
|
{
|
|
if (FlameWreathTimer >= diff)
|
|
FlameWreathTimer -= diff;
|
|
else FlameWreathTimer = 0;
|
|
|
|
if (FlameWreathCheckTime <= diff)
|
|
{
|
|
for (byte i = 0; i < 3; ++i)
|
|
{
|
|
if (FlameWreathTarget[i].IsEmpty())
|
|
continue;
|
|
|
|
Unit unit = Global.ObjAccessor.GetUnit(me, FlameWreathTarget[i]);
|
|
if (unit != null && !unit.IsWithinDist2d(FWTargPosX[i], FWTargPosY[i], 3))
|
|
{
|
|
unit.CastSpell(unit, 20476, new CastSpellExtraArgs(TriggerCastFlags.FullMask)
|
|
.SetOriginalCaster(me.GetGUID()));
|
|
unit.CastSpell(unit, 11027, true);
|
|
FlameWreathTarget[i].Clear();
|
|
}
|
|
}
|
|
FlameWreathCheckTime = 500;
|
|
}
|
|
else FlameWreathCheckTime -= diff;
|
|
}
|
|
|
|
me.SetCanMelee(ArcaneCooldown != 0 && FireCooldown != 0 && FrostCooldown != 0);
|
|
}
|
|
|
|
public override void DamageTaken(Unit pAttacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null)
|
|
{
|
|
if (!DrinkInturrupted && Drinking && damage != 0)
|
|
DrinkInturrupted = true;
|
|
}
|
|
|
|
public override void SpellHit(WorldObject caster, SpellInfo spellInfo)
|
|
{
|
|
//We only care about interrupt effects and only if they are durring a spell currently being cast
|
|
if (!spellInfo.HasEffect(SpellEffectName.InterruptCast) || !me.IsNonMeleeSpellCast(false))
|
|
return;
|
|
|
|
//Interrupt effect
|
|
me.InterruptNonMeleeSpells(false);
|
|
|
|
//Normally we would set the cooldown equal to the spell duration
|
|
//but we do not have access to the DurationStore
|
|
|
|
switch (CurrentNormalSpell)
|
|
{
|
|
case SpellIds.Arcmissle: ArcaneCooldown = 5000; break;
|
|
case SpellIds.Fireball: FireCooldown = 5000; break;
|
|
case SpellIds.Frostbolt: FrostCooldown = 5000; break;
|
|
}
|
|
}
|
|
|
|
public override void MoveInLineOfSight(Unit who)
|
|
{
|
|
base.MoveInLineOfSight(who);
|
|
|
|
if (SeenAtiesh || me.IsInCombat() || me.GetDistance2d(who) > me.GetAttackDistance(who) + 10.0f)
|
|
return;
|
|
|
|
Player player = who.ToPlayer();
|
|
if (player == null)
|
|
return;
|
|
|
|
foreach (uint id in AtieshStaves)
|
|
{
|
|
if (!PlayerHasWeaponEquipped(player, id))
|
|
continue;
|
|
|
|
SeenAtiesh = true;
|
|
Talk(TextIds.SayAtiesh);
|
|
me.SetFacingTo(me.GetAbsoluteAngle(player));
|
|
me.ClearUnitState(UnitState.Moving);
|
|
me.GetMotionMaster().MoveDistract(7 * Time.InMilliseconds, me.GetAbsoluteAngle(who));
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool PlayerHasWeaponEquipped(Player player, uint itemEntry)
|
|
{
|
|
Item item = player.GetItemByPos(InventorySlots.Bag0, EquipmentSlot.MainHand);
|
|
if (item != null && item.GetEntry() == itemEntry)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
[Script]
|
|
class water_elemental : ScriptedAI
|
|
{
|
|
public water_elemental(Creature creature) : base(creature) { }
|
|
|
|
public override void Reset()
|
|
{
|
|
_scheduler.Schedule(TimeSpan.FromMilliseconds(2000 + (RandomHelper.Rand32() % 3000)), task =>
|
|
{
|
|
DoCastVictim(SpellIds.Waterbolt);
|
|
task.Repeat(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5));
|
|
});
|
|
}
|
|
|
|
public override void JustEngagedWith(Unit who) { }
|
|
|
|
public override void UpdateAI(uint diff)
|
|
{
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
_scheduler.Update(diff);
|
|
}
|
|
}
|
|
} |