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:
blinkysc
2026-02-18 05:31:53 -06:00
committed by GitHub
parent 65a869ea27
commit 4599f26ae9
76 changed files with 22915 additions and 5181 deletions

View File

@@ -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;