mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-03-07 01:30:31 +00:00
refactor(Core/Spells): QAston proc system (#24233)
Co-authored-by: blinkysc <blinkysc@users.noreply.github.com> Co-authored-by: QAston <qaston@gmail.com> Co-authored-by: joschiwald <joschiwald@online.de> Co-authored-by: ariel- <ariel-@users.noreply.github.com> Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Co-authored-by: blinkysc <your-github-email@example.com> Co-authored-by: Tereneckla <Tereneckla@users.noreply.github.com> Co-authored-by: Andrew <47818697+Nyeriah@users.noreply.github.com>
This commit is contained in:
@@ -104,8 +104,8 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS] =
|
||||
&AuraEffect::HandleAuraModSchoolImmunity, // 39 SPELL_AURA_SCHOOL_IMMUNITY
|
||||
&AuraEffect::HandleAuraModDmgImmunity, // 40 SPELL_AURA_DAMAGE_IMMUNITY
|
||||
&AuraEffect::HandleAuraModDispelImmunity, // 41 SPELL_AURA_DISPEL_IMMUNITY
|
||||
&AuraEffect::HandleNoImmediateEffect, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Unit::ProcDamageAndSpellFor and Unit::HandleProcTriggerSpell
|
||||
&AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Unit::ProcDamageAndSpellFor
|
||||
&AuraEffect::HandleNoImmediateEffect, // 42 SPELL_AURA_PROC_TRIGGER_SPELL implemented in Aura::TriggerProcOnEvent
|
||||
&AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in Aura::TriggerProcOnEvent
|
||||
&AuraEffect::HandleAuraTrackCreatures, // 44 SPELL_AURA_TRACK_CREATURES
|
||||
&AuraEffect::HandleAuraTrackResources, // 45 SPELL_AURA_TRACK_RESOURCES
|
||||
&AuraEffect::HandleNULL, // 46 SPELL_AURA_46 (used in test spells 54054 and 54058, and spell 48050) (3.0.8a)
|
||||
@@ -700,8 +700,6 @@ void AuraEffect::CalculateSpellMod()
|
||||
m_spellmod->type = SpellModType(GetAuraType()); // SpellModType value == spell aura types
|
||||
m_spellmod->spellId = GetId();
|
||||
m_spellmod->mask = GetSpellInfo()->Effects[GetEffIndex()].SpellClassMask;
|
||||
m_spellmod->charges = GetBase()->GetCharges();
|
||||
m_spellmod->priority = GetSpellInfo()->SpellPriority;
|
||||
}
|
||||
m_spellmod->value = GetAmount();
|
||||
break;
|
||||
@@ -1181,6 +1179,72 @@ void AuraEffect::PeriodicTick(AuraApplication* aurApp, Unit* caster) const
|
||||
}
|
||||
}
|
||||
|
||||
bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) const
|
||||
{
|
||||
bool result = GetBase()->CallScriptCheckEffectProcHandlers(this, aurApp, eventInfo);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
|
||||
switch (GetAuraType())
|
||||
{
|
||||
case SPELL_AURA_MOD_CONFUSE:
|
||||
case SPELL_AURA_MOD_FEAR:
|
||||
case SPELL_AURA_MOD_STUN:
|
||||
case SPELL_AURA_MOD_ROOT:
|
||||
case SPELL_AURA_TRANSFORM:
|
||||
{
|
||||
DamageInfo* damageInfo = eventInfo.GetDamageInfo();
|
||||
if (!damageInfo || !damageInfo->GetDamage())
|
||||
return false;
|
||||
|
||||
// Spell own damage at apply won't break CC
|
||||
if (spellInfo && spellInfo == GetSpellInfo())
|
||||
{
|
||||
Aura* aura = GetBase();
|
||||
// called from spellcast, should not have ticked yet
|
||||
if (aura->GetDuration() == aura->GetMaxDuration())
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_MECHANIC_IMMUNITY:
|
||||
case SPELL_AURA_MOD_MECHANIC_RESISTANCE:
|
||||
// compare mechanic
|
||||
if (!spellInfo || !(spellInfo->GetAllEffectsMechanicMask() & (1 << GetMiscValue())))
|
||||
return false;
|
||||
break;
|
||||
case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK:
|
||||
// skip melee hits and instant cast spells
|
||||
if (!eventInfo.GetProcSpell() || !eventInfo.GetProcSpell()->GetCastTime())
|
||||
return false;
|
||||
break;
|
||||
case SPELL_AURA_MOD_POWER_COST_SCHOOL:
|
||||
case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT:
|
||||
{
|
||||
// Skip melee hits and spells with wrong school or zero cost
|
||||
if (!spellInfo || !(spellInfo->GetSchoolMask() & GetMiscValue())
|
||||
|| !spellInfo->ManaCost || !spellInfo->ManaCostPercentage)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case SPELL_AURA_REFLECT_SPELLS_SCHOOL:
|
||||
// Skip melee hits and spells with wrong school
|
||||
if (!spellInfo || !(spellInfo->GetSchoolMask() & GetMiscValue()))
|
||||
return false;
|
||||
break;
|
||||
case SPELL_AURA_MOD_DAMAGE_FROM_CASTER:
|
||||
// Compare casters
|
||||
if (GetCasterGUID() != eventInfo.GetActor()->GetGUID())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
|
||||
{
|
||||
bool prevented = GetBase()->CallScriptEffectProcHandlers(this, aurApp, eventInfo);
|
||||
@@ -6200,31 +6264,6 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const
|
||||
{
|
||||
switch (GetSpellInfo()->SpellFamilyName)
|
||||
{
|
||||
case SPELLFAMILY_DRUID:
|
||||
{
|
||||
switch (GetSpellInfo()->Id)
|
||||
{
|
||||
// Frenzied Regeneration
|
||||
case 22842:
|
||||
{
|
||||
// Converts up to 10 rage per second into health for $d. Each point of rage is converted into ${$m2/10}.1% of max health.
|
||||
// Should be manauser
|
||||
if (!target->HasActivePowerType(POWER_RAGE))
|
||||
break;
|
||||
uint32 rage = target->GetPower(POWER_RAGE);
|
||||
// Nothing todo
|
||||
if (rage == 0)
|
||||
break;
|
||||
int32 mod = (rage < 100) ? rage : 100;
|
||||
int32 points = target->CalculateSpellDamage(target, GetSpellInfo(), 1);
|
||||
int32 regen = target->GetMaxHealth() * (mod * points / 10) / 1000;
|
||||
target->CastCustomSpell(target, 22845, ®en, 0, 0, true, 0, this);
|
||||
target->SetPower(POWER_RAGE, rage - mod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPELLFAMILY_HUNTER:
|
||||
{
|
||||
// Explosive Shot
|
||||
@@ -6251,15 +6290,6 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPELLFAMILY_SHAMAN:
|
||||
if (GetId() == 52179) // Astral Shift
|
||||
{
|
||||
// Periodic need for remove visual on stun/fear/silence lost
|
||||
if (!(target->GetUnitFlags() & (UNIT_FLAG_STUNNED | UNIT_FLAG_FLEEING | UNIT_FLAG_SILENCED)))
|
||||
target->RemoveAurasDueToSpell(52179);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SPELLFAMILY_DEATHKNIGHT:
|
||||
switch (GetId())
|
||||
{
|
||||
@@ -6426,21 +6456,6 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPELLFAMILY_SHAMAN:
|
||||
{
|
||||
switch (auraId)
|
||||
{
|
||||
// Lightning Shield (The Earthshatterer set trigger after cast Lighting Shield)
|
||||
case 28820:
|
||||
{
|
||||
// Need remove self if Lightning Shield not active
|
||||
if (!target->GetAuraEffect(SPELL_AURA_PROC_TRIGGER_SPELL, SPELLFAMILY_SHAMAN, 0x400, 0, 0))
|
||||
target->RemoveAurasDueToSpell(28820);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -6450,10 +6465,6 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster)
|
||||
// Spell exist but require custom code
|
||||
switch (auraId)
|
||||
{
|
||||
// Mana Tide
|
||||
case 16191:
|
||||
target->CastCustomSpell(target, triggerSpellId, &m_amount, nullptr, nullptr, true, nullptr, this);
|
||||
return;
|
||||
// Poison (Grobbulus)
|
||||
case 28158:
|
||||
case 54362:
|
||||
@@ -6670,18 +6681,6 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const
|
||||
damage = damageReductedArmor;
|
||||
}
|
||||
|
||||
// Curse of Agony damage-per-tick calculation
|
||||
if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_WARLOCK && (GetSpellInfo()->SpellFamilyFlags[0] & 0x400) && GetSpellInfo()->SpellIconID == 544)
|
||||
{
|
||||
uint32 totalTick = GetTotalTicks();
|
||||
// 1..4 ticks, 1/2 from normal tick damage
|
||||
if (m_tickNumber <= totalTick / 3)
|
||||
damage = damage / 2;
|
||||
// 9..12 ticks, 3/2 from normal tick damage
|
||||
else if (m_tickNumber > totalTick * 2 / 3)
|
||||
damage += (damage + 1) / 2; // +1 prevent 0.5 damage possible lost at 1..4 ticks
|
||||
// 5..8 ticks have normal tick damage
|
||||
}
|
||||
}
|
||||
|
||||
// calculate crit chance
|
||||
@@ -6748,7 +6747,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const
|
||||
|
||||
Unit::DealDamage(caster, target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true);
|
||||
|
||||
Unit::ProcDamageAndSpell(caster, target, caster ? procAttacker : 0, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo(), nullptr, GetEffIndex(), nullptr, &dmgInfo);
|
||||
Unit::ProcSkillsAndAuras(caster, target, caster ? procAttacker : 0, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo(), nullptr, GetEffIndex(), nullptr, &dmgInfo);
|
||||
}
|
||||
|
||||
void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) const
|
||||
@@ -6842,7 +6841,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
|
||||
|
||||
new_damage = Unit::DealDamage(caster, target, damage, &cleanDamage, DOT, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), false);
|
||||
|
||||
Unit::ProcDamageAndSpell(caster, target, caster ? procAttacker : 0, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo(), nullptr, GetEffIndex(), nullptr, &dmgInfo);
|
||||
Unit::ProcSkillsAndAuras(caster, target, caster ? procAttacker : 0, procVictim, procEx, damage, BASE_ATTACK, GetSpellInfo(), nullptr, GetEffIndex(), nullptr, &dmgInfo);
|
||||
|
||||
if (!caster || !caster->IsAlive())
|
||||
return;
|
||||
@@ -6955,22 +6954,6 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wild Growth = amount + (6 - 2*doneTicks) * ticks* amount / 100
|
||||
if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID && GetSpellInfo()->SpellIconID == 2864)
|
||||
{
|
||||
uint32 tickNumber = GetTickNumber() - 1;
|
||||
int32 tempAmount = m_spellInfo->Effects[m_effIndex].CalcValue(caster, &m_baseAmount, nullptr);
|
||||
|
||||
float drop = 2.0f;
|
||||
|
||||
// Item - Druid T10 Restoration 2P Bonus
|
||||
if (caster)
|
||||
if (AuraEffect* aurEff = caster->GetAuraEffect(70658, 0))
|
||||
AddPct(drop, -aurEff->GetAmount());
|
||||
|
||||
damage += GetTotalTicks() * tempAmount * (6 - (drop * tickNumber)) * 0.01f;
|
||||
}
|
||||
|
||||
if (GetBase()->GetType() == DYNOBJ_AURA_TYPE)
|
||||
damage = caster->SpellHealingBonusDone(target, GetSpellInfo(), damage, DOT, GetEffIndex(), 0.0f, GetBase()->GetStackAmount());
|
||||
damage = target->SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DOT, GetBase()->GetStackAmount());
|
||||
@@ -7039,7 +7022,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
|
||||
|
||||
// ignore item heals
|
||||
if (!haveCastItem && GetAuraType() != SPELL_AURA_OBS_MOD_HEALTH) // xinef: dont allow obs_mod_health to proc spells, this is passive regeneration and not hot
|
||||
Unit::ProcDamageAndSpell(caster, target, caster ? procAttacker : 0, procVictim, procEx, heal, BASE_ATTACK, GetSpellInfo(), nullptr, GetEffIndex(), nullptr, nullptr, &healInfo);
|
||||
Unit::ProcSkillsAndAuras(caster, target, caster ? procAttacker : 0, procVictim, procEx, heal, BASE_ATTACK, GetSpellInfo(), nullptr, GetEffIndex(), nullptr, nullptr, &healInfo);
|
||||
}
|
||||
|
||||
void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) const
|
||||
@@ -7220,14 +7203,14 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con
|
||||
// Set trigger flag
|
||||
uint32 procAttacker = PROC_FLAG_DONE_PERIODIC;
|
||||
uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC;
|
||||
uint32 procEx = createProcExtendMask(&damageInfo, SPELL_MISS_NONE) | PROC_EX_INTERNAL_DOT;
|
||||
if (damageInfo.damage)
|
||||
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
|
||||
|
||||
caster->DealSpellDamage(&damageInfo, true);
|
||||
|
||||
DamageInfo dmgInfo(damageInfo, DOT);
|
||||
Unit::ProcDamageAndSpell(caster, damageInfo.target, procAttacker, procVictim, procEx, damageInfo.damage, BASE_ATTACK, spellProto, nullptr, GetEffIndex(), nullptr, &dmgInfo);
|
||||
DamageInfo dmgInfo(damageInfo, DOT, BASE_ATTACK, SPELL_MISS_NONE);
|
||||
uint32 hitMask = dmgInfo.GetHitMask() | PROC_EX_INTERNAL_DOT;
|
||||
Unit::ProcSkillsAndAuras(caster, damageInfo.target, procAttacker, procVictim, hitMask, damageInfo.damage, BASE_ATTACK, spellProto, nullptr, GetEffIndex(), nullptr, &dmgInfo);
|
||||
}
|
||||
|
||||
void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
|
||||
|
||||
@@ -98,6 +98,7 @@ public:
|
||||
void PeriodicTick(AuraApplication* aurApp, Unit* caster) const;
|
||||
|
||||
void HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo);
|
||||
bool CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) const;
|
||||
|
||||
void CleanupTriggeredSpells(Unit* target);
|
||||
|
||||
|
||||
@@ -990,12 +990,6 @@ bool Aura::ModStackAmount(int32 num, AuraRemoveMode removeMode, bool periodicRes
|
||||
|
||||
// reset charges
|
||||
SetCharges(CalcMaxCharges());
|
||||
// FIXME: not a best way to synchronize charges, but works
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
if (AuraEffect* aurEff = GetEffect(i))
|
||||
if (aurEff->GetAuraType() == SPELL_AURA_ADD_FLAT_MODIFIER || aurEff->GetAuraType() == SPELL_AURA_ADD_PCT_MODIFIER)
|
||||
if (SpellModifier* mod = aurEff->GetSpellModifier())
|
||||
mod->charges = GetCharges();
|
||||
}
|
||||
|
||||
SetStackAmount(stackAmount);
|
||||
@@ -1772,24 +1766,13 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
|
||||
}
|
||||
break;
|
||||
case SPELLFAMILY_ROGUE:
|
||||
// Remove Vanish on stealth remove
|
||||
if (GetId() == 1784)
|
||||
{
|
||||
// Overkill, Master of Subtlety
|
||||
if (caster && GetSpellInfo()->SpellIconID == 250)
|
||||
{
|
||||
if (caster->GetDummyAuraEffect(SPELLFAMILY_ROGUE, 2114, 0))
|
||||
caster->CastSpell(caster, 31666, true);
|
||||
|
||||
if (caster->GetAuraEffectDummy(58426))
|
||||
caster->CastSpell(caster, 58428, true);
|
||||
}
|
||||
// Remove Vanish on stealth remove
|
||||
if (GetId() == 1784)
|
||||
{
|
||||
target->RemoveAurasWithFamily(SPELLFAMILY_ROGUE, 0x800, 0, 0, ObjectGuid::Empty);
|
||||
target->RemoveAurasDueToSpell(18461);
|
||||
}
|
||||
break;
|
||||
target->RemoveAurasWithFamily(SPELLFAMILY_ROGUE, 0x800, 0, 0, ObjectGuid::Empty);
|
||||
target->RemoveAurasDueToSpell(18461);
|
||||
}
|
||||
break;
|
||||
case SPELLFAMILY_SHAMAN:
|
||||
{
|
||||
// Ghost Wolf Speed (PvP 58 lvl set)
|
||||
@@ -1839,6 +1822,39 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
|
||||
// mods at aura apply or remove
|
||||
switch (GetSpellInfo()->SpellFamilyName)
|
||||
{
|
||||
case SPELLFAMILY_ROGUE:
|
||||
// Stealth
|
||||
if (GetSpellInfo()->SpellFamilyFlags[0] & 0x00400000)
|
||||
{
|
||||
// Master of Subtlety
|
||||
if (AuraEffect const* aurEff = target->GetAuraEffectOfRankedSpell(31221, 0))
|
||||
{
|
||||
if (!apply)
|
||||
target->CastSpell(target, 31666, true);
|
||||
else
|
||||
{
|
||||
// Remove counter aura
|
||||
target->RemoveAurasDueToSpell(31666);
|
||||
|
||||
int32 amount = aurEff->GetAmount();
|
||||
target->CastCustomSpell(target, 31665, &amount, nullptr, nullptr, true);
|
||||
}
|
||||
}
|
||||
// Overkill
|
||||
if (target->HasAura(58426))
|
||||
{
|
||||
if (!apply)
|
||||
target->CastSpell(target, 58428, true);
|
||||
else
|
||||
{
|
||||
// Remove counter aura
|
||||
target->RemoveAurasDueToSpell(58428);
|
||||
|
||||
target->CastSpell(target, 58427, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SPELLFAMILY_HUNTER:
|
||||
switch (GetId())
|
||||
{
|
||||
@@ -1871,24 +1887,8 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
|
||||
case SPELLFAMILY_PALADIN:
|
||||
switch (GetId())
|
||||
{
|
||||
case 19746:
|
||||
case 31821:
|
||||
// Aura Mastery Triggered Spell Handler
|
||||
// If apply Concentration Aura -> trigger -> apply Aura Mastery Immunity
|
||||
// If remove Concentration Aura -> trigger -> remove Aura Mastery Immunity
|
||||
// If remove Aura Mastery -> trigger -> remove Aura Mastery Immunity
|
||||
// Do effects only on aura owner
|
||||
if (GetCasterGUID() != target->GetGUID())
|
||||
break;
|
||||
if (apply)
|
||||
{
|
||||
if ((GetSpellInfo()->Id == 31821 && target->HasAura(19746, GetCasterGUID())) || (GetSpellInfo()->Id == 19746 && target->HasAura(31821)))
|
||||
target->CastSpell(target, 64364, true);
|
||||
}
|
||||
else
|
||||
target->RemoveAurasDueToSpell(64364, GetCasterGUID());
|
||||
break;
|
||||
case 31842:
|
||||
case 31842: // Divine Illumination
|
||||
// Item - Paladin T10 Holy 2P Bonus
|
||||
if (caster && caster->HasAura(70755))
|
||||
{
|
||||
if (apply)
|
||||
@@ -1898,23 +1898,6 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Sanctified Retribution, Improved Devotion Aura, Swift Retribution, Improved Concentration Aura
|
||||
if (caster == target && GetSpellInfo()->GetSpellSpecific() == SPELL_SPECIFIC_AURA)
|
||||
{
|
||||
if (apply)
|
||||
{
|
||||
target->CastSpell(target, 63510, true);
|
||||
target->CastSpell(target, 63514, true);
|
||||
target->CastSpell(target, 63531, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
target->RemoveAura(63510);
|
||||
target->RemoveAura(63514);
|
||||
target->RemoveAura(63531);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1990,8 +1973,13 @@ bool Aura::CanStackWith(Aura const* existingAura) const
|
||||
}
|
||||
|
||||
// passive auras don't stack with another rank of the spell cast by same caster
|
||||
// Exception: item-sourced auras from different items can stack (e.g., weapon imbues on MH/OH)
|
||||
if (IsPassive() && sameCaster && (m_spellInfo->IsDifferentRankOf(existingSpellInfo) || (m_spellInfo->Id == existingSpellInfo->Id && m_castItemGuid.IsEmpty())))
|
||||
return false;
|
||||
{
|
||||
// Allow stacking if both auras are from different items
|
||||
if (!(GetCastItemGUID() && existingAura->GetCastItemGUID() && GetCastItemGUID() != existingAura->GetCastItemGUID()))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
@@ -2102,9 +2090,11 @@ bool Aura::CanStackWith(Aura const* existingAura) const
|
||||
// don't allow passive area auras to stack
|
||||
if (m_spellInfo->IsMultiSlotAura() && !IsArea())
|
||||
return true;
|
||||
if (GetCastItemGUID() && existingAura->GetCastItemGUID())
|
||||
if (GetCastItemGUID() != existingAura->GetCastItemGUID() && m_spellInfo->HasAttribute(SPELL_ATTR0_CU_ENCHANT_PROC))
|
||||
return true;
|
||||
|
||||
// Allow item-sourced auras from different items to stack (e.g., weapon imbues on MH/OH, enchant procs)
|
||||
if ((IsPassive() || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_ENCHANT_PROC)) && GetCastItemGUID() && existingAura->GetCastItemGUID() && GetCastItemGUID() != existingAura->GetCastItemGUID())
|
||||
return true;
|
||||
|
||||
// same spell with same caster should not stack
|
||||
return false;
|
||||
}
|
||||
@@ -2112,22 +2102,32 @@ bool Aura::CanStackWith(Aura const* existingAura) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Aura::IsProcOnCooldown() const
|
||||
bool Aura::IsProcOnCooldown(TimePoint now) const
|
||||
{
|
||||
/*if (m_procCooldown)
|
||||
{
|
||||
if (m_procCooldown > GameTime::GetGameTime().count())
|
||||
return true;
|
||||
}*/
|
||||
return false;
|
||||
if (GetType() == UNIT_AURA_TYPE)
|
||||
if (Player* player = GetUnitOwner()->ToPlayer())
|
||||
if (player->GetCommandStatus(CHEAT_COOLDOWN))
|
||||
return false;
|
||||
|
||||
return m_procCooldown > now;
|
||||
}
|
||||
|
||||
void Aura::AddProcCooldown(Milliseconds /*msec*/)
|
||||
void Aura::AddProcCooldown(TimePoint cooldownEnd)
|
||||
{
|
||||
//m_procCooldown = std:chrono::steady_clock::now() + msec;
|
||||
m_procCooldown = cooldownEnd;
|
||||
}
|
||||
|
||||
void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo)
|
||||
void Aura::AddProcCooldown(SpellProcEntry const* procEntry, TimePoint now)
|
||||
{
|
||||
AddProcCooldown(now + procEntry->Cooldown);
|
||||
}
|
||||
|
||||
void Aura::ResetProcCooldown()
|
||||
{
|
||||
m_procCooldown = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, TimePoint now)
|
||||
{
|
||||
bool prepare = CallScriptPrepareProcHandlers(aurApp, eventInfo);
|
||||
if (!prepare)
|
||||
@@ -2145,50 +2145,88 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf
|
||||
ASSERT(procEntry);
|
||||
|
||||
// cooldowns should be added to the whole aura (see 51698 area aura)
|
||||
AddProcCooldown(procEntry->Cooldown);
|
||||
AddProcCooldown(now + procEntry->Cooldown);
|
||||
}
|
||||
|
||||
bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const
|
||||
uint8 Aura::GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, TimePoint now) const
|
||||
{
|
||||
SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId());
|
||||
|
||||
// only auras with spell proc entry can trigger proc
|
||||
if (!procEntry)
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
// check spell triggering us
|
||||
if (Spell const* spell = eventInfo.GetProcSpell())
|
||||
{
|
||||
// Do not allow auras to proc from effect triggered from itself
|
||||
if (spell->GetTriggeredByAuraSpellInfo() == m_spellInfo)
|
||||
return 0;
|
||||
|
||||
// check if aura can proc when spell is triggered (exception for hunter auto shot & wands)
|
||||
if (!GetSpellInfo()->HasAttribute(SPELL_ATTR3_CAN_PROC_FROM_PROCS) &&
|
||||
!(procEntry->AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC) &&
|
||||
!(eventInfo.GetTypeMask() & AUTO_ATTACK_PROC_FLAG_MASK))
|
||||
{
|
||||
if (spell->IsTriggered() && !spell->GetSpellInfo()->HasAttribute(SPELL_ATTR3_NOT_A_PROC))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// do not allow aura proc if proc is caused by a spell cast by item
|
||||
if (spell->m_CastItem && (procEntry->AttributesMask & PROC_ATTR_CANT_PROC_FROM_ITEM_CAST))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check if we have charges to proc with
|
||||
if (IsUsingCharges() && !GetCharges())
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
// check proc cooldown
|
||||
if (IsProcOnCooldown())
|
||||
return false;
|
||||
|
||||
/// @todo:
|
||||
// something about triggered spells triggering, and add extra attack effect
|
||||
if (IsProcOnCooldown(now))
|
||||
return 0;
|
||||
|
||||
// do checks against db data
|
||||
if (!sSpellMgr->CanSpellTriggerProcOnEvent(*procEntry, eventInfo))
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
// check if spell was affected by this aura's spellmod (used by Arcane Potency and similar effects)
|
||||
// only applies when consuming charges or stacks
|
||||
if ((procEntry->AttributesMask & PROC_ATTR_REQ_SPELLMOD) && (IsUsingCharges() || (procEntry->AttributesMask & PROC_ATTR_USE_STACKS_FOR_CHARGES)))
|
||||
{
|
||||
Spell const* procSpell = eventInfo.GetProcSpell();
|
||||
if (!procSpell || procSpell->m_appliedMods.find(const_cast<Aura*>(this)) == procSpell->m_appliedMods.end())
|
||||
return 0;
|
||||
}
|
||||
|
||||
// do checks using conditions table
|
||||
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_PROC, GetId());
|
||||
ConditionSourceInfo condInfo = ConditionSourceInfo(eventInfo.GetActor(), eventInfo.GetActionTarget());
|
||||
if (!sConditionMgr->IsObjectMeetToConditions(condInfo, conditions))
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
// AuraScript Hook
|
||||
bool check = const_cast<Aura*>(this)->CallScriptCheckProcHandlers(aurApp, eventInfo);
|
||||
if (!check)
|
||||
return false;
|
||||
if (!const_cast<Aura*>(this)->CallScriptCheckProcHandlers(aurApp, eventInfo))
|
||||
return 0;
|
||||
|
||||
/// @todo:
|
||||
// do allow additional requirements for procs
|
||||
// this is needed because this is the last moment in which you can prevent aura charge drop on proc
|
||||
// and possibly a way to prevent default checks (if there're going to be any)
|
||||
// At least one effect has to pass checks to proc aura
|
||||
uint8 procEffectMask = aurApp->GetEffectMask();
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (AuraEffect* aurEff = GetEffect(i))
|
||||
{
|
||||
if (procEffectMask & (1 << i))
|
||||
{
|
||||
if ((procEntry->DisableEffectsMask & (1u << i)) || !aurEff->CheckEffectProc(aurApp, eventInfo))
|
||||
procEffectMask &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!procEffectMask)
|
||||
return 0;
|
||||
|
||||
// Check if current equipment meets aura requirements
|
||||
// do that only for passive spells
|
||||
/// @todo: this needs to be unified for all kinds of auras
|
||||
Unit* target = aurApp->GetTarget();
|
||||
if (IsPassive() && target->IsPlayer() && GetSpellInfo()->EquippedItemClass != -1)
|
||||
{
|
||||
@@ -2198,7 +2236,7 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI
|
||||
if (GetSpellInfo()->EquippedItemClass == ITEM_CLASS_WEAPON)
|
||||
{
|
||||
if (target->ToPlayer()->IsInFeralForm())
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
if (DamageInfo const* damageInfo = eventInfo.GetDamageInfo())
|
||||
{
|
||||
@@ -2223,13 +2261,15 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI
|
||||
}
|
||||
|
||||
if (!item || item->IsBroken() || !item->IsFitToSpellRequirements(GetSpellInfo()))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return roll_chance_f(CalcProcChance(*procEntry, eventInfo));
|
||||
float procChance = CalcProcChance(*procEntry, eventInfo);
|
||||
if (roll_chance_f(procChance))
|
||||
return procEffectMask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const
|
||||
@@ -2239,33 +2279,71 @@ float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& event
|
||||
// so talents modifying chances and judgements will have properly calculated proc chance
|
||||
if (Unit* caster = GetCaster())
|
||||
{
|
||||
// calculate ppm chance if present and we're using weapon
|
||||
// If PPM exists calculate chance from PPM
|
||||
if (eventInfo.GetDamageInfo() && procEntry.ProcsPerMinute != 0)
|
||||
{
|
||||
uint32 WeaponSpeed = caster->GetAttackTime(eventInfo.GetDamageInfo()->GetAttackType());
|
||||
chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ProcsPerMinute, GetSpellInfo());
|
||||
SpellInfo const* procSpell = eventInfo.GetSpellInfo();
|
||||
uint32 attackSpeed = 0;
|
||||
if (!procSpell || procSpell->DmgClass == SPELL_DAMAGE_CLASS_MELEE || procSpell->IsRangedWeaponSpell())
|
||||
{
|
||||
attackSpeed = caster->GetAttackTime(eventInfo.GetDamageInfo()->GetAttackType());
|
||||
}
|
||||
else // spells use their cast time for PPM calculations
|
||||
{
|
||||
if (procSpell->CastTimeEntry)
|
||||
attackSpeed = procSpell->CastTimeEntry->CastTime;
|
||||
|
||||
// instants and fast spells use 1.5s cast speed
|
||||
if (attackSpeed < 1500)
|
||||
attackSpeed = 1500;
|
||||
}
|
||||
chance = caster->GetPPMProcChance(attackSpeed, procEntry.ProcsPerMinute, GetSpellInfo());
|
||||
}
|
||||
// apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell)
|
||||
if (Player* modOwner = caster->GetSpellModOwner())
|
||||
modOwner->ApplySpellMod(GetId(), SPELLMOD_CHANCE_OF_SUCCESS, chance);
|
||||
}
|
||||
|
||||
// proc chance is reduced by an additional 3.333% per level past 60
|
||||
if ((procEntry.AttributesMask & PROC_ATTR_REDUCE_PROC_60) && eventInfo.GetActor()->GetLevel() > 60)
|
||||
chance = std::max(0.f, (1.f - ((eventInfo.GetActor()->GetLevel() - 60) * 1.f / 30.f)) * chance);
|
||||
|
||||
return chance;
|
||||
}
|
||||
|
||||
void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo)
|
||||
void Aura::TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo)
|
||||
{
|
||||
CallScriptProcHandlers(aurApp, eventInfo);
|
||||
bool prevented = CallScriptProcHandlers(aurApp, eventInfo);
|
||||
if (!prevented)
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (!(procEffectMask & (1 << i)))
|
||||
continue;
|
||||
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
if (aurApp->HasEffect(i))
|
||||
// OnEffectProc / AfterEffectProc hooks handled in AuraEffect::HandleProc()
|
||||
GetEffect(i)->HandleProc(aurApp, eventInfo);
|
||||
if (aurApp->HasEffect(i))
|
||||
GetEffect(i)->HandleProc(aurApp, eventInfo);
|
||||
}
|
||||
|
||||
CallScriptAfterProcHandlers(aurApp, eventInfo);
|
||||
CallScriptAfterProcHandlers(aurApp, eventInfo);
|
||||
}
|
||||
|
||||
ConsumeProcCharges(sSpellMgr->GetSpellProcEntry(GetId()));
|
||||
}
|
||||
|
||||
void Aura::ConsumeProcCharges(SpellProcEntry const* procEntry)
|
||||
{
|
||||
// Remove aura if we've used last charge to proc
|
||||
if (IsUsingCharges() && !GetCharges())
|
||||
Remove();
|
||||
if (procEntry->AttributesMask & PROC_ATTR_USE_STACKS_FOR_CHARGES)
|
||||
{
|
||||
ModStackAmount(-1);
|
||||
}
|
||||
else if (IsUsingCharges())
|
||||
{
|
||||
if (!GetCharges())
|
||||
Remove();
|
||||
}
|
||||
}
|
||||
|
||||
void Aura::_DeleteRemovedApplications()
|
||||
@@ -2570,6 +2648,23 @@ bool Aura::CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventI
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Aura::CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo)
|
||||
{
|
||||
bool result = true;
|
||||
for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
|
||||
{
|
||||
(*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC, aurApp);
|
||||
std::list<AuraScript::CheckEffectProcHandler>::iterator effEndItr = (*scritr)->DoCheckEffectProc.end(), effItr = (*scritr)->DoCheckEffectProc.begin();
|
||||
for (; effItr != effEndItr; ++effItr)
|
||||
if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex()))
|
||||
result &= effItr->Call(*scritr, aurEff, eventInfo);
|
||||
|
||||
(*scritr)->_FinishScriptCall();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Aura::CallScriptAfterCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo, bool isTriggeredAtSpellProcEvent)
|
||||
{
|
||||
bool result = isTriggeredAtSpellProcEvent;
|
||||
|
||||
@@ -193,17 +193,17 @@ public:
|
||||
bool IsAuraStronger(Aura const* newAura) const;
|
||||
|
||||
// Proc system
|
||||
// this subsystem is not yet in use - the core of it is functional, but still some research has to be done
|
||||
// and some dependant problems fixed before it can replace old proc system (for example cooldown handling)
|
||||
// currently proc system functionality is implemented in Unit::ProcDamageAndSpell
|
||||
bool IsProcOnCooldown() const;
|
||||
void AddProcCooldown(Milliseconds msec);
|
||||
bool IsProcOnCooldown(TimePoint now) const;
|
||||
void AddProcCooldown(TimePoint cooldownEnd);
|
||||
void AddProcCooldown(SpellProcEntry const* procEntry, TimePoint now);
|
||||
void ResetProcCooldown();
|
||||
bool IsUsingCharges() const { return m_isUsingCharges; }
|
||||
void SetUsingCharges(bool val) { m_isUsingCharges = val; }
|
||||
void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo);
|
||||
bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const;
|
||||
void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, TimePoint now);
|
||||
uint8 GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, TimePoint now) const;
|
||||
float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const;
|
||||
void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo);
|
||||
void TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo);
|
||||
void ConsumeProcCharges(SpellProcEntry const* procEntry);
|
||||
|
||||
// AuraScript
|
||||
void LoadScripts();
|
||||
@@ -227,6 +227,7 @@ public:
|
||||
|
||||
// Spell Proc Hooks
|
||||
bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
|
||||
bool CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo);
|
||||
bool CallScriptAfterCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo, bool isTriggeredAtSpellProcEvent);
|
||||
bool CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
|
||||
bool CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
|
||||
@@ -270,6 +271,8 @@ protected:
|
||||
bool m_isSingleTarget: 1; // true if it's a single target spell and registered at caster - can change at spell steal for example
|
||||
bool m_isUsingCharges: 1;
|
||||
|
||||
TimePoint m_procCooldown;
|
||||
|
||||
private:
|
||||
Unit::AuraApplicationList m_removedApplications;
|
||||
|
||||
|
||||
@@ -2267,7 +2267,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/)
|
||||
else if (HasTriggeredCastFlag(TRIGGERED_DISALLOW_PROC_EVENTS))
|
||||
m_procEx |= PROC_EX_INTERNAL_TRIGGERED;
|
||||
}
|
||||
// Totem casts require spellfamilymask defined in spell_proc_event to proc
|
||||
// Totem casts require spellfamilymask defined in spell_proc to proc
|
||||
if (m_originalCaster && m_caster != m_originalCaster && m_caster->IsCreature() && m_caster->ToCreature()->IsTotem() && m_caster->IsControlledByPlayer())
|
||||
m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY;
|
||||
}
|
||||
@@ -2648,7 +2648,6 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
// Fill base trigger info
|
||||
uint32 procAttacker = m_procAttacker;
|
||||
uint32 procVictim = m_procVictim;
|
||||
uint32 procEx = m_procEx;
|
||||
|
||||
// Trigger info was not filled in spell::preparedatafortriggersystem - we do it now
|
||||
if (canEffectTrigger && !procAttacker && !procVictim)
|
||||
@@ -2705,20 +2704,21 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
uint32 addhealth = m_healing;
|
||||
|
||||
if (crit)
|
||||
{
|
||||
procEx |= PROC_EX_CRITICAL_HIT;
|
||||
addhealth = Unit::SpellCriticalHealingBonus(caster, m_spellInfo, addhealth, nullptr);
|
||||
}
|
||||
else
|
||||
procEx |= PROC_EX_NORMAL_HIT;
|
||||
|
||||
HealInfo healInfo(caster, unitTarget, addhealth, m_spellInfo, m_spellInfo->GetSchoolMask());
|
||||
|
||||
// Set hitMask based on crit
|
||||
if (crit)
|
||||
healInfo.AddHitMask(PROC_HIT_CRITICAL);
|
||||
else
|
||||
healInfo.AddHitMask(PROC_HIT_NORMAL);
|
||||
|
||||
// Xinef: override with forced crit, only visual result
|
||||
if (GetSpellValue()->ForcedCritResult)
|
||||
{
|
||||
crit = true;
|
||||
procEx |= PROC_EX_CRITICAL_HIT;
|
||||
healInfo.AddHitMask(PROC_HIT_CRITICAL);
|
||||
}
|
||||
|
||||
int32 gain = caster->HealBySpell(healInfo, crit);
|
||||
@@ -2729,14 +2729,17 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
unitTarget->getHostileRefMgr().threatAssist(caster, threat, m_spellInfo);
|
||||
m_healing = gain;
|
||||
|
||||
// Xinef: if heal acutally healed something, add no overheal flag
|
||||
// Xinef: if heal actually healed something, add no overheal flag
|
||||
if (m_healing)
|
||||
procEx |= PROC_EX_NO_OVERHEAL;
|
||||
healInfo.AddHitMask(PROC_EX_NO_OVERHEAL);
|
||||
|
||||
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
|
||||
if (canEffectTrigger)
|
||||
Unit::ProcDamageAndSpell(caster, unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
{
|
||||
uint32 hitMask = m_procEx | healInfo.GetHitMask();
|
||||
Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, hitMask, addhealth, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
m_triggeredByAuraSpell.effectIndex, this, nullptr, &healInfo);
|
||||
}
|
||||
}
|
||||
// Do damage and triggers
|
||||
else if (m_damage > 0)
|
||||
@@ -2802,7 +2805,6 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
if (reflectedSpell)
|
||||
effectUnit->SendSpellNonMeleeReflectLog(&damageInfo, effectUnit);
|
||||
|
||||
procEx |= createProcExtendMask(&damageInfo, missInfo);
|
||||
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
|
||||
|
||||
caster->DealSpellDamage(&damageInfo, true, this);
|
||||
@@ -2813,13 +2815,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
|
||||
if (canEffectTrigger)
|
||||
{
|
||||
DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE);
|
||||
Unit::ProcDamageAndSpell(caster, unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, missInfo);
|
||||
uint32 hitMask = m_procEx | dmgInfo.GetHitMask();
|
||||
Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, hitMask, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
m_triggeredByAuraSpell.effectIndex, this, &dmgInfo);
|
||||
|
||||
if (caster->IsPlayer() && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 &&
|
||||
m_spellInfo->HasAttribute(SPELL_ATTR4_SUPPRESS_WEAPON_PROCS) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED))
|
||||
caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx);
|
||||
caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, dmgInfo.GetHitMask());
|
||||
}
|
||||
|
||||
m_damage = damageInfo.damage;
|
||||
@@ -2829,19 +2832,19 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
|
||||
{
|
||||
// Fill base damage struct (unitTarget - is real spell target)
|
||||
SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo, m_spellSchoolMask);
|
||||
procEx |= createProcExtendMask(&damageInfo, missInfo);
|
||||
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
|
||||
if (canEffectTrigger)
|
||||
{
|
||||
DamageInfo dmgInfo(damageInfo, NODAMAGE);
|
||||
Unit::ProcDamageAndSpell(caster, unitTarget, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
DamageInfo dmgInfo(damageInfo, NODAMAGE, m_attackType, missInfo);
|
||||
uint32 hitMask = m_procEx | dmgInfo.GetHitMask();
|
||||
Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, hitMask, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
m_triggeredByAuraSpell.effectIndex, this, &dmgInfo);
|
||||
|
||||
// Xinef: eg. rogue poisons can proc off cheap shot, etc. so this block should be here also
|
||||
// Xinef: ofc count only spells that HIT the target, little hack used to fool the system
|
||||
if ((procEx & PROC_EX_NORMAL_HIT || procEx & PROC_EX_CRITICAL_HIT) && caster->IsPlayer() && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 &&
|
||||
if ((dmgInfo.GetHitMask() & (PROC_HIT_NORMAL | PROC_HIT_CRITICAL)) && caster->IsPlayer() && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 &&
|
||||
m_spellInfo->HasAttribute(SPELL_ATTR4_SUPPRESS_WEAPON_PROCS) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED))
|
||||
caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim | PROC_FLAG_TAKEN_DAMAGE, procEx);
|
||||
caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim | PROC_FLAG_TAKEN_DAMAGE, dmgInfo.GetHitMask());
|
||||
}
|
||||
|
||||
// Failed Pickpocket, reveal rogue
|
||||
@@ -3194,20 +3197,6 @@ void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask)
|
||||
/// @todo: move this code to scripts
|
||||
if (m_preCastSpell)
|
||||
{
|
||||
// Paladin immunity shields
|
||||
if (m_preCastSpell == 61988)
|
||||
{
|
||||
// Cast Forbearance
|
||||
m_caster->CastSpell(unit, 25771, true);
|
||||
// Cast Avenging Wrath Marker
|
||||
unit->CastSpell(unit, 61987, true);
|
||||
}
|
||||
|
||||
// Avenging Wrath
|
||||
if (m_preCastSpell == 61987)
|
||||
// Cast the serverside immunity shield marker
|
||||
m_caster->CastSpell(unit, 61988, true);
|
||||
|
||||
// Fearie Fire (Feral) - damage
|
||||
if (m_preCastSpell == 60089)
|
||||
m_caster->CastSpell(unit, m_preCastSpell, true);
|
||||
@@ -3896,48 +3885,6 @@ 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<TargetInfo>::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;
|
||||
}
|
||||
|
||||
Unit::ProcDamageAndSpell(m_originalCaster, 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);
|
||||
|
||||
@@ -4012,6 +3959,46 @@ void Spell::_cast(bool skipCheck)
|
||||
if (modOwner)
|
||||
modOwner->SetSpellModTakingSpell(this, false);
|
||||
|
||||
// Handle procs on cast - only for non-triggered spells
|
||||
// Triggered spells (from auras, items, etc.) should not fire CAST phase procs
|
||||
// as they are not player-initiated casts. This prevents issues like Arcane Potency
|
||||
// charges being consumed by periodic damage effects (e.g., Blizzard ticks).
|
||||
// Must be called AFTER handle_immediate() so spell mods (like Missile Barrage's
|
||||
// duration reduction) are applied before the aura is consumed by the proc.
|
||||
if (m_originalCaster && !IsTriggered())
|
||||
{
|
||||
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 hitMask = PROC_HIT_NORMAL;
|
||||
|
||||
for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
|
||||
{
|
||||
if (ihit->missCondition != SPELL_MISS_NONE)
|
||||
continue;
|
||||
|
||||
if (!ihit->crit)
|
||||
continue;
|
||||
|
||||
hitMask |= PROC_HIT_CRITICAL;
|
||||
break;
|
||||
}
|
||||
|
||||
Unit::ProcSkillsAndAuras(m_originalCaster, m_originalCaster, procAttacker, PROC_FLAG_NONE, hitMask, 1, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
m_triggeredByAuraSpell.effectIndex, this, nullptr, nullptr, PROC_SPELL_PHASE_CAST);
|
||||
}
|
||||
|
||||
if (std::vector<int32> const* spell_triggered = sSpellMgr->GetSpellLinked(m_spellInfo->Id))
|
||||
{
|
||||
for (int32 id : *spell_triggered)
|
||||
@@ -4269,24 +4256,20 @@ void Spell::_handle_finish_phase()
|
||||
}
|
||||
}
|
||||
|
||||
uint32 procEx = PROC_EX_NORMAL_HIT;
|
||||
uint32 hitMask = PROC_HIT_NORMAL;
|
||||
for (std::list<TargetInfo>::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;
|
||||
hitMask |= PROC_HIT_CRITICAL;
|
||||
break;
|
||||
}
|
||||
|
||||
Unit::ProcDamageAndSpell(m_originalCaster, m_originalCaster, procAttacker, PROC_FLAG_NONE, procEx, 1, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
Unit::ProcSkillsAndAuras(m_originalCaster, m_originalCaster, procAttacker, PROC_FLAG_NONE, hitMask, 1, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
|
||||
m_triggeredByAuraSpell.effectIndex, this, nullptr, nullptr, PROC_SPELL_PHASE_FINISH);
|
||||
}
|
||||
}
|
||||
@@ -8209,7 +8192,7 @@ bool ReflectEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
|
||||
{
|
||||
Unit* target = ObjectAccessor::GetUnit(*_caster, _targetGUID);
|
||||
if (target && _caster->IsInMap(target))
|
||||
Unit::ProcDamageAndSpell(_caster, target, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_EX_REFLECT, 1, BASE_ATTACK, _spellInfo);
|
||||
Unit::ProcSkillsAndAuras(_caster, target, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_EX_REFLECT, 1, BASE_ATTACK, _spellInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -599,6 +599,7 @@ public:
|
||||
std::list<TargetInfo>* GetUniqueTargetInfo() { return &m_UniqueTargetInfo; }
|
||||
|
||||
[[nodiscard]] uint32 GetTriggeredByAuraTickNumber() const { return m_triggeredByAuraSpell.tickNumber; }
|
||||
[[nodiscard]] SpellInfo const* GetTriggeredByAuraSpellInfo() const { return m_triggeredByAuraSpell.spellInfo; }
|
||||
|
||||
[[nodiscard]] TriggerCastFlags GetTriggeredCastFlags() const { return _triggeredCastFlags; }
|
||||
|
||||
|
||||
@@ -3927,39 +3927,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
|
||||
|
||||
return;
|
||||
}
|
||||
// Stoneclaw Totem
|
||||
case 55328: // Rank 1
|
||||
case 55329: // Rank 2
|
||||
case 55330: // Rank 3
|
||||
case 55332: // Rank 4
|
||||
case 55333: // Rank 5
|
||||
case 55335: // Rank 6
|
||||
case 55278: // Rank 7
|
||||
case 58589: // Rank 8
|
||||
case 58590: // Rank 9
|
||||
case 58591: // Rank 10
|
||||
{
|
||||
int32 basepoints0 = damage;
|
||||
// Cast Absorb on totems
|
||||
for (uint8 slot = SUMMON_SLOT_TOTEM_FIRE; slot < MAX_TOTEM_SLOT; ++slot)
|
||||
{
|
||||
if (!unitTarget->m_SummonSlot[slot])
|
||||
continue;
|
||||
|
||||
Creature* totem = unitTarget->GetMap()->GetCreature(unitTarget->m_SummonSlot[slot]);
|
||||
if (totem && totem->IsTotem())
|
||||
{
|
||||
m_caster->CastCustomSpell(totem, 55277, &basepoints0, nullptr, nullptr, true);
|
||||
}
|
||||
}
|
||||
// Glyph of Stoneclaw Totem
|
||||
if (AuraEffect* aur = unitTarget->GetAuraEffect(63298, 0))
|
||||
{
|
||||
basepoints0 *= aur->GetAmount();
|
||||
m_caster->CastCustomSpell(unitTarget, 55277, &basepoints0, nullptr, nullptr, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 61263: // for item Intravenous Healing Potion (44698)
|
||||
{
|
||||
if (!m_caster || !unitTarget)
|
||||
|
||||
@@ -1313,7 +1313,6 @@ bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const
|
||||
if (affectSpell->SpellFamilyName != SpellFamilyName)
|
||||
return false;
|
||||
|
||||
// true
|
||||
if (mod->mask & SpellFamilyFlags)
|
||||
return true;
|
||||
|
||||
|
||||
@@ -1236,15 +1236,6 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
spellInfo->Effects[EFFECT_0].SpellClassMask[1] |= 0x20; // prayer of mending
|
||||
});
|
||||
|
||||
// Power Infusion
|
||||
ApplySpellFix({ 10060 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
// hack to fix stacking with arcane power
|
||||
spellInfo->Effects[EFFECT_2].Effect = SPELL_EFFECT_APPLY_AURA;
|
||||
spellInfo->Effects[EFFECT_2].ApplyAuraName = SPELL_AURA_ADD_PCT_MODIFIER;
|
||||
spellInfo->Effects[EFFECT_2].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_TARGET_ALLY);
|
||||
});
|
||||
|
||||
// Lifebloom final bloom
|
||||
ApplySpellFix({ 33778 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
@@ -3695,13 +3686,6 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
spellInfo->Effects[EFFECT_0].ApplyAuraName = SPELL_AURA_MOD_CHARM;
|
||||
});
|
||||
|
||||
// Persistent Shield
|
||||
ApplySpellFix({ 26467 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->Effects[EFFECT_0].ApplyAuraName = SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE;
|
||||
spellInfo->Effects[EFFECT_0].TriggerSpell = 26470;
|
||||
});
|
||||
|
||||
// Deadly Swiftness
|
||||
ApplySpellFix({ 31255 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
|
||||
@@ -784,166 +784,6 @@ SpellGroupStackRule SpellMgr::GetSpellGroupStackRule(SpellGroup group) const
|
||||
return SPELL_GROUP_STACK_RULE_DEFAULT;
|
||||
}
|
||||
|
||||
SpellProcEventEntry const* SpellMgr::GetSpellProcEvent(uint32 spellId) const
|
||||
{
|
||||
SpellProcEventMap::const_iterator itr = mSpellProcEventMap.find(spellId);
|
||||
if (itr != mSpellProcEventMap.end())
|
||||
return &itr->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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 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
|
||||
if ((procFlags & EventProcFlag) == 0)
|
||||
return false;
|
||||
|
||||
// Xinef: Always trigger for this, including TAKEN_DAMAGE
|
||||
if (EventProcFlag & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH | PROC_FLAG_TAKEN_DAMAGE))
|
||||
return true;
|
||||
|
||||
bool hasFamilyMask = false;
|
||||
|
||||
if (procFlags & PROC_FLAG_DONE_PERIODIC)
|
||||
{
|
||||
if (procExtra & PROC_EX_INTERNAL_HOT)
|
||||
{
|
||||
if (EventProcFlag == PROC_FLAG_DONE_PERIODIC)
|
||||
{
|
||||
/// no aura with only PROC_FLAG_DONE_PERIODIC and spellFamilyName == 0 can proc from a HOT.
|
||||
if (!spellProto->SpellFamilyName)
|
||||
return false;
|
||||
}
|
||||
/// Aura must have positive procflags for a HOT to proc
|
||||
else if (!(EventProcFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)))
|
||||
return false;
|
||||
}
|
||||
/// Aura must have negative or neutral(PROC_FLAG_DONE_PERIODIC only) procflags for a DOT to proc
|
||||
else if (EventProcFlag != PROC_FLAG_DONE_PERIODIC)
|
||||
if (!(EventProcFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG | PROC_FLAG_DONE_TRAP_ACTIVATION)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (procFlags & PROC_FLAG_TAKEN_PERIODIC)
|
||||
{
|
||||
if (procExtra & PROC_EX_INTERNAL_HOT)
|
||||
{
|
||||
/// No aura that only has PROC_FLAG_TAKEN_PERIODIC can proc from a HOT.
|
||||
if (EventProcFlag == PROC_FLAG_TAKEN_PERIODIC)
|
||||
return false;
|
||||
/// Aura must have positive procflags for a HOT to proc
|
||||
if (!(EventProcFlag & (PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS)))
|
||||
return false;
|
||||
}
|
||||
/// Aura must have negative or neutral(PROC_FLAG_TAKEN_PERIODIC only) procflags for a DOT to proc
|
||||
else if (EventProcFlag != PROC_FLAG_TAKEN_PERIODIC)
|
||||
if (!(EventProcFlag & (PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG)))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Trap casts are active by default
|
||||
if (procFlags & PROC_FLAG_DONE_TRAP_ACTIVATION)
|
||||
active = true;
|
||||
|
||||
if (spellProcEvent) // Exist event data
|
||||
{
|
||||
// Store extra req
|
||||
procEvent_procEx = spellProcEvent->procEx;
|
||||
procEvent_procPhase = spellProcEvent->procPhase;
|
||||
|
||||
// For melee triggers
|
||||
if (!procSpellInfo)
|
||||
{
|
||||
// Check (if set) for school (melee attack have Normal school)
|
||||
if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0)
|
||||
return false;
|
||||
}
|
||||
else // For spells need check school/spell family/family mask
|
||||
{
|
||||
// Check (if set) for school
|
||||
if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpellInfo->SchoolMask) == 0)
|
||||
return false;
|
||||
|
||||
// Check (if set) for 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 & 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (procExtra & (PROC_EX_INTERNAL_REQ_FAMILY))
|
||||
{
|
||||
if (!hasFamilyMask)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(procEvent_procPhase & procPhase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for extra req (if none) and hit/crit
|
||||
if (procEvent_procEx == PROC_EX_NONE)
|
||||
{
|
||||
// No extra req, so can trigger only for hit/crit - spell has to be active
|
||||
if ((procExtra & (PROC_EX_NORMAL_HIT | PROC_EX_CRITICAL_HIT)) && active)
|
||||
return true;
|
||||
}
|
||||
else // Passive spells hits here only if resist/reflect/immune/evade
|
||||
{
|
||||
if (procExtra & AURA_SPELL_PROC_EX_MASK)
|
||||
{
|
||||
// if spell marked as procing only from not active spells
|
||||
if (active && procEvent_procEx & PROC_EX_NOT_ACTIVE_SPELL)
|
||||
return false;
|
||||
// if spell marked as procing only from active spells
|
||||
if (!active && procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL)
|
||||
return false;
|
||||
// Exist req for PROC_EX_EX_TRIGGER_ALWAYS
|
||||
if (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS)
|
||||
return true;
|
||||
// PROC_EX_NOT_ACTIVE_SPELL and PROC_EX_ONLY_ACTIVE_SPELL flags handle: if passed checks before
|
||||
if ((procExtra & (PROC_EX_NORMAL_HIT | PROC_EX_CRITICAL_HIT)) && ((procEvent_procEx & (AURA_SPELL_PROC_EX_MASK)) == 0))
|
||||
return true;
|
||||
}
|
||||
// Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other)
|
||||
if (procEvent_procEx & procExtra)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const
|
||||
{
|
||||
SpellProcMap::const_iterator itr = mSpellProcMap.find(spellId);
|
||||
@@ -964,6 +804,14 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
|
||||
if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget()))
|
||||
return false;
|
||||
|
||||
// check mana cost requirement (used by Clearcasting and similar effects)
|
||||
if (procEntry.AttributesMask & PROC_ATTR_REQ_MANA_COST)
|
||||
{
|
||||
SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
|
||||
if (!spellInfo || (!spellInfo->ManaCost && !spellInfo->ManaCostPercentage))
|
||||
return false;
|
||||
}
|
||||
|
||||
// always trigger for these types
|
||||
if (eventInfo.GetTypeMask() & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH))
|
||||
return true;
|
||||
@@ -973,13 +821,16 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
|
||||
return false;
|
||||
|
||||
// check spell family name/flags (if set) for spells
|
||||
if (eventInfo.GetTypeMask() & (PERIODIC_PROC_FLAG_MASK | SPELL_PROC_FLAG_MASK | PROC_FLAG_DONE_TRAP_ACTIVATION))
|
||||
if (eventInfo.GetTypeMask() & SPELL_PROC_FLAG_MASK)
|
||||
{
|
||||
if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName != eventInfo.GetSpellInfo()->SpellFamilyName))
|
||||
return false;
|
||||
if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo())
|
||||
{
|
||||
if (procEntry.SpellFamilyName && procEntry.SpellFamilyName != eventSpellInfo->SpellFamilyName)
|
||||
return false;
|
||||
|
||||
if (procEntry.SpellFamilyMask && !(procEntry.SpellFamilyMask & eventInfo.GetSpellInfo()->SpellFamilyFlags))
|
||||
return false;
|
||||
if (procEntry.SpellFamilyMask && !(procEntry.SpellFamilyMask & eventSpellInfo->SpellFamilyFlags))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check spell type mask (if set)
|
||||
@@ -996,8 +847,11 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
|
||||
return false;
|
||||
}
|
||||
|
||||
// check hit mask (on taken hit or on done hit, but not on spell cast phase)
|
||||
if ((eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) || ((eventInfo.GetTypeMask() & DONE_HIT_PROC_FLAG_MASK) && !(eventInfo.GetSpellPhaseMask() & PROC_SPELL_PHASE_CAST)))
|
||||
// check hit mask (on taken hit or on done hit)
|
||||
// For CAST phase with DONE flags, only check if HitMask is explicitly set (crit is pre-calculated for travel time spells)
|
||||
if ((eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) ||
|
||||
((eventInfo.GetTypeMask() & DONE_HIT_PROC_FLAG_MASK) &&
|
||||
(!(eventInfo.GetSpellPhaseMask() & PROC_SPELL_PHASE_CAST) || procEntry.HitMask)))
|
||||
{
|
||||
uint32 hitMask = procEntry.HitMask;
|
||||
// get default values if hit mask not set
|
||||
@@ -1916,98 +1770,65 @@ void SpellMgr::LoadSpellGroupStackRules()
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void SpellMgr::LoadSpellProcEvents()
|
||||
static bool InitTriggerAuraData();
|
||||
static bool isTriggerAura[TOTAL_AURAS];
|
||||
static bool isAlwaysTriggeredAura[TOTAL_AURAS];
|
||||
static bool procPrepared = InitTriggerAuraData();
|
||||
|
||||
// List of auras that CAN trigger but may not exist in spell_proc
|
||||
// in most case need for drop charges
|
||||
// in some types of aura need do additional check
|
||||
// for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic
|
||||
bool InitTriggerAuraData()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
mSpellProcEventMap.clear(); // need for reload case
|
||||
|
||||
// 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)
|
||||
for (uint16 i = 0; i < TOTAL_AURAS; ++i)
|
||||
{
|
||||
LOG_WARN("server.loading", ">> Loaded 0 spell proc event conditions. DB table `spell_proc_event` is empty.");
|
||||
return;
|
||||
isTriggerAura[i] = false;
|
||||
isAlwaysTriggeredAura[i] = false;
|
||||
}
|
||||
isTriggerAura[SPELL_AURA_DUMMY] = true; // Most dummy auras should require scripting
|
||||
isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true; // "Any direct damaging attack will revive targets"
|
||||
isTriggerAura[SPELL_AURA_MOD_THREAT] = true; // Only one spell: 28762 part of Mage T3 8p bonus
|
||||
isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura does not have charges but needs to be removed on trigger
|
||||
isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_STEALTH] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_FEAR] = true; // Aura does not have charges but needs to be removed on trigger
|
||||
isTriggerAura[SPELL_AURA_MOD_ROOT] = true;
|
||||
isTriggerAura[SPELL_AURA_TRANSFORM] = true;
|
||||
isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true;
|
||||
isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true;
|
||||
isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true;
|
||||
isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true;
|
||||
isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER] = true;
|
||||
isTriggerAura[SPELL_AURA_ADD_CASTER_HIT_TRIGGER] = true;
|
||||
isTriggerAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_MELEE_HASTE] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE] = true;
|
||||
isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE] = true;
|
||||
isTriggerAura[SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE] = true;
|
||||
isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true;
|
||||
isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true;
|
||||
isTriggerAura[SPELL_AURA_ADD_FLAT_MODIFIER] = true;
|
||||
isTriggerAura[SPELL_AURA_ADD_PCT_MODIFIER] = true;
|
||||
isTriggerAura[SPELL_AURA_ABILITY_IGNORE_AURASTATE] = true;
|
||||
|
||||
uint32 count = 0;
|
||||
isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true;
|
||||
isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR] = true;
|
||||
isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true;
|
||||
isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true;
|
||||
isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true;
|
||||
isAlwaysTriggeredAura[SPELL_AURA_SPELL_MAGNET] = true;
|
||||
isAlwaysTriggeredAura[SPELL_AURA_SCHOOL_ABSORB] = true;
|
||||
isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
int32 spellId = fields[0].Get<int32>();
|
||||
|
||||
bool allRanks = false;
|
||||
if (spellId < 0)
|
||||
{
|
||||
allRanks = true;
|
||||
spellId = -spellId;
|
||||
}
|
||||
|
||||
SpellInfo const* spellInfo = GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Spell {} listed in `spell_proc_event` does not exist", spellId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allRanks)
|
||||
{
|
||||
if (!spellInfo->IsRanked())
|
||||
LOG_ERROR("sql.sql", "Spell {} listed in `spell_proc_event` with all ranks, but spell has no ranks.", spellId);
|
||||
|
||||
if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Spell {} listed in `spell_proc_event` is not first rank of spell.", spellId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
SpellProcEventEntry spellProcEvent;
|
||||
|
||||
spellProcEvent.schoolMask = fields[1].Get<int8>();
|
||||
spellProcEvent.spellFamilyName = fields[2].Get<uint16>();
|
||||
spellProcEvent.spellFamilyMask[0] = fields[3].Get<uint32>();
|
||||
spellProcEvent.spellFamilyMask[1] = fields[4].Get<uint32>();
|
||||
spellProcEvent.spellFamilyMask[2] = fields[5].Get<uint32>();
|
||||
spellProcEvent.procFlags = fields[6].Get<uint32>();
|
||||
spellProcEvent.procEx = fields[7].Get<uint32>();
|
||||
spellProcEvent.procPhase = fields[8].Get<uint32>();
|
||||
spellProcEvent.ppmRate = fields[9].Get<float>();
|
||||
spellProcEvent.customChance = fields[10].Get<float>();
|
||||
spellProcEvent.cooldown = fields[11].Get<uint32>();
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (mSpellProcEventMap.find(spellInfo->Id) != mSpellProcEventMap.end())
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Spell {} listed in `spell_proc_event` already has its first rank in table.", spellInfo->Id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!spellInfo->ProcFlags && !spellProcEvent.procFlags)
|
||||
LOG_ERROR("sql.sql", "Spell {} listed in `spell_proc_event` probally not triggered spell", spellInfo->Id);
|
||||
|
||||
mSpellProcEventMap[spellInfo->Id] = spellProcEvent;
|
||||
|
||||
if (allRanks)
|
||||
spellInfo = spellInfo->GetNextRankSpell();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} Extra Spell Proc Event Conditions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpellMgr::LoadSpellProcs()
|
||||
@@ -2016,8 +1837,8 @@ void SpellMgr::LoadSpellProcs()
|
||||
|
||||
mSpellProcMap.clear(); // need for reload case
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
||||
QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc");
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, DisableEffectsMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc");
|
||||
if (!result)
|
||||
{
|
||||
LOG_WARN("server.loading", ">> Loaded 0 Spell Proc Conditions And Data. DB table `spell_proc` Is Empty.");
|
||||
@@ -2067,10 +1888,11 @@ void SpellMgr::LoadSpellProcs()
|
||||
baseProcEntry.SpellPhaseMask = fields[8].Get<uint32>();
|
||||
baseProcEntry.HitMask = fields[9].Get<uint32>();
|
||||
baseProcEntry.AttributesMask = fields[10].Get<uint32>();
|
||||
baseProcEntry.ProcsPerMinute = fields[11].Get<float>();
|
||||
baseProcEntry.Chance = fields[12].Get<float>();
|
||||
baseProcEntry.Cooldown = Milliseconds(fields[13].Get<uint32>());
|
||||
baseProcEntry.Charges = fields[14].Get<uint32>();
|
||||
baseProcEntry.DisableEffectsMask = fields[11].Get<uint32>();
|
||||
baseProcEntry.ProcsPerMinute = fields[12].Get<float>();
|
||||
baseProcEntry.Chance = fields[13].Get<float>();
|
||||
baseProcEntry.Cooldown = Milliseconds(fields[14].Get<uint32>());
|
||||
baseProcEntry.Charges = fields[15].Get<uint32>();
|
||||
|
||||
while (spellInfo)
|
||||
{
|
||||
@@ -2104,8 +1926,6 @@ void SpellMgr::LoadSpellProcs()
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has negative value in `ProcsPerMinute` field", spellId);
|
||||
procEntry.ProcsPerMinute = 0;
|
||||
}
|
||||
if (procEntry.Chance == 0 && procEntry.ProcsPerMinute == 0)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} doesn't have `Chance` and `ProcsPerMinute` values defined, proc will not be triggered", spellId);
|
||||
if (procEntry.Charges > 99)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has too big value in `Charges` field", spellId);
|
||||
@@ -2125,8 +1945,30 @@ void SpellMgr::LoadSpellProcs()
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has `SpellPhaseMask` value defined, but it won't be used for defined `ProcFlags` value", spellId);
|
||||
if (procEntry.HitMask & ~PROC_HIT_MASK_ALL)
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `HitMask` set: {}", spellId, procEntry.HitMask);
|
||||
if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH)))))
|
||||
// HitMask is valid for: TAKEN procs, or DONE procs at any phase (CAST phase has pre-calculated crit for travel-time spells)
|
||||
if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK))
|
||||
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has `HitMask` value defined, but it won't be used for defined `ProcFlags` and `SpellPhaseMask` values", spellId);
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
if ((procEntry.DisableEffectsMask & (1u << i)) && !spellInfo->Effects[i].IsAura())
|
||||
LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId {} has DisableEffectsMask with effect {}, but effect {} is not an aura effect", spellId, static_cast<uint32>(i), static_cast<uint32>(i));
|
||||
if (procEntry.AttributesMask & PROC_ATTR_REQ_SPELLMOD)
|
||||
{
|
||||
bool found = false;
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (!spellInfo->Effects[i].IsAura())
|
||||
continue;
|
||||
|
||||
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_ADD_PCT_MODIFIER || spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_ADD_FLAT_MODIFIER)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId {} has Attribute PROC_ATTR_REQ_SPELLMOD, but spell has no spell mods. Proc will not be triggered", spellId);
|
||||
}
|
||||
|
||||
mSpellProcMap[spellInfo->Id] = procEntry;
|
||||
|
||||
@@ -2140,6 +1982,104 @@ void SpellMgr::LoadSpellProcs()
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded {} spell proc conditions and data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
|
||||
// Generate default procs for spells with proc flags but no explicit spell_proc entry
|
||||
// This ensures backward compatibility and covers spells that rely on DBC data
|
||||
LOG_INFO("server.loading", "Generating spell proc data from SpellMap...");
|
||||
count = 0;
|
||||
oldMSTime = getMSTime();
|
||||
|
||||
for (SpellInfo const* spellInfo : mSpellInfoMap)
|
||||
{
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
// Skip if already has explicit entry
|
||||
if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end())
|
||||
continue;
|
||||
|
||||
// Check if spell has any trigger aura effects
|
||||
bool found = false, addTriggerFlag = false;
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (!spellInfo->Effects[i].IsEffect())
|
||||
continue;
|
||||
|
||||
uint32 auraName = spellInfo->Effects[i].ApplyAuraName;
|
||||
if (!auraName)
|
||||
continue;
|
||||
|
||||
if (!isTriggerAura[auraName])
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
|
||||
if (!addTriggerFlag && isAlwaysTriggeredAura[auraName])
|
||||
addTriggerFlag = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
continue;
|
||||
|
||||
// Skip if no proc flags in DBC
|
||||
if (!spellInfo->ProcFlags)
|
||||
continue;
|
||||
|
||||
// Generate default proc entry from DBC data
|
||||
SpellProcEntry procEntry;
|
||||
procEntry.SchoolMask = 0;
|
||||
procEntry.SpellFamilyName = spellInfo->SpellFamilyName;
|
||||
procEntry.SpellFamilyMask[0] = 0;
|
||||
procEntry.SpellFamilyMask[1] = 0;
|
||||
procEntry.SpellFamilyMask[2] = 0;
|
||||
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
if (spellInfo->Effects[i].IsEffect() && isTriggerAura[spellInfo->Effects[i].ApplyAuraName])
|
||||
procEntry.SpellFamilyMask |= spellInfo->Effects[i].SpellClassMask;
|
||||
|
||||
procEntry.ProcFlags = spellInfo->ProcFlags;
|
||||
procEntry.SpellTypeMask = PROC_SPELL_TYPE_MASK_ALL;
|
||||
procEntry.SpellPhaseMask = PROC_SPELL_PHASE_HIT;
|
||||
procEntry.HitMask = PROC_HIT_NONE; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent
|
||||
|
||||
// Reflect auras should only proc off reflects
|
||||
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS) || spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS_SCHOOL))
|
||||
{
|
||||
procEntry.HitMask = PROC_HIT_REFLECT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
procEntry.AttributesMask = 0;
|
||||
if (spellInfo->ProcFlags & PROC_FLAG_KILL)
|
||||
procEntry.AttributesMask |= PROC_ATTR_REQ_EXP_OR_HONOR;
|
||||
if (addTriggerFlag)
|
||||
procEntry.AttributesMask |= PROC_ATTR_TRIGGERED_CAN_PROC;
|
||||
|
||||
// Calculate DisableEffectsMask for effects that shouldn't trigger procs
|
||||
uint32 nonProcMask = 0;
|
||||
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||
{
|
||||
if (!spellInfo->Effects[i].IsAura())
|
||||
continue;
|
||||
if (!isTriggerAura[spellInfo->Effects[i].ApplyAuraName])
|
||||
nonProcMask |= 1u << i;
|
||||
}
|
||||
procEntry.DisableEffectsMask = nonProcMask;
|
||||
|
||||
procEntry.ProcsPerMinute = 0;
|
||||
procEntry.Chance = static_cast<float>(spellInfo->ProcChance);
|
||||
procEntry.Cooldown = Milliseconds::zero();
|
||||
procEntry.Charges = spellInfo->ProcCharges;
|
||||
|
||||
mSpellProcMap[spellInfo->Id] = procEntry;
|
||||
++count;
|
||||
}
|
||||
|
||||
LOG_INFO("server.loading", ">> Generated spell proc data for {} spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void SpellMgr::LoadSpellBonuses()
|
||||
@@ -3176,11 +3116,15 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
|
||||
case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC:
|
||||
case SPELL_EFFECT_ENCHANT_HELD_ITEM:
|
||||
{
|
||||
// only enchanting profession enchantments procs can stack
|
||||
// Only Enchanting profession enchant procs can stack when dual-wielding
|
||||
// DK runes (e.g., Unholy Strength) should refresh, not stack
|
||||
if (IsPartOfSkillLine(SKILL_ENCHANTING, i))
|
||||
{
|
||||
uint32 enchantId = spellInfo->Effects[j].MiscValue;
|
||||
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
|
||||
if (!enchant)
|
||||
break;
|
||||
|
||||
for (uint8 s = 0; s < MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS; ++s)
|
||||
{
|
||||
if (enchant->type[s] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
|
||||
|
||||
@@ -154,21 +154,23 @@ enum ProcFlags
|
||||
| PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS | PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS,
|
||||
|
||||
SPELL_PROC_FLAG_MASK = PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS | PROC_FLAG_TAKEN_SPELL_MELEE_DMG_CLASS
|
||||
| PROC_FLAG_DONE_RANGED_AUTO_ATTACK | PROC_FLAG_TAKEN_RANGED_AUTO_ATTACK
|
||||
| PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS | PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS
|
||||
| PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS
|
||||
| PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG
|
||||
| PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS
|
||||
| PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG,
|
||||
|
||||
SPELL_CAST_PROC_FLAG_MASK = SPELL_PROC_FLAG_MASK | PROC_FLAG_DONE_TRAP_ACTIVATION | RANGED_PROC_FLAG_MASK,
|
||||
| PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG
|
||||
| PROC_FLAG_DONE_PERIODIC | PROC_FLAG_TAKEN_PERIODIC
|
||||
| PROC_FLAG_DONE_TRAP_ACTIVATION,
|
||||
|
||||
PERIODIC_PROC_FLAG_MASK = PROC_FLAG_DONE_PERIODIC | PROC_FLAG_TAKEN_PERIODIC,
|
||||
|
||||
DONE_HIT_PROC_FLAG_MASK = PROC_FLAG_DONE_MELEE_AUTO_ATTACK | PROC_FLAG_DONE_RANGED_AUTO_ATTACK
|
||||
| PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS
|
||||
| PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG
|
||||
| PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG
|
||||
| PROC_FLAG_DONE_PERIODIC | PROC_FLAG_DONE_MAINHAND_ATTACK | PROC_FLAG_DONE_OFFHAND_ATTACK,
|
||||
| PROC_FLAG_DONE_SPELL_MELEE_DMG_CLASS | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS
|
||||
| PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG
|
||||
| PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG
|
||||
| PROC_FLAG_DONE_PERIODIC | PROC_FLAG_DONE_TRAP_ACTIVATION
|
||||
| PROC_FLAG_DONE_MAINHAND_ATTACK | PROC_FLAG_DONE_OFFHAND_ATTACK,
|
||||
|
||||
TAKEN_HIT_PROC_FLAG_MASK = PROC_FLAG_TAKEN_MELEE_AUTO_ATTACK | PROC_FLAG_TAKEN_RANGED_AUTO_ATTACK
|
||||
| PROC_FLAG_TAKEN_SPELL_MELEE_DMG_CLASS | PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS
|
||||
@@ -268,24 +270,15 @@ enum ProcFlagsHit
|
||||
|
||||
enum ProcAttributes
|
||||
{
|
||||
PROC_ATTR_REQ_EXP_OR_HONOR = 0x0000010,
|
||||
PROC_ATTR_REQ_EXP_OR_HONOR = 0x0000001, // requires proc target to give exp or honor for aura proc
|
||||
PROC_ATTR_TRIGGERED_CAN_PROC = 0x0000002, // aura can proc even with triggered spells
|
||||
PROC_ATTR_REQ_MANA_COST = 0x0000004, // requires triggering spell to have a mana cost for aura proc
|
||||
PROC_ATTR_REQ_SPELLMOD = 0x0000008, // requires triggering spell to be affected by proccing aura to drop charges
|
||||
PROC_ATTR_USE_STACKS_FOR_CHARGES = 0x0000010, // consuming proc drops a stack from proccing aura instead of charge
|
||||
PROC_ATTR_REDUCE_PROC_60 = 0x0000080, // aura should have a reduced chance to proc if level of proc actor > 60
|
||||
PROC_ATTR_CANT_PROC_FROM_ITEM_CAST = 0x0000100, // do not allow aura proc if proc is caused by a spell casted by item
|
||||
};
|
||||
|
||||
struct SpellProcEventEntry
|
||||
{
|
||||
uint32 schoolMask; // if nonzero - bit mask for matching proc condition based on spell candidate's school: Fire=2, Mask=1<<(2-1)=2
|
||||
uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value
|
||||
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_
|
||||
};
|
||||
|
||||
typedef std::unordered_map<uint32, SpellProcEventEntry> SpellProcEventMap;
|
||||
|
||||
struct SpellProcEntry
|
||||
{
|
||||
uint32 SchoolMask; // if nonzero - bitmask for matching proc condition based on spell's school
|
||||
@@ -296,6 +289,7 @@ struct SpellProcEntry
|
||||
uint32 SpellPhaseMask; // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase
|
||||
uint32 HitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit
|
||||
uint32 AttributesMask; // bitmask, see ProcAttributes
|
||||
uint32 DisableEffectsMask; // bitmask of effects to disable from triggering proc
|
||||
float ProcsPerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60
|
||||
float Chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if ProcsPerMinute set
|
||||
Milliseconds Cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura
|
||||
@@ -685,10 +679,6 @@ public:
|
||||
SpellGroupStackRule CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const;
|
||||
SpellGroupStackRule GetSpellGroupStackRule(SpellGroup group_id) const;
|
||||
|
||||
// Spell proc event table
|
||||
[[nodiscard]] SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) 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;
|
||||
bool CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const;
|
||||
@@ -769,7 +759,6 @@ public:
|
||||
void LoadSpellTargetPositions();
|
||||
void LoadSpellGroups();
|
||||
void LoadSpellGroupStackRules();
|
||||
void LoadSpellProcEvents();
|
||||
void LoadSpellProcs();
|
||||
void LoadSpellBonuses();
|
||||
void LoadSpellThreats();
|
||||
@@ -801,7 +790,6 @@ private:
|
||||
SpellGroupSpellMap mSpellGroupSpell;
|
||||
SpellGroupStackMap mSpellGroupStack;
|
||||
SameEffectStackMap mSpellSameEffectStack;
|
||||
SpellProcEventMap mSpellProcEventMap;
|
||||
SpellProcMap mSpellProcMap;
|
||||
SpellBonusMap mSpellBonusMap;
|
||||
SpellThreatMap mSpellThreatMap;
|
||||
|
||||
@@ -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<CheckEffectProcHandler>::iterator itr = DoCheckEffectProc.begin(); itr != DoCheckEffectProc.end(); ++itr)
|
||||
if (!itr->GetAffectedEffectsMask(entry))
|
||||
LOG_ERROR("spells.scripts", "Spell `{}` Effect `{}` of script `{}` did not match dbc effect data - handler bound to hook `DoCheckEffectProc` of AuraScript won't be executed", entry->Id, itr->ToString(), m_scriptName->c_str());
|
||||
|
||||
for (std::list<AfterCheckProcHandler>::iterator itr = DoAfterCheckProc.begin(); itr != DoAfterCheckProc.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 `DoAfterCheckProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
|
||||
@@ -906,6 +910,17 @@ bool AuraScript::CheckProcHandler::Call(AuraScript* auraScript, ProcEventInfo& e
|
||||
return (auraScript->*_HandlerScript)(eventInfo);
|
||||
}
|
||||
|
||||
AuraScript::CheckEffectProcHandler::CheckEffectProcHandler(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName)
|
||||
: EffectBase(effIndex, effName)
|
||||
{
|
||||
_HandlerScript = handlerScript;
|
||||
}
|
||||
|
||||
bool AuraScript::CheckEffectProcHandler::Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo)
|
||||
{
|
||||
return (auraScript->*_HandlerScript)(aurEff, eventInfo);
|
||||
}
|
||||
|
||||
AuraScript::AfterCheckProcHandler::AfterCheckProcHandler(AuraAfterCheckProcFnType handlerScript)
|
||||
{
|
||||
_HandlerScript = handlerScript;
|
||||
@@ -1037,9 +1052,9 @@ DynamicObject* AuraScript::GetDynobjOwner() const
|
||||
return m_aura->GetDynobjOwner();
|
||||
}
|
||||
|
||||
void AuraScript::Remove(uint32 removeMode)
|
||||
void AuraScript::Remove(AuraRemoveMode removeMode)
|
||||
{
|
||||
m_aura->Remove((AuraRemoveMode)removeMode);
|
||||
m_aura->Remove(removeMode);
|
||||
}
|
||||
|
||||
Aura* AuraScript::GetAura() const
|
||||
@@ -1177,6 +1192,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_EFFECT_PROC:
|
||||
case AURA_SCRIPT_HOOK_AFTER_CHECK_PROC:
|
||||
case AURA_SCRIPT_HOOK_PREPARE_PROC:
|
||||
case AURA_SCRIPT_HOOK_PROC:
|
||||
|
||||
@@ -499,6 +499,7 @@ enum AuraScriptHookType
|
||||
AURA_SCRIPT_HOOK_AFTER_DISPEL,
|
||||
// Spell Proc Hooks
|
||||
AURA_SCRIPT_HOOK_CHECK_PROC,
|
||||
AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC,
|
||||
AURA_SCRIPT_HOOK_AFTER_CHECK_PROC,
|
||||
AURA_SCRIPT_HOOK_PREPARE_PROC,
|
||||
AURA_SCRIPT_HOOK_PROC,
|
||||
@@ -530,6 +531,7 @@ public:
|
||||
typedef void(CLASSNAME::*AuraEffectAbsorbFnType)(AuraEffect*, DamageInfo &, uint32 &); \
|
||||
typedef void(CLASSNAME::*AuraEffectSplitFnType)(AuraEffect*, DamageInfo &, uint32 &); \
|
||||
typedef bool(CLASSNAME::*AuraCheckProcFnType)(ProcEventInfo&); \
|
||||
typedef bool(CLASSNAME::*AuraCheckEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \
|
||||
typedef bool(CLASSNAME::*AuraAfterCheckProcFnType)(ProcEventInfo&, bool); \
|
||||
typedef void(CLASSNAME::*AuraProcFnType)(ProcEventInfo&); \
|
||||
typedef void(CLASSNAME::*AuraEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \
|
||||
@@ -640,6 +642,14 @@ public:
|
||||
private:
|
||||
AuraCheckProcFnType _HandlerScript;
|
||||
};
|
||||
class CheckEffectProcHandler : public EffectBase
|
||||
{
|
||||
public:
|
||||
CheckEffectProcHandler(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName);
|
||||
bool Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo);
|
||||
private:
|
||||
AuraCheckEffectProcFnType _HandlerScript;
|
||||
};
|
||||
class AfterCheckProcHandler
|
||||
{
|
||||
public:
|
||||
@@ -678,6 +688,7 @@ public:
|
||||
class EffectManaShieldFunction : public AuraScript::EffectManaShieldHandler { public: EffectManaShieldFunction(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectManaShieldHandler((AuraScript::AuraEffectAbsorbFnType)_pEffectHandlerScript, _effIndex) {} }; \
|
||||
class EffectSplitFunction : public AuraScript::EffectSplitHandler { public: EffectSplitFunction(AuraEffectSplitFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectSplitHandler((AuraScript::AuraEffectSplitFnType)_pEffectHandlerScript, _effIndex) {} }; \
|
||||
class CheckProcHandlerFunction : public AuraScript::CheckProcHandler { public: CheckProcHandlerFunction(AuraCheckProcFnType handlerScript) : AuraScript::CheckProcHandler((AuraScript::AuraCheckProcFnType)handlerScript) {} }; \
|
||||
class CheckEffectProcHandlerFunction : public AuraScript::CheckEffectProcHandler { public: CheckEffectProcHandlerFunction(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName) : AuraScript::CheckEffectProcHandler((AuraScript::AuraCheckEffectProcFnType)handlerScript, effIndex, effName) {} }; \
|
||||
class AfterCheckProcHandlerFunction : public AuraScript::AfterCheckProcHandler { public: AfterCheckProcHandlerFunction(AuraAfterCheckProcFnType handlerScript) : AuraScript::AfterCheckProcHandler((AuraScript::AuraAfterCheckProcFnType)handlerScript) {} }; \
|
||||
class AuraProcHandlerFunction : public AuraScript::AuraProcHandler { public: AuraProcHandlerFunction(AuraProcFnType handlerScript) : AuraScript::AuraProcHandler((AuraScript::AuraProcFnType)handlerScript) {} }; \
|
||||
class EffectProcHandlerFunction : public AuraScript::EffectProcHandler { public: EffectProcHandlerFunction(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName) : AuraScript::EffectProcHandler((AuraScript::AuraEffectProcFnType)effectHandlerScript, effIndex, effName) {} }; \
|
||||
@@ -816,6 +827,13 @@ public:
|
||||
// where function is: bool function (ProcEventInfo& eventInfo);
|
||||
HookList<CheckProcHandler> DoCheckProc;
|
||||
#define AuraCheckProcFn(F) CheckProcHandlerFunction(&F)
|
||||
|
||||
// executed when aura effect checks if it can proc the aura
|
||||
// example: DoCheckEffectProc += AuraCheckEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier);
|
||||
// where function is: bool function (AuraEffect const* aurEff, ProcEventInfo& eventInfo);
|
||||
HookList<CheckEffectProcHandler> DoCheckEffectProc;
|
||||
#define AuraCheckEffectProcFn(F, I, N) CheckEffectProcHandlerFunction(&F, I, N)
|
||||
|
||||
// executed when aura checks if it can proc
|
||||
// example: DoAfterCheckProc += AuraCheckProcFn(class::function);
|
||||
// where function is: bool function (ProcEventInfo& eventInfo);
|
||||
@@ -870,7 +888,7 @@ public:
|
||||
DynamicObject* GetDynobjOwner() const;
|
||||
|
||||
// removes aura with remove mode (see AuraRemoveMode enum)
|
||||
void Remove(uint32 removeMode = 0);
|
||||
void Remove(AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT);
|
||||
// returns aura object of script
|
||||
Aura* GetAura() const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user