diff --git a/data/sql/updates/pending_db_world/rev_1639592441019507900.sql b/data/sql/updates/pending_db_world/rev_1639592441019507900.sql new file mode 100644 index 000000000..b5b2efdde --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1639592441019507900.sql @@ -0,0 +1,3 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1639592441019507900'); + +ALTER TABLE `spell_proc_event` ADD COLUMN `procPhase` INT UNSIGNED DEFAULT 0 NOT NULL AFTER `procEx`; diff --git a/data/sql/updates/pending_db_world/rev_1639592443028716900.sql b/data/sql/updates/pending_db_world/rev_1639592443028716900.sql new file mode 100644 index 000000000..f9c1c25fd --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1639592443028716900.sql @@ -0,0 +1,17 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1639592443028716900'); + +UPDATE `spell_proc_event` SET `procPhase`=1 WHERE `entry` IN (-16880,32748,59887,-51521,18820,24389,31794,33299,36032,37214,37601,39530,40442,43819, +44543,44545,59630,65005,67386,67389,67667,67670,67698,67752,69762,70811,71585,71602,71645,72419,74396,75465,75474); +UPDATE `spell_proc_event` SET `procPhase`=3 WHERE `entry` IN (44543,44545); +UPDATE `spell_proc_event` SET `procPhase`=4 WHERE `entry` IN (-59088,-56342,-31571,-19184,-14156,32216,37168,46916,51698,51700,51701,52437); + +DELETE FROM `spell_proc_event` WHERE `entry` IN (16166,16188,16246,16886,17116,4341,28200,63280,65007); +INSERT INTO `spell_proc_event` (`entry`,`SchoolMask`,`SpellFamilyName`,`procPhase`) VALUES +(16166,0,0,1), +(16188,8,11,1), +(16246,0,0,1), +(17116,8,7,1), +(4341,0,0,1), +(28200,0,0,1), +(63280,0,11,1), +(65007,0,0,1); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index b9fba23d4..332ccdbca 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -5849,15 +5849,15 @@ void Unit::SendSpellNonMeleeDamageLog(Unit* target, SpellInfo const* spellInfo, SendSpellNonMeleeDamageLog(&log); } -void Unit::ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 amount, WeaponAttackType attType, SpellInfo const* procSpellInfo, SpellInfo const* procAura, int8 procAuraEffectIndex, Spell const* procSpell, DamageInfo* damageInfo, HealInfo* healInfo) +void Unit::ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 amount, WeaponAttackType attType, SpellInfo const* procSpellInfo, SpellInfo const* procAura, int8 procAuraEffectIndex, Spell const* procSpell, DamageInfo* damageInfo, HealInfo* healInfo, uint32 procPhase) { // Not much to do if no flags are set. if (procAttacker) - ProcDamageAndSpellFor(false, victim, procAttacker, procExtra, attType, procSpellInfo, amount, procAura, procAuraEffectIndex, procSpell, damageInfo, healInfo); + ProcDamageAndSpellFor(false, victim, procAttacker, procExtra, attType, procSpellInfo, amount, procAura, procAuraEffectIndex, procSpell, damageInfo, healInfo, procPhase); // Now go on with a victim's events'n'auras // Not much to do if no flags are set or there is no victim if (victim && victim->IsAlive() && procVictim) - victim->ProcDamageAndSpellFor(true, this, procVictim, procExtra, attType, procSpellInfo, amount, procAura, procAuraEffectIndex, procSpell, damageInfo, healInfo); + victim->ProcDamageAndSpellFor(true, this, procVictim, procExtra, attType, procSpellInfo, amount, procAura, procAuraEffectIndex, procSpell, damageInfo, healInfo, procPhase); } void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo* pInfo) @@ -8403,7 +8403,7 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp return false; } -bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown) +bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown, uint32 procPhase) { // Get triggered aura spell info SpellInfo const* auraSpellInfo = triggeredByAura->GetSpellInfo(); @@ -9295,18 +9295,21 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg // Fingers of Frost, synchronise with Frostbite case 44544: { - // Find Frostbite - if (AuraEffect* aurEff = this->GetAuraEffect(SPELL_AURA_ADD_TARGET_TRIGGER, SPELLFAMILY_MAGE, 119, EFFECT_0)) + if (procPhase == PROC_SPELL_PHASE_HIT) { - if (!victim) - return false; + // Find Frostbite + if (AuraEffect* aurEff = this->GetAuraEffect(SPELL_AURA_ADD_TARGET_TRIGGER, SPELLFAMILY_MAGE, 119, EFFECT_0)) + { + if (!victim) + return false; - uint8 fofRank = sSpellMgr->GetSpellRank(triggeredByAura->GetId()); - uint8 fbRank = sSpellMgr->GetSpellRank(aurEff->GetId()); - uint8 chance = uint8(std::ceil(fofRank * fbRank * 16.6f)); + uint8 fofRank = sSpellMgr->GetSpellRank(triggeredByAura->GetId()); + uint8 fbRank = sSpellMgr->GetSpellRank(aurEff->GetId()); + uint8 chance = uint8(std::ceil(fofRank * fbRank * 16.6f)); - if (roll_chance_i(chance)) - CastSpell(victim, aurEff->GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true); + if (roll_chance_i(chance)) + CastSpell(victim, aurEff->GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true); + } } break; } @@ -15503,13 +15506,13 @@ uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missC return procEx; } -void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpellInfo, uint32 damage, SpellInfo const* procAura, int8 procAuraEffectIndex, Spell const* procSpell, DamageInfo* damageInfo, HealInfo* healInfo) +void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpellInfo, uint32 damage, SpellInfo const* procAura, int8 procAuraEffectIndex, Spell const* procSpell, DamageInfo* damageInfo, HealInfo* healInfo, uint32 procPhase) { // Player is loaded now - do not allow passive spell casts to proc if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->GetSession()->PlayerLoading()) return; // For melee/ranged based attack need update skills and set some Aura states if victim present - if (procFlag & MELEE_BASED_TRIGGER_MASK && target) + if (procFlag & MELEE_BASED_TRIGGER_MASK && target && procPhase == PROC_SPELL_PHASE_HIT) { // Xinef: Shaman in ghost wolf form cant proc anything melee based if (!isVictim && GetShapeshiftForm() == FORM_GHOSTWOLF) @@ -15598,7 +15601,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u Unit* actor = isVictim ? target : this; Unit* actionTarget = !isVictim ? target : this; - ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, procSpell, damageInfo, healInfo, procAura, procAuraEffectIndex); + ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, procPhase, procExtra, procSpell, damageInfo, healInfo, procAura, procAuraEffectIndex); ProcTriggeredList procTriggered; // Fill procTriggered list @@ -15788,7 +15791,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u { LOG_DEBUG("spells.aura", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); // Don`t drop charge or add cooldown for not started trigger - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpellInfo, procFlag, procExtra, cooldown)) + if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpellInfo, procFlag, procExtra, cooldown, procPhase)) takeCharges = true; break; } @@ -15847,7 +15850,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u { LOG_DEBUG("spells.aura", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpellInfo, procFlag, procExtra, cooldown)) + if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpellInfo, procFlag, procExtra, cooldown, procPhase)) takeCharges = true; break; } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 1bf82dcb1..0917ef592 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1492,8 +1492,8 @@ public: static void Kill(Unit* killer, Unit* victim, bool durabilityLoss = true, WeaponAttackType attackType = BASE_ATTACK, SpellInfo const* spellProto = nullptr); static int32 DealHeal(Unit* healer, Unit* victim, uint32 addhealth); - void ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellInfo const* procSpellInfo = nullptr, SpellInfo const* procAura = nullptr, int8 procAuraEffectIndex = -1, Spell const* procSpell = nullptr, DamageInfo* damageInfo = nullptr, HealInfo* healInfo = nullptr); - void ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpellInfo, uint32 damage, SpellInfo const* procAura = nullptr, int8 procAuraEffectIndex = -1, Spell const* procSpell = nullptr, DamageInfo* damageInfo = nullptr, HealInfo* healInfo = nullptr); + void ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellInfo const* procSpellInfo = nullptr, SpellInfo const* procAura = nullptr, int8 procAuraEffectIndex = -1, Spell const* procSpell = nullptr, DamageInfo* damageInfo = nullptr, HealInfo* healInfo = nullptr, uint32 procPhase = 2 /*PROC_SPELL_PHASE_HIT*/); + void ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpellInfo, uint32 damage, SpellInfo const* procAura = nullptr, int8 procAuraEffectIndex = -1, Spell const* procSpell = nullptr, DamageInfo* damageInfo = nullptr, HealInfo* healInfo = nullptr, uint32 procPhase = 2 /*PROC_SPELL_PHASE_HIT*/); void GetProcAurasTriggeredOnEvent(std::list& aurasTriggeringProc, std::list* procAuras, ProcEventInfo eventInfo); void TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo); @@ -2464,7 +2464,7 @@ private: 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); + bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, uint32 procPhase); bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown); bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura); bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 1473a2586..ca200351d 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3720,6 +3720,51 @@ void Spell::_cast(bool skipCheck) // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()... SendSpellGo(); + if (modOwner) + modOwner->SetSpellModTakingSpell(this, false); + + if (m_originalCaster) + { + // Handle procs on cast + uint32 procAttacker = m_procAttacker; + if (!procAttacker) + { + bool IsPositive = m_spellInfo->IsPositive(); + if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) + { + procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; + } + else + { + procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG; + } + } + + uint32 procEx = PROC_EX_NORMAL_HIT; + + for (std::list::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + { + if (ihit->missCondition != SPELL_MISS_NONE) + { + continue; + } + + if (!ihit->crit) + { + continue; + } + + procEx |= PROC_EX_CRITICAL_HIT; + break; + } + + m_originalCaster->ProcDamageAndSpell(m_originalCaster, procAttacker, PROC_FLAG_NONE, procEx, 1, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell.spellInfo, + m_triggeredByAuraSpell.effectIndex, this, nullptr, nullptr, PROC_SPELL_PHASE_CAST); + } + + if (modOwner) + modOwner->SetSpellModTakingSpell(this, true); + // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells if ((m_spellInfo->Speed > 0.0f && !m_spellInfo->IsChanneled())/* xinef: we dont need this || m_spellInfo->Id == 14157*/) { @@ -3974,6 +4019,44 @@ void Spell::_handle_finish_phase() if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->IsCooldownStartedOnEvent()) m_caster->ToPlayer()->SendCooldownEvent(m_spellInfo); } + + // Handle procs on finish + if (m_originalCaster) + { + uint32 procAttacker = m_procAttacker; + if (!procAttacker) + { + bool IsPositive = m_spellInfo->IsPositive(); + if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) + { + procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG; + } + else + { + procAttacker = IsPositive ? PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS : PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG; + } + } + + uint32 procEx = PROC_EX_NORMAL_HIT; + for (std::list::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + { + if (ihit->missCondition != SPELL_MISS_NONE) + { + continue; + } + + if (!ihit->crit) + { + continue; + } + + procEx |= PROC_EX_CRITICAL_HIT; + break; + } + + m_originalCaster->ProcDamageAndSpell(m_originalCaster, procAttacker, PROC_FLAG_NONE, procEx, 1, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell.spellInfo, + m_triggeredByAuraSpell.effectIndex, this, nullptr, nullptr, PROC_SPELL_PHASE_FINISH); + } } void Spell::SendSpellCooldown() diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 937788a00..4203e2ed5 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -738,9 +738,11 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, Spell { // No extra req need uint32 procEvent_procEx = PROC_EX_NONE; + uint32 procEvent_procPhase = PROC_SPELL_PHASE_HIT; uint32 procFlags = eventInfo.GetTypeMask(); uint32 procExtra = eventInfo.GetHitMask(); + uint32 procPhase = eventInfo.GetSpellPhaseMask(); SpellInfo const* procSpellInfo = eventInfo.GetSpellInfo(); // check prockFlags for condition @@ -798,6 +800,7 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, Spell { // Store extra req procEvent_procEx = spellProcEvent->procEx; + procEvent_procPhase = spellProcEvent->procPhase; // For melee triggers if (!procSpellInfo) @@ -847,6 +850,11 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, Spell return false; } + if (!(procEvent_procPhase & procPhase)) + { + return false; + } + // Check for extra req (if none) and hit/crit if (procEvent_procEx == PROC_EX_NONE) { @@ -1730,8 +1738,8 @@ void SpellMgr::LoadSpellProcEvents() mSpellProcEventMap.clear(); // need for reload case - // 0 1 2 3 4 5 6 7 8 9 10 - QueryResult result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event"); + // 0 1 2 3 4 5 6 7 8 9 10 11 + QueryResult result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, procPhase, ppmRate, CustomChance, Cooldown FROM spell_proc_event"); if (!result) { LOG_INFO("server.loading", ">> Loaded 0 spell proc event conditions. DB table `spell_proc_event` is empty."); @@ -1781,9 +1789,16 @@ void SpellMgr::LoadSpellProcEvents() spellProcEvent.spellFamilyMask[2] = fields[5].GetUInt32(); spellProcEvent.procFlags = fields[6].GetUInt32(); spellProcEvent.procEx = fields[7].GetUInt32(); - spellProcEvent.ppmRate = fields[8].GetFloat(); - spellProcEvent.customChance = fields[9].GetFloat(); - spellProcEvent.cooldown = fields[10].GetUInt32(); + spellProcEvent.procPhase = fields[8].GetUInt32(); + spellProcEvent.ppmRate = fields[9].GetFloat(); + spellProcEvent.customChance = fields[10].GetFloat(); + spellProcEvent.cooldown = fields[11].GetUInt32(); + + // PROC_SPELL_PHASE_NONE is by default PROC_SPELL_PHASE_HIT + if (spellProcEvent.procPhase == PROC_SPELL_PHASE_NONE) + { + spellProcEvent.procPhase = PROC_SPELL_PHASE_HIT; + } while (spellInfo) { diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 1c0504830..c847f2f7c 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -278,6 +278,7 @@ struct SpellProcEventEntry flag96 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do) uint32 procFlags; // bitmask for matching proc event uint32 procEx; // proc Extend info (see ProcFlagsEx) + uint32 procPhase; // proc phase (see ProcFlagsSpellPhase) float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc float customChance; // Owerride chance (in most cases for debug only) uint32 cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_