Start adding missing scripts Part 6

This commit is contained in:
hondacrx
2022-08-15 11:37:13 -04:00
parent 1a93fc725c
commit 24befcee13
13 changed files with 5347 additions and 4 deletions
@@ -355,9 +355,9 @@ namespace Scripts.EasternKingdoms.BlackrockMountain.BlackrockDepths.CorenDirebre
}
}
class go_direbrew_mole_machineAI : GameObjectAI
class go_direbrew_mole_machine : GameObjectAI
{
public go_direbrew_mole_machineAI(GameObject go) : base(go) { }
public go_direbrew_mole_machine(GameObject go) : base(go) { }
public override void Reset()
{
@@ -100,12 +100,12 @@ namespace Scripts.EasternKingdoms.BlackrockMountain.BlackrockDepths.Magmus
}
[Script]
class npc_ironhand_guardianAI : ScriptedAI
class npc_ironhand_guardian : ScriptedAI
{
InstanceScript _instance;
bool _active;
public npc_ironhand_guardianAI(Creature creature) : base(creature)
public npc_ironhand_guardian(Creature creature) : base(creature)
{
_instance = me.GetInstanceScript();
_active = false;
@@ -0,0 +1,158 @@
/*
* Copyright (C) 2012-2020 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 Game.AI;
using Game.Entities;
using Game.Scripting;
using Game.Spells;
using System;
namespace Scripts.EasternKingdoms.Karazhan.Curator
{
struct SpellIds
{
public const uint HatefulBolt = 30383;
public const uint Evocation = 30254;
public const uint ArcaneInfusion = 30403;
public const uint Berserk = 26662;
public const uint SummonAstralFlareNe = 30236;
public const uint SummonAstralFlareNw = 30239;
public const uint SummonAstralFlareSe = 30240;
public const uint SummonAstralFlareSw = 30241;
}
struct TextIds
{
public const uint SayAggro = 0;
public const uint SaySummon = 1;
public const uint SayEvocate = 2;
public const uint SayEnrage = 3;
public const uint SayKill = 4;
public const uint SayDeath = 5;
}
struct MiscConst
{
public const uint GroupAstralFlare = 1;
}
[Script]
class boss_curator : BossAI
{
public boss_curator(Creature creature) : base(creature, DataTypes.Curator) { }
public override void Reset()
{
_Reset();
_infused = false;
}
public override void KilledUnit(Unit victim)
{
if (victim.IsPlayer())
Talk(TextIds.SayKill);
}
public override void JustDied(Unit killer)
{
_JustDied();
Talk(TextIds.SayDeath);
}
public override void JustEngagedWith(Unit who)
{
base.JustEngagedWith(who);
Talk(TextIds.SayAggro);
_scheduler.Schedule(TimeSpan.FromSeconds(12), task =>
{
Unit target = SelectTarget(SelectTargetMethod.MaxThreat, 1);
if (target)
DoCast(target, SpellIds.HatefulBolt);
task.Repeat(TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(15));
});
_scheduler.Schedule(TimeSpan.FromSeconds(10), MiscConst.GroupAstralFlare, task =>
{
if (RandomHelper.randChance(50))
Talk(TextIds.SaySummon);
DoCastSelf(RandomHelper.RAND(SpellIds.SummonAstralFlareNe, SpellIds.SummonAstralFlareNw, SpellIds.SummonAstralFlareSe, SpellIds.SummonAstralFlareSw), new CastSpellExtraArgs(true));
int mana = (me.GetMaxPower(PowerType.Mana) / 10);
if (mana != 0)
{
me.ModifyPower(PowerType.Mana, -mana);
if (me.GetPower(PowerType.Mana) * 100 / me.GetMaxPower(PowerType.Mana) < 10)
{
Talk(TextIds.SayEvocate);
me.InterruptNonMeleeSpells(false);
DoCastSelf(SpellIds.Evocation);
}
}
task.Repeat(TimeSpan.FromSeconds(10));
});
_scheduler.Schedule(TimeSpan.FromMinutes(12), ScheduleTasks =>
{
Talk(TextIds.SayEnrage);
DoCastSelf(SpellIds.Berserk, new CastSpellExtraArgs(true));
});
}
public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null)
{
if (!HealthAbovePct(15) && !_infused)
{
_infused = true;
_scheduler.Schedule(TimeSpan.FromMilliseconds(1), task => DoCastSelf(SpellIds.ArcaneInfusion, new CastSpellExtraArgs(true)));
_scheduler.CancelGroup(MiscConst.GroupAstralFlare);
}
}
public override void UpdateAI(uint diff)
{
_scheduler.Update(diff, () => DoMeleeAttackIfReady());
}
bool _infused;
}
[Script]
class npc_curator_astral_flare : ScriptedAI
{
public npc_curator_astral_flare(Creature creature) : base(creature)
{
me.SetReactState(ReactStates.Passive);
}
public override void Reset()
{
_scheduler.Schedule(TimeSpan.FromSeconds(2), task =>
{
me.SetReactState(ReactStates.Aggressive);
me.RemoveUnitFlag(UnitFlags.Uninteractible);
DoZoneInCombat();
});
}
public override void UpdateAI(uint diff)
{
_scheduler.Update(diff);
}
}
}
@@ -0,0 +1,391 @@
/*
* Copyright (C) 2012-2020 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 Game.Entities;
using Game.Maps;
using Game.Scripting;
using System;
namespace Scripts.EasternKingdoms.Karazhan
{
struct DataTypes
{
public const uint Attumen = 0;
public const uint Moroes = 1;
public const uint MaidenOfVirtue = 2;
public const uint OptionalBoss = 3;
public const uint OperaPerformance = 4;
public const uint Curator = 5;
public const uint Aran = 6;
public const uint Terestian = 7;
public const uint Netherspite = 8;
public const uint Chess = 9;
public const uint Malchezzar = 10;
public const uint Nightbane = 11;
public const uint OperaOzDeathcount = 14;
public const uint Kilrek = 15;
public const uint GoCurtains = 18;
public const uint GoStagedoorleft = 19;
public const uint GoStagedoorright = 20;
public const uint GoLibraryDoor = 21;
public const uint GoMassiveDoor = 22;
public const uint GoNetherDoor = 23;
public const uint GoGameDoor = 24;
public const uint GoGameExitDoor = 25;
public const uint ImageOfMedivh = 26;
public const uint MastersTerraceDoor1 = 27;
public const uint MastersTerraceDoor2 = 28;
public const uint GoSideEntranceDoor = 29;
public const uint GoBlackenedUrn = 30;
}
struct CreatureIds
{
public const uint HyakissTheLurker = 16179;
public const uint RokadTheRavager = 16181;
public const uint ShadikithTheGlider = 16180;
public const uint TerestianIllhoof = 15688;
public const uint Moroes = 15687;
public const uint Nightbane = 17225;
public const uint AttumenUnmounted = 15550;
public const uint AttumenMounted = 16152;
public const uint Midnight = 16151;
// Trash
public const uint ColdmistWidow = 16171;
public const uint ColdmistStalker = 16170;
public const uint Shadowbat = 16173;
public const uint VampiricShadowbat = 16175;
public const uint GreaterShadowbat = 16174;
public const uint PhaseHound = 16178;
public const uint Dreadbeast = 16177;
public const uint Shadowbeast = 16176;
public const uint Kilrek = 17229;
}
struct GameObjectIds
{
public const uint StageCurtain = 183932;
public const uint StageDoorLeft = 184278;
public const uint StageDoorRight = 184279;
public const uint PrivateLibraryDoor = 184517;
public const uint MassiveDoor = 185521;
public const uint GamesmanHallDoor = 184276;
public const uint GamesmanHallExitDoor = 184277;
public const uint NetherspaceDoor = 185134;
public const uint MastersTerraceDoor = 184274;
public const uint MastersTerraceDoor2 = 184280;
public const uint SideEntranceDoor = 184275;
public const uint DustCoveredChest = 185119;
public const uint BlackenedUrn = 194092;
}
enum KZMisc
{
OptionalBossRequiredDeathCount = 50
}
[Script]
class instance_karazhan : InstanceMapScript
{
public static Position[] OptionalSpawn =
{
new Position(-10960.981445f, -1940.138428f, 46.178097f, 4.12f), // Hyakiss the Lurker
new Position(-10945.769531f, -2040.153320f, 49.474438f, 0.077f), // Shadikith the Glider
new Position(-10899.903320f, -2085.573730f, 49.474449f, 1.38f) // Rokad the Ravager
};
public instance_karazhan() : base(nameof(instance_karazhan), 532) { }
class instance_karazhan_InstanceMapScript : InstanceScript
{
uint OperaEvent;
uint OzDeathCount;
uint OptionalBossCount;
ObjectGuid CurtainGUID;
ObjectGuid StageDoorLeftGUID;
ObjectGuid StageDoorRightGUID;
ObjectGuid KilrekGUID;
ObjectGuid TerestianGUID;
ObjectGuid MoroesGUID;
ObjectGuid NightbaneGUID;
ObjectGuid LibraryDoor; // Door at Shade of Aran
ObjectGuid MassiveDoor; // Door at Netherspite
ObjectGuid SideEntranceDoor; // Side Entrance
ObjectGuid GamesmansDoor; // Door before Chess
ObjectGuid GamesmansExitDoor; // Door after Chess
ObjectGuid NetherspaceDoor; // Door at Malchezaar
ObjectGuid[] MastersTerraceDoor = new ObjectGuid[2];
ObjectGuid ImageGUID;
ObjectGuid DustCoveredChest;
ObjectGuid BlackenedUrnGUID;
public instance_karazhan_InstanceMapScript(InstanceMap map) : base(map)
{
SetHeaders("KZ");
SetBossNumber(12);
// 1 - Oz, 2 - Hood, 3 - Raj, this never gets altered.
OperaEvent = RandomHelper.URand(1, 3);
OzDeathCount = 0;
OptionalBossCount = 0;
}
public override void OnCreatureCreate(Creature creature)
{
switch (creature.GetEntry())
{
case CreatureIds.Kilrek:
KilrekGUID = creature.GetGUID();
break;
case CreatureIds.TerestianIllhoof:
TerestianGUID = creature.GetGUID();
break;
case CreatureIds.Moroes:
MoroesGUID = creature.GetGUID();
break;
case CreatureIds.Nightbane:
NightbaneGUID = creature.GetGUID();
break;
default:
break;
}
}
public override void OnUnitDeath(Unit unit)
{
Creature creature = unit.ToCreature();
if (!creature)
return;
switch (creature.GetEntry())
{
case CreatureIds.ColdmistWidow:
case CreatureIds.ColdmistStalker:
case CreatureIds.Shadowbat:
case CreatureIds.VampiricShadowbat:
case CreatureIds.GreaterShadowbat:
case CreatureIds.PhaseHound:
case CreatureIds.Dreadbeast:
case CreatureIds.Shadowbeast:
if (GetBossState(DataTypes.OptionalBoss) == EncounterState.ToBeDecided)
{
++OptionalBossCount;
if (OptionalBossCount == (uint)KZMisc.OptionalBossRequiredDeathCount)
{
switch (RandomHelper.URand(CreatureIds.HyakissTheLurker, CreatureIds.RokadTheRavager))
{
case CreatureIds.HyakissTheLurker:
instance.SummonCreature(CreatureIds.HyakissTheLurker, OptionalSpawn[0]);
break;
case CreatureIds.ShadikithTheGlider:
instance.SummonCreature(CreatureIds.ShadikithTheGlider, OptionalSpawn[1]);
break;
case CreatureIds.RokadTheRavager:
instance.SummonCreature(CreatureIds.RokadTheRavager, OptionalSpawn[2]);
break;
}
}
}
break;
default:
break;
}
}
public override void SetData(uint type, uint data)
{
switch (type)
{
case DataTypes.OperaOzDeathcount:
if (data == (uint)EncounterState.Special)
++OzDeathCount;
else if (data == (uint)EncounterState.InProgress)
OzDeathCount = 0;
break;
}
}
public override bool SetBossState(uint type, EncounterState state)
{
if (!base.SetBossState(type, state))
return false;
switch (type)
{
case DataTypes.OperaPerformance:
if (state == EncounterState.Done)
{
HandleGameObject(StageDoorLeftGUID, true);
HandleGameObject(StageDoorRightGUID, true);
GameObject sideEntrance = instance.GetGameObject(SideEntranceDoor);
if (sideEntrance)
sideEntrance.RemoveFlag(GameObjectFlags.Locked);
UpdateEncounterStateForKilledCreature(16812, null);
}
break;
case DataTypes.Chess:
if (state == EncounterState.Done)
DoRespawnGameObject(DustCoveredChest, TimeSpan.FromHours(24));
break;
default:
break;
}
return true;
}
public override void SetGuidData(uint type, ObjectGuid data)
{
if (type == DataTypes.ImageOfMedivh)
ImageGUID = data;
}
public override void OnGameObjectCreate(GameObject go)
{
switch (go.GetEntry())
{
case GameObjectIds.StageCurtain:
CurtainGUID = go.GetGUID();
break;
case GameObjectIds.StageDoorLeft:
StageDoorLeftGUID = go.GetGUID();
if (GetBossState(DataTypes.OperaPerformance) == EncounterState.Done)
go.SetGoState(GameObjectState.Active);
break;
case GameObjectIds.StageDoorRight:
StageDoorRightGUID = go.GetGUID();
if (GetBossState(DataTypes.OperaPerformance) == EncounterState.Done)
go.SetGoState(GameObjectState.Active);
break;
case GameObjectIds.PrivateLibraryDoor:
LibraryDoor = go.GetGUID();
break;
case GameObjectIds.MassiveDoor:
MassiveDoor = go.GetGUID();
break;
case GameObjectIds.GamesmanHallDoor:
GamesmansDoor = go.GetGUID();
break;
case GameObjectIds.GamesmanHallExitDoor:
GamesmansExitDoor = go.GetGUID();
break;
case GameObjectIds.NetherspaceDoor:
NetherspaceDoor = go.GetGUID();
break;
case GameObjectIds.MastersTerraceDoor:
MastersTerraceDoor[0] = go.GetGUID();
break;
case GameObjectIds.MastersTerraceDoor2:
MastersTerraceDoor[1] = go.GetGUID();
break;
case GameObjectIds.SideEntranceDoor:
SideEntranceDoor = go.GetGUID();
if (GetBossState(DataTypes.OperaPerformance) == EncounterState.Done)
go.SetFlag(GameObjectFlags.Locked);
else
go.RemoveFlag(GameObjectFlags.Locked);
break;
case GameObjectIds.DustCoveredChest:
DustCoveredChest = go.GetGUID();
break;
case GameObjectIds.BlackenedUrn:
BlackenedUrnGUID = go.GetGUID();
break;
}
switch (OperaEvent)
{
/// @todo Set Object visibilities for Opera based on performance
case 1:
break;
case 2:
break;
case 3:
break;
}
}
public override uint GetData(uint type)
{
switch (type)
{
case DataTypes.OperaPerformance:
return OperaEvent;
case DataTypes.OperaOzDeathcount:
return OzDeathCount;
}
return 0;
}
public override ObjectGuid GetGuidData(uint type)
{
switch (type)
{
case DataTypes.Kilrek:
return KilrekGUID;
case DataTypes.Terestian:
return TerestianGUID;
case DataTypes.Moroes:
return MoroesGUID;
case DataTypes.Nightbane:
return NightbaneGUID;
case DataTypes.GoStagedoorleft:
return StageDoorLeftGUID;
case DataTypes.GoStagedoorright:
return StageDoorRightGUID;
case DataTypes.GoCurtains:
return CurtainGUID;
case DataTypes.GoLibraryDoor:
return LibraryDoor;
case DataTypes.GoMassiveDoor:
return MassiveDoor;
case DataTypes.GoSideEntranceDoor:
return SideEntranceDoor;
case DataTypes.GoGameDoor:
return GamesmansDoor;
case DataTypes.GoGameExitDoor:
return GamesmansExitDoor;
case DataTypes.GoNetherDoor:
return NetherspaceDoor;
case DataTypes.MastersTerraceDoor1:
return MastersTerraceDoor[0];
case DataTypes.MastersTerraceDoor2:
return MastersTerraceDoor[1];
case DataTypes.ImageOfMedivh:
return ImageGUID;
case DataTypes.GoBlackenedUrn:
return BlackenedUrnGUID;
}
return ObjectGuid.Empty;
}
}
public override InstanceScript GetInstanceScript(InstanceMap map)
{
return new instance_karazhan_InstanceMapScript(map);
}
}
}
@@ -0,0 +1,100 @@
/*
* Copyright (C) 2012-2020 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 Game.AI;
using Game.Entities;
using Game.Scripting;
using Game.Spells;
using System;
namespace Scripts.EasternKingdoms.Karazhan.MaidenOfVirtue
{
struct SpellIds
{
public const uint Repentance = 29511;
public const uint Holyfire = 29522;
public const uint Holywrath = 32445;
public const uint Holyground = 29523;
public const uint Berserk = 26662;
}
struct TextIds
{
public const uint SayAggro = 0;
public const uint SaySlay = 1;
public const uint SayRepentance = 2;
public const uint SayDeath = 3;
}
[Script]
class boss_maiden_of_virtue : BossAI
{
public boss_maiden_of_virtue(Creature creature) : base(creature, DataTypes.MaidenOfVirtue) { }
public override void KilledUnit(Unit Victim)
{
if (RandomHelper.randChance(50))
Talk(TextIds.SaySlay);
}
public override void JustDied(Unit killer)
{
Talk(TextIds.SayDeath);
_JustDied();
}
public override void JustEngagedWith(Unit who)
{
base.JustEngagedWith(who);
Talk(TextIds.SayAggro);
DoCastSelf(SpellIds.Holyground, new CastSpellExtraArgs(true));
_scheduler.Schedule(TimeSpan.FromSeconds(33), TimeSpan.FromSeconds(45), task =>
{
DoCastVictim(SpellIds.Repentance);
Talk(TextIds.SayRepentance);
task.Repeat(TimeSpan.FromSeconds(35));
});
_scheduler.Schedule(TimeSpan.FromSeconds(8), task =>
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 50, true);
if (target)
DoCast(target, SpellIds.Holyfire);
task.Repeat(TimeSpan.FromSeconds(8), TimeSpan.FromSeconds(19));
});
_scheduler.Schedule(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(25), task =>
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 80, true);
if (target)
DoCast(target, SpellIds.Holywrath);
task.Repeat(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(25));
});
_scheduler.Schedule(TimeSpan.FromMinutes(10), task =>
{
DoCastSelf(SpellIds.Berserk, new CastSpellExtraArgs(true));
});
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
_scheduler.Update(diff, () => DoMeleeAttackIfReady());
}
}
}
@@ -0,0 +1,366 @@
/*
* Copyright (C) 2012-2020 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 Game.AI;
using Game.Entities;
using Game.Scripting;
using Game.Spells;
using System;
using System.Collections.Generic;
namespace Scripts.EasternKingdoms.Karazhan.Midnight
{
struct SpellIds
{
// Attumen
public const uint Shadowcleave = 29832;
public const uint IntangiblePresence = 29833;
public const uint SpawnSmoke = 10389;
public const uint Charge = 29847;
// Midnight
public const uint Knockdown = 29711;
public const uint SummonAttumen = 29714;
public const uint Mount = 29770;
public const uint SummonAttumenMounted = 29799;
}
struct TextIds
{
public const uint SayKill = 0;
public const uint SayRandom = 1;
public const uint SayDisarmed = 2;
public const uint SayMidnightKill = 3;
public const uint SayAppear = 4;
public const uint SayMount = 5;
public const uint SayDeath = 3;
// Midnight
public const uint EmoteCallAttumen = 0;
public const uint EmoteMountUp = 1;
}
enum Phases
{
None,
AttumenEngages,
Mounted
}
[Script]
class boss_attumen : BossAI
{
ObjectGuid _midnightGUID;
Phases _phase;
public boss_attumen(Creature creature) : base(creature, DataTypes.Attumen)
{
Initialize();
}
void Initialize()
{
_midnightGUID.Clear();
_phase = Phases.None;
}
public override void Reset()
{
Initialize();
base.Reset();
}
public override void EnterEvadeMode(EvadeReason why)
{
Creature midnight = ObjectAccessor.GetCreature(me, _midnightGUID);
if (midnight)
base._DespawnAtEvade(TimeSpan.FromSeconds(10), midnight);
me.DespawnOrUnsummon();
}
public override void ScheduleTasks()
{
_scheduler.Schedule(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(25), task =>
{
DoCastVictim(SpellIds.Shadowcleave);
task.Repeat(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(25));
});
_scheduler.Schedule(TimeSpan.FromSeconds(25), TimeSpan.FromSeconds(45), task =>
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0);
if (target)
DoCast(target, SpellIds.IntangiblePresence);
task.Repeat(TimeSpan.FromSeconds(25), TimeSpan.FromSeconds(45));
});
_scheduler.Schedule(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(60), task =>
{
Talk(TextIds.SayRandom);
task.Repeat(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(60));
});
}
public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null)
{
// Attumen does not die until he mounts Midnight, let health fall to 1 and prevent further damage.
if (damage >= me.GetHealth() && _phase != Phases.Mounted)
damage = (uint)(me.GetHealth() - 1);
if (_phase == Phases.AttumenEngages && me.HealthBelowPctDamaged(25, damage))
{
_phase = Phases.None;
Creature midnight = ObjectAccessor.GetCreature(me, _midnightGUID);
if (midnight)
midnight.GetAI().DoCastAOE(SpellIds.Mount, new CastSpellExtraArgs(true));
}
}
public override void KilledUnit(Unit victim)
{
Talk(TextIds.SayKill);
}
public override void JustSummoned(Creature summon)
{
if (summon.GetEntry() == CreatureIds.AttumenMounted)
{
Creature midnight = ObjectAccessor.GetCreature(me, _midnightGUID);
if (midnight)
{
if (midnight.GetHealth() > me.GetHealth())
summon.SetHealth(midnight.GetHealth());
else
summon.SetHealth(me.GetHealth());
summon.GetAI().DoZoneInCombat();
summon.GetAI().SetGUID(_midnightGUID, (int)CreatureIds.Midnight);
}
}
base.JustSummoned(summon);
}
public override void IsSummonedBy(WorldObject summoner)
{
if (summoner.GetEntry() == CreatureIds.Midnight)
_phase = Phases.AttumenEngages;
if (summoner.GetEntry() == CreatureIds.AttumenUnmounted)
{
_phase = Phases.Mounted;
DoCastSelf(SpellIds.SpawnSmoke);
_scheduler.Schedule(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(25), task =>
{
Unit target = null;
List<Unit> targetList = new();
foreach (var refe in me.GetThreatManager().GetSortedThreatList())
{
target = refe.GetVictim();
if (target && !target.IsWithinDist(me, 8.00f, false) && target.IsWithinDist(me, 25.0f, false))
targetList.Add(target);
target = null;
}
if (!targetList.Empty())
target = targetList.SelectRandom();
DoCast(target, SpellIds.Charge);
task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(25));
});
_scheduler.Schedule(TimeSpan.FromSeconds(25), TimeSpan.FromSeconds(35), task =>
{
DoCastVictim(SpellIds.Knockdown);
task.Repeat(TimeSpan.FromSeconds(25), TimeSpan.FromSeconds(35));
});
}
}
public override void JustDied(Unit killer)
{
Talk(TextIds.SayDeath);
Unit midnight = Global.ObjAccessor.GetUnit(me, _midnightGUID);
if (midnight)
midnight.KillSelf();
_JustDied();
}
public override void SetGUID(ObjectGuid guid, int id)
{
if (id == CreatureIds.Midnight)
_midnightGUID = guid;
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim() && _phase != Phases.None)
return;
_scheduler.Update(diff, () => DoMeleeAttackIfReady());
}
public override void SpellHit(WorldObject caster, SpellInfo spellInfo)
{
if (spellInfo.Mechanic == Mechanics.Disarm)
Talk(TextIds.SayDisarmed);
if (spellInfo.Id == SpellIds.Mount)
{
Creature midnight = ObjectAccessor.GetCreature(me, _midnightGUID);
if (midnight)
{
_phase = Phases.None;
_scheduler.CancelAll();
midnight.AttackStop();
midnight.RemoveAllAttackers();
midnight.SetReactState(ReactStates.Passive);
midnight.GetMotionMaster().MoveFollow(me, 2.0f, 0.0f);
midnight.GetAI().Talk(TextIds.EmoteMountUp);
me.AttackStop();
me.RemoveAllAttackers();
me.SetReactState(ReactStates.Passive);
me.GetMotionMaster().MoveFollow(midnight, 2.0f, 0.0f);
Talk(TextIds.SayMount);
_scheduler.Schedule(TimeSpan.FromSeconds(1), task =>
{
Creature midnight = ObjectAccessor.GetCreature(me, _midnightGUID);
if (midnight)
{
if (me.IsWithinDist2d(midnight, 5.0f))
{
DoCastAOE(SpellIds.SummonAttumenMounted);
me.SetVisible(false);
me.GetMotionMaster().Clear();
midnight.SetVisible(false);
}
else
{
midnight.GetMotionMaster().MoveFollow(me, 2.0f, 0.0f);
me.GetMotionMaster().MoveFollow(midnight, 2.0f, 0.0f);
task.Repeat();
}
}
});
}
}
}
}
[Script]
class boss_midnight : BossAI
{
ObjectGuid _attumenGUID;
Phases _phase;
public boss_midnight(Creature creature) : base(creature, DataTypes.Attumen)
{
Initialize();
}
void Initialize()
{
_phase = Phases.None;
}
public override void Reset()
{
Initialize();
base.Reset();
me.SetVisible(true);
me.SetReactState(ReactStates.Defensive);
}
public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null)
{
// Midnight never dies, let health fall to 1 and prevent further damage.
if (damage >= me.GetHealth())
damage = (uint)(me.GetHealth() - 1);
if (_phase == Phases.None && me.HealthBelowPctDamaged(95, damage))
{
_phase = Phases.AttumenEngages;
Talk(TextIds.EmoteCallAttumen);
DoCastAOE(SpellIds.SummonAttumen);
}
else if (_phase == Phases.AttumenEngages && me.HealthBelowPctDamaged(25, damage))
{
_phase = Phases.Mounted;
DoCastAOE(SpellIds.Mount, new CastSpellExtraArgs(true));
}
}
public override void JustSummoned(Creature summon)
{
if (summon.GetEntry() == CreatureIds.AttumenUnmounted)
{
_attumenGUID = summon.GetGUID();
summon.GetAI().SetGUID(me.GetGUID(), (int)CreatureIds.Midnight);
summon.GetAI().AttackStart(me.GetVictim());
summon.GetAI().Talk(TextIds.SayAppear);
}
base.JustSummoned(summon);
}
public override void JustEngagedWith(Unit who)
{
base.JustEngagedWith(who);
_scheduler.Schedule(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(25), task =>
{
DoCastVictim(SpellIds.Knockdown);
task.Repeat(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(25));
});
}
public override void EnterEvadeMode(EvadeReason why)
{
base._DespawnAtEvade(TimeSpan.FromSeconds(10));
}
public override void KilledUnit(Unit victim)
{
if (_phase == Phases.AttumenEngages)
{
Unit unit = Global.ObjAccessor.GetUnit(me, _attumenGUID);
if (unit)
Talk(TextIds.SayMidnightKill, unit);
}
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim() || _phase == Phases.Mounted)
return;
_scheduler.Update(diff, () => DoMeleeAttackIfReady());
}
}
}
@@ -0,0 +1,737 @@
/*
* Copyright (C) 2012-2020 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 Game.AI;
using Game.Entities;
using Game.Maps;
using Game.Scripting;
using Game.Spells;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Scripts.EasternKingdoms.Karazhan.Moroes
{
struct SpellIds
{
public const uint Vanish = 29448;
public const uint Garrote = 37066;
public const uint Blind = 34694;
public const uint Gouge = 29425;
public const uint Frenzy = 37023;
// Adds
public const uint Manaburn = 29405;
public const uint Mindfly = 29570;
public const uint Swpain = 34441;
public const uint Shadowform = 29406;
public const uint Hammerofjustice = 13005;
public const uint Judgementofcommand = 29386;
public const uint Sealofcommand = 29385;
public const uint Dispelmagic = 15090;
public const uint Greaterheal = 29564;
public const uint Holyfire = 29563;
public const uint Pwshield = 29408;
public const uint Cleanse = 29380;
public const uint Greaterblessofmight = 29381;
public const uint Holylight = 29562;
public const uint Divineshield = 41367;
public const uint Hamstring = 9080;
public const uint Mortalstrike = 29572;
public const uint Whirlwind = 29573;
public const uint Disarm = 8379;
public const uint Heroicstrike = 29567;
public const uint Shieldbash = 11972;
public const uint Shieldwall = 29390;
}
struct TextIds
{
public const uint SayAggro = 0;
public const uint SaySpecial = 1;
public const uint SayKill = 2;
public const uint SayDeath = 3;
}
struct MiscConst
{
public const uint GroupNonEnrage = 1;
public static Position[] Locations =
{
new Position(-10991.0f, -1884.33f, 81.73f, 0.614315f),
new Position(-10989.4f, -1885.88f, 81.73f, 0.904913f),
new Position(-10978.1f, -1887.07f, 81.73f, 2.035550f),
new Position(-10975.9f, -1885.81f, 81.73f, 2.253890f)
};
public static uint[] Adds =
{
17007,
19872,
19873,
19874,
19875,
19876,
};
}
[Script]
class boss_moroes : BossAI
{
public ObjectGuid[] AddGUID = new ObjectGuid[4];
uint[] AddId = new uint[4];
bool InVanish;
bool Enrage;
public boss_moroes(Creature creature) : base(creature, DataTypes.Moroes)
{
Initialize();
}
void Initialize()
{
Enrage = false;
InVanish = false;
}
public override void Reset()
{
Initialize();
if (me.IsAlive())
SpawnAdds();
base.Reset();
}
public override void JustEngagedWith(Unit who)
{
base.JustEngagedWith(who);
_scheduler.Schedule(TimeSpan.FromSeconds(5), MiscConst.GroupNonEnrage, task =>
{
for (byte i = 0; i < 4; ++i)
{
if (!AddGUID[i].IsEmpty())
{
Creature temp = ObjectAccessor.GetCreature(me, AddGUID[i]);
if (temp && temp.IsAlive())
if (!temp.GetVictim())
temp.GetAI().AttackStart(me.GetVictim());
}
}
task.Repeat();
});
_scheduler.Schedule(TimeSpan.FromSeconds(23), MiscConst.GroupNonEnrage, task =>
{
DoCastVictim(SpellIds.Gouge);
task.Repeat(TimeSpan.FromSeconds(40));
});
_scheduler.Schedule(TimeSpan.FromSeconds(30), MiscConst.GroupNonEnrage, task =>
{
DoCast(me, SpellIds.Vanish);
InVanish = true;
task.Schedule(TimeSpan.FromSeconds(5), garroteTask =>
{
Talk(TextIds.SaySpecial);
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 100, true);
if (target)
target.CastSpell(target, SpellIds.Garrote, true);
InVanish = false;
});
task.Repeat();
});
_scheduler.Schedule(TimeSpan.FromSeconds(35), MiscConst.GroupNonEnrage, task =>
{
Unit target = SelectTarget(SelectTargetMethod.MinDistance, 0, 0.0f, true, false);
if (target)
DoCast(target, SpellIds.Blind);
task.Repeat(TimeSpan.FromSeconds(40));
});
Talk(TextIds.SayAggro);
AddsAttack();
}
public override void KilledUnit(Unit victim)
{
Talk(TextIds.SayKill);
}
public override void JustDied(Unit killer)
{
Talk(TextIds.SayDeath);
base.JustDied(killer);
DeSpawnAdds();
//remove aura from spell Garrote when Moroes dies
instance.DoRemoveAurasDueToSpellOnPlayers(SpellIds.Garrote);
}
void SpawnAdds()
{
DeSpawnAdds();
if (isAddlistEmpty())
{
List<uint> AddList = MiscConst.Adds.ToList();
AddList.RandomResize(4);
byte i = 0;
foreach (var entry in AddList)
{
Creature creature = me.SummonCreature(entry, MiscConst.Locations[i], TempSummonType.CorpseTimedDespawn, TimeSpan.FromSeconds(10));
if (creature)
{
AddGUID[i] = creature.GetGUID();
AddId[i] = entry;
}
}
}
else
{
for (byte i = 0; i < 4; ++i)
{
Creature creature = me.SummonCreature(AddId[i], MiscConst.Locations[i], TempSummonType.CorpseTimedDespawn, TimeSpan.FromSeconds(10));
if (creature)
AddGUID[i] = creature.GetGUID();
}
}
}
bool isAddlistEmpty()
{
for (byte i = 0; i < 4; ++i)
if (AddId[i] == 0)
return true;
return false;
}
void DeSpawnAdds()
{
for (byte i = 0; i < 4; ++i)
{
if (!AddGUID[i].IsEmpty())
{
Creature temp = ObjectAccessor.GetCreature(me, AddGUID[i]);
if (temp)
temp.DespawnOrUnsummon();
}
}
}
void AddsAttack()
{
for (byte i = 0; i < 4; ++i)
{
if (!AddGUID[i].IsEmpty())
{
Creature temp = ObjectAccessor.GetCreature((me), AddGUID[i]);
if (temp && temp.IsAlive())
{
temp.GetAI().AttackStart(me.GetVictim());
DoZoneInCombat(temp);
}
else
EnterEvadeMode();
}
}
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
if (!Enrage && HealthBelowPct(30))
{
DoCast(me, SpellIds.Frenzy);
Enrage = true;
_scheduler.CancelGroup(MiscConst.GroupNonEnrage);
}
_scheduler.Update(diff, () =>
{
if (!InVanish)
DoMeleeAttackIfReady();
});
}
}
[Script]
class boss_moroes_guest : ScriptedAI
{
InstanceScript instance;
ObjectGuid[] GuestGUID = new ObjectGuid[4];
public boss_moroes_guest(Creature creature) : base(creature)
{
instance = creature.GetInstanceScript();
}
public override void Reset()
{
instance.SetBossState(DataTypes.Moroes, EncounterState.NotStarted);
}
public void AcquireGUID()
{
Creature Moroes = ObjectAccessor.GetCreature(me, instance.GetGuidData(DataTypes.Moroes));
if (Moroes)
{
for (byte i = 0; i < 4; ++i)
{
ObjectGuid Guid = Moroes.GetAI<boss_moroes>().AddGUID[i];
if (!Guid.IsEmpty())
GuestGUID[i] = Guid;
}
}
}
public Unit SelectGuestTarget()
{
ObjectGuid TempGUID = GuestGUID[RandomHelper.Rand32() % 4];
if (!TempGUID.IsEmpty())
{
Unit unit = Global.ObjAccessor.GetUnit(me, TempGUID);
if (unit && unit.IsAlive())
return unit;
}
return me;
}
public override void UpdateAI(uint diff)
{
if (instance.GetBossState(DataTypes.Moroes) != EncounterState.InProgress)
EnterEvadeMode();
DoMeleeAttackIfReady();
}
}
[Script]
class boss_baroness_dorothea_millstipe : boss_moroes_guest
{
//Shadow Priest
public boss_baroness_dorothea_millstipe(Creature creature) : base(creature)
{
Initialize();
}
void Initialize()
{
ManaBurn_Timer = 7000;
MindFlay_Timer = 1000;
ShadowWordPain_Timer = 6000;
}
uint ManaBurn_Timer;
uint MindFlay_Timer;
uint ShadowWordPain_Timer;
public override void Reset()
{
Initialize();
DoCast(me, SpellIds.Shadowform, new CastSpellExtraArgs(true));
base.Reset();
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
base.UpdateAI(diff);
if (MindFlay_Timer <= diff)
{
DoCastVictim(SpellIds.Mindfly);
MindFlay_Timer = 12000; // 3 sec channeled
}
else MindFlay_Timer -= diff;
if (ManaBurn_Timer <= diff)
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 100, true);
if (target)
if (target.GetPowerType() == PowerType.Mana)
DoCast(target, SpellIds.Manaburn);
ManaBurn_Timer = 5000; // 3 sec cast
}
else ManaBurn_Timer -= diff;
if (ShadowWordPain_Timer <= diff)
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 100, true);
if (target)
{
DoCast(target, SpellIds.Swpain);
ShadowWordPain_Timer = 7000;
}
}
else ShadowWordPain_Timer -= diff;
}
}
[Script]
class boss_baron_rafe_dreuger : boss_moroes_guest
{
//Retr Pally
public boss_baron_rafe_dreuger(Creature creature) : base(creature)
{
Initialize();
}
void Initialize()
{
HammerOfJustice_Timer = 1000;
SealOfCommand_Timer = 7000;
JudgementOfCommand_Timer = SealOfCommand_Timer + 29000;
}
uint HammerOfJustice_Timer;
uint SealOfCommand_Timer;
uint JudgementOfCommand_Timer;
public override void Reset()
{
Initialize();
base.Reset();
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
base.UpdateAI(diff);
if (SealOfCommand_Timer <= diff)
{
DoCast(me, SpellIds.Sealofcommand);
SealOfCommand_Timer = 32000;
JudgementOfCommand_Timer = 29000;
}
else SealOfCommand_Timer -= diff;
if (JudgementOfCommand_Timer <= diff)
{
DoCastVictim(SpellIds.Judgementofcommand);
JudgementOfCommand_Timer = SealOfCommand_Timer + 29000;
}
else JudgementOfCommand_Timer -= diff;
if (HammerOfJustice_Timer <= diff)
{
DoCastVictim(SpellIds.Hammerofjustice);
HammerOfJustice_Timer = 12000;
}
else HammerOfJustice_Timer -= diff;
}
}
[Script]
class boss_lady_catriona_von_indi : boss_moroes_guest
{
//Holy Priest
public boss_lady_catriona_von_indi(Creature creature) : base(creature)
{
Initialize();
}
void Initialize()
{
DispelMagic_Timer = 11000;
GreaterHeal_Timer = 1500;
HolyFire_Timer = 5000;
PowerWordShield_Timer = 1000;
}
uint DispelMagic_Timer;
uint GreaterHeal_Timer;
uint HolyFire_Timer;
uint PowerWordShield_Timer;
public override void Reset()
{
Initialize();
AcquireGUID();
base.Reset();
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
base.UpdateAI(diff);
if (PowerWordShield_Timer <= diff)
{
DoCast(me, SpellIds.Pwshield);
PowerWordShield_Timer = 15000;
}
else PowerWordShield_Timer -= diff;
if (GreaterHeal_Timer <= diff)
{
Unit target = SelectGuestTarget();
DoCast(target, SpellIds.Greaterheal);
GreaterHeal_Timer = 17000;
}
else GreaterHeal_Timer -= diff;
if (HolyFire_Timer <= diff)
{
DoCastVictim(SpellIds.Holyfire);
HolyFire_Timer = 22000;
}
else HolyFire_Timer -= diff;
if (DispelMagic_Timer <= diff)
{
Unit target = RandomHelper.RAND(SelectGuestTarget(), SelectTarget(SelectTargetMethod.Random, 0, 100, true));
if (target)
DoCast(target, SpellIds.Dispelmagic);
DispelMagic_Timer = 25000;
}
else DispelMagic_Timer -= diff;
}
}
[Script]
class boss_lady_keira_berrybuck : boss_moroes_guest
{
//Holy Pally
public boss_lady_keira_berrybuck(Creature creature) : base(creature)
{
Initialize();
}
void Initialize()
{
Cleanse_Timer = 13000;
GreaterBless_Timer = 1000;
HolyLight_Timer = 7000;
DivineShield_Timer = 31000;
}
uint Cleanse_Timer;
uint GreaterBless_Timer;
uint HolyLight_Timer;
uint DivineShield_Timer;
public override void Reset()
{
Initialize();
AcquireGUID();
base.Reset();
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
base.UpdateAI(diff);
if (DivineShield_Timer <= diff)
{
DoCast(me, SpellIds.Divineshield);
DivineShield_Timer = 31000;
}
else DivineShield_Timer -= diff;
if (HolyLight_Timer <= diff)
{
Unit target = SelectGuestTarget();
DoCast(target, SpellIds.Holylight);
HolyLight_Timer = 10000;
}
else HolyLight_Timer -= diff;
if (GreaterBless_Timer <= diff)
{
Unit target = SelectGuestTarget();
DoCast(target, SpellIds.Greaterblessofmight);
GreaterBless_Timer = 50000;
}
else GreaterBless_Timer -= diff;
if (Cleanse_Timer <= diff)
{
Unit target = SelectGuestTarget();
DoCast(target, SpellIds.Cleanse);
Cleanse_Timer = 10000;
}
else Cleanse_Timer -= diff;
}
}
[Script]
class boss_lord_robin_daris : boss_moroes_guest
{
//Arms Warr
public boss_lord_robin_daris(Creature creature) : base(creature)
{
Initialize();
}
void Initialize()
{
Hamstring_Timer = 7000;
MortalStrike_Timer = 10000;
WhirlWind_Timer = 21000;
}
uint Hamstring_Timer;
uint MortalStrike_Timer;
uint WhirlWind_Timer;
public override void Reset()
{
Initialize();
base.Reset();
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
base.UpdateAI(diff);
if (Hamstring_Timer <= diff)
{
DoCastVictim(SpellIds.Hamstring);
Hamstring_Timer = 12000;
}
else Hamstring_Timer -= diff;
if (MortalStrike_Timer <= diff)
{
DoCastVictim(SpellIds.Mortalstrike);
MortalStrike_Timer = 18000;
}
else MortalStrike_Timer -= diff;
if (WhirlWind_Timer <= diff)
{
DoCast(me, SpellIds.Whirlwind);
WhirlWind_Timer = 21000;
}
else WhirlWind_Timer -= diff;
}
}
[Script]
class boss_lord_crispin_ference : boss_moroes_guest
{
//Arms Warr
public boss_lord_crispin_ference(Creature creature) : base(creature)
{
Initialize();
}
void Initialize()
{
Disarm_Timer = 6000;
HeroicStrike_Timer = 10000;
ShieldBash_Timer = 8000;
ShieldWall_Timer = 4000;
}
uint Disarm_Timer;
uint HeroicStrike_Timer;
uint ShieldBash_Timer;
uint ShieldWall_Timer;
public override void Reset()
{
Initialize();
base.Reset();
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
base.UpdateAI(diff);
if (Disarm_Timer <= diff)
{
DoCastVictim(SpellIds.Disarm);
Disarm_Timer = 12000;
}
else Disarm_Timer -= diff;
if (HeroicStrike_Timer <= diff)
{
DoCastVictim(SpellIds.Heroicstrike);
HeroicStrike_Timer = 10000;
}
else HeroicStrike_Timer -= diff;
if (ShieldBash_Timer <= diff)
{
DoCastVictim(SpellIds.Shieldbash);
ShieldBash_Timer = 13000;
}
else ShieldBash_Timer -= diff;
if (ShieldWall_Timer <= diff)
{
DoCast(me, SpellIds.Shieldwall);
ShieldWall_Timer = 21000;
}
else ShieldWall_Timer -= diff;
}
}
}
@@ -0,0 +1,358 @@
/*
* Copyright (C) 2012-2020 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 Game.AI;
using Game.Entities;
using Game.Maps;
using Game.Scripting;
using Game.Spells;
using System;
using System.Numerics;
namespace Scripts.EasternKingdoms.Karazhan.Netherspite
{
struct SpellIds
{
public const uint NetherburnAura = 30522;
public const uint Voidzone = 37063;
public const uint NetherInfusion = 38688;
public const uint Netherbreath = 38523;
public const uint BanishVisual = 39833;
public const uint BanishRoot = 42716;
public const uint Empowerment = 38549;
public const uint NetherspiteRoar = 38684;
}
struct TextIds
{
public const uint EmotePhasePortal = 0;
public const uint EmotePhaseBanish = 1;
}
enum Portals
{
Red = 0, // Perseverence
Green = 1, // Serenity
Blue = 2 // Dominance
}
struct MiscConst
{
public static Vector3[] PortalCoord =
{
new Vector3(-11195.353516f, -1613.237183f, 278.237258f), // Left side
new Vector3(-11137.846680f, -1685.607422f, 278.239258f), // Right side
new Vector3(-11094.493164f, -1591.969238f, 279.949188f) // Back side
};
public static uint[] PortalID = { 17369, 17367, 17368 };
public static uint[] PortalVisual = { 30487, 30490, 30491 };
public static uint[] PortalBeam = { 30465, 30464, 30463 };
public static uint[] PlayerBuff = { 30421, 30422, 30423 };
public static uint[] NetherBuff = { 30466, 30467, 30468 };
public static uint[] PlayerDebuff = { 38637, 38638, 38639 };
}
[Script]
class boss_netherspite : ScriptedAI
{
InstanceScript instance;
bool PortalPhase;
bool Berserk;
uint PhaseTimer; // timer for phase switching
uint VoidZoneTimer;
uint NetherInfusionTimer; // berserking timer
uint NetherbreathTimer;
uint EmpowermentTimer;
uint PortalTimer; // timer for beam checking
ObjectGuid[] PortalGUID = new ObjectGuid[3]; // guid's of portals
ObjectGuid[] BeamerGUID = new ObjectGuid[3]; // guid's of auxiliary beaming portals
ObjectGuid[] BeamTarget = new ObjectGuid[3]; // guid's of portals' current targets
public boss_netherspite(Creature creature) : base(creature)
{
Initialize();
instance = creature.GetInstanceScript();
PortalPhase = false;
PhaseTimer = 0;
EmpowermentTimer = 0;
PortalTimer = 0;
}
void Initialize()
{
Berserk = false;
NetherInfusionTimer = 540000;
VoidZoneTimer = 15000;
NetherbreathTimer = 3000;
}
bool IsBetween(WorldObject u1, WorldObject target, WorldObject u2) // the in-line checker
{
if (!u1 || !u2 || !target)
return false;
float xn, yn, xp, yp, xh, yh;
xn = u1.GetPositionX();
yn = u1.GetPositionY();
xp = u2.GetPositionX();
yp = u2.GetPositionY();
xh = target.GetPositionX();
yh = target.GetPositionY();
// check if target is between (not checking distance from the beam yet)
if (dist(xn, yn, xh, yh) >= dist(xn, yn, xp, yp) || dist(xp, yp, xh, yh) >= dist(xn, yn, xp, yp))
return false;
// check distance from the beam
return (Math.Abs((xn - xp) * yh + (yp - yn) * xh - xn * yp + xp * yn) / dist(xn, yn, xp, yp) < 1.5f);
}
float dist(float xa, float ya, float xb, float yb) // auxiliary method for distance
{
return MathF.Sqrt((xa - xb) * (xa - xb) + (ya - yb) * (ya - yb));
}
public override void Reset()
{
Initialize();
HandleDoors(true);
DestroyPortals();
}
void SummonPortals()
{
uint r = RandomHelper.Rand32() % 4;
int[] pos = new int[3];
pos[(int)Portals.Red] = ((r % 2) != 0 ? (r > 1 ? 2 : 1) : 0);
pos[(int)Portals.Green] = ((r % 2) != 0 ? 0 : (r > 1 ? 2 : 1));
pos[(int)Portals.Blue] = (r > 1 ? 1 : 2); // Blue Portal not on the left side (0)
for (int i = 0; i < 3; ++i)
{
Creature portal = me.SummonCreature(MiscConst.PortalID[i], MiscConst.PortalCoord[pos[i]].X, MiscConst.PortalCoord[pos[i]].Y, MiscConst.PortalCoord[pos[i]].Z, 0, TempSummonType.TimedDespawn, TimeSpan.FromMinutes(1));
if (portal)
{
PortalGUID[i] = portal.GetGUID();
portal.AddAura(MiscConst.PortalVisual[i], portal);
}
}
}
void DestroyPortals()
{
for (int i = 0; i < 3; ++i)
{
Creature portal = ObjectAccessor.GetCreature(me, PortalGUID[i]);
if (portal)
portal.DisappearAndDie();
Creature portal1 = ObjectAccessor.GetCreature(me, BeamerGUID[i]);
if (portal1)
portal1.DisappearAndDie();
PortalGUID[i].Clear();
BeamTarget[i].Clear();
}
}
void UpdatePortals() // Here we handle the beams' behavior
{
for (int j = 0; j < 3; ++j) // j = color
{
Creature portal = ObjectAccessor.GetCreature(me, PortalGUID[j]);
if (portal)
{
// the one who's been cast upon before
Unit current = Global.ObjAccessor.GetUnit(portal, BeamTarget[j]);
// temporary store for the best suitable beam reciever
Unit target = me;
var players = me.GetMap().GetPlayers();
// get the best suitable target
foreach (var player in players)
{
if (player && player.IsAlive() // alive
&& (!target || target.GetDistance2d(portal) > player.GetDistance2d(portal)) // closer than current best
&& !player.HasAura(MiscConst.PlayerDebuff[j]) // not exhausted
&& !player.HasAura(MiscConst.PlayerBuff[(j + 1) % 3]) // not on another beam
&& !player.HasAura(MiscConst.PlayerBuff[(j + 2) % 3])
&& IsBetween(me, player, portal)) // on the beam
target = player;
}
// buff the target
if (target.IsPlayer())
target.AddAura(MiscConst.PlayerBuff[j], target);
else
target.AddAura(MiscConst.NetherBuff[j], target);
// cast visual beam on the chosen target if switched
// simple target switching isn't working . using BeamerGUID to cast (workaround)
if (!current || target != current)
{
BeamTarget[j] = target.GetGUID();
// remove currently beaming portal
Creature beamer = ObjectAccessor.GetCreature(portal, BeamerGUID[j]);
if (beamer)
{
beamer.CastSpell(target, MiscConst.PortalBeam[j], false);
beamer.DisappearAndDie();
BeamerGUID[j].Clear();
}
// create new one and start beaming on the target
Creature beamer1 = portal.SummonCreature(MiscConst.PortalID[j], portal.GetPositionX(), portal.GetPositionY(), portal.GetPositionZ(), portal.GetOrientation(), TempSummonType.TimedDespawn, TimeSpan.FromMinutes(1));
if (beamer1)
{
beamer1.CastSpell(target, MiscConst.PortalBeam[j], false);
BeamerGUID[j] = beamer1.GetGUID();
}
}
// aggro target if Red Beam
if (j == (int)Portals.Red && me.GetVictim() != target && target.IsPlayer())
AddThreat(target, 100000.0f);
}
}
}
void SwitchToPortalPhase()
{
me.RemoveAurasDueToSpell(SpellIds.BanishRoot);
me.RemoveAurasDueToSpell(SpellIds.BanishVisual);
SummonPortals();
PhaseTimer = 60000;
PortalPhase = true;
PortalTimer = 10000;
EmpowermentTimer = 10000;
Talk(TextIds.EmotePhasePortal);
}
void SwitchToBanishPhase()
{
me.RemoveAurasDueToSpell(SpellIds.Empowerment);
me.RemoveAurasDueToSpell(SpellIds.NetherburnAura);
DoCast(me, SpellIds.BanishVisual, new CastSpellExtraArgs(true));
DoCast(me, SpellIds.BanishRoot, new CastSpellExtraArgs(true));
DestroyPortals();
PhaseTimer = 30000;
PortalPhase = false;
Talk(TextIds.EmotePhaseBanish);
for (byte i = 0; i < 3; ++i)
me.RemoveAurasDueToSpell(MiscConst.NetherBuff[i]);
}
void HandleDoors(bool open) // Massive Door switcher
{
GameObject Door = ObjectAccessor.GetGameObject(me, instance.GetGuidData(DataTypes.GoMassiveDoor));
if (Door)
Door.SetGoState(open ? GameObjectState.Active : GameObjectState.Ready);
}
public override void JustEngagedWith(Unit who)
{
HandleDoors(false);
SwitchToPortalPhase();
}
public override void JustDied(Unit killer)
{
HandleDoors(true);
DestroyPortals();
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
// Void Zone
if (VoidZoneTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod.Random, 1, 45, true), SpellIds.Voidzone, new CastSpellExtraArgs(true));
VoidZoneTimer = 15000;
}
else VoidZoneTimer -= diff;
// NetherInfusion Berserk
if (!Berserk && NetherInfusionTimer <= diff)
{
me.AddAura(SpellIds.NetherInfusion, me);
DoCast(me, SpellIds.NetherspiteRoar);
Berserk = true;
}
else NetherInfusionTimer -= diff;
if (PortalPhase) // Portal Phase
{
// Distribute beams and buffs
if (PortalTimer <= diff)
{
UpdatePortals();
PortalTimer = 1000;
}
else PortalTimer -= diff;
// Empowerment & Nether Burn
if (EmpowermentTimer <= diff)
{
DoCast(me, SpellIds.Empowerment);
me.AddAura(SpellIds.NetherburnAura, me);
EmpowermentTimer = 90000;
}
else EmpowermentTimer -= diff;
if (PhaseTimer <= diff)
{
if (!me.IsNonMeleeSpellCast(false))
{
SwitchToBanishPhase();
return;
}
}
else PhaseTimer -= diff;
}
else // Banish Phase
{
// Netherbreath
if (NetherbreathTimer <= diff)
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 40, true);
if (target)
DoCast(target, SpellIds.Netherbreath);
NetherbreathTimer = RandomHelper.URand(5000, 7000);
}
else NetherbreathTimer -= diff;
if (PhaseTimer <= diff)
{
if (!me.IsNonMeleeSpellCast(false))
{
SwitchToPortalPhase();
return;
}
}
else PhaseTimer -= diff;
}
DoMeleeAttackIfReady();
}
}
}
@@ -0,0 +1,386 @@
/*
* Copyright (C) 2012-2020 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 Game.AI;
using Game.Entities;
using Game.Maps;
using Game.Scripting;
using Game.Spells;
using System;
namespace Scripts.EasternKingdoms.Karazhan.Nightbane
{
struct SpellIds
{
public const uint BellowingRoar = 36922;
public const uint CharredEarth = 30129;
public const uint Cleave = 30131;
public const uint DistractingAsh = 30130;
public const uint RainOfBones = 37098;
public const uint SmokingBlast = 30128;
public const uint SmokingBlastT = 37057;
public const uint SmolderingBreath = 30210;
public const uint SummonSkeleton = 30170;
public const uint TailSweep = 25653;
}
struct TextIds
{
public const uint EmoteSummon = 0;
public const uint YellAggro = 1;
public const uint YellFlyPhase = 2;
public const uint YellLandPhase = 3;
public const uint EmoteBreath = 4;
}
struct PointIds
{
public const uint IntroStart = 0;
public const uint IntroEnd = 1;
public const uint IntroLanding = 2;
public const uint PhaseTwoFly = 3;
public const uint PhaseTwoPreFly = 4;
public const uint PhaseTwoLanding = 5;
public const uint PhaseTwoEnd = 6;
}
struct SplineChainIds
{
public const uint IntroStart = 1;
public const uint IntroEnd = 2;
public const uint IntroLanding = 3;
public const uint SecondLanding = 4;
public const uint PhaseTwo = 5;
}
enum NightbanePhases
{
Intro = 0,
Ground,
Fly
}
struct MiscConst
{
public const int ActionSummon = 0;
public const uint PathPhaseTwo = 13547500;
public const uint GroupGround = 1;
public const uint GroupFly = 2;
public static Position FlyPosition = new Position(-11160.13f, -1870.683f, 97.73876f, 0.0f);
public static Position FlyPositionLeft = new Position(-11094.42f, -1866.992f, 107.8375f, 0.0f);
public static Position FlyPositionRight = new Position(-11193.77f, -1921.983f, 107.9845f, 0.0f);
}
[Script]
class boss_nightbane : BossAI
{
byte _flyCount;
NightbanePhases phase;
public boss_nightbane(Creature creature) : base(creature, DataTypes.Nightbane) { }
public override void Reset()
{
_Reset();
_flyCount = 0;
me.SetDisableGravity(true);
HandleTerraceDoors(true);
GameObject urn = ObjectAccessor.GetGameObject(me, instance.GetGuidData(DataTypes.GoBlackenedUrn));
if (urn)
urn.RemoveFlag(GameObjectFlags.InUse);
}
public override void EnterEvadeMode(EvadeReason why)
{
me.SetDisableGravity(true);
base.EnterEvadeMode(why);
}
public override void JustReachedHome()
{
_DespawnAtEvade();
}
public override void JustDied(Unit killer)
{
_JustDied();
HandleTerraceDoors(true);
}
public override void DoAction(int action)
{
if (action == MiscConst.ActionSummon)
{
Talk(TextIds.EmoteSummon);
phase = NightbanePhases.Intro;
me.SetActive(true);
me.SetFarVisible(true);
me.RemoveUnitFlag(UnitFlags.Uninteractible);
me.GetMotionMaster().MoveAlongSplineChain(PointIds.IntroStart, SplineChainIds.IntroStart, false);
HandleTerraceDoors(false);
}
}
void SetupGroundPhase()
{
phase = NightbanePhases.Ground;
_scheduler.Schedule(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(15), MiscConst.GroupGround, task =>
{
DoCastVictim(SpellIds.Cleave);
task.Repeat(TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(15));
});
_scheduler.Schedule(TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(23), MiscConst.GroupGround, task =>
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 0.0f, true);
if (target)
if (!me.HasInArc(MathF.PI, target))
DoCast(target, SpellIds.TailSweep);
task.Repeat(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(30));
});
_scheduler.Schedule(TimeSpan.FromSeconds(48), MiscConst.GroupGround, task =>
{
DoCastAOE(SpellIds.BellowingRoar);
});
_scheduler.Schedule(TimeSpan.FromSeconds(12), TimeSpan.FromSeconds(18), MiscConst.GroupGround, task =>
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 0.0f, true);
if (target)
DoCast(target, SpellIds.CharredEarth);
task.Repeat(TimeSpan.FromSeconds(18), TimeSpan.FromSeconds(21));
});
_scheduler.Schedule(TimeSpan.FromSeconds(26), TimeSpan.FromSeconds(30), MiscConst.GroupGround, task =>
{
DoCastVictim(SpellIds.SmolderingBreath);
task.Repeat(TimeSpan.FromSeconds(28), TimeSpan.FromSeconds(40));
});
_scheduler.Schedule(TimeSpan.FromSeconds(82), MiscConst.GroupGround, task =>
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 0.0f, true);
if (target)
DoCast(target, SpellIds.DistractingAsh);
});
}
void HandleTerraceDoors(bool open)
{
instance.HandleGameObject(instance.GetGuidData(DataTypes.MastersTerraceDoor1), open);
instance.HandleGameObject(instance.GetGuidData(DataTypes.MastersTerraceDoor2), open);
}
public override void JustEngagedWith(Unit who)
{
base.JustEngagedWith(who);
Talk(TextIds.YellAggro);
SetupGroundPhase();
}
public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null)
{
if (phase == NightbanePhases.Fly)
{
if (damage >= me.GetHealth())
damage = (uint)(me.GetHealth() - 1);
return;
}
if ((_flyCount == 0 && HealthBelowPct(75)) || (_flyCount == 1 && HealthBelowPct(50)) || (_flyCount == 2 && HealthBelowPct(25)))
{
phase = NightbanePhases.Fly;
StartPhaseFly();
}
}
public override void MovementInform(MovementGeneratorType type, uint pointId)
{
if (type == MovementGeneratorType.SplineChain)
{
switch (pointId)
{
case PointIds.IntroStart:
me.SetStandState(UnitStandStateType.Stand);
_scheduler.Schedule(TimeSpan.FromMilliseconds(1), task =>
{
me.GetMotionMaster().MoveAlongSplineChain(PointIds.IntroEnd, SplineChainIds.IntroEnd, false);
});
break;
case PointIds.IntroEnd:
_scheduler.Schedule(TimeSpan.FromSeconds(2), task =>
{
me.GetMotionMaster().MoveAlongSplineChain(PointIds.IntroLanding, SplineChainIds.IntroLanding, false);
});
break;
case PointIds.IntroLanding:
me.SetDisableGravity(false);
me.HandleEmoteCommand(Emote.OneshotLand);
_scheduler.Schedule(TimeSpan.FromSeconds(3), task =>
{
me.SetImmuneToPC(false);
DoZoneInCombat();
});
break;
case PointIds.PhaseTwoLanding:
phase = NightbanePhases.Ground;
me.SetDisableGravity(false);
me.HandleEmoteCommand(Emote.OneshotLand);
_scheduler.Schedule(TimeSpan.FromSeconds(3), task =>
{
SetupGroundPhase();
me.SetReactState(ReactStates.Aggressive);
});
break;
case PointIds.PhaseTwoEnd:
_scheduler.Schedule(TimeSpan.FromMilliseconds(1), task =>
{
me.GetMotionMaster().MoveAlongSplineChain(PointIds.PhaseTwoLanding, SplineChainIds.SecondLanding, false);
});
break;
default:
break;
}
}
else if (type == MovementGeneratorType.Point)
{
if (pointId == PointIds.PhaseTwoFly)
{
_scheduler.Schedule(TimeSpan.FromSeconds(33), MiscConst.GroupFly, task =>
{
_scheduler.CancelGroup(MiscConst.GroupFly);
_scheduler.Schedule(TimeSpan.FromSeconds(2), MiscConst.GroupGround, landTask =>
{
Talk(TextIds.YellLandPhase);
me.SetDisableGravity(true);
me.GetMotionMaster().MoveAlongSplineChain(PointIds.PhaseTwoEnd, SplineChainIds.PhaseTwo, false);
});
});
_scheduler.Schedule(TimeSpan.FromSeconds(2), MiscConst.GroupFly, task =>
{
Talk(TextIds.EmoteBreath);
task.Schedule(TimeSpan.FromSeconds(3), MiscConst.GroupFly, somethingTask =>
{
ResetThreatList();
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 0.0f, true);
if (target)
{
me.SetFacingToObject(target);
DoCast(target, SpellIds.RainOfBones);
}
});
});
_scheduler.Schedule(TimeSpan.FromSeconds(21), MiscConst.GroupFly, task =>
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 0.0f, true);
if (target)
DoCast(target, SpellIds.SmokingBlastT);
task.Repeat(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(7));
});
_scheduler.Schedule(TimeSpan.FromSeconds(17), MiscConst.GroupFly, task =>
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 0.0f, true);
if (target)
DoCast(target, SpellIds.SmokingBlast);
task.Repeat(TimeSpan.FromMilliseconds(1400));
});
}
else if (pointId == PointIds.PhaseTwoPreFly)
{
_scheduler.Schedule(TimeSpan.FromMilliseconds(1), task =>
{
me.GetMotionMaster().MovePoint(PointIds.PhaseTwoFly, MiscConst.FlyPosition, true);
});
}
}
}
void StartPhaseFly()
{
++_flyCount;
Talk(TextIds.YellFlyPhase);
_scheduler.CancelGroup(MiscConst.GroupGround);
me.InterruptNonMeleeSpells(false);
me.HandleEmoteCommand(Emote.OneshotLiftoff);
me.SetDisableGravity(true);
me.SetReactState(ReactStates.Passive);
me.AttackStop();
if (me.GetDistance(MiscConst.FlyPositionLeft) < me.GetDistance(MiscConst.FlyPosition))
me.GetMotionMaster().MovePoint(PointIds.PhaseTwoPreFly, MiscConst.FlyPositionLeft, true);
else if (me.GetDistance(MiscConst.FlyPositionRight) < me.GetDistance(MiscConst.FlyPosition))
me.GetMotionMaster().MovePoint(PointIds.PhaseTwoPreFly, MiscConst.FlyPositionRight, true);
else
me.GetMotionMaster().MovePoint(PointIds.PhaseTwoFly, MiscConst.FlyPosition, true);
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim() && phase != NightbanePhases.Intro)
return;
_scheduler.Update(diff, () => DoMeleeAttackIfReady());
}
}
[Script] // 37098 - Rain of Bones
class spell_rain_of_bones_AuraScript : AuraScript
{
public override bool Validate(SpellInfo spellInfo)
{
return ValidateSpellInfo(SpellIds.SummonSkeleton);
}
void OnTrigger(AuraEffect aurEff)
{
if (aurEff.GetTickNumber() % 5 == 0)
GetTarget().CastSpell(GetTarget(), SpellIds.SummonSkeleton, true);
}
public override void Register()
{
OnEffectPeriodic.Add(new EffectPeriodicHandler(OnTrigger, 1, AuraType.PeriodicTriggerSpell));
}
}
[Script]
class go_blackened_urn : GameObjectAI
{
InstanceScript instance;
public go_blackened_urn(GameObject go) : base(go)
{
instance = go.GetInstanceScript();
}
public override bool OnGossipHello(Player player)
{
if (me.HasFlag(GameObjectFlags.InUse))
return false;
if (instance.GetBossState(DataTypes.Nightbane) == EncounterState.Done || instance.GetBossState(DataTypes.Nightbane) == EncounterState.InProgress)
return false;
Creature nightbane = ObjectAccessor.GetCreature(me, instance.GetGuidData(DataTypes.Nightbane));
if (nightbane)
{
me.SetFlag(GameObjectFlags.InUse);
nightbane.GetAI().DoAction(MiscConst.ActionSummon);
}
return false;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,567 @@
/*
* Copyright (C) 2012-2020 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 Game.AI;
using Game.Entities;
using Game.Maps;
using Game.Scripting;
using Game.Spells;
using System;
using System.Collections.Generic;
using System.Numerics;
namespace Scripts.EasternKingdoms.Karazhan.PrinceMalchezaar
{
struct TextIds
{
public const uint SayAggro = 0;
public const uint SayAxeToss1 = 1;
public const uint SayAxeToss2 = 2;
//public const uint SaySpecial1 = 3; Not used, needs to be implemented, but I don't know where it should be used.
//public const uint SaySpecial2 = 4; Not used, needs to be implemented, but I don't know where it should be used.
//public const uint SaySpecial3 = 5; Not used, needs to be implemented, but I don't know where it should be used.
public const uint SaySlay = 6;
public const uint SaySummon = 7;
public const uint SayDeath = 8;
}
struct SpellIds
{
public const uint Enfeeble = 30843; //Enfeeble during phase 1 and 2
public const uint EnfeebleEffect = 41624;
public const uint Shadownova = 30852; //Shadownova used during all phases
public const uint SwPain = 30854; //Shadow word pain during phase 1 and 3 (different targeting rules though)
public const uint ThrashPassive = 12787; //Extra attack chance during phase 2
public const uint SunderArmor = 30901; //Sunder armor during phase 2
public const uint ThrashAura = 12787; //Passive proc chance for thrash
public const uint EquipAxes = 30857; //Visual for axe equiping
public const uint AmplifyDamage = 39095; //Amplifiy during phase 3
public const uint Cleave = 30131; //Same as Nightbane.
public const uint Hellfire = 30859; //Infenals' hellfire aura
public const uint InfernalRelay = 30834;
}
struct MiscConst
{
public const uint TotalInfernalPoints = 18;
public const uint NetherspiteInfernal = 17646; //The netherspite infernal creature
public const uint MalchezarsAxe = 17650; //Malchezar's axes (creatures), summoned during phase 3
public const uint InfernalModelInvisible = 11686; //Infernal Effects
public const int EquipIdAxe = 33542; //Axes info
}
[Script]
class netherspite_infernal : ScriptedAI
{
public ObjectGuid Malchezaar;
public Vector2 Point;
public netherspite_infernal(Creature creature) : base(creature) { }
public override void Reset() { }
public override void JustEngagedWith(Unit who) { }
public override void MoveInLineOfSight(Unit who) { }
public override void UpdateAI(uint diff)
{
_scheduler.Update(diff);
}
public override void KilledUnit(Unit who)
{
Unit unit = Global.ObjAccessor.GetUnit(me, Malchezaar);
if (unit)
{
Creature creature = unit.ToCreature();
if (creature)
creature.GetAI().KilledUnit(who);
}
}
public override void SpellHit(WorldObject caster, SpellInfo spellInfo)
{
if (spellInfo.Id == SpellIds.InfernalRelay)
{
me.SetDisplayId(me.GetNativeDisplayId());
me.SetUnitFlag(UnitFlags.Uninteractible);
_scheduler.Schedule(TimeSpan.FromSeconds(4), task => DoCast(me, SpellIds.Hellfire));
_scheduler.Schedule(TimeSpan.FromSeconds(170), task =>
{
Creature pMalchezaar = ObjectAccessor.GetCreature(me, Malchezaar);
if (pMalchezaar && pMalchezaar.IsAlive())
pMalchezaar.GetAI<boss_malchezaar>().Cleanup(me, Point);
});
}
}
public override void DamageTaken(Unit done_by, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null)
{
if (!done_by || done_by.GetGUID() != Malchezaar)
damage = 0;
}
}
[Script]
class boss_malchezaar : ScriptedAI
{
static Vector2[] InfernalPoints =
{
new Vector2(-10922.8f, -1985.2f),
new Vector2(-10916.2f, -1996.2f),
new Vector2(-10932.2f, -2008.1f),
new Vector2(-10948.8f, -2022.1f),
new Vector2(-10958.7f, -1997.7f),
new Vector2(-10971.5f, -1997.5f),
new Vector2(-10990.8f, -1995.1f),
new Vector2(-10989.8f, -1976.5f),
new Vector2(-10971.6f, -1973.0f),
new Vector2(-10955.5f, -1974.0f),
new Vector2(-10939.6f, -1969.8f),
new Vector2(-10958.0f, -1952.2f),
new Vector2(-10941.7f, -1954.8f),
new Vector2(-10943.1f, -1988.5f),
new Vector2(-10948.8f, -2005.1f),
new Vector2(-10984.0f, -2019.3f),
new Vector2(-10932.8f, -1979.6f),
new Vector2(-10935.7f, -1996.0f)
};
InstanceScript instance;
uint EnfeebleTimer;
uint EnfeebleResetTimer;
uint ShadowNovaTimer;
uint SWPainTimer;
uint SunderArmorTimer;
uint AmplifyDamageTimer;
uint Cleave_Timer;
uint InfernalTimer;
uint AxesTargetSwitchTimer;
uint InfernalCleanupTimer;
List<ObjectGuid> infernals = new();
List<Vector2> positions = new();
ObjectGuid[] axes = new ObjectGuid[2];
ObjectGuid[] enfeeble_targets = new ObjectGuid[5];
ulong[] enfeeble_health = new ulong[5];
uint phase;
public boss_malchezaar(Creature creature) : base(creature)
{
Initialize();
instance = creature.GetInstanceScript();
}
void Initialize()
{
EnfeebleTimer = 30000;
EnfeebleResetTimer = 38000;
ShadowNovaTimer = 35500;
SWPainTimer = 20000;
AmplifyDamageTimer = 5000;
Cleave_Timer = 8000;
InfernalTimer = 40000;
InfernalCleanupTimer = 47000;
AxesTargetSwitchTimer = RandomHelper.URand(7500, 20000);
SunderArmorTimer = RandomHelper.URand(5000, 10000);
phase = 1;
for (byte i = 0; i < 5; ++i)
{
enfeeble_targets[i].Clear();
enfeeble_health[i] = 0;
}
}
public override void Reset()
{
AxesCleanup();
ClearWeapons();
InfernalCleanup();
positions.Clear();
Initialize();
for (byte i = 0; i < MiscConst.TotalInfernalPoints; ++i)
positions.Add(InfernalPoints[i]);
instance.HandleGameObject(instance.GetGuidData(DataTypes.GoNetherDoor), true);
}
public override void KilledUnit(Unit victim)
{
Talk(TextIds.SaySlay);
}
public override void JustDied(Unit killer)
{
Talk(TextIds.SayDeath);
AxesCleanup();
ClearWeapons();
InfernalCleanup();
positions.Clear();
for (byte i = 0; i < MiscConst.TotalInfernalPoints; ++i)
positions.Add(InfernalPoints[i]);
instance.HandleGameObject(instance.GetGuidData(DataTypes.GoNetherDoor), true);
}
public override void JustEngagedWith(Unit who)
{
Talk(TextIds.SayAggro);
instance.HandleGameObject(instance.GetGuidData(DataTypes.GoNetherDoor), false); // Open the door leading further in
}
void InfernalCleanup()
{
//Infernal Cleanup
foreach (var guid in infernals)
{
Unit pInfernal = Global.ObjAccessor.GetUnit(me, guid);
if (pInfernal && pInfernal.IsAlive())
{
pInfernal.SetVisible(false);
pInfernal.SetDeathState(DeathState.JustDied);
}
}
infernals.Clear();
}
void AxesCleanup()
{
for (byte i = 0; i < 2; ++i)
{
Unit axe = Global.ObjAccessor.GetUnit(me, axes[i]);
if (axe && axe.IsAlive())
axe.KillSelf();
axes[i].Clear();
}
}
void ClearWeapons()
{
SetEquipmentSlots(false, 0, 0);
me.SetCanDualWield(false);
}
void EnfeebleHealthEffect()
{
SpellInfo info = Global.SpellMgr.GetSpellInfo(SpellIds.EnfeebleEffect, GetDifficulty());
if (info == null)
return;
Unit tank = me.GetThreatManager().GetCurrentVictim();
List<Unit> targets = new();
foreach (var refe in me.GetThreatManager().GetSortedThreatList())
{
Unit target = refe.GetVictim();
if (target != tank && target.IsAlive() && target.IsPlayer())
targets.Add(target);
}
if (targets.Empty())
return;
//cut down to size if we have more than 5 targets
targets.RandomResize(5);
uint i = 0;
foreach (var target in targets)
{
if (target)
{
enfeeble_targets[i] = target.GetGUID();
enfeeble_health[i] = target.GetHealth();
CastSpellExtraArgs args = new(TriggerCastFlags.FullMask);
args.OriginalCaster = me.GetGUID();
target.CastSpell(target, SpellIds.Enfeeble, args);
target.SetHealth(1);
}
i++;
}
}
void EnfeebleResetHealth()
{
for (byte i = 0; i < 5; ++i)
{
Unit target = Global.ObjAccessor.GetUnit(me, enfeeble_targets[i]);
if (target && target.IsAlive())
target.SetHealth(enfeeble_health[i]);
enfeeble_targets[i].Clear();
enfeeble_health[i] = 0;
}
}
void SummonInfernal(uint diff)
{
Vector2 point = Vector2.Zero;
Position pos = null;
if ((me.GetMapId() != 532) || positions.Empty())
pos = me.GetRandomNearPosition(60);
else
{
point = positions.SelectRandom();
pos.Relocate(point.X, point.Y, 275.5f, RandomHelper.FRand(0.0f, (MathF.PI * 2)));
}
Creature infernal = me.SummonCreature(MiscConst.NetherspiteInfernal, pos, TempSummonType.TimedDespawn, TimeSpan.FromMinutes(3));
if (infernal)
{
infernal.SetDisplayId(MiscConst.InfernalModelInvisible);
infernal.SetFaction(me.GetFaction());
if (point != Vector2.Zero)
infernal.GetAI<netherspite_infernal>().Point = point;
infernal.GetAI<netherspite_infernal>().Malchezaar = me.GetGUID();
infernals.Add(infernal.GetGUID());
DoCast(infernal, SpellIds.InfernalRelay);
}
Talk(TextIds.SaySummon);
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
if (EnfeebleResetTimer != 0 && EnfeebleResetTimer <= diff) // Let's not forget to reset that
{
EnfeebleResetHealth();
EnfeebleResetTimer = 0;
}
else EnfeebleResetTimer -= diff;
if (me.HasUnitState(UnitState.Stunned)) // While shifting to phase 2 malchezaar stuns himself
return;
if (me.GetVictim() && me.GetTarget() != me.GetVictim().GetGUID())
me.SetTarget(me.GetVictim().GetGUID());
if (phase == 1)
{
if (HealthBelowPct(60))
{
me.InterruptNonMeleeSpells(false);
phase = 2;
//animation
DoCast(me, SpellIds.EquipAxes);
//text
Talk(TextIds.SayAxeToss1);
//passive thrash aura
DoCast(me, SpellIds.ThrashAura, new CastSpellExtraArgs(true));
//models
SetEquipmentSlots(false, MiscConst.EquipIdAxe, MiscConst.EquipIdAxe);
me.SetBaseAttackTime(WeaponAttackType.OffAttack, (me.GetBaseAttackTime(WeaponAttackType.BaseAttack) * 150) / 100);
me.SetCanDualWield(true);
}
}
else if (phase == 2)
{
if (HealthBelowPct(30))
{
InfernalTimer = 15000;
phase = 3;
ClearWeapons();
//remove thrash
me.RemoveAurasDueToSpell(SpellIds.ThrashAura);
Talk(TextIds.SayAxeToss2);
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 100, true);
for (byte i = 0; i < 2; ++i)
{
Creature axe = me.SummonCreature(MiscConst.MalchezarsAxe, me.GetPositionX(), me.GetPositionY(), me.GetPositionZ(), 0, TempSummonType.TimedDespawnOutOfCombat, TimeSpan.FromSeconds(1));
if (axe)
{
axe.SetUnitFlag(UnitFlags.Uninteractible);
axe.SetFaction(me.GetFaction());
axes[i] = axe.GetGUID();
if (target)
{
axe.GetAI().AttackStart(target);
AddThreat(target, 10000000.0f, axe);
}
}
}
if (ShadowNovaTimer > 35000)
ShadowNovaTimer = EnfeebleTimer + 5000;
return;
}
if (SunderArmorTimer <= diff)
{
DoCastVictim(SpellIds.SunderArmor);
SunderArmorTimer = RandomHelper.URand(10000, 18000);
}
else SunderArmorTimer -= diff;
if (Cleave_Timer <= diff)
{
DoCastVictim(SpellIds.Cleave);
Cleave_Timer = RandomHelper.URand(6000, 12000);
}
else Cleave_Timer -= diff;
}
else
{
if (AxesTargetSwitchTimer <= diff)
{
AxesTargetSwitchTimer = RandomHelper.URand(7500, 20000);
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 100, true);
if (target)
{
for (byte i = 0; i < 2; ++i)
{
Unit axe = Global.ObjAccessor.GetUnit(me, axes[i]);
if (axe)
{
if (axe.GetVictim())
ResetThreat(axe.GetVictim(), axe);
AddThreat(target, 1000000.0f, axe);
}
}
}
}
else AxesTargetSwitchTimer -= diff;
if (AmplifyDamageTimer <= diff)
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 100, true);
if (target)
DoCast(target, SpellIds.AmplifyDamage);
AmplifyDamageTimer = RandomHelper.URand(20000, 30000);
}
else AmplifyDamageTimer -= diff;
}
//Time for global and double timers
if (InfernalTimer <= diff)
{
SummonInfernal(diff);
InfernalTimer = phase == 3 ? 14500 : 44500u; // 15 secs in phase 3, 45 otherwise
}
else InfernalTimer -= diff;
if (ShadowNovaTimer <= diff)
{
DoCastVictim(SpellIds.Shadownova);
ShadowNovaTimer = phase == 3 ? 31000 : uint.MaxValue;
}
else ShadowNovaTimer -= diff;
if (phase != 2)
{
if (SWPainTimer <= diff)
{
Unit target;
if (phase == 1)
target = me.GetVictim(); // the tank
else // anyone but the tank
target = SelectTarget(SelectTargetMethod.Random, 1, 100, true);
if (target)
DoCast(target, SpellIds.SwPain);
SWPainTimer = 20000;
}
else SWPainTimer -= diff;
}
if (phase != 3)
{
if (EnfeebleTimer <= diff)
{
EnfeebleHealthEffect();
EnfeebleTimer = 30000;
ShadowNovaTimer = 5000;
EnfeebleResetTimer = 9000;
}
else EnfeebleTimer -= diff;
}
if (phase == 2)
DoMeleeAttacksIfReady();
else
DoMeleeAttackIfReady();
}
void DoMeleeAttacksIfReady()
{
if (me.IsWithinMeleeRange(me.GetVictim()) && !me.IsNonMeleeSpellCast(false))
{
//Check for base attack
if (me.IsAttackReady() && me.GetVictim())
{
me.AttackerStateUpdate(me.GetVictim());
me.ResetAttackTimer();
}
//Check for offhand attack
if (me.IsAttackReady(WeaponAttackType.OffAttack) && me.GetVictim())
{
me.AttackerStateUpdate(me.GetVictim(), WeaponAttackType.OffAttack);
me.ResetAttackTimer(WeaponAttackType.OffAttack);
}
}
}
public void Cleanup(Creature infernal, Vector2 point)
{
foreach (var guid in infernals)
{
if (guid == infernal.GetGUID())
{
infernals.Remove(guid);
break;
}
}
positions.Add(point);
}
}
}
@@ -0,0 +1,568 @@
/*
* Copyright (C) 2012-2020 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 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();
// 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)
{
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)
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)
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)
{
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)
{
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)
{
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 && !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;
}
if (ArcaneCooldown != 0 && FireCooldown != 0 && FrostCooldown != 0)
DoMeleeAttackIfReady();
}
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)
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 && 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);
}
}
}
@@ -0,0 +1,259 @@
/*
* Copyright (C) 2012-2020 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 Game.AI;
using Game.Entities;
using Game.Scripting;
using Game.Spells;
using System;
namespace Scripts.EasternKingdoms.Karazhan.TerestianIllhoof
{
struct SpellIds
{
public const uint ShadowBolt = 30055;
public const uint SummonImp = 30066;
public const uint FiendishPortal1 = 30171;
public const uint FiendishPortal2 = 30179;
public const uint Berserk = 32965;
public const uint SummonFiendishImp = 30184;
public const uint BrokenPact = 30065;
public const uint AmplifyFlames = 30053;
public const uint Firebolt = 30050;
public const uint SummonDemonchains = 30120;
public const uint DemonChains = 30206;
public const uint Sacrifice = 30115;
}
struct TextIds
{
public const uint SaySlay = 0;
public const uint SayDeath = 1;
public const uint SayAggro = 2;
public const uint SaySacrifice = 3;
public const uint SaySummonPortal = 4;
}
struct MiscConst
{
public const uint NpcFiendishPortal = 17265;
public const int ActionDespawnImps = 1;
}
[Script]
class boss_terestian : BossAI
{
public boss_terestian(Creature creature) : base(creature, DataTypes.Terestian) { }
public override void Reset()
{
EntryCheckPredicate pred = new(MiscConst.NpcFiendishPortal);
summons.DoAction(MiscConst.ActionDespawnImps, pred);
_Reset();
_scheduler.Schedule(TimeSpan.FromSeconds(1), task =>
{
Unit target = SelectTarget(SelectTargetMethod.MaxThreat, 0);
if (target)
DoCast(target, SpellIds.ShadowBolt);
task.Repeat(TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(10));
});
_scheduler.Schedule(TimeSpan.FromSeconds(3), task =>
{
me.RemoveAurasDueToSpell(SpellIds.BrokenPact);
DoCastAOE(SpellIds.SummonImp, new CastSpellExtraArgs(true));
});
_scheduler.Schedule(TimeSpan.FromSeconds(30), task =>
{
Unit target = SelectTarget(SelectTargetMethod.Random, 0, 100.0f, true);
if (target)
{
DoCast(target, SpellIds.Sacrifice, new CastSpellExtraArgs(true));
target.CastSpell(target, SpellIds.SummonDemonchains, true);
Talk(TextIds.SaySacrifice);
}
task.Repeat(TimeSpan.FromSeconds(42));
});
_scheduler.Schedule(TimeSpan.FromSeconds(10), task =>
{
Talk(TextIds.SaySummonPortal);
DoCastAOE(SpellIds.FiendishPortal1);
});
_scheduler.Schedule(TimeSpan.FromSeconds(11), task =>
{
DoCastAOE(SpellIds.FiendishPortal2, new CastSpellExtraArgs(true));
});
_scheduler.Schedule(TimeSpan.FromMinutes(10), task =>
{
DoCastSelf(SpellIds.Berserk, new CastSpellExtraArgs(true));
});
}
public override void JustEngagedWith(Unit who)
{
base.JustEngagedWith(who);
Talk(TextIds.SayAggro);
}
public override void SpellHit(WorldObject caster, SpellInfo spellInfo)
{
if (spellInfo.Id == SpellIds.BrokenPact)
{
_scheduler.Schedule(TimeSpan.FromSeconds(32), task =>
{
me.RemoveAurasDueToSpell(SpellIds.BrokenPact);
DoCastAOE(SpellIds.SummonImp, new CastSpellExtraArgs(true));
});
}
}
public override void KilledUnit(Unit victim)
{
if (victim.IsPlayer())
Talk(TextIds.SaySlay);
}
public override void JustDied(Unit killer)
{
Talk(TextIds.SayDeath);
EntryCheckPredicate pred = new(MiscConst.NpcFiendishPortal);
summons.DoAction(MiscConst.ActionDespawnImps, pred);
_JustDied();
}
public override void UpdateAI(uint diff)
{
_scheduler.Update(diff, () => DoMeleeAttackIfReady());
}
}
[Script]
class npc_kilrek : ScriptedAI
{
public npc_kilrek(Creature creature) : base(creature) { }
public override void Reset()
{
_scheduler.Schedule(TimeSpan.FromSeconds(8), task =>
{
DoCastVictim(SpellIds.AmplifyFlames);
task.Repeat(TimeSpan.FromSeconds(9));
});
}
public override void JustDied(Unit killer)
{
DoCastAOE(SpellIds.BrokenPact, new CastSpellExtraArgs(true));
me.DespawnOrUnsummon(TimeSpan.FromSeconds(15));
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
_scheduler.Update(diff, () =>
{
DoMeleeAttackIfReady();
});
}
}
[Script]
class npc_demon_chain : PassiveAI
{
ObjectGuid _sacrificeGUID;
public npc_demon_chain(Creature creature) : base(creature) { }
public override void IsSummonedBy(WorldObject summoner)
{
_sacrificeGUID = summoner.GetGUID();
DoCastSelf(SpellIds.DemonChains, new CastSpellExtraArgs(true));
}
public override void JustDied(Unit killer)
{
Unit sacrifice = Global.ObjAccessor.GetUnit(me, _sacrificeGUID);
if (sacrifice)
sacrifice.RemoveAurasDueToSpell(SpellIds.Sacrifice);
}
}
[Script]
class npc_fiendish_portal : PassiveAI
{
SummonList _summons;
public npc_fiendish_portal(Creature creature) : base(creature)
{
_summons = new(me);
}
public override void Reset()
{
_scheduler.Schedule(TimeSpan.FromMilliseconds(2400), TimeSpan.FromSeconds(8), task =>
{
DoCastAOE(SpellIds.SummonFiendishImp, new CastSpellExtraArgs(true));
task.Repeat();
});
}
public override void DoAction(int action)
{
if (action == MiscConst.ActionDespawnImps)
_summons.DespawnAll();
}
public override void JustSummoned(Creature summon)
{
_summons.Summon(summon);
DoZoneInCombat(summon);
}
public override void UpdateAI(uint diff)
{
_scheduler.Update(diff);
}
}
[Script]
class npc_fiendish_imp : ScriptedAI
{
public npc_fiendish_imp(Creature creature) : base(creature) { }
public override void Reset()
{
_scheduler.Schedule(TimeSpan.FromSeconds(2), task =>
{
DoCastVictim(SpellIds.Firebolt);
task.Repeat(TimeSpan.FromMilliseconds(2400));
});
me.ApplySpellImmune(0, SpellImmunity.School, SpellSchoolMask.Fire, true);
}
public override void UpdateAI(uint diff)
{
if (!UpdateVictim())
return;
_scheduler.Update(diff, () => DoMeleeAttackIfReady());
}
}
}