From 54d5d598987e840f623bb0dfff4b235c9ac7149a Mon Sep 17 00:00:00 2001 From: hondacrx Date: Mon, 4 Jul 2022 20:11:21 -0400 Subject: [PATCH] Start adding missing scripts Part1 --- Source/Game/AI/CoreAI/CreatureAI.cs | 2 +- .../GarothiWorldbreaker.cs | 870 ++++++++++++++++++ .../InstanceAntorusTheBurningThrone.cs | 117 +++ .../AlteracValley/AlteracValley.cs | 154 ++++ .../EasternKingdoms/AlteracValley/Balinda.cs | 162 ++++ .../EasternKingdoms/AlteracValley/Drekthar.cs | 112 +++ .../AlteracValley/Galvangar.cs | 113 +++ .../EasternKingdoms/AlteracValley/Vanndar.cs | 96 ++ 8 files changed, 1625 insertions(+), 1 deletion(-) create mode 100644 Source/Scripts/Argus/AntorusTheBurningThrone/GarothiWorldbreaker.cs create mode 100644 Source/Scripts/Argus/AntorusTheBurningThrone/InstanceAntorusTheBurningThrone.cs create mode 100644 Source/Scripts/EasternKingdoms/AlteracValley/AlteracValley.cs create mode 100644 Source/Scripts/EasternKingdoms/AlteracValley/Balinda.cs create mode 100644 Source/Scripts/EasternKingdoms/AlteracValley/Drekthar.cs create mode 100644 Source/Scripts/EasternKingdoms/AlteracValley/Galvangar.cs create mode 100644 Source/Scripts/EasternKingdoms/AlteracValley/Vanndar.cs diff --git a/Source/Game/AI/CoreAI/CreatureAI.cs b/Source/Game/AI/CoreAI/CreatureAI.cs index 82715d099..e727b2a40 100644 --- a/Source/Game/AI/CoreAI/CreatureAI.cs +++ b/Source/Game/AI/CoreAI/CreatureAI.cs @@ -405,7 +405,7 @@ namespace Game.AI return IsInBounds(_boundary, who) != _negateBoundary; } - public bool CheckInRoom() + public virtual bool CheckInRoom() { if (IsInBoundary()) return true; diff --git a/Source/Scripts/Argus/AntorusTheBurningThrone/GarothiWorldbreaker.cs b/Source/Scripts/Argus/AntorusTheBurningThrone/GarothiWorldbreaker.cs new file mode 100644 index 000000000..db0c4e68d --- /dev/null +++ b/Source/Scripts/Argus/AntorusTheBurningThrone/GarothiWorldbreaker.cs @@ -0,0 +1,870 @@ +/* + * Copyright (C) 2012-2020 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 . + */ + +using Game.Entities; +using Game.Scripting; +using Game.AI; +using Game.Maps; +using Game.Spells; +using System.Linq; +using System.Collections.Generic; +using Framework.Constants; +using System; + +namespace Scripts.Argus.AntorusTheBurningThrone.GarothiWorldbreaker +{ + struct TextIds + { + // Garothi Worldbreaker + public const uint SayAggro = 0; + public const uint SayDisengage = 1; + public const uint SayAnnounceApocalypseDrive = 2; + public const uint SayApocalypseDrive = 3; + public const uint SayAnnounceEradication = 4; + public const uint SayFinishApocalypseDrive = 5; + public const uint SayDecimation = 6; + public const uint SayAnnihilation = 7; + public const uint SayAnnounceFelBombardment = 8; + public const uint SaySlay = 9; + public const uint SayDeath = 10; + + // Decimator + public const uint SayAnnounceDecimation = 0; + } + + struct SpellIds + { + // Garothi Worldbreaker + public const uint Melee = 248229; + public const uint ApocalypseDrive = 244152; + public const uint ApocalypseDrivePeriodicDamage = 253300; + public const uint ApocalypseDriveFinalDamage = 240277; + public const uint Eradication = 244969; + public const uint Empowered = 245237; + public const uint RestoreHealth = 246012; + public const uint AnnihilatorCannonEject = 245527; + public const uint DecimatorCannonEject = 245515; + public const uint FelBombardmentSelector = 244150; + public const uint FelBombardmentWarning = 246220; + public const uint FelBombardmentDummy = 245219; + public const uint FelBombardmentPeriodic = 244536; + public const uint CannonChooser = 245124; + public const uint SearingBarrageAnnihilator = 246368; + public const uint SearingBarrageDecimator = 244395; + public const uint SearingBarrageDummyAnnihilator = 244398; + public const uint SearingBarrageDummyDecimator = 246369; + public const uint SearingBarrageSelector = 246360; + public const uint SearingBarrageDamageAnnihilator = 244400; + public const uint SearingBarrageDamageDecimator = 246373; + public const uint Carnage = 244106; + + // Decimator + public const uint DecimationSelector = 244399; + public const uint DecimationWarning = 244410; + public const uint DecimationCastVisual = 245338; + public const uint DecimationMissile = 244448; + + // Annihilator + public const uint AnnihilationSummon = 244790; + public const uint AnnihilationSelector = 247572; + public const uint AnnihilationDummy = 244294; + public const uint AnnihilationDamageUnsplitted = 244762; + + // Annihilation + public const uint AnnihilationAreaTrigger = 244795; + public const uint AnnihilationWarning = 244799; + + // Garothi Worldbreaker (Surging Fel) + public const uint SurgingFelAreaTrigger = 246655; + public const uint SurgingFelDamage = 246663; + } + + struct EventIds + { + // Garothi Worldbreaker + public const uint ReengagePlayers = 1; + public const uint FelBombardment= 2; + public const uint SearingBarrage= 3; + public const uint CannonChooser = 4; + public const uint SurgingFel = 5; + } + + struct MiscConst + { + public const uint MinTargetsSize = 2; + public const uint MaxTargetsSize = 6; + + public const byte SummonGroupIdSurgingFel = 0; + public const uint EncounterIdGarothiWorldbreaker = 2076; + public const ushort AnimKitIdCannonDestroyed = 13264; + public const uint DataLastFiredCannon = 0; + + public const uint MaxApocalypseDriveCount = 2; + public static Position AnnihilationCenterReferencePos = new(-3296.72f, 9767.78f, -60.0f); + + public static void PreferNonTankTargetsAndResizeTargets(List targets, Unit caster) + { + if (targets.Empty()) + return; + + List targetsCopy = targets; + byte size = (byte)targetsCopy.Count; + // Selecting our prefered target size based on total targets (min 10 player: 2, max 30 player: 6) + byte preferedSize = (byte)(Math.Min(Math.Max(MathF.Ceiling(size / 5), MiscConst.MinTargetsSize), MiscConst.MaxTargetsSize)); + + // Now we get rid of the tank as these abilities prefer non-tanks above tanks as long as there are alternatives + targetsCopy.RemoveAll(new VictimCheck(caster, false)); + + // We have less available nontank targets than we want, include tanks + if (targetsCopy.Count < preferedSize) + targets.RandomResize(preferedSize); + else + { + // Our target list has enough alternative targets, resize + targetsCopy.RandomResize(preferedSize); + targets.Clear(); + targets.AddRange(targetsCopy); + } + } + } + + [Script] + class boss_garothi_worldbreaker : BossAI + { + public boss_garothi_worldbreaker(Creature creature) : base(creature, DataTypes.GarothiWorldbreaker) + { + _lastCanonEntry = CreatureIds.Decimator; + SetCombatMovement(false); + me.SetReactState(ReactStates.Passive); + } + + public override void InitializeAI() + { + switch (GetDifficulty()) + { + case Difficulty.MythicRaid: + case Difficulty.HeroicRaid: + _apocalypseDriveHealthLimit[0] = 65; + _apocalypseDriveHealthLimit[1] = 35; + break; + case Difficulty.NormalRaid: + case Difficulty.LFRNew: + _apocalypseDriveHealthLimit[0] = 60; + _apocalypseDriveHealthLimit[1] = 20; + break; + default: + break; + } + } + + public override void JustAppeared() + { + me.SummonCreatureGroup(MiscConst.SummonGroupIdSurgingFel); + } + + public override void JustEngagedWith(Unit who) + { + me.SetReactState(ReactStates.Aggressive); + base.JustEngagedWith(who); + Talk(TextIds.SayAggro); + DoCastSelf(SpellIds.Melee); + instance.SendEncounterUnit(EncounterFrameType.Engage, me); + _events.ScheduleEvent(EventIds.FelBombardment, TimeSpan.FromSeconds(9)); + _events.ScheduleEvent(EventIds.CannonChooser, TimeSpan.FromSeconds(8)); + } + + public override void EnterEvadeMode(EvadeReason why) + { + Talk(TextIds.SayDisengage); + _EnterEvadeMode(); + instance.SendEncounterUnit(EncounterFrameType.Disengage, me); + _events.Reset(); + CleanupEncounter(); + _DespawnAtEvade(TimeSpan.FromSeconds(30)); + } + + public override void KilledUnit(Unit victim) + { + if (victim.IsPlayer()) + Talk(TextIds.SaySlay, victim); + } + + public override void JustDied(Unit killer) + { + _JustDied(); + Talk(TextIds.SayDeath); + CleanupEncounter(); + instance.SendBossKillCredit(MiscConst.EncounterIdGarothiWorldbreaker); + instance.SendEncounterUnit(EncounterFrameType.Disengage, me); + } + + public override void OnSpellCast(SpellInfo spell) + { + switch (spell.Id) + { + case SpellIds.ApocalypseDriveFinalDamage: + if (_apocalypseDriveCount < MiscConst.MaxApocalypseDriveCount) + _events.Reset(); + _events.ScheduleEvent(EventIds.ReengagePlayers, TimeSpan.FromSeconds(3.5)); + HideCannons(); + me.RemoveUnitFlag(UnitFlags.Uninteractible); + break; + default: + break; + } + } + + public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null) + { + if (me.HealthBelowPctDamaged(_apocalypseDriveHealthLimit[_apocalypseDriveCount], damage)) + { + me.AttackStop(); + me.SetReactState(ReactStates.Passive); + me.InterruptNonMeleeSpells(true); + me.SetFacingTo(me.GetHomePosition().GetOrientation()); + _events.Reset(); + + if (GetDifficulty() == Difficulty.MythicRaid || GetDifficulty() == Difficulty.HeroicRaid) + _events.ScheduleEvent(EventIds.SurgingFel, TimeSpan.FromSeconds(8)); + + DoCastSelf(SpellIds.ApocalypseDrive); + DoCastSelf(SpellIds.ApocalypseDriveFinalDamage); + Talk(TextIds.SayAnnounceApocalypseDrive); + Talk(TextIds.SayApocalypseDrive); + me.SetUnitFlag(UnitFlags.Uninteractible); + + Creature decimator = instance.GetCreature(DataTypes.Decimator); + if (decimator) + { + instance.SendEncounterUnit(EncounterFrameType.Engage, decimator, 2); + decimator.SetUnitFlag(UnitFlags.InCombat); + decimator.RemoveUnitFlag(UnitFlags.Uninteractible); + } + + Creature annihilator = instance.GetCreature(DataTypes.Annihilator); + if (annihilator) + { + instance.SendEncounterUnit(EncounterFrameType.Engage, annihilator, 2); + annihilator.SetUnitFlag(UnitFlags.InCombat); + annihilator.RemoveUnitFlag(UnitFlags.Uninteractible); + } + ++_apocalypseDriveCount; + } + } + + public override void JustSummoned(Creature summon) + { + summons.Summon(summon); + switch (summon.GetEntry()) + { + case CreatureIds.Annihilation: + summon.CastSpell(summon, SpellIds.AnnihilationWarning); + summon.CastSpell(summon, SpellIds.AnnihilationAreaTrigger); + break; + case CreatureIds.Annihilator: + case CreatureIds.Decimator: + summon.SetReactState(ReactStates.Passive); + break; + case CreatureIds.GarothiWorldbreaker: + _surgingFelDummyGuids.Add(summon.GetGUID()); + break; + default: + break; + } + } + + public override void SummonedCreatureDies(Creature summon, Unit killer) + { + switch (summon.GetEntry()) + { + case CreatureIds.Decimator: + case CreatureIds.Annihilator: + me.InterruptNonMeleeSpells(true); + me.RemoveAurasDueToSpell(SpellIds.ApocalypseDrive); + me.RemoveUnitFlag(UnitFlags.Uninteractible); + + if (summon.GetEntry() == CreatureIds.Annihilator) + _searingBarrageSpellId = SpellIds.SearingBarrageAnnihilator; + else + _searingBarrageSpellId = SpellIds.SearingBarrageDecimator; + + if (_apocalypseDriveCount < MiscConst.MaxApocalypseDriveCount) + _events.Reset(); + + _events.ScheduleEvent(EventIds.SearingBarrage, TimeSpan.FromSeconds(3.5)); + _events.ScheduleEvent(EventIds.ReengagePlayers, TimeSpan.FromSeconds(3.5)); + _castEradication = true; + + if (summon.GetEntry() == CreatureIds.Decimator) + DoCastSelf(SpellIds.DecimatorCannonEject); + else + DoCastSelf(SpellIds.AnnihilatorCannonEject); + + me.PlayOneShotAnimKitId(MiscConst.AnimKitIdCannonDestroyed); + HideCannons(); + break; + default: + break; + } + } + + public override uint GetData(uint type) + { + if (type == MiscConst.DataLastFiredCannon) + return _lastCanonEntry; + + return 0; + } + + public override void SetData(uint type, uint value) + { + if (type == MiscConst.DataLastFiredCannon) + _lastCanonEntry = value; + } + + public override void UpdateAI(uint diff) + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + if (me.HasUnitState(UnitState.Casting) && !me.HasAura(SpellIds.ApocalypseDrive)) + return; + + _events.ExecuteEvents(eventId => + { + switch (eventId) + { + case EventIds.ReengagePlayers: + DoCastSelf(SpellIds.Empowered); + DoCastSelf(SpellIds.RestoreHealth); + if (_castEradication) + { + DoCastSelf(SpellIds.Eradication); + Talk(TextIds.SayAnnounceEradication); + Talk(TextIds.SayFinishApocalypseDrive); + _castEradication = false; + } + me.SetReactState(ReactStates.Aggressive); + _events.ScheduleEvent(EventIds.FelBombardment, TimeSpan.FromSeconds(20)); + _events.ScheduleEvent(EventIds.CannonChooser, TimeSpan.FromSeconds(18)); + break; + case EventIds.FelBombardment: + DoCastAOE(SpellIds.FelBombardmentSelector); + _events.Repeat(TimeSpan.FromSeconds(20)); + break; + case EventIds.SearingBarrage: + DoCastSelf(_searingBarrageSpellId); + break; + case EventIds.CannonChooser: + DoCastSelf(SpellIds.CannonChooser); + _events.Repeat(TimeSpan.FromSeconds(16)); + break; + case EventIds.SurgingFel: + { + var guids = _surgingFelDummyGuids; + guids.Remove(_lastSurgingFelDummyGuid); + _lastSurgingFelDummyGuid = guids.SelectRandom(); + Creature dummy = ObjectAccessor.GetCreature(me, _lastSurgingFelDummyGuid); + if (dummy) + dummy.CastSpell(dummy, SpellIds.SurgingFelAreaTrigger); + + _events.Repeat(TimeSpan.FromSeconds(8)); + break; + } + default: + break; + } + }); + + if (me.GetVictim() && me.GetVictim().IsWithinMeleeRange(me)) + DoMeleeAttackIfReady(); + else + DoSpellAttackIfReady(SpellIds.Carnage); + } + + byte[] _apocalypseDriveHealthLimit = new byte[MiscConst.MaxApocalypseDriveCount]; + byte _apocalypseDriveCount; + uint _searingBarrageSpellId; + uint _lastCanonEntry; + bool _castEradication; + ObjectGuid _lastSurgingFelDummyGuid; + List _surgingFelDummyGuids; + + void CleanupEncounter() + { + Creature decimator = instance.GetCreature(DataTypes.Decimator); + if (decimator) + instance.SendEncounterUnit(EncounterFrameType.Disengage, decimator); + + Creature annihilator = instance.GetCreature(DataTypes.Annihilator); + if (annihilator) + instance.SendEncounterUnit(EncounterFrameType.Disengage, annihilator); + + instance.DoRemoveAurasDueToSpellOnPlayers(SpellIds.DecimationWarning); + instance.DoRemoveAurasDueToSpellOnPlayers(SpellIds.FelBombardmentWarning); + instance.DoRemoveAurasDueToSpellOnPlayers(SpellIds.FelBombardmentPeriodic); + summons.DespawnAll(); + } + + void HideCannons() + { + Creature decimator = instance.GetCreature(DataTypes.Decimator); + if (decimator) + { + instance.SendEncounterUnit(EncounterFrameType.Disengage, decimator); + decimator.SetUnitFlag(UnitFlags.Uninteractible | UnitFlags.Immune); + } + + Creature annihilator = instance.GetCreature(DataTypes.Annihilator); + if (annihilator) + { + instance.SendEncounterUnit(EncounterFrameType.Disengage, annihilator); + annihilator.SetUnitFlag(UnitFlags.Uninteractible | UnitFlags.Immune); + } + } + } + + [Script] + class at_garothi_annihilation : AreaTriggerAI + { + public at_garothi_annihilation(AreaTrigger areatrigger) : base(areatrigger) + { + Initialize(); + } + + void Initialize() + { + _playerCount = 0; + } + + public override void OnUnitEnter(Unit unit) + { + if (!unit.IsPlayer()) + return; + + _playerCount++; + + Unit annihilation = at.GetCaster(); + if (annihilation) + annihilation.RemoveAurasDueToSpell(SpellIds.AnnihilationWarning); + } + + public override void OnUnitExit(Unit unit) + { + if (!unit.IsPlayer()) + return; + + _playerCount--; + + if (_playerCount == 0 && !at.IsRemoved()) + { + Unit annihilation = at.GetCaster(); + if (annihilation != null) + annihilation.CastSpell(annihilation, SpellIds.AnnihilationWarning); + } + } + + byte _playerCount; + } + + [Script] + class spell_garothi_apocalypse_drive : AuraScript + { + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.ApocalypseDrivePeriodicDamage); + } + + void HandlePeriodic(AuraEffect aurEff) + { + GetTarget().CastSpell(GetTarget(), SpellIds.ApocalypseDrivePeriodicDamage, new CastSpellExtraArgs(aurEff)); + } + + public override void Register() + { + OnEffectPeriodic.Add(new EffectPeriodicHandler(HandlePeriodic, 1, AuraType.PeriodicDummy)); + } + } + + [Script] + class spell_garothi_fel_bombardment_selector : SpellScript + { + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.FelBombardmentWarning, SpellIds.FelBombardmentDummy); + } + + void FilterTargets(List targets) + { + if (targets.Empty()) + return; + + Unit caster = GetCaster(); + if (caster) + targets.RemoveAll(new VictimCheck(caster, true)); + } + + void HandleWarningEffect(uint effIndex) + { + Creature caster = GetCaster() ? GetCaster().ToCreature() : null; + if (!caster || !caster.IsAIEnabled()) + return; + + Unit target = GetHitUnit(); + caster.GetAI().Talk(TextIds.SayAnnounceFelBombardment, target); + caster.CastSpell(target, SpellIds.FelBombardmentWarning, true); + caster.CastSpell(target, SpellIds.FelBombardmentDummy, true); + } + + public override void Register() + { + OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 0, Targets.UnitSrcAreaEnemy)); + OnEffectHitTarget.Add(new EffectHandler(HandleWarningEffect, 0, SpellEffectName.Dummy)); + } + } + + [Script] + class spell_garothi_fel_bombardment_warning : AuraScript + { + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.FelBombardmentPeriodic); + } + + void AfterRemove(AuraEffect aurEff, AuraEffectHandleModes mode) + { + if (GetTargetApplication().GetRemoveMode() == AuraRemoveMode.Expire) + { + Unit caster = GetCaster(); + if (caster) + caster.CastSpell(GetTarget(), SpellIds.FelBombardmentPeriodic, true); + } + } + + public override void Register() + { + AfterEffectRemove.Add(new EffectApplyHandler(AfterRemove, 0, AuraType.Dummy, AuraEffectHandleModes.Real)); + } + } + + [Script] + class spell_garothi_fel_bombardment_periodic : AuraScript + { + public override bool Validate(SpellInfo spellInfo) + { + return !spellInfo.GetEffects().Empty() && ValidateSpellInfo((uint)spellInfo.GetEffect(0).CalcValue()); + } + + void HandlePeriodic(AuraEffect aurEff) + { + Unit caster = GetCaster(); + if (caster) + caster.CastSpell(GetTarget(), (uint)aurEff.GetSpellEffectInfo().CalcValue(caster), true); + } + + public override void Register() + { + OnEffectPeriodic.Add(new EffectPeriodicHandler(HandlePeriodic, 0, AuraType.PeriodicTriggerSpell)); + } + } + + [Script] + class spell_garothi_searing_barrage_dummy : SpellScript + { + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.SearingBarrageSelector); + } + + void HandleHit(uint effIndex) + { + GetHitUnit().CastSpell(GetHitUnit(), SpellIds.SearingBarrageSelector, new CastSpellExtraArgs(TriggerCastFlags.FullMask).AddSpellMod(SpellValueMod.BasePoint0, (int)GetSpellInfo().Id)); + } + + public override void Register() + { + OnEffectHitTarget.Add(new EffectHandler(HandleHit, 0, SpellEffectName.Dummy)); + } + } + + [Script] + class spell_garothi_searing_barrage_selector : SpellScript + { + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.SearingBarrageDamageAnnihilator, SpellIds.SearingBarrageDamageDecimator, SpellIds.SearingBarrageDummyAnnihilator, SpellIds.SearingBarrageDummyDecimator); + } + + void FilterTargets(List targets) + { + MiscConst.PreferNonTankTargetsAndResizeTargets(targets, GetCaster()); + } + + void HandleHit(uint effIndex) + { + uint spellId = GetEffectValue() == SpellIds.SearingBarrageDummyAnnihilator ? SpellIds.SearingBarrageDamageAnnihilator : SpellIds.SearingBarrageDamageDecimator; + Unit caster = GetCaster(); + if (caster) + caster.CastSpell(GetHitUnit(), spellId, true); + } + + public override void Register() + { + OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 0, Targets.UnitSrcAreaEntry)); + OnEffectHitTarget.Add(new EffectHandler(HandleHit, 0, SpellEffectName.Dummy)); + } + } + + [Script] + class spell_garothi_decimation_selector : SpellScript + { + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.DecimationWarning); + } + + void FilterTargets(List targets) + { + MiscConst.PreferNonTankTargetsAndResizeTargets(targets, GetCaster()); + } + + void HandleHit(uint effIndex) + { + Unit caster = GetCaster(); + if (caster) + { + caster.CastSpell(GetHitUnit(), SpellIds.DecimationWarning, true); + Creature decimator = caster.ToCreature(); + if (decimator) + if (decimator.IsAIEnabled()) + decimator.GetAI().Talk(TextIds.SayAnnounceDecimation, GetHitUnit()); + } + } + + public override void Register() + { + OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 0, Targets.UnitSrcAreaEnemy)); + OnEffectHitTarget.Add(new EffectHandler(HandleHit, 0, SpellEffectName.Dummy)); + } + } + + [Script] + class spell_garothi_decimation_warning : AuraScript + { + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.DecimationMissile); + } + + void AfterRemove(AuraEffect aurEff, AuraEffectHandleModes mode) + { + if (GetTargetApplication().GetRemoveMode() == AuraRemoveMode.Expire) + { + Unit caster = GetCaster(); + if (caster) + { + caster.CastSpell(GetTarget(), SpellIds.DecimationMissile, true); + if (!caster.HasUnitState(UnitState.Casting)) + caster.CastSpell(caster, SpellIds.DecimationCastVisual); + } + } + } + + public override void Register() + { + AfterEffectRemove.Add(new EffectApplyHandler(AfterRemove, 0, AuraType.Dummy, AuraEffectHandleModes.Real)); + } + } + + [Script] + class spell_garothi_carnage : AuraScript + { + void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo) + { + // Usually we could just handle this via spell_proc but since we want + // to silence the console message because it's not a spell trigger proc, we need a script here. + PreventDefaultAction(); + Remove(); + } + + public override void Register() + { + OnEffectProc.Add(new EffectProcHandler(HandleProc, 0, AuraType.PeriodicTriggerSpell)); + } + } + + [Script] + class spell_garothi_annihilation_selector : SpellScript + { + public override bool Validate(SpellInfo spellInfo) + { + return !spellInfo.GetEffects().Empty() && ValidateSpellInfo((uint)spellInfo.GetEffect(0).CalcValue()); + } + + void HandleHit(uint effIndex) + { + Unit caster = GetCaster(); + if (caster) + caster.CastSpell(GetHitUnit(), (uint)GetEffectInfo().CalcValue(caster), true); + } + + public override void Register() + { + OnEffectHitTarget.Add(new EffectHandler(HandleHit, 0, SpellEffectName.Dummy)); + } + } + + [Script] + class spell_garothi_annihilation_triggered : SpellScript + { + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.AnnihilationDamageUnsplitted); + } + + void HandleHit(uint effIndex) + { + Unit target = GetHitUnit(); + if (target.HasAura(SpellIds.AnnihilationWarning)) + target.CastSpell(target, SpellIds.AnnihilationDamageUnsplitted, true); + + target.RemoveAllAuras(); + } + + public override void Register() + { + OnEffectHitTarget.Add(new EffectHandler(HandleHit, 1, SpellEffectName.Dummy)); + } + } + + [Script] + class spell_garothi_eradication : SpellScript + { + void ChangeDamage() + { + Unit caster = GetCaster(); + if (caster) + { + uint damageReduction = (uint)MathFunctions.CalculatePct(GetHitDamage(), GetHitUnit().GetDistance(caster)); + SetHitDamage((int)(GetHitDamage() - damageReduction)); + } + } + + public override void Register() + { + OnHit.Add(new HitHandler(ChangeDamage)); + } + } + + [Script] + class spell_garothi_surging_fel : AuraScript + { + public override bool Validate(SpellInfo spellInfo) + { + return ValidateSpellInfo(SpellIds.SurgingFelDamage); + } + + void AfterRemove(AuraEffect aurEff, AuraEffectHandleModes mode) + { + if (GetTargetApplication().GetRemoveMode() == AuraRemoveMode.Expire) + GetTarget().CastSpell(GetTarget(), SpellIds.SurgingFelDamage, true); + } + + public override void Register() + { + AfterEffectRemove.Add(new EffectApplyHandler(AfterRemove, 0, AuraType.AreaTrigger, AuraEffectHandleModes.Real)); + } + } + + [Script] + class spell_garothi_cannon_chooser : SpellScript + { + void HandleDummyEffect(uint effIndex) + { + Creature caster = GetHitCreature(); + if (!caster || !caster.IsAIEnabled()) + return; + + InstanceScript instance = caster.GetInstanceScript(); + if (instance == null) + return; + + Creature decimator = instance.GetCreature(DataTypes.Decimator); + Creature annihilator = instance.GetCreature(DataTypes.Annihilator); + uint lastCannonEntry = caster.GetAI().GetData(MiscConst.DataLastFiredCannon); + + if ((lastCannonEntry == CreatureIds.Annihilator && decimator) || (decimator && !annihilator)) + { + decimator.CastSpell(decimator, SpellIds.DecimationSelector, true); + caster.GetAI().Talk(TextIds.SayDecimation, decimator); + lastCannonEntry = CreatureIds.Decimator; + } + else if ((lastCannonEntry == CreatureIds.Decimator && annihilator) || (annihilator && !decimator)) + { + byte count = (byte)(caster.GetMap().GetDifficultyID() == Difficulty.MythicRaid ? MiscConst.MaxTargetsSize : + Math.Max(MiscConst.MinTargetsSize, Math.Ceiling((float)caster.GetMap().GetPlayersCountExceptGMs() / 5))); + + for (byte i = 0; i < count; i++) + { + float x = MiscConst.AnnihilationCenterReferencePos.GetPositionX() + MathF.Cos(RandomHelper.FRand(0.0f, MathF.PI * 2)) * RandomHelper.FRand(15.0f, 30.0f); + float y = MiscConst.AnnihilationCenterReferencePos.GetPositionY() + MathF.Sin(RandomHelper.FRand(0.0f, MathF.PI * 2)) * RandomHelper.FRand(15.0f, 30.0f); + float z = caster.GetMap().GetHeight(caster.GetPhaseShift(), x, y, MiscConst.AnnihilationCenterReferencePos.GetPositionZ()); + annihilator.CastSpell(new Position(x, y, z), SpellIds.AnnihilationSummon, new CastSpellExtraArgs(true)); + } + + annihilator.CastSpell(annihilator, SpellIds.AnnihilationDummy); + annihilator.CastSpell(annihilator, SpellIds.AnnihilationSelector); + caster.GetAI().Talk(TextIds.SayAnnihilation); + lastCannonEntry = CreatureIds.Annihilator; + } + + caster.GetAI().SetData(MiscConst.DataLastFiredCannon, lastCannonEntry); + } + + public override void Register() + { + OnEffectHitTarget.Add(new EffectHandler(HandleDummyEffect, 0, SpellEffectName.Dummy)); + } + } + + class VictimCheck : ICheck + { + public VictimCheck(Unit caster, bool keepTank) + { + _caster = caster; + _keepTank = keepTank; + } + + public bool Invoke(WorldObject obj) + { + Unit unit = obj.ToUnit(); + if (!unit) + return true; + + if (_caster.GetVictim() && _caster.GetVictim() != unit) + return _keepTank; + + return false; + } + + Unit _caster; + bool _keepTank; // true = remove all nontank targets | false = remove current tank + } +} + diff --git a/Source/Scripts/Argus/AntorusTheBurningThrone/InstanceAntorusTheBurningThrone.cs b/Source/Scripts/Argus/AntorusTheBurningThrone/InstanceAntorusTheBurningThrone.cs new file mode 100644 index 000000000..ac4ccf544 --- /dev/null +++ b/Source/Scripts/Argus/AntorusTheBurningThrone/InstanceAntorusTheBurningThrone.cs @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012-2020 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 . + */ +using Game.Maps; +using Game.Scripting; +using Game.Entities; +using Framework.Constants; + +namespace Scripts.Argus.AntorusTheBurningThrone +{ + struct DataTypes + { + // Encounters + public const uint GarothiWorldbreaker = 0; + public const uint FelhoundsOfSageras = 1; + public const uint AntoranHighCommand = 2; + public const uint PortalKeeperHasabel = 3; + public const uint EonarTheLifeBinder = 4; + public const uint ImonarTheSoulhunter = 5; + public const uint Kingaroth = 6; + public const uint Varimathras = 7; + public const uint TheCovenOfShivarra = 8; + public const uint Aggramar = 9; + public const uint ArgusTheUnmaker = 10; + + // Encounter related data + public const uint Decimator = 11; + public const uint Annihilator = 12; + } + + struct BossIds + { + // Bosses + public const uint GarothiWorldbreaker = 122450; + public const uint EncounterCount = 10; + } + + struct CreatureIds + { + // Garothi Worldbreaker + public const uint Decimator = 122773; + public const uint Annihilator = 122778; + public const uint Annihilation = 122818; + public const uint GarothiWorldbreaker = 124167; + } + + struct GameObjectIds + { + public const uint Collision = 277365; + public const uint Rock = 278488; + } + + [Script] + class instance_antorus_the_burning_throne : InstanceMapScript + { + static ObjectData[] creatureData = + { + new ObjectData(BossIds.GarothiWorldbreaker, DataTypes.GarothiWorldbreaker), + new ObjectData(CreatureIds.Decimator, DataTypes.Decimator), + new ObjectData(CreatureIds.Annihilator, DataTypes.Annihilator) + }; + + static DoorData[] doorData = + { + new DoorData(GameObjectIds.Collision, DataTypes.GarothiWorldbreaker, DoorType.Passage), + new DoorData(GameObjectIds.Rock, DataTypes.GarothiWorldbreaker, DoorType.Passage) + }; + + public instance_antorus_the_burning_throne() : base("instance_antorus_the_burning_throne", 757) { } + + class instance_antorus_the_burning_throne_InstanceMapScript : InstanceScript + { + public instance_antorus_the_burning_throne_InstanceMapScript(InstanceMap map) : base(map) + { + SetHeaders("ABT"); + SetBossNumber(BossIds.EncounterCount); + LoadObjectData(creatureData, null); + LoadDoorData(doorData); + } + + public override void OnCreatureCreate(Creature creature) + { + base.OnCreatureCreate(creature); + + switch (creature.GetEntry()) + { + case CreatureIds.Annihilation: + Creature garothi = GetCreature(DataTypes.GarothiWorldbreaker); + if (garothi) + garothi.GetAI().JustSummoned(creature); + break; + default: + break; + } + } + } + + public override InstanceScript GetInstanceScript(InstanceMap map) + { + return new instance_antorus_the_burning_throne_InstanceMapScript(map); + } + } +} + diff --git a/Source/Scripts/EasternKingdoms/AlteracValley/AlteracValley.cs b/Source/Scripts/EasternKingdoms/AlteracValley/AlteracValley.cs new file mode 100644 index 000000000..ea8b15400 --- /dev/null +++ b/Source/Scripts/EasternKingdoms/AlteracValley/AlteracValley.cs @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012-2020 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 . + */ + +using Framework.Constants; +using Game.AI; +using Game.Entities; +using Game.Scripting; +using System; + +namespace Scripts.EasternKingdoms.AlteracValley +{ + struct SpellIds + { + public const uint Charge = 22911; + public const uint Cleave = 40504; + public const uint DemoralizingShout = 23511; + public const uint Enrage = 8599; + public const uint Whirlwind = 13736; + + public const uint NorthMarshal = 45828; + public const uint SouthMarshal = 45829; + public const uint StonehearthMarshal = 45830; + public const uint IcewingMarshal = 45831; + public const uint IcebloodWarmaster = 45822; + public const uint TowerPointWarmaster = 45823; + public const uint WestFrostwolfWarmaster = 45824; + public const uint EastFrostwolfWarmaster = 45826; + } + + struct CreatureIds + { + public const uint NorthMarshal = 14762; + public const uint SouthMarshal = 14763; + public const uint IcewingMarshal = 14764; + public const uint StonehearthMarshal = 14765; + public const uint EastFrostwolfWarmaster = 14772; + public const uint IcebloodWarmaster = 14773; + public const uint TowerPointWarmaster = 14776; + public const uint WestFrostwolfWarmaster = 14777; + } + + [Script] + class npc_av_marshal_or_warmaster : ScriptedAI + { + (uint npcEntry, uint spellId)[] _auraPairs = + { + new (CreatureIds.NorthMarshal, SpellIds.NorthMarshal), + new (CreatureIds.SouthMarshal, SpellIds.SouthMarshal), + new (CreatureIds.StonehearthMarshal, SpellIds.StonehearthMarshal), + new (CreatureIds.IcewingMarshal, SpellIds.IcewingMarshal), + new (CreatureIds.EastFrostwolfWarmaster, SpellIds.EastFrostwolfWarmaster), + new (CreatureIds.WestFrostwolfWarmaster, SpellIds.WestFrostwolfWarmaster), + new (CreatureIds.TowerPointWarmaster, SpellIds.TowerPointWarmaster), + new (CreatureIds.IcebloodWarmaster, SpellIds.IcebloodWarmaster) + }; + + bool _hasAura; + + public npc_av_marshal_or_warmaster(Creature creature) : base(creature) + { + Initialize(); + } + + void Initialize() + { + _hasAura = false; + } + + public override void Reset() + { + Initialize(); + + _scheduler.CancelAll(); + _scheduler.Schedule(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(12), task => + { + DoCastVictim(SpellIds.Charge); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(25)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(11), task => + { + DoCastVictim(SpellIds.Cleave); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(16)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(2), task => + { + DoCast(me, SpellIds.DemoralizingShout); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(15)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(20), task => + { + DoCast(me, SpellIds.Whirlwind); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(25)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(20), task => + { + DoCast(me, SpellIds.Enrage); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(5), task => + { + Position _homePosition = me.GetHomePosition(); + if (me.GetDistance2d(_homePosition.GetPositionX(), _homePosition.GetPositionY()) > 50.0f) + { + EnterEvadeMode(); + return; + } + task.Repeat(TimeSpan.FromSeconds(5)); + }); + } + + public override void JustAppeared() + { + Reset(); + } + + public override void UpdateAI(uint diff) + { + // I have a feeling this isn't blizzlike, but owell, I'm only passing by and cleaning up. + if (!_hasAura) + { + for (byte i = 0; i < _auraPairs.Length; ++i) + if (_auraPairs[i].npcEntry == me.GetEntry()) + DoCast(me, _auraPairs[i].spellId); + + _hasAura = true; + } + + if (!UpdateVictim()) + return; + + _scheduler.Update(diff); + + if (me.HasUnitState(UnitState.Casting)) + return; + + DoMeleeAttackIfReady(); + } + } +} + diff --git a/Source/Scripts/EasternKingdoms/AlteracValley/Balinda.cs b/Source/Scripts/EasternKingdoms/AlteracValley/Balinda.cs new file mode 100644 index 000000000..23758e66e --- /dev/null +++ b/Source/Scripts/EasternKingdoms/AlteracValley/Balinda.cs @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2012-2020 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 . + */ + +using Framework.Constants; +using Game.AI; +using Game.Entities; +using Game.Scripting; +using Game.Spells; +using System; +using System.Collections.Generic; + +namespace Scripts.EasternKingdoms.AlteracValley.Balinda +{ + struct SpellIds + { + public const uint ArcaneExplosion = 46608; + public const uint ConeOfCold = 38384; + public const uint Fireball = 46988; + public const uint Frostbolt = 46987; + public const uint SummonWaterElemental = 45067; + public const uint Iceblock = 46604; + } + + struct TextIds + { + public const uint SayAggro = 0; + public const uint SayEvade = 1; + public const uint SaySalvation = 2; + } + + struct ActionIds + { + public const int BuffYell = -30001; // shared from Battleground + } + + [Script] + class boss_balinda : ScriptedAI + { + SummonList summons; + ObjectGuid WaterElementalGUID; + bool HasCastIceblock; + + public boss_balinda(Creature creature) : base(creature) + { + summons = new(me); + Initialize(); + } + + void Initialize() + { + WaterElementalGUID.Clear(); + HasCastIceblock = false; + } + + public override void Reset() + { + Initialize(); + _scheduler.CancelAll(); + summons.DespawnAll(); + } + + public override void JustEngagedWith(Unit who) + { + Talk(TextIds.SayAggro); + _scheduler.Schedule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(15), task => + { + DoCastVictim(SpellIds.ArcaneExplosion); + task.Repeat(); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(8), task => + { + DoCastVictim(SpellIds.ConeOfCold); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(20)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(1), task => + { + DoCastVictim(SpellIds.Fireball); + task.Repeat(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(9)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(4), task => + { + DoCastVictim(SpellIds.Frostbolt); + task.Repeat(TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(12)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(3), task => + { + if (summons.Empty()) + DoCast(SpellIds.SummonWaterElemental); + task.Repeat(TimeSpan.FromSeconds(50)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(5), task => + { + if (me.GetDistance2d(me.GetHomePosition().GetPositionX(), me.GetHomePosition().GetPositionY()) > 50) + { + EnterEvadeMode(); + Talk(TextIds.SayEvade); + } + Creature elemental = ObjectAccessor.GetCreature(me, WaterElementalGUID); + if (elemental != null) + if (elemental.GetDistance2d(me.GetHomePosition().GetPositionX(), me.GetHomePosition().GetPositionY()) > 50) + elemental.GetAI().EnterEvadeMode(); + task.Repeat(); + }); + } + + public override void JustSummoned(Creature summoned) + { + summoned.GetAI().AttackStart(SelectTarget(SelectTargetMethod.Random, 0, 50, true)); + summoned.SetFaction(me.GetFaction()); + WaterElementalGUID = summoned.GetGUID(); + summons.Summon(summoned); + } + + public override void SummonedCreatureDespawn(Creature summoned) + { + summons.Despawn(summoned); + } + + public override void JustDied(Unit killer) + { + summons.DespawnAll(); + } + + public override void DoAction(int actionId) + { + if (actionId == ActionIds.BuffYell) + Talk(TextIds.SayAggro); + } + + public override void DamageTaken(Unit attacker, ref uint damage, DamageEffectType damageType, SpellInfo spellInfo = null) + { + if (me.HealthBelowPctDamaged(40, damage) && !HasCastIceblock) + { + DoCast(SpellIds.Iceblock); + HasCastIceblock = true; + } + } + + public override void UpdateAI(uint diff) + { + if (!UpdateVictim()) + return; + + _scheduler.Update(diff, () => DoMeleeAttackIfReady()); + } + } +} + diff --git a/Source/Scripts/EasternKingdoms/AlteracValley/Drekthar.cs b/Source/Scripts/EasternKingdoms/AlteracValley/Drekthar.cs new file mode 100644 index 000000000..06ecc26a8 --- /dev/null +++ b/Source/Scripts/EasternKingdoms/AlteracValley/Drekthar.cs @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012-2020 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 . + */ + +using Game.AI; +using Game.Entities; +using Game.Scripting; +using System; + +namespace Scripts.EasternKingdoms.AlteracValley.Drekthar +{ + struct SpellIds + { + public const uint Whirlwind = 15589; + public const uint Whirlwind2 = 13736; + public const uint Knockdown = 19128; + public const uint Frenzy = 8269; + public const uint SweepingStrikes = 18765; // not sure + public const uint Cleave = 20677; // not sure + public const uint Windfury = 35886; // not sure + public const uint Stormpike = 51876; // not sure + } + + struct TextIds + { + public const uint SayAggro = 0; + public const uint SayEvade = 1; + public const uint SayRespawn = 2; + public const uint SayRandom = 3; + } + + [Script] + class boss_drekthar : ScriptedAI + { + public boss_drekthar(Creature creature) : base(creature) { } + + public override void Reset() + { + _scheduler.CancelAll(); + } + + public override void JustEngagedWith(Unit who) + { + Talk(TextIds.SayAggro); + _scheduler.Schedule(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(20), task => + { + DoCastVictim(SpellIds.Whirlwind); + task.Repeat(TimeSpan.FromSeconds(8), TimeSpan.FromSeconds(18)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(20), task => + { + DoCastVictim(SpellIds.Whirlwind2); + task.Repeat(TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(25)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(12), task => + { + DoCastVictim(SpellIds.Knockdown); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(15)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(6), task => + { + DoCastVictim(SpellIds.Frenzy); + task.Repeat(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(30)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(30), task => + { + Talk(TextIds.SayRandom); + task.Repeat(); + }); + } + + public override void JustAppeared() + { + Reset(); + Talk(TextIds.SayRespawn); + } + + public override bool CheckInRoom() + { + if (me.GetDistance2d(me.GetHomePosition().GetPositionX(), me.GetHomePosition().GetPositionY()) > 50) + { + EnterEvadeMode(); + Talk(TextIds.SayEvade); + return false; + } + + return true; + } + + public override void UpdateAI(uint diff) + { + if (!UpdateVictim() || !CheckInRoom()) + return; + + _scheduler.Update(diff, () => DoMeleeAttackIfReady()); + } + } +} + diff --git a/Source/Scripts/EasternKingdoms/AlteracValley/Galvangar.cs b/Source/Scripts/EasternKingdoms/AlteracValley/Galvangar.cs new file mode 100644 index 000000000..362dc0cc7 --- /dev/null +++ b/Source/Scripts/EasternKingdoms/AlteracValley/Galvangar.cs @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012-2020 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 . + */ + +using Game.AI; +using Game.Entities; +using Game.Scripting; +using System; + +namespace Scripts.EasternKingdoms.AlteracValley.Galvangar +{ + struct SpellIds + { + public const uint Cleave = 15284; + public const uint FrighteningShout = 19134; + public const uint Whirlwind1 = 15589; + public const uint Whirlwind2 = 13736; + public const uint MortalStrike = 16856; + } + + struct TextIds + { + public const uint SayAggro = 0; + public const uint SayEvade = 1; + public const uint SayBuff = 2; + } + + struct ActionIds + { + public const int BuffYell = -30001; // shared from Battleground + } + + [Script] + class boss_galvangar : ScriptedAI + { + public boss_galvangar(Creature creature) : base(creature) { } + + public override void Reset() + { + _scheduler.CancelAll(); + } + + public override void JustEngagedWith(Unit who) + { + Talk(TextIds.SayAggro); + _scheduler.Schedule(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(9), task => + { + DoCastVictim(SpellIds.Cleave); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(16)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(19), task => + { + DoCastVictim(SpellIds.FrighteningShout); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(15)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(13), task => + { + DoCastVictim(SpellIds.Whirlwind1); + task.Repeat(TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(10)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(20), task => + { + DoCastVictim(SpellIds.Whirlwind2); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(25)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(20), task => + { + DoCastVictim(SpellIds.MortalStrike); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30)); + }); + } + + public override void DoAction(int actionId) + { + if (actionId == ActionIds.BuffYell) + Talk(TextIds.SayBuff); + } + + public override bool CheckInRoom() + { + if (me.GetDistance2d(me.GetHomePosition().GetPositionX(), me.GetHomePosition().GetPositionY()) > 50) + { + EnterEvadeMode(); + Talk(TextIds.SayEvade); + return false; + } + + return true; + } + + public override void UpdateAI(uint diff) + { + if (!UpdateVictim() || !CheckInRoom()) + return; + + _scheduler.Update(diff, () => DoMeleeAttackIfReady()); + } + } +} + diff --git a/Source/Scripts/EasternKingdoms/AlteracValley/Vanndar.cs b/Source/Scripts/EasternKingdoms/AlteracValley/Vanndar.cs new file mode 100644 index 000000000..976cfa508 --- /dev/null +++ b/Source/Scripts/EasternKingdoms/AlteracValley/Vanndar.cs @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2012-2020 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 . + */ + +using Game.AI; +using Game.Entities; +using Game.Scripting; +using System; + +namespace Scripts.EasternKingdoms.AlteracValley.Vanndar +{ + struct SpellIds + { + public const uint Avatar = 19135; + public const uint Thunderclap = 15588; + public const uint Stormbolt = 20685; // not sure + } + + struct TextIds + { + public const uint YellAggro = 0; + public const uint YellEvade = 1; + //public const uint YellRespawn1 = -1810010; // Missing in database + //public const uint YellRespawn2 = -1810011; // Missing in database + public const uint YellRandom = 2; + public const uint YellSpell = 3; + } + + [Script] + class boss_vanndar : ScriptedAI + { + public boss_vanndar(Creature creature) : base(creature) { } + + public override void Reset() + { + _scheduler.CancelAll(); + } + + public override void JustEngagedWith(Unit who) + { + _scheduler.Schedule(TimeSpan.FromSeconds(3), task => + { + DoCastVictim(SpellIds.Avatar); + task.Repeat(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(4), task => + { + DoCastVictim(SpellIds.Thunderclap); + task.Repeat(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(15)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(6), task => + { + DoCastVictim(SpellIds.Stormbolt); + task.Repeat(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(25)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(30), task => + { + Talk(TextIds.YellRandom); + task.Repeat(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(30)); + }); + _scheduler.Schedule(TimeSpan.FromSeconds(5), task => + { + if (me.GetDistance2d(me.GetHomePosition().GetPositionX(), me.GetHomePosition().GetPositionY()) > 50) + { + EnterEvadeMode(); + Talk(TextIds.YellEvade); + } + task.Repeat(); + }); + + Talk(TextIds.YellAggro); + } + + public override void UpdateAI(uint diff) + { + if (!UpdateVictim()) + return; + + _scheduler.Update(diff, () => DoMeleeAttackIfReady()); + } + } +} +