/* * 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.Entities; using Game.Scripting; using Game.Spells; using System; namespace Scripts.Spells.Monk { struct SpellIds { public const uint CracklingJadeLightningChannel = 117952; public const uint CracklingJadeLightningChiProc = 123333; public const uint CracklingJadeLightningKnockback = 117962; public const uint CracklingJadeLightningKnockbackCd = 117953; public const uint ProvokeSingleTarget = 116189; public const uint ProvokeAoe = 118635; public const uint SoothingMist = 115175; public const uint StanceOfTheSpiritedCrane = 154436; public const uint StaggerDamageAura = 124255; public const uint StaggerHeavy = 124273; public const uint StaggerLight = 124275; public const uint StaggerModerate = 124274; public const uint SurgingMistHeal = 116995; } [Script] // 117952 - Crackling Jade Lightning class spell_monk_crackling_jade_lightning : AuraScript { public override bool Validate(SpellInfo spellInfo) { return ValidateSpellInfo(SpellIds.StanceOfTheSpiritedCrane, SpellIds.CracklingJadeLightningChiProc); } void OnTick(AuraEffect aurEff) { Unit caster = GetCaster(); if (caster) if (caster.HasAura(SpellIds.StanceOfTheSpiritedCrane)) caster.CastSpell(caster, SpellIds.CracklingJadeLightningChiProc, new CastSpellExtraArgs(TriggerCastFlags.FullMask)); } public override void Register() { OnEffectPeriodic.Add(new EffectPeriodicHandler(OnTick, 0, AuraType.PeriodicDamage)); } } [Script] // 117959 - Crackling Jade Lightning class spell_monk_crackling_jade_lightning_knockback_proc_aura : AuraScript { public override bool Validate(SpellInfo spellInfo) { return ValidateSpellInfo(SpellIds.CracklingJadeLightningKnockback, SpellIds.CracklingJadeLightningKnockbackCd); } bool CheckProc(ProcEventInfo eventInfo) { if (GetTarget().HasAura(SpellIds.CracklingJadeLightningKnockbackCd)) return false; if (eventInfo.GetActor().HasAura(SpellIds.CracklingJadeLightningChannel, GetTarget().GetGUID())) return false; Spell currentChanneledSpell = GetTarget().GetCurrentSpell(CurrentSpellTypes.Channeled); if (!currentChanneledSpell || currentChanneledSpell.GetSpellInfo().Id != SpellIds.CracklingJadeLightningChannel) return false; return true; } void HandleProc(AuraEffect aurEff, ProcEventInfo eventInfo) { GetTarget().CastSpell(eventInfo.GetActor(), SpellIds.CracklingJadeLightningKnockback, new CastSpellExtraArgs(TriggerCastFlags.FullMask)); GetTarget().CastSpell(GetTarget(), SpellIds.CracklingJadeLightningKnockbackCd, new CastSpellExtraArgs(TriggerCastFlags.FullMask)); } public override void Register() { DoCheckProc.Add(new CheckProcHandler(CheckProc)); OnEffectProc.Add(new EffectProcHandler(HandleProc, 0, AuraType.Dummy)); } } [Script] // 115546 - Provoke class spell_monk_provoke : SpellScript { const uint BlackOxStatusEntry = 61146; public override bool Validate(SpellInfo spellInfo) { if (!spellInfo.GetExplicitTargetMask().HasAnyFlag(SpellCastTargetFlags.UnitMask)) // ensure GetExplTargetUnit() will return something meaningful during CheckCast return false; return ValidateSpellInfo(SpellIds.ProvokeSingleTarget, SpellIds.ProvokeAoe); } SpellCastResult CheckExplicitTarget() { if (GetExplTargetUnit().GetEntry() != BlackOxStatusEntry) { SpellInfo singleTarget = Global.SpellMgr.GetSpellInfo(SpellIds.ProvokeSingleTarget, GetCastDifficulty()); SpellCastResult singleTargetExplicitResult = singleTarget.CheckExplicitTarget(GetCaster(), GetExplTargetUnit()); if (singleTargetExplicitResult != SpellCastResult.SpellCastOk) return singleTargetExplicitResult; } else if (GetExplTargetUnit().GetOwnerGUID() != GetCaster().GetGUID()) return SpellCastResult.BadTargets; return SpellCastResult.SpellCastOk; } void HandleDummy(uint effIndex) { PreventHitDefaultEffect(effIndex); if (GetHitUnit().GetEntry() != BlackOxStatusEntry) GetCaster().CastSpell(GetHitUnit(), SpellIds.ProvokeSingleTarget, true); else GetCaster().CastSpell(GetHitUnit(), SpellIds.ProvokeAoe, true); } public override void Register() { OnCheckCast.Add(new CheckCastHandler(CheckExplicitTarget)); OnEffectHitTarget.Add(new EffectHandler(HandleDummy, 0, SpellEffectName.Dummy)); } } //static constexpr SpellEffIndex AuraStaggerEffectTick = EFFECT_0; //static constexpr SpellEffIndex AuraStaggerEffectTotal = EFFECT_1; [Script] // 115069 - Stagger class spell_monk_stagger : AuraScript { public override bool Validate(SpellInfo spellInfo) { return ValidateSpellInfo(SpellIds.StaggerLight, SpellIds.StaggerModerate, SpellIds.StaggerHeavy); } void AbsorbNormal(AuraEffect aurEff, DamageInfo dmgInfo, ref uint absorbAmount) { Absorb(dmgInfo, 1.0f); } void AbsorbMagic(AuraEffect aurEff, DamageInfo dmgInfo, ref uint absorbAmount) { AuraEffect effect = GetEffect(4); if (effect == null) return; Absorb(dmgInfo, effect.GetAmount() / 100.0f); } void Absorb(DamageInfo dmgInfo, float multiplier) { // Prevent default action (which would remove the aura) PreventDefaultAction(); // make sure damage doesn't come from stagger damage spell SPELL_MONK_STAGGER_DAMAGE_AURA SpellInfo dmgSpellInfo = dmgInfo.GetSpellInfo(); if (dmgSpellInfo != null) if (dmgSpellInfo.Id == SpellIds.StaggerDamageAura) return; AuraEffect effect = GetEffect(0); if (effect == null) return; Unit target = GetTarget(); float agility = target.GetStat(Stats.Agility); float baseAmount = MathFunctions.CalculatePct(agility, effect.GetAmount()); float K = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.ArmorConstant, target.GetLevel(), -2, 0, target.GetClass()); float newAmount = (baseAmount / (baseAmount + K)); newAmount *= multiplier; // Absorb X percentage of the damage float absorbAmount = dmgInfo.GetDamage() * newAmount; if (absorbAmount > 0) { dmgInfo.AbsorbDamage((uint)absorbAmount); // Cast stagger and make it tick on each tick AddAndRefreshStagger(absorbAmount); } } public override void Register() { OnEffectAbsorb.Add(new EffectAbsorbHandler(AbsorbNormal, 1)); OnEffectAbsorb.Add(new EffectAbsorbHandler(AbsorbMagic, 2)); } void AddAndRefreshStagger(float amount) { Unit target = GetTarget(); Aura auraStagger = FindExistingStaggerEffect(target); if (auraStagger != null) { AuraEffect effStaggerRemaining = auraStagger.GetEffect(1); if (effStaggerRemaining == null) return; float newAmount = effStaggerRemaining.GetAmount() + amount; uint spellId = GetStaggerSpellId(target, newAmount); if (spellId == effStaggerRemaining.GetSpellInfo().Id) { auraStagger.RefreshDuration(); effStaggerRemaining.ChangeAmount((int)newAmount, false, true /* reapply */); } else { // amount changed the stagger type so we need to change the stagger amount (e.g. from medium to light) GetTarget().RemoveAura(auraStagger); AddNewStagger(target, spellId, newAmount); } } else AddNewStagger(target, GetStaggerSpellId(target, amount), amount); } uint GetStaggerSpellId(Unit unit, float amount) { const float StaggerHeavy = 0.6f; const float StaggerModerate = 0.3f; float staggerPct = amount / unit.GetMaxHealth(); return (staggerPct >= StaggerHeavy) ? SpellIds.StaggerHeavy : (staggerPct >= StaggerModerate) ? SpellIds.StaggerModerate : SpellIds.StaggerLight; } void AddNewStagger(Unit unit, uint staggerSpellId, float staggerAmount) { // We only set the total stagger amount. The amount per tick will be set by the stagger spell script unit.CastSpell(unit, staggerSpellId, new CastSpellExtraArgs(SpellValueMod.BasePoint1, (int)staggerAmount).SetTriggerFlags(TriggerCastFlags.FullMask)); } public static Aura FindExistingStaggerEffect(Unit unit) { Aura auraLight = unit.GetAura(SpellIds.StaggerLight); if (auraLight != null) return auraLight; Aura auraModerate = unit.GetAura(SpellIds.StaggerModerate); if (auraModerate != null) return auraModerate; Aura auraHeavy = unit.GetAura(SpellIds.StaggerHeavy); if (auraHeavy != null) return auraHeavy; return null; } } [Script] // 124255 - Stagger - SPELL_MONK_STAGGER_DAMAGE_AURA class spell_monk_stagger_damage_aura : AuraScript { public override bool Validate(SpellInfo spellInfo) { return ValidateSpellInfo(SpellIds.StaggerLight, SpellIds.StaggerModerate, SpellIds.StaggerHeavy); } void OnPeriodicDamage(AuraEffect aurEff) { // Update our light/medium/heavy stagger with the correct stagger amount left Aura auraStagger = spell_monk_stagger.FindExistingStaggerEffect(GetTarget()); if (auraStagger != null) { AuraEffect auraEff = auraStagger.GetEffect(1); if (auraEff != null) { float total = auraEff.GetAmount(); float tickDamage = aurEff.GetDamage(); auraEff.ChangeAmount((int)(total - tickDamage)); } } } public override void Register() { OnEffectPeriodic.Add(new EffectPeriodicHandler(OnPeriodicDamage, 0, AuraType.PeriodicDamage)); } } [Script] // 124273, 124274, 124275 - Light/Moderate/Heavy Stagger - SPELL_MONK_STAGGER_LIGHT / SPELL_MONK_STAGGER_MODERATE / SPELL_MONK_STAGGER_HEAVY class spell_monk_stagger_debuff_aura : AuraScript { float _period; public override bool Load() { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(SpellIds.StaggerDamageAura, GetCastDifficulty()); SpellEffectInfo effInfo = spellInfo?.GetEffect(0); if (effInfo == null) return false; _period = (float)effInfo.ApplyAuraPeriod; return true; } void OnReapply(AuraEffect aurEff, AuraEffectHandleModes mode) { // Calculate damage per tick float total = aurEff.GetAmount(); float perTick = total * _period / (float)GetDuration(); // should be same as GetMaxDuration() TODO: verify // Set amount on effect for tooltip AuraEffect effInfo = GetAura().GetEffect(0); if (effInfo != null) effInfo.ChangeAmount((int)perTick); // Set amount on damage aura (or cast it if needed) CastOrChangeTickDamage(perTick); } void OnRemove(AuraEffect aurEff, AuraEffectHandleModes mode) { if (mode != AuraEffectHandleModes.Real) return; // Remove damage aura GetTarget().RemoveAura(SpellIds.StaggerDamageAura); } public override void Register() { AfterEffectApply.Add(new EffectApplyHandler(OnReapply, 1, AuraType.Dummy, AuraEffectHandleModes.RealOrReapplyMask)); AfterEffectRemove.Add(new EffectApplyHandler(OnRemove, 1, AuraType.Dummy, AuraEffectHandleModes.Real)); } void CastOrChangeTickDamage(float tickDamage) { Unit unit = GetTarget(); Aura auraDamage = unit.GetAura(SpellIds.StaggerDamageAura); if (auraDamage == null) { unit.CastSpell(unit, SpellIds.StaggerDamageAura, true); auraDamage = unit.GetAura(SpellIds.StaggerDamageAura); } if (auraDamage != null) { AuraEffect eff = auraDamage.GetEffect(0); if (eff != null) eff.SetDamage((int)tickDamage); } } } }