From 9480f79c2ae368b41d37a3448d6522f6f83d160e Mon Sep 17 00:00:00 2001 From: Shauren Date: Thu, 3 Aug 2023 21:32:20 +0200 Subject: [PATCH] Core/Scripts: Added damage and healing calculation hook to spell and aura scripts allowing to override base damage amount, flat value mod and percent value mod separately --- src/server/game/Entities/Unit/Unit.cpp | 80 ++++++++++++------- src/server/game/Entities/Unit/Unit.h | 12 +-- .../game/Spells/Auras/SpellAuraEffects.cpp | 14 ++-- src/server/game/Spells/Auras/SpellAuras.cpp | 14 ++++ src/server/game/Spells/Auras/SpellAuras.h | 1 + src/server/game/Spells/Spell.cpp | 26 ++++++ src/server/game/Spells/Spell.h | 4 + src/server/game/Spells/SpellEffects.cpp | 25 +++--- src/server/game/Spells/SpellScript.cpp | 37 +++++++++ src/server/game/Spells/SpellScript.h | 47 ++++++++--- 10 files changed, 199 insertions(+), 61 deletions(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index e56ca8016c..caff43418e 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -1214,7 +1214,7 @@ void Unit::CalculateMeleeDamage(Unit* victim, CalcDamageInfo* damageInfo, Weapon uint32 damage = 0; damage += CalculateDamage(damageInfo->AttackType, false, true); // Add melee damage bonus - damage = MeleeDamageBonusDone(damageInfo->Target, damage, damageInfo->AttackType, DIRECT_DAMAGE, nullptr, true, SpellSchoolMask(damageInfo->DamageSchoolMask)); + damage = MeleeDamageBonusDone(damageInfo->Target, damage, damageInfo->AttackType, DIRECT_DAMAGE, nullptr, MECHANIC_NONE, true, SpellSchoolMask(damageInfo->DamageSchoolMask)); damage = damageInfo->Target->MeleeDamageBonusTaken(this, damage, damageInfo->AttackType, nullptr, SpellSchoolMask(damageInfo->DamageSchoolMask)); // Script Hook For CalculateMeleeDamage -- Allow scripts to change the Damage pre class mitigation calculations @@ -6180,22 +6180,35 @@ void Unit::EnergizeBySpell(Unit* victim, SpellInfo const* spellInfo, int32 damag SendEnergizeSpellLog(victim, spellInfo->Id, damage, powerType); } -uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, uint8 effIndex, uint32 stack) const +int32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, int32 pdamage, DamageEffectType damagetype, uint8 effIndex, uint32 stack /*= 1*/, Spell* spell /*= nullptr*/, AuraEffect const* aurEff /*= nullptr*/) const { - if (!spellProto || !victim || damagetype == DIRECT_DAMAGE) + if (!spellProto || !victim) return pdamage; + int32 DoneTotal = 0; + float DoneTotalMod = 1.0f; + + auto callDamageScript = [&](int32& dmg, int32& flatMod, float& pctMod) + { + if (spell) + spell->CallScriptCalcDamageHandlers(victim, dmg, flatMod, pctMod); + else if (aurEff) + aurEff->GetBase()->CallScriptCalcDamageAndHealingHandlers(aurEff, aurEff->GetBase()->GetApplicationOfTarget(victim->GetGUID()), victim, dmg, flatMod, pctMod); + }; + // Some spells don't benefit from done mods - if (spellProto->HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS)) - return pdamage; + if (damagetype == DIRECT_DAMAGE || spellProto->HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS)) + { + callDamageScript(pdamage, DoneTotal, DoneTotalMod); + return int32(std::max(float(pdamage + DoneTotal) * DoneTotalMod, 0.0f)); + } // Totems get their bonus damage from their owner if (IsCreature() && IsTotem()) if (Unit* creator = ObjectAccessor::GetUnit(*this, GetCreatorGUID())) - return creator->SpellDamageBonusDone(victim, spellProto, pdamage, damagetype, effIndex, stack); + return creator->SpellDamageBonusDone(victim, spellProto, pdamage, damagetype, effIndex, stack, spell, aurEff); - int32 DoneTotal = 0; - float DoneTotalMod = SpellDamagePctDone(victim, spellProto, damagetype); + DoneTotalMod = SpellDamagePctDone(victim, spellProto, damagetype); // done scripted mod (take it from owner) Unit const* owner = GetOwner() ? GetOwner() : this; @@ -6328,13 +6341,14 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin DoneTotal += int32(DoneAdvertisedBenefit * spellPowerCoeff * stack); } - float tmpDamage = float(int32(pdamage) + DoneTotal) * DoneTotalMod; + callDamageScript(pdamage, DoneTotal, DoneTotalMod); + float tmpDamage = float(pdamage + DoneTotal) * DoneTotalMod; // apply spellmod to Done damage (flat and pct) if (Player* modOwner = GetSpellModOwner()) modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SpellModOp::PeriodicHealingAndDamage : SpellModOp::HealingAndDamage, tmpDamage); - return uint32(std::max(tmpDamage, 0.0f)); + return int32(std::max(tmpDamage, 0.0f)); } float Unit::SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, DamageEffectType damagetype) const @@ -6518,7 +6532,7 @@ float Unit::SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, Damage return DoneTotalMod; } -uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype) const +int32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, int32 pdamage, DamageEffectType damagetype) const { if (!spellProto || damagetype == DIRECT_DAMAGE) return pdamage; @@ -6601,7 +6615,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui } float tmpDamage = pdamage * TakenTotalMod; - return uint32(std::max(tmpDamage, 0.0f)); + return int32(std::max(tmpDamage, 0.0f)); } int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask, bool withSpellPowerPctMod /*= false*/) const @@ -6949,12 +6963,12 @@ float Unit::SpellCritChanceTaken(Unit const* caster, SpellInfo const* spellInfo, return damage; } -uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint8 effIndex, uint32 stack) const +int32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, int32 healamount, DamageEffectType damagetype, uint8 effIndex, uint32 stack /*= 1*/, Spell* spell /*= nullptr*/, AuraEffect const* aurEff /*= nullptr*/) const { // For totems get healing bonus from owner (statue isn't totem in fact) if (GetTypeId() == TYPEID_UNIT && IsTotem()) if (Unit* creator = ObjectAccessor::GetUnit(*this, GetCreatorGUID())) - return creator->SpellHealingBonusDone(victim, spellProto, healamount, damagetype, effIndex, stack); + return creator->SpellHealingBonusDone(victim, spellProto, healamount, damagetype, effIndex, stack, spell, aurEff); // Some spells don't benefit from done mods if (spellProto->HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS)) @@ -7055,13 +7069,19 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui DoneTotal = 0; } - float heal = float(int32(healamount) + DoneTotal) * DoneTotalMod; + if (spell) + spell->CallScriptCalcHealingHandlers(victim, healamount, DoneTotal, DoneTotalMod); + else if (aurEff) + aurEff->GetBase()->CallScriptCalcDamageAndHealingHandlers(aurEff, aurEff->GetBase()->GetApplicationOfTarget(victim->GetGUID()), victim, healamount, DoneTotal, DoneTotalMod); + + float heal = float(healamount + DoneTotal) * DoneTotalMod; + // apply spellmod to Done amount if (Player* modOwner = GetSpellModOwner()) modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SpellModOp::PeriodicHealingAndDamage : SpellModOp::HealingAndDamage, heal); - return uint32(std::max(heal, 0.0f)); + return int32(std::max(heal, 0.0f)); } float Unit::SpellHealingPctDone(Unit* victim, SpellInfo const* spellProto) const @@ -7143,7 +7163,7 @@ float Unit::SpellHealingPctDone(Unit* victim, SpellInfo const* spellProto) const return DoneTotalMod; } -uint32 Unit::SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype) const +int32 Unit::SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, int32 healamount, DamageEffectType damagetype) const { float TakenTotalMod = 1.0f; @@ -7190,7 +7210,7 @@ uint32 Unit::SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, u float heal = healamount * TakenTotalMod; - return uint32(std::max(heal, 0.0f)); + return int32(std::max(heal, 0.0f)); } int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask, bool withSpellPowerPctMod /*= false*/) const @@ -7474,7 +7494,7 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Worl return false; } -uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 damage, WeaponAttackType attType, DamageEffectType damagetype, SpellInfo const* spellProto /*= nullptr*/, bool useSpellBonusData /*= true*/, SpellSchoolMask damageSchoolMask /*= SPELL_SCHOOL_MASK_NORMAL*/) +int32 Unit::MeleeDamageBonusDone(Unit* victim, int32 damage, WeaponAttackType attType, DamageEffectType damagetype, SpellInfo const* spellProto /*= nullptr*/, Mechanics mechanic /*= MECHANIC_NONE*/, bool useSpellBonusData /*= true*/, SpellSchoolMask damageSchoolMask /*= SPELL_SCHOOL_MASK_NORMAL*/, Spell* spell /*= nullptr*/, AuraEffect const* aurEff /*= nullptr*/) { if (!victim || damage == 0) return 0; @@ -7562,7 +7582,10 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 damage, WeaponAttackType }); // Add SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC percent bonus - if (spellProto) + // Add SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC percent bonus + if (mechanic != MECHANIC_NONE) + AddPct(DoneTotalMod, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC, mechanic)); + else if (spellProto && spellProto->Mechanic) AddPct(DoneTotalMod, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DAMAGE_DONE_FOR_MECHANIC, spellProto->Mechanic)); if (!spellProto) @@ -7578,20 +7601,23 @@ uint32 Unit::MeleeDamageBonusDone(Unit* victim, uint32 damage, WeaponAttackType })); } - float damageF = damage; + if (spell) + spell->CallScriptCalcDamageHandlers(victim, damage, DoneFlatBenefit, DoneTotalMod); + else if (aurEff) + aurEff->GetBase()->CallScriptCalcDamageAndHealingHandlers(aurEff, aurEff->GetBase()->GetApplicationOfTarget(victim->GetGUID()), victim, damage, DoneFlatBenefit, DoneTotalMod); + + float damageF = float(damage + DoneFlatBenefit) * DoneTotalMod; // apply spellmod to Done damage if (spellProto) if (Player* modOwner = GetSpellModOwner()) modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SpellModOp::PeriodicHealingAndDamage : SpellModOp::HealingAndDamage, damageF); - damageF = (damageF + DoneFlatBenefit) * DoneTotalMod; - // bonus result can be negative - return uint32(std::max(damageF, 0.0f)); + return int32(std::max(damageF, 0.0f)); } -uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto /*= nullptr*/, SpellSchoolMask damageSchoolMask /*= SPELL_SCHOOL_MASK_NORMAL*/) +int32 Unit::MeleeDamageBonusTaken(Unit* attacker, int32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto /*= nullptr*/, SpellSchoolMask damageSchoolMask /*= SPELL_SCHOOL_MASK_NORMAL*/) { if (pdamage == 0) return 0; @@ -7624,7 +7650,7 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT return true; }); - if ((TakenFlatBenefit < 0) && (pdamage < static_cast(-TakenFlatBenefit))) + if ((TakenFlatBenefit < 0) && (pdamage < -TakenFlatBenefit)) return 0; // Taken total percent damage auras @@ -7729,7 +7755,7 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT } float tmpDamage = float(pdamage + TakenFlatBenefit) * TakenTotalMod; - return uint32(std::max(tmpDamage, 0.0f)); + return int32(std::max(tmpDamage, 0.0f)); } void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 88f63fe8eb..c5be8b8d25 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1595,16 +1595,16 @@ class TC_GAME_API Unit : public WorldObject Unit* GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo = nullptr); int32 SpellBaseDamageBonusDone(SpellSchoolMask schoolMask, bool withSpellPowerPctMod = false) const; - uint32 SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, uint8 effIndex, uint32 stack = 1) const; + int32 SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, int32 pdamage, DamageEffectType damagetype, uint8 effIndex, uint32 stack = 1, Spell* spell = nullptr, AuraEffect const* aurEff = nullptr) const; float SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, DamageEffectType damagetype) const; - uint32 SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype) const; + int32 SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, int32 pdamage, DamageEffectType damagetype) const; int32 SpellBaseHealingBonusDone(SpellSchoolMask schoolMask, bool withSpellPowerPctMod = false) const; - uint32 SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint8 effIndex, uint32 stack = 1) const; + int32 SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, int32 healamount, DamageEffectType damagetype, uint8 effIndex, uint32 stack = 1, Spell* spell = nullptr, AuraEffect const* aurEff = nullptr) const; float SpellHealingPctDone(Unit* victim, SpellInfo const* spellProto) const; - uint32 SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype) const; + int32 SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, int32 healamount, DamageEffectType damagetype) const; - uint32 MeleeDamageBonusDone(Unit* pVictim, uint32 damage, WeaponAttackType attType, DamageEffectType damagetype, SpellInfo const* spellProto = nullptr, bool useSpellBonusData = true, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL); - uint32 MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto = nullptr, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL); + int32 MeleeDamageBonusDone(Unit* pVictim, int32 damage, WeaponAttackType attType, DamageEffectType damagetype, SpellInfo const* spellProto = nullptr, Mechanics mechanic = MECHANIC_NONE, bool useSpellBonusData = true, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, Spell* spell = nullptr, AuraEffect const* aurEff = nullptr); + int32 MeleeDamageBonusTaken(Unit* attacker, int32 pdamage, WeaponAttackType attType, SpellInfo const* spellProto = nullptr, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL); bool IsBlockCritical() const; float SpellCritChanceDone(SpellInfo const* spellInfo, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK, bool isPeriodic = false) const; diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 07135847b9..6ac438f9f4 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -595,11 +595,11 @@ int32 AuraEffect::CalculateAmount(Unit* caster) case SPELL_AURA_PERIODIC_DAMAGE: case SPELL_AURA_PERIODIC_LEECH: if (caster && GetBase()->GetType() == UNIT_AURA_TYPE) - amount = caster->SpellDamageBonusDone(GetBase()->GetUnitOwner(), GetSpellInfo(), amount, DOT, GetEffIndex()); + amount = caster->SpellDamageBonusDone(GetBase()->GetUnitOwner(), GetSpellInfo(), amount, DOT, GetEffIndex(), 1, nullptr, this); break; case SPELL_AURA_PERIODIC_HEAL: if (caster && GetBase()->GetType() == UNIT_AURA_TYPE) - amount = caster->SpellHealingBonusDone(GetBase()->GetUnitOwner(), GetSpellInfo(), amount, DOT, GetEffIndex()); + amount = caster->SpellHealingBonusDone(GetBase()->GetUnitOwner(), GetSpellInfo(), amount, DOT, GetEffIndex(), 1, nullptr, this); break; default: break; @@ -5748,7 +5748,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const { // leave only target depending bonuses, rest is handled in calculate amount if (GetBase()->GetType() == DYNOBJ_AURA_TYPE) - damage = caster->SpellDamageBonusDone(target, GetSpellInfo(), damage, DOT, GetEffIndex(), GetBase()->GetStackAmount()); + damage = caster->SpellDamageBonusDone(target, GetSpellInfo(), damage, DOT, GetEffIndex(), GetBase()->GetStackAmount(), nullptr, this); switch (GetSpellInfo()->SpellFamilyName) { @@ -5879,7 +5879,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c // dynobj auras must always have a caster if (GetBase()->GetType() == DYNOBJ_AURA_TYPE) - damage = ASSERT_NOTNULL(caster)->SpellDamageBonusDone(target, GetSpellInfo(), damage, DOT, GetEffIndex(), GetBase()->GetStackAmount()); + damage = ASSERT_NOTNULL(caster)->SpellDamageBonusDone(target, GetSpellInfo(), damage, DOT, GetEffIndex(), GetBase()->GetStackAmount(), nullptr, this); damage = target->SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DOT); @@ -5943,7 +5943,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c float gainMultiplier = GetSpellInfo()->Effects[GetEffIndex()].CalcValueMultiplier(caster); - uint32 heal = uint32(caster->SpellHealingBonusDone(caster, GetSpellInfo(), uint32(new_damage * gainMultiplier), DOT, GetEffIndex(), GetBase()->GetStackAmount())); + uint32 heal = uint32(caster->SpellHealingBonusDone(caster, GetSpellInfo(), uint32(new_damage * gainMultiplier), DOT, GetEffIndex(), GetBase()->GetStackAmount(), nullptr, this)); heal = uint32(caster->SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DOT)); HealInfo healInfo(caster, caster, heal, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); @@ -6009,7 +6009,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const else { if (GetBase()->GetType() == DYNOBJ_AURA_TYPE) - damage = caster->SpellHealingBonusDone(target, GetSpellInfo(), damage, DOT, GetEffIndex(), GetBase()->GetStackAmount()) * caster->SpellHealingPctDone(target, m_spellInfo); + damage = caster->SpellHealingBonusDone(target, GetSpellInfo(), damage, DOT, GetEffIndex(), GetBase()->GetStackAmount(), nullptr, this) * caster->SpellHealingPctDone(target, m_spellInfo); } damage = target->SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DOT); @@ -6302,7 +6302,7 @@ void AuraEffect::HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEv } SpellNonMeleeDamage damageInfo(target, triggerTarget, GetId(), GetSpellInfo()->SchoolMask); - uint32 damage = target->SpellDamageBonusDone(triggerTarget, GetSpellInfo(), GetAmount(), SPELL_DIRECT_DAMAGE, GetEffIndex()); + uint32 damage = target->SpellDamageBonusDone(triggerTarget, GetSpellInfo(), GetAmount(), SPELL_DIRECT_DAMAGE, GetEffIndex(), 1, nullptr, this); damage = triggerTarget->SpellDamageBonusTaken(target, GetSpellInfo(), damage, SPELL_DIRECT_DAMAGE); target->CalculateSpellDamageTaken(&damageInfo, damage, GetSpellInfo()); Unit:: DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 13d03e9bb6..32fd6e6a9c 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -2248,6 +2248,20 @@ void Aura::CallScriptEffectCalcSpellModHandlers(AuraEffect const* aurEff, SpellM } } +void Aura::CallScriptCalcDamageAndHealingHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod) +{ + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_CALC_DAMAGE_AND_HEALING); + auto effEndItr = (*scritr)->DoEffectCalcDamageAndHealing.end(), effItr = (*scritr)->DoEffectCalcDamageAndHealing.begin(); + for (; effItr != effEndItr; ++effItr) + if (effItr->Filter(m_spellInfo, SpellEffIndex(aurEff->GetEffIndex()))) + effItr->Call(aurEff, victim, damageOrHealing, flatMod, pctMod); + + (*scritr)->_FinishScriptCall(); + } +} + void Aura::CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount, bool& defaultPrevented) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 4c9ea10228..c0a5bb12cc 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -253,6 +253,7 @@ class TC_GAME_API Aura void CallScriptEffectCalcAmountHandlers(AuraEffect const* aurEff, int32& amount, bool & canBeRecalculated); void CallScriptEffectCalcPeriodicHandlers(AuraEffect const* aurEff, bool& isPeriodic, int32& amplitude); void CallScriptEffectCalcSpellModHandlers(AuraEffect const* aurEff, SpellModifier*& spellMod); + void CallScriptCalcDamageAndHealingHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod); void CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& absorbAmount, bool& defaultPrevented); void CallScriptEffectAfterAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32& absorbAmount); void CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32& absorbAmount, bool& defaultPrevented); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 742b2a197f..5272b8181d 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -8427,6 +8427,32 @@ void Spell::CallScriptAfterHitHandlers() } } +void Spell::CallScriptCalcDamageHandlers(Unit* victim, int32& damage, int32& flatMod, float& pctMod) +{ + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_CALC_DAMAGE); + auto hookItrEnd = (*scritr)->CalcDamage.end(), hookItr = (*scritr)->CalcDamage.begin(); + for (; hookItr != hookItrEnd; ++hookItr) + (*hookItr).Call(victim, damage, flatMod, pctMod); + + (*scritr)->_FinishScriptCall(); + } +} + +void Spell::CallScriptCalcHealingHandlers(Unit* victim, int32& healing, int32& flatMod, float& pctMod) +{ + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(SPELL_SCRIPT_HOOK_CALC_HEALING); + auto hookItrEnd = (*scritr)->CalcHealing.end(), hookItr = (*scritr)->CalcHealing.begin(); + for (; hookItr != hookItrEnd; ++hookItr) + (*hookItr).Call(victim, healing, flatMod, pctMod); + + (*scritr)->_FinishScriptCall(); + } +} + void Spell::CallScriptObjectAreaTargetSelectHandlers(std::list& targets, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 00c7eefe56..571eb1d147 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -772,6 +772,10 @@ class TC_GAME_API Spell void CallScriptBeforeHitHandlers(); void CallScriptOnHitHandlers(); void CallScriptAfterHitHandlers(); + public: + void CallScriptCalcDamageHandlers(Unit* victim, int32& damage, int32& flatMod, float& pctMod); + void CallScriptCalcHealingHandlers(Unit* victim, int32& healing, int32& flatMod, float& pctMod); + protected: void CallScriptObjectAreaTargetSelectHandlers(std::list& targets, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType); void CallScriptObjectTargetSelectHandlers(WorldObject*& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType); void CallScriptDestinationTargetSelectHandlers(SpellDestination& target, SpellEffIndex effIndex, SpellImplicitTargetInfo const& targetType); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 1b3ce4635c..d87487b95a 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -451,7 +451,7 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) if (unitCaster && damage > 0 && apply_direct_bonus) { - damage = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, effIndex); + damage = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE, effIndex, 1, this); damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE); } @@ -1017,7 +1017,7 @@ void Spell::EffectPowerDrain(SpellEffIndex effIndex) // add spell damage bonus if (unitCaster) { - damage = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effIndex); + damage = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effIndex, 1, this); damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); } @@ -1172,9 +1172,9 @@ void Spell::EffectHeal(SpellEffIndex effIndex) } // Death Pact - return pct of max health to caster else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00080000) - addhealth = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, int32(unitCaster->CountPctFromMaxHealth(damage)), HEAL, effIndex); + addhealth = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, int32(unitCaster->CountPctFromMaxHealth(damage)), HEAL, effIndex, 1, this); else - addhealth = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, effIndex); + addhealth = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, addhealth, HEAL, effIndex, 1, this); addhealth = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, addhealth, HEAL); @@ -1200,7 +1200,7 @@ void Spell::EffectHealPct(SpellEffIndex effIndex) uint32 heal = unitTarget->CountPctFromMaxHealth(damage); if (unitCaster) { - heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, effIndex); + heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, effIndex, 1, this); heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL); } @@ -1222,7 +1222,7 @@ void Spell::EffectHealMechanical(SpellEffIndex effIndex) uint32 heal = damage; if (unitCaster) { - heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, effIndex); + heal = unitCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, heal, HEAL, effIndex, 1, this); heal = unitTarget->SpellHealingBonusTaken(unitCaster, m_spellInfo, heal, HEAL); } @@ -1239,7 +1239,7 @@ void Spell::EffectHealthLeech(SpellEffIndex effIndex) if (unitCaster) { - damage = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effIndex); + damage = unitCaster->SpellDamageBonusDone(unitTarget, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE, effIndex, 1, this); damage = unitTarget->SpellDamageBonusTaken(unitCaster, m_spellInfo, uint32(damage), SPELL_DIRECT_DAMAGE); } @@ -1260,7 +1260,7 @@ void Spell::EffectHealthLeech(SpellEffIndex effIndex) if (unitCaster && unitCaster->IsAlive()) { - healthGain = unitCaster->SpellHealingBonusDone(unitCaster, m_spellInfo, healthGain, HEAL, effIndex); + healthGain = unitCaster->SpellHealingBonusDone(unitCaster, m_spellInfo, healthGain, HEAL, effIndex, 1, this); healthGain = unitCaster->SpellHealingBonusTaken(unitCaster, m_spellInfo, healthGain, HEAL); HealInfo healInfo(unitCaster, unitCaster, healthGain, m_spellInfo, m_spellSchoolMask); @@ -2776,6 +2776,8 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) bool normalized = false; float weaponDamagePercentMod = 1.0f; + Mechanics mechanic = MECHANIC_NONE; + for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) { switch (m_spellInfo->Effects[j].Effect) @@ -2792,8 +2794,11 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) ApplyPct(weaponDamagePercentMod, damage); break; default: - break; // not weapon damage effect, just skip + continue; // not weapon damage effect, just skip } + + if (m_spellInfo->Effects[j].Mechanic != MECHANIC_NONE && mechanic == MECHANIC_NONE) + mechanic = m_spellInfo->Effects[j].Mechanic; } if (useWeaponDamage) @@ -2943,7 +2948,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) weaponDamage = std::max(weaponDamage, 0); // Add melee damage bonuses (also check for negative) - weaponDamage = unitCaster->MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, SPELL_DIRECT_DAMAGE, m_spellInfo, !useWeaponDamage); + weaponDamage = unitCaster->MeleeDamageBonusDone(unitTarget, weaponDamage, m_attackType, SPELL_DIRECT_DAMAGE, m_spellInfo, mechanic, !useWeaponDamage, m_spellSchoolMask, this); m_damage += unitTarget->MeleeDamageBonusTaken(unitCaster, weaponDamage, m_attackType, m_spellInfo); } diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index 5e5b2160cc..6668d9f30b 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -113,6 +113,27 @@ bool SpellScript::_Validate(SpellInfo const* entry) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnDestinationTargetSelect` of SpellScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); + if (CalcDamage.size()) + { + if (!entry->HasEffect(SPELL_EFFECT_SCHOOL_DAMAGE) + && !entry->HasEffect(SPELL_EFFECT_POWER_DRAIN) + && !entry->HasEffect(SPELL_EFFECT_HEALTH_LEECH) + && !entry->HasEffect(SPELL_EFFECT_WEAPON_DAMAGE) + && !entry->HasEffect(SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL) + && !entry->HasEffect(SPELL_EFFECT_NORMALIZED_WEAPON_DMG) + && !entry->HasEffect(SPELL_EFFECT_WEAPON_PERCENT_DAMAGE)) + TC_LOG_ERROR("scripts", "Spell `%u` script `%s` does not have a damage effect - handler bound to hook `CalcDamage` of SpellScript won't be executed", entry->Id, m_scriptName->c_str()); + } + + if (CalcHealing.size()) + { + if (!entry->HasEffect(SPELL_EFFECT_HEAL) + && !entry->HasEffect(SPELL_EFFECT_HEAL_PCT) + && !entry->HasEffect(SPELL_EFFECT_HEAL_MECHANICAL) + && !entry->HasEffect(SPELL_EFFECT_HEALTH_LEECH)) + TC_LOG_ERROR("scripts", "Spell `%u` script `%s` does not have a damage effect - handler bound to hook `CalcHealing` of SpellScript won't be executed", entry->Id, m_scriptName->c_str()); + } + return _SpellScript::_Validate(entry); } @@ -145,6 +166,17 @@ bool SpellScript::IsInCheckCastHook() const { return m_currentScriptState == SPELL_SCRIPT_HOOK_CHECK_CAST; } + +bool SpellScript::IsAfterTargetSelectionPhase() const +{ + return IsInHitPhase() + || IsInEffectHook() + || m_currentScriptState == SPELL_SCRIPT_HOOK_ON_CAST + || m_currentScriptState == SPELL_SCRIPT_HOOK_AFTER_CAST + || m_currentScriptState == SPELL_SCRIPT_HOOK_CALC_DAMAGE + || m_currentScriptState == SPELL_SCRIPT_HOOK_CALC_HEALING; +} + bool SpellScript::IsInTargetHook() const { switch (m_currentScriptState) @@ -514,6 +546,10 @@ bool AuraScript::_Validate(SpellInfo const* entry) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `DoEffectCalcSpellMod` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); + for (auto itr = DoEffectCalcDamageAndHealing.begin(); itr != DoEffectCalcDamageAndHealing.end(); ++itr) + if (!(*itr).GetAffectedEffectsMask(entry)) + TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `DoEffectCalcDamageAndHealing` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); + for (auto itr = OnEffectAbsorb.begin(); itr != OnEffectAbsorb.end(); ++itr) if (!(*itr).GetAffectedEffectsMask(entry)) TC_LOG_ERROR("scripts", "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectAbsorb` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); @@ -808,6 +844,7 @@ Unit* AuraScript::GetTarget() const case AURA_SCRIPT_HOOK_EFFECT_AFTER_APPLY: case AURA_SCRIPT_HOOK_EFFECT_AFTER_REMOVE: case AURA_SCRIPT_HOOK_EFFECT_PERIODIC: + case AURA_SCRIPT_HOOK_EFFECT_CALC_DAMAGE_AND_HEALING: case AURA_SCRIPT_HOOK_EFFECT_ABSORB: case AURA_SCRIPT_HOOK_EFFECT_AFTER_ABSORB: case AURA_SCRIPT_HOOK_EFFECT_MANASHIELD: diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index ea3040d186..37875118d6 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -479,6 +479,8 @@ enum SpellScriptHookType SPELL_SCRIPT_HOOK_BEFORE_CAST, SPELL_SCRIPT_HOOK_ON_CAST, SPELL_SCRIPT_HOOK_AFTER_CAST, + SPELL_SCRIPT_HOOK_CALC_DAMAGE, + SPELL_SCRIPT_HOOK_CALC_HEALING, SPELL_SCRIPT_HOOK_ON_SPELL_START }; @@ -494,10 +496,11 @@ class TC_GAME_API SpellScript : public _SpellScript // DO NOT OVERRIDE THESE IN SCRIPTS public: - using CheckCastHook = HookHandler; - using EffectNameHook = EffectNameHookHandler; - using HitHook = HookHandler; - using CastHook = HookHandler; + using CheckCastHook = HookHandler; + using EffectNameHook = EffectNameHookHandler; + using HitHook = HookHandler; + using CastHook = HookHandler; + using DamageAndHealingCalcHook = HookHandler; public: SpellScript() @@ -508,6 +511,7 @@ class TC_GAME_API SpellScript : public _SpellScript AfterCast(this), OnCheckCast(this), OnEffectLaunch(this), OnEffectLaunchTarget(this), OnEffectHit(this), OnEffectHitTarget(this), OnEffectSuccessfulDispel(this), + CalcDamage(this), CalcHealing(this), BeforeHit(this), OnHit(this), AfterHit(this), OnObjectAreaTargetSelect(this), OnObjectTargetSelect(this), OnDestinationTargetSelect(this) { @@ -521,6 +525,7 @@ class TC_GAME_API SpellScript : public _SpellScript void _PrepareScriptCall(SpellScriptHookType hookType); void _FinishScriptCall(); bool IsInCheckCastHook() const; + bool IsAfterTargetSelectionPhase() const; bool IsInTargetHook() const; bool IsInModifiableHook() const; bool IsInHitPhase() const; @@ -562,6 +567,16 @@ class TC_GAME_API SpellScript : public _SpellScript EffectNameHook::HookList OnEffectHitTarget; EffectNameHook::HookList OnEffectSuccessfulDispel; + public: // SpellScript interface - Calc Damage or Heal hooks + + // CalcDamage.Register(this, &class::function); + // Function is: void function(Unit* victim, int32& damage, int32& flatMod, float& pctMod); + DamageAndHealingCalcHook::HookList CalcDamage; + + // CalcHealing.Register(this, &class::function); + // Function is: void function(Unit* victim, int32& healing, int32& flatMod, float& pctMod); + DamageAndHealingCalcHook::HookList CalcHealing; + public: // SpellScript interface - Hit hooks // BeforeHit.Register(this, class::function); @@ -600,6 +615,8 @@ class TC_GAME_API SpellScript : public _SpellScript // 5. AfterCast - executed after spell missile is launched and immediate spell actions are done // 6. OnEffectLaunch - executed just before specified effect handler call - when spell missile is launched // 7. OnEffectLaunchTarget - executed just before specified effect handler call - when spell missile is launched - called for each target from spell target map + // 10a. CalcDamage - executed during specified effect handler call - when spell missile is launched - called for each target from spell target map + // 10b. CalcHealing - executed during specified effect handler call - when spell missile is launched - called for each target from spell target map // 8. OnEffectHit - executed just before specified effect handler call - when spell missile hits dest // 9. BeforeHit - executed just before spell hits a target - called for each target from spell target map // 10. OnEffectHitTarget - executed just before specified effect handler call - called for each target from spell target map @@ -720,6 +737,7 @@ enum AuraScriptHookType AURA_SCRIPT_HOOK_EFFECT_CALC_AMOUNT, AURA_SCRIPT_HOOK_EFFECT_CALC_PERIODIC, AURA_SCRIPT_HOOK_EFFECT_CALC_SPELLMOD, + AURA_SCRIPT_HOOK_EFFECT_CALC_DAMAGE_AND_HEALING, AURA_SCRIPT_HOOK_EFFECT_ABSORB, AURA_SCRIPT_HOOK_EFFECT_AFTER_ABSORB, AURA_SCRIPT_HOOK_EFFECT_MANASHIELD, @@ -751,13 +769,14 @@ class TC_GAME_API AuraScript : public _SpellScript // DO NOT OVERRIDE THESE IN SCRIPTS public: - using AuraCheckAreaTargetHookHandler = HookHandler; - using AuraDispelHookHandler = HookHandler; - using AuraEffectPeriodicHookHandler = AuraNameHookHandler; - using AuraEffectUpdatePeriodicHookHandler = AuraNameHookHandler; - using AuraEffectCalcAmountHookHandler = AuraNameHookHandler; - using AuraEffectCalcPeriodicHookHandler = AuraNameHookHandler; - using AuraEffectCalcSpellModHookHandler = AuraNameHookHandler; + using AuraCheckAreaTargetHookHandler = HookHandler; + using AuraDispelHookHandler = HookHandler; + using AuraEffectPeriodicHookHandler = AuraNameHookHandler; + using AuraEffectUpdatePeriodicHookHandler = AuraNameHookHandler; + using AuraEffectCalcAmountHookHandler = AuraNameHookHandler; + using AuraEffectCalcPeriodicHookHandler = AuraNameHookHandler; + using AuraEffectCalcSpellModHookHandler = AuraNameHookHandler; + using EffectCalcDamageAndHealingHookHandler = AuraNameHookHandler; template struct TC_GAME_API AuraEffectHandleModeHookHandler : AuraNameHookHandler @@ -868,6 +887,7 @@ class TC_GAME_API AuraScript : public _SpellScript OnEffectRemove(this), AfterEffectRemove(this), OnEffectPeriodic(this), OnEffectUpdatePeriodic(this), DoEffectCalcAmount(this), DoEffectCalcPeriodic(this), DoEffectCalcSpellMod(this), + DoEffectCalcDamageAndHealing(this), OnEffectAbsorb(this), AfterEffectAbsorb(this), OnEffectManaShield(this), AfterEffectManaShield(this), OnEffectSplit(this), @@ -965,6 +985,11 @@ class TC_GAME_API AuraScript : public _SpellScript // where function is: void function (AuraEffect const* aurEff, SpellModifier*& spellMod); AuraEffectCalcSpellModHookHandler::HookList DoEffectCalcSpellMod; + // executed when aura effect calculates damage or healing for dots and hots + // example: DoEffectCalcDamageAndHealing.Register(this, &class::function, EffectIndexSpecifier, EffectAuraNameSpecifier); + // where function is: void function (AuraEffect const* aurEff, Unit* victim, int32& damageOrHealing, int32& flatMod, float& pctMod); + EffectCalcDamageAndHealingHookHandler::HookList DoEffectCalcDamageAndHealing; + // executed when absorb aura effect is going to reduce damage // example: OnEffectAbsorb.Register(this, &class::function, EffectIndexSpecifier); // where function is: void function (AuraEffect const* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount);