diff --git a/data/sql/updates/pending_db_world/rev_1662888682772550200.sql b/data/sql/updates/pending_db_world/rev_1662888682772550200.sql new file mode 100644 index 000000000..63b38ab26 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1662888682772550200.sql @@ -0,0 +1,7 @@ +-- +DELETE FROM `spell_script_names` WHERE `spell_id` IN (-44543,74396); +INSERT INTO `spell_script_names` VALUES +(-44543, 'spell_mage_fingers_of_frost_proc_aura'), +(74396, 'spell_mage_fingers_of_frost_proc'); + +UPDATE `spell_proc_event` SET `procPhase`=7 WHERE `entry` IN (44543,44545); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index fb9e51ff6..33e7e4c0f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -169,6 +169,7 @@ ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, : _actor(actor), _actionTarget(actionTarget), _procTarget(procTarget), _typeMask(typeMask), _spellTypeMask(spellTypeMask), _spellPhaseMask(spellPhaseMask), _hitMask(hitMask), _spell(spell), _damageInfo(damageInfo), _healInfo(healInfo), _triggeredByAuraSpell(triggeredByAuraSpell), _procAuraEffectIndex(procAuraEffectIndex) { + _chance.reset(); } SpellInfo const* ProcEventInfo::GetSpellInfo() const @@ -15852,18 +15853,29 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (!active && !isVictim && !(procFlag & PROC_FLAG_DONE_PERIODIC) && procSpellInfo && procSpellInfo->SpellFamilyName && (procSpellInfo->HasAura(SPELL_AURA_PERIODIC_DAMAGE) || procSpellInfo->HasAura(SPELL_AURA_PERIODIC_HEAL))) active = true; - if (!IsTriggeredAtSpellProcEvent(target, triggerData.aura, attType, isVictim, active, triggerData.spellProcEvent, eventInfo)) + // AuraScript Hook + if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo)) + { continue; + } + + bool isTriggeredAtSpellProcEvent = IsTriggeredAtSpellProcEvent(target, triggerData.aura, attType, isVictim, active, triggerData.spellProcEvent, eventInfo); + + // AuraScript Hook + triggerData.aura->CallScriptCheckAfterProcHandlers(itr->second, eventInfo); + + if (!isTriggeredAtSpellProcEvent) + { + continue; + } // do checks using conditions table ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_PROC, spellProto->Id); ConditionSourceInfo condInfo = ConditionSourceInfo(eventInfo.GetActor(), eventInfo.GetActionTarget()); if (!sConditionMgr->IsObjectMeetToConditions(condInfo, conditions)) + { continue; - - // AuraScript Hook - if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo)) - continue; + } // Triggered spells not triggering additional spells //bool triggered = !spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_FROM_PROCS) ? @@ -17164,11 +17176,17 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, WeaponAttackTyp } } + if (eventInfo.GetProcChance()) + { + chance = *eventInfo.GetProcChance(); + } + // Apply chance modifer aura if (Player* modOwner = GetSpellModOwner()) { modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); } + return roll_chance_f(chance); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 8d87b3062..383b45247 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -25,6 +25,7 @@ #include "HostileRefMgr.h" #include "MotionMaster.h" #include "Object.h" +#include "Optional.h" #include "SpellAuraDefines.h" #include "SpellDefines.h" #include "ThreatMgr.h" @@ -836,6 +837,7 @@ private: HealInfo* _healInfo; SpellInfo const* const _triggeredByAuraSpell; int8 _procAuraEffectIndex; + std::optional _chance; public: explicit ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell const* spell, DamageInfo* damageInfo, HealInfo* healInfo, SpellInfo const* triggeredByAuraSpell = nullptr, int8 procAuraEffectIndex = -1); @@ -855,6 +857,9 @@ public: [[nodiscard]] int8 GetTriggerAuraEffectIndex() const { return _procAuraEffectIndex; } [[nodiscard]] uint32 GetProcCooldown() const { return _cooldown; } void SetProcCooldown(uint32 cooldown) { _cooldown = cooldown; } + [[nodiscard]] std::optional GetProcChance() const { return _chance; } + void SetProcChance(float chance) { _chance = chance; } + void ResetProcChance() { _chance.reset(); } }; // Struct for use in Unit::CalculateMeleeDamage diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 0a9e80332..822fa289a 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -2620,6 +2620,22 @@ bool Aura::CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventI return result; } +bool Aura::CallScriptCheckAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) +{ + bool result = true; + for (std::list::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_AFTER_PROC, aurApp); + std::list::iterator hookItrEnd = (*scritr)->DoCheckAfterProc.end(), hookItr = (*scritr)->DoCheckAfterProc.begin(); + for (; hookItr != hookItrEnd; ++hookItr) + result &= hookItr->Call(*scritr, eventInfo); + + (*scritr)->_FinishScriptCall(); + } + + return result; +} + bool Aura::CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) { bool prepare = true; diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 4600a4242..d98f391dc 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -226,6 +226,7 @@ public: // Spell Proc Hooks bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); + bool CallScriptCheckAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); void CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index aa354377a..4842cede7 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -733,6 +733,10 @@ bool AuraScript::_Validate(SpellInfo const* entry) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) LOG_ERROR("spells.scripts", "Spell `{}` of script `{}` does not have apply aura effect - handler bound to hook `DoCheckProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); + for (std::list::iterator itr = DoCheckAfterProc.begin(); itr != DoCheckAfterProc.end(); ++itr) + if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) + LOG_ERROR("spells.scripts", "Spell `{}` of script `{}` does not have apply aura effect - handler bound to hook `DoCheckAfterProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); + for (std::list::iterator itr = DoPrepareProc.begin(); itr != DoPrepareProc.end(); ++itr) if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) LOG_ERROR("spells.scripts", "Spell `{}` of script `{}` does not have apply aura effect - handler bound to hook `DoPrepareProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); @@ -1163,6 +1167,7 @@ Unit* AuraScript::GetTarget() const case AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD: case AURA_SCRIPT_HOOK_EFFECT_SPLIT: case AURA_SCRIPT_HOOK_CHECK_PROC: + case AURA_SCRIPT_HOOK_CHECK_AFTER_PROC: case AURA_SCRIPT_HOOK_PREPARE_PROC: case AURA_SCRIPT_HOOK_PROC: case AURA_SCRIPT_HOOK_AFTER_PROC: diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 138dafeeb..b52d2215b 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -499,6 +499,7 @@ enum AuraScriptHookType AURA_SCRIPT_HOOK_AFTER_DISPEL, // Spell Proc Hooks AURA_SCRIPT_HOOK_CHECK_PROC, + AURA_SCRIPT_HOOK_CHECK_AFTER_PROC, AURA_SCRIPT_HOOK_PREPARE_PROC, AURA_SCRIPT_HOOK_PROC, AURA_SCRIPT_HOOK_EFFECT_PROC, @@ -804,6 +805,10 @@ public: // example: DoCheckProc += AuraCheckProcFn(class::function); // where function is: bool function (ProcEventInfo& eventInfo); HookList DoCheckProc; + // executed when aura checks if it can proc + // example: DoCheckAfterProc += AuraCheckProcFn(class::function); + // where function is: bool function (ProcEventInfo& eventInfo); + HookList DoCheckAfterProc; #define AuraCheckProcFn(F) CheckProcHandlerFunction(&F) // executed before aura procs (possibility to prevent charge drop/cooldown) diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index c00f88157..699ae8ff4 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -55,6 +55,7 @@ enum MageSpells SPELL_MAGE_SUMMON_WATER_ELEMENTAL_PERMANENT = 70908, SPELL_MAGE_SUMMON_WATER_ELEMENTAL_TEMPORARY = 70907, SPELL_MAGE_GLYPH_OF_BLAST_WAVE = 62126, + SPELL_MAGE_FINGERS_OF_FROST = 44543 }; class spell_mage_arcane_blast : public SpellScript @@ -932,6 +933,114 @@ class spell_mage_summon_water_elemental : public SpellScript } }; +#define FingersOfFrostScriptName "spell_mage_fingers_of_frost_proc_aura" +class spell_mage_fingers_of_frost_proc_aura : public AuraScript +{ PrepareAuraScript(spell_mage_fingers_of_frost_proc_aura); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetSpellPhaseMask() != PROC_SPELL_PHASE_CAST) + { + eventInfo.SetProcChance(_chance); + } + + return true; + } + + bool CheckAfterProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetSpellPhaseMask() != PROC_SPELL_PHASE_CAST) + { + eventInfo.ResetProcChance(); + } + + return true; + } + + void HandleOnEffectProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + if (eventInfo.GetSpellPhaseMask() == PROC_SPELL_PHASE_CAST) + { + _chance = 100.f; + _spell = eventInfo.GetProcSpell(); + + if (!_spell || _spell->GetDelayMoment() <= 0) + { + PreventDefaultAction(); + } + } + else + { + if (eventInfo.GetSpellPhaseMask() == PROC_SPELL_PHASE_FINISH || ((_spell && _spell->GetDelayMoment() > 0) || !eventInfo.GetDamageInfo())) + { + PreventDefaultAction(); + } + + _chance = 0.f; + _spell = nullptr; + } + } + + void HandleAfterEffectProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) + { + if (eventInfo.GetSpellPhaseMask() == PROC_SPELL_PHASE_HIT) + { + _chance = 100.f; + } + else if (eventInfo.GetSpellPhaseMask() == PROC_SPELL_PHASE_FINISH) + { + _chance = 0.f; + _spell = nullptr; + } + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_mage_fingers_of_frost_proc_aura::CheckProc); + DoCheckAfterProc += AuraCheckProcFn(spell_mage_fingers_of_frost_proc_aura::CheckAfterProc); + OnEffectProc += AuraEffectProcFn(spell_mage_fingers_of_frost_proc_aura::HandleOnEffectProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + AfterEffectProc += AuraEffectProcFn(spell_mage_fingers_of_frost_proc_aura::HandleAfterEffectProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + +public: + Spell const* GetProcSpell() const { return _spell; } + +private: + float _chance = 0.f; + Spell const* _spell = nullptr; +}; + +typedef spell_mage_fingers_of_frost_proc_aura spell_mage_fingers_of_frost_proc_aura_script; + +class spell_mage_fingers_of_frost_proc : public AuraScript +{ + PrepareAuraScript(spell_mage_fingers_of_frost_proc); + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (Aura* aura = GetCaster()->GetAuraOfRankedSpell(SPELL_MAGE_FINGERS_OF_FROST)) + { + if (spell_mage_fingers_of_frost_proc_aura_script* script = dynamic_cast(aura->GetScriptByName(FingersOfFrostScriptName))) + { + if (Spell const* fofProcSpell = script->GetProcSpell()) + { + if (fofProcSpell == eventInfo.GetProcSpell()) + { + return false; + } + } + } + } + + return true; + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_mage_fingers_of_frost_proc::CheckProc); + } +}; + void AddSC_mage_spell_scripts() { RegisterSpellScript(spell_mage_arcane_blast); @@ -955,4 +1064,6 @@ void AddSC_mage_spell_scripts() RegisterSpellScript(spell_mage_master_of_elements); RegisterSpellScript(spell_mage_polymorph_cast_visual); RegisterSpellScript(spell_mage_summon_water_elemental); + RegisterSpellScript(spell_mage_fingers_of_frost_proc_aura); + RegisterSpellScript(spell_mage_fingers_of_frost_proc); }