diff options
-rw-r--r-- | sql/updates/world/master/2021_04_22_00_world_monk_stagger.sql | 8 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_monk.cpp | 230 |
2 files changed, 238 insertions, 0 deletions
diff --git a/sql/updates/world/master/2021_04_22_00_world_monk_stagger.sql b/sql/updates/world/master/2021_04_22_00_world_monk_stagger.sql new file mode 100644 index 00000000000..327d6aeeed6 --- /dev/null +++ b/sql/updates/world/master/2021_04_22_00_world_monk_stagger.sql @@ -0,0 +1,8 @@ +-- Stagger +DELETE FROM `spell_script_names` WHERE `ScriptName` IN ('spell_monk_stagger', 'spell_monk_stagger_damage_aura', 'spell_monk_stagger_debuff_aura'); +INSERT INTO `spell_script_names` (`spell_id`,`ScriptName`) VALUES +(115069, 'spell_monk_stagger'), +(124255, 'spell_monk_stagger_damage_aura'), +(124273, 'spell_monk_stagger_debuff_aura'), +(124274, 'spell_monk_stagger_debuff_aura'), +(124275, 'spell_monk_stagger_debuff_aura'); diff --git a/src/server/scripts/Spells/spell_monk.cpp b/src/server/scripts/Spells/spell_monk.cpp index 772244cf753..b12aa458e68 100644 --- a/src/server/scripts/Spells/spell_monk.cpp +++ b/src/server/scripts/Spells/spell_monk.cpp @@ -21,6 +21,7 @@ */ #include "ScriptMgr.h" +#include "DB2Stores.h" #include "Spell.h" #include "SpellAuraEffects.h" #include "SpellInfo.h" @@ -38,6 +39,10 @@ enum MonkSpells SPELL_MONK_PROVOKE_AOE = 118635, SPELL_MONK_SOOTHING_MIST = 115175, SPELL_MONK_STANCE_OF_THE_SPIRITED_CRANE = 154436, + SPELL_MONK_STAGGER_DAMAGE_AURA = 124255, + SPELL_MONK_STAGGER_HEAVY = 124273, + SPELL_MONK_STAGGER_LIGHT = 124275, + SPELL_MONK_STAGGER_MODERATE = 124274, SPELL_MONK_SURGING_MIST_HEAL = 116995, }; @@ -159,9 +164,234 @@ class spell_monk_provoke : public SpellScript } }; +// Utility for stagger scripts +Aura* FindExistingStaggerEffect(Unit* unit) +{ + if (Aura* auraLight = unit->GetAura(SPELL_MONK_STAGGER_LIGHT)) + return auraLight; + + if (Aura* auraModerate = unit->GetAura(SPELL_MONK_STAGGER_MODERATE)) + return auraModerate; + + if (Aura* auraHeavy = unit->GetAura(SPELL_MONK_STAGGER_HEAVY)) + return auraHeavy; + + return nullptr; +} + +static constexpr SpellEffIndex AuraStaggerEffectTick = EFFECT_0; +static constexpr SpellEffIndex AuraStaggerEffectTotal = EFFECT_1; + +// 115069 - Stagger +class spell_monk_stagger : public AuraScript +{ + PrepareAuraScript(spell_monk_stagger); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_MONK_STAGGER_LIGHT, SPELL_MONK_STAGGER_MODERATE, SPELL_MONK_STAGGER_HEAVY }); + } + + void AbsorbNormal(AuraEffect* /*aurEff*/, DamageInfo& dmgInfo, uint32& /*absorbAmount*/) + { + Absorb(dmgInfo, 1.0f); + } + + void AbsorbMagic(AuraEffect* /*aurEff*/, DamageInfo& dmgInfo, uint32& /*absorbAmount*/) + { + AuraEffect const* effect = GetEffect(EFFECT_4); + if (!effect) + return; + + Absorb(dmgInfo, float(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 + if (SpellInfo const* dmgSpellInfo = dmgInfo.GetSpellInfo()) + if (dmgSpellInfo->Id == SPELL_MONK_STAGGER_DAMAGE_AURA) + return; + + AuraEffect const* effect = GetEffect(AuraStaggerEffectTick); + if (!effect) + return; + + Unit* target = GetTarget(); + float agility = target->GetStat(STAT_AGILITY); + float base = CalculatePct(agility, float(effect->GetAmount())); + float K = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::ArmorConstant, target->getLevel(), -2, 0, Classes(target->getClass())); + + float newAmount = (base / (base + K)); + newAmount *= multiplier; + + // Absorb X percentage of the damage + float absorbAmount = float(dmgInfo.GetDamage()) * newAmount; + if (absorbAmount > 0) + { + dmgInfo.AbsorbDamage(absorbAmount); + + // Cast stagger and make it tick on each tick + AddAndRefreshStagger(absorbAmount); + } + } + + void Register() override + { + OnEffectAbsorb += AuraEffectAbsorbFn(spell_monk_stagger::AbsorbNormal, EFFECT_1); + OnEffectAbsorb += AuraEffectAbsorbFn(spell_monk_stagger::AbsorbMagic, EFFECT_2); + } + +private: + void AddAndRefreshStagger(float amount) + { + Unit* target = GetTarget(); + if (Aura* auraStagger = FindExistingStaggerEffect(target)) + { + AuraEffect* effStaggerRemaining = auraStagger->GetEffect(AuraStaggerEffectTotal); + if (!effStaggerRemaining) + return; + + float newAmount = effStaggerRemaining->GetAmount() + amount; + uint32 spellId = GetStaggerSpellId(target, newAmount); + if (spellId == effStaggerRemaining->GetSpellInfo()->Id) + { + auraStagger->RefreshDuration(); + effStaggerRemaining->ChangeAmount(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); + } + + uint32 GetStaggerSpellId(Unit* unit, float amount) + { + const float StaggerHeavy = 0.6f; + const float StaggerModerate = 0.3f; + + float staggerPct = amount / float(unit->GetMaxHealth()); + return (staggerPct >= StaggerHeavy) ? SPELL_MONK_STAGGER_HEAVY : + (staggerPct >= StaggerModerate) ? SPELL_MONK_STAGGER_MODERATE : + SPELL_MONK_STAGGER_LIGHT; + } + + void AddNewStagger(Unit* unit, uint32 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, CastSpellExtraArgs(SPELLVALUE_BASE_POINT1, staggerAmount).SetTriggerFlags(TRIGGERED_FULL_MASK)); + } +}; + +// 124255 - Stagger - SPELL_MONK_STAGGER_DAMAGE_AURA +class spell_monk_stagger_damage_aura : public AuraScript +{ + PrepareAuraScript(spell_monk_stagger_damage_aura); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_MONK_STAGGER_LIGHT, SPELL_MONK_STAGGER_MODERATE, SPELL_MONK_STAGGER_HEAVY }); + } + + void OnPeriodicDamage(AuraEffect const* aurEff) + { + // Update our light/medium/heavy stagger with the correct stagger amount left + if (Aura* auraStagger = FindExistingStaggerEffect(GetTarget())) + { + if (AuraEffect* auraEff = auraStagger->GetEffect(AuraStaggerEffectTotal)) + { + float total = float(auraEff->GetAmount()); + float tickDamage = float(aurEff->GetDamage()); + auraEff->ChangeAmount(total - tickDamage); + } + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_monk_stagger_damage_aura::OnPeriodicDamage, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } +}; + +// 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 : public AuraScript +{ + PrepareAuraScript(spell_monk_stagger_debuff_aura); + + bool Load() override + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_MONK_STAGGER_DAMAGE_AURA, GetCastDifficulty()); + SpellEffectInfo const* effInfo = !spellInfo ? nullptr : spellInfo->GetEffect(EFFECT_0); + if (!effInfo) + return false; + + _period = float(effInfo->ApplyAuraPeriod); + return true; + } + + void OnReapply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + // Calculate damage per tick + float total = float(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(AuraStaggerEffectTick); + if (effInfo) + effInfo->ChangeAmount(perTick); + + // Set amount on damage aura (or cast it if needed) + CastOrChangeTickDamage(perTick); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes mode) + { + if (mode != AURA_EFFECT_HANDLE_REAL) + return; + + // Remove damage aura + GetTarget()->RemoveAura(SPELL_MONK_STAGGER_DAMAGE_AURA); + } + + void Register() override + { + AfterEffectApply += AuraEffectRemoveFn(spell_monk_stagger_debuff_aura::OnReapply, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + AfterEffectRemove += AuraEffectRemoveFn(spell_monk_stagger_debuff_aura::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + +private: + float _period = 0.0f; + + void CastOrChangeTickDamage(float tickDamage) + { + Unit* unit = GetTarget(); + Aura* auraDamage = unit->GetAura(SPELL_MONK_STAGGER_DAMAGE_AURA); + if (!auraDamage) + { + unit->CastSpell(unit, SPELL_MONK_STAGGER_DAMAGE_AURA, true); + auraDamage = unit->GetAura(SPELL_MONK_STAGGER_DAMAGE_AURA); + } + + if (auraDamage) + if (AuraEffect* eff = auraDamage->GetEffect(AuraStaggerEffectTick)) + eff->SetDamage(tickDamage); + } +}; + void AddSC_monk_spell_scripts() { RegisterAuraScript(spell_monk_crackling_jade_lightning); RegisterAuraScript(spell_monk_crackling_jade_lightning_knockback_proc_aura); RegisterSpellScript(spell_monk_provoke); + RegisterAuraScript(spell_monk_stagger); + RegisterAuraScript(spell_monk_stagger_damage_aura); + RegisterAuraScript(spell_monk_stagger_debuff_aura); } |