refactor(Core/Spells): Implement QAston Proc System (#11079)

* .

* sql

* .

* .

* 1

* 2

* 3

* 4

* 5

* 6

* 7

* 8

* 9

* 10

* 11

* 12

* 13

* 14

* 15

* Update spell_item.cpp

* Update Unit.cpp

* 16

* 17

* 18

* 19

* 20

* 21

* Update Unit.cpp

* REVERT UltraNIX Commit

* 22

* 23

* .

* .

* .

* warrior

* warlock

* shaman rogue priest paladin mage

* spell item

* hunter

* druid

* dk

* war

* error style

* Update rev_1647677899565690722.sql

* Update rev_1647677899565690722.sql

* Update rev_1647677899565690722.sql

* .

* DOND DEL ME WAD DO DO

* error 2

* .

* .

* .

* FIX

* Update SpellInfoCorrections.cpp

* Update SpellInfoCorrections.cpp

* .

* ja genau

* Update .gitignore

* .

* .

* .,

* .

* .

* .

* .

* Update Unit.cpp
This commit is contained in:
IntelligentQuantum
2022-10-02 21:09:34 +03:30
committed by GitHub
parent 5189b43a28
commit cbd3fd0967
54 changed files with 9126 additions and 5957 deletions

View File

@@ -103,8 +103,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 AuraEffect::HandleProc
&AuraEffect::HandleNoImmediateEffect, // 43 SPELL_AURA_PROC_TRIGGER_DAMAGE implemented in AuraEffect::HandleProc
&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)
@@ -323,7 +323,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS] =
&AuraEffect::HandleNoImmediateEffect, //259 SPELL_AURA_MOD_HOT_PCT implemented in Unit::SpellHealingBonus
&AuraEffect::HandleNoImmediateEffect, //260 SPELL_AURA_SCREEN_EFFECT (miscvalue = id in ScreenEffect.dbc) not required any code
&AuraEffect::HandlePhase, //261 SPELL_AURA_PHASE
&AuraEffect::HandleNoImmediateEffect, //262 SPELL_AURA_ABILITY_IGNORE_AURASTATE implemented in spell::cancast
&AuraEffect::HandleNoImmediateEffect, //262 SPELL_AURA_ABILITY_IGNORE_AURASTATE implemented in Spell::CheckCast
&AuraEffect::HandleAuraAllowOnlyAbility, //263 SPELL_AURA_ALLOW_ONLY_ABILITY player can use only abilities set in SpellClassMask
&AuraEffect::HandleUnused, //264 unused (3.2.0)
&AuraEffect::HandleUnused, //265 unused (3.2.0)
@@ -631,7 +631,7 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load)
{
// Apply periodic time mod
if (modOwner)
modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_amplitude);
modOwner->ApplySpellMod<SPELLMOD_ACTIVATION_TIME>(GetId(), m_amplitude);
if (caster)
{
@@ -689,7 +689,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->value = GetAmount();
break;
@@ -1072,14 +1071,11 @@ bool AuraEffect::IsAffectedOnSpell(SpellInfo const* spell) const
{
if (!spell)
return false;
// Check family name
if (spell->SpellFamilyName != m_spellInfo->SpellFamilyName)
// Check family name and EffectClassMask
if (!spell->IsAffected(m_spellInfo->SpellFamilyName, m_spellInfo->Effects[m_effIndex].SpellClassMask))
return false;
// Check EffectClassMask
if (m_spellInfo->Effects[m_effIndex].SpellClassMask & spell->SpellFamilyFlags)
return true;
return false;
return true;
}
bool AuraEffect::HasSpellClassMask() const
@@ -1146,6 +1142,99 @@ 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 == eventInfo.GetSpellInfo())
{
Aura* aura = GetBase();
// called from spellcast, should not have ticked yet
if (aura->GetDuration() == aura->GetMaxDuration())
{
return false;
}
}
break;
}
break;
case SPELL_AURA_MECHANIC_IMMUNITY:
case SPELL_AURA_MOD_MECHANIC_RESISTANCE:
// Compare mechanic
if (!spellInfo || static_cast<int32>(spellInfo->Mechanic) != GetMiscValue())
{
return false;
}
break;
case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK:
// Skip melee hits and instant cast spells
if (!spellInfo || !spellInfo->CalcCastTime())
{
return false;
}
break;
case SPELL_AURA_MOD_DAMAGE_FROM_CASTER:
// Compare casters
if (GetCasterGUID() != eventInfo.GetActor()->GetGUID())
{
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->ManaCost && !spellInfo->ManaCostPercentage) || // Cost Check
!(spellInfo->GetSchoolMask() & GetMiscValue())) // School Check
{
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_PROC_TRIGGER_SPELL:
case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE:
{
// Don't proc extra attacks while already processing extra attack spell
uint32 triggerSpellId = GetSpellInfo()->Effects[GetEffIndex()].TriggerSpell;
if (SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(triggerSpellId))
{
if (aurApp->GetTarget()->m_extraAttacks && triggeredSpellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
{
return false;
}
}
break;
}
default:
break;
}
return result;
}
void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
{
bool prevented = GetBase()->CallScriptEffectProcHandlers(this, aurApp, eventInfo);
@@ -1154,6 +1243,16 @@ void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
switch (GetAuraType())
{
// CC Auras which use their amount to drop
// Are there anyu more auras which need this?
case SPELL_AURA_MOD_CONFUSE:
case SPELL_AURA_MOD_FEAR:
case SPELL_AURA_MOD_STUN:
case SPELL_AURA_MOD_ROOT:
case SPELL_AURA_TRANSFORM:
HandleBreakableCCAuraProc(aurApp, eventInfo);
break;
case SPELL_AURA_DUMMY:
case SPELL_AURA_PROC_TRIGGER_SPELL:
HandleProcTriggerSpellAuraProc(aurApp, eventInfo);
break;
@@ -2878,7 +2977,6 @@ void AuraEffect::HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bo
displayId = 0;
}
}
}
target->Mount(displayId, vehicleId, GetMiscValue());
}
@@ -5116,12 +5214,6 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
if (target->GetTypeId() == TYPEID_PLAYER)
target->ToPlayer()->RemoveAmmo(); // not use ammo and not allow use
break;
case 71563:
{
if (Aura* newAura = target->AddAura(71564, target))
newAura->SetStackAmount(newAura->GetSpellInfo()->StackAmount);
return;
}
}
}
// AT REMOVE
@@ -6354,10 +6446,8 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const
mitigatedDamage += resilienceReduction;
}
damage = std::max(0, dmg);
cleanDamage.mitigated_damage = std::max(0, mitigatedDamage);
DamageInfo dmgInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK, cleanDamage.mitigated_damage);
DamageInfo dmgInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, cleanDamage.mitigated_damage);
Unit::CalcAbsorbResist(dmgInfo);
uint32 absorb = dmgInfo.GetAbsorb();
@@ -6371,12 +6461,15 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const
// Set trigger flag
uint32 procAttacker = PROC_FLAG_DONE_PERIODIC;
uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC;
uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT;
uint32 hitMask = dmgInfo.GetHitMask();
if (absorb > 0)
procEx |= PROC_EX_ABSORB;
hitMask |= PROC_HIT_ABSORB;
if (damage)
{
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
hitMask = crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL;
}
int32 overkill = damage - target->GetHealth();
if (overkill < 0)
@@ -6387,7 +6480,8 @@ 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);
// allow null caster to call this function
caster->ProcSkillsAndAuras(target, caster ? procAttacker : 0, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &dmgInfo, nullptr);
}
void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) const
@@ -6440,10 +6534,8 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
cleanDamageAmount += resilienceReduction;
}
damage = std::max(0, dmg);
cleanDamage.mitigated_damage = std::max(0, cleanDamageAmount);
DamageInfo dmgInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, BASE_ATTACK, cleanDamage.mitigated_damage);
DamageInfo dmgInfo(caster, target, damage, GetSpellInfo(), GetSpellInfo()->GetSchoolMask(), DOT, cleanDamage.mitigated_damage);
Unit::CalcAbsorbResist(dmgInfo);
uint32 absorb = dmgInfo.GetAbsorb();
@@ -6453,12 +6545,15 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
// Set trigger flag
uint32 procAttacker = PROC_FLAG_DONE_PERIODIC;
uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC;
uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_DOT;
uint32 hitMask = dmgInfo.GetHitMask();
if (absorb > 0)
procEx |= PROC_EX_ABSORB;
hitMask |= PROC_HIT_ABSORB;
if (dmgInfo.GetDamage())
{
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
hitMask |= crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL;
}
if (target->GetHealth() < dmgInfo.GetDamage())
{
@@ -6476,7 +6571,8 @@ 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);
// allow null caster to call this function
caster->ProcSkillsAndAuras(target, caster ? procAttacker : 0, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, hitMask, nullptr, &dmgInfo, nullptr);
if (!caster || !caster->IsAlive())
return;
@@ -6489,6 +6585,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
HealInfo healInfo(caster, caster, heal, GetSpellInfo(), GetSpellInfo()->GetSchoolMask());
int32 gain = caster->HealBySpell(healInfo);
caster->getHostileRefMgr().threatAssist(caster, gain * 0.5f, GetSpellInfo());
caster->ProcSkillsAndAuras(caster, PROC_FLAG_DONE_PERIODIC, PROC_FLAG_TAKEN_PERIODIC, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo);
}
void AuraEffect::HandlePeriodicHealthFunnelAuraTick(Unit* target, Unit* caster) const
@@ -6622,13 +6719,13 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
HealInfo healInfo(caster, target, heal, GetSpellInfo(), GetSpellInfo()->GetSchoolMask());
Unit::CalcHealAbsorb(healInfo);
int32 gain = Unit::DealHeal(caster, target, healInfo.GetHeal());
Unit::DealHeal(healInfo);
SpellPeriodicAuraLogInfo pInfo(this, healInfo.GetHeal(), healInfo.GetHeal() - gain, healInfo.GetAbsorb(), 0, 0.0f, crit);
SpellPeriodicAuraLogInfo pInfo(this, healInfo.GetHeal(), healInfo.GetHeal() - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit);
target->SendPeriodicAuraLog(&pInfo);
if (caster)
target->getHostileRefMgr().threatAssist(caster, float(gain) * 0.5f, GetSpellInfo());
target->getHostileRefMgr().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, GetSpellInfo());
bool haveCastItem = GetBase()->GetCastItemGUID();
@@ -6638,9 +6735,9 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
if (target != caster && GetSpellInfo()->HasAttribute(SPELL_ATTR2_NO_TARGET_PER_SECOND_COST))
{
uint32 manaPerSecond = GetSpellInfo()->ManaPerSecond;
if ((int32)manaPerSecond > gain && gain > 0)
if (manaPerSecond > healInfo.GetEffectiveHeal() && healInfo.GetEffectiveHeal() > 0)
{
manaPerSecond = gain;
manaPerSecond = healInfo.GetEffectiveHeal();
}
uint32 absorb2 = 0;
@@ -6652,13 +6749,15 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
uint32 procAttacker = PROC_FLAG_DONE_PERIODIC;
uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC;
uint32 procEx = (crit ? PROC_EX_CRITICAL_HIT : PROC_EX_NORMAL_HIT) | PROC_EX_INTERNAL_HOT;
uint32 hitMask = (crit ? PROC_HIT_CRITICAL : PROC_HIT_NORMAL);
if (healInfo.GetAbsorb() > 0)
procEx |= PROC_EX_ABSORB;
{
hitMask |= PROC_HIT_ABSORB;
}
// 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);
caster->ProcSkillsAndAuras(target, caster ? procAttacker : 0, procVictim, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo);
}
void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) const
@@ -6839,14 +6938,32 @@ 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;
uint32 hitMask = createProcHitMask(&damageInfo, SPELL_MISS_NONE);
uint32 spellTypeMask = PROC_SPELL_TYPE_NO_DMG_HEAL;
if (damageInfo.damage)
{
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
spellTypeMask |= PROC_SPELL_TYPE_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, hitMask);
caster->ProcSkillsAndAuras(target, procAttacker, procVictim, spellTypeMask, PROC_SPELL_PHASE_NONE, hitMask, nullptr, &dmgInfo, nullptr);
}
void AuraEffect::HandleBreakableCCAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
{
int32 const damageLeft = GetAmount() - static_cast<int32>(eventInfo.GetDamageInfo()->GetDamage());
if (damageLeft <= 0)
{
aurApp->GetTarget()->RemoveAura(aurApp);
}
else
{
SetAmount(damageLeft);
}
}
void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo)
@@ -6862,7 +6979,7 @@ void AuraEffect::HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEve
}
else
{
LOG_DEBUG("spells.aura", "AuraEffect::HandleProcTriggerSpellAuraProc: Could not trigger spell {} from aura {} proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId());
LOG_ERROR("spells.aura", "AuraEffect::HandleProcTriggerSpellAuraProc: Could not trigger spell {} from aura {} proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId());
}
}
@@ -6883,7 +7000,7 @@ void AuraEffect::HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp
}
else
{
LOG_DEBUG("spells.aura", "AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell {} from aura {} proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId());
LOG_ERROR("spells.aura", "AuraEffect::HandleProcTriggerSpellWithValueAuraProc: Could not trigger spell {} from aura {} proc, because the spell does not have an entry in Spell.dbc.", triggerSpellId, GetId());
}
}

View File

@@ -49,7 +49,6 @@ public:
Aura* GetBase() const { return m_base; }
void GetTargetList(std::list<Unit*>& targetList) const;
void GetApplicationList(std::list<AuraApplication*>& applicationList) const;
SpellModifier* GetSpellModifier() const { return m_spellmod; }
SpellInfo const* GetSpellInfo() const { return m_spellInfo; }
uint32 GetId() const;
@@ -96,6 +95,7 @@ public:
void SendTickImmune(Unit* target, Unit* caster) const;
void PeriodicTick(AuraApplication* aurApp, Unit* caster) const;
bool CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) const;
void HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo);
void CleanupTriggeredSpells(Unit* target);
@@ -333,6 +333,7 @@ public:
void HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) const;
// aura effect proc handlers
void HandleBreakableCCAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo);
void HandleProcTriggerSpellAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo);
void HandleProcTriggerSpellWithValueAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo);
void HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEventInfo& eventInfo);

View File

@@ -411,7 +411,7 @@ Aura::Aura(SpellInfo const* spellproto, WorldObject* owner, Unit* caster, Item*
m_castItemGuid(itemGUID ? itemGUID : castItem ? castItem->GetGUID() : ObjectGuid::Empty), m_castItemEntry(castItem ? castItem->GetEntry() : 0), m_applyTime(GameTime::GetGameTime().count()),
m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0),
m_casterLevel(caster ? caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1),
m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_triggeredByAuraSpellInfo(nullptr)
m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_procCooldown(std::chrono::steady_clock::time_point::min()), m_triggeredByAuraSpellInfo(nullptr)
{
if ((m_spellInfo->ManaPerSecond || m_spellInfo->ManaPerSecondPerLevel) && !m_spellInfo->HasAttribute(SPELL_ATTR2_NO_TARGET_PER_SECOND_COST))
m_timeCla = 1 * IN_MILLISECONDS;
@@ -875,7 +875,7 @@ int32 Aura::CalcMaxDuration(Unit* caster) const
// IsPermanent() checks max duration (which we are supposed to calculate here)
if (maxDuration != -1 && modOwner)
modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, maxDuration);
modOwner->ApplySpellMod<SPELLMOD_DURATION>(GetId(), maxDuration);
return maxDuration;
}
@@ -885,7 +885,7 @@ void Aura::SetDuration(int32 duration, bool withMods)
{
if (Unit* caster = GetCaster())
if (Player* modOwner = caster->GetSpellModOwner())
modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, duration);
modOwner->ApplySpellMod<SPELLMOD_DURATION>(GetId(), duration);
}
m_duration = duration;
SetNeedClientUpdateForTargets();
@@ -976,11 +976,11 @@ uint8 Aura::CalcMaxCharges(Unit* caster) const
{
uint32 maxProcCharges = m_spellInfo->ProcCharges;
if (SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()))
maxProcCharges = procEntry->charges;
maxProcCharges = procEntry->Charges;
if (caster)
if (Player* modOwner = caster->GetSpellModOwner())
modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, maxProcCharges);
modOwner->ApplySpellMod<SPELLMOD_CHARGES>(GetId(), maxProcCharges);
return maxProcCharges;
}
@@ -1062,12 +1062,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);
@@ -1202,7 +1196,7 @@ int32 Aura::CalcDispelChance(Unit* auraTarget, bool offensive) const
// Apply dispel mod from aura caster
if (Unit* caster = GetCaster())
if (Player* modOwner = caster->GetSpellModOwner())
modOwner->ApplySpellMod(GetId(), SPELLMOD_RESIST_DISPEL_CHANCE, resistChance);
modOwner->ApplySpellMod<SPELLMOD_RESIST_DISPEL_CHANCE>(GetId(), resistChance);
// Dispel resistance from target SPELL_AURA_MOD_DISPEL_RESIST
// Only affects offensive dispels
@@ -1260,7 +1254,7 @@ void Aura::HandleAllEffects(AuraApplication* aurApp, uint8 mode, bool apply)
m_effects[i]->HandleEffect(aurApp, mode, apply);
}
void Aura::GetApplicationList(std::list<AuraApplication*>& applicationList) const
void Aura::GetApplicationList(Unit::AuraApplicationList& applicationList) const
{
for (Aura::ApplicationMap::const_iterator appIter = m_applications.begin(); appIter != m_applications.end(); ++appIter)
{
@@ -1439,6 +1433,9 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
}
break;
case 44544: // Fingers of Frost
// Refresh or add visual aura
target->CastCustomSpell(74396, SPELLVALUE_AURA_STACK, sSpellMgr->AssertSpellInfo(74396)->StackAmount, (Unit*)nullptr, true);
break;
{
// See if we already have the indicator aura. If not, create one.
if (Aura* aur = target->GetAura(74396))
@@ -1663,10 +1660,6 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
target->CastSpell(target, 32612, true, nullptr, GetEffect(1));
target->CombatStop();
break;
case 74396: // Fingers of Frost
// Remove the IGNORE_AURASTATE aura
target->RemoveAurasDueToSpell(44544);
break;
case 44401: // Missile Barrage
case 48108: // Hot Streak
case 57761: // Fireball!
@@ -2192,22 +2185,17 @@ bool Aura::CanStackWith(Aura const* existingAura, bool remove) const
return true;
}
bool Aura::IsProcOnCooldown() const
bool Aura::IsProcOnCooldown(std::chrono::steady_clock::time_point now) const
{
/*if (m_procCooldown)
{
if (m_procCooldown > GameTime::GetGameTime().count())
return true;
}*/
return false;
return m_procCooldown > now;
}
void Aura::AddProcCooldown(uint32 /*msec*/)
void Aura::AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd)
{
//m_procCooldown = GameTime::GetGameTime().count() + msec;
m_procCooldown = cooldownEnd;
}
void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo)
void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now)
{
bool prepare = CallScriptPrepareProcHandlers(aurApp, eventInfo);
if (!prepare)
@@ -2225,47 +2213,101 @@ 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, std::chrono::steady_clock::time_point 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->IsTriggeredByAura(m_spellInfo))
return 0;
// check if aura can proc when spell is triggered (exception for hunter auto shot & wands)
if (spell->IsTriggered() && !(procEntry->AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC) && !(eventInfo.GetTypeMask() & AUTO_ATTACK_PROC_FLAG_MASK))
if (!GetSpellInfo()->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED))
return 0;
}
// check don't break stealth attr present
if (m_spellInfo->HasAura(SPELL_AURA_MOD_STEALTH))
{
if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo())
if (spellInfo->HasAttribute(SPELL_ATTR0_CU_DONT_BREAK_STEALTH))
return 0;
}
// check if we have charges to proc with
if (IsUsingCharges() && !GetCharges())
return false;
if (IsUsingCharges())
{
if (!GetCharges())
return 0;
if (procEntry->AttributesMask & PROC_ATTR_REQ_SPELLMOD)
{
if (Spell const* spell = eventInfo.GetProcSpell())
{
if (!spell->m_appliedMods.count(const_cast<Aura*>(this)))
{
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;
if (!SpellMgr::CanSpellTriggerProcOnEvent(*procEntry, eventInfo))
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;
return 0;
// 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 (procEffectMask & (1 << i))
if ((procEntry->AttributesMask & (PROC_ATTR_DISABLE_EFF_0 << i)) || !GetEffect(i)->CheckEffectProc(aurApp, eventInfo))
procEffectMask &= ~(1 << i);
}
if (!procEffectMask)
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)
// Aura added by spoell can't trigger from self (prevent drop charges/do triggers)
// But except periodic and kill triggers (can triggered from self)
if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo())
{
if (spellInfo->Id == GetId() && !(eventInfo.GetTypeMask() & (PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL)))
{
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
@@ -2278,7 +2320,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())
{
@@ -2309,39 +2351,50 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI
}
}
return roll_chance_f(CalcProcChance(*procEntry, eventInfo));
if (roll_chance_f(CalcProcChance(*procEntry, eventInfo)))
return procEffectMask;
return 0;
}
float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const
{
float chance = procEntry.chance;
float chance = procEntry.Chance;
// calculate chances depending on unit with caster's data
// 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 (eventInfo.GetDamageInfo() && procEntry.ratePerMinute != 0)
if (eventInfo.GetDamageInfo() && procEntry.ProcsPerMinute != 0)
{
uint32 WeaponSpeed = caster->GetAttackTime(eventInfo.GetDamageInfo()->GetAttackType());
chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ratePerMinute, GetSpellInfo());
chance = caster->GetPPMProcChance(WeaponSpeed, 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);
modOwner->ApplySpellMod<SPELLMOD_CHANCE_OF_SUCCESS>(GetId(), 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);
}
// Remove aura if we've used last charge to proc
if (IsUsingCharges() && !GetCharges())
@@ -2637,30 +2690,14 @@ void Aura::CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication con
bool Aura::CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo)
{
bool result = true;
for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr)
for (auto & m_loadedScript : m_loadedScripts)
{
(*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_PROC, aurApp);
std::list<AuraScript::CheckProcHandler>::iterator hookItrEnd = (*scritr)->DoCheckProc.end(), hookItr = (*scritr)->DoCheckProc.begin();
m_loadedScript->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_PROC, aurApp);
auto hookItrEnd = m_loadedScript->DoCheckProc.end(), hookItr = m_loadedScript->DoCheckProc.begin();
for (; hookItr != hookItrEnd; ++hookItr)
result &= hookItr->Call(*scritr, eventInfo);
result &= hookItr->Call(m_loadedScript, eventInfo);
(*scritr)->_FinishScriptCall();
}
return result;
}
bool Aura::CallScriptCheckAfterProcHandlers(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_AFTER_PROC, aurApp);
std::list<AuraScript::CheckProcHandler>::iterator hookItrEnd = (*scritr)->DoCheckAfterProc.end(), hookItr = (*scritr)->DoCheckAfterProc.begin();
for (; hookItr != hookItrEnd; ++hookItr)
result &= hookItr->Call(*scritr, eventInfo);
(*scritr)->_FinishScriptCall();
m_loadedScript->_FinishScriptCall();
}
return result;
@@ -2715,6 +2752,26 @@ void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventI
}
}
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 hookItrEnd = (*scritr)->DoCheckEffectProc.end(), hookItr = (*scritr)->DoCheckEffectProc.begin();
for (; hookItr != hookItrEnd; ++hookItr)
{
if (hookItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex()))
result &= hookItr->Call(*scritr, aurEff, eventInfo);
}
(*scritr)->_FinishScriptCall();
}
return result;
}
bool Aura::CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo)
{
bool preventDefault = false;

View File

@@ -191,18 +191,14 @@ public:
bool CanStackWith(Aura const* checkAura, bool remove) const;
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(uint32 msec);
bool IsProcOnCooldown(std::chrono::steady_clock::time_point now) const;
void AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd);
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, std::chrono::steady_clock::time_point now);
uint8 GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const;
float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const;
void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo);
void TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo);
// AuraScript
void LoadScripts();
@@ -226,7 +222,7 @@ public:
// Spell Proc Hooks
bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
bool CallScriptCheckAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
bool CallScriptCheckEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo);
bool CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
bool CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
void CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo);
@@ -269,6 +265,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;
std::chrono::steady_clock::time_point m_procCooldown;
private:
Unit::AuraApplicationList m_removedApplications;

View File

@@ -640,7 +640,7 @@ Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags,
m_healing = 0;
m_procAttacker = 0;
m_procVictim = 0;
m_procEx = 0;
m_hitMask = 0;
focusObject = nullptr;
m_cast_count = 0;
m_glyphIndex = 0;
@@ -1811,7 +1811,7 @@ void Spell::SelectImplicitChainTargets(SpellEffIndex effIndex, SpellImplicitTarg
{
uint32 maxTargets = m_spellInfo->Effects[effIndex].ChainTarget;
if (Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, maxTargets, this);
modOwner->ApplySpellMod<SPELLMOD_JUMP_TARGETS>(m_spellInfo->Id, maxTargets, this);
if (maxTargets > 1)
{
@@ -2273,12 +2273,11 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar
}
}
void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/)
void Spell::prepareDataForTriggerSystem()
{
//==========================================================================================
// Now fill data for trigger system, need know:
// can spell trigger another or not (m_canTrigger)
// Create base triggers flags for Attacker and Victim (m_procAttacker, m_procVictim and m_procEx)
// Create base triggers flags for Attacker and Victim (m_procAttacker, m_procVictim and m_hitMask)
//==========================================================================================
m_procVictim = m_procAttacker = 0;
@@ -2317,7 +2316,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/)
// For other spells trigger procflags are set in Spell::DoAllEffectOnTarget
// Because spell positivity is dependant on target
}
m_procEx = PROC_EX_NONE;
m_hitMask = PROC_HIT_NONE;
// Hunter trap spells - activation proc for Lock and Load, Entrapment and Misdirection
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER &&
@@ -2326,10 +2325,11 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/)
m_spellInfo->SpellFamilyFlags[2] & 0x00064000)) // Explosive and Immolation Trap
{
m_procAttacker |= PROC_FLAG_DONE_TRAP_ACTIVATION;
}
/* Effects which are result of aura proc from triggered spell cannot proc
to prevent chain proc of these spells */
// also fill up other flags (DoAllEffectOnTarget only fills up flag if both are not set)
m_procAttacker |= PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG;
m_procVictim |= PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG;
}
// Hellfire Effect - trigger as DOT
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags[0] & 0x00000040)
@@ -2337,20 +2337,6 @@ void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/)
m_procAttacker = PROC_FLAG_DONE_PERIODIC;
m_procVictim = PROC_FLAG_TAKEN_PERIODIC;
}
// Ranged autorepeat attack is set as triggered spell - ignore it
if (!(m_procAttacker & PROC_FLAG_DONE_RANGED_AUTO_ATTACK))
{
if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS &&
(m_spellInfo->HasAttribute(SPELL_ATTR2_ACTIVE_THREAT) ||
m_spellInfo->HasAttribute(SPELL_ATTR3_NOT_A_PROC)))
m_procEx |= PROC_EX_INTERNAL_CANT_PROC;
else if (_triggeredCastFlags & TRIGGERED_DISALLOW_PROC_EVENTS)
m_procEx |= PROC_EX_INTERNAL_TRIGGERED;
}
// Totem casts require spellfamilymask defined in spell_proc_event to proc
if (m_originalCaster && m_caster != m_originalCaster && m_caster->GetTypeId() == TYPEID_UNIT && m_caster->ToCreature()->IsTotem() && m_caster->IsControlledByPlayer())
m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY;
}
void Spell::CleanupTargetList()
@@ -2362,6 +2348,32 @@ void Spell::CleanupTargetList()
m_delayTrajectory = 0;
}
class ProcReflectDelayed : public BasicEvent
{
public:
ProcReflectDelayed(Unit* owner, ObjectGuid casterGuid) : _victim(owner), _casterGuid(casterGuid) { }
bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override
{
Unit* caster = ObjectAccessor::GetUnit(*_victim, _casterGuid);
if (!caster)
return true;
uint32 const typeMaskActor = PROC_FLAG_NONE;
uint32 const typeMaskActionTarget = PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG;
uint32 const spellTypeMask = PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL;
uint32 const spellPhaseMask = PROC_SPELL_PHASE_NONE;
uint32 const hitMask = PROC_HIT_REFLECT;
caster->ProcSkillsAndAuras(_victim, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, nullptr, nullptr, nullptr);
return true;
}
private:
Unit* _victim;
ObjectGuid _casterGuid;
};
void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= true*/, bool implicit /*= true*/)
{
for (uint32 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
@@ -2373,11 +2385,8 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*=
return;
if (checkIfValid)
{
SpellCastResult res = m_spellInfo->CheckTarget(m_caster, target, implicit);
if (res != SPELL_CAST_OK)
if (m_spellInfo->CheckTarget(m_caster, target, implicit) != SPELL_CAST_OK) // skip stealth checks for AOE
return;
}
// Check for effect immune skip if immuned
for (uint32 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
@@ -2448,23 +2457,22 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*=
targetInfo.timeDelay = (uint64) floor(dist / m_spellInfo->Speed * 1000.0f);
// Calculate minimum incoming time
if (m_delayMoment == 0 || m_delayMoment > targetInfo.timeDelay)
if (!m_delayMoment || m_delayMoment > targetInfo.timeDelay)
m_delayMoment = targetInfo.timeDelay;
}
else
targetInfo.timeDelay = 0LL;
targetInfo.timeDelay = 0ULL;
// If target reflect spell back to caster
if (targetInfo.missCondition == SPELL_MISS_REFLECT)
{
// Calculate reflected spell result on caster
targetInfo.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect);
targetInfo.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, false); // can't reflect twice
if (targetInfo.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell
targetInfo.reflectResult = SPELL_MISS_PARRY;
// Proc spell reflect aura when missile hits the original target
target->m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target->m_Events.CalculateTime(targetInfo.timeDelay));
// Increase time interval for reflected spells by 1.5
m_caster->m_Events.AddEvent(new ReflectEvent(m_caster, targetInfo.targetGUID, m_spellInfo), m_caster->m_Events.CalculateTime(targetInfo.timeDelay));
targetInfo.timeDelay += targetInfo.timeDelay >> 1;
m_spellFlags |= SPELL_FLAG_REFLECTED;
@@ -2545,7 +2553,7 @@ void Spell::AddGOTarget(GameObject* go, uint32 effectMask)
if (dist < 5.0f)
dist = 5.0f;
target.timeDelay = uint64(floor(dist / m_spellInfo->Speed * 1000.0f));
if (m_delayMoment == 0 || m_delayMoment > target.timeDelay)
if (!m_delayMoment || m_delayMoment > target.timeDelay)
m_delayMoment = target.timeDelay;
}
else
@@ -2660,8 +2668,8 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
PrepareScriptHitHandlers();
CallScriptBeforeHitHandlers(missInfo);
//Spells with this flag cannot trigger if effect is casted on self
bool canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_SUPRESS_CASTER_PROCS) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE2);
// Spells with this flag cannot trigger if effect is casted on self
bool const canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_SUPRESS_CASTER_PROCS) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE2);
bool reflectedSpell = missInfo == SPELL_MISS_REFLECT;
Unit* spellHitTarget = nullptr;
@@ -2699,18 +2707,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
if (missInfo != SPELL_MISS_NONE && m_needComboPoints && m_targets.GetUnitTargetGUID() == target->targetGUID)
{
m_needComboPoints = false;
// Restore spell mods for a miss/dodge/parry Cold Blood
// TODO: check how broad this rule should be
if (m_caster->GetTypeId() == TYPEID_PLAYER && (missInfo == SPELL_MISS_MISS || missInfo == SPELL_MISS_DODGE || missInfo == SPELL_MISS_PARRY))
m_caster->ToPlayer()->RestoreSpellMods(this, 14177);
}
// Fill base trigger info
uint32 procAttacker = m_procAttacker;
uint32 procVictim = m_procVictim;
uint32 procEx = m_procEx;
uint32 hitMask = m_hitMask;
// Trigger info was not filled in spell::preparedatafortriggersystem - we do it now
// Trigger info was not filled in Spell::prepareDataForTriggerSystem - we do it now
if (canEffectTrigger && !procAttacker && !procVictim)
{
bool positive = true;
@@ -2766,11 +2770,11 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
if (crit)
{
procEx |= PROC_EX_CRITICAL_HIT;
hitMask |= PROC_HIT_CRITICAL;
addhealth = Unit::SpellCriticalHealingBonus(caster, m_spellInfo, addhealth, nullptr);
}
else
procEx |= PROC_EX_NORMAL_HIT;
hitMask |= PROC_HIT_NORMAL;
HealInfo healInfo(caster, unitTarget, addhealth, m_spellInfo, m_spellInfo->GetSchoolMask());
@@ -2778,27 +2782,20 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
if (GetSpellValue()->ForcedCritResult)
{
crit = true;
procEx |= PROC_EX_CRITICAL_HIT;
hitMask |= PROC_HIT_CRITICAL;
}
int32 gain = caster->HealBySpell(healInfo, crit);
unitTarget->getHostileRefMgr().threatAssist(caster, float(gain) * 0.5f, m_spellInfo);
m_healing = gain;
// Xinef: if heal acutally healed something, add no overheal flag
if (m_healing)
procEx |= PROC_EX_NO_OVERHEAL;
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
// Do triggers for unit
if (canEffectTrigger)
Unit::ProcDamageAndSpell(caster, unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
m_triggeredByAuraSpell.effectIndex, this, nullptr, &healInfo);
caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_HIT, hitMask, this, nullptr, &healInfo);
}
// Do damage and triggers
else if (m_damage > 0)
{
caster->SetLastDamagedTargetGuid(unitTarget->GetGUID());
// Fill base damage struct (unitTarget - is real spell target)
SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo, m_spellSchoolMask);
@@ -2858,7 +2855,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
if (reflectedSpell)
effectUnit->SendSpellNonMeleeReflectLog(&damageInfo, effectUnit);
procEx |= createProcExtendMask(&damageInfo, missInfo);
hitMask |= createProcHitMask(&damageInfo, missInfo);
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
caster->DealSpellDamage(&damageInfo, true, this);
@@ -2866,16 +2863,15 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
// do procs after damage, eg healing effects
// no need to check if target is alive, done in procdamageandspell
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
// Do triggers for unit
if (canEffectTrigger)
{
DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE);
Unit::ProcDamageAndSpell(caster, unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
m_triggeredByAuraSpell.effectIndex, this, &dmgInfo);
DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, hitMask);
caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, hitMask, this, &dmgInfo, nullptr);
if (caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 &&
m_spellInfo->HasAttribute(SPELL_ATTR4_SUPRESS_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(dmgInfo);
}
m_damage = damageInfo.damage;
@@ -2885,19 +2881,16 @@ 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)
hitMask |= createProcHitMask(&damageInfo, missInfo);
// Do triggers for unit
if (canEffectTrigger)
{
DamageInfo dmgInfo(damageInfo, NODAMAGE);
Unit::ProcDamageAndSpell(caster, unitTarget, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
m_triggeredByAuraSpell.effectIndex, this, &dmgInfo);
DamageInfo spellNoDamageInfo(damageInfo, NODAMAGE, m_attackType, hitMask);
caster->ProcSkillsAndAuras(unitTarget, procAttacker, procVictim, PROC_SPELL_TYPE_NO_DMG_HEAL, PROC_SPELL_PHASE_HIT, hitMask, this, &spellNoDamageInfo, nullptr);
// 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->GetTypeId() == TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 &&
m_spellInfo->HasAttribute(SPELL_ATTR4_SUPRESS_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);
if (caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) &&
!m_spellInfo->HasAttribute(SPELL_ATTR4_SUPRESS_WEAPON_PROCS) && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED))
caster->ToPlayer()->CastItemCombatSpell(spellNoDamageInfo);
}
// Failed Pickpocket, reveal rogue
@@ -3121,10 +3114,6 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
m_spellAura = Aura::TryRefreshStackOrCreate(aurSpellInfo, effectMask, unit, m_originalCaster,
(aurSpellInfo == m_spellInfo) ? &m_spellValue->EffectBasePoints[0] : &basePoints[0], m_CastItem, ObjectGuid::Empty, &refresh, refreshPeriodic);
// xinef: if aura was not refreshed, add proc ex
if (!refresh)
m_procEx |= PROC_EX_NO_AURA_REFRESH;
if (m_spellAura)
{
// Set aura stack amount to desired value
@@ -3253,8 +3242,8 @@ void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask)
// info confirmed with retail sniffs of permafrost and shadow weaving
if (!m_hitTriggerSpells.empty())
{
int _duration = 0;
for (HitTriggerSpellList::const_iterator i = m_hitTriggerSpells.begin(); i != m_hitTriggerSpells.end(); ++i)
int32 _duration = 0;
for (auto i = m_hitTriggerSpells.begin(); i != m_hitTriggerSpells.end(); ++i)
{
if (CanExecuteTriggersOnHit(effMask, i->triggeredByAura) && roll_chance_i(i->chance))
{
@@ -3380,7 +3369,7 @@ bool Spell::UpdateChanneledTargetList()
}
if (Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this);
modOwner->ApplySpellMod<SPELLMOD_RANGE>(m_spellInfo->Id, range, this);
// xinef: add little tolerance level
range += std::min(3.0f, range * 0.1f); // 10% but no more than 3yd
@@ -3543,7 +3532,7 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const
}
// Prepare data for triggers
prepareDataForTriggerSystem(triggeredByAura);
prepareDataForTriggerSystem();
// calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail)
m_casttime = (_triggeredCastFlags & TRIGGERED_CAST_DIRECTLY) ? 0 : m_spellInfo->CalcCastTime(m_caster, this);
@@ -3727,10 +3716,6 @@ void Spell::cancel(bool bySelf)
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->NeedSendSpectatorData())
ArenaSpectator::SendCommand_Spell(m_caster->FindMap(), m_caster->GetGUID(), "SPE", m_spellInfo->Id, bySelf ? 99998 : 99999);
// spell is canceled-take mods and clear list
if (Player* player = m_caster->GetSpellModOwner())
player->RemoveSpellMods(this);
m_appliedMods.clear();
break;
default:
@@ -3955,26 +3940,14 @@ void Spell::_cast(bool skipCheck)
}
}
uint32 procEx = PROC_EX_NORMAL_HIT;
uint32 hitMask = m_hitMask;
for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
if (!(hitMask & PROC_HIT_CRITICAL))
{
if (ihit->missCondition != SPELL_MISS_NONE)
{
continue;
}
if (!ihit->crit)
{
continue;
}
procEx |= PROC_EX_CRITICAL_HIT;
break;
hitMask |= PROC_HIT_NORMAL;
}
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);
m_originalCaster->ProcSkillsAndAuras(m_originalCaster, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_CAST, hitMask, this, nullptr, nullptr);
}
if (modOwner)
@@ -3995,11 +3968,6 @@ void Spell::_cast(bool skipCheck)
if (m_caster->HasUnitState(UNIT_STATE_CASTING) && !m_caster->IsNonMeleeSpellCast(false, false, true))
m_caster->ClearUnitState(UNIT_STATE_CASTING);
// remove all applied mods at this point
// dont allow user to use them twice in case spell did not reach current target
if (modOwner)
modOwner->RemoveSpellMods(this);
// Xinef: why do we keep focus after spell is sent to air?
// Xinef: Because of this, in the middle of some animation after setting targetguid to 0 etc
// Xinef: we get focused to it out of nowhere...
@@ -4092,7 +4060,7 @@ void Spell::handle_immediate()
// First mod_duration then haste - see Missile Barrage
// Apply duration mod
if (Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration);
// Apply haste mods
if (m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, m_spellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC))
@@ -4264,10 +4232,8 @@ void Spell::_handle_finish_phase()
m_caster->AddComboPoints(m_comboTarget, m_comboPointGain);
}
if (m_spellInfo->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
{
m_caster->SetLastExtraAttackSpell(m_spellInfo->Id);
}
if (m_caster->m_extraAttacks && GetSpellInfo()->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS))
m_caster->HandleProcExtraAttackFor(m_caster->GetVictim());
if (!IsAutoRepeat() && !IsNextMeleeSwingSpell())
if (m_caster->GetCharmerOrOwnerPlayerOrPlayerItself())
@@ -4296,25 +4262,13 @@ void Spell::_handle_finish_phase()
}
}
uint32 procEx = PROC_EX_NORMAL_HIT;
for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
uint32 hitMask = m_hitMask;
if (!(hitMask & PROC_HIT_CRITICAL))
{
if (ihit->missCondition != SPELL_MISS_NONE)
{
continue;
}
if (!ihit->crit)
{
continue;
}
procEx |= PROC_EX_CRITICAL_HIT;
break;
hitMask |= PROC_HIT_NORMAL;
}
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_FINISH);
m_originalCaster->ProcSkillsAndAuras(m_originalCaster, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_FINISH, hitMask, this, nullptr, nullptr);
}
}
@@ -4504,11 +4458,6 @@ void Spell::finish(bool ok)
if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_triggeredByAuraSpell)
m_caster->ToPlayer()->UpdatePotionCooldown(this);
// Take mods after trigger spell (needed for 14177 to affect 48664)
// mods are taken only on succesfull cast and independantly from targets of the spell
if (Player* player = m_caster->GetSpellModOwner())
player->RemoveSpellMods(this);
// xinef: clear reactive auras states after spell cast
if (m_spellInfo->CasterAuraState == AURA_STATE_DEFENSE || m_spellInfo->CasterAuraState == AURA_STATE_HUNTER_PARRY)
m_caster->ModifyAuraState(AuraStateType(m_spellInfo->CasterAuraState), false);
@@ -5293,7 +5242,7 @@ void Spell::TakePower()
hit = false;
//lower spell cost on fail (by talent aura)
if (Player* modOwner = m_caster->ToPlayer()->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_SPELL_COST_REFUND_ON_FAIL, m_powerCost, this);
modOwner->ApplySpellMod<SPELLMOD_SPELL_COST_REFUND_ON_FAIL>(m_spellInfo->Id, m_powerCost, this);
}
break;
}
@@ -5394,7 +5343,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 RuneCostID)
{
runeCost[i] = src->RuneCost[i];
if (Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this);
modOwner->ApplySpellMod<SPELLMOD_COST>(m_spellInfo->Id, runeCost[i], this);
}
runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later
@@ -5434,7 +5383,7 @@ void Spell::TakeRunePower(bool didHit)
{
runeCost[i] = runeCostData->RuneCost[i];
if (Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, runeCost[i], this);
modOwner->ApplySpellMod<SPELLMOD_COST>(m_spellInfo->Id, runeCost[i], this);
}
runeCost[RUNE_DEATH] = 0; // calculated later
@@ -5831,7 +5780,11 @@ SpellCastResult Spell::CheckCast(bool strict)
// Xinef: do not check explicit target for triggered spell casted on self with targetflag enemy
if (!m_triggeredByAuraSpell || m_targets.GetUnitTarget() != m_caster || !(m_spellInfo->GetExplicitTargetMask() & TARGET_FLAG_UNIT_ENEMY))
{
SpellCastResult castResult = m_spellInfo->CheckExplicitTarget((m_originalCaster && m_caster->GetEntry() != WORLD_TRIGGER) ? m_originalCaster : m_caster, m_targets.GetObjectTarget(), m_targets.GetItemTarget());
Unit* caster = m_caster;
if (m_originalCaster && m_caster->GetEntry() != WORLD_TRIGGER) // Do a simplified check for gameobject casts
caster = m_originalCaster;
SpellCastResult castResult = m_spellInfo->CheckExplicitTarget(caster, m_targets.GetObjectTarget(), m_targets.GetItemTarget());
if (castResult != SPELL_CAST_OK)
return castResult;
}
@@ -5839,7 +5792,7 @@ SpellCastResult Spell::CheckCast(bool strict)
if (Unit* target = m_targets.GetUnitTarget())
{
SpellCastResult castResult = m_spellInfo->CheckTarget(m_caster, target, false);
SpellCastResult castResult = m_spellInfo->CheckTarget(m_caster, target, m_caster->GetEntry() == WORLD_TRIGGER); // skip stealth checks for GO casts
if (castResult != SPELL_CAST_OK)
return castResult;
@@ -7035,7 +6988,7 @@ SpellCastResult Spell::CheckRange(bool strict)
range_type = SPELL_RANGE_RANGED;
if (Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this);
modOwner->ApplySpellMod<SPELLMOD_RANGE>(m_spellInfo->Id, max_range, this);
// xinef: dont check max_range to strictly after cast
if (range_type != SPELL_RANGE_MELEE && !strict)
@@ -7749,7 +7702,7 @@ void Spell::Delayed() // only called in DealDamage()
//check pushback reduce
int32 delaytime = 500; // spellcasting delay is normally 500ms
int32 delayReduce = 100; // must be initialized to 100 for percent modifiers
m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this);
m_caster->ToPlayer()->ApplySpellMod<SPELLMOD_NOT_LOSE_CASTING_TIME>(m_spellInfo->Id, delayReduce, this);
delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100;
if (delayReduce >= 100)
return;
@@ -7787,7 +7740,7 @@ void Spell::DelayedChannel()
int32 delaytime = CalculatePct(duration, 25); // channeling delay is normally 25% of its time per hit
int32 delayReduce = 100; // must be initialized to 100 for percent modifiers
m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_NOT_LOSE_CASTING_TIME, delayReduce, this);
m_caster->ToPlayer()->ApplySpellMod<SPELLMOD_NOT_LOSE_CASTING_TIME>(m_spellInfo->Id, delayReduce, this);
delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100;
if (delayReduce >= 100)
return;
@@ -8167,14 +8120,6 @@ bool SpellEvent::IsDeletable() const
return m_Spell->IsDeletable();
}
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);
return true;
}
bool Spell::IsValidDeadOrAliveTarget(Unit const* target) const
{
if (target->IsAlive())
@@ -8750,26 +8695,24 @@ void Spell::PrepareTriggersExecutedOnHit()
// save auras which were present on spell caster on cast, to prevent triggered auras from affecting caster
// and to correctly calculate proc chance when combopoints are present
Unit::AuraEffectList const& targetTriggers = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER);
for (Unit::AuraEffectList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i)
for (AuraEffect const* aurEff : targetTriggers)
{
if (!(*i)->IsAffectedOnSpell(m_spellInfo))
if (!aurEff->IsAffectedOnSpell(m_spellInfo))
continue;
SpellInfo const* auraSpellInfo = (*i)->GetSpellInfo();
uint32 auraSpellIdx = (*i)->GetEffIndex();
SpellInfo const* auraSpellInfo = aurEff->GetSpellInfo();
uint32 auraSpellIdx = aurEff->GetEffIndex();
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(auraSpellInfo->Effects[auraSpellIdx].TriggerSpell))
{
// calculate the chance using spell base amount, because aura amount is not updated on combo-points change
// this possibly needs fixing
int32 auraBaseAmount = (*i)->GetBaseAmount();
int32 auraBaseAmount = aurEff->GetBaseAmount();
// proc chance is stored in effect amount
int32 chance = m_caster->CalculateSpellDamage(nullptr, auraSpellInfo, auraSpellIdx, &auraBaseAmount);
chance *= aurEff->GetBase()->GetStackAmount();
// build trigger and add to the list
HitTriggerSpell spellTriggerInfo;
spellTriggerInfo.triggeredSpell = spellInfo;
spellTriggerInfo.triggeredByAura = auraSpellInfo;
spellTriggerInfo.triggeredByEffIdx = (*i)->GetEffIndex();
spellTriggerInfo.chance = chance * (*i)->GetBase()->GetStackAmount();
m_hitTriggerSpells.push_back(spellTriggerInfo);
m_hitTriggerSpells.emplace_back(spellInfo, auraSpellInfo, chance);
}
}
}
@@ -8814,8 +8757,8 @@ void Spell::TriggerGlobalCooldown()
if (m_spellInfo->StartRecoveryTime >= MIN_GCD && m_spellInfo->StartRecoveryTime <= MAX_GCD)
{
// gcd modifier auras are applied only to own spells and only players have such mods
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_GLOBAL_COOLDOWN, gcd, this);
if (Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod<SPELLMOD_GLOBAL_COOLDOWN>(m_spellInfo->Id, gcd, this);
// Apply haste rating
if (m_spellInfo->StartRecoveryCategory == 133 && m_spellInfo->StartRecoveryTime == 1500 && m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE &&
@@ -8824,10 +8767,7 @@ void Spell::TriggerGlobalCooldown()
gcd = int32(float(gcd) * m_caster->GetFloatValue(UNIT_MOD_CAST_SPEED));
}
if (gcd < MIN_GCD)
gcd = MIN_GCD;
else if (gcd > MAX_GCD)
gcd = MAX_GCD;
RoundToInterval<int32>(gcd, MIN_GCD, MAX_GCD);
}
// Only players or controlled units have global cooldown

View File

@@ -414,7 +414,7 @@ public:
void EffectCastButtons(SpellEffIndex effIndex);
void EffectRechargeManaGem(SpellEffIndex effIndex);
typedef std::set<Aura*> UsedSpellMods;
typedef std::unordered_set<Aura*> UsedSpellMods;
void InitExplicitTargets(SpellCastTargets const& targets);
void SelectExplicitTargets();
@@ -554,6 +554,8 @@ public:
bool IsAutoActionResetSpell() const;
bool IsIgnoringCooldowns() const;
bool IsTriggeredByAura(SpellInfo const* auraSpellInfo) const { return (auraSpellInfo == m_triggeredByAuraSpell.spellInfo); }
bool IsDeletable() const { return !m_referencedFromCurrentSpell && !m_executedCurrently; }
void SetReferencedFromCurrent(bool yes) { m_referencedFromCurrentSpell = yes; }
bool IsInterruptable() const { return !m_executedCurrently; }
@@ -670,8 +672,8 @@ public:
// ******************************************
uint32 m_procAttacker; // Attacker trigger flags
uint32 m_procVictim; // Victim trigger flags
uint32 m_procEx;
void prepareDataForTriggerSystem(AuraEffect const* triggeredByAura);
uint32 m_hitMask;
void prepareDataForTriggerSystem();
// *****************************************
// Spell target subsystem
@@ -739,6 +741,9 @@ public:
struct HitTriggerSpell
{
HitTriggerSpell(SpellInfo const* spellInfo, SpellInfo const* auraSpellInfo, int32 procChance) :
triggeredSpell(spellInfo), triggeredByAura(auraSpellInfo), chance(procChance) { }
SpellInfo const* triggeredSpell;
SpellInfo const* triggeredByAura;
uint8 triggeredByEffIdx;
@@ -747,7 +752,7 @@ public:
bool CanExecuteTriggersOnHit(uint8 effMask, SpellInfo const* triggeredByAura = nullptr) const;
void PrepareTriggersExecutedOnHit();
typedef std::list<HitTriggerSpell> HitTriggerSpellList;
typedef std::vector<HitTriggerSpell> HitTriggerSpellList;
HitTriggerSpellList m_hitTriggerSpells;
// effect helpers
@@ -838,17 +843,4 @@ namespace Acore
}
typedef void(Spell::*pEffect)(SpellEffIndex effIndex);
class ReflectEvent : public BasicEvent
{
public:
ReflectEvent(Unit* caster, ObjectGuid targetGUID, SpellInfo const* spellInfo) : _caster(caster), _targetGUID(targetGUID), _spellInfo(spellInfo) { }
bool Execute(uint64 e_time, uint32 p_time) override;
protected:
Unit* _caster;
ObjectGuid _targetGUID;
SpellInfo const* _spellInfo;
};
#endif

View File

@@ -72,38 +72,38 @@ enum SpellAuraInterruptFlags
AURA_INTERRUPT_FLAG_NOT_VICTIM = (AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_TAKE_DAMAGE | AURA_INTERRUPT_FLAG_DIRECT_DAMAGE),
};
enum SpellModOp
enum SpellModOp : uint8
{
SPELLMOD_DAMAGE = 0,
SPELLMOD_DURATION = 1,
SPELLMOD_THREAT = 2,
SPELLMOD_EFFECT1 = 3,
SPELLMOD_CHARGES = 4,
SPELLMOD_RANGE = 5,
SPELLMOD_RADIUS = 6,
SPELLMOD_CRITICAL_CHANCE = 7,
SPELLMOD_ALL_EFFECTS = 8,
SPELLMOD_NOT_LOSE_CASTING_TIME = 9,
SPELLMOD_CASTING_TIME = 10,
SPELLMOD_COOLDOWN = 11,
SPELLMOD_EFFECT2 = 12,
SPELLMOD_IGNORE_ARMOR = 13,
SPELLMOD_COST = 14,
SPELLMOD_CRIT_DAMAGE_BONUS = 15,
SPELLMOD_RESIST_MISS_CHANCE = 16,
SPELLMOD_JUMP_TARGETS = 17,
SPELLMOD_CHANCE_OF_SUCCESS = 18,
SPELLMOD_ACTIVATION_TIME = 19,
SPELLMOD_DAMAGE_MULTIPLIER = 20,
SPELLMOD_GLOBAL_COOLDOWN = 21,
SPELLMOD_DOT = 22,
SPELLMOD_EFFECT3 = 23,
SPELLMOD_BONUS_MULTIPLIER = 24,
SPELLMOD_DAMAGE = 0,
SPELLMOD_DURATION = 1,
SPELLMOD_THREAT = 2,
SPELLMOD_EFFECT1 = 3,
SPELLMOD_CHARGES = 4,
SPELLMOD_RANGE = 5,
SPELLMOD_RADIUS = 6,
SPELLMOD_CRITICAL_CHANCE = 7,
SPELLMOD_ALL_EFFECTS = 8,
SPELLMOD_NOT_LOSE_CASTING_TIME = 9,
SPELLMOD_CASTING_TIME = 10,
SPELLMOD_COOLDOWN = 11,
SPELLMOD_EFFECT2 = 12,
SPELLMOD_IGNORE_ARMOR = 13,
SPELLMOD_COST = 14,
SPELLMOD_CRIT_DAMAGE_BONUS = 15,
SPELLMOD_RESIST_MISS_CHANCE = 16,
SPELLMOD_JUMP_TARGETS = 17,
SPELLMOD_CHANCE_OF_SUCCESS = 18,
SPELLMOD_ACTIVATION_TIME = 19,
SPELLMOD_DAMAGE_MULTIPLIER = 20,
SPELLMOD_GLOBAL_COOLDOWN = 21,
SPELLMOD_DOT = 22,
SPELLMOD_EFFECT3 = 23,
SPELLMOD_BONUS_MULTIPLIER = 24,
// spellmod 25
SPELLMOD_PROC_PER_MINUTE = 26,
SPELLMOD_VALUE_MULTIPLIER = 27,
SPELLMOD_RESIST_DISPEL_CHANCE = 28,
SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell
SPELLMOD_PROC_PER_MINUTE = 26,
SPELLMOD_VALUE_MULTIPLIER = 27,
SPELLMOD_RESIST_DISPEL_CHANCE = 28,
SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell
SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30
};
@@ -143,7 +143,7 @@ enum TriggerCastFlags
TRIGGERED_IGNORE_CASTER_AURASTATE = 0x00000800, //! Will ignore caster aura states including combat requirements and death state
TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE = 0x00002000, //! Will ignore mounted/on vehicle restrictions
TRIGGERED_IGNORE_CASTER_AURAS = 0x00010000, //! Will ignore caster aura restrictions or requirements
TRIGGERED_DISALLOW_PROC_EVENTS = 0x00020000, //! Disallows proc events from triggered spell (default)
// reuse 0x00020000
TRIGGERED_DONT_REPORT_CAST_ERROR = 0x00040000, //! Will return SPELL_FAILED_DONT_REPORT in CheckCast functions
TRIGGERED_FULL_MASK = 0x0007FFFF, //! Used when doing CastSpell with triggered == true
TRIGGERED_IGNORE_EQUIPPED_ITEM_REQUIREMENT = 0x00080000, //! Will ignore equipped item requirements

View File

@@ -310,7 +310,7 @@ void Spell::EffectEnvironmentalDMG(SpellEffIndex /*effIndex*/)
unitTarget->ToPlayer()->EnvironmentalDamage(DAMAGE_FIRE, damage);
else
{
DamageInfo dmgInfo(m_caster, unitTarget, damage, m_spellInfo, m_spellInfo->GetSchoolMask(), SPELL_DIRECT_DAMAGE);
DamageInfo dmgInfo(m_caster, unitTarget, damage, m_spellInfo, m_spellInfo->GetSchoolMask(), SPELL_DIRECT_DAMAGE, BASE_ATTACK);
uint32 absorb = dmgInfo.GetAbsorb();
uint32 resist = dmgInfo.GetResist();
@@ -2342,7 +2342,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex)
int32 duration = m_spellInfo->GetDuration();
if (Player* modOwner = m_originalCaster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration);
TempSummon* summon = nullptr;
@@ -3148,7 +3148,7 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex)
int32 duration = m_spellInfo->GetDuration();
if(Player* modOwner = m_originalCaster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration);
Player* owner = m_originalCaster->ToPlayer();
if (!owner && m_originalCaster->ToCreature()->IsTotem())
@@ -3722,6 +3722,7 @@ void Spell::EffectInterruptCast(SpellEffIndex effIndex)
{
int32 duration = m_originalCaster->ModSpellDuration(m_spellInfo, unitTarget, m_originalCaster->CalcSpellDuration(m_spellInfo), false, 1 << effIndex);
unitTarget->ProhibitSpellSchool(curSpellInfo->GetSchoolMask(), duration/*spellInfo->GetDuration()*/);
m_originalCaster->ProcSkillsAndAuras(unitTarget, PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_HIT, PROC_HIT_INTERRUPT, nullptr, nullptr, nullptr);
}
ExecuteLogEffectInterruptCast(effIndex, unitTarget, curSpellInfo->Id);
unitTarget->InterruptSpell(CurrentSpellTypes(i), false);
@@ -4682,18 +4683,17 @@ void Spell::EffectResurrect(SpellEffIndex effIndex)
void Spell::EffectAddExtraAttacks(SpellEffIndex effIndex)
{
if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)
{
return;
}
if (!unitTarget || !unitTarget->IsAlive())
{
if (!unitTarget || !unitTarget->IsAlive() || !unitTarget->GetVictim())
return;
}
unitTarget->AddExtraAttacks(damage);
if (unitTarget->m_extraAttacks)
return;
ExecuteLogEffectExtraAttacks(effIndex, unitTarget, damage);
unitTarget->m_extraAttacks = damage;
ExecuteLogEffectExtraAttacks(effIndex, unitTarget->GetVictim(), damage);
}
void Spell::EffectParry(SpellEffIndex /*effIndex*/)
@@ -5816,19 +5816,8 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex)
m_runesState = m_caster->ToPlayer()->GetRunesState();
uint32 count = damage;
if (count == 0) count = 1;
for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j)
{
if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue))
{
if (m_spellInfo->Id == 45529)
if (player->GetBaseRune(j) != RuneType(m_spellInfo->Effects[effIndex].MiscValueB))
continue;
player->SetRuneCooldown(j, 0);
player->SetGracePeriod(j, player->IsInCombat()); // xinef: reset grace period
--count;
}
}
if (count == 0)
count = 1;
// Blood Tap
if (m_spellInfo->Id == 45529 && count > 0)
@@ -5836,10 +5825,10 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex)
for (uint32 l = 0; l < MAX_RUNES && count > 0; ++l)
{
// Check if both runes are on cd as that is the only time when this needs to come into effect
if ((player->GetRuneCooldown(l) && player->GetCurrentRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l + 1) && player->GetCurrentRune(l + 1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)))
if ((player->GetRuneCooldown(l) && player->GetCurrentRune(l) == RUNE_BLOOD) && (player->GetRuneCooldown(l + 1) && player->GetCurrentRune(l + 1) == RUNE_BLOOD))
{
// Should always update the rune with the lowest cd
if (player->GetRuneCooldown(l) >= player->GetRuneCooldown(l + 1))
if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l + 1))
l++;
player->SetRuneCooldown(l, 0);
player->SetGracePeriod(l, player->IsInCombat()); // xinef: reset grace period
@@ -5850,6 +5839,15 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex)
}
}
for (uint32 j = 0; j < MAX_RUNES && count > 0; ++j)
{
if (player->GetRuneCooldown(j) && player->GetCurrentRune(j) == RuneType(m_spellInfo->Effects[effIndex].MiscValue))
{
player->SetRuneCooldown(j, 0);
--count;
}
}
// Empower rune weapon
if (m_spellInfo->Id == 47568)
{
@@ -5859,7 +5857,7 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex)
for (uint32 i = 0; i < MAX_RUNES; ++i)
{
if (player->GetRuneCooldown(i) && (player->GetCurrentRune(i) == RUNE_FROST || player->GetCurrentRune(i) == RUNE_DEATH))
if (player->GetRuneCooldown(i) && (player->GetCurrentRune(i) == RUNE_FROST))
{
player->SetRuneCooldown(i, 0);
player->SetGracePeriod(i, player->IsInCombat()); // xinef: reset grace period
@@ -6061,7 +6059,7 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const*
int32 duration = m_spellInfo->GetDuration();
if (Player* modOwner = m_originalCaster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
modOwner->ApplySpellMod<SPELLMOD_DURATION>(m_spellInfo->Id, duration);
//TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
Map* map = caster->GetMap();

View File

@@ -524,7 +524,7 @@ float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const
{
float multiplier = ValueMultiplier;
if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr))
modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_VALUE_MULTIPLIER, multiplier, spell);
modOwner->ApplySpellMod<SPELLMOD_VALUE_MULTIPLIER>(_spellInfo->Id, multiplier, spell);
return multiplier;
}
@@ -532,7 +532,7 @@ float SpellEffectInfo::CalcDamageMultiplier(Unit* caster, Spell* spell) const
{
float multiplier = DamageMultiplier;
if (Player* modOwner = (caster ? caster->GetSpellModOwner() : nullptr))
modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplier, spell);
modOwner->ApplySpellMod<SPELLMOD_DAMAGE_MULTIPLIER>(_spellInfo->Id, multiplier, spell);
return multiplier;
}
@@ -552,7 +552,7 @@ float SpellEffectInfo::CalcRadius(Unit* caster, Spell* spell) const
radius += RadiusEntry->RadiusPerLevel * caster->getLevel();
radius = std::min(radius, RadiusEntry->RadiusMax);
if (Player* modOwner = caster->GetSpellModOwner())
modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_RADIUS, radius, spell);
modOwner->ApplySpellMod<SPELLMOD_RADIUS>(_spellInfo->Id, radius, spell);
}
return radius;
@@ -831,7 +831,7 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry)
SpellVisual = spellEntry->SpellVisual;
SpellIconID = spellEntry->SpellIconID;
ActiveIconID = spellEntry->ActiveIconID;
SpellPriority = spellEntry->SpellPriority;
Priority = spellEntry->SpellPriority;
SpellName = spellEntry->SpellName;
Rank = spellEntry->Rank;
MaxTargetLevel = spellEntry->MaxTargetLevel;
@@ -1268,6 +1268,26 @@ bool SpellInfo::IsAutoRepeatRangedSpell() const
return AttributesEx2 & SPELL_ATTR2_AUTO_REPEAT;
}
bool SpellInfo::IsAffected(uint32 familyName, flag96 const& familyFlags) const
{
if (!familyName)
{
return true;
}
if (familyName != SpellFamilyName)
{
return false;
}
if (familyFlags && !(familyFlags & SpellFamilyFlags))
{
return false;
}
return true;
}
bool SpellInfo::IsAffectedBySpellMods() const
{
return !(AttributesEx3 & SPELL_ATTR3_IGNORE_CASTER_MODIFIERS);
@@ -1292,15 +1312,7 @@ bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const
return true;
}
// False if affect_spell == nullptr or spellFamily not equal
if (affectSpell->SpellFamilyName != SpellFamilyName)
return false;
// true
if (mod->mask & SpellFamilyFlags)
return true;
return false;
return IsAffected(affectSpell->SpellFamilyName, mod->mask);
}
bool SpellInfo::CanPierceImmuneAura(SpellInfo const* aura) const
@@ -2311,7 +2323,7 @@ float SpellInfo::GetMaxRange(bool positive, Unit* caster, Spell* spell) const
range = RangeEntry->RangeMax[0];
if (caster)
if (Player* modOwner = caster->GetSpellModOwner())
modOwner->ApplySpellMod(Id, SPELLMOD_RANGE, range, spell);
modOwner->ApplySpellMod<SPELLMOD_RANGE>(Id, range, spell);
return range;
}
@@ -2446,7 +2458,7 @@ int32 SpellInfo::CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask, S
// Apply cost mod by spell
if (Player* modOwner = caster->GetSpellModOwner())
modOwner->ApplySpellMod(Id, SPELLMOD_COST, powerCost, spell);
modOwner->ApplySpellMod<SPELLMOD_COST>(Id, powerCost, spell);
if (!caster->IsControlledByPlayer())
{

View File

@@ -377,7 +377,7 @@ public:
std::array<uint32, 2> SpellVisual;
uint32 SpellIconID;
uint32 ActiveIconID;
uint32 SpellPriority;
uint32 Priority;
std::array<char const*, 16> SpellName;
std::array<char const*, 16> Rank;
uint32 MaxTargetLevel;
@@ -463,6 +463,8 @@ public:
bool IsRangedWeaponSpell() const;
bool IsAutoRepeatRangedSpell() const;
bool IsAffected(uint32 familyName, flag96 const& familyFlags) const;
bool IsAffectedBySpellMods() const;
bool IsAffectedBySpellMod(SpellModifier const* mod) const;

View File

@@ -171,7 +171,7 @@ void SpellMgr::LoadSpellInfoCorrections()
53232, // Rapid Killing (Rank 2)
}, [](SpellInfo* spellInfo)
{
spellInfo->AttributesEx3 |= SPELL_ATTR3_CAN_PROC_FROM_PROCS; // Entries were not updated after spell effect change, we have to do that manually
spellInfo->AttributesEx3 |= SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED; // Entries were not updated after spell effect change, we have to do that manually
});
ApplySpellFix({
@@ -257,7 +257,7 @@ void SpellMgr::LoadSpellInfoCorrections()
ApplySpellFix({ 57761 }, [](SpellInfo* spellInfo)
{
spellInfo->ProcCharges = 1;
spellInfo->SpellPriority = 50;
spellInfo->Priority = 50;
});
// Tidal Wave
@@ -272,10 +272,10 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx3 |= SPELL_ATTR3_DOT_STACKING_RULE;
});
// Ascendance (Talisman of Ascendance trinket)
ApplySpellFix({ 28200 }, [](SpellInfo* spellInfo)
// Death and Decay
ApplySpellFix({ 52212 }, [](SpellInfo* spellInfo)
{
spellInfo->ProcCharges = 6;
spellInfo->AttributesEx6 |= SPELL_ATTR6_IGNORE_PHASE_SHIFT;
});
// The Eye of Acherus (no spawn in phase 2 in db)

View File

@@ -727,166 +727,6 @@ void SpellMgr::GetSetOfSpellsInSpellGroupWithFlag(uint32 group_id, SpellGroupSpe
availableElixirs.insert(itr->first); // insert spell id
}
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);
@@ -895,54 +735,78 @@ SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const
return nullptr;
}
bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const
bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo)
{
// proc type doesn't match
if (!(eventInfo.GetTypeMask() & procEntry.typeMask))
if (!(eventInfo.GetTypeMask() & procEntry.ProcFlags))
return false;
// check XP or honor target requirement
if (procEntry.attributesMask & PROC_ATTR_REQ_EXP_OR_HONOR)
if (procEntry.AttributesMask & PROC_ATTR_REQ_EXP_OR_HONOR)
if (Player* actor = eventInfo.GetActor()->ToPlayer())
if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget()))
return false;
// check mana requirement
if (procEntry.AttributesMask & PROC_ATTR_REQ_MANA_COST)
if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo())
if (!eventSpellInfo->ManaCost && !eventSpellInfo->ManaCostPercentage)
return false;
// always trigger for these types
if (eventInfo.GetTypeMask() & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH))
return true;
// do triggered cast checks
// Do not consider autoattacks as triggered spells
if (!(procEntry.AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC) && !(eventInfo.GetTypeMask() & AUTO_ATTACK_PROC_FLAG_MASK))
{
if (Spell const* spell = eventInfo.GetProcSpell())
{
if (spell->IsTriggered())
{
SpellInfo const* spellInfo = spell->GetSpellInfo();
if (!spellInfo->HasAttribute(SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2) &&
!spellInfo->HasAttribute(SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC))
return false;
}
}
}
// check school mask (if set) for other trigger types
if (procEntry.schoolMask && !(eventInfo.GetSchoolMask() & procEntry.schoolMask))
if (procEntry.SchoolMask && !(eventInfo.GetSchoolMask() & procEntry.SchoolMask))
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() & (PERIODIC_PROC_FLAG_MASK | SPELL_PROC_FLAG_MASK))
{
if (procEntry.spellFamilyName && (procEntry.spellFamilyName != eventInfo.GetSpellInfo()->SpellFamilyName))
return false;
if (procEntry.spellFamilyMask && !(procEntry.spellFamilyMask & eventInfo.GetSpellInfo()->SpellFamilyFlags))
return false;
if (SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo())
{
if (!eventSpellInfo->IsAffected(procEntry.SpellFamilyName, procEntry.SpellFamilyMask))
{
return false;
}
}
}
// check spell type mask (if set)
if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))
{
if (procEntry.spellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.spellTypeMask))
if (procEntry.SpellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.SpellTypeMask))
return false;
}
// check spell phase mask
if (eventInfo.GetTypeMask() & REQ_SPELL_PHASE_PROC_FLAG_MASK)
{
if (!(eventInfo.GetSpellPhaseMask() & procEntry.spellPhaseMask))
if (!(eventInfo.GetSpellPhaseMask() & procEntry.SpellPhaseMask))
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)))
{
uint32 hitMask = procEntry.hitMask;
uint32 hitMask = procEntry.HitMask;
// get default values if hit mask not set
if (!hitMask)
{
@@ -1733,111 +1597,17 @@ void SpellMgr::LoadSpellGroupStackRules()
LOG_INFO("server.loading", " ");
}
void SpellMgr::LoadSpellProcEvents()
{
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)
{
LOG_WARN("server.loading", ">> Loaded 0 spell proc event conditions. DB table `spell_proc_event` is empty.");
return;
}
uint32 count = 0;
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", " ");
}
void SpellMgr::LoadSpellProcs()
{
uint32 oldMSTime = getMSTime();
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, typeMask, spellTypeMask, spellPhaseMask, hitMask, attributesMask, ratePerMinute, chance, cooldown, charges FROM spell_proc");
// 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");
if (!result)
{
LOG_WARN("server.loading", ">> Loaded 0 Spell Proc Conditions And Data. DB table `spell_proc` Is Empty.");
LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty.");
LOG_INFO("server.loading", " ");
return;
}
@@ -1874,21 +1644,20 @@ void SpellMgr::LoadSpellProcs()
SpellProcEntry baseProcEntry;
baseProcEntry.schoolMask = fields[1].Get<int8>();
baseProcEntry.spellFamilyName = fields[2].Get<uint16>();
baseProcEntry.spellFamilyMask[0] = fields[3].Get<uint32>();
baseProcEntry.spellFamilyMask[1] = fields[4].Get<uint32>();
baseProcEntry.spellFamilyMask[2] = fields[5].Get<uint32>();
baseProcEntry.typeMask = fields[6].Get<uint32>();
baseProcEntry.spellTypeMask = fields[7].Get<uint32>();
baseProcEntry.spellPhaseMask = fields[8].Get<uint32>();
baseProcEntry.hitMask = fields[9].Get<uint32>();
baseProcEntry.attributesMask = fields[10].Get<uint32>();
baseProcEntry.ratePerMinute = fields[11].Get<float>();
baseProcEntry.chance = fields[12].Get<float>();
float cooldown = fields[13].Get<float>();
baseProcEntry.cooldown = uint32(cooldown);
baseProcEntry.charges = fields[14].Get<uint32>();
baseProcEntry.SchoolMask = fields[1].Get<int8>();
baseProcEntry.SpellFamilyName = fields[2].Get<uint16>();
baseProcEntry.SpellFamilyMask[0] = fields[3].Get<uint32>();
baseProcEntry.SpellFamilyMask[1] = fields[4].Get<uint32>();
baseProcEntry.SpellFamilyMask[2] = fields[5].Get<uint32>();
baseProcEntry.ProcFlags = fields[6].Get<uint32>();
baseProcEntry.SpellTypeMask = fields[7].Get<uint32>();
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<uint8>();
while (spellInfo)
{
@@ -1900,56 +1669,54 @@ void SpellMgr::LoadSpellProcs()
SpellProcEntry procEntry = SpellProcEntry(baseProcEntry);
// take defaults from dbcs
if (!procEntry.typeMask)
procEntry.typeMask = spellInfo->ProcFlags;
if (!procEntry.charges)
procEntry.charges = spellInfo->ProcCharges;
if (!procEntry.chance && !procEntry.ratePerMinute)
procEntry.chance = float(spellInfo->ProcChance);
if (!procEntry.ProcFlags)
procEntry.ProcFlags = spellInfo->ProcFlags;
if (!procEntry.Charges)
procEntry.Charges = spellInfo->ProcCharges;
if (!procEntry.Chance && !procEntry.ProcsPerMinute)
procEntry.Chance = float(spellInfo->ProcChance);
// validate data
if (procEntry.schoolMask & ~SPELL_SCHOOL_MASK_ALL)
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has wrong `schoolMask` set: {}", spellId, procEntry.schoolMask);
if (procEntry.spellFamilyName && (procEntry.spellFamilyName < 3 || procEntry.spellFamilyName > 17 || procEntry.spellFamilyName == 14 || procEntry.spellFamilyName == 16))
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has wrong `spellFamilyName` set: {}", spellId, procEntry.spellFamilyName);
if (procEntry.chance < 0)
if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL)
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `SchoolMask` set: {}", spellId, procEntry.SchoolMask);
if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < 3 || procEntry.SpellFamilyName > 17 || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16))
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `SpellFamilyName` set: {}", spellId, procEntry.SpellFamilyName);
if (procEntry.Chance < 0)
{
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has negative value in `chance` field", spellId);
procEntry.chance = 0;
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has negative value in `Chance` field", spellId);
procEntry.Chance = 0;
}
if (procEntry.ratePerMinute < 0)
if (procEntry.ProcsPerMinute < 0)
{
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has negative value in `ratePerMinute` field", spellId);
procEntry.ratePerMinute = 0;
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has negative value in `ProcsPerMinute` field", spellId);
procEntry.ProcsPerMinute = 0;
}
if (cooldown < 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 negative value in `cooldown` field", spellId);
procEntry.cooldown = 0;
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has too big value in `Charges` field", spellId);
procEntry.Charges = 99;
}
if (procEntry.chance == 0 && procEntry.ratePerMinute == 0)
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} doesn't have `chance` and `ratePerMinute` 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);
procEntry.charges = 99;
}
if (!procEntry.typeMask)
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} doesn't have `typeMask` value defined, proc will not be triggered", spellId);
if (procEntry.spellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL)
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has wrong `spellTypeMask` set: {}", spellId, procEntry.spellTypeMask);
if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has `spellTypeMask` value defined, but it won't be used for defined `typeMask` value", spellId);
if (!procEntry.spellPhaseMask && procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK)
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} doesn't have `spellPhaseMask` value defined, but it's required for defined `typeMask` value, proc will not be triggered", spellId);
if (procEntry.spellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL)
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has wrong `spellPhaseMask` set: {}", spellId, procEntry.spellPhaseMask);
if (procEntry.spellPhaseMask && !(procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK))
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has `spellPhaseMask` value defined, but it won't be used for defined `typeMask` 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.typeMask & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.typeMask & DONE_HIT_PROC_FLAG_MASK && (!procEntry.spellPhaseMask || procEntry.spellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH)))))
LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId {} has `hitMask` value defined, but it won't be used for defined `typeMask` and `spellPhaseMask` values", spellId);
if (!procEntry.ProcFlags)
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} doesn't have `ProcFlags` value defined, proc will not be triggered", spellId);
if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL)
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `SpellTypeMask` set: {}", spellId, procEntry.SpellTypeMask);
if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has `SpellTypeMask` value defined, but it won't be used for defined `ProcFlags` value", spellId);
if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} doesn't have `SpellPhaseMask` value defined, but it's required for defined `ProcFlags` value, proc will not be triggered", spellId);
if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL)
LOG_ERROR("sql.sql", "`spell_proc` table entry for SpellId {} has wrong `SpellPhaseMask` set: {}", spellId, procEntry.SpellPhaseMask);
if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK))
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)))))
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.AttributesMask & (PROC_ATTR_DISABLE_EFF_0 << i)) && !spellInfo->Effects[i].IsAura())
LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId {} has Attribute PROC_ATTR_DISABLE_EFF_{}, but effect {} is not an aura effect", spellInfo->Id, static_cast<uint32>(i), static_cast<uint32>(i));
mSpellProcMap[spellInfo->Id] = procEntry;
@@ -1961,8 +1728,175 @@ void SpellMgr::LoadSpellProcs()
++count;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} spell proc conditions and data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", ">> Loaded {} Extra Spell Proc Event Conditions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
// Define can trigger auras
bool isTriggerAura[TOTAL_AURAS];
// Triggered always, even from triggered spells
bool isAlwaysTriggeredAura[TOTAL_AURAS];
// SpellTypeMask to add to the proc
uint32 spellTypeMask[TOTAL_AURAS];
// List of auras that CAN trigger but may not exist in spell_proc
// in most cases needed to drop charges
// some aura types need additional checks (eg SPELL_AURA_MECHANIC_IMMUNITY needs mechanic check)
// see AuraEffect::CheckEffectProc
for (uint16 i = 0; i < TOTAL_AURAS; ++i)
{
isTriggerAura[i] = false;
isAlwaysTriggeredAura[i] = false;
spellTypeMask[i] = PROC_SPELL_TYPE_MASK_ALL;
}
isTriggerAura[SPELL_AURA_DUMMY] = true; // Most dummy auras should require scripting, but there are some exceptions (ie 12311)
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;
isAlwaysTriggeredAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true;
isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true;
isAlwaysTriggeredAura[SPELL_AURA_MOD_CONFUSE] = true;
isAlwaysTriggeredAura[SPELL_AURA_MOD_FEAR] = true;
isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true;
isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true;
isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true;
spellTypeMask[SPELL_AURA_MOD_STEALTH] = PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL;
spellTypeMask[SPELL_AURA_MOD_CONFUSE] = PROC_SPELL_TYPE_DAMAGE;
spellTypeMask[SPELL_AURA_MOD_FEAR] = PROC_SPELL_TYPE_DAMAGE;
spellTypeMask[SPELL_AURA_MOD_ROOT] = PROC_SPELL_TYPE_DAMAGE;
spellTypeMask[SPELL_AURA_MOD_STUN] = PROC_SPELL_TYPE_DAMAGE;
spellTypeMask[SPELL_AURA_TRANSFORM] = PROC_SPELL_TYPE_DAMAGE;
// This generates default procs to retain compatibility with previous proc system
LOG_INFO("server.loading", "Generating spell proc data from SpellMap...");
count = 0;
oldMSTime = getMSTime();
for (SpellInfo const* spellInfo : mSpellInfoMap)
{
if (!spellInfo)
continue;
// Data already present in DB, overwrites default proc
if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end())
continue;
// Nothing to do if no flags set
if (!spellInfo->ProcFlags)
continue;
bool addTriggerFlag = false;
uint32 procSpellTypeMask = PROC_SPELL_TYPE_NONE;
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;
procSpellTypeMask |= spellTypeMask[auraName];
if (isAlwaysTriggeredAura[auraName])
addTriggerFlag = true;
// many proc auras with taken procFlag mask don't have attribute "can proc with triggered"
// they should proc nevertheless (example mage armor spells with judgement)
if (!addTriggerFlag && (spellInfo->ProcFlags & TAKEN_HIT_PROC_FLAG_MASK) != 0)
{
switch (auraName)
{
case SPELL_AURA_PROC_TRIGGER_SPELL:
case SPELL_AURA_PROC_TRIGGER_DAMAGE:
addTriggerFlag = true;
break;
default:
break;
}
}
break;
}
if (!procSpellTypeMask)
continue;
SpellProcEntry procEntry;
procEntry.SchoolMask = 0;
procEntry.ProcFlags = spellInfo->ProcFlags;
procEntry.SpellFamilyName = 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;
if (procEntry.SpellFamilyMask)
procEntry.SpellFamilyName = spellInfo->SpellFamilyName;
procEntry.SpellTypeMask = procSpellTypeMask;
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;
procEntry.ProcsPerMinute = 0;
procEntry.Chance = 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));
}
void SpellMgr::LoadSpellBonuses()

View File

@@ -154,13 +154,15 @@ 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,
| PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG | PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG
| PROC_FLAG_DONE_TRAP_ACTIVATION,
SPELL_CAST_PROC_FLAG_MASK = SPELL_PROC_FLAG_MASK | PROC_FLAG_DONE_TRAP_ACTIVATION | RANGED_PROC_FLAG_MASK,
SPELL_CAST_PROC_FLAG_MASK = SPELL_PROC_FLAG_MASK | PROC_FLAG_DONE_TRAP_ACTIVATION,
PERIODIC_PROC_FLAG_MASK = PROC_FLAG_DONE_PERIODIC | PROC_FLAG_TAKEN_PERIODIC,
@@ -168,7 +170,8 @@ enum ProcFlags
| 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_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
@@ -188,46 +191,6 @@ enum ProcFlags
PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS | \
PROC_FLAG_TAKEN_SPELL_RANGED_DMG_CLASS)
enum ProcFlagsExLegacy
{
PROC_EX_NONE = 0x0000000, // If none can tigger on Hit/Crit only (passive spells MUST defined by SpellFamily flag)
PROC_EX_NORMAL_HIT = 0x0000001, // If set only from normal hit (only damage spells)
PROC_EX_CRITICAL_HIT = 0x0000002,
PROC_EX_MISS = 0x0000004,
PROC_EX_RESIST = 0x0000008,
PROC_EX_DODGE = 0x0000010,
PROC_EX_PARRY = 0x0000020,
PROC_EX_BLOCK = 0x0000040,
PROC_EX_EVADE = 0x0000080,
PROC_EX_IMMUNE = 0x0000100,
PROC_EX_DEFLECT = 0x0000200,
PROC_EX_ABSORB = 0x0000400,
PROC_EX_REFLECT = 0x0000800,
PROC_EX_INTERRUPT = 0x0001000, // Melee hit result can be Interrupt (not used)
PROC_EX_FULL_BLOCK = 0x0002000, // block all attack damage
PROC_EX_RESERVED2 = 0x0004000,
PROC_EX_NOT_ACTIVE_SPELL = 0x0008000, // Spell mustn't do damage/heal to proc
PROC_EX_EX_TRIGGER_ALWAYS = 0x0010000, // If set trigger always no matter of hit result
PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000, // If set trigger always but only one time (not implemented yet)
PROC_EX_ONLY_ACTIVE_SPELL = 0x0040000, // Spell has to do damage/heal to proc
PROC_EX_NO_OVERHEAL = 0x0080000, // Proc if heal did some work
PROC_EX_NO_AURA_REFRESH = 0x0100000, // Proc if aura was not refreshed
PROC_EX_ONLY_FIRST_TICK = 0x0200000, // Proc only on first tick (in case of periodic spells)
// Flags for internal use - do not use these in db!
PROC_EX_INTERNAL_CANT_PROC = 0x0800000,
PROC_EX_INTERNAL_DOT = 0x1000000,
PROC_EX_INTERNAL_HOT = 0x2000000,
PROC_EX_INTERNAL_TRIGGERED = 0x4000000,
PROC_EX_INTERNAL_REQ_FAMILY = 0x8000000
};
#define AURA_SPELL_PROC_EX_MASK \
(PROC_EX_NORMAL_HIT | PROC_EX_CRITICAL_HIT | PROC_EX_MISS | \
PROC_EX_RESIST | PROC_EX_DODGE | PROC_EX_PARRY | PROC_EX_BLOCK | \
PROC_EX_EVADE | PROC_EX_IMMUNE | PROC_EX_DEFLECT | \
PROC_EX_ABSORB | PROC_EX_REFLECT | PROC_EX_INTERRUPT)
enum ProcFlagsSpellType
{
PROC_SPELL_TYPE_NONE = 0x0000000,
@@ -261,45 +224,37 @@ enum ProcFlagsHit
PROC_HIT_DEFLECT = 0x0000200,
PROC_HIT_ABSORB = 0x0000400, // partial or full absorb
PROC_HIT_REFLECT = 0x0000800,
PROC_HIT_INTERRUPT = 0x0001000, // (not used atm)
PROC_HIT_INTERRUPT = 0x0001000,
PROC_HIT_FULL_BLOCK = 0x0002000,
PROC_HIT_MASK_ALL = 0x2FFF,
PROC_HIT_MASK_ALL = 0x0002FFF,
};
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
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_
PROC_ATTR_DISABLE_EFF_0 = 0x0000010, // explicitly disables aura proc from effects, USE ONLY IF 100% SURE AURA SHOULDN'T PROC
PROC_ATTR_DISABLE_EFF_1 = 0x0000020, // used to avoid a console error if the spell has invalid trigger spell and handled elsewhere
PROC_ATTR_DISABLE_EFF_2 = 0x0000040 // or handling not needed
};
typedef std::unordered_map<uint32, SpellProcEventEntry> SpellProcEventMap;
struct SpellProcEntry
{
uint32 schoolMask; // if nonzero - bitmask for matching proc condition based on spell's school
uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName
flag96 spellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags
uint32 typeMask; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags
uint32 spellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType
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
float ratePerMinute; // 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 perMinuteRate set
uint32 cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura
uint32 charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite
uint32 SchoolMask; // if nonzero - bitmask for matching proc condition based on spell's school
uint32 SpellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName
flag96 SpellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags
uint32 ProcFlags; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags
uint32 SpellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType
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
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 perMinuteRate set
Milliseconds Cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura
uint32 Charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite
};
typedef std::unordered_map<uint32, SpellProcEntry> SpellProcMap;
@@ -669,13 +624,9 @@ public:
SpellGroupStackFlags CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2, bool remove, bool areaAura) const;
void GetSetOfSpellsInSpellGroupWithFlag(uint32 group_id, SpellGroupSpecialFlags flag, std::set<uint32>& availableElixirs) 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;
static bool CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo);
// Spell bonus data table
[[nodiscard]] SpellBonusEntry const* GetSpellBonusData(uint32 spellId) const;
@@ -750,7 +701,6 @@ public:
void LoadSpellTargetPositions();
void LoadSpellGroups();
void LoadSpellGroupStackRules();
void LoadSpellProcEvents();
void LoadSpellProcs();
void LoadSpellBonuses();
void LoadSpellThreats();
@@ -779,7 +729,6 @@ private:
SpellTargetPositionMap mSpellTargetPositions;
SpellGroupMap mSpellGroupMap;
SpellGroupStackMap mSpellGroupStackMap;
SpellProcEventMap mSpellProcEventMap;
SpellProcMap mSpellProcMap;
SpellBonusMap mSpellBonusMap;
SpellThreatMap mSpellThreatMap;

View File

@@ -733,6 +733,9 @@ 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<CheckProcHandler>::iterator itr = DoCheckAfterProc.begin(); itr != DoCheckAfterProc.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 `DoCheckAfterProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str());
@@ -906,6 +909,17 @@ bool AuraScript::CheckProcHandler::Call(AuraScript* auraScript, ProcEventInfo& e
return (auraScript->*_HandlerScript)(eventInfo);
}
AuraScript::CheckEffectProcHandler::CheckEffectProcHandler(AuraCheckEffectProcFnType handlerScript, uint8 effIndex, uint16 effName)
: AuraScript::EffectBase(effIndex, effName)
{
_HandlerScript = handlerScript;
}
bool AuraScript::CheckEffectProcHandler::Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo)
{
return (auraScript->*_HandlerScript)(aurEff, eventInfo);
}
AuraScript::AuraProcHandler::AuraProcHandler(AuraProcFnType handlerScript)
{
_HandlerScript = handlerScript;
@@ -1167,6 +1181,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_CHECK_AFTER_PROC:
case AURA_SCRIPT_HOOK_PREPARE_PROC:
case AURA_SCRIPT_HOOK_PROC:

View File

@@ -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_CHECK_AFTER_PROC,
AURA_SCRIPT_HOOK_PREPARE_PROC,
AURA_SCRIPT_HOOK_PROC,
@@ -529,7 +530,8 @@ public:
typedef void(CLASSNAME::*AuraEffectCalcSpellModFnType)(AuraEffect const*, SpellModifier* &); \
typedef void(CLASSNAME::*AuraEffectAbsorbFnType)(AuraEffect*, DamageInfo &, uint32 &); \
typedef void(CLASSNAME::*AuraEffectSplitFnType)(AuraEffect*, DamageInfo &, uint32 &); \
typedef bool(CLASSNAME::*AuraCheckProcFnType)(ProcEventInfo&); \
typedef bool(CLASSNAME::*AuraCheckProcFnType)(ProcEventInfo&); \
typedef bool(CLASSNAME::*AuraCheckEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \
typedef void(CLASSNAME::*AuraProcFnType)(ProcEventInfo&); \
typedef void(CLASSNAME::*AuraEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \
@@ -639,6 +641,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 AuraProcHandler
{
public:
@@ -668,8 +678,9 @@ public:
class EffectAbsorbFunction : public AuraScript::EffectAbsorbHandler { public: EffectAbsorbFunction(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectAbsorbHandler((AuraScript::AuraEffectAbsorbFnType)_pEffectHandlerScript, _effIndex) {} }; \
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 AuraProcHandlerFunction : public AuraScript::AuraProcHandler { public: AuraProcHandlerFunction(AuraProcFnType handlerScript) : AuraScript::AuraProcHandler((AuraScript::AuraProcFnType)handlerScript) {} }; \
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 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) {} }; \
#define PrepareAuraScript(CLASSNAME) AURASCRIPT_FUNCTION_TYPE_DEFINES(CLASSNAME) AURASCRIPT_FUNCTION_CAST_DEFINES(CLASSNAME)
@@ -811,6 +822,12 @@ public:
HookList<CheckProcHandler> DoCheckAfterProc;
#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 before aura procs (possibility to prevent charge drop/cooldown)
// example: DoPrepareProc += AuraProcFn(class::function);
// where function is: void function (ProcEventInfo& eventInfo);