aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Spells/SpellMgr.cpp
diff options
context:
space:
mode:
authorariel- <ariel-@users.noreply.github.com>2016-09-25 20:47:28 -0300
committerariel- <ariel-@users.noreply.github.com>2016-10-04 20:21:55 -0300
commite641d0c7d71bdb21e443bf52bf508b1581e07839 (patch)
tree764e1afadfa69a8c1d1a9c709498c017d12409b4 /src/server/game/Spells/SpellMgr.cpp
parent23902ff266c28fa83f3bab5646b3e5b93ab55380 (diff)
Core/Spells: Implementation of QAston proc system
- Move checks from Unit::IsTriggeredAtSpellProcEvent (old system) to Aura::IsProcTriggeredOnEvent (new system) - Templatize SpellModOp param of Player::ApplySpellMod, also killed charge counter from SpellModifier and Player system for handling charges... no point in having 3 different systems doing the same thing - Automatically add default entries to spellProcMap, based on spellinfo (else auras won't proc without an entry) Based on old Unit::ProcDamageAndSpellFor - Old Unit::ProcDamageAndSpellFor renamed to Unit::ProcSkillsAndReactives and made private, will no longer handle auras. - Start making use of HealInfo::AbsorbHeal in unit calculations, add effective healing info to HealInfo struct - Changes in spell reflection system, emulates old behaviour, delaying aura drop - Removed old charge count hacks in SpellMgr::LoadSpellInfoCorrections - Removed bogus error log when procChance is 0: Some auras have initial 0 procChance but modified by SPELLMOD_CHANCE_OF_SUCCESS - Fixed TriggerAurasProcOnEvent logic that tried to trigger twice from actor. - Allow non damaging spells with DamageClass Melee or Ranged to proc character enchants. Ref issue #17034: * http://web.archive.org/web/20110309092008/http://elitistjerks.com/f47/t49865-paladin_retribution_pve/ * When an auto-attack lands (does not dodge/parry/miss) that can proc a seal the of the following things happen independently of each other (see 2 roll system). * 1) A "hidden strike" which uses melee combat mechanics occurs. If it lands it refreshes/stacks SoV DoT. Only white swings can trigger a refresh or stack. (This hidden strike mechanic can also proc things like berserking..) * 2) A weapon damage based proc will occur if you used a special (CS/DS/judge) or if you have a 5 stack (from auto attacks). This attack can not be avoided. * Holy Vengeance is the "hidden strike" it has an apply aura effect and damage class melee. - Fixed Blood Tap interaction with Death Runes (btw, don't know what was going on with those MiscValueB, spell 45529 doesn't have any MiscValueB in SPELL_EFFECT_ACTIVATE_RUNE) - Ported some AuraEffect checks from old Unit.cpp function. added new AuraScript hook to check procs of an specific effect - Allow only AuraEffects that passed the check to proc, this won't block whole aura from proccing (and lose charges) if at least one of the effects procs, though - Changes in spell mod system (for SPELLMOD_CASTING_TIME). fixes #17558. - Added an exception for SPELLMOD_CRITICAL_CHANCE too, fixes #15193
Diffstat (limited to 'src/server/game/Spells/SpellMgr.cpp')
-rw-r--r--src/server/game/Spells/SpellMgr.cpp608
1 files changed, 249 insertions, 359 deletions
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 54684f78186..bab1a34c038 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -18,6 +18,7 @@
#include "SpellMgr.h"
#include "SpellInfo.h"
+#include "Spell.h"
#include "ObjectMgr.h"
#include "SpellAuraDefines.h"
#include "SharedDefines.h"
@@ -779,172 +780,6 @@ SpellGroupStackRule SpellMgr::GetSpellGroupStackRule(SpellGroup group) const
return SPELL_GROUP_STACK_RULE_DEFAULT;
}
-SpellProcEventEntry const* SpellMgr::GetSpellProcEvent(uint32 spellId) const
-{
- SpellProcEventMap::const_iterator itr = mSpellProcEventMap.find(spellId);
- if (itr != mSpellProcEventMap.end())
- return &itr->second;
- return NULL;
-}
-
-bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) const
-{
- // No extra req need
- uint32 procEvent_procEx = PROC_EX_NONE;
-
- // check prockFlags for condition
- if ((procFlags & EventProcFlag) == 0)
- return false;
-
- bool hasFamilyMask = false;
-
- /**
-
- * @brief Check auras procced by periodics
-
- *Only damaging Dots can proc auras with PROC_FLAG_TAKEN_DAMAGE
-
- *Only Dots can proc if ONLY has PROC_FLAG_DONE_PERIODIC or PROC_FLAG_TAKEN_PERIODIC.
-
- *Hots can proc if ONLY has PROC_FLAG_DONE_PERIODIC and spellfamily != 0
-
- *Only Dots can proc auras with PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG or PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_NEG
-
- *Only Hots can proc auras with PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS or PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS
-
- *Only Dots can proc auras with PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG or PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_NEG
-
- *Only Hots can proc auras with PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_POS or PROC_FLAG_TAKEN_SPELL_NONE_DMG_CLASS_POS
-
- * @param procSpell the spell proccing the aura
- * @param procFlags proc_flags of spellProc
- * @param procExtra proc_EX of procSpell
- * @param EventProcFlag proc_flags of aura to be procced
- * @param spellProto SpellInfo of aura to be procced
-
- */
-
- /// Quick Check - If PROC_FLAG_TAKEN_DAMAGE is set for aura and procSpell dealt damage, proc no matter what kind of spell that deals the damage.
- if (procFlags & PROC_FLAG_TAKEN_DAMAGE && EventProcFlag & PROC_FLAG_TAKEN_DAMAGE)
- return true;
-
- if (procFlags & PROC_FLAG_DONE_PERIODIC && EventProcFlag & 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
- /// Traps are negative spells but not always do damage (only hunter traps set PROC_FLAG_DONE_TRAP_ACTIVATION)
- 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 && EventProcFlag & 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;
-
- // Always trigger for this
- if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH))
- return true;
-
- if (spellProcEvent) // Exist event data
- {
- // Store extra req
- procEvent_procEx = spellProcEvent->procEx;
-
- // For melee triggers
- if (procSpell == NULL)
- {
- // Check (if set) for school (melee attack has 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 & procSpell->SchoolMask) == 0)
- return false;
-
- // Check (if set) for spellFamilyName
- if (spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName))
- return false;
-
- // spellFamilyName is Ok need check for spellFamilyMask if present
- if (spellProcEvent->spellFamilyMask)
- {
- if (!(spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags))
- return false;
- hasFamilyMask = true;
- // Some spells are not considered as active even with spellfamilyflags set
- if (!(procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL))
- active = true;
- }
- }
- }
-
- if (procExtra & (PROC_EX_INTERNAL_REQ_FAMILY))
- {
- if (!hasFamilyMask)
- 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);
@@ -965,10 +800,31 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
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
+ if (!(procEntry.AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC))
+ {
+ 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))
return false;
@@ -1851,224 +1707,276 @@ void SpellMgr::LoadSpellGroupStackRules()
TC_LOG_INFO("server.loading", ">> Loaded %u spell group stack rules in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
-void SpellMgr::LoadSpellProcEvents()
+// Used for prepare can/can't triggr aura
+static bool InitTriggerAuraData();
+// Define can trigger auras
+static bool isTriggerAura[TOTAL_AURAS];
+// Triggered always, even from triggered spells
+static bool isAlwaysTriggeredAura[TOTAL_AURAS];
+// Prepare lists
+static bool procPrepared = InitTriggerAuraData();
+
+// List of auras that CAN be trigger but may not exist in spell_proc_event
+// in most case need for drop charges
+// in some types of aura need do additional check
+// for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic
+bool InitTriggerAuraData()
+{
+ for (uint16 i = 0; i < TOTAL_AURAS; ++i)
+ {
+ isTriggerAura[i] = false;
+ isAlwaysTriggeredAura[i] = false;
+ }
+ isTriggerAura[SPELL_AURA_DUMMY] = true; // Most dummy auras should require scripting. Remove?
+ 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_FEAR] = true;
+ isAlwaysTriggeredAura[SPELL_AURA_MOD_ROOT] = true;
+ isAlwaysTriggeredAura[SPELL_AURA_MOD_STUN] = true;
+ isAlwaysTriggeredAura[SPELL_AURA_TRANSFORM] = true;
+ isAlwaysTriggeredAura[SPELL_AURA_SPELL_MAGNET] = true;
+ isAlwaysTriggeredAura[SPELL_AURA_SCHOOL_ABSORB] = true;
+ isAlwaysTriggeredAura[SPELL_AURA_MOD_STEALTH] = true;
+
+ return true;
+}
+
+void SpellMgr::LoadSpellProcs()
{
uint32 oldMSTime = getMSTime();
- mSpellProcEventMap.clear(); // need for reload case
+ mSpellProcMap.clear(); // need for reload case
- // 0 1 2 3 4 5 6 7 8 9 10
- QueryResult result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event");
- if (!result)
- {
- TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc event conditions. DB table `spell_proc_event` is empty.");
- return;
- }
+ // 0 1 2 3 4 5
+ QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, "
+ // 6 7 8 9 10 11 12 13 14
+ "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc");
uint32 count = 0;
-
- do
+ if (result)
{
- Field* fields = result->Fetch();
-
- int32 spellId = fields[0].GetInt32();
-
- bool allRanks = false;
- if (spellId < 0)
- {
- allRanks = true;
- spellId = -spellId;
- }
-
- SpellInfo const* spellInfo = GetSpellInfo(spellId);
- if (!spellInfo)
+ do
{
- TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` does not exist.", spellId);
- continue;
- }
+ Field* fields = result->Fetch();
- if (allRanks)
- {
- if (!spellInfo->IsRanked())
- TC_LOG_ERROR("sql.sql", "The spell %u is listed in `spell_proc_event` with all ranks, but spell has no ranks.", spellId);
+ int32 spellId = fields[0].GetInt32();
- if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
+ bool allRanks = false;
+ if (spellId < 0)
{
- TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is not first rank of spell.", spellId);
- continue;
+ allRanks = true;
+ spellId = -spellId;
}
- }
-
- SpellProcEventEntry spellProcEvent;
-
- spellProcEvent.schoolMask = fields[1].GetInt8();
- spellProcEvent.spellFamilyName = fields[2].GetUInt16();
- spellProcEvent.spellFamilyMask[0] = fields[3].GetUInt32();
- spellProcEvent.spellFamilyMask[1] = fields[4].GetUInt32();
- spellProcEvent.spellFamilyMask[2] = fields[5].GetUInt32();
- spellProcEvent.procFlags = fields[6].GetUInt32();
- spellProcEvent.procEx = fields[7].GetUInt32();
- spellProcEvent.ppmRate = fields[8].GetFloat();
- spellProcEvent.customChance = fields[9].GetFloat();
- spellProcEvent.cooldown = fields[10].GetUInt32();
- while (spellInfo)
- {
- if (mSpellProcEventMap.find(spellInfo->Id) != mSpellProcEventMap.end())
+ SpellInfo const* spellInfo = GetSpellInfo(spellId);
+ if (!spellInfo)
{
- TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` already has its first rank in table.", spellInfo->Id);
- break;
+ TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` does not exist", spellId);
+ continue;
}
- if (!spellInfo->ProcFlags && !spellProcEvent.procFlags)
- TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is probably not a triggered spell.", spellInfo->Id);
+ if (allRanks)
+ {
+ if (!spellInfo->IsRanked())
+ TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId);
- mSpellProcEventMap[spellInfo->Id] = spellProcEvent;
+ if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
+ {
+ TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` is not the first rank of the spell.", spellId);
+ continue;
+ }
+ }
- if (allRanks)
- spellInfo = spellInfo->GetNextRankSpell();
- else
- break;
- }
+ SpellProcEntry baseProcEntry;
+
+ baseProcEntry.SchoolMask = fields[1].GetInt8();
+ baseProcEntry.SpellFamilyName = fields[2].GetUInt16();
+ baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32();
+ baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32();
+ baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32();
+ baseProcEntry.ProcFlags = fields[6].GetUInt32();
+ baseProcEntry.SpellTypeMask = fields[7].GetUInt32();
+ baseProcEntry.SpellPhaseMask = fields[8].GetUInt32();
+ baseProcEntry.HitMask = fields[9].GetUInt32();
+ baseProcEntry.AttributesMask = fields[10].GetUInt32();
+ baseProcEntry.ProcsPerMinute = fields[11].GetFloat();
+ baseProcEntry.Chance = fields[12].GetFloat();
+ baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32());
+ baseProcEntry.Charges = fields[14].GetUInt8();
+
+ while (spellInfo)
+ {
+ if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end())
+ {
+ TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` already has its first rank in the table.", spellInfo->Id);
+ break;
+ }
- ++count;
+ SpellProcEntry procEntry = SpellProcEntry(baseProcEntry);
+
+ // take defaults from dbcs
+ 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)
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask);
+ if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < SPELLFAMILY_MAGE || procEntry.SpellFamilyName > SPELLFAMILY_PET || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16))
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName);
+ if (procEntry.Chance < 0)
+ {
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id);
+ procEntry.Chance = 0;
+ }
+ if (procEntry.ProcsPerMinute < 0)
+ {
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id);
+ procEntry.ProcsPerMinute = 0;
+ }
+ if (!procEntry.ProcFlags)
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id);
+ if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL)
+ TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask);
+ if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id);
+ if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id);
+ if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL)
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask);
+ if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK))
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id);
+ if (procEntry.HitMask & ~PROC_HIT_MASK_ALL)
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, 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)))))
+ TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id);
+
+ mSpellProcMap[spellInfo->Id] = procEntry;
+
+ if (allRanks)
+ spellInfo = spellInfo->GetNextRankSpell();
+ else
+ break;
+ }
+ ++count;
+ } while (result->NextRow());
}
- while (result->NextRow());
+ else
+ TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty.");
- TC_LOG_INFO("server.loading", ">> Loaded %u extra spell proc event conditions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
-}
+ TC_LOG_INFO("server.loading", ">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
-void SpellMgr::LoadSpellProcs()
-{
- uint32 oldMSTime = getMSTime();
+ // This generates default procs to retain compatibility with previous proc system
+ TC_LOG_INFO("server.loading", "Generating spell proc data from SpellMap...");
+ count = 0;
+ oldMSTime = getMSTime();
- mSpellProcMap.clear(); // need for reload case
+ for (SpellInfo const* spellInfo : mSpellInfoMap)
+ {
+ if (!spellInfo)
+ continue;
- // 0 1 2 3 4 5
- QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, "
- // 6 7 8 9 10 11 12 13 14
- "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc");
+ if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end())
+ continue;
- if (!result)
- {
- TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty.");
- return;
- }
+ bool found = false, addTriggerFlag = false;
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ if (!spellInfo->Effects[i].IsEffect())
+ continue;
- uint32 count = 0;
- do
- {
- Field* fields = result->Fetch();
+ uint32 auraName = spellInfo->Effects[i].ApplyAuraName;
+ if (!auraName)
+ continue;
- int32 spellId = fields[0].GetInt32();
+ if (!isTriggerAura[auraName])
+ continue;
- bool allRanks = false;
- if (spellId < 0)
- {
- allRanks = true;
- spellId = -spellId;
- }
+ found = true;
- SpellInfo const* spellInfo = GetSpellInfo(spellId);
- if (!spellInfo)
- {
- TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` does not exist", spellId);
- continue;
+ if (!addTriggerFlag && isAlwaysTriggeredAura[auraName])
+ addTriggerFlag = true;
+ break;
}
- if (allRanks)
- {
- if (!spellInfo->IsRanked())
- TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId);
+ if (!found)
+ continue;
- if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
- {
- TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` is not the first rank of the spell.", spellId);
- continue;
- }
- }
+ if (!spellInfo->ProcFlags)
+ continue;
- SpellProcEntry baseProcEntry;
+ SpellProcEntry procEntry;
+ procEntry.SchoolMask = 0;
+ procEntry.SpellFamilyName = spellInfo->SpellFamilyName;
+ procEntry.ProcFlags = spellInfo->ProcFlags;
+ 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;
- baseProcEntry.SchoolMask = fields[1].GetInt8();
- baseProcEntry.SpellFamilyName = fields[2].GetUInt16();
- baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32();
- baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32();
- baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32();
- baseProcEntry.ProcFlags = fields[6].GetUInt32();
- baseProcEntry.SpellTypeMask = fields[7].GetUInt32();
- baseProcEntry.SpellPhaseMask = fields[8].GetUInt32();
- baseProcEntry.HitMask = fields[9].GetUInt32();
- baseProcEntry.AttributesMask = fields[10].GetUInt32();
- baseProcEntry.ProcsPerMinute = fields[11].GetFloat();
- baseProcEntry.Chance = fields[12].GetFloat();
- baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32());
- baseProcEntry.Charges = fields[14].GetUInt8();
+ procEntry.SpellTypeMask = PROC_SPELL_TYPE_MASK_ALL;
+ procEntry.SpellPhaseMask = PROC_SPELL_PHASE_HIT;
+ procEntry.HitMask = PROC_HIT_NONE; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent
- while (spellInfo)
+ // Reflect auras should only proc off reflects
+ for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
- if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end())
+ if (spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS) || spellInfo->Effects[i].IsAura(SPELL_AURA_REFLECT_SPELLS_SCHOOL))
{
- TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` already has its first rank in the table.", spellInfo->Id);
+ procEntry.HitMask = PROC_HIT_REFLECT;
break;
}
+ }
- SpellProcEntry procEntry = SpellProcEntry(baseProcEntry);
-
- // take defaults from dbcs
- 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)
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask);
- if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < 3 || procEntry.SpellFamilyName > 17 || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16))
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName);
- if (procEntry.Chance < 0)
- {
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id);
- procEntry.Chance = 0;
- }
- if (procEntry.ProcsPerMinute < 0)
- {
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id);
- procEntry.ProcsPerMinute = 0;
- }
- if (procEntry.Chance == 0 && procEntry.ProcsPerMinute == 0)
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `Chance` and `ProcsPerMinute` values defined, proc will not be triggered", spellInfo->Id);
- if (!procEntry.ProcFlags)
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id);
- if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL)
- TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask);
- if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id);
- if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id);
- if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL)
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask);
- if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK))
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id);
- if (procEntry.HitMask & ~PROC_HIT_MASK_ALL)
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, 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)))))
- TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id);
-
- mSpellProcMap[spellInfo->Id] = procEntry;
+ 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;
- if (allRanks)
- spellInfo = spellInfo->GetNextRankSpell();
- else
- break;
- }
+ procEntry.ProcsPerMinute = 0;
+ procEntry.Chance = spellInfo->ProcChance;
+ procEntry.Cooldown = Milliseconds::zero();
+ procEntry.Charges = spellInfo->ProcCharges;
+
+ mSpellProcMap[spellInfo->Id] = procEntry;
++count;
}
- while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Generated spell proc data for %u spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
void SpellMgr::LoadSpellBonusess()
@@ -3037,7 +2945,7 @@ void SpellMgr::LoadSpellInfoCorrections()
case 59725: // Improved Spell Reflection - aoe aura
// Target entry seems to be wrong for this spell :/
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER_AREA_PARTY);
- spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_10_YARDS_2);
+ spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_20_YARDS);
break;
case 44978: // Wild Magic
case 45001:
@@ -3130,31 +3038,13 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->MaxAffectedTargets = 1;
spellInfo->Effects[EFFECT_0].TriggerSpell = 33760;
break;
- case 17941: // Shadow Trance
- case 22008: // Netherwind Focus
- case 31834: // Light's Grace
- case 34936: // Backlash
- case 48108: // Hot Streak
- case 51124: // Killing Machine
- case 54741: // Firestarter
- case 57761: // Fireball!
- case 64823: // Item - Druid T8 Balance 4P Bonus
- case 34477: // Misdirection
- case 44401: // Missile Barrage
- case 18820: // Insight
- spellInfo->ProcCharges = 1;
- break;
case 44544: // Fingers of Frost
spellInfo->Effects[EFFECT_0].SpellClassMask = flag96(685904631, 1151048, 0);
break;
- case 74396: // Fingers of Frost visual buff
case 53257: // Cobra Strikes
spellInfo->ProcCharges = 2;
spellInfo->StackAmount = 0;
break;
- case 28200: // Ascendance (Talisman of Ascendance trinket)
- spellInfo->ProcCharges = 6;
- break;
case 49224: // Magic Suppression - DK
case 49610: // Magic Suppression - DK
case 49611: // Magic Suppression - DK