/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * 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 2 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 . */ /* * Scripts for spells with SPELLFAMILY_MONK and SPELLFAMILY_GENERIC spells used by monk players. * Scriptnames of files in this file should be prefixed with "spell_monk_". */ #include "ScriptMgr.h" #include "AreaTrigger.h" #include "AreaTriggerAI.h" #include "DB2Stores.h" #include "PathGenerator.h" #include "Player.h" #include "Spell.h" #include "SpellAuraEffects.h" #include "SpellInfo.h" #include "SpellMgr.h" #include "SpellScript.h" enum MonkSpells { SPELL_MONK_BURST_OF_LIFE_TALENT = 399226, SPELL_MONK_BURST_OF_LIFE_HEAL = 399230, SPELL_MONK_CALMING_COALESCENCE = 388220, SPELL_MONK_COMBAT_CONDITIONING = 128595, SPELL_MONK_CRACKLING_JADE_LIGHTNING_CHANNEL = 117952, SPELL_MONK_CRACKLING_JADE_LIGHTNING_CHI_PROC = 123333, SPELL_MONK_CRACKLING_JADE_LIGHTNING_KNOCKBACK = 117962, SPELL_MONK_CRACKLING_JADE_LIGHTNING_KNOCKBACK_CD = 117953, SPELL_MONK_ENVELOPING_MIST = 124682, SPELL_MONK_JADE_WALK = 450552, SPELL_MONK_MISTS_OF_LIFE = 388548, SPELL_MONK_MORTAL_WOUNDS = 115804, SPELL_MONK_POWER_STRIKE_PROC = 129914, SPELL_MONK_POWER_STRIKE_ENERGIZE = 121283, SPELL_MONK_PRESSURE_POINTS = 450432, SPELL_MONK_PROVOKE_SINGLE_TARGET = 116189, SPELL_MONK_PROVOKE_AOE = 118635, SPELL_MONK_NO_FEATHER_FALL = 79636, SPELL_MONK_OPEN_PALM_STRIKES_TALENT = 392970, SPELL_MONK_RENEWING_MIST = 119611, SPELL_MONK_ROLL_BACKWARD = 109131, SPELL_MONK_ROLL_FORWARD = 107427, SPELL_MONK_SAVE_THEM_ALL_HEAL_BONUS = 390105, SPELL_MONK_SONG_OF_CHI_JI_STUN = 198909, 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, }; // 399226 - Burst of Life (attached to 116849 - Life Cocoon) class spell_monk_burst_of_life : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MONK_BURST_OF_LIFE_HEAL }) && ValidateSpellEffect({ { SPELL_MONK_BURST_OF_LIFE_TALENT, EFFECT_0 } }); } bool Load() override { Unit* caster = GetCaster(); return caster && caster->HasAuraEffect(SPELL_MONK_BURST_OF_LIFE_TALENT, EFFECT_0); } void AfterRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) const { AuraRemoveMode removeMode = GetTargetApplication()->GetRemoveMode(); if (removeMode != AURA_REMOVE_BY_EXPIRE && (removeMode != AURA_REMOVE_BY_ENEMY_SPELL || aurEff->GetAmount())) return; Unit* caster = GetCaster(); if (!caster) return; AuraEffect const* burstOfLife = caster->GetAuraEffect(SPELL_MONK_BURST_OF_LIFE_TALENT, EFFECT_0); if (!burstOfLife) return; caster->CastSpell(GetTarget(), SPELL_MONK_BURST_OF_LIFE_HEAL, CastSpellExtraArgsInit{ .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, .SpellValueOverrides = { { SPELLVALUE_MAX_TARGETS, burstOfLife->GetAmount() } } }); } void Register() override { AfterEffectRemove += AuraEffectRemoveFn(spell_monk_burst_of_life::AfterRemove, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB, AURA_EFFECT_HANDLE_REAL); } }; // 399230 - Burst of Life class spell_monk_burst_of_life_heal : public SpellScript { void FilterTargets(std::list& targets) const { Trinity::SelectRandomInjuredTargets(targets, GetSpellValue()->MaxAffectedTargets, true, GetExplTargetUnit()); } void Register() override { OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_monk_burst_of_life_heal::FilterTargets, EFFECT_1, TARGET_UNIT_DEST_AREA_ALLY); } }; // 117952 - Crackling Jade Lightning class spell_monk_crackling_jade_lightning : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo( { SPELL_MONK_STANCE_OF_THE_SPIRITED_CRANE, SPELL_MONK_CRACKLING_JADE_LIGHTNING_CHI_PROC }); } void OnTick(AuraEffect const* /*aurEff*/) { if (Unit* caster = GetCaster()) if (caster->HasAura(SPELL_MONK_STANCE_OF_THE_SPIRITED_CRANE)) caster->CastSpell(caster, SPELL_MONK_CRACKLING_JADE_LIGHTNING_CHI_PROC, TRIGGERED_FULL_MASK); } void Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_monk_crackling_jade_lightning::OnTick, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); } }; // 117959 - Crackling Jade Lightning class spell_monk_crackling_jade_lightning_knockback_proc_aura : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo( { SPELL_MONK_CRACKLING_JADE_LIGHTNING_KNOCKBACK, SPELL_MONK_CRACKLING_JADE_LIGHTNING_KNOCKBACK_CD }); } bool CheckProc(ProcEventInfo& eventInfo) { if (GetTarget()->HasAura(SPELL_MONK_CRACKLING_JADE_LIGHTNING_KNOCKBACK_CD)) return false; if (eventInfo.GetActor()->HasAura(SPELL_MONK_CRACKLING_JADE_LIGHTNING_CHANNEL, GetTarget()->GetGUID())) return false; Spell* currentChanneledSpell = GetTarget()->GetCurrentSpell(CURRENT_CHANNELED_SPELL); if (!currentChanneledSpell || currentChanneledSpell->GetSpellInfo()->Id != SPELL_MONK_CRACKLING_JADE_LIGHTNING_CHANNEL) return false; return true; } void HandleProc(AuraEffect* /*aurEff*/, ProcEventInfo& eventInfo) { GetTarget()->CastSpell(eventInfo.GetActor(), SPELL_MONK_CRACKLING_JADE_LIGHTNING_KNOCKBACK, TRIGGERED_FULL_MASK); GetTarget()->CastSpell(GetTarget(), SPELL_MONK_CRACKLING_JADE_LIGHTNING_KNOCKBACK_CD, TRIGGERED_FULL_MASK); } void Register() override { DoCheckProc += AuraCheckProcFn(spell_monk_crackling_jade_lightning_knockback_proc_aura::CheckProc); OnEffectProc += AuraEffectProcFn(spell_monk_crackling_jade_lightning_knockback_proc_aura::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); } }; // 450553 - Jade Walk class spell_monk_jade_walk : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MONK_JADE_WALK }); } void HandlePeriodicTick(AuraEffect const* aurEff) { Unit* target = GetTarget(); if (!target->IsInCombat()) target->CastSpell(target, SPELL_MONK_JADE_WALK, CastSpellExtraArgsInit{ .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, .TriggeringAura = aurEff }); else target->RemoveAurasDueToSpell(SPELL_MONK_JADE_WALK); } void Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_monk_jade_walk::HandlePeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); } }; // 116849 - Life Cocoon class spell_monk_life_cocoon : public SpellScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MONK_CALMING_COALESCENCE }); } void CalculateAbsorb(SpellEffIndex /*effIndex*/) { int32 absorb = GetCaster()->CountPctFromMaxHealth(GetEffectValue()); if (Player* player = GetCaster()->ToPlayer()) AddPct(absorb, player->GetRatingBonusValue(CR_VERSATILITY_HEALING_DONE)); if (AuraEffect* calmingCoalescence = GetCaster()->GetAuraEffect(SPELL_MONK_CALMING_COALESCENCE, EFFECT_0, GetCaster()->GetGUID())) { AddPct(absorb, calmingCoalescence->GetAmount()); calmingCoalescence->GetBase()->Remove(); } GetSpell()->SetSpellValue({ SPELLVALUE_BASE_POINT0, absorb }); } void Register() override { OnEffectLaunch += SpellEffectFn(spell_monk_life_cocoon::CalculateAbsorb, EFFECT_2, SPELL_EFFECT_DUMMY); } }; // 388548 - Mists of Life (attached to 116849 - Life Cocoon) class spell_monk_mists_of_life : public SpellScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MONK_MISTS_OF_LIFE, SPELL_MONK_RENEWING_MIST, SPELL_MONK_ENVELOPING_MIST }); } bool Load() override { return GetCaster()->HasAuraEffect(SPELL_MONK_MISTS_OF_LIFE, EFFECT_0); } void HandleEffectApply(SpellEffIndex /*effIndex*/) const { Unit* caster = GetCaster(); Unit* target = GetHitUnit(); CastSpellExtraArgs args; args.SetTriggerFlags(TRIGGERED_IGNORE_GCD | TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_IGNORE_CAST_TIME | TRIGGERED_DONT_REPORT_CAST_ERROR); args.SetTriggeringSpell(GetSpell()); caster->CastSpell(target, SPELL_MONK_RENEWING_MIST, args); caster->CastSpell(target, SPELL_MONK_ENVELOPING_MIST, args); } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_monk_mists_of_life::HandleEffectApply, EFFECT_0, SPELL_EFFECT_APPLY_AURA); } }; // 392972 - Open Palm Strikes class spell_monk_open_palm_strikes : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellEffect({ { SPELL_MONK_OPEN_PALM_STRIKES_TALENT, EFFECT_1} }); } bool CheckProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*procInfo*/) { AuraEffect const* talent = GetTarget()->GetAuraEffect(SPELL_MONK_OPEN_PALM_STRIKES_TALENT, EFFECT_1); return talent && roll_chance_i(talent->GetAmount()); } void Register() override { DoCheckEffectProc += AuraCheckEffectProcFn(spell_monk_open_palm_strikes::CheckProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } }; // 121817 - Power Strike class spell_monk_power_strike_periodic : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MONK_POWER_STRIKE_PROC }); } void HandlePeriodic(AuraEffect const* /*aurEff*/) { GetTarget()->CastSpell(GetTarget(), SPELL_MONK_POWER_STRIKE_PROC, true); } void Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_monk_power_strike_periodic::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); } }; // 129914 - Power Strike Proc class spell_monk_power_strike_proc : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MONK_POWER_STRIKE_ENERGIZE }); } void HandleProc(AuraEffect* /*aurEff*/, ProcEventInfo& /*eventInfo*/) { GetTarget()->CastSpell(GetTarget(), SPELL_MONK_POWER_STRIKE_ENERGIZE, true); } void Register() override { OnEffectProc += AuraEffectProcFn(spell_monk_power_strike_proc::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); } }; // 115078 - Paralysis class spell_monk_pressure_points : public SpellScript { bool Validate(SpellInfo const* spellInfo) override { return ValidateSpellInfo({ SPELL_MONK_PRESSURE_POINTS }) && ValidateSpellEffect({ { spellInfo->Id, EFFECT_2 } }) && spellInfo->GetEffect(EFFECT_2).IsEffect(SPELL_EFFECT_DISPEL); } bool Load() override { return !GetCaster()->HasAura(SPELL_MONK_PRESSURE_POINTS); } static void PreventDispel(SpellScript const&, WorldObject*& target) { target = nullptr; } void Register() override { OnObjectTargetSelect += SpellObjectTargetSelectFn(spell_monk_pressure_points::PreventDispel, EFFECT_2, TARGET_UNIT_TARGET_ENEMY); } }; // 115546 - Provoke class spell_monk_provoke : public SpellScript { static uint32 const BlackOxStatusEntry = 61146; bool Validate(SpellInfo const* spellInfo) override { if (!(spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT_MASK)) // ensure GetExplTargetUnit() will return something meaningful during CheckCast return false; return ValidateSpellInfo( { SPELL_MONK_PROVOKE_SINGLE_TARGET, SPELL_MONK_PROVOKE_AOE }); } SpellCastResult CheckExplicitTarget() { if (GetExplTargetUnit()->GetEntry() != BlackOxStatusEntry) { SpellInfo const* singleTarget = sSpellMgr->AssertSpellInfo(SPELL_MONK_PROVOKE_SINGLE_TARGET, GetCastDifficulty()); SpellCastResult singleTargetExplicitResult = singleTarget->CheckExplicitTarget(GetCaster(), GetExplTargetUnit()); if (singleTargetExplicitResult != SPELL_CAST_OK) return singleTargetExplicitResult; } else if (GetExplTargetUnit()->GetOwnerGUID() != GetCaster()->GetGUID()) return SPELL_FAILED_BAD_TARGETS; return SPELL_CAST_OK; } void HandleDummy(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); if (GetHitUnit()->GetEntry() != BlackOxStatusEntry) GetCaster()->CastSpell(GetHitUnit(), SPELL_MONK_PROVOKE_SINGLE_TARGET, true); else GetCaster()->CastSpell(GetHitUnit(), SPELL_MONK_PROVOKE_AOE, true); } void Register() override { OnCheckCast += SpellCheckCastFn(spell_monk_provoke::CheckExplicitTarget); OnEffectHitTarget += SpellEffectFn(spell_monk_provoke::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; // 107428 - Rising Sun Kick class spell_monk_rising_sun_kick : public SpellScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MONK_COMBAT_CONDITIONING, SPELL_MONK_MORTAL_WOUNDS }); } bool Load() override { return GetCaster()->HasAura(SPELL_MONK_COMBAT_CONDITIONING); } void HandleOnHit(SpellEffIndex /*effIndex*/) const { GetCaster()->CastSpell(GetHitUnit(), SPELL_MONK_MORTAL_WOUNDS, CastSpellExtraArgsInit{ .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, .TriggeringSpell = GetSpell() }); } void Register() override { OnEffectLaunchTarget += SpellEffectFn(spell_monk_rising_sun_kick::HandleOnHit, EFFECT_0, SPELL_EFFECT_TRIGGER_SPELL); } }; // 109132 - Roll class spell_monk_roll : public SpellScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MONK_ROLL_BACKWARD, SPELL_MONK_ROLL_FORWARD, SPELL_MONK_NO_FEATHER_FALL }); } SpellCastResult CheckCast() { if (GetCaster()->HasUnitState(UNIT_STATE_ROOT)) return SPELL_FAILED_ROOTED; return SPELL_CAST_OK; } void HandleDummy(SpellEffIndex /*effIndex*/) { GetCaster()->CastSpell(GetCaster(), GetCaster()->HasUnitMovementFlag(MOVEMENTFLAG_BACKWARD) ? SPELL_MONK_ROLL_BACKWARD : SPELL_MONK_ROLL_FORWARD, TRIGGERED_IGNORE_CAST_IN_PROGRESS); GetCaster()->CastSpell(GetCaster(), SPELL_MONK_NO_FEATHER_FALL, true); } void Register() override { OnCheckCast += SpellCheckCastFn(spell_monk_roll::CheckCast); OnEffectHitTarget += SpellEffectFn(spell_monk_roll::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; // 107427 - Roll // 109131 - Roll (backward) class spell_monk_roll_aura : public AuraScript { void CalcMovementAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) { amount += 100; } void CalcImmunityAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) { amount -= 100; } void ChangeRunBackSpeed(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { GetTarget()->SetSpeed(MOVE_RUN_BACK, GetTarget()->GetSpeed(MOVE_RUN)); } void RestoreRunBackSpeed(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { GetTarget()->UpdateSpeed(MOVE_RUN_BACK); } void Register() override { // Values need manual correction DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_monk_roll_aura::CalcMovementAmount, EFFECT_0, SPELL_AURA_MOD_SPEED_NO_CONTROL); DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_monk_roll_aura::CalcMovementAmount, EFFECT_2, SPELL_AURA_MOD_MINIMUM_SPEED); DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_monk_roll_aura::CalcImmunityAmount, EFFECT_5, SPELL_AURA_MECHANIC_IMMUNITY); DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_monk_roll_aura::CalcImmunityAmount, EFFECT_6, SPELL_AURA_MECHANIC_IMMUNITY); // This is a special aura that sets backward run speed equal to forward speed AfterEffectApply += AuraEffectApplyFn(spell_monk_roll_aura::ChangeRunBackSpeed, EFFECT_4, SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED, AURA_EFFECT_HANDLE_REAL); AfterEffectRemove += AuraEffectApplyFn(spell_monk_roll_aura::RestoreRunBackSpeed, EFFECT_4, SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED, AURA_EFFECT_HANDLE_REAL); } }; // 389579 - Save Them All class spell_monk_save_them_all : public AuraScript { bool Validate(SpellInfo const* spellInfo) override { return ValidateSpellInfo({ SPELL_MONK_SAVE_THEM_ALL_HEAL_BONUS }) && ValidateSpellEffect({ { spellInfo->Id, EFFECT_2 } }); } bool CheckProc(ProcEventInfo const& eventInfo) const { return eventInfo.GetActionTarget()->HealthBelowPct(GetEffectInfo(EFFECT_2).CalcValue(eventInfo.GetActor())); } void HandleProc(AuraEffect const* aurEff, ProcEventInfo const& /*eventInfo*/) const { GetTarget()->CastSpell(GetTarget(), SPELL_MONK_SAVE_THEM_ALL_HEAL_BONUS, CastSpellExtraArgsInit{ .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, .TriggeringAura = aurEff }); } void Register() override { DoCheckProc += AuraCheckProcFn(spell_monk_save_them_all::CheckProc); OnEffectProc += AuraEffectProcFn(spell_monk_save_them_all::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); } }; // 198898 - Song of Chi-Ji struct at_monk_song_of_chi_ji : AreaTriggerAI { using AreaTriggerAI::AreaTriggerAI; void OnInitialize() override { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(at->GetSpellId(), DIFFICULTY_NONE); if (!spellInfo) return; Unit* caster = at->GetCaster(); if (!caster) return; Position destPos = at->GetFirstCollisionPosition(spellInfo->GetMaxRange(false, caster), 0.0f); PathGenerator path(at); path.CalculatePath(destPos.GetPositionX(), destPos.GetPositionY(), destPos.GetPositionZ(), false); at->InitSplines(path.GetPath()); } void OnUnitEnter(Unit* unit) override { if (Unit* caster = at->GetCaster()) if (caster->IsValidAttackTarget(unit)) caster->CastSpell(unit, SPELL_MONK_SONG_OF_CHI_JI_STUN, TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR); } }; // 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 { 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()), 0); 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 { 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->GetAmount()); 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 { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_MONK_STAGGER_DAMAGE_AURA }) && ValidateSpellEffect({ { SPELL_MONK_STAGGER_DAMAGE_AURA, EFFECT_0 } }); } bool Load() override { _period = float(sSpellMgr->AssertSpellInfo(SPELL_MONK_STAGGER_DAMAGE_AURA, GetCastDifficulty())->GetEffect(EFFECT_0).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->ChangeAmount(tickDamage); } }; // 116841 - Tiger's Lust class spell_monk_tigers_lust : public SpellScript { void HandleRemoveImpairingAuras(SpellEffIndex /*effIndex*/) { GetHitUnit()->RemoveMovementImpairingAuras(true); } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_monk_tigers_lust::HandleRemoveImpairingAuras, EFFECT_0, SPELL_EFFECT_APPLY_AURA); } }; void AddSC_monk_spell_scripts() { RegisterSpellScript(spell_monk_burst_of_life); RegisterSpellScript(spell_monk_burst_of_life_heal); RegisterSpellScript(spell_monk_crackling_jade_lightning); RegisterSpellScript(spell_monk_crackling_jade_lightning_knockback_proc_aura); RegisterSpellScript(spell_monk_jade_walk); RegisterSpellScript(spell_monk_life_cocoon); RegisterSpellScript(spell_monk_mists_of_life); RegisterSpellScript(spell_monk_open_palm_strikes); RegisterSpellScript(spell_monk_power_strike_periodic); RegisterSpellScript(spell_monk_power_strike_proc); RegisterSpellScript(spell_monk_pressure_points); RegisterSpellScript(spell_monk_provoke); RegisterSpellScript(spell_monk_rising_sun_kick); RegisterSpellScript(spell_monk_roll); RegisterSpellScript(spell_monk_roll_aura); RegisterSpellScript(spell_monk_save_them_all); RegisterAreaTriggerAI(at_monk_song_of_chi_ji); RegisterSpellScript(spell_monk_stagger); RegisterSpellScript(spell_monk_stagger_damage_aura); RegisterSpellScript(spell_monk_stagger_debuff_aura); RegisterSpellScript(spell_monk_tigers_lust); }