From c3259dabcfbee5bb721772826a340460ab674cbc Mon Sep 17 00:00:00 2001 From: UltraNix <80540499+UltraNix@users.noreply.github.com> Date: Tue, 21 Sep 2021 14:02:06 +0200 Subject: [PATCH] fix(Core/Spells): Implemented PROC_EX_ONLY_FIRST_TICK (#7933) --- .../rev_1631823461170788600.sql | 5 ++++ src/server/game/Entities/Unit/Unit.cpp | 11 ++++---- src/server/game/Entities/Unit/Unit.h | 8 +++--- src/server/game/Spells/Spell.cpp | 2 ++ src/server/game/Spells/Spell.h | 6 ++++- src/server/game/Spells/SpellMgr.cpp | 26 +++++++++++++++---- src/server/game/Spells/SpellMgr.h | 3 ++- 7 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1631823461170788600.sql diff --git a/data/sql/updates/pending_db_world/rev_1631823461170788600.sql b/data/sql/updates/pending_db_world/rev_1631823461170788600.sql new file mode 100644 index 000000000..c43b0ec05 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1631823461170788600.sql @@ -0,0 +1,5 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1631823461170788600'); + +UPDATE `spell_proc_event` SET `procEx`=0x0200003 WHERE `entry`=-11213; +UPDATE `spell_proc_event` SET `procEx`=0x0200002 WHERE `entry`=16164; +UPDATE `spell_proc_event` SET `procEx`=0x0210000 WHERE `entry`=16864; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 29d038163..4c30e03b4 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -142,7 +142,7 @@ void DamageInfo::BlockDamage(uint32 amount) m_damage -= amount; } -ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo, SpellInfo const* triggeredByAuraSpell, int8 procAuraEffectIndex) +ProcEventInfo::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, int8 procAuraEffectIndex) : _actor(actor), _actionTarget(actionTarget), _procTarget(procTarget), _typeMask(typeMask), _spellTypeMask(spellTypeMask), _spellPhaseMask(spellPhaseMask), _hitMask(hitMask), _spell(spell), _damageInfo(damageInfo), _healInfo(healInfo), _triggeredByAuraSpell(triggeredByAuraSpell), _procAuraEffectIndex(procAuraEffectIndex) { @@ -15407,7 +15407,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u DamageInfo damageInfo = DamageInfo(actor, actionTarget, damage, procSpellInfo, procSpellInfo ? SpellSchoolMask(procSpellInfo->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE); HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpellInfo, procSpellInfo ? SpellSchoolMask(procSpellInfo->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL); - ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, nullptr, &damageInfo, &healInfo, procAura, procAuraEffectIndex); + ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, procSpell, &damageInfo, &healInfo, procAura, procAuraEffectIndex); ProcTriggeredList procTriggered; // Fill procTriggered list @@ -15439,7 +15439,7 @@ 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, procSpellInfo, procFlag, procExtra, attType, isVictim, active, triggerData.spellProcEvent, eventInfo)) + if (!IsTriggeredAtSpellProcEvent(target, triggerData.aura, attType, isVictim, active, triggerData.spellProcEvent, eventInfo)) continue; // do checks using conditions table @@ -16483,9 +16483,10 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) return true; } -bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent, ProcEventInfo const& eventInfo) +bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent, ProcEventInfo const& eventInfo) { SpellInfo const* spellProto = aura->GetSpellInfo(); + SpellInfo const* procSpell = eventInfo.GetSpellInfo(); // let the aura be handled by new proc system if it has new entry if (sSpellMgr->GetSpellProcEntry(spellProto->Id)) @@ -16520,7 +16521,7 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const return false; // Check spellProcEvent data requirements - if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellProto, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) + if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellProto, spellProcEvent, EventProcFlag, eventInfo, active)) return false; // In most cases req get honor or XP from kill if (EventProcFlag & PROC_FLAG_KILL && GetTypeId() == TYPEID_PLAYER) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index eab47bb63..5b54d050c 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -958,14 +958,14 @@ private: uint32 _spellPhaseMask; uint32 _hitMask; uint32 _cooldown; - Spell* _spell; + Spell const* _spell; DamageInfo* _damageInfo; HealInfo* _healInfo; SpellInfo const* const _triggeredByAuraSpell; int8 _procAuraEffectIndex; public: - explicit ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, uint32 typeMask, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo, SpellInfo const* triggeredByAuraSpell = nullptr, int8 procAuraEffectIndex = -1); + 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); Unit* GetActor() { return _actor; }; [[nodiscard]] Unit* GetActionTarget() const { return _actionTarget; } [[nodiscard]] Unit* GetProcTarget() const { return _procTarget; } @@ -975,7 +975,7 @@ public: [[nodiscard]] uint32 GetHitMask() const { return _hitMask; } [[nodiscard]] SpellInfo const* GetSpellInfo() const; [[nodiscard]] SpellSchoolMask GetSchoolMask() const { return SPELL_SCHOOL_MASK_NONE; } - [[nodiscard]] Spell* GetProcSpell() const { return _spell; } + [[nodiscard]] Spell const* GetProcSpell() const { return _spell; } [[nodiscard]] DamageInfo* GetDamageInfo() const { return _damageInfo; } [[nodiscard]] HealInfo* GetHealInfo() const { return _healInfo; } [[nodiscard]] SpellInfo const* GetTriggerAuraSpell() const { return _triggeredByAuraSpell; } @@ -2571,7 +2571,7 @@ protected: bool _instantCast; private: - bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent, ProcEventInfo const& eventInfo); + bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent, ProcEventInfo const& eventInfo); bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool* handled); bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index f2169de5f..969a545c8 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -641,6 +641,7 @@ Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, m_preCastSpell = 0; m_triggeredByAuraSpell = nullptr; m_triggeredByAuraEffectIndex = -1; + m_triggeredByAuraTickNumber = 0; m_spellAura = nullptr; m_pathFinder = nullptr; // pussywizard _scriptsLoaded = false; @@ -3238,6 +3239,7 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const { m_triggeredByAuraSpell = triggeredByAura->GetSpellInfo(); m_triggeredByAuraEffectIndex = triggeredByAura->GetEffIndex(); + m_triggeredByAuraTickNumber = triggeredByAura->GetTickNumber(); } // create and add update event for this spell diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 8d9ff7a88..89f9c86af 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -545,7 +545,10 @@ public: // xinef: moved to public void LoadScripts(); std::list* GetUniqueTargetInfo() { return &m_UniqueTargetInfo; } -protected: + + [[nodiscard]] uint32 GetTriggeredByAuraTickNumber() const { return m_triggeredByAuraTickNumber; } + + protected: bool HasGlobalCooldown() const; void TriggerGlobalCooldown(); void CancelGlobalCooldown(); @@ -722,6 +725,7 @@ protected: // and in same time need aura data and after aura deleting. SpellInfo const* m_triggeredByAuraSpell; int8 m_triggeredByAuraEffectIndex; + uint32 m_triggeredByAuraTickNumber; bool m_skipCheck; uint8 m_auraScaleMask; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index e467432ca..8053942f3 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -724,11 +724,15 @@ SpellProcEventEntry const* SpellMgr::GetSpellProcEvent(uint32 spellId) const return nullptr; } -bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) const +bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, ProcEventInfo const& eventInfo, bool active) const { // No extra req need uint32 procEvent_procEx = PROC_EX_NONE; + uint32 procFlags = eventInfo.GetTypeMask(); + uint32 procExtra = eventInfo.GetHitMask(); + SpellInfo const* procSpellInfo = eventInfo.GetSpellInfo(); + // check prockFlags for condition if ((procFlags & EventProcFlag) == 0) return false; @@ -786,7 +790,7 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, Spell procEvent_procEx = spellProcEvent->procEx; // For melee triggers - if (procSpell == nullptr) + if (!procSpellInfo) { // Check (if set) for school (melee attack have Normal school) if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0) @@ -795,23 +799,35 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, Spell else // For spells need check school/spell family/family mask { // Check (if set) for school - if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpell->SchoolMask) == 0) + if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpellInfo->SchoolMask) == 0) return false; // Check (if set) for spellFamilyName - if (spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName)) + if (spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpellInfo->SpellFamilyName)) return false; // spellFamilyName is Ok need check for spellFamilyMask if present if (spellProcEvent->spellFamilyMask) { - if (!(spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags)) + if (!(spellProcEvent->spellFamilyMask & procSpellInfo->SpellFamilyFlags)) return false; hasFamilyMask = true; // Some spells are not considered as active even with have spellfamilyflags if (!(procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL)) active = true; } + + // Check tick numbers + if (procEvent_procEx & PROC_EX_ONLY_FIRST_TICK) + { + if (Spell const* procSpell = eventInfo.GetProcSpell()) + { + if (procSpell->GetTriggeredByAuraTickNumber() > 1) + { + return false; + } + } + } } } diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 8ae5d9edb..60690446f 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -201,6 +201,7 @@ enum ProcFlagsExLegacy PROC_EX_ONLY_ACTIVE_SPELL = 0x0040000, // Spell has to do damage/heal to proc PROC_EX_NO_OVERHEAL = 0x0080000, // Proc if heal did some work PROC_EX_NO_AURA_REFRESH = 0x0100000, // Proc if aura was not refreshed + PROC_EX_ONLY_FIRST_TICK = 0x0200000, // Proc only on first tick (in case of periodic spells) // Flags for internal use - do not use these in db! PROC_EX_INTERNAL_CANT_PROC = 0x0800000, @@ -650,7 +651,7 @@ public: // Spell proc event table [[nodiscard]] SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const; - bool IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) const; + bool IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, ProcEventInfo const& eventInfo, bool active) const; // Spell proc table [[nodiscard]] SpellProcEntry const* GetSpellProcEntry(uint32 spellId) const;