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
+43 -62
View File
@@ -7372,7 +7372,7 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32
if (entry)
{
if (entry->PPMChance)
chance = GetPPMProcChance(proto->Delay, entry->PPMChance, spellInfo);
chance = GetPPMProcChance(GetAttackTime(attType), entry->PPMChance, spellInfo);
else if (entry->customChance)
chance = (float)entry->customChance;
}
@@ -9745,8 +9745,8 @@ bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod
if (!mod || !spellInfo)
return false;
// Mod out of charges
if (spell && mod->charges == -1 && spell->m_appliedMods.find(mod->ownerAura) == spell->m_appliedMods.end())
// First time this aura applies a mod to us and is out of charges
if (spell && mod->ownerAura && mod->ownerAura->IsUsingCharges() && !mod->ownerAura->GetCharges() && !spell->m_appliedMods.count(mod->ownerAura))
return false;
// +duration to infinite duration spells making them limited
@@ -9769,7 +9769,7 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s
auto calculateSpellMod = [&](SpellModifier* mod)
{
// xinef: temporary pets cannot use charged mods of owner, needed for mirror image QQ they should use their own auras
if (temporaryPet && mod->charges != 0)
if (temporaryPet && mod->ownerAura && mod->ownerAura->IsUsingCharges())
return;
// skip if already instant or cost is free
@@ -9801,10 +9801,10 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s
if (mod->op == SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100)
return;
// xinef: special exception for surge of light, dont affect crit chance if previous mods were not applied
else if (mod->op == SPELLMOD_CRITICAL_CHANCE && spell && !HasSpellMod(mod, spell))
else if (mod->op == SPELLMOD_CRITICAL_CHANCE && !HasSpellModApplied(mod, spell))
return;
// xinef: special case for backdraft gcd reduce with backlast time reduction, dont affect gcd if cast time was not applied
else if (mod->op == SPELLMOD_GLOBAL_COOLDOWN && spell && !HasSpellMod(mod, spell))
else if (mod->op == SPELLMOD_GLOBAL_COOLDOWN && !HasSpellModApplied(mod, spell))
return;
// xinef: those two mods should be multiplicative (Glyph of Renew)
@@ -9814,7 +9814,7 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s
totalmul += CalculatePct(1.0f, mod->value);
}
DropModCharge(mod, spell);
ApplyModToSpell(mod, spell);
};
// Drop charges for triggering spells instead of triggered ones
@@ -9823,10 +9823,6 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s
for (auto mod : m_spellMods[op])
{
// Charges can be set only for mods with auras
if (!mod->ownerAura)
ASSERT(!mod->charges);
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -9843,17 +9839,6 @@ template AC_GAME_API void Player::ApplySpellMod(uint32 spellId, SpellModOp op, i
template AC_GAME_API void Player::ApplySpellMod(uint32 spellId, SpellModOp op, uint32& basevalue, Spell* spell, bool temporaryPet);
template AC_GAME_API void Player::ApplySpellMod(uint32 spellId, SpellModOp op, float& basevalue, Spell* spell, bool temporaryPet);
// Binary predicate for sorting SpellModifiers
struct SpellModPredicate
{
bool operator() (SpellModifier const* a, SpellModifier const* b) const
{
if (a->type != b->type)
return a->type == SPELLMOD_FLAT;
return a->priority > b->priority;
}
};
void Player::AddSpellMod(SpellModifier* mod, bool apply)
{
LOG_DEBUG("spells.aura", "Player::AddSpellMod {}", mod->spellId);
@@ -9870,7 +9855,7 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply)
if (mod->mask & _mask)
{
int32 val = 0;
for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr)
for (SpellModContainer::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr)
{
if ((*itr)->type == mod->type && (*itr)->mask & _mask)
val += (*itr)->value;
@@ -9886,12 +9871,11 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply)
if (apply)
{
m_spellMods[mod->op].push_back(mod);
m_spellMods[mod->op].sort(SpellModPredicate());
m_spellMods[mod->op].insert(mod);
}
else
{
m_spellMods[mod->op].remove(mod);
m_spellMods[mod->op].erase(mod);
// mods bound to aura will be removed in AuraEffect::~AuraEffect
if (!mod->ownerAura)
delete mod;
@@ -9908,7 +9892,7 @@ void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura)
for (uint8 i = 0; i < MAX_SPELLMOD; ++i)
{
for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end(); ++itr)
for (SpellModContainer::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end(); ++itr)
{
SpellModifier* mod = *itr;
@@ -9937,20 +9921,6 @@ void Player::RestoreSpellMods(Spell* spell, uint32 ownerAuraId, Aura* aura)
// from applied mods (Else, an aura with two mods on the current spell would
// only see the first of its modifier restored)
aurasQueue.push_back(mod->ownerAura);
// add mod charges back to mod
if (mod->charges == -1)
mod->charges = 1;
else
mod->charges++;
// Do not set more spellmods than available
if (mod->ownerAura->GetCharges() < mod->charges)
mod->charges = mod->ownerAura->GetCharges();
// Skip this check for now - aura charges may change due to various reason
/// @todo track these changes correctly
//ASSERT (mod->ownerAura->GetCharges() <= mod->charges);
}
}
@@ -9985,14 +9955,14 @@ void Player::RemoveSpellMods(Spell* spell)
for (uint8 i = 0; i < MAX_SPELLMOD; ++i)
{
for (SpellModList::const_iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();)
for (SpellModContainer::const_iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();)
{
SpellModifier* mod = *itr;
++itr;
// don't handle spells with proc_event entry defined
// don't handle spells with spell_proc entry defined
// this is a temporary workaround, because all spellmods should be handled like that
if (sSpellMgr->GetSpellProcEvent(mod->spellId))
if (sSpellMgr->GetSpellProcEntry(mod->spellId))
{
continue;
}
@@ -10018,20 +9988,14 @@ void Player::RemoveSpellMods(Spell* spell)
if (sp->SpellIconID == 3261 || sp->SpellIconID == 2999 || sp->SpellIconID == 2938)
if (AuraEffect* aurEff = GetAuraEffectDummy(64869))
if (roll_chance_i(aurEff->GetAmount()))
{
mod->charges = 1;
continue;
}
continue; // don't consume charge
}
// ROGUE MUTILATE WITH COLD BLOOD
if (spellInfo->Id == 5374)
{
SpellInfo const* sp = mod->ownerAura->GetSpellInfo();
if (sp->Id == 14177) // Cold Blood
{
mod->charges = 1;
continue;
}
continue; // don't consume charge
}
if (mod->ownerAura->DropCharge(AURA_REMOVE_BY_EXPIRE))
@@ -10040,15 +10004,25 @@ void Player::RemoveSpellMods(Spell* spell)
}
}
void Player::DropModCharge(SpellModifier* mod, Spell* spell)
void Player::ApplyModToSpell(SpellModifier* mod, Spell* spell)
{
if (spell && mod->ownerAura && mod->charges > 0)
{
if (--mod->charges == 0)
mod->charges = -1;
if (!spell)
return;
spell->m_appliedMods.insert(mod->ownerAura);
}
// don't do anything with no charges
if (mod->ownerAura->IsUsingCharges() && !mod->ownerAura->GetCharges())
return;
// register inside spell, proc system uses this to drop charges
spell->m_appliedMods.insert(mod->ownerAura);
}
bool Player::HasSpellModApplied(SpellModifier* mod, Spell* spell)
{
if (!spell)
return false;
return spell->m_appliedMods.count(mod->ownerAura) != 0;
}
void Player::SetSpellModTakingSpell(Spell* spell, bool apply)
@@ -11770,6 +11744,9 @@ void Player::ApplyEquipCooldown(Item* pItem)
if (pItem->GetTemplate()->HasFlag(ITEM_FLAG_NO_EQUIP_COOLDOWN))
return;
if (GetCommandStatus(CHEAT_COOLDOWN))
return;
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
_Spell const& spellData = pItem->GetTemplate()->Spells[i];
@@ -11778,11 +11755,15 @@ void Player::ApplyEquipCooldown(Item* pItem)
if (!spellData.SpellId)
continue;
// xinef: apply hidden cooldown for procs
// apply proc cooldown to equip auras if we have any
if (spellData.SpellTrigger == ITEM_SPELLTRIGGER_ON_EQUIP)
{
// xinef: uint32(-1) special marker for proc cooldowns
AddSpellCooldown(spellData.SpellId, uint32(-1), 30 * IN_MILLISECONDS);
SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(spellData.SpellId);
if (!procEntry)
continue;
if (Aura* itemAura = GetAura(spellData.SpellId, GetGUID(), pItem->GetGUID()))
itemAura->AddProcCooldown(std::chrono::steady_clock::now() + std::chrono::seconds(30));
continue;
}
+11 -11
View File
@@ -180,20 +180,19 @@ enum TalentTree // talent tabs
// Spell modifier (used for modify other spells)
struct SpellModifier
{
SpellModifier(Aura* _ownerAura = nullptr) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), charges(0), mask(), ownerAura(_ownerAura) {}
SpellModOp op : 8;
SpellModType type : 8;
int16 charges : 16;
int32 value{0};
SpellModifier(Aura* _ownerAura = nullptr) : op(SPELLMOD_DAMAGE), type(SPELLMOD_FLAT), value(0), mask(), spellId(0), ownerAura(_ownerAura) {}
SpellModOp op;
SpellModType type;
int32 value;
flag96 mask;
uint32 spellId{0};
uint32 spellId;
Aura* const ownerAura;
uint32 priority{0};
};
typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap;
typedef std::unordered_map<uint32, PlayerSpell*> PlayerSpellMap;
typedef std::list<SpellModifier*> SpellModList;
typedef std::unordered_set<SpellModifier*> SpellModContainer;
typedef GuidList WhisperListContainer;
@@ -1801,7 +1800,8 @@ public:
void RemoveSpellMods(Spell* spell);
void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr);
void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr);
void DropModCharge(SpellModifier* mod, Spell* spell);
static void ApplyModToSpell(SpellModifier* mod, Spell* spell);
[[nodiscard]] static bool HasSpellModApplied(SpellModifier* mod, Spell* spell);
void SetSpellModTakingSpell(Spell* spell, bool apply);
[[nodiscard]] bool HasSpellCooldown(uint32 spell_id) const override;
@@ -2640,7 +2640,7 @@ public:
// mt maps
[[nodiscard]] const PlayerTalentMap& GetTalentMap() const { return m_talents; }
[[nodiscard]] uint32 GetNextSave() const { return m_nextSave; }
[[nodiscard]] SpellModList const& GetSpellModList(uint32 type) const { return m_spellMods[type]; }
[[nodiscard]] SpellModContainer const& GetSpellModList(uint32 type) const { return m_spellMods[type]; }
void SetServerSideVisibility(ServerSideVisibilityType type, AccountTypes sec);
void SetServerSideVisibilityDetect(ServerSideVisibilityType type, AccountTypes sec);
@@ -2873,7 +2873,7 @@ protected:
uint32 m_baseHealthRegen;
int32 m_spellPenetrationItemMod;
SpellModList m_spellMods[MAX_SPELLMOD];
SpellModContainer m_spellMods[MAX_SPELLMOD];
//uint32 m_pad;
// Spell* m_spellModTakingSpell; // Spell for which charges are dropped in spell::finish
File diff suppressed because it is too large Load Diff
+16 -16
View File
@@ -347,6 +347,7 @@ private:
uint32 m_resist;
uint32 m_block;
uint32 m_cleanDamage;
uint32 m_hitMask;
// amalgamation constructor (used for proc)
DamageInfo(DamageInfo const& dmg1, DamageInfo const& dmg2);
@@ -355,7 +356,8 @@ public:
explicit DamageInfo(Unit* _attacker, Unit* _victim, uint32 _damage, SpellInfo const* _spellInfo, SpellSchoolMask _schoolMask, DamageEffectType _damageType, uint32 cleanDamage = 0);
explicit DamageInfo(CalcDamageInfo const& dmgInfo); // amalgamation wrapper
DamageInfo(CalcDamageInfo const& dmgInfo, uint8 damageIndex);
DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType);
DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, uint32 hitMask);
DamageInfo(SpellNonMeleeDamage const& spellNonMeleeDamage, DamageEffectType damageType, WeaponAttackType attackType, SpellMissInfo missInfo);
void ModifyDamage(int32 amount);
void AbsorbDamage(uint32 amount);
@@ -373,6 +375,8 @@ public:
[[nodiscard]] uint32 GetResist() const { return m_resist; };
[[nodiscard]] uint32 GetBlock() const { return m_block; };
[[nodiscard]] uint32 GetHitMask() const;
void AddHitMask(uint32 hitMask) { m_hitMask |= hitMask; }
[[nodiscard]] uint32 GetUnmitigatedDamage() const;
};
@@ -386,9 +390,10 @@ private:
uint32 m_absorb;
SpellInfo const* const m_spellInfo;
SpellSchoolMask const m_schoolMask;
uint32 m_hitMask;
public:
explicit HealInfo(Unit* _healer, Unit* _target, uint32 _heal, SpellInfo const* _spellInfo, SpellSchoolMask _schoolMask)
: m_healer(_healer), m_target(_target), m_heal(_heal), m_spellInfo(_spellInfo), m_schoolMask(_schoolMask)
: m_healer(_healer), m_target(_target), m_heal(_heal), m_spellInfo(_spellInfo), m_schoolMask(_schoolMask), m_hitMask(0)
{
m_absorb = 0;
m_effectiveHeal = 0;
@@ -421,6 +426,8 @@ public:
[[nodiscard]] uint32 GetAbsorb() const { return m_absorb; }
[[nodiscard]] SpellInfo const* GetSpellInfo() const { return m_spellInfo; };
[[nodiscard]] SpellSchoolMask GetSchoolMask() const { return m_schoolMask; };
[[nodiscard]] uint32 GetHitMask() const { return m_hitMask; }
void AddHitMask(uint32 hitMask) { m_hitMask |= hitMask; }
};
class ProcEventInfo
@@ -451,7 +458,7 @@ public:
[[nodiscard]] uint32 GetSpellPhaseMask() const { return _spellPhaseMask; }
[[nodiscard]] uint32 GetHitMask() const { return _hitMask; }
[[nodiscard]] SpellInfo const* GetSpellInfo() const;
[[nodiscard]] SpellSchoolMask GetSchoolMask() const { return SPELL_SCHOOL_MASK_NONE; }
[[nodiscard]] SpellSchoolMask GetSchoolMask() const;
[[nodiscard]] Spell const* GetProcSpell() const { return _spell; }
[[nodiscard]] DamageInfo* GetDamageInfo() const { return _damageInfo; }
[[nodiscard]] HealInfo* GetHealInfo() const { return _healInfo; }
@@ -486,7 +493,6 @@ struct CalcDamageInfo
WeaponAttackType attackType; //
uint32 procAttacker;
uint32 procVictim;
uint32 procEx;
uint32 cleanDamage; // Used only for rage calculation
MeleeHitOutcome hitOutCome; /// @todo: remove this field (need use TargetState)
};
@@ -530,7 +536,6 @@ struct SpellPeriodicAuraLogInfo
};
void createProcFlags(SpellInfo const* spellInfo, WeaponAttackType attackType, bool positive, uint32& procAttacker, uint32& procVictim);
uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition);
#define MAX_DECLINED_NAME_CASES 5
@@ -627,8 +632,6 @@ typedef std::unordered_map<uint32, uint32> PacketCooldowns;
#define ATTACK_DISPLAY_DELAY 200
#define MAX_PLAYER_STEALTH_DETECT_RANGE 30.0f // max distance for detection targets by player
struct SpellProcEventEntry; // used only privately
enum class SpeedOpcodeIndex : uint32
{
PC,
@@ -671,6 +674,7 @@ public:
typedef std::vector<AuraEffect*> AuraEffectList;
typedef std::list<Aura*> AuraList;
typedef std::list<AuraApplication*> AuraApplicationList;
typedef std::vector<std::pair<uint8 /*procEffectMask*/, AuraApplication*>> AuraApplicationProcContainer;
typedef std::list<DiminishingReturn> Diminishing;
typedef GuidUnorderedSet ComboPointHolderSet;
@@ -1542,14 +1546,14 @@ public:
bool CanProc() { return !m_procDeep; }
void SetCantProc(bool apply);
static void ProcDamageAndSpell(Unit* actor, Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellInfo const* procSpellInfo = nullptr, SpellInfo const* procAura = nullptr, int8 procAuraEffectIndex = -1, Spell const* procSpell = nullptr, DamageInfo* damageInfo = nullptr, HealInfo* healInfo = nullptr, uint32 procPhase = 2 /*PROC_SPELL_PHASE_HIT*/);
void ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpellInfo, uint32 damage, SpellInfo const* procAura = nullptr, int8 procAuraEffectIndex = -1, Spell const* procSpell = nullptr, DamageInfo* damageInfo = nullptr, HealInfo* healInfo = nullptr, uint32 procPhase = 2 /*PROC_SPELL_PHASE_HIT*/);
static void ProcSkillsAndAuras(Unit* actor, Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellInfo const* procSpellInfo = nullptr, SpellInfo const* procAura = nullptr, int8 procAuraEffectIndex = -1, Spell const* procSpell = nullptr, DamageInfo* damageInfo = nullptr, HealInfo* healInfo = nullptr, uint32 procPhase = 2 /*PROC_SPELL_PHASE_HIT*/);
void ProcSkillsAndReactives(bool isVictim, Unit* target, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellInfo const* procSpellInfo, uint32 damage, SpellInfo const* procAura = nullptr, int8 procAuraEffectIndex = -1, Spell const* procSpell = nullptr, DamageInfo* damageInfo = nullptr, HealInfo* healInfo = nullptr, uint32 procPhase = 2 /*PROC_SPELL_PHASE_HIT*/);
void GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo);
void GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo);
void TriggerAurasProcOnEvent(CalcDamageInfo& damageInfo);
void TriggerAurasProcOnEvent(std::list<AuraApplication*>* myProcAuras, std::list<AuraApplication*>* targetProcAuras, Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo);
void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, std::list<AuraApplication*>& procAuras);
void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& procAuras);
[[nodiscard]] float GetWeaponProcChance() const;
float GetPPMProcChance(uint32 WeaponSpeed, float PPM, SpellInfo const* spellProto) const;
@@ -2188,11 +2192,7 @@ protected:
bool _instantCast;
private:
bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent, ProcEventInfo const& eventInfo);
bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, ProcEventInfo const& eventInfo);
bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool* handled);
bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, uint32 procPhase, ProcEventInfo& eventInfo);
bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown);
// Legacy proc handlers removed - all procs now use AuraScripts and spell_proc table
bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura);
bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura);