aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Spells/SpellMgr.cpp
diff options
context:
space:
mode:
authorQAston <qaston@gmail.com>2011-07-26 23:09:28 +0200
committerQAston <qaston@gmail.com>2011-07-26 23:09:28 +0200
commitb0fe236265465a0f39aa98a8cee2916d1ccfaa02 (patch)
tree77ed4bde46de983c280a542d657a30b24865638c /src/server/game/Spells/SpellMgr.cpp
parent29c228a80170e4264129d4e3bed4d2fc41aca5a7 (diff)
Core: Use new SpellInfo class in core. Sadly, this commit is not compatibile with some of the custom code. To make your code work again you may need to change:
*SpellEntry is now SpellInfo *GetSpellProto is now GetSpellInfo *SpellEntry::Effect*[effIndex] is now avalible under SpellInfo.Effects[effIndex].* *sSpellStore.LookupEntry is no longer valid, use sSpellMgr->GetSpellInfo() *SpellFunctions from SpellMgr.h like DoSpellStuff(spellId) are now: spellInfo->DoStuff() *SpellMgr::CalculateEffectValue and similar functions are now avalible in SpellEffectInfo class. *GET_SPELL macro is removed, code which used it is moved to SpellMgr::LoadDbcDataCorrections *code which affected dbc data in SpellMgr::LoadSpellCustomAttr is now moved to LoadDbcDataCorrections
Diffstat (limited to 'src/server/game/Spells/SpellMgr.cpp')
-rwxr-xr-xsrc/server/game/Spells/SpellMgr.cpp4629
1 files changed, 1671 insertions, 2958 deletions
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index c6f03c0e3e5..a14d53eb667 100755
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -17,9 +17,11 @@
*/
#include "SpellMgr.h"
+#include "SpellInfo.h"
#include "ObjectMgr.h"
#include "SpellAuras.h"
#include "SpellAuraDefines.h"
+#include "SharedDefines.h"
#include "DBCStores.h"
#include "World.h"
#include "Chat.h"
@@ -29,1313 +31,697 @@
#include "MapManager.h"
#include "BattlegroundIC.h"
-bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS];
-SpellEffectTargetTypes EffectTargetType[TOTAL_SPELL_EFFECTS];
-SpellSelectTargetTypes SpellTargetType[TOTAL_SPELL_TARGETS];
-
-SpellMgr::SpellMgr()
+bool IsPrimaryProfessionSkill(uint32 skill)
{
- for (uint8 i = 0; i < TOTAL_SPELL_EFFECTS; ++i)
- {
- switch (i)
- {
- case SPELL_EFFECT_PERSISTENT_AREA_AURA: //27
- case SPELL_EFFECT_SUMMON: //28
- case SPELL_EFFECT_TRIGGER_MISSILE: //32
- case SPELL_EFFECT_TRANS_DOOR: //50 summon object
- case SPELL_EFFECT_SUMMON_PET: //56
- case SPELL_EFFECT_ADD_FARSIGHT: //72
- case SPELL_EFFECT_SUMMON_OBJECT_WILD: //76
- //case SPELL_EFFECT_SUMMON_CRITTER: //97 not 303
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: //104
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: //105
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: //106
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: //107
- case SPELL_EFFECT_SUMMON_DEAD_PET: //109
- case SPELL_EFFECT_TRIGGER_SPELL_2: //151 ritual of summon
- EffectTargetType[i] = SPELL_REQUIRE_DEST;
- break;
- case SPELL_EFFECT_PARRY: // 0
- case SPELL_EFFECT_BLOCK: // 0
- case SPELL_EFFECT_SKILL: // always with dummy 3 as A
- //case SPELL_EFFECT_LEARN_SPELL: // 0 may be 5 pet
- case SPELL_EFFECT_TRADE_SKILL: // 0 or 1
- case SPELL_EFFECT_PROFICIENCY: // 0
- EffectTargetType[i] = SPELL_REQUIRE_NONE;
- break;
- case SPELL_EFFECT_ENCHANT_ITEM:
- case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
- case SPELL_EFFECT_DISENCHANT:
- //in 243 this is 0, in 309 it is 1
- //so both item target and unit target is pushed, and cause crash
- //case SPELL_EFFECT_FEED_PET:
- case SPELL_EFFECT_PROSPECTING:
- case SPELL_EFFECT_MILLING:
- case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC:
- EffectTargetType[i] = SPELL_REQUIRE_ITEM;
- break;
- //caster must be pushed otherwise no sound
- case SPELL_EFFECT_APPLY_AREA_AURA_PARTY:
- case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
- case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY:
- case SPELL_EFFECT_APPLY_AREA_AURA_PET:
- case SPELL_EFFECT_APPLY_AREA_AURA_OWNER:
- case SPELL_EFFECT_APPLY_AREA_AURA_RAID:
- case SPELL_EFFECT_CHARGE:
- case SPELL_EFFECT_CHARGE_DEST:
- case SPELL_EFFECT_JUMP:
- case SPELL_EFFECT_JUMP_DEST:
- case SPELL_EFFECT_LEAP_BACK:
- EffectTargetType[i] = SPELL_REQUIRE_CASTER;
- break;
- //case SPELL_EFFECT_WMO_DAMAGE:
- //case SPELL_EFFECT_WMO_REPAIR:
- //case SPELL_EFFECT_WMO_CHANGE:
- // EffectTargetType[i] = SPELL_REQUIRE_GOBJECT;
- // break;
- default:
- EffectTargetType[i] = SPELL_REQUIRE_UNIT;
- break;
- }
- }
-
- for (uint8 i = 0; i < TOTAL_SPELL_TARGETS; ++i)
- {
- switch (i)
- {
- case TARGET_UNIT_CASTER:
- case TARGET_UNIT_CASTER_FISHING:
- case TARGET_UNIT_MASTER:
- case TARGET_UNIT_PET:
- case TARGET_UNIT_PARTY_CASTER:
- case TARGET_UNIT_RAID_CASTER:
- case TARGET_UNIT_VEHICLE:
- case TARGET_UNIT_PASSENGER_0:
- case TARGET_UNIT_PASSENGER_1:
- case TARGET_UNIT_PASSENGER_2:
- case TARGET_UNIT_PASSENGER_3:
- case TARGET_UNIT_PASSENGER_4:
- case TARGET_UNIT_PASSENGER_5:
- case TARGET_UNIT_PASSENGER_6:
- case TARGET_UNIT_PASSENGER_7:
- case TARGET_UNIT_SUMMONER:
- SpellTargetType[i] = TARGET_TYPE_UNIT_CASTER;
- break;
- case TARGET_UNIT_TARGET_MINIPET:
- case TARGET_UNIT_TARGET_ALLY:
- case TARGET_UNIT_TARGET_RAID:
- case TARGET_UNIT_TARGET_ANY:
- case TARGET_UNIT_TARGET_ENEMY:
- case TARGET_UNIT_TARGET_PARTY:
- case TARGET_UNIT_TARGET_PASSENGER:
- case TARGET_UNIT_TARGET_ALLY_PARTY:
- case TARGET_UNIT_TARGET_CLASS_RAID:
- case TARGET_UNIT_CHAINHEAL:
- SpellTargetType[i] = TARGET_TYPE_UNIT_TARGET;
- break;
- case TARGET_UNIT_NEARBY_ENEMY:
- case TARGET_UNIT_NEARBY_ALLY:
- case TARGET_UNIT_NEARBY_ENTRY:
- case TARGET_UNIT_NEARBY_PARTY:
- case TARGET_UNIT_NEARBY_RAID:
- case TARGET_GAMEOBJECT_NEARBY_ENTRY:
- SpellTargetType[i] = TARGET_TYPE_UNIT_NEARBY;
- break;
- case TARGET_UNIT_AREA_ENEMY_SRC:
- case TARGET_UNIT_AREA_ALLY_SRC:
- case TARGET_UNIT_AREA_ENTRY_SRC:
- case TARGET_UNIT_AREA_PARTY_SRC:
- case TARGET_GAMEOBJECT_AREA_SRC:
- SpellTargetType[i] = TARGET_TYPE_AREA_SRC;
- break;
- case TARGET_UNIT_AREA_ENEMY_DST:
- case TARGET_UNIT_AREA_ALLY_DST:
- case TARGET_UNIT_AREA_ENTRY_DST:
- case TARGET_UNIT_AREA_PARTY_DST:
- case TARGET_GAMEOBJECT_AREA_DST:
- SpellTargetType[i] = TARGET_TYPE_AREA_DST;
- break;
- case TARGET_UNIT_CONE_ENEMY:
- case TARGET_UNIT_CONE_ALLY:
- case TARGET_UNIT_CONE_ENTRY:
- case TARGET_UNIT_CONE_ENEMY_UNKNOWN:
- case TARGET_UNIT_AREA_PATH:
- case TARGET_GAMEOBJECT_AREA_PATH:
- SpellTargetType[i] = TARGET_TYPE_AREA_CONE;
- break;
- case TARGET_DST_CASTER:
- case TARGET_SRC_CASTER:
- case TARGET_MINION:
- case TARGET_DEST_CASTER_FRONT_LEAP:
- case TARGET_DEST_CASTER_FRONT:
- case TARGET_DEST_CASTER_BACK:
- case TARGET_DEST_CASTER_RIGHT:
- case TARGET_DEST_CASTER_LEFT:
- case TARGET_DEST_CASTER_FRONT_LEFT:
- case TARGET_DEST_CASTER_BACK_LEFT:
- case TARGET_DEST_CASTER_BACK_RIGHT:
- case TARGET_DEST_CASTER_FRONT_RIGHT:
- case TARGET_DEST_CASTER_RANDOM:
- case TARGET_DEST_CASTER_RADIUS:
- SpellTargetType[i] = TARGET_TYPE_DEST_CASTER;
- break;
- case TARGET_DST_TARGET_ENEMY:
- case TARGET_DEST_TARGET_ANY:
- case TARGET_DEST_TARGET_FRONT:
- case TARGET_DEST_TARGET_BACK:
- case TARGET_DEST_TARGET_RIGHT:
- case TARGET_DEST_TARGET_LEFT:
- case TARGET_DEST_TARGET_FRONT_LEFT:
- case TARGET_DEST_TARGET_BACK_LEFT:
- case TARGET_DEST_TARGET_BACK_RIGHT:
- case TARGET_DEST_TARGET_FRONT_RIGHT:
- case TARGET_DEST_TARGET_RANDOM:
- case TARGET_DEST_TARGET_RADIUS:
- SpellTargetType[i] = TARGET_TYPE_DEST_TARGET;
- break;
- case TARGET_DEST_DYNOBJ_ENEMY:
- case TARGET_DEST_DYNOBJ_ALLY:
- case TARGET_DEST_DYNOBJ_NONE:
- case TARGET_DEST_DEST:
- case TARGET_DEST_TRAJ:
- case TARGET_DEST_DEST_FRONT_LEFT:
- case TARGET_DEST_DEST_BACK_LEFT:
- case TARGET_DEST_DEST_BACK_RIGHT:
- case TARGET_DEST_DEST_FRONT_RIGHT:
- case TARGET_DEST_DEST_FRONT:
- case TARGET_DEST_DEST_BACK:
- case TARGET_DEST_DEST_RIGHT:
- case TARGET_DEST_DEST_LEFT:
- case TARGET_DEST_DEST_RANDOM:
- case TARGET_DEST_DEST_RANDOM_DIR_DIST:
- SpellTargetType[i] = TARGET_TYPE_DEST_DEST;
- break;
- case TARGET_DST_DB:
- case TARGET_DST_HOME:
- case TARGET_DST_NEARBY_ENTRY:
- SpellTargetType[i] = TARGET_TYPE_DEST_SPECIAL;
- break;
- case TARGET_UNIT_CHANNEL_TARGET:
- case TARGET_DEST_CHANNEL_TARGET:
- case TARGET_DEST_CHANNEL_CASTER:
- SpellTargetType[i] = TARGET_TYPE_CHANNEL;
- break;
- default:
- SpellTargetType[i] = TARGET_TYPE_DEFAULT;
- }
- }
-
- for (int32 i = 0; i < TOTAL_SPELL_TARGETS; ++i)
- {
- switch (i)
- {
- case TARGET_UNIT_AREA_ENEMY_DST:
- case TARGET_UNIT_AREA_ENEMY_SRC:
- case TARGET_UNIT_AREA_ALLY_DST:
- case TARGET_UNIT_AREA_ALLY_SRC:
- case TARGET_UNIT_AREA_ENTRY_DST:
- case TARGET_UNIT_AREA_ENTRY_SRC:
- case TARGET_UNIT_AREA_PARTY_DST:
- case TARGET_UNIT_AREA_PARTY_SRC:
- case TARGET_UNIT_TARGET_ALLY_PARTY:
- case TARGET_UNIT_PARTY_CASTER:
- case TARGET_UNIT_CONE_ENEMY:
- case TARGET_UNIT_CONE_ALLY:
- case TARGET_UNIT_CONE_ENEMY_UNKNOWN:
- case TARGET_UNIT_AREA_PATH:
- case TARGET_GAMEOBJECT_AREA_PATH:
- case TARGET_UNIT_RAID_CASTER:
- IsAreaEffectTarget[i] = true;
- break;
- default:
- IsAreaEffectTarget[i] = false;
- break;
- }
- }
-}
-
-SpellMgr::~SpellMgr()
-{
-}
-
-bool SpellMgr::IsSrcTargetSpell(SpellEntry const *spellInfo) const
-{
- for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i)
- {
- if (SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_AREA_SRC || SpellTargetType[spellInfo->EffectImplicitTargetB[i]] == TARGET_TYPE_AREA_SRC)
- return true;
- }
- return false;
-}
-
-int32 GetSpellDuration(SpellEntry const *spellInfo)
-{
- if (!spellInfo)
- return 0;
- SpellDurationEntry const* du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex);
- if (!du)
- return 0;
- return (du->Duration[0] == -1) ? -1 : abs(du->Duration[0]);
-}
-
-int32 GetSpellMaxDuration(SpellEntry const *spellInfo)
-{
- if (!spellInfo)
- return 0;
- SpellDurationEntry const* du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex);
- if (!du)
- return 0;
- return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]);
-}
-
-uint32 GetDispelChance(Unit* auraCaster, Unit* target, uint32 spellId, bool offensive, bool *result)
-{
- // we assume that aura dispel chance is 100% on start
- // need formula for level difference based chance
- int32 resist_chance = 0;
-
- // Apply dispel mod from aura caster
- if (auraCaster)
- if (Player* modOwner = auraCaster->GetSpellModOwner())
- modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_DISPEL_CHANCE, resist_chance);
-
- // Dispel resistance from target SPELL_AURA_MOD_DISPEL_RESIST
- // Only affects offensive dispels
- if (offensive && target)
- resist_chance += target->GetTotalAuraModifier(SPELL_AURA_MOD_DISPEL_RESIST);
-
- // Try dispel
- if (result)
- *result = !roll_chance_i(resist_chance);
-
- resist_chance = resist_chance < 0 ? 0 : resist_chance;
- resist_chance = resist_chance > 100 ? 100 : resist_chance;
- return 100 - resist_chance;
-}
-
-uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell* spell)
-{
- SpellCastTimesEntry const* spellCastTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex);
-
- // not all spells have cast time index and this is all is pasiive abilities
- if (!spellCastTimeEntry)
- return 0;
-
- int32 castTime = spellCastTimeEntry->CastTime;
-
- if (Unit *caster = (spell ? spell->GetCaster() : NULL))
- caster->ModSpellCastTime(spellInfo, castTime, spell);
-
- if (spellInfo->Attributes & SPELL_ATTR0_REQ_AMMO && (!spell || !(spell->IsAutoRepeat())))
- castTime += 500;
-
- return (castTime > 0) ? uint32(castTime) : 0;
-}
-
-bool IsPassiveSpell(uint32 spellId)
-{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);
- if (!spellInfo)
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(skill);
+ if (!pSkill)
return false;
- return IsPassiveSpell(spellInfo);
-}
-
-bool IsPassiveSpell(SpellEntry const* spellInfo)
-{
- if (spellInfo->Attributes & SPELL_ATTR0_PASSIVE)
- return true;
- return false;
-}
-bool IsAutocastableSpell(uint32 spellId)
-{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);
- if (!spellInfo)
- return false;
- if (spellInfo->Attributes & SPELL_ATTR0_PASSIVE)
+ if (pSkill->categoryId != SKILL_CATEGORY_PROFESSION)
return false;
- if (spellInfo->AttributesEx & SPELL_ATTR1_UNAUTOCASTABLE_BY_PET)
- return false;
- return true;
-}
-
-bool IsHigherHankOfSpell(uint32 spellId_1, uint32 spellId_2)
-{
- return sSpellMgr->GetSpellRank(spellId_1) < sSpellMgr->GetSpellRank(spellId_2);
-}
-uint32 CalculatePowerCost(SpellEntry const* spellInfo, Unit const* caster, SpellSchoolMask schoolMask)
-{
- // Spell drain all exist power on cast (Only paladin lay of Hands)
- if (spellInfo->AttributesEx & SPELL_ATTR1_DRAIN_ALL_POWER)
- {
- // If power type - health drain all
- if (spellInfo->powerType == POWER_HEALTH)
- return caster->GetHealth();
- // Else drain all power
- if (spellInfo->powerType < MAX_POWERS)
- return caster->GetPower(Powers(spellInfo->powerType));
- sLog->outError("CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id);
- return 0;
- }
-
- // Base powerCost
- int32 powerCost = spellInfo->manaCost;
- // PCT cost from total amount
- if (spellInfo->ManaCostPercentage)
- {
- switch (spellInfo->powerType)
- {
- // health as power used
- case POWER_HEALTH:
- powerCost += int32(CalculatePctU(caster->GetCreateHealth(), spellInfo->ManaCostPercentage));
- break;
- case POWER_MANA:
- powerCost += int32(CalculatePctU(caster->GetCreateMana(), spellInfo->ManaCostPercentage));
- break;
- case POWER_RAGE:
- case POWER_FOCUS:
- case POWER_ENERGY:
- case POWER_HAPPINESS:
- powerCost += int32(CalculatePctU(caster->GetMaxPower(Powers(spellInfo->powerType)), spellInfo->ManaCostPercentage));
- break;
- case POWER_RUNE:
- case POWER_RUNIC_POWER:
- sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "CalculateManaCost: Not implemented yet!");
- break;
- default:
- sLog->outError("CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id);
- return 0;
- }
- }
- SpellSchools school = GetFirstSchoolInMask(schoolMask);
- // Flat mod from caster auras by spell school
- powerCost += caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school);
- // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost)
- if (spellInfo->AttributesEx4 & SPELL_ATTR4_SPELL_VS_EXTEND_COST)
- powerCost += caster->GetAttackTime(OFF_ATTACK) / 100;
- // Apply cost mod by spell
- if (Player* modOwner = caster->GetSpellModOwner())
- modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, powerCost);
-
- if (spellInfo->Attributes & SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION)
- powerCost = int32(powerCost / (1.117f * spellInfo->spellLevel / caster->getLevel() -0.1327f));
-
- // PCT mod from user auras by school
- powerCost = int32(powerCost * (1.0f + caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER + school)));
- if (powerCost < 0)
- powerCost = 0;
- return powerCost;
+ return true;
}
-bool IsSpellRequiringFocusedTarget(SpellEntry const* spellInfo)
+bool IsPartOfSkillLine(uint32 skillId, uint32 spellId)
{
- for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS; ++i)
- {
- if (SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_UNIT_TARGET
- || SpellTargetType[spellInfo->EffectImplicitTargetB[i]] == TARGET_TYPE_UNIT_TARGET
- || SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_CHANNEL
- || SpellTargetType[spellInfo->EffectImplicitTargetB[i]] == TARGET_TYPE_CHANNEL
- || SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_DEST_TARGET
- || SpellTargetType[spellInfo->EffectImplicitTargetB[i]] == TARGET_TYPE_DEST_TARGET)
+ SkillLineAbilityMapBounds skillBounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
+ for (SkillLineAbilityMap::const_iterator itr = skillBounds.first; itr != skillBounds.second; ++itr)
+ if (itr->second->skillId == skillId)
return true;
- }
- return false;
-}
-Unit* GetTriggeredSpellCaster(SpellEntry const* spellInfo, Unit* caster, Unit* target)
-{
- if (IsSpellRequiringFocusedTarget(spellInfo))
- return caster;
- return target;
+ return false;
}
-AuraState GetSpellAuraState(SpellEntry const* spellInfo)
+DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, bool triggered)
{
- // Seals
- if (IsSealSpell(spellInfo))
- return AURA_STATE_JUDGEMENT;
-
- // Conflagrate aura state on Immolate and Shadowflame
- if (spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK &&
- // Immolate
- ((spellInfo->SpellFamilyFlags[0] & 4) ||
- // Shadowflame
- (spellInfo->SpellFamilyFlags[2] & 2)))
- return AURA_STATE_CONFLAGRATE;
-
- // Faerie Fire (druid versions)
- if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x400)
- return AURA_STATE_FAERIE_FIRE;
-
- // Sting (hunter's pet ability)
- if (spellInfo->Category == 1133)
- return AURA_STATE_FAERIE_FIRE;
-
- // Victorious
- if (spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellFamilyFlags[1] & 0x00040000)
- return AURA_STATE_WARRIOR_VICTORY_RUSH;
-
- // Swiftmend state on Regrowth & Rejuvenation
- if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x50)
- return AURA_STATE_SWIFTMEND;
-
- // Deadly poison aura state
- if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags[0] & 0x10000)
- return AURA_STATE_DEADLY_POISON;
-
- // Enrage aura state
- if (spellInfo->Dispel == DISPEL_ENRAGE)
- return AURA_STATE_ENRAGE;
-
- // Bleeding aura state
- if (GetAllSpellMechanicMask(spellInfo) & 1<<MECHANIC_BLEED)
- return AURA_STATE_BLEEDING;
-
- if (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST)
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STUN
- || spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_ROOT)
- return AURA_STATE_FROZEN;
+ if (spellproto->IsPositive())
+ return DIMINISHING_NONE;
- switch (spellInfo->Id)
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
- case 71465: // Divine Surge
- return AURA_STATE_UNKNOWN22;
- default:
- break;
+ if (spellproto->Effects[i].ApplyAuraName == SPELL_AURA_MOD_TAUNT)
+ return DIMINISHING_TAUNT;
}
- return AURA_STATE_NONE;
-}
-
-SpellSpecific GetSpellSpecific(SpellEntry const* spellInfo)
-{
- switch (spellInfo->SpellFamilyName)
+ // Explicit Diminishing Groups
+ switch (spellproto->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
{
- // Food / Drinks (mostly)
- if (spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED)
- {
- bool food = false;
- bool drink = false;
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- switch (spellInfo->EffectApplyAuraName[i])
- {
- // Food
- case SPELL_AURA_MOD_REGEN:
- case SPELL_AURA_OBS_MOD_HEALTH:
- food = true;
- break;
- // Drink
- case SPELL_AURA_MOD_POWER_REGEN:
- case SPELL_AURA_OBS_MOD_POWER:
- drink = true;
- break;
- default:
- break;
- }
- }
-
- if (food && drink)
- return SPELL_SPECIFIC_FOOD_AND_DRINK;
- else if (food)
- return SPELL_SPECIFIC_FOOD;
- else if (drink)
- return SPELL_SPECIFIC_DRINK;
- }
- // scrolls effects
- else
- {
- uint32 firstSpell = sSpellMgr->GetFirstSpellInChain(spellInfo->Id);
- switch (firstSpell)
- {
- case 8118: // Strength
- case 8099: // Stamina
- case 8112: // Spirit
- case 8096: // Intellect
- case 8115: // Agility
- case 8091: // Armor
- return SPELL_SPECIFIC_SCROLL;
- case 12880: // Enrage (Enrage)
- case 57518: // Enrage (Wrecking Crew)
- return SPELL_SPECIFIC_WARRIOR_ENRAGE;
- }
- }
+ // Pet charge effects (Infernal Awakening, Demon Charge)
+ if (spellproto->SpellVisual[0] == 2816 && spellproto->SpellIconID == 15)
+ return DIMINISHING_CONTROLLED_STUN;
+ // Gnaw
+ else if (spellproto->Id == 47481)
+ return DIMINISHING_CONTROLLED_STUN;
break;
}
+ // Event spells
+ case SPELLFAMILY_UNK1:
+ return DIMINISHING_NONE;
case SPELLFAMILY_MAGE:
{
- // family flags 18(Molten), 25(Frost/Ice), 28(Mage)
- if (spellInfo->SpellFamilyFlags[0] & 0x12040000)
- return SPELL_SPECIFIC_MAGE_ARMOR;
-
- // Arcane brillance and Arcane intelect (normal check fails because of flags difference)
- if (spellInfo->SpellFamilyFlags[0] & 0x400)
- return SPELL_SPECIFIC_MAGE_ARCANE_BRILLANCE;
-
- if ((spellInfo->SpellFamilyFlags[0] & 0x1000000) && spellInfo->EffectApplyAuraName[0] == SPELL_AURA_MOD_CONFUSE)
- return SPELL_SPECIFIC_MAGE_POLYMORPH;
-
+ // Frostbite
+ if (spellproto->SpellFamilyFlags[1] & 0x80000000)
+ return DIMINISHING_ROOT;
+ // Shattered Barrier
+ else if (spellproto->SpellVisual[0] == 12297)
+ return DIMINISHING_ROOT;
+ // Deep Freeze
+ else if (spellproto->SpellIconID == 2939 && spellproto->SpellVisual[0] == 9963)
+ return DIMINISHING_CONTROLLED_STUN;
+ // Frost Nova / Freeze (Water Elemental)
+ else if (spellproto->SpellIconID == 193)
+ return DIMINISHING_CONTROLLED_ROOT;
+ // Dragon's Breath
+ else if (spellproto->SpellFamilyFlags[0] & 0x800000)
+ return DIMINISHING_DISORIENT;
break;
}
case SPELLFAMILY_WARRIOR:
{
- if (spellInfo->Id == 12292) // Death Wish
- return SPELL_SPECIFIC_WARRIOR_ENRAGE;
-
+ // Improved Hamstring
+ if (spellproto->AttributesEx3 & 0x80000 && spellproto->SpellIconID == 23)
+ return DIMINISHING_ROOT;
+ // Charge Stun (own diminishing)
+ else if (spellproto->SpellFamilyFlags[0] & 0x01000000)
+ return DIMINISHING_CHARGE;
break;
}
case SPELLFAMILY_WARLOCK:
{
- // only warlock curses have this
- if (spellInfo->Dispel == DISPEL_CURSE)
- return SPELL_SPECIFIC_CURSE;
-
- // Warlock (Demon Armor | Demon Skin | Fel Armor)
- if (spellInfo->SpellFamilyFlags[1] & 0x20000020 || spellInfo->SpellFamilyFlags[2] & 0x00000010)
- return SPELL_SPECIFIC_WARLOCK_ARMOR;
-
- //seed of corruption and corruption
- if (spellInfo->SpellFamilyFlags[1] & 0x10 || spellInfo->SpellFamilyFlags[0] & 0x2)
- return SPELL_SPECIFIC_WARLOCK_CORRUPTION;
+ // Death Coil
+ if (spellproto->SpellFamilyFlags[0] & 0x80000)
+ return DIMINISHING_HORROR;
+ // Seduction
+ else if (spellproto->SpellFamilyFlags[1] & 0x10000000)
+ return DIMINISHING_FEAR;
break;
}
case SPELLFAMILY_PRIEST:
{
- // Divine Spirit and Prayer of Spirit
- if (spellInfo->SpellFamilyFlags[0] & 0x20)
- return SPELL_SPECIFIC_PRIEST_DIVINE_SPIRIT;
-
+ // Psychic Horror
+ if (spellproto->SpellFamilyFlags[2] & 0x2000)
+ return DIMINISHING_HORROR;
break;
}
- case SPELLFAMILY_HUNTER:
+ case SPELLFAMILY_DRUID:
{
- // only hunter stings have this
- if (spellInfo->Dispel == DISPEL_POISON)
- return SPELL_SPECIFIC_STING;
-
- // only hunter aspects have this (but not all aspects in hunter family)
- if (spellInfo->SpellFamilyFlags.HasFlag(0x00380000, 0x00440000, 0x00001010))
- return SPELL_SPECIFIC_ASPECT;
-
+ // Pounce
+ if (spellproto->SpellFamilyFlags[0] & 0x20000)
+ return DIMINISHING_OPENING_STUN;
+ // Cyclone
+ else if (spellproto->SpellFamilyFlags[1] & 0x20)
+ return DIMINISHING_CYCLONE;
+ // Entangling Roots
+ // Nature's Grasp
+ else if (spellproto->SpellFamilyFlags[0] & 0x00000200)
+ return DIMINISHING_CONTROLLED_ROOT;
break;
}
- case SPELLFAMILY_PALADIN:
+ case SPELLFAMILY_ROGUE:
{
- if (IsSealSpell(spellInfo))
- return SPELL_SPECIFIC_SEAL;
-
- if (spellInfo->SpellFamilyFlags[0] & 0x00002190)
- return SPELL_SPECIFIC_HAND;
-
- // Judgement of Wisdom, Judgement of Light, Judgement of Justice
- if (spellInfo->Id == 20184 || spellInfo->Id == 20185 || spellInfo->Id == 20186)
- return SPELL_SPECIFIC_JUDGEMENT;
-
- // only paladin auras have this (for palaldin class family)
- if (spellInfo->SpellFamilyFlags[2] & 0x00000020)
- return SPELL_SPECIFIC_AURA;
-
+ // Gouge
+ if (spellproto->SpellFamilyFlags[0] & 0x8)
+ return DIMINISHING_DISORIENT;
+ // Blind
+ else if (spellproto->SpellFamilyFlags[0] & 0x1000000)
+ return DIMINISHING_FEAR;
+ // Cheap Shot
+ else if (spellproto->SpellFamilyFlags[0] & 0x400)
+ return DIMINISHING_OPENING_STUN;
break;
}
- case SPELLFAMILY_SHAMAN:
+ case SPELLFAMILY_HUNTER:
{
- if (IsElementalShield(spellInfo))
- return SPELL_SPECIFIC_ELEMENTAL_SHIELD;
-
+ // Scatter Shot (own diminishing)
+ if ((spellproto->SpellFamilyFlags[0] & 0x40000) && spellproto->SpellIconID == 132)
+ return DIMINISHING_SCATTER_SHOT;
+ // Entrapment (own diminishing)
+ else if (spellproto->SpellVisual[0] == 7484 && spellproto->SpellIconID == 20)
+ return DIMINISHING_ENTRAPMENT;
+ // Wyvern Sting mechanic is MECHANIC_SLEEP but the diminishing is DIMINISHING_DISORIENT
+ else if ((spellproto->SpellFamilyFlags[1] & 0x1000) && spellproto->SpellIconID == 1721)
+ return DIMINISHING_DISORIENT;
+ // Freezing Arrow
+ else if (spellproto->SpellFamilyFlags[0] & 0x8)
+ return DIMINISHING_DISORIENT;
break;
}
-
- case SPELLFAMILY_DEATHKNIGHT:
- if (spellInfo->Id == 48266 || spellInfo->Id == 48263 || spellInfo->Id == 48265)
- //if (spellInfo->Category == 47)
- return SPELL_SPECIFIC_PRESENCE;
+ case SPELLFAMILY_PALADIN:
+ {
+ // Turn Evil
+ if ((spellproto->SpellFamilyFlags[1] & 0x804000) && spellproto->SpellIconID == 309)
+ return DIMINISHING_FEAR;
break;
- }
-
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA)
+ }
+ case SPELLFAMILY_DEATHKNIGHT:
{
- switch (spellInfo->EffectApplyAuraName[i])
- {
- case SPELL_AURA_MOD_CHARM:
- case SPELL_AURA_MOD_POSSESS_PET:
- case SPELL_AURA_MOD_POSSESS:
- case SPELL_AURA_AOE_CHARM:
- return SPELL_SPECIFIC_CHARM;
- case SPELL_AURA_TRACK_CREATURES:
- case SPELL_AURA_TRACK_RESOURCES:
- case SPELL_AURA_TRACK_STEALTHED:
- return SPELL_SPECIFIC_TRACKER;
- case SPELL_AURA_PHASE:
- return SPELL_SPECIFIC_PHASE;
- }
+ // Hungering Cold (no flags)
+ if (spellproto->SpellIconID == 2797)
+ return DIMINISHING_DISORIENT;
+ break;
}
+ default:
+ break;
}
- return SPELL_SPECIFIC_NORMAL;
-}
+ // Lastly - Set diminishing depending on mechanic
+ uint32 mechanic = spellproto->GetAllEffectsMechanicMask();
+ if (mechanic & (1 << MECHANIC_CHARM))
+ return DIMINISHING_MIND_CONTROL;
+ if (mechanic & (1 << MECHANIC_SILENCE))
+ return DIMINISHING_SILENCE;
+ if (mechanic & (1 << MECHANIC_SLEEP))
+ return DIMINISHING_SLEEP;
+ if (mechanic & ((1 << MECHANIC_SAPPED) | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_SHACKLE)))
+ return DIMINISHING_DISORIENT;
+ // Mechanic Knockout, except Blast Wave
+ if (mechanic & (1 << MECHANIC_KNOCKOUT) && spellproto->SpellIconID != 292)
+ return DIMINISHING_DISORIENT;
+ if (mechanic & (1 << MECHANIC_DISARM))
+ return DIMINISHING_DISARM;
+ if (mechanic & (1 << MECHANIC_FEAR))
+ return DIMINISHING_FEAR;
+ if (mechanic & (1 << MECHANIC_STUN))
+ return triggered ? DIMINISHING_STUN : DIMINISHING_CONTROLLED_STUN;
+ if (mechanic & (1 << MECHANIC_BANISH))
+ return DIMINISHING_BANISH;
+ if (mechanic & (1 << MECHANIC_ROOT))
+ return triggered ? DIMINISHING_ROOT : DIMINISHING_CONTROLLED_ROOT;
-// target not allow have more one spell specific from same caster
-bool IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1, SpellSpecific spellSpec2)
-{
- switch (spellSpec1)
- {
- case SPELL_SPECIFIC_SEAL:
- case SPELL_SPECIFIC_HAND:
- case SPELL_SPECIFIC_AURA:
- case SPELL_SPECIFIC_STING:
- case SPELL_SPECIFIC_CURSE:
- case SPELL_SPECIFIC_ASPECT:
- case SPELL_SPECIFIC_JUDGEMENT:
- case SPELL_SPECIFIC_WARLOCK_CORRUPTION:
- return spellSpec1 == spellSpec2;
- default:
- return false;
- }
+ return DIMINISHING_NONE;
}
-bool IsSingleFromSpellSpecificPerTarget(SpellSpecific spellSpec1, SpellSpecific spellSpec2)
+DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group)
{
- switch (spellSpec1)
+ switch (group)
{
- case SPELL_SPECIFIC_PHASE:
- case SPELL_SPECIFIC_TRACKER:
- case SPELL_SPECIFIC_WARLOCK_ARMOR:
- case SPELL_SPECIFIC_MAGE_ARMOR:
- case SPELL_SPECIFIC_ELEMENTAL_SHIELD:
- case SPELL_SPECIFIC_MAGE_POLYMORPH:
- case SPELL_SPECIFIC_PRESENCE:
- case SPELL_SPECIFIC_CHARM:
- case SPELL_SPECIFIC_SCROLL:
- case SPELL_SPECIFIC_WARRIOR_ENRAGE:
- case SPELL_SPECIFIC_MAGE_ARCANE_BRILLANCE:
- case SPELL_SPECIFIC_PRIEST_DIVINE_SPIRIT:
- return spellSpec1 == spellSpec2;
- case SPELL_SPECIFIC_FOOD:
- return spellSpec2 == SPELL_SPECIFIC_FOOD
- || spellSpec2 == SPELL_SPECIFIC_FOOD_AND_DRINK;
- case SPELL_SPECIFIC_DRINK:
- return spellSpec2 == SPELL_SPECIFIC_DRINK
- || spellSpec2 == SPELL_SPECIFIC_FOOD_AND_DRINK;
- case SPELL_SPECIFIC_FOOD_AND_DRINK:
- return spellSpec2 == SPELL_SPECIFIC_FOOD
- || spellSpec2 == SPELL_SPECIFIC_DRINK
- || spellSpec2 == SPELL_SPECIFIC_FOOD_AND_DRINK;
+ case DIMINISHING_TAUNT:
+ case DIMINISHING_CONTROLLED_STUN:
+ case DIMINISHING_STUN:
+ case DIMINISHING_OPENING_STUN:
+ case DIMINISHING_CYCLONE:
+ case DIMINISHING_CHARGE:
+ return DRTYPE_ALL;
default:
- return false;
+ return DRTYPE_PLAYER;
}
}
-bool IsPositiveTarget(uint32 targetA, uint32 targetB)
+DiminishingLevels GetDiminishingReturnsMaxLevel(DiminishingGroup group)
{
- // non-positive targets
- switch (targetA)
+ switch (group)
{
- case TARGET_UNIT_NEARBY_ENEMY:
- case TARGET_UNIT_TARGET_ENEMY:
- case TARGET_UNIT_AREA_ENEMY_SRC:
- case TARGET_UNIT_AREA_ENEMY_DST:
- case TARGET_UNIT_CONE_ENEMY:
- case TARGET_UNIT_AREA_PATH:
- case TARGET_DEST_DYNOBJ_ENEMY:
- case TARGET_DST_TARGET_ENEMY:
- return false;
+ case DIMINISHING_TAUNT:
+ return DIMINISHING_LEVEL_TAUNT_IMMUNE;
default:
- break;
+ return DIMINISHING_LEVEL_IMMUNE;
}
- if (targetB)
- return IsPositiveTarget(targetB, 0);
- return true;
}
-bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) const
+int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellInfo const* spellproto)
{
- SpellEntry const* spellproto = sSpellStore.LookupEntry(spellId);
- if (!spellproto)
- return false;
-
- // not found a single positive spell with this attribute
- if (spellproto->Attributes & SPELL_ATTR0_NEGATIVE_1)
- return false;
+ if (!IsDiminishingReturnsGroupDurationLimited(group))
+ return 0;
+ // Explicit diminishing duration
switch (spellproto->SpellFamilyName)
{
- case SPELLFAMILY_GENERIC:
- switch (spellId)
- {
- case 34700: // Allergic Reaction
- case 61716: // Rabbit Costume
- case 61734: // Noblegarden Bunny
- case 61987: // Avenging Wrath Marker
- case 61988: // Divine Shield exclude aura
- case 62532: // Conservator's Grip
- return false;
- case 30877: // Tag Murloc
- case 62344: // Fists of Stone
- return true;
- default:
- break;
- }
- break;
- case SPELLFAMILY_MAGE:
- // Amplify Magic, Dampen Magic
- if (spellproto->SpellFamilyFlags[0] == 0x00002000)
- return true;
- // Ignite
- if (spellproto->SpellIconID == 45)
- return true;
- break;
- case SPELLFAMILY_PRIEST:
- switch (spellId)
- {
- case 64844: // Divine Hymn
- case 64904: // Hymn of Hope
- case 47585: // Dispersion
- return true;
- default:
- break;
- }
+ case SPELLFAMILY_DRUID:
+ {
+ // Faerie Fire - limit to 40 seconds in PvP (3.1)
+ if (spellproto->SpellFamilyFlags[0] & 0x400)
+ return 40 * IN_MILLISECONDS;
break;
+ }
case SPELLFAMILY_HUNTER:
- // Aspect of the Viper
- if (spellId == 34074)
- return true;
+ {
+ // Wyvern Sting
+ if (spellproto->SpellFamilyFlags[1] & 0x1000)
+ return 6 * IN_MILLISECONDS;
+ // Hunter's Mark
+ if (spellproto->SpellFamilyFlags[0] & 0x400)
+ return 120 * IN_MILLISECONDS;
break;
- case SPELLFAMILY_SHAMAN:
- if (spellId == 30708)
- return false;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Repentance - limit to 6 seconds in PvP
+ if (spellproto->SpellFamilyFlags[0] & 0x4)
+ return 6 * IN_MILLISECONDS;
break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Banish - limit to 6 seconds in PvP
+ if (spellproto->SpellFamilyFlags[1] & 0x8000000)
+ return 6 * IN_MILLISECONDS;
+ // Curse of Tongues - limit to 12 seconds in PvP
+ else if (spellproto->SpellFamilyFlags[2] & 0x800)
+ return 12 * IN_MILLISECONDS;
+ // Curse of Elements - limit to 120 seconds in PvP
+ else if (spellproto->SpellFamilyFlags[1] & 0x200)
+ return 120 * IN_MILLISECONDS;
+ break;
+ }
default:
break;
}
- switch (spellproto->Mechanic)
+ return 10 * IN_MILLISECONDS;
+}
+
+bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group)
+{
+ switch (group)
{
- case MECHANIC_IMMUNE_SHIELD:
+ case DIMINISHING_CONTROLLED_STUN:
+ case DIMINISHING_STUN:
+ case DIMINISHING_ENTRAPMENT:
+ case DIMINISHING_CONTROLLED_ROOT:
+ case DIMINISHING_ROOT:
+ case DIMINISHING_FEAR:
+ case DIMINISHING_MIND_CONTROL:
+ case DIMINISHING_DISORIENT:
+ case DIMINISHING_CYCLONE:
+ case DIMINISHING_BANISH:
+ case DIMINISHING_LIMITONLY:
+ case DIMINISHING_OPENING_STUN:
+ case DIMINISHING_HORROR:
+ case DIMINISHING_SLEEP:
return true;
default:
- break;
+ return false;
}
+}
- // Special case: effects which determine positivity of whole spell
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- if (spellproto->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH)
- return true;
- }
+SpellMgr::SpellMgr()
+{
+}
- switch (spellproto->Effect[effIndex])
- {
- case SPELL_EFFECT_DUMMY:
- // some explicitly required dummy effect sets
- switch (spellId)
- {
- case 28441:
- return false; // AB Effect 000
- default:
- break;
- }
- break;
- // always positive effects (check before target checks that provided non-positive result in some case for positive effects)
- case SPELL_EFFECT_HEAL:
- case SPELL_EFFECT_LEARN_SPELL:
- case SPELL_EFFECT_SKILL_STEP:
- case SPELL_EFFECT_HEAL_PCT:
- case SPELL_EFFECT_ENERGIZE_PCT:
- return true;
+SpellMgr::~SpellMgr()
+{
+ UnloadSpellInfoStore();
+}
- // non-positive aura use
- case SPELL_EFFECT_APPLY_AURA:
- case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
+/// Some checks for spells, to prevent adding deprecated/broken spells for trainers, spell book, etc
+bool SpellMgr::IsSpellValid(SpellInfo const *spellInfo, Player *pl, bool msg)
+{
+ // not exist
+ if (!spellInfo)
+ return false;
+
+ bool need_check_reagents = false;
+
+ // check effects
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ switch (spellInfo->Effects[i].Effect)
{
- switch (spellproto->EffectApplyAuraName[effIndex])
+ case 0:
+ continue;
+
+ // craft spell for crafting non-existed item (break client recipes list show)
+ case SPELL_EFFECT_CREATE_ITEM:
+ case SPELL_EFFECT_CREATE_ITEM_2:
{
- case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from bas point sign (negative -> negative)
- case SPELL_AURA_MOD_STAT:
- case SPELL_AURA_MOD_SKILL:
- case SPELL_AURA_MOD_DODGE_PERCENT:
- case SPELL_AURA_MOD_HEALING_PCT:
- case SPELL_AURA_MOD_HEALING_DONE:
- case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
- if (SpellMgr::CalculateSpellEffectAmount(spellproto, effIndex) < 0)
- return false;
- break;
- case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from bas point sign (positive -> negative)
- if (SpellMgr::CalculateSpellEffectAmount(spellproto, effIndex) > 0)
- return false;
- break;
- case SPELL_AURA_MOD_CRIT_PCT:
- case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
- if (SpellMgr::CalculateSpellEffectAmount(spellproto, effIndex) > 0)
- return true; // some expected positive spells have SPELL_ATTR1_NEGATIVE
- break;
- case SPELL_AURA_ADD_TARGET_TRIGGER:
- return true;
- case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
- case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
- if (!deep)
+ if (spellInfo->Effects[i].ItemType == 0)
+ {
+ // skip auto-loot crafting spells, its not need explicit item info (but have special fake items sometime)
+ if (!spellInfo->IsLootCrafting())
{
- uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex];
- SpellEntry const* spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId);
-
- if (spellTriggeredProto)
+ if (msg)
{
- // non-positive targets of main spell return early
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- if (!spellTriggeredProto->Effect[i])
- continue;
- // if non-positive trigger cast targeted to positive target this main cast is non-positive
- // this will place this spell auras as debuffs
- if (IsPositiveTarget(spellTriggeredProto->EffectImplicitTargetA[effIndex], spellTriggeredProto->EffectImplicitTargetB[effIndex]) && !_isPositiveEffect(spellTriggeredId, i, true))
- return false;
- }
+ if (pl)
+ ChatHandler(pl).PSendSysMessage("Craft spell %u not have create item entry.", spellInfo->Id);
+ else
+ sLog->outErrorDb("Craft spell %u not have create item entry.", spellInfo->Id);
}
- }
- case SPELL_AURA_PROC_TRIGGER_SPELL:
- // many positive auras have negative triggered spells at damage for example and this not make it negative (it can be canceled for example)
- break;
- case SPELL_AURA_MOD_STUN: //have positive and negative spells, we can't sort its correctly at this moment.
- if (effIndex == 0 && spellproto->Effect[1] == 0 && spellproto->Effect[2] == 0)
- return false; // but all single stun aura spells is negative
- break;
- case SPELL_AURA_MOD_PACIFY_SILENCE:
- if (spellproto->Id == 24740) // Wisp Costume
- return true;
- return false;
- case SPELL_AURA_MOD_ROOT:
- case SPELL_AURA_MOD_SILENCE:
- case SPELL_AURA_GHOST:
- case SPELL_AURA_PERIODIC_LEECH:
- case SPELL_AURA_MOD_STALKED:
- case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
- case SPELL_AURA_PREVENT_RESSURECTION:
- return false;
- case SPELL_AURA_PERIODIC_DAMAGE: // used in positive spells also.
- // part of negative spell if casted at self (prevent cancel)
- if (spellproto->EffectImplicitTargetA[effIndex] == TARGET_UNIT_CASTER)
- return false;
- break;
- case SPELL_AURA_MOD_DECREASE_SPEED: // used in positive spells also
- // part of positive spell if casted at self
- if (spellproto->EffectImplicitTargetA[effIndex] != TARGET_UNIT_CASTER)
return false;
- // but not this if this first effect (didn't find better check)
- if (spellproto->Attributes & SPELL_ATTR0_NEGATIVE_1 && effIndex == 0)
- return false;
- break;
- case SPELL_AURA_MECHANIC_IMMUNITY:
+ }
+
+ }
+ // also possible IsLootCrafting case but fake item must exist anyway
+ else if (!sObjectMgr->GetItemTemplate(spellInfo->Effects[i].ItemType))
{
- // non-positive immunities
- switch (spellproto->EffectMiscValue[effIndex])
+ if (msg)
{
- case MECHANIC_BANDAGE:
- case MECHANIC_SHIELD:
- case MECHANIC_MOUNT:
- case MECHANIC_INVULNERABILITY:
- return false;
- default:
- break;
+ if (pl)
+ ChatHandler(pl).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Effects[i].ItemType);
+ else
+ sLog->outErrorDb("Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Effects[i].ItemType);
}
- break;
+ return false;
}
- case SPELL_AURA_ADD_FLAT_MODIFIER: // mods
- case SPELL_AURA_ADD_PCT_MODIFIER:
+
+ need_check_reagents = true;
+ break;
+ }
+ case SPELL_EFFECT_LEARN_SPELL:
+ {
+ SpellInfo const* spellInfo2 = sSpellMgr->GetSpellInfo(spellInfo->Effects[i].TriggerSpell);
+ if (!IsSpellValid(spellInfo2, pl, msg))
{
- // non-positive mods
- switch (spellproto->EffectMiscValue[effIndex])
+ if (msg)
{
- case SPELLMOD_COST: // dependent from bas point sign (negative -> positive)
- if (SpellMgr::CalculateSpellEffectAmount(spellproto, effIndex) > 0)
- {
- if (!deep)
- {
- bool negative = true;
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- if (i != effIndex)
- if (_isPositiveEffect(spellId, i, true))
- {
- negative = false;
- break;
- }
- }
- if (negative)
- return false;
- }
- }
- break;
- default:
- break;
+ if (pl)
+ ChatHandler(pl).PSendSysMessage("Spell %u learn to broken spell %u, and then...", spellInfo->Id, spellInfo->Effects[i].TriggerSpell);
+ else
+ sLog->outErrorDb("Spell %u learn to invalid spell %u, and then...", spellInfo->Id, spellInfo->Effects[i].TriggerSpell);
}
- break;
+ return false;
}
- default:
- break;
+ break;
}
- break;
}
- default:
- break;
}
- // non-positive targets
- if (!IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex], spellproto->EffectImplicitTargetB[effIndex]))
- return false;
-
- if (!deep && spellproto->EffectTriggerSpell[effIndex]
- && !spellproto->EffectApplyAuraName[effIndex]
- && IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex], spellproto->EffectImplicitTargetB[effIndex])
- && !_isPositiveSpell(spellproto->EffectTriggerSpell[effIndex], true))
- return false;
+ if (need_check_reagents)
+ {
+ for (uint8 j = 0; j < MAX_SPELL_REAGENTS; ++j)
+ {
+ if (spellInfo->Reagent[j] > 0 && !sObjectMgr->GetItemTemplate(spellInfo->Reagent[j]))
+ {
+ if (msg)
+ {
+ if (pl)
+ ChatHandler(pl).PSendSysMessage("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Reagent[j]);
+ else
+ sLog->outErrorDb("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Reagent[j]);
+ }
+ return false;
+ }
+ }
+ }
- // ok, positive
return true;
}
-bool IsPositiveSpell(uint32 spellId)
+uint32 SpellMgr::GetSpellDifficultyId(uint32 spellId) const
{
- if (!sSpellStore.LookupEntry(spellId)) // non-existing spells
- return false;
- return !(sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE);
+ SpellDifficultySearcherMap::const_iterator i = mSpellDifficultySearcherMap.find(spellId);
+ return i == mSpellDifficultySearcherMap.end() ? 0 : (*i).second;
}
-bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
+void SpellMgr::SetSpellDifficultyId(uint32 spellId, uint32 id)
{
- if (!sSpellStore.LookupEntry(spellId))
- return false;
- switch (effIndex)
- {
- default:
- case 0:
- return !(sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE_EFF0);
- case 1:
- return !(sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE_EFF1);
- case 2:
- return !(sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE_EFF2);
- }
+ mSpellDifficultySearcherMap[spellId] = id;
}
-bool SpellMgr::_isPositiveSpell(uint32 spellId, bool deep) const
+uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, Unit* caster) const
{
- SpellEntry const* spellproto = sSpellStore.LookupEntry(spellId);
- if (!spellproto)
- return false;
+ if (!GetSpellInfo(spellId))
+ return spellId;
- // spells with at least one negative effect are considered negative
- // some self-applied spells have negative effects but in self casting case negative check ignored.
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- if (!_isPositiveEffect(spellId, i, deep))
- return false;
- return true;
-}
-
-bool IsSingleTargetSpell(SpellEntry const* spellInfo)
-{
- // all other single target spells have if it has AttributesEx5
- if (spellInfo->AttributesEx5 & SPELL_ATTR5_SINGLE_TARGET_SPELL)
- return true;
+ if (!caster || !caster->GetMap() || !caster->GetMap()->IsDungeon())
+ return spellId;
- switch (GetSpellSpecific(spellInfo))
+ uint32 mode = uint32(caster->GetMap()->GetSpawnMode());
+ if (mode >= MAX_DIFFICULTY)
{
- case SPELL_SPECIFIC_JUDGEMENT:
- return true;
- default:
- break;
+ sLog->outError("SpellMgr::GetSpellIdForDifficulty: Incorrect Difficulty for spell %u.", spellId);
+ return spellId; //return source spell
}
- return false;
-}
+ uint32 difficultyId = GetSpellDifficultyId(spellId);
+ if (!difficultyId)
+ return spellId; //return source spell, it has only REGULAR_DIFFICULTY
-bool IsSingleTargetSpells(SpellEntry const* spellInfo1, SpellEntry const* spellInfo2)
-{
- // TODO - need better check
- // Equal icon and spellfamily
- if (spellInfo1->SpellFamilyName == spellInfo2->SpellFamilyName &&
- spellInfo1->SpellIconID == spellInfo2->SpellIconID)
- return true;
-
- // TODO - need found Judgements rule
- SpellSpecific spec1 = GetSpellSpecific(spellInfo1);
- // spell with single target specific types
- switch (spec1)
+ SpellDifficultyEntry const *difficultyEntry = sSpellDifficultyStore.LookupEntry(difficultyId);
+ if (!difficultyEntry)
{
- case SPELL_SPECIFIC_JUDGEMENT:
- case SPELL_SPECIFIC_MAGE_POLYMORPH:
- if (GetSpellSpecific(spellInfo2) == spec1)
- return true;
- break;
- default:
- break;
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellIdForDifficulty: SpellDifficultyEntry not found for spell %u. This should never happen.", spellId);
+ return spellId; //return source spell
}
- return false;
-}
-
-SpellCastResult GetErrorAtShapeshiftedCast(SpellEntry const* spellInfo, uint32 form)
-{
- // talents that learn spells can have stance requirements that need ignore
- // (this requirement only for client-side stance show in talent description)
- if (GetTalentSpellCost(spellInfo->Id) > 0 &&
- (spellInfo->Effect[0] == SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[1] == SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[2] == SPELL_EFFECT_LEARN_SPELL))
- return SPELL_CAST_OK;
-
- uint32 stanceMask = (form ? 1 << (form - 1) : 0);
-
- if (stanceMask & spellInfo->StancesNot) // can explicitly not be casted in this stance
- return SPELL_FAILED_NOT_SHAPESHIFT;
-
- if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance
- return SPELL_CAST_OK;
-
- bool actAsShifted = false;
- SpellShapeshiftEntry const* shapeInfo = NULL;
- if (form > 0)
+ if (difficultyEntry->SpellID[mode] <= 0 && mode > DUNGEON_DIFFICULTY_HEROIC)
{
- shapeInfo = sSpellShapeshiftStore.LookupEntry(form);
- if (!shapeInfo)
- {
- sLog->outError("GetErrorAtShapeshiftedCast: unknown shapeshift %u", form);
- return SPELL_CAST_OK;
- }
- actAsShifted = !(shapeInfo->flags1 & 1); // shapeshift acts as normal form for spells
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellIdForDifficulty: spell %u mode %u spell is NULL, using mode %u", spellId, mode, mode - 2);
+ mode -= 2;
}
- if (actAsShifted)
- {
- if (spellInfo->Attributes & SPELL_ATTR0_NOT_SHAPESHIFT) // not while shapeshifted
- return SPELL_FAILED_NOT_SHAPESHIFT;
- else if (spellInfo->Stances != 0) // needs other shapeshift
- return SPELL_FAILED_ONLY_SHAPESHIFT;
- }
- else
+ if (difficultyEntry->SpellID[mode] <= 0)
{
- // needs shapeshift
- if (!(spellInfo->AttributesEx2 & SPELL_ATTR2_NOT_NEED_SHAPESHIFT) && spellInfo->Stances != 0)
- return SPELL_FAILED_ONLY_SHAPESHIFT;
+ sLog->outErrorDb("SpellMgr::GetSpellIdForDifficulty: spell %u mode %u spell is 0. Check spelldifficulty_dbc!", spellId, mode);
+ return spellId;
}
- // Check if stance disables cast of not-stance spells
- // Example: cannot cast any other spells in zombie or ghoul form
- // TODO: Find a way to disable use of these spells clientside
- if (shapeInfo && shapeInfo->flags1 & 0x400)
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellIdForDifficulty: spellid for spell %u in mode %u is %d", spellId, mode, difficultyEntry->SpellID[mode]);
+ return uint32(difficultyEntry->SpellID[mode]);
+}
+
+SpellInfo const* SpellMgr::GetSpellForDifficultyFromSpell(SpellInfo const* spell, Unit* caster) const
+{
+ uint32 newSpellId = GetSpellIdForDifficulty(spell->Id, caster);
+ SpellInfo const* newSpell = GetSpellInfo(newSpellId);
+ if (!newSpell)
{
- if (!(stanceMask & spellInfo->Stances))
- return SPELL_FAILED_ONLY_SHAPESHIFT;
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellForDifficultyFromSpell: spell %u not found. Check spelldifficulty_dbc!", newSpellId);
+ return spell;
}
- return SPELL_CAST_OK;
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellForDifficultyFromSpell: Spell id for instance mode is %u (original %u)", newSpell->Id, spell->Id);
+ return newSpell;
}
-void SpellMgr::LoadSpellTargetPositions()
+SpellChainNode const* SpellMgr::GetSpellChainNode(uint32 spell_id) const
{
- uint32 oldMSTime = getMSTime();
+ SpellChainMap::const_iterator itr = mSpellChains.find(spell_id);
+ if (itr == mSpellChains.end())
+ return NULL;
- mSpellTargetPositions.clear(); // need for reload case
+ return &itr->second;
+}
- // 0 1 2 3 4 5
- QueryResult result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM spell_target_position");
- if (!result)
- {
- sLog->outString(">> Loaded 0 spell target coordinates. DB table `spell_target_position` is empty.");
- sLog->outString();
- return;
- }
+uint32 SpellMgr::GetFirstSpellInChain(uint32 spell_id) const
+{
+ if (SpellChainNode const* node = GetSpellChainNode(spell_id))
+ return node->first->Id;
- uint32 count = 0;
+ return spell_id;
+}
- do
- {
- Field *fields = result->Fetch();
+uint32 SpellMgr::GetLastSpellInChain(uint32 spell_id) const
+{
+ if (SpellChainNode const* node = GetSpellChainNode(spell_id))
+ return node->last->Id;
- uint32 Spell_ID = fields[0].GetUInt32();
+ return spell_id;
+}
- SpellTargetPosition st;
+uint32 SpellMgr::GetNextSpellInChain(uint32 spell_id) const
+{
+ if (SpellChainNode const* node = GetSpellChainNode(spell_id))
+ return node->next ? node->next->Id : NULL;
- st.target_mapId = fields[1].GetUInt32();
- st.target_X = fields[2].GetFloat();
- st.target_Y = fields[3].GetFloat();
- st.target_Z = fields[4].GetFloat();
- st.target_Orientation = fields[5].GetFloat();
+ return 0;
+}
- MapEntry const* mapEntry = sMapStore.LookupEntry(st.target_mapId);
- if (!mapEntry)
- {
- sLog->outErrorDb("Spell (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.", Spell_ID, st.target_mapId);
- continue;
- }
+uint32 SpellMgr::GetPrevSpellInChain(uint32 spell_id) const
+{
+ if (SpellChainNode const* node = GetSpellChainNode(spell_id))
+ return node->prev ? node->prev->Id : NULL;
- if (st.target_X==0 && st.target_Y==0 && st.target_Z==0)
- {
- sLog->outErrorDb("Spell (ID:%u) target coordinates not provided.", Spell_ID);
- continue;
- }
+ return 0;
+}
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(Spell_ID);
- if (!spellInfo)
- {
- sLog->outErrorDb("Spell (ID:%u) listed in `spell_target_position` does not exist.", Spell_ID);
- continue;
- }
+uint8 SpellMgr::GetSpellRank(uint32 spell_id) const
+{
+ if (SpellChainNode const* node = GetSpellChainNode(spell_id))
+ return node->rank;
- bool found = false;
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- if (spellInfo->EffectImplicitTargetA[i] == TARGET_DST_DB || spellInfo->EffectImplicitTargetB[i] == TARGET_DST_DB)
- {
- // additional requirements
- if (spellInfo->Effect[i]==SPELL_EFFECT_BIND && spellInfo->EffectMiscValue[i])
- {
- uint32 area_id = sMapMgr->GetAreaId(st.target_mapId, st.target_X, st.target_Y, st.target_Z);
- if (area_id != uint32(spellInfo->EffectMiscValue[i]))
- {
- sLog->outErrorDb("Spell (Id: %u) listed in `spell_target_position` expected point to zone %u bit point to zone %u.", Spell_ID, spellInfo->EffectMiscValue[i], area_id);
- break;
- }
- }
+ return 0;
+}
- found = true;
- break;
- }
- }
- if (!found)
- {
- sLog->outErrorDb("Spell (Id: %u) listed in `spell_target_position` does not have target TARGET_DST_DB (17).", Spell_ID);
- continue;
- }
+uint32 SpellMgr::GetSpellWithRank(uint32 spell_id, uint32 rank, bool strict) const
+{
+ if (SpellChainNode const* node = GetSpellChainNode(spell_id))
+ {
+ if (rank != node->rank)
+ return GetSpellWithRank(node->rank < rank ? node->next->Id : node->prev->Id, rank, strict);
+ }
+ else if (strict && rank > 1)
+ return 0;
+ return spell_id;
+}
- mSpellTargetPositions[Spell_ID] = st;
- ++count;
+SpellRequiredMapBounds SpellMgr::GetSpellsRequiredForSpellBounds(uint32 spell_id) const
+{
+ return SpellRequiredMapBounds(mSpellReq.lower_bound(spell_id), mSpellReq.upper_bound(spell_id));
+}
- } while (result->NextRow());
+SpellsRequiringSpellMapBounds SpellMgr::GetSpellsRequiringSpellBounds(uint32 spell_id) const
+{
+ return SpellsRequiringSpellMapBounds(mSpellsReqSpell.lower_bound(spell_id), mSpellsReqSpell.upper_bound(spell_id));
+}
- // Check all spells
- for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
+bool SpellMgr::IsSpellRequiringSpell(uint32 spellid, uint32 req_spellid) const
+{
+ SpellsRequiringSpellMapBounds spellsRequiringSpell = GetSpellsRequiringSpellBounds(req_spellid);
+ for (SpellsRequiringSpellMap::const_iterator itr = spellsRequiringSpell.first; itr != spellsRequiringSpell.second; ++itr)
{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(i);
- if (!spellInfo)
- continue;
-
- bool found = false;
- for (int j = 0; j < MAX_SPELL_EFFECTS; ++j)
- {
- switch (spellInfo->EffectImplicitTargetA[j])
- {
- case TARGET_DST_DB:
- found = true;
- break;
- }
- if (found)
- break;
- switch (spellInfo->EffectImplicitTargetB[j])
- {
- case TARGET_DST_DB:
- found = true;
- break;
- }
- if (found)
- break;
- }
- if (found)
- {
-// if (!sSpellMgr->GetSpellTargetPosition(i))
-// sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell (ID: %u) does not have record in `spell_target_position`", i);
- }
+ if (itr->second == spellid)
+ return true;
}
+ return false;
+}
- sLog->outString(">> Loaded %u spell teleport coordinates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
+const SpellsRequiringSpellMap SpellMgr::GetSpellsRequiringSpell()
+{
+ return this->mSpellsReqSpell;
}
-bool SpellMgr::IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const
+uint32 SpellMgr::GetSpellRequired(uint32 spell_id) const
{
- // false for spellInfo == NULL
- if (!spellInfo || !mod)
- return false;
+ SpellRequiredMap::const_iterator itr = mSpellReq.find(spell_id);
- SpellEntry const* affect_spell = sSpellStore.LookupEntry(mod->spellId);
- // False if affect_spell == NULL or spellFamily not equal
- if (!affect_spell || affect_spell->SpellFamilyName != spellInfo->SpellFamilyName)
- return false;
+ if (itr == mSpellReq.end())
+ return 0;
- // true
- if (mod->mask & spellInfo->SpellFamilyFlags)
- return true;
+ return itr->second;
+}
- return false;
+SpellLearnSkillNode const* SpellMgr::GetSpellLearnSkill(uint32 spell_id) const
+{
+ SpellLearnSkillMap::const_iterator itr = mSpellLearnSkills.find(spell_id);
+ if (itr != mSpellLearnSkills.end())
+ return &itr->second;
+ else
+ return NULL;
}
-void SpellMgr::LoadSpellProcEvents()
+SpellLearnSpellMapBounds SpellMgr::GetSpellLearnSpellMapBounds(uint32 spell_id) const
{
- uint32 oldMSTime = getMSTime();
+ return SpellLearnSpellMapBounds(mSpellLearnSpells.lower_bound(spell_id), mSpellLearnSpells.upper_bound(spell_id));
+}
- mSpellProcEventMap.clear(); // need for reload case
+bool SpellMgr::IsSpellLearnSpell(uint32 spell_id) const
+{
+ return mSpellLearnSpells.find(spell_id) != mSpellLearnSpells.end();
+}
- uint32 count = 0;
+bool SpellMgr::IsSpellLearnToSpell(uint32 spell_id1, uint32 spell_id2) const
+{
+ SpellLearnSpellMapBounds bounds = GetSpellLearnSpellMapBounds(spell_id1);
+ for (SpellLearnSpellMap::const_iterator i = bounds.first; i != bounds.second; ++i)
+ if (i->second.spell == spell_id2)
+ return true;
+ return false;
+}
- // 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)
+SpellTargetPosition const* SpellMgr::GetSpellTargetPosition(uint32 spell_id) const
+{
+ SpellTargetPositionMap::const_iterator itr = mSpellTargetPositions.find(spell_id);
+ if (itr != mSpellTargetPositions.end())
+ return &itr->second;
+ return NULL;
+}
+
+SpellSpellGroupMapBounds SpellMgr::GetSpellSpellGroupMapBounds(uint32 spell_id) const
+{
+ spell_id = GetFirstSpellInChain(spell_id);
+ return SpellSpellGroupMapBounds(mSpellSpellGroup.lower_bound(spell_id), mSpellSpellGroup.upper_bound(spell_id));
+}
+
+uint32 SpellMgr::IsSpellMemberOfSpellGroup(uint32 spellid, SpellGroup groupid) const
+{
+ SpellSpellGroupMapBounds spellGroup = GetSpellSpellGroupMapBounds(spellid);
+ for (SpellSpellGroupMap::const_iterator itr = spellGroup.first; itr != spellGroup.second ; ++itr)
{
- sLog->outString(">> Loaded %u spell proc event conditions", count);
- sLog->outString();
- return;
+ if (itr->second == groupid)
+ return true;
}
+ return false;
+}
- uint32 customProc = 0;
- do
- {
- Field *fields = result->Fetch();
+SpellGroupSpellMapBounds SpellMgr::GetSpellGroupSpellMapBounds(SpellGroup group_id) const
+{
+ return SpellGroupSpellMapBounds(mSpellGroupSpell.lower_bound(group_id), mSpellGroupSpell.upper_bound(group_id));
+}
- uint32 entry = fields[0].GetUInt32();
+void SpellMgr::GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells) const
+{
+ std::set<SpellGroup> usedGroups;
+ GetSetOfSpellsInSpellGroup(group_id, foundSpells, usedGroups);
+}
- SpellEntry const* spell = sSpellStore.LookupEntry(entry);
- if (!spell)
+void SpellMgr::GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells, std::set<SpellGroup>& usedGroups) const
+{
+ if (usedGroups.find(group_id) != usedGroups.end())
+ return;
+ usedGroups.insert(group_id);
+
+ SpellGroupSpellMapBounds groupSpell = GetSpellGroupSpellMapBounds(group_id);
+ for (SpellGroupSpellMap::const_iterator itr = groupSpell.first; itr != groupSpell.second ; ++itr)
+ {
+ if (itr->second < 0)
{
- sLog->outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry);
- continue;
+ SpellGroup currGroup = (SpellGroup)abs(itr->second);
+ GetSetOfSpellsInSpellGroup(currGroup, foundSpells, usedGroups);
}
+ else
+ {
+ foundSpells.insert(itr->second);
+ }
+ }
+}
- SpellProcEventEntry spe;
-
- spe.schoolMask = fields[1].GetUInt32();
- spe.spellFamilyName = fields[2].GetUInt32();
- spe.spellFamilyMask[0] = fields[3].GetUInt32();
- spe.spellFamilyMask[1] = fields[4].GetUInt32();
- spe.spellFamilyMask[2] = fields[5].GetUInt32();
- spe.procFlags = fields[6].GetUInt32();
- spe.procEx = fields[7].GetUInt32();
- spe.ppmRate = fields[8].GetFloat();
- spe.customChance = fields[9].GetFloat();
- spe.cooldown = fields[10].GetUInt32();
-
- mSpellProcEventMap[entry] = spe;
-
- if (spell->procFlags == 0)
+SpellGroupStackRule SpellMgr::CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const
+{
+ uint32 spellid_1 = spellInfo1->GetFirstRankSpell()->Id;
+ uint32 spellid_2 = spellInfo2->GetFirstRankSpell()->Id;
+ if (spellid_1 == spellid_2)
+ return SPELL_GROUP_STACK_RULE_DEFAULT;
+ // find SpellGroups which are common for both spells
+ SpellSpellGroupMapBounds spellGroup1 = GetSpellSpellGroupMapBounds(spellid_1);
+ std::set<SpellGroup> groups;
+ for (SpellSpellGroupMap::const_iterator itr = spellGroup1.first; itr != spellGroup1.second ; ++itr)
+ {
+ if (IsSpellMemberOfSpellGroup(spellid_2, itr->second))
{
- if (spe.procFlags == 0)
+ bool add = true;
+ SpellGroupSpellMapBounds groupSpell = GetSpellGroupSpellMapBounds(itr->second);
+ for (SpellGroupSpellMap::const_iterator itr2 = groupSpell.first; itr2 != groupSpell.second ; ++itr2)
{
- sLog->outErrorDb("Spell %u listed in `spell_proc_event` probally not triggered spell", entry);
- continue;
+ if (itr2->second < 0)
+ {
+ SpellGroup currGroup = (SpellGroup)abs(itr2->second);
+ if (IsSpellMemberOfSpellGroup(spellid_1, currGroup) && IsSpellMemberOfSpellGroup(spellid_2, currGroup))
+ {
+ add = false;
+ break;
+ }
+ }
}
- customProc++;
+ if (add)
+ groups.insert(itr->second);
}
- ++count;
- } while (result->NextRow());
+ }
- if (customProc)
- sLog->outString(">> Loaded %u extra and %u custom spell proc event conditions in %u ms", count, customProc, GetMSTimeDiffToNow(oldMSTime));
- else
- sLog->outString(">> Loaded %u extra spell proc event conditions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
+ SpellGroupStackRule rule = SPELL_GROUP_STACK_RULE_DEFAULT;
+
+ for (std::set<SpellGroup>::iterator itr = groups.begin() ; itr!= groups.end() ; ++itr)
+ {
+ SpellGroupStackMap::const_iterator found = mSpellGroupStack.find(*itr);
+ if (found != mSpellGroupStack.end())
+ rule = found->second;
+ if (rule)
+ break;
+ }
+ return rule;
+}
+
+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(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellEntry const* procSpell, uint32 procFlags, uint32 procExtra, bool active)
+bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active)
{
// No extra req need
uint32 procEvent_procEx = PROC_EX_NONE;
@@ -1464,146 +850,12 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr
return false;
}
-void SpellMgr::LoadSpellProcs()
+SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const
{
- uint32 oldMSTime = getMSTime();
-
- mSpellProcMap.clear(); // need for reload case
-
- uint32 count = 0;
-
- // 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");
- if (!result)
- {
- sLog->outString(">> Loaded %u spell proc conditions and data", count);
- sLog->outString();
- return;
- }
-
- do
- {
- Field* fields = result->Fetch();
-
- int32 spellId = fields[0].GetInt32();
-
- bool allRanks = false;
- if (spellId <=0)
- {
- allRanks = true;
- spellId = -spellId;
- }
-
- SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellId);
- if (!spellEntry)
- {
- sLog->outErrorDb("Spell %u listed in `spell_proc` does not exist", spellId);
- continue;
- }
-
- if (allRanks)
- {
- if (sSpellMgr->GetFirstSpellInChain(spellId) != uint32(spellId))
- {
- sLog->outErrorDb("Spell %u listed in `spell_proc` is not first rank of spell.", fields[0].GetInt32());
- continue;
- }
- }
-
- SpellProcEntry baseProcEntry;
-
- baseProcEntry.schoolMask = fields[1].GetUInt32();
- baseProcEntry.spellFamilyName = fields[2].GetUInt32();
- baseProcEntry.spellFamilyMask[0] = fields[3].GetUInt32();
- baseProcEntry.spellFamilyMask[1] = fields[4].GetUInt32();
- baseProcEntry.spellFamilyMask[2] = fields[5].GetUInt32();
- baseProcEntry.typeMask = fields[6].GetUInt32();
- baseProcEntry.spellTypeMask = fields[7].GetUInt32();
- baseProcEntry.spellPhaseMask = fields[8].GetUInt32();
- baseProcEntry.hitMask = fields[9].GetUInt32();
- baseProcEntry.attributesMask = fields[10].GetUInt32();
- baseProcEntry.ratePerMinute = fields[11].GetFloat();
- baseProcEntry.chance = fields[12].GetFloat();
- float cooldown = fields[13].GetFloat();
- baseProcEntry.cooldown = uint32(cooldown);
- baseProcEntry.charges = fields[14].GetUInt32();
-
- while(true)
- {
- if (mSpellProcMap.find(spellId) != mSpellProcMap.end())
- {
- sLog->outErrorDb("Spell %u listed in `spell_proc` has duplicate entry in the table", spellId);
- break;
- }
- SpellProcEntry procEntry = SpellProcEntry(baseProcEntry);
-
- // take defaults from dbcs
- if (!procEntry.typeMask)
- procEntry.typeMask = spellEntry->procFlags;
- if (!procEntry.charges)
- procEntry.charges = spellEntry->procCharges;
- if (!procEntry.chance && !procEntry.ratePerMinute)
- procEntry.chance = float(spellEntry->procChance);
-
- // validate data
- if (procEntry.schoolMask & ~SPELL_SCHOOL_MASK_ALL)
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `schoolMask` set: %u", spellId, procEntry.schoolMask);
- if (procEntry.spellFamilyName && (procEntry.spellFamilyName < 3 || procEntry.spellFamilyName > 17 || procEntry.spellFamilyName == 14 || procEntry.spellFamilyName == 16))
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellFamilyName` set: %u", spellId, procEntry.spellFamilyName);
- if (procEntry.chance < 0)
- {
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `chance` field", spellId);
- procEntry.chance = 0;
- }
- if (procEntry.ratePerMinute < 0)
- {
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `ratePerMinute` field", spellId);
- procEntry.ratePerMinute = 0;
- }
- if (cooldown < 0)
- {
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `cooldown` field", spellId);
- procEntry.cooldown = 0;
- }
- if (procEntry.chance == 0 && procEntry.ratePerMinute == 0)
- sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `chance` and `ratePerMinute` values defined, proc will not be triggered", spellId);
- if (procEntry.charges > 99)
- {
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has too big value in `charges` field", spellId);
- procEntry.charges = 99;
- }
- if (!procEntry.typeMask)
- sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `typeMask` value defined, proc will not be triggered", spellId);
- if (procEntry.spellTypeMask & ~PROC_SPELL_PHASE_MASK_ALL)
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellTypeMask` set: %u", spellId, procEntry.spellTypeMask);
- if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
- sLog->outErrorDb("`spell_proc` table entry for spellId %u 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)
- sLog->outErrorDb("`spell_proc` table entry for spellId %u 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)
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellPhaseMask` set: %u", spellId, procEntry.spellPhaseMask);
- if (procEntry.spellPhaseMask && !(procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK))
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has `spellPhaseMask` value defined, but it won't be used for defined `typeMask` value", spellId);
- if (procEntry.hitMask & ~PROC_HIT_MASK_ALL)
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `hitMask` set: %u", 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)))))
- sLog->outErrorDb("`spell_proc` table entry for spellId %u has `hitMask` value defined, but it won't be used for defined `typeMask` and `spellPhaseMask` values", spellId);
-
- mSpellProcMap[spellId] = procEntry;
-
- if (allRanks)
- {
- spellId = sSpellMgr->GetNextSpellInChain(spellId);
- spellEntry = sSpellStore.LookupEntry(spellId);
- }
- else
- break;
- }
- ++count;
- } while (result->NextRow());
-
- sLog->outString(">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
+ SpellProcMap::const_iterator itr = mSpellProcMap.find(spellId);
+ if (itr != mSpellProcMap.end())
+ return &itr->second;
+ return NULL;
}
bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo)
@@ -1671,43 +923,609 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
return true;
}
-void SpellMgr::LoadSpellBonusess()
+SpellBonusEntry const* SpellMgr::GetSpellBonusData(uint32 spellId) const
+{
+ // Lookup data
+ SpellBonusMap::const_iterator itr = mSpellBonusMap.find(spellId);
+ if (itr != mSpellBonusMap.end())
+ return &itr->second;
+ // Not found, try lookup for 1 spell rank if exist
+ if (uint32 rank_1 = GetFirstSpellInChain(spellId))
+ {
+ SpellBonusMap::const_iterator itr2 = mSpellBonusMap.find(rank_1);
+ if (itr2 != mSpellBonusMap.end())
+ return &itr2->second;
+ }
+ return NULL;
+}
+
+uint16 SpellMgr::GetSpellThreat(uint32 spellid) const
+{
+ SpellThreatMap::const_iterator itr = mSpellThreatMap.find(spellid);
+ if (itr == mSpellThreatMap.end())
+ return 0;
+
+ return itr->second;
+}
+
+SkillLineAbilityMapBounds SpellMgr::GetSkillLineAbilityMapBounds(uint32 spell_id) const
+{
+ return SkillLineAbilityMapBounds(mSkillLineAbilityMap.lower_bound(spell_id), mSkillLineAbilityMap.upper_bound(spell_id));
+}
+
+PetAura const* SpellMgr::GetPetAura(uint32 spell_id, uint8 eff)
+{
+ SpellPetAuraMap::const_iterator itr = mSpellPetAuraMap.find((spell_id<<8) + eff);
+ if (itr != mSpellPetAuraMap.end())
+ return &itr->second;
+ else
+ return NULL;
+}
+
+SpellEnchantProcEntry const* SpellMgr::GetSpellEnchantProcEvent(uint32 enchId) const
+{
+ SpellEnchantProcEventMap::const_iterator itr = mSpellEnchantProcEventMap.find(enchId);
+ if (itr != mSpellEnchantProcEventMap.end())
+ return &itr->second;
+ return NULL;
+}
+
+bool SpellMgr::IsArenaAllowedEnchancment(uint32 ench_id) const
+{
+ return mEnchantCustomAttr[ench_id];
+}
+
+const std::vector<int32>* SpellMgr::GetSpellLinked(int32 spell_id) const
+{
+ SpellLinkedMap::const_iterator itr = mSpellLinkedMap.find(spell_id);
+ return itr != mSpellLinkedMap.end() ? &(itr->second) : NULL;
+}
+
+PetLevelupSpellSet const* SpellMgr::GetPetLevelupSpellList(uint32 petFamily) const
+{
+ PetLevelupSpellMap::const_iterator itr = mPetLevelupSpellMap.find(petFamily);
+ if (itr != mPetLevelupSpellMap.end())
+ return &itr->second;
+ else
+ return NULL;
+}
+
+PetDefaultSpellsEntry const* SpellMgr::GetPetDefaultSpellsEntry(int32 id) const
+{
+ PetDefaultSpellsMap::const_iterator itr = mPetDefaultSpellsMap.find(id);
+ if (itr != mPetDefaultSpellsMap.end())
+ return &itr->second;
+ return NULL;
+}
+
+SpellAreaMapBounds SpellMgr::GetSpellAreaMapBounds(uint32 spell_id) const
+{
+ return SpellAreaMapBounds(mSpellAreaMap.lower_bound(spell_id), mSpellAreaMap.upper_bound(spell_id));
+}
+
+SpellAreaForQuestMapBounds SpellMgr::GetSpellAreaForQuestMapBounds(uint32 quest_id, bool active) const
+{
+ if (active)
+ return SpellAreaForQuestMapBounds(mSpellAreaForActiveQuestMap.lower_bound(quest_id), mSpellAreaForActiveQuestMap.upper_bound(quest_id));
+ else
+ return SpellAreaForQuestMapBounds(mSpellAreaForQuestMap.lower_bound(quest_id), mSpellAreaForQuestMap.upper_bound(quest_id));
+}
+
+SpellAreaForQuestMapBounds SpellMgr::GetSpellAreaForQuestEndMapBounds(uint32 quest_id) const
+{
+ return SpellAreaForQuestMapBounds(mSpellAreaForQuestEndMap.lower_bound(quest_id), mSpellAreaForQuestEndMap.upper_bound(quest_id));
+}
+
+SpellAreaForAuraMapBounds SpellMgr::GetSpellAreaForAuraMapBounds(uint32 spell_id) const
+{
+ return SpellAreaForAuraMapBounds(mSpellAreaForAuraMap.lower_bound(spell_id), mSpellAreaForAuraMap.upper_bound(spell_id));
+}
+
+SpellAreaForAreaMapBounds SpellMgr::GetSpellAreaForAreaMapBounds(uint32 area_id) const
+{
+ return SpellAreaForAreaMapBounds(mSpellAreaForAreaMap.lower_bound(area_id), mSpellAreaForAreaMap.upper_bound(area_id));
+}
+
+bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const
+{
+ if (gender != GENDER_NONE) // not in expected gender
+ if (!player || gender != player->getGender())
+ return false;
+
+ if (raceMask) // not in expected race
+ if (!player || !(raceMask & player->getRaceMask()))
+ return false;
+
+ if (areaId) // not in expected zone
+ if (newZone != areaId && newArea != areaId)
+ return false;
+
+ if (questStart) // not in expected required quest state
+ if (!player || ((!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)))
+ return false;
+
+ if (questEnd) // not in expected forbidden quest state
+ if (!player || player->GetQuestRewardStatus(questEnd))
+ return false;
+
+ if (auraSpell) // not have expected aura
+ if (!player || (auraSpell > 0 && !player->HasAura(auraSpell)) || (auraSpell < 0 && player->HasAura(-auraSpell)))
+ return false;
+
+ // Extra conditions -- leaving the possibility add extra conditions...
+ switch (spellId)
+ {
+ case 58600: // No fly Zone - Dalaran
+ {
+ if (!player)
+ return false;
+
+ AreaTableEntry const* pArea = GetAreaEntryByAreaID(player->GetAreaId());
+ if (!(pArea && pArea->flags & AREA_FLAG_NO_FLY_ZONE))
+ return false;
+ if (!player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !player->HasAuraType(SPELL_AURA_FLY))
+ return false;
+ break;
+ }
+ case 68719: // Oil Refinery - Isle of Conquest.
+ case 68720: // Quarry - Isle of Conquest.
+ {
+ if (player->GetBattlegroundTypeId() != BATTLEGROUND_IC || !player->GetBattleground())
+ return false;
+
+ uint8 nodeType = spellId == 68719 ? NODE_TYPE_REFINERY : NODE_TYPE_QUARRY;
+ uint8 nodeState = player->GetTeamId() == TEAM_ALLIANCE ? NODE_STATE_CONTROLLED_A : NODE_STATE_CONTROLLED_H;
+
+ BattlegroundIC* pIC = static_cast<BattlegroundIC*>(player->GetBattleground());
+ if (pIC->GetNodeState(nodeType) == nodeState)
+ return true;
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void SpellMgr::LoadSpellInfos()
+{
+
+}
+
+void SpellMgr::LoadSpellRanks()
{
uint32 oldMSTime = getMSTime();
- mSpellBonusMap.clear(); // need for reload case
- uint32 count = 0;
- // 0 1 2 3 4
- QueryResult result = WorldDatabase.Query("SELECT entry, direct_bonus, dot_bonus, ap_bonus, ap_dot_bonus FROM spell_bonus_data");
+ // cleanup core data before reload - remove reference to ChainNode from SpellInfo
+ for (SpellChainMap::iterator itr = mSpellChains.begin(); itr != mSpellChains.end(); ++itr)
+ {
+ mSpellInfoMap[itr->first]->ChainEntry = NULL;
+ }
+ mSpellChains.clear();
+
+ QueryResult result = WorldDatabase.Query("SELECT first_spell_id, spell_id, rank from spell_ranks ORDER BY first_spell_id , rank");
+
if (!result)
{
- sLog->outString(">> Loaded %u spell bonus data", count);
+ sLog->outString(">> Loaded 0 spell rank records");
+ sLog->outString();
+ sLog->outErrorDb("`spell_ranks` table is empty!");
+ return;
+ }
+
+ uint32 rows = 0;
+ bool finished = false;
+
+ do
+ {
+ // spellid, rank
+ std::list < std::pair < int32, int32 > > rankChain;
+ int32 currentSpell = -1;
+ int32 lastSpell = -1;
+
+ // fill one chain
+ while (currentSpell == lastSpell && !finished)
+ {
+ Field *fields = result->Fetch();
+
+ currentSpell = fields[0].GetUInt32();
+ if (lastSpell == -1)
+ lastSpell = currentSpell;
+ uint32 spell_id = fields[1].GetUInt32();
+ uint32 rank = fields[2].GetUInt32();
+
+ // don't drop the row if we're moving to the next rank
+ if (currentSpell == lastSpell)
+ {
+ rankChain.push_back(std::make_pair(spell_id, rank));
+ if (!result->NextRow())
+ finished = true;
+ }
+ else
+ break;
+ }
+ // check if chain is made with valid first spell
+ SpellInfo const* first = sSpellMgr->GetSpellInfo(lastSpell);
+ if (!first)
+ {
+ sLog->outErrorDb("Spell rank identifier(first_spell_id) %u listed in `spell_ranks` does not exist!", lastSpell);
+ continue;
+ }
+ // check if chain is long enough
+ if (rankChain.size() < 2)
+ {
+ sLog->outErrorDb("There is only 1 spell rank for identifier(first_spell_id) %u in `spell_ranks`, entry is not needed!", lastSpell);
+ continue;
+ }
+ int32 curRank = 0;
+ bool valid = true;
+ // check spells in chain
+ for (std::list<std::pair<int32, int32> >::iterator itr = rankChain.begin() ; itr!= rankChain.end(); ++itr)
+ {
+ SpellInfo const* spell = sSpellMgr->GetSpellInfo(itr->first);
+ if (!spell)
+ {
+ sLog->outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not exist!", itr->first, itr->second, lastSpell);
+ valid = false;
+ break;
+ }
+ ++curRank;
+ if (itr->second != curRank)
+ {
+ sLog->outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not have proper rank value(should be %u)!", itr->first, itr->second, lastSpell, curRank);
+ valid = false;
+ break;
+ }
+ }
+ if (!valid)
+ continue;
+ int32 prevRank = 0;
+ // insert the chain
+ std::list<std::pair<int32, int32> >::iterator itr = rankChain.begin();
+ do
+ {
+ ++rows;
+ int32 addedSpell = itr->first;
+ mSpellChains[addedSpell].first = GetSpellInfo(lastSpell);
+ mSpellChains[addedSpell].last = GetSpellInfo(rankChain.back().first);
+ mSpellChains[addedSpell].rank = itr->second;
+ mSpellChains[addedSpell].prev = GetSpellInfo(prevRank);
+ mSpellInfoMap[addedSpell]->ChainEntry = &mSpellChains[addedSpell];
+ prevRank = addedSpell;
+ ++itr;
+ if (itr == rankChain.end())
+ {
+ mSpellChains[addedSpell].next = NULL;
+ break;
+ }
+ else
+ mSpellChains[addedSpell].next = GetSpellInfo(itr->first);
+ }
+ while (true);
+ } while (!finished);
+
+ sLog->outString(">> Loaded %u spell rank records in %u ms", rows, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
+}
+
+void SpellMgr::LoadSpellRequired()
+{
+ uint32 oldMSTime = getMSTime();
+
+ mSpellsReqSpell.clear(); // need for reload case
+ mSpellReq.clear(); // need for reload case
+
+ QueryResult result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required");
+
+ if (!result)
+ {
+ sLog->outString(">> Loaded 0 spell required records");
sLog->outString();
+ sLog->outErrorDb("`spell_required` table is empty!");
return;
}
+ uint32 rows = 0;
do
{
Field *fields = result->Fetch();
- uint32 entry = fields[0].GetUInt32();
- SpellEntry const* spell = sSpellStore.LookupEntry(entry);
+ uint32 spell_id = fields[0].GetUInt32();
+ uint32 spell_req = fields[1].GetUInt32();
+ // check if chain is made with valid first spell
+ SpellInfo const* spell = sSpellMgr->GetSpellInfo(spell_id);
if (!spell)
{
- sLog->outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry);
+ sLog->outErrorDb("spell_id %u in `spell_required` table is not found in dbcs, skipped", spell_id);
+ continue;
+ }
+ SpellInfo const* req_spell = sSpellMgr->GetSpellInfo(spell_req);
+ if (!req_spell)
+ {
+ sLog->outErrorDb("req_spell %u in `spell_required` table is not found in dbcs, skipped", spell_req);
+ continue;
+ }
+ if (GetFirstSpellInChain(spell_id) == GetFirstSpellInChain(spell_req))
+ {
+ sLog->outErrorDb("req_spell %u and spell_id %u in `spell_required` table are ranks of the same spell, entry not needed, skipped", spell_req, spell_id);
+ continue;
+ }
+ if (IsSpellRequiringSpell(spell_id, spell_req))
+ {
+ sLog->outErrorDb("duplicated entry of req_spell %u and spell_id %u in `spell_required`, skipped", spell_req, spell_id);
continue;
}
- SpellBonusEntry& sbe = mSpellBonusMap[entry];
- sbe.direct_damage = fields[1].GetFloat();
- sbe.dot_damage = fields[2].GetFloat();
- sbe.ap_bonus = fields[3].GetFloat();
- sbe.ap_dot_bonus = fields[4].GetFloat();
+ mSpellReq.insert (std::pair<uint32, uint32>(spell_id, spell_req));
+ mSpellsReqSpell.insert (std::pair<uint32, uint32>(spell_req, spell_id));
+ ++rows;
+ } while (result->NextRow());
+
+ sLog->outString(">> Loaded %u spell required records in %u ms", rows, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
+}
+
+void SpellMgr::LoadSpellLearnSkills()
+{
+ uint32 oldMSTime = getMSTime();
+
+ mSpellLearnSkills.clear(); // need for reload case
+
+ // search auto-learned skills and add its to map also for use in unlearn spells/talents
+ uint32 dbc_count = 0;
+ for (uint32 spell = 0; spell < sSpellMgr->GetSpellInfoStoreSize(); ++spell)
+ {
+ SpellInfo const* entry = sSpellMgr->GetSpellInfo(spell);
+
+ if (!entry)
+ continue;
+
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ if (entry->Effects[i].Effect == SPELL_EFFECT_SKILL)
+ {
+ SpellLearnSkillNode dbc_node;
+ dbc_node.skill = entry->Effects[i].MiscValue;
+ dbc_node.step = entry->Effects[i].CalcValue();
+ if (dbc_node.skill != SKILL_RIDING)
+ dbc_node.value = 1;
+ else
+ dbc_node.value = dbc_node.step * 75;
+ dbc_node.maxvalue = dbc_node.step * 75;
+ mSpellLearnSkills[spell] = dbc_node;
+ ++dbc_count;
+ break;
+ }
+ }
+ }
+
+ sLog->outString(">> Loaded %u Spell Learn Skills from DBC in %u ms", dbc_count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
+}
+
+void SpellMgr::LoadSpellLearnSpells()
+{
+ uint32 oldMSTime = getMSTime();
+
+ mSpellLearnSpells.clear(); // need for reload case
+
+ // 0 1 2
+ QueryResult result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell");
+ if (!result)
+ {
+ sLog->outString(">> Loaded 0 spell learn spells");
+ sLog->outString();
+ sLog->outErrorDb("`spell_learn_spell` table is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 spell_id = fields[0].GetUInt32();
+
+ SpellLearnSpellNode node;
+ node.spell = fields[1].GetUInt32();
+ node.active = fields[2].GetBool();
+ node.autoLearned= false;
+
+ if (!sSpellMgr->GetSpellInfo(spell_id))
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_learn_spell` does not exist", spell_id);
+ continue;
+ }
+
+ if (!sSpellMgr->GetSpellInfo(node.spell))
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_learn_spell` learning not existed spell %u", spell_id, node.spell);
+ continue;
+ }
+
+ if (GetTalentSpellCost(node.spell))
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_learn_spell` attempt learning talent spell %u, skipped", spell_id, node.spell);
+ continue;
+ }
+
+ mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell_id, node));
++count;
} while (result->NextRow());
- sLog->outString(">> Loaded %u extra spell bonus data in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ // search auto-learned spells and add its to map also for use in unlearn spells/talents
+ uint32 dbc_count = 0;
+ for (uint32 spell = 0; spell < sSpellMgr->GetSpellInfoStoreSize(); ++spell)
+ {
+ SpellInfo const* entry = sSpellMgr->GetSpellInfo(spell);
+
+ if (!entry)
+ continue;
+
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ if (entry->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL)
+ {
+ SpellLearnSpellNode dbc_node;
+ dbc_node.spell = entry->Effects[i].TriggerSpell;
+ dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself)
+
+ // ignore learning not existed spells (broken/outdated/or generic learnig spell 483
+ if (!sSpellMgr->GetSpellInfo(dbc_node.spell))
+ continue;
+
+ // talent or passive spells or skill-step spells auto-casted and not need dependent learning,
+ // pet teaching spells must not be dependent learning (casted)
+ // other required explicit dependent learning
+ dbc_node.autoLearned = entry->Effects[i].TargetA == TARGET_UNIT_PET || GetTalentSpellCost(spell) > 0 || entry->IsPassive() || entry->HasEffect(SPELL_EFFECT_SKILL_STEP);
+
+ SpellLearnSpellMapBounds db_node_bounds = GetSpellLearnSpellMapBounds(spell);
+
+ bool found = false;
+ for (SpellLearnSpellMap::const_iterator itr = db_node_bounds.first; itr != db_node_bounds.second; ++itr)
+ {
+ if (itr->second.spell == dbc_node.spell)
+ {
+ sLog->outErrorDb("Spell %u auto-learn spell %u in spell.dbc then the record in `spell_learn_spell` is redundant, please fix DB.",
+ spell, dbc_node.spell);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) // add new spell-spell pair if not found
+ {
+ mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell, dbc_node));
+ ++dbc_count;
+ }
+ }
+ }
+ }
+
+ sLog->outString(">> Loaded %u spell learn spells + %u found in DBC in %u ms", count, dbc_count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
+}
+
+void SpellMgr::LoadSpellTargetPositions()
+{
+ uint32 oldMSTime = getMSTime();
+
+ mSpellTargetPositions.clear(); // need for reload case
+
+ // 0 1 2 3 4 5
+ QueryResult result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM spell_target_position");
+ if (!result)
+ {
+ sLog->outString(">> Loaded 0 spell target coordinates. DB table `spell_target_position` is empty.");
+ sLog->outString();
+ return;
+ }
+
+ uint32 count = 0;
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 Spell_ID = fields[0].GetUInt32();
+
+ SpellTargetPosition st;
+
+ st.target_mapId = fields[1].GetUInt32();
+ st.target_X = fields[2].GetFloat();
+ st.target_Y = fields[3].GetFloat();
+ st.target_Z = fields[4].GetFloat();
+ st.target_Orientation = fields[5].GetFloat();
+
+ MapEntry const* mapEntry = sMapStore.LookupEntry(st.target_mapId);
+ if (!mapEntry)
+ {
+ sLog->outErrorDb("Spell (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.", Spell_ID, st.target_mapId);
+ continue;
+ }
+
+ if (st.target_X==0 && st.target_Y==0 && st.target_Z==0)
+ {
+ sLog->outErrorDb("Spell (ID:%u) target coordinates not provided.", Spell_ID);
+ continue;
+ }
+
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(Spell_ID);
+ if (!spellInfo)
+ {
+ sLog->outErrorDb("Spell (ID:%u) listed in `spell_target_position` does not exist.", Spell_ID);
+ continue;
+ }
+
+ bool found = false;
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ if (spellInfo->Effects[i].TargetA == TARGET_DST_DB || spellInfo->Effects[i].TargetB == TARGET_DST_DB)
+ {
+ // additional requirements
+ if (spellInfo->Effects[i].Effect == SPELL_EFFECT_BIND && spellInfo->Effects[i].MiscValue)
+ {
+ uint32 area_id = sMapMgr->GetAreaId(st.target_mapId, st.target_X, st.target_Y, st.target_Z);
+ if (area_id != uint32(spellInfo->Effects[i].MiscValue))
+ {
+ sLog->outErrorDb("Spell (Id: %u) listed in `spell_target_position` expected point to zone %u bit point to zone %u.", Spell_ID, spellInfo->Effects[i].MiscValue, area_id);
+ break;
+ }
+ }
+
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ sLog->outErrorDb("Spell (Id: %u) listed in `spell_target_position` does not have target TARGET_DST_DB (17).", Spell_ID);
+ continue;
+ }
+
+ mSpellTargetPositions[Spell_ID] = st;
+ ++count;
+
+ } while (result->NextRow());
+
+ /*
+ // Check all spells
+ for (uint32 i = 1; i < GetSpellInfoStoreSize; ++i)
+ {
+ SpellInfo const* spellInfo = GetSpellInfo(i);
+ if (!spellInfo)
+ continue;
+
+ bool found = false;
+ for (int j = 0; j < MAX_SPELL_EFFECTS; ++j)
+ {
+ switch (spellInfo->Effects[j].TargetA)
+ {
+ case TARGET_DST_DB:
+ found = true;
+ break;
+ }
+ if (found)
+ break;
+ switch (spellInfo->Effects[j].TargetB)
+ {
+ case TARGET_DST_DB:
+ found = true;
+ break;
+ }
+ if (found)
+ break;
+ }
+ if (found)
+ {
+ if (!sSpellMgr->GetSpellTargetPosition(i))
+ sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell (ID: %u) does not have record in `spell_target_position`", i);
+ }
+ }*/
+
+ sLog->outString(">> Loaded %u spell teleport coordinates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
@@ -1762,14 +1580,14 @@ void SpellMgr::LoadSpellGroups()
}
else
{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->second);
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->second);
if (!spellInfo)
{
sLog->outErrorDb("Spell %u listed in `spell_group` does not exist", itr->second);
mSpellGroupSpell.erase(itr++);
}
- else if (GetSpellRank(itr->second) > 1)
+ else if (spellInfo->GetRank() > 1)
{
sLog->outErrorDb("Spell %u listed in `spell_group` is not first rank of spell", itr->second);
mSpellGroupSpell.erase(itr++);
@@ -1841,467 +1659,475 @@ void SpellMgr::LoadSpellGroupStackRules()
sLog->outString();
}
-void SpellMgr::LoadSpellThreats()
+void SpellMgr::LoadSpellProcEvents()
{
uint32 oldMSTime = getMSTime();
- mSpellThreatMap.clear(); // need for reload case
+ mSpellProcEventMap.clear(); // need for reload case
uint32 count = 0;
- // 0 1
- QueryResult result = WorldDatabase.Query("SELECT entry, Threat FROM spell_threat");
+ // 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)
{
- sLog->outString(">> Loaded %u aggro generating spells", count);
+ sLog->outString(">> Loaded %u spell proc event conditions", count);
sLog->outString();
return;
}
+ uint32 customProc = 0;
do
{
Field *fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
- uint16 Threat = fields[1].GetUInt16();
- if (!sSpellStore.LookupEntry(entry))
+ SpellInfo const* spell = sSpellMgr->GetSpellInfo(entry);
+ if (!spell)
{
- sLog->outErrorDb("Spell %u listed in `spell_threat` does not exist", entry);
+ sLog->outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry);
continue;
}
- mSpellThreatMap[entry] = Threat;
+ SpellProcEventEntry spe;
+
+ spe.schoolMask = fields[1].GetUInt32();
+ spe.spellFamilyName = fields[2].GetUInt32();
+ spe.spellFamilyMask[0] = fields[3].GetUInt32();
+ spe.spellFamilyMask[1] = fields[4].GetUInt32();
+ spe.spellFamilyMask[2] = fields[5].GetUInt32();
+ spe.procFlags = fields[6].GetUInt32();
+ spe.procEx = fields[7].GetUInt32();
+ spe.ppmRate = fields[8].GetFloat();
+ spe.customChance = fields[9].GetFloat();
+ spe.cooldown = fields[10].GetUInt32();
+
+ mSpellProcEventMap[entry] = spe;
+ if (spell->ProcFlags == 0)
+ {
+ if (spe.procFlags == 0)
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_proc_event` probally not triggered spell", entry);
+ continue;
+ }
+ customProc++;
+ }
++count;
} while (result->NextRow());
- sLog->outString(">> Loaded %u aggro generating spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ if (customProc)
+ sLog->outString(">> Loaded %u extra and %u custom spell proc event conditions in %u ms", count, customProc, GetMSTimeDiffToNow(oldMSTime));
+ else
+ sLog->outString(">> Loaded %u extra spell proc event conditions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
-bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1, uint32 spellId_2) const
+void SpellMgr::LoadSpellProcs()
{
- SpellEntry const* spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
- if (!spellInfo_1 || !spellInfo_2) return false;
- if (spellInfo_1->Id == spellId_2) return false;
+ uint32 oldMSTime = getMSTime();
- return GetFirstSpellInChain(spellInfo_1->Id) == GetFirstSpellInChain(spellId_2);
-}
+ mSpellProcMap.clear(); // need for reload case
-bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo)
-{
- if (IsPassiveSpell(spellInfo->Id)) // ranked passive spell
- return false;
- if (spellInfo->powerType != POWER_MANA && spellInfo->powerType != POWER_HEALTH)
- return false;
- if (IsProfessionOrRidingSpell(spellInfo->Id))
- return false;
+ uint32 count = 0;
- if (sSpellMgr->IsSkillBonusSpell(spellInfo->Id))
- return false;
+ // 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");
+ if (!result)
+ {
+ sLog->outString(">> Loaded %u spell proc conditions and data", count);
+ sLog->outString();
+ return;
+ }
- // All stance spells. if any better way, change it.
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ do
{
- switch (spellInfo->SpellFamilyName)
+ Field* fields = result->Fetch();
+
+ int32 spellId = fields[0].GetInt32();
+
+ bool allRanks = false;
+ if (spellId <=0)
{
- case SPELLFAMILY_PALADIN:
- // Paladin aura Spell
- if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_RAID)
- return false;
- break;
- case SPELLFAMILY_DRUID:
- // Druid form Spell
- if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA &&
- spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT)
- return false;
- break;
- case SPELLFAMILY_ROGUE:
- // Rogue Stealth
- if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA &&
- spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT)
- return false;
+ allRanks = true;
+ spellId = -spellId;
}
- }
- return true;
-}
-bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId)
-{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);
- if (!spellInfo)
- return false;
-
- for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
- {
- if (spellInfo->Effect[i] == SPELL_EFFECT_SKILL)
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spellId);
+ if (!spellEntry)
{
- uint32 skill = spellInfo->EffectMiscValue[i];
+ sLog->outErrorDb("Spell %u listed in `spell_proc` does not exist", spellId);
+ continue;
+ }
- bool found = IsProfessionOrRidingSkill(skill);
- if (found)
- return true;
+ if (allRanks)
+ {
+ if (sSpellMgr->GetFirstSpellInChain(spellId) != uint32(spellId))
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_proc` is not first rank of spell.", fields[0].GetInt32());
+ continue;
+ }
}
- }
- return false;
-}
-bool SpellMgr::IsProfessionSpell(uint32 spellId)
-{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);
- if (!spellInfo)
- return false;
+ SpellProcEntry baseProcEntry;
- for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
- {
- if (spellInfo->Effect[i] == SPELL_EFFECT_SKILL)
+ baseProcEntry.schoolMask = fields[1].GetUInt32();
+ baseProcEntry.spellFamilyName = fields[2].GetUInt32();
+ baseProcEntry.spellFamilyMask[0] = fields[3].GetUInt32();
+ baseProcEntry.spellFamilyMask[1] = fields[4].GetUInt32();
+ baseProcEntry.spellFamilyMask[2] = fields[5].GetUInt32();
+ baseProcEntry.typeMask = fields[6].GetUInt32();
+ baseProcEntry.spellTypeMask = fields[7].GetUInt32();
+ baseProcEntry.spellPhaseMask = fields[8].GetUInt32();
+ baseProcEntry.hitMask = fields[9].GetUInt32();
+ baseProcEntry.attributesMask = fields[10].GetUInt32();
+ baseProcEntry.ratePerMinute = fields[11].GetFloat();
+ baseProcEntry.chance = fields[12].GetFloat();
+ float cooldown = fields[13].GetFloat();
+ baseProcEntry.cooldown = uint32(cooldown);
+ baseProcEntry.charges = fields[14].GetUInt32();
+
+ while(true)
{
- uint32 skill = spellInfo->EffectMiscValue[i];
+ if (mSpellProcMap.find(spellId) != mSpellProcMap.end())
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_proc` has duplicate entry in the table", spellId);
+ break;
+ }
+ SpellProcEntry procEntry = SpellProcEntry(baseProcEntry);
- bool found = IsProfessionSkill(skill);
- if (found)
- return true;
- }
- }
- return false;
-}
+ // take defaults from dbcs
+ if (!procEntry.typeMask)
+ procEntry.typeMask = spellEntry->ProcFlags;
+ if (!procEntry.charges)
+ procEntry.charges = spellEntry->ProcCharges;
+ if (!procEntry.chance && !procEntry.ratePerMinute)
+ procEntry.chance = float(spellEntry->ProcChance);
-bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId)
-{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);
- if (!spellInfo)
- return false;
+ // validate data
+ if (procEntry.schoolMask & ~SPELL_SCHOOL_MASK_ALL)
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `schoolMask` set: %u", spellId, procEntry.schoolMask);
+ if (procEntry.spellFamilyName && (procEntry.spellFamilyName < 3 || procEntry.spellFamilyName > 17 || procEntry.spellFamilyName == 14 || procEntry.spellFamilyName == 16))
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellFamilyName` set: %u", spellId, procEntry.spellFamilyName);
+ if (procEntry.chance < 0)
+ {
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `chance` field", spellId);
+ procEntry.chance = 0;
+ }
+ if (procEntry.ratePerMinute < 0)
+ {
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `ratePerMinute` field", spellId);
+ procEntry.ratePerMinute = 0;
+ }
+ if (cooldown < 0)
+ {
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `cooldown` field", spellId);
+ procEntry.cooldown = 0;
+ }
+ if (procEntry.chance == 0 && procEntry.ratePerMinute == 0)
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `chance` and `ratePerMinute` values defined, proc will not be triggered", spellId);
+ if (procEntry.charges > 99)
+ {
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has too big value in `charges` field", spellId);
+ procEntry.charges = 99;
+ }
+ if (!procEntry.typeMask)
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `typeMask` value defined, proc will not be triggered", spellId);
+ if (procEntry.spellTypeMask & ~PROC_SPELL_PHASE_MASK_ALL)
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellTypeMask` set: %u", spellId, procEntry.spellTypeMask);
+ if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)))
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u 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)
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u 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)
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellPhaseMask` set: %u", spellId, procEntry.spellPhaseMask);
+ if (procEntry.spellPhaseMask && !(procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK))
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has `spellPhaseMask` value defined, but it won't be used for defined `typeMask` value", spellId);
+ if (procEntry.hitMask & ~PROC_HIT_MASK_ALL)
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `hitMask` set: %u", 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)))))
+ sLog->outErrorDb("`spell_proc` table entry for spellId %u has `hitMask` value defined, but it won't be used for defined `typeMask` and `spellPhaseMask` values", spellId);
- for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
- {
- if (spellInfo->Effect[i] == SPELL_EFFECT_SKILL)
- {
- uint32 skill = spellInfo->EffectMiscValue[i];
+ mSpellProcMap[spellId] = procEntry;
- bool found = IsPrimaryProfessionSkill(skill);
- if (found)
- return true;
+ if (allRanks)
+ {
+ spellId = sSpellMgr->GetNextSpellInChain(spellId);
+ spellEntry = sSpellMgr->GetSpellInfo(spellId);
+ }
+ else
+ break;
}
- }
- return false;
-}
+ ++count;
+ } while (result->NextRow());
-bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const
-{
- return IsPrimaryProfessionSpell(spellId) && GetSpellRank(spellId) == 1;
+ sLog->outString(">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
}
-bool SpellMgr::IsSkillBonusSpell(uint32 spellId) const
+void SpellMgr::LoadSpellBonusess()
{
- SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId);
+ uint32 oldMSTime = getMSTime();
- for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
+ mSpellBonusMap.clear(); // need for reload case
+ uint32 count = 0;
+ // 0 1 2 3 4
+ QueryResult result = WorldDatabase.Query("SELECT entry, direct_bonus, dot_bonus, ap_bonus, ap_dot_bonus FROM spell_bonus_data");
+ if (!result)
{
- SkillLineAbilityEntry const* pAbility = _spell_idx->second;
- if (!pAbility || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
- continue;
-
- if (pAbility->req_skill_value > 0)
- return true;
+ sLog->outString(">> Loaded %u spell bonus data", count);
+ sLog->outString();
+ return;
}
- return false;
-}
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 entry = fields[0].GetUInt32();
-bool SpellMgr::IsSkillTypeSpell(uint32 spellId, SkillType type) const
-{
- SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId);
+ SpellInfo const* spell = sSpellMgr->GetSpellInfo(entry);
+ if (!spell)
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry);
+ continue;
+ }
- for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
- if (_spell_idx->second->skillId == uint32(type))
- return true;
+ SpellBonusEntry& sbe = mSpellBonusMap[entry];
+ sbe.direct_damage = fields[1].GetFloat();
+ sbe.dot_damage = fields[2].GetFloat();
+ sbe.ap_bonus = fields[3].GetFloat();
+ sbe.ap_dot_bonus = fields[4].GetFloat();
- return false;
+ ++count;
+ } while (result->NextRow());
+
+ sLog->outString(">> Loaded %u extra spell bonus data in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
}
-// basepoints provided here have to be valid basepoints (use SpellMgr::CalculateSpellEffectBaseAmount)
-int32 SpellMgr::CalculateSpellEffectAmount(SpellEntry const* spellEntry, uint8 effIndex, Unit const* caster, int32 const* effBasePoints, Unit const* /*target*/)
+void SpellMgr::LoadSpellThreats()
{
- float basePointsPerLevel = spellEntry->EffectRealPointsPerLevel[effIndex];
- int32 basePoints = effBasePoints ? *effBasePoints : spellEntry->EffectBasePoints[effIndex];
- int32 randomPoints = int32(spellEntry->EffectDieSides[effIndex]);
+ uint32 oldMSTime = getMSTime();
+
+ mSpellThreatMap.clear(); // need for reload case
+
+ uint32 count = 0;
- // base amount modification based on spell lvl vs caster lvl
- if (caster)
+ // 0 1
+ QueryResult result = WorldDatabase.Query("SELECT entry, Threat FROM spell_threat");
+ if (!result)
{
- int32 level = int32(caster->getLevel());
- if (level > int32(spellEntry->maxLevel) && spellEntry->maxLevel > 0)
- level = int32(spellEntry->maxLevel);
- else if (level < int32(spellEntry->baseLevel))
- level = int32(spellEntry->baseLevel);
- level -= int32(spellEntry->spellLevel);
- basePoints += int32(level * basePointsPerLevel);
+ sLog->outString(">> Loaded %u aggro generating spells", count);
+ sLog->outString();
+ return;
}
- // roll in a range <1;EffectDieSides> as of patch 3.3.3
- switch (randomPoints)
+ do
{
- case 0: break;
- case 1: basePoints += 1; break; // range 1..1
- default:
- // range can have positive (1..rand) and negative (rand..1) values, so order its for irand
- int32 randvalue = (randomPoints >= 1)
- ? irand(1, randomPoints)
- : irand(randomPoints, 1);
+ Field *fields = result->Fetch();
- basePoints += randvalue;
- break;
- }
+ uint32 entry = fields[0].GetUInt32();
+ uint16 Threat = fields[1].GetUInt16();
- float value = float(basePoints);
+ if (!sSpellMgr->GetSpellInfo(entry))
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_threat` does not exist", entry);
+ continue;
+ }
- // random damage
- if (caster)
- {
- // bonus amount from combo points
- if (caster->m_movedPlayer)
- if (uint8 comboPoints = caster->m_movedPlayer->GetComboPoints())
- if (float comboDamage = spellEntry->EffectPointsPerComboPoint[effIndex])
- value += comboDamage * comboPoints;
-
- value = caster->ApplyEffectModifiers(spellEntry, effIndex, value);
-
- // amount multiplication based on caster's level
- if (!basePointsPerLevel && (spellEntry->Attributes & SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION && spellEntry->spellLevel) &&
- spellEntry->Effect[effIndex] != SPELL_EFFECT_WEAPON_PERCENT_DAMAGE &&
- spellEntry->Effect[effIndex] != SPELL_EFFECT_KNOCK_BACK &&
- spellEntry->EffectApplyAuraName[effIndex] != SPELL_AURA_MOD_SPEED_ALWAYS &&
- spellEntry->EffectApplyAuraName[effIndex] != SPELL_AURA_MOD_SPEED_NOT_STACK &&
- spellEntry->EffectApplyAuraName[effIndex] != SPELL_AURA_MOD_INCREASE_SPEED &&
- spellEntry->EffectApplyAuraName[effIndex] != SPELL_AURA_MOD_DECREASE_SPEED)
- //there are many more: slow speed, -healing pct
- value *= 0.25f * exp(caster->getLevel() * (70 - spellEntry->spellLevel) / 1000.0f);
- //value = int32(value * (int32)getLevel() / (int32)(spellProto->spellLevel ? spellProto->spellLevel : 1));
- }
+ mSpellThreatMap[entry] = Threat;
- return int32(value);
-}
+ ++count;
+ } while (result->NextRow());
-int32 SpellMgr::CalculateSpellEffectBaseAmount(int32 value, SpellEntry const* spellEntry, uint8 effIndex)
-{
- if (spellEntry->EffectDieSides[effIndex] == 0)
- return value;
- else
- return value - 1;
+ sLog->outString(">> Loaded %u aggro generating spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
}
-float SpellMgr::CalculateSpellEffectValueMultiplier(SpellEntry const* spellEntry, uint8 effIndex, Unit* caster, Spell* spell)
+void SpellMgr::LoadSkillLineAbilityMap()
{
- float multiplier = spellEntry->EffectValueMultiplier[effIndex];
- if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL))
- modOwner->ApplySpellMod(spellEntry->Id, SPELLMOD_VALUE_MULTIPLIER, multiplier, spell);
- return multiplier;
-}
+ uint32 oldMSTime = getMSTime();
-float SpellMgr::CalculateSpellEffectDamageMultiplier(SpellEntry const* spellEntry, uint8 effIndex, Unit* caster, Spell* spell)
-{
- float multiplier = spellEntry->EffectDamageMultiplier[effIndex];
- if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL))
- modOwner->ApplySpellMod(spellEntry->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplier, spell);
- return multiplier;
+ mSkillLineAbilityMap.clear();
+
+ uint32 count = 0;
+
+ for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
+ {
+ SkillLineAbilityEntry const* SkillInfo = sSkillLineAbilityStore.LookupEntry(i);
+ if (!SkillInfo)
+ continue;
+
+ mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId, SkillInfo));
+ ++count;
+ }
+
+ sLog->outString(">> Loaded %u SkillLineAbility MultiMap Data in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
}
-SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const
+void SpellMgr::LoadSpellPetAuras()
{
- // ignore passive spells
- if (IsPassiveSpell(spellInfo->Id))
- return spellInfo;
+ uint32 oldMSTime = getMSTime();
- bool needRankSelection = false;
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ mSpellPetAuraMap.clear(); // need for reload case
+
+ // 0 1 2 3
+ QueryResult result = WorldDatabase.Query("SELECT spell, effectId, pet, aura FROM spell_pet_auras");
+ if (!result)
{
- if (IsPositiveEffect(spellInfo->Id, i) && (
- spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA ||
- spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY ||
- spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_RAID
-))
- {
- needRankSelection = true;
- break;
- }
+ sLog->outString(">> Loaded 0 spell pet auras. DB table `spell_pet_auras` is empty.");
+ sLog->outString();
+ return;
}
- // not required
- if (!needRankSelection)
- return spellInfo;
+ uint32 count = 0;
- for (uint32 nextSpellId = spellInfo->Id; nextSpellId != 0; nextSpellId = GetPrevSpellInChain(nextSpellId))
+ do
{
- SpellEntry const* nextSpellInfo = sSpellStore.LookupEntry(nextSpellId);
- if (!nextSpellInfo)
- break;
+ Field *fields = result->Fetch();
- // if found appropriate level
- if (playerLevel + 10 >= nextSpellInfo->spellLevel)
- return nextSpellInfo;
+ uint32 spell = fields[0].GetUInt32();
+ uint8 eff = fields[1].GetUInt8();
+ uint32 pet = fields[2].GetUInt32();
+ uint32 aura = fields[3].GetUInt32();
- // one rank less then
- }
+ SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff);
+ if (itr != mSpellPetAuraMap.end())
+ itr->second.AddAura(pet, aura);
+ else
+ {
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell);
+ if (!spellInfo)
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell);
+ continue;
+ }
+ if (spellInfo->Effects[eff].Effect != SPELL_EFFECT_DUMMY &&
+ (spellInfo->Effects[eff].Effect != SPELL_EFFECT_APPLY_AURA ||
+ spellInfo->Effects[eff].ApplyAuraName != SPELL_AURA_DUMMY))
+ {
+ sLog->outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell);
+ continue;
+ }
- // not found
- return NULL;
+ SpellInfo const* spellInfo2 = sSpellMgr->GetSpellInfo(aura);
+ if (!spellInfo2)
+ {
+ sLog->outErrorDb("Aura %u listed in `spell_pet_auras` does not exist", aura);
+ continue;
+ }
+
+ PetAura pa(pet, aura, spellInfo->Effects[eff].TargetA == TARGET_UNIT_PET, spellInfo->Effects[eff].CalcValue());
+ mSpellPetAuraMap[(spell<<8) + eff] = pa;
+ }
+
+ ++count;
+ } while (result->NextRow());
+
+ sLog->outString(">> Loaded %u spell pet auras in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
}
-void SpellMgr::LoadSpellLearnSkills()
+// Fill custom data about enchancments
+void SpellMgr::LoadEnchantCustomAttr()
{
uint32 oldMSTime = getMSTime();
- mSpellLearnSkills.clear(); // need for reload case
+ uint32 size = sSpellItemEnchantmentStore.GetNumRows();
+ mEnchantCustomAttr.resize(size);
- // search auto-learned skills and add its to map also for use in unlearn spells/talents
- uint32 dbc_count = 0;
- for (uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell)
+ uint32 count = 0;
+
+ for (uint32 i = 0; i < size; ++i)
+ mEnchantCustomAttr[i] = 0;
+
+ for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
- SpellEntry const* entry = sSpellStore.LookupEntry(spell);
+ SpellInfo const* spellInfo = GetSpellInfo(i);
+ if (!spellInfo)
+ continue;
- if (!entry)
+ // TODO: find a better check
+ if (!(spellInfo->AttributesEx2 & SPELL_ATTR2_UNK13) || !(spellInfo->Attributes & SPELL_ATTR0_NOT_SHAPESHIFT))
continue;
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j)
{
- if (entry->Effect[i] == SPELL_EFFECT_SKILL)
+ if (spellInfo->Effects[j].Effect == SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY)
{
- SpellLearnSkillNode dbc_node;
- dbc_node.skill = entry->EffectMiscValue[i];
- dbc_node.step = SpellMgr::CalculateSpellEffectAmount(entry, i);
- if (dbc_node.skill != SKILL_RIDING)
- dbc_node.value = 1;
- else
- dbc_node.value = dbc_node.step * 75;
- dbc_node.maxvalue = dbc_node.step * 75;
- mSpellLearnSkills[spell] = dbc_node;
- ++dbc_count;
+ uint32 enchId = spellInfo->Effects[j].MiscValue;
+ SpellItemEnchantmentEntry const* ench = sSpellItemEnchantmentStore.LookupEntry(enchId);
+ if (!ench)
+ continue;
+ mEnchantCustomAttr[enchId] = true;
+ ++count;
break;
}
}
}
- sLog->outString(">> Loaded %u Spell Learn Skills from DBC in %u ms", dbc_count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString(">> Loaded %u custom enchant attributes in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
-void SpellMgr::LoadSpellLearnSpells()
+void SpellMgr::LoadSpellEnchantProcData()
{
uint32 oldMSTime = getMSTime();
- mSpellLearnSpells.clear(); // need for reload case
+ mSpellEnchantProcEventMap.clear(); // need for reload case
- // 0 1 2
- QueryResult result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell");
+ uint32 count = 0;
+
+ // 0 1 2 3
+ QueryResult result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data");
if (!result)
{
- sLog->outString(">> Loaded 0 spell learn spells");
+ sLog->outString(">> Loaded %u spell enchant proc event conditions", count);
sLog->outString();
- sLog->outErrorDb("`spell_learn_spell` table is empty!");
return;
}
- uint32 count = 0;
-
do
{
Field *fields = result->Fetch();
- uint32 spell_id = fields[0].GetUInt32();
-
- SpellLearnSpellNode node;
- node.spell = fields[1].GetUInt32();
- node.active = fields[2].GetBool();
- node.autoLearned= false;
+ uint32 enchantId = fields[0].GetUInt32();
- if (!sSpellStore.LookupEntry(spell_id))
+ SpellItemEnchantmentEntry const* ench = sSpellItemEnchantmentStore.LookupEntry(enchantId);
+ if (!ench)
{
- sLog->outErrorDb("Spell %u listed in `spell_learn_spell` does not exist", spell_id);
+ sLog->outErrorDb("Enchancment %u listed in `spell_enchant_proc_data` does not exist", enchantId);
continue;
}
- if (!sSpellStore.LookupEntry(node.spell))
- {
- sLog->outErrorDb("Spell %u listed in `spell_learn_spell` learning not existed spell %u", spell_id, node.spell);
- continue;
- }
+ SpellEnchantProcEntry spe;
- if (GetTalentSpellCost(node.spell))
- {
- sLog->outErrorDb("Spell %u listed in `spell_learn_spell` attempt learning talent spell %u, skipped", spell_id, node.spell);
- continue;
- }
+ spe.customChance = fields[1].GetUInt32();
+ spe.PPMChance = fields[2].GetFloat();
+ spe.procEx = fields[3].GetUInt32();
- mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell_id, node));
+ mSpellEnchantProcEventMap[enchantId] = spe;
++count;
} while (result->NextRow());
- // search auto-learned spells and add its to map also for use in unlearn spells/talents
- uint32 dbc_count = 0;
- for (uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell)
- {
- SpellEntry const* entry = sSpellStore.LookupEntry(spell);
-
- if (!entry)
- continue;
-
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- if (entry->Effect[i] == SPELL_EFFECT_LEARN_SPELL)
- {
- SpellLearnSpellNode dbc_node;
- dbc_node.spell = entry->EffectTriggerSpell[i];
- dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself)
-
- // ignore learning not existed spells (broken/outdated/or generic learnig spell 483
- if (!sSpellStore.LookupEntry(dbc_node.spell))
- continue;
-
- // talent or passive spells or skill-step spells auto-casted and not need dependent learning,
- // pet teaching spells must not be dependent learning (casted)
- // other required explicit dependent learning
- dbc_node.autoLearned = entry->EffectImplicitTargetA[i] == TARGET_UNIT_PET || GetTalentSpellCost(spell) > 0 || IsPassiveSpell(spell) || IsSpellHaveEffect(entry, SPELL_EFFECT_SKILL_STEP);
-
- SpellLearnSpellMapBounds db_node_bounds = GetSpellLearnSpellMapBounds(spell);
-
- bool found = false;
- for (SpellLearnSpellMap::const_iterator itr = db_node_bounds.first; itr != db_node_bounds.second; ++itr)
- {
- if (itr->second.spell == dbc_node.spell)
- {
- sLog->outErrorDb("Spell %u auto-learn spell %u in spell.dbc then the record in `spell_learn_spell` is redundant, please fix DB.",
- spell, dbc_node.spell);
- found = true;
- break;
- }
- }
-
- if (!found) // add new spell-spell pair if not found
- {
- mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell, dbc_node));
- ++dbc_count;
- }
- }
- }
- }
-
- sLog->outString(">> Loaded %u spell learn spells + %u found in DBC in %u ms", count, dbc_count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString(">> Loaded %u enchant proc data definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
-void SpellMgr::LoadSpellPetAuras()
+void SpellMgr::LoadSpellLinked()
{
uint32 oldMSTime = getMSTime();
- mSpellPetAuraMap.clear(); // need for reload case
+ mSpellLinkedMap.clear(); // need for reload case
- // 0 1 2 3
- QueryResult result = WorldDatabase.Query("SELECT spell, effectId, pet, aura FROM spell_pet_auras");
+ // 0 1 2
+ QueryResult result = WorldDatabase.Query("SELECT spell_trigger, spell_effect, type FROM spell_linked_spell");
if (!result)
{
- sLog->outString(">> Loaded 0 spell pet auras. DB table `spell_pet_auras` is empty.");
+ sLog->outString(">> Loaded 0 linked spells. DB table `spell_linked_spell` is empty.");
sLog->outString();
return;
}
@@ -2312,45 +2138,36 @@ void SpellMgr::LoadSpellPetAuras()
{
Field *fields = result->Fetch();
- uint32 spell = fields[0].GetUInt32();
- uint8 eff = fields[1].GetUInt8();
- uint32 pet = fields[2].GetUInt32();
- uint32 aura = fields[3].GetUInt32();
+ int32 trigger = fields[0].GetInt32();
+ int32 effect = fields[1].GetInt32();
+ int32 type = fields[2].GetInt32();
- SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff);
- if (itr != mSpellPetAuraMap.end())
- itr->second.AddAura(pet, aura);
- else
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(abs(trigger));
+ if (!spellInfo)
{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
- if (!spellInfo)
- {
- sLog->outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell);
- continue;
- }
- if (spellInfo->Effect[eff] != SPELL_EFFECT_DUMMY &&
- (spellInfo->Effect[eff] != SPELL_EFFECT_APPLY_AURA ||
- spellInfo->EffectApplyAuraName[eff] != SPELL_AURA_DUMMY))
- {
- sLog->outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell);
- continue;
- }
-
- SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(aura);
- if (!spellInfo2)
- {
- sLog->outErrorDb("Aura %u listed in `spell_pet_auras` does not exist", aura);
- continue;
- }
+ sLog->outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(trigger));
+ continue;
+ }
+ spellInfo = sSpellMgr->GetSpellInfo(abs(effect));
+ if (!spellInfo)
+ {
+ sLog->outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(effect));
+ continue;
+ }
- PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[eff] == TARGET_UNIT_PET, SpellMgr::CalculateSpellEffectAmount(spellInfo, eff));
- mSpellPetAuraMap[(spell<<8) + eff] = pa;
+ if (type) //we will find a better way when more types are needed
+ {
+ if (trigger > 0)
+ trigger += SPELL_LINKED_MAX_SPELLS * type;
+ else
+ trigger -= SPELL_LINKED_MAX_SPELLS * type;
}
+ mSpellLinkedMap[trigger].push_back(effect);
++count;
} while (result->NextRow());
- sLog->outString(">> Loaded %u spell pet auras in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString(">> Loaded %u linked spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
@@ -2390,18 +2207,18 @@ void SpellMgr::LoadPetLevelupSpellMap()
if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
continue;
- SpellEntry const* spell = sSpellStore.LookupEntry(skillLine->spellId);
+ SpellInfo const* spell = sSpellMgr->GetSpellInfo(skillLine->spellId);
if (!spell) // not exist or triggered or talent
continue;
- if (!spell->spellLevel)
+ if (!spell->SpellLevel)
continue;
PetLevelupSpellSet& spellSet = mPetLevelupSpellMap[creatureFamily->ID];
if (spellSet.empty())
++family_count;
- spellSet.insert(PetLevelupSpellSet::value_type(spell->spellLevel, spell->Id));
+ spellSet.insert(PetLevelupSpellSet::value_type(spell->SpellLevel, spell->Id));
++count;
}
}
@@ -2499,18 +2316,17 @@ void SpellMgr::LoadPetDefaultSpells()
oldMSTime = getMSTime();
// different summon spells
- for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
+ for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
-
- SpellEntry const* spellEntry = sSpellStore.LookupEntry(i);
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(i);
if (!spellEntry)
continue;
for (uint8 k = 0; k < MAX_SPELL_EFFECTS; ++k)
{
- if (spellEntry->Effect[k] == SPELL_EFFECT_SUMMON || spellEntry->Effect[k] == SPELL_EFFECT_SUMMON_PET)
+ if (spellEntry->Effects[k].Effect == SPELL_EFFECT_SUMMON || spellEntry->Effects[k].Effect == SPELL_EFFECT_SUMMON_PET)
{
- uint32 creature_id = spellEntry->EffectMiscValue[k];
+ uint32 creature_id = spellEntry->Effects[k].MiscValue;
CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(creature_id);
if (!cInfo)
continue;
@@ -2541,99 +2357,6 @@ void SpellMgr::LoadPetDefaultSpells()
sLog->outString();
}
-/// Some checks for spells, to prevent adding deprecated/broken spells for trainers, spell book, etc
-bool SpellMgr::IsSpellValid(SpellEntry const *spellInfo, Player *pl, bool msg)
-{
- // not exist
- if (!spellInfo)
- return false;
-
- bool need_check_reagents = false;
-
- // check effects
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- switch (spellInfo->Effect[i])
- {
- case 0:
- continue;
-
- // craft spell for crafting non-existed item (break client recipes list show)
- case SPELL_EFFECT_CREATE_ITEM:
- case SPELL_EFFECT_CREATE_ITEM_2:
- {
- if (spellInfo->EffectItemType[i] == 0)
- {
- // skip auto-loot crafting spells, its not need explicit item info (but have special fake items sometime)
- if (!IsLootCraftingSpell(spellInfo))
- {
- if (msg)
- {
- if (pl)
- ChatHandler(pl).PSendSysMessage("Craft spell %u not have create item entry.", spellInfo->Id);
- else
- sLog->outErrorDb("Craft spell %u not have create item entry.", spellInfo->Id);
- }
- return false;
- }
-
- }
- // also possible IsLootCraftingSpell case but fake item must exist anyway
- else if (!sObjectMgr->GetItemTemplate(spellInfo->EffectItemType[i]))
- {
- if (msg)
- {
- if (pl)
- ChatHandler(pl).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->EffectItemType[i]);
- else
- sLog->outErrorDb("Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->EffectItemType[i]);
- }
- return false;
- }
-
- need_check_reagents = true;
- break;
- }
- case SPELL_EFFECT_LEARN_SPELL:
- {
- SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(spellInfo->EffectTriggerSpell[i]);
- if (!IsSpellValid(spellInfo2, pl, msg))
- {
- if (msg)
- {
- if (pl)
- ChatHandler(pl).PSendSysMessage("Spell %u learn to broken spell %u, and then...", spellInfo->Id, spellInfo->EffectTriggerSpell[i]);
- else
- sLog->outErrorDb("Spell %u learn to invalid spell %u, and then...", spellInfo->Id, spellInfo->EffectTriggerSpell[i]);
- }
- return false;
- }
- break;
- }
- }
- }
-
- if (need_check_reagents)
- {
- for (uint8 j = 0; j < MAX_SPELL_REAGENTS; ++j)
- {
- if (spellInfo->Reagent[j] > 0 && !sObjectMgr->GetItemTemplate(spellInfo->Reagent[j]))
- {
- if (msg)
- {
- if (pl)
- ChatHandler(pl).PSendSysMessage("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Reagent[j]);
- else
- sLog->outErrorDb("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Reagent[j]);
- }
- return false;
- }
- }
- }
-
- return true;
-}
-
void SpellMgr::LoadSpellAreas()
{
uint32 oldMSTime = getMSTime();
@@ -2672,10 +2395,10 @@ void SpellMgr::LoadSpellAreas()
spellArea.gender = Gender(fields[7].GetUInt8());
spellArea.autocast = fields[8].GetBool();
- if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell))
+ if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell))
{
if (spellArea.autocast)
- const_cast<SpellEntry*>(spellInfo)->Attributes |= SPELL_ATTR0_CANT_CANCEL;
+ const_cast<SpellInfo*>(spellInfo)->Attributes |= SPELL_ATTR0_CANT_CANCEL;
}
else
{
@@ -2742,7 +2465,7 @@ void SpellMgr::LoadSpellAreas()
if (spellArea.auraSpell)
{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell));
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(abs(spellArea.auraSpell));
if (!spellInfo)
{
sLog->outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell, abs(spellArea.auraSpell));
@@ -2835,901 +2558,58 @@ void SpellMgr::LoadSpellAreas()
sLog->outString();
}
-SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player)
-{
- // normal case
- if (spellInfo->AreaGroupId > 0)
- {
- bool found = false;
- AreaGroupEntry const* groupEntry = sAreaGroupStore.LookupEntry(spellInfo->AreaGroupId);
- while (groupEntry)
- {
- for (uint8 i = 0; i < MAX_GROUP_AREA_IDS; ++i)
- if (groupEntry->AreaId[i] == zone_id || groupEntry->AreaId[i] == area_id)
- found = true;
- if (found || !groupEntry->nextGroup)
- break;
- // Try search in next group
- groupEntry = sAreaGroupStore.LookupEntry(groupEntry->nextGroup);
- }
-
- if (!found)
- return SPELL_FAILED_INCORRECT_AREA;
- }
-
- // continent limitation (virtual continent)
- if (spellInfo->AttributesEx4 & SPELL_ATTR4_CAST_ONLY_IN_OUTLAND)
- {
- uint32 v_map = GetVirtualMapForMapAndZone(map_id, zone_id);
- MapEntry const* mapEntry = sMapStore.LookupEntry(v_map);
- if (!mapEntry || mapEntry->addon < 1 || !mapEntry->IsContinent())
- return SPELL_FAILED_INCORRECT_AREA;
- }
-
- // raid instance limitation
- if (spellInfo->AttributesEx6 & SPELL_ATTR6_NOT_IN_RAID_INSTANCE)
- {
- MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
- if (!mapEntry || mapEntry->IsRaid())
- return SPELL_FAILED_NOT_IN_RAID_INSTANCE;
- }
-
- // DB base check (if non empty then must fit at least single for allow)
- SpellAreaMapBounds saBounds = sSpellMgr->GetSpellAreaMapBounds(spellInfo->Id);
- if (saBounds.first != saBounds.second)
- {
- for (SpellAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
- {
- if (itr->second.IsFitToRequirements(player, zone_id, area_id))
- return SPELL_CAST_OK;
- }
- return SPELL_FAILED_INCORRECT_AREA;
- }
-
- // bg spell checks
- switch (spellInfo->Id)
- {
- case 23333: // Warsong Flag
- case 23335: // Silverwing Flag
- return map_id == 489 && player && player->InBattleground() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
- case 34976: // Netherstorm Flag
- return map_id == 566 && player && player->InBattleground() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
- case 2584: // Waiting to Resurrect
- case 22011: // Spirit Heal Channel
- case 22012: // Spirit Heal
- case 24171: // Resurrection Impact Visual
- case 42792: // Recently Dropped Flag
- case 43681: // Inactive
- case 44535: // Spirit Heal (mana)
- {
- MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
- if (!mapEntry)
- return SPELL_FAILED_INCORRECT_AREA;
-
- return zone_id == 4197 || (mapEntry->IsBattleground() && player && player->InBattleground()) ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
- }
- case 44521: // Preparation
- {
- if (!player)
- return SPELL_FAILED_REQUIRES_AREA;
-
- MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
- if (!mapEntry)
- return SPELL_FAILED_INCORRECT_AREA;
-
- if (!mapEntry->IsBattleground())
- return SPELL_FAILED_REQUIRES_AREA;
-
- Battleground* bg = player->GetBattleground();
- return bg && bg->GetStatus() == STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
- }
- case 32724: // Gold Team (Alliance)
- case 32725: // Green Team (Alliance)
- case 35774: // Gold Team (Horde)
- case 35775: // Green Team (Horde)
- {
- MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
- if (!mapEntry)
- return SPELL_FAILED_INCORRECT_AREA;
-
- return mapEntry->IsBattleArena() && player && player->InBattleground() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
- }
- case 32727: // Arena Preparation
- {
- if (!player)
- return SPELL_FAILED_REQUIRES_AREA;
-
- MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
- if (!mapEntry)
- return SPELL_FAILED_INCORRECT_AREA;
-
- if (!mapEntry->IsBattleArena())
- return SPELL_FAILED_REQUIRES_AREA;
-
- Battleground* bg = player->GetBattleground();
- return bg && bg->GetStatus() == STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
- }
- }
-
- // aura limitations
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- switch (spellInfo->EffectApplyAuraName[i])
- {
- case SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED:
- case SPELL_AURA_FLY:
- {
- if (player && !player->IsKnowHowFlyIn(map_id, zone_id))
- return SPELL_FAILED_INCORRECT_AREA;
- }
- }
- }
-
- return SPELL_CAST_OK;
-}
-
-void SpellMgr::LoadSkillLineAbilityMap()
-{
- uint32 oldMSTime = getMSTime();
-
- mSkillLineAbilityMap.clear();
-
- uint32 count = 0;
-
- for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i)
- {
- SkillLineAbilityEntry const* SkillInfo = sSkillLineAbilityStore.LookupEntry(i);
- if (!SkillInfo)
- continue;
-
- mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId, SkillInfo));
- ++count;
- }
-
- sLog->outString(">> Loaded %u SkillLineAbility MultiMap Data in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
-}
-
-DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered)
-{
- if (IsPositiveSpell(spellproto->Id))
- return DIMINISHING_NONE;
-
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- if (spellproto->EffectApplyAuraName[i] == SPELL_AURA_MOD_TAUNT)
- return DIMINISHING_TAUNT;
- }
-
- // Explicit Diminishing Groups
- switch (spellproto->SpellFamilyName)
- {
- // Event spells
- case SPELLFAMILY_UNK1:
- return DIMINISHING_NONE;
- case SPELLFAMILY_DEATHKNIGHT:
- {
- // Hungering Cold (no flags)
- if (spellproto->SpellIconID == 2797)
- return DIMINISHING_DISORIENT;
- // Mark of Blood
- else if ((spellproto->SpellFamilyFlags[0] & 0x10000000) && spellproto->SpellIconID == 2285)
- return DIMINISHING_LIMITONLY;
- break;
- }
- case SPELLFAMILY_DRUID:
- {
- // Pounce
- if (spellproto->SpellFamilyFlags[0] & 0x20000)
- return DIMINISHING_OPENING_STUN;
- // Cyclone
- else if (spellproto->SpellFamilyFlags[1] & 0x20)
- return DIMINISHING_CYCLONE;
- // Entangling Roots
- // Nature's Grasp
- else if (spellproto->SpellFamilyFlags[0] & 0x00000200)
- return DIMINISHING_CONTROLLED_ROOT;
- // Faerie Fire
- else if (spellproto->SpellFamilyFlags[0] & 0x400)
- return DIMINISHING_LIMITONLY;
- break;
- }
- case SPELLFAMILY_HUNTER:
- {
- // Hunter's mark
- if ((spellproto->SpellFamilyFlags[0] & 0x400) && spellproto->SpellIconID == 538)
- return DIMINISHING_LIMITONLY;
- // Scatter Shot (own diminishing)
- else if ((spellproto->SpellFamilyFlags[0] & 0x40000) && spellproto->SpellIconID == 132)
- return DIMINISHING_SCATTER_SHOT;
- // Entrapment (own diminishing)
- else if (spellproto->SpellVisual[0] == 7484 && spellproto->SpellIconID == 20)
- return DIMINISHING_ENTRAPMENT;
- // Wyvern Sting mechanic is MECHANIC_SLEEP but the diminishing is DIMINISHING_DISORIENT
- else if ((spellproto->SpellFamilyFlags[1] & 0x1000) && spellproto->SpellIconID == 1721)
- return DIMINISHING_DISORIENT;
- // Freezing Arrow
- else if (spellproto->SpellFamilyFlags[0] & 0x8)
- return DIMINISHING_DISORIENT;
- break;
- }
- case SPELLFAMILY_PALADIN:
- {
- // Judgement of Justice - limit duration to 10s in PvP
- if (spellproto->SpellFamilyFlags[0] & 0x100000)
- return DIMINISHING_LIMITONLY;
- // Turn Evil
- else if ((spellproto->SpellFamilyFlags[1] & 0x804000) && spellproto->SpellIconID == 309)
- return DIMINISHING_FEAR;
- break;
- }
- case SPELLFAMILY_PRIEST:
- {
- // Psychic Horror
- if (spellproto->SpellFamilyFlags[2] & 0x2000)
- return DIMINISHING_HORROR;
- break;
- }
- case SPELLFAMILY_ROGUE:
- {
- // Gouge
- if (spellproto->SpellFamilyFlags[0] & 0x8)
- return DIMINISHING_DISORIENT;
- // Blind
- else if (spellproto->SpellFamilyFlags[0] & 0x1000000)
- return DIMINISHING_FEAR;
- // Cheap Shot
- else if (spellproto->SpellFamilyFlags[0] & 0x400)
- return DIMINISHING_OPENING_STUN;
- // Crippling poison - Limit to 10 seconds in PvP (No SpellFamilyFlags)
- else if (spellproto->SpellIconID == 163)
- return DIMINISHING_LIMITONLY;
- break;
- }
- case SPELLFAMILY_MAGE:
- {
- // Frostbite
- if (spellproto->SpellFamilyFlags[1] & 0x80000000)
- return DIMINISHING_ROOT;
- // Shattered Barrier
- else if (spellproto->SpellVisual[0] == 12297)
- return DIMINISHING_ROOT;
- // Deep Freeze
- else if (spellproto->SpellIconID == 2939 && spellproto->SpellVisual[0] == 9963)
- return DIMINISHING_CONTROLLED_STUN;
- // Frost Nova / Freeze (Water Elemental)
- else if (spellproto->SpellIconID == 193)
- return DIMINISHING_CONTROLLED_ROOT;
- // Dragon's Breath
- else if (spellproto->SpellFamilyFlags[0] & 0x800000)
- return DIMINISHING_DISORIENT;
- break;
- }
- case SPELLFAMILY_WARLOCK:
- {
- // Death Coil
- if (spellproto->SpellFamilyFlags[0] & 0x80000)
- return DIMINISHING_HORROR;
- // Curses/etc
- else if ((spellproto->SpellFamilyFlags[0] & 0x80000000) || (spellproto->SpellFamilyFlags[1] & 0x200))
- return DIMINISHING_LIMITONLY;
- // Seduction
- else if (spellproto->SpellFamilyFlags[1] & 0x10000000)
- return DIMINISHING_FEAR;
- break;
- }
- case SPELLFAMILY_WARRIOR:
- {
- // Hamstring - limit duration to 10s in PvP
- if (spellproto->SpellFamilyFlags[0] & 0x2)
- return DIMINISHING_LIMITONLY;
- // Improved Hamstring
- else if (spellproto->AttributesEx3 & 0x80000 && spellproto->SpellIconID == 23)
- return DIMINISHING_ROOT;
- // Charge Stun (own diminishing)
- else if (spellproto->SpellFamilyFlags[0] & 0x01000000)
- return DIMINISHING_CHARGE;
- break;
- }
- // Must be below SPELLFAMILY_WARRIOR for Charge to work
- case SPELLFAMILY_GENERIC:
- {
- // Pet charge effects (Infernal Awakening, Demon Charge)
- if (spellproto->SpellVisual[0] == 2816 && spellproto->SpellIconID == 15)
- return DIMINISHING_CONTROLLED_STUN;
- // Gnaw
- else if (spellproto->Id == 47481)
- return DIMINISHING_CONTROLLED_STUN;
- }
- default:
- break;
- }
-
- // Lastly - Set diminishing depending on mechanic
- uint32 mechanic = GetAllSpellMechanicMask(spellproto);
- if (mechanic & (1 << MECHANIC_CHARM))
- return DIMINISHING_MIND_CONTROL;
- if (mechanic & (1 << MECHANIC_SILENCE))
- return DIMINISHING_SILENCE;
- if (mechanic & (1 << MECHANIC_SLEEP))
- return DIMINISHING_SLEEP;
- if (mechanic & ((1 << MECHANIC_SAPPED) | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_SHACKLE)))
- return DIMINISHING_DISORIENT;
- // Mechanic Knockout, except Blast Wave
- if (mechanic & (1 << MECHANIC_KNOCKOUT) && spellproto->SpellIconID != 292)
- return DIMINISHING_DISORIENT;
- if (mechanic & (1 << MECHANIC_DISARM))
- return DIMINISHING_DISARM;
- if (mechanic & (1 << MECHANIC_FEAR))
- return DIMINISHING_FEAR;
- if (mechanic & (1 << MECHANIC_STUN))
- return triggered ? DIMINISHING_STUN : DIMINISHING_CONTROLLED_STUN;
- if (mechanic & (1 << MECHANIC_BANISH))
- return DIMINISHING_BANISH;
- if (mechanic & (1 << MECHANIC_ROOT))
- return triggered ? DIMINISHING_ROOT : DIMINISHING_CONTROLLED_ROOT;
-
- return DIMINISHING_NONE;
-}
-
-int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry const* spellproto)
-{
- if (!IsDiminishingReturnsGroupDurationLimited(group))
- return 0;
-
- // Explicit diminishing duration
- switch (spellproto->SpellFamilyName)
- {
- case SPELLFAMILY_DRUID:
- {
- // Faerie Fire - limit to 40 seconds in PvP (3.1)
- if (spellproto->SpellFamilyFlags[0] & 0x400)
- return 40 * IN_MILLISECONDS;
- break;
- }
- case SPELLFAMILY_HUNTER:
- {
- // Wyvern Sting
- if (spellproto->SpellFamilyFlags[1] & 0x1000)
- return 6 * IN_MILLISECONDS;
- // Hunter's Mark
- if (spellproto->SpellFamilyFlags[0] & 0x400)
- return 120 * IN_MILLISECONDS;
- break;
- }
- case SPELLFAMILY_PALADIN:
- {
- // Repentance - limit to 6 seconds in PvP
- if (spellproto->SpellFamilyFlags[0] & 0x4)
- return 6 * IN_MILLISECONDS;
- break;
- }
- case SPELLFAMILY_WARLOCK:
- {
- // Banish - limit to 6 seconds in PvP
- if (spellproto->SpellFamilyFlags[1] & 0x8000000)
- return 6 * IN_MILLISECONDS;
- // Curse of Tongues - limit to 12 seconds in PvP
- else if (spellproto->SpellFamilyFlags[2] & 0x800)
- return 12 * IN_MILLISECONDS;
- // Curse of Elements - limit to 120 seconds in PvP
- else if (spellproto->SpellFamilyFlags[1] & 0x200)
- return 120 * IN_MILLISECONDS;
- break;
- }
- default:
- break;
- }
-
- return 10 * IN_MILLISECONDS;
-}
-
-bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group)
-{
- switch (group)
- {
- case DIMINISHING_CONTROLLED_STUN:
- case DIMINISHING_STUN:
- case DIMINISHING_ENTRAPMENT:
- case DIMINISHING_CONTROLLED_ROOT:
- case DIMINISHING_ROOT:
- case DIMINISHING_FEAR:
- case DIMINISHING_MIND_CONTROL:
- case DIMINISHING_DISORIENT:
- case DIMINISHING_CYCLONE:
- case DIMINISHING_BANISH:
- case DIMINISHING_LIMITONLY:
- case DIMINISHING_OPENING_STUN:
- case DIMINISHING_HORROR:
- case DIMINISHING_SLEEP:
- return true;
- default:
- return false;
- }
-}
-
-DiminishingLevels GetDiminishingReturnsMaxLevel(DiminishingGroup group)
-{
- switch (group)
- {
- case DIMINISHING_TAUNT:
- return DIMINISHING_LEVEL_TAUNT_IMMUNE;
- default:
- return DIMINISHING_LEVEL_IMMUNE;
- }
-}
-
-DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group)
-{
- switch (group)
- {
- case DIMINISHING_TAUNT:
- case DIMINISHING_CONTROLLED_STUN:
- case DIMINISHING_STUN:
- case DIMINISHING_OPENING_STUN:
- case DIMINISHING_CYCLONE:
- case DIMINISHING_CHARGE:
- return DRTYPE_ALL;
- case DIMINISHING_FEAR:
- case DIMINISHING_CONTROLLED_ROOT:
- case DIMINISHING_ROOT:
- case DIMINISHING_MIND_CONTROL:
- case DIMINISHING_DISORIENT:
- case DIMINISHING_ENTRAPMENT:
- case DIMINISHING_SILENCE:
- case DIMINISHING_DISARM:
- case DIMINISHING_BANISH:
- case DIMINISHING_SCATTER_SHOT:
- case DIMINISHING_HORROR:
- case DIMINISHING_SLEEP:
- return DRTYPE_PLAYER;
- default:
- break;
- }
-
- return DRTYPE_NONE;
-}
-
-bool IsPartOfSkillLine(uint32 skillId, uint32 spellId)
-{
- SkillLineAbilityMapBounds skillBounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
- for (SkillLineAbilityMap::const_iterator itr = skillBounds.first; itr != skillBounds.second; ++itr)
- if (itr->second->skillId == skillId)
- return true;
-
- return false;
-}
-
-bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const
-{
- if (gender != GENDER_NONE) // not in expected gender
- if (!player || gender != player->getGender())
- return false;
-
- if (raceMask) // not in expected race
- if (!player || !(raceMask & player->getRaceMask()))
- return false;
-
- if (areaId) // not in expected zone
- if (newZone != areaId && newArea != areaId)
- return false;
-
- if (questStart) // not in expected required quest state
- if (!player || ((!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)))
- return false;
-
- if (questEnd) // not in expected forbidden quest state
- if (!player || player->GetQuestRewardStatus(questEnd))
- return false;
-
- if (auraSpell) // not have expected aura
- if (!player || (auraSpell > 0 && !player->HasAura(auraSpell)) || (auraSpell < 0 && player->HasAura(-auraSpell)))
- return false;
-
- // Extra conditions -- leaving the possibility add extra conditions...
- switch (spellId)
- {
- case 58600: // No fly Zone - Dalaran
- {
- if (!player)
- return false;
-
- AreaTableEntry const* pArea = GetAreaEntryByAreaID(player->GetAreaId());
- if (!(pArea && pArea->flags & AREA_FLAG_NO_FLY_ZONE))
- return false;
- if (!player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !player->HasAuraType(SPELL_AURA_FLY))
- return false;
- break;
- }
- case 68719: // Oil Refinery - Isle of Conquest.
- case 68720: // Quarry - Isle of Conquest.
- {
- if (player->GetBattlegroundTypeId() != BATTLEGROUND_IC || !player->GetBattleground())
- return false;
-
- uint8 nodeType = spellId == 68719 ? NODE_TYPE_REFINERY : NODE_TYPE_QUARRY;
- uint8 nodeState = player->GetTeamId() == TEAM_ALLIANCE ? NODE_STATE_CONTROLLED_A : NODE_STATE_CONTROLLED_H;
-
- BattlegroundIC* pIC = static_cast<BattlegroundIC*>(player->GetBattleground());
- if (pIC->GetNodeState(nodeType) == nodeState)
- return true;
-
- return false;
- }
- }
-
- return true;
-}
-
-bool SpellMgr::CanAurasStack(Aura const *aura1, Aura const *aura2, bool sameCaster) const
-{
- SpellEntry const* spellInfo_1 = aura1->GetSpellProto();
- SpellEntry const* spellInfo_2 = aura2->GetSpellProto();
- SpellSpecific spellSpec_1 = GetSpellSpecific(spellInfo_1);
- SpellSpecific spellSpec_2 = GetSpellSpecific(spellInfo_2);
- if (spellSpec_1 && spellSpec_2)
- if (IsSingleFromSpellSpecificPerTarget(spellSpec_1, spellSpec_2)
- || (sameCaster && IsSingleFromSpellSpecificPerCaster(spellSpec_1, spellSpec_2)))
- return false;
-
- SpellGroupStackRule stackRule = CheckSpellGroupStackRules(spellInfo_1->Id, spellInfo_2->Id);
- if (stackRule)
- {
- if (stackRule == SPELL_GROUP_STACK_RULE_EXCLUSIVE)
- return false;
- if (sameCaster && stackRule == SPELL_GROUP_STACK_RULE_EXCLUSIVE_FROM_SAME_CASTER)
- return false;
- }
-
- if (spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName)
- return true;
-
- if (!sameCaster)
- {
- if (spellInfo_1->AttributesEx3 & SPELL_ATTR3_STACK_FOR_DIFF_CASTERS)
- return true;
-
- // check same periodic auras
- for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- {
- switch (spellInfo_1->EffectApplyAuraName[i])
- {
- // DOT or HOT from different casters will stack
- case SPELL_AURA_PERIODIC_DAMAGE:
- case SPELL_AURA_PERIODIC_DUMMY:
- case SPELL_AURA_PERIODIC_HEAL:
- case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
- case SPELL_AURA_PERIODIC_ENERGIZE:
- case SPELL_AURA_PERIODIC_MANA_LEECH:
- case SPELL_AURA_PERIODIC_LEECH:
- case SPELL_AURA_POWER_BURN_MANA:
- case SPELL_AURA_OBS_MOD_POWER:
- case SPELL_AURA_OBS_MOD_HEALTH:
- case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE:
- // periodic auras which target areas are not allowed to stack this way (replenishment for example)
- if (IsAreaOfEffectSpellEffect(spellInfo_1, i) || IsAreaOfEffectSpellEffect(spellInfo_2, i))
- break;
- return true;
- default:
- break;
- }
- }
- }
-
- bool isVehicleAura1 = false;
- bool isVehicleAura2 = false;
- uint8 i = 0;
- while (i < MAX_SPELL_EFFECTS && !(isVehicleAura1 && isVehicleAura2))
- {
- if (spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_CONTROL_VEHICLE)
- isVehicleAura1 = true;
- if (spellInfo_2->EffectApplyAuraName[i] == SPELL_AURA_CONTROL_VEHICLE)
- isVehicleAura2 = true;
-
- ++i;
- }
-
- if (isVehicleAura1 && isVehicleAura2)
- {
- Vehicle* veh = NULL;
- if (aura1->GetOwner()->ToUnit())
- veh = aura1->GetOwner()->ToUnit()->GetVehicleKit();
-
- if (!veh) // We should probably just let it stack. Vehicle system will prevent undefined behaviour later
- return true;
-
- if (!veh->GetAvailableSeatCount())
- return false; // No empty seat available
-
- return true; // Empty seat available (skip rest)
- }
-
- uint32 spellId_1 = GetLastSpellInChain(spellInfo_1->Id);
- uint32 spellId_2 = GetLastSpellInChain(spellInfo_2->Id);
-
- // same spell
- if (spellId_1 == spellId_2)
- {
- // Hack for Incanter's Absorption
- if (spellId_1 == 44413)
- return true;
- if (aura1->GetCastItemGUID() && aura2->GetCastItemGUID())
- if (aura1->GetCastItemGUID() != aura2->GetCastItemGUID() && (GetSpellCustomAttr(spellId_1) & SPELL_ATTR0_CU_ENCHANT_PROC))
- return true;
- // same spell with same caster should not stack
- return false;
- }
-
- return true;
-}
-
-bool CanSpellDispelAura(SpellEntry const* dispelSpell, SpellEntry const* aura)
-{
- // These auras (like ressurection sickness) can't be dispelled
- if (aura->Attributes & SPELL_ATTR0_NEGATIVE_1)
- return false;
-
- // These spells (like Mass Dispel) can dispell all auras
- if (dispelSpell->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)
- return true;
-
- // These auras (like Divine Shield) can't be dispelled
- if (aura->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)
- return false;
-
- // These auras (Cyclone for example) are not dispelable
- if (aura->AttributesEx & SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE)
- return false;
-
- return true;
-}
-
-bool CanSpellPierceImmuneAura(SpellEntry const* pierceSpell, SpellEntry const* aura)
-{
- // these spells pierce all avalible spells (Resurrection Sickness for example)
- if (pierceSpell->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY)
- return true;
-
- // these spells (Cyclone for example) can pierce all...
- if ((pierceSpell->AttributesEx & SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE)
- // ...but not these (Divine shield for example)
- && !(aura && (aura->Mechanic == MECHANIC_IMMUNE_SHIELD || aura->Mechanic == MECHANIC_INVULNERABILITY)))
- return true;
-
- return false;
-}
-
-void SpellMgr::LoadSpellEnchantProcData()
-{
- uint32 oldMSTime = getMSTime();
-
- mSpellEnchantProcEventMap.clear(); // need for reload case
-
- uint32 count = 0;
-
- // 0 1 2 3
- QueryResult result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data");
- if (!result)
- {
- sLog->outString(">> Loaded %u spell enchant proc event conditions", count);
- sLog->outString();
- return;
- }
-
- do
- {
- Field *fields = result->Fetch();
-
- uint32 enchantId = fields[0].GetUInt32();
-
- SpellItemEnchantmentEntry const* ench = sSpellItemEnchantmentStore.LookupEntry(enchantId);
- if (!ench)
- {
- sLog->outErrorDb("Enchancment %u listed in `spell_enchant_proc_data` does not exist", enchantId);
- continue;
- }
-
- SpellEnchantProcEntry spe;
-
- spe.customChance = fields[1].GetUInt32();
- spe.PPMChance = fields[2].GetFloat();
- spe.procEx = fields[3].GetUInt32();
-
- mSpellEnchantProcEventMap[enchantId] = spe;
-
- ++count;
- } while (result->NextRow());
-
- sLog->outString(">> Loaded %u enchant proc data definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
-}
-
-void SpellMgr::LoadSpellRequired()
+void SpellMgr::LoadSpellInfoStore()
{
uint32 oldMSTime = getMSTime();
- mSpellsReqSpell.clear(); // need for reload case
- mSpellReq.clear(); // need for reload case
-
- QueryResult result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required");
+ UnloadSpellInfoStore();
+ mSpellInfoMap.resize(sSpellStore.GetNumRows(), NULL);
- if (!result)
+ for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
{
- sLog->outString(">> Loaded 0 spell required records");
- sLog->outString();
- sLog->outErrorDb("`spell_required` table is empty!");
- return;
+ if (SpellEntry const* spellEntry = sSpellStore.LookupEntry(i))
+ mSpellInfoMap[i] = new SpellInfo(spellEntry);
}
- uint32 rows = 0;
-
- do
- {
- Field *fields = result->Fetch();
-
- uint32 spell_id = fields[0].GetUInt32();
- uint32 spell_req = fields[1].GetUInt32();
- // check if chain is made with valid first spell
- SpellEntry const* spell = sSpellStore.LookupEntry(spell_id);
- if (!spell)
- {
- sLog->outErrorDb("spell_id %u in `spell_required` table is not found in dbcs, skipped", spell_id);
- continue;
- }
- SpellEntry const* req_spell = sSpellStore.LookupEntry(spell_req);
- if (!req_spell)
- {
- sLog->outErrorDb("req_spell %u in `spell_required` table is not found in dbcs, skipped", spell_req);
- continue;
- }
- if (GetFirstSpellInChain(spell_id) == GetFirstSpellInChain(spell_req))
- {
- sLog->outErrorDb("req_spell %u and spell_id %u in `spell_required` table are ranks of the same spell, entry not needed, skipped", spell_req, spell_id);
- continue;
- }
- if (IsSpellRequiringSpell(spell_id, spell_req))
- {
- sLog->outErrorDb("duplicated entry of req_spell %u and spell_id %u in `spell_required`, skipped", spell_req, spell_id);
- continue;
- }
- mSpellReq.insert (std::pair<uint32, uint32>(spell_id, spell_req));
- mSpellsReqSpell.insert (std::pair<uint32, uint32>(spell_req, spell_id));
- ++rows;
- } while (result->NextRow());
-
- sLog->outString(">> Loaded %u spell required records in %u ms", rows, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString(">> Loaded spell custom attributes in %u ms", GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
-void SpellMgr::LoadSpellRanks()
+void SpellMgr::UnloadSpellInfoStore()
{
- uint32 oldMSTime = getMSTime();
-
- mSpellChains.clear(); // need for reload case
-
- QueryResult result = WorldDatabase.Query("SELECT first_spell_id, spell_id, rank from spell_ranks ORDER BY first_spell_id , rank");
-
- if (!result)
+ for (uint32 i = 0; i < mSpellInfoMap.size(); ++i)
{
- sLog->outString(">> Loaded 0 spell rank records");
- sLog->outString();
- sLog->outErrorDb("`spell_ranks` table is empty!");
- return;
+ if (mSpellInfoMap[i])
+ delete mSpellInfoMap[i];
}
-
- uint32 rows = 0;
- bool finished = false;
-
- do
- {
- // spellid, rank
- std::list < std::pair < int32, int32 > > rankChain;
- int32 currentSpell = -1;
- int32 lastSpell = -1;
-
- // fill one chain
- while (currentSpell == lastSpell && !finished)
- {
- Field *fields = result->Fetch();
-
- currentSpell = fields[0].GetUInt32();
- if (lastSpell == -1)
- lastSpell = currentSpell;
- uint32 spell_id = fields[1].GetUInt32();
- uint32 rank = fields[2].GetUInt32();
-
- // don't drop the row if we're moving to the next rank
- if (currentSpell == lastSpell)
- {
- rankChain.push_back(std::make_pair(spell_id, rank));
- if (!result->NextRow())
- finished = true;
- }
- else
- break;
- }
- // check if chain is made with valid first spell
- SpellEntry const* first = sSpellStore.LookupEntry(lastSpell);
- if (!first)
- {
- sLog->outErrorDb("Spell rank identifier(first_spell_id) %u listed in `spell_ranks` does not exist!", lastSpell);
- continue;
- }
- // check if chain is long enough
- if (rankChain.size() < 2)
- {
- sLog->outErrorDb("There is only 1 spell rank for identifier(first_spell_id) %u in `spell_ranks`, entry is not needed!", lastSpell);
- continue;
- }
- int32 curRank = 0;
- bool valid = true;
- // check spells in chain
- for (std::list<std::pair<int32, int32> >::iterator itr = rankChain.begin() ; itr!= rankChain.end(); ++itr)
- {
- SpellEntry const* spell = sSpellStore.LookupEntry(itr->first);
- if (!spell)
- {
- sLog->outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not exist!", itr->first, itr->second, lastSpell);
- valid = false;
- break;
- }
- ++curRank;
- if (itr->second != curRank)
- {
- sLog->outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not have proper rank value(should be %u)!", itr->first, itr->second, lastSpell, curRank);
- valid = false;
- break;
- }
- }
- if (!valid)
- continue;
- int32 prevRank = 0;
- // insert the chain
- std::list<std::pair<int32, int32> >::iterator itr = rankChain.begin();
- do
- {
- ++rows;
- int32 addedSpell = itr->first;
- mSpellChains[addedSpell].first = lastSpell;
- mSpellChains[addedSpell].last = rankChain.back().first;
- mSpellChains[addedSpell].rank = itr->second;
- mSpellChains[addedSpell].prev = prevRank;
- prevRank = addedSpell;
- ++itr;
- if (itr == rankChain.end())
- {
- mSpellChains[addedSpell].next = 0;
- break;
- }
- else
- mSpellChains[addedSpell].next = itr->first;
- }
- while (true);
- } while (!finished);
-
- sLog->outString(">> Loaded %u spell rank records in %u ms", rows, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
+ mSpellInfoMap.clear();
}
-// set data in core for now
void SpellMgr::LoadSpellCustomAttr()
{
uint32 oldMSTime = getMSTime();
- mSpellCustomAttr.resize(GetSpellStore()->GetNumRows(), 0); // initialize with 0 values
-
- uint32 count = 0;
-
- SpellEntry* spellInfo = NULL;
- for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
+ SpellInfo* spellInfo = NULL;
+ for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
- spellInfo = (SpellEntry*)sSpellStore.LookupEntry(i);
+ spellInfo = mSpellInfoMap[i];
if (!spellInfo)
continue;
for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
{
- switch (spellInfo->Effect[j])
+ switch (spellInfo->Effects[j].ApplyAuraName)
+ {
+ case SPELL_AURA_MOD_POSSESS:
+ case SPELL_AURA_MOD_CONFUSE:
+ case SPELL_AURA_MOD_CHARM:
+ case SPELL_AURA_AOE_CHARM:
+ case SPELL_AURA_MOD_FEAR:
+ case SPELL_AURA_MOD_STUN:
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CC;
+ break;
+ }
+ switch (spellInfo->Effects[j].Effect)
{
case SPELL_EFFECT_SCHOOL_DAMAGE:
case SPELL_EFFECT_WEAPON_DAMAGE:
@@ -3737,27 +2617,17 @@ void SpellMgr::LoadSpellCustomAttr()
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
case SPELL_EFFECT_HEAL:
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_DIRECT_DAMAGE;
- ++count;
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_DIRECT_DAMAGE;
break;
case SPELL_EFFECT_CHARGE:
case SPELL_EFFECT_CHARGE_DEST:
case SPELL_EFFECT_JUMP:
case SPELL_EFFECT_JUMP_DEST:
case SPELL_EFFECT_LEAP_BACK:
- if (!spellInfo->speed && !spellInfo->SpellFamilyName)
- spellInfo->speed = SPEED_CHARGE;
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_CHARGE;
- ++count;
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_CHARGE;
break;
case SPELL_EFFECT_PICKPOCKET:
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_PICKPOCKET;
- break;
- case SPELL_EFFECT_TRIGGER_SPELL:
- if (IsPositionTarget(spellInfo->EffectImplicitTargetA[j]) ||
- spellInfo->Targets & (TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION))
- spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE;
- ++count;
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_PICKPOCKET;
break;
case SPELL_EFFECT_ENCHANT_ITEM:
case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
@@ -3767,88 +2637,44 @@ void SpellMgr::LoadSpellCustomAttr()
// only enchanting profession enchantments procs can stack
if (IsPartOfSkillLine(SKILL_ENCHANTING, i))
{
- uint32 enchantId = spellInfo->EffectMiscValue[j];
+ uint32 enchantId = spellInfo->Effects[j].MiscValue;
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
for (uint8 s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s)
{
if (enchant->type[s] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
continue;
- SpellEntry const* procInfo = sSpellStore.LookupEntry(enchant->spellid[s]);
+ SpellInfo* procInfo = (SpellInfo*)sSpellMgr->GetSpellInfo(enchant->spellid[s]);
if (!procInfo)
continue;
// if proced directly from enchantment, not via proc aura
// NOTE: Enchant Weapon - Blade Ward also has proc aura spell and is proced directly
// however its not expected to stack so this check is good
- if (IsSpellHaveAura(procInfo, SPELL_AURA_PROC_TRIGGER_SPELL))
+ if (procInfo->HasAura(SPELL_AURA_PROC_TRIGGER_SPELL))
continue;
- mSpellCustomAttr[enchant->spellid[s]] |= SPELL_ATTR0_CU_ENCHANT_PROC;
+ procInfo->AttributesCu |= SPELL_ATTR0_CU_ENCHANT_PROC;
}
}
break;
}
}
-
- switch (SpellTargetType[spellInfo->EffectImplicitTargetA[j]])
- {
- case TARGET_TYPE_UNIT_TARGET:
- case TARGET_TYPE_DEST_TARGET:
- spellInfo->Targets |= TARGET_FLAG_UNIT;
- ++count;
- break;
- default:
- break;
- }
}
- for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
- {
- switch (spellInfo->EffectApplyAuraName[j])
- {
- case SPELL_AURA_MOD_POSSESS:
- case SPELL_AURA_MOD_CONFUSE:
- case SPELL_AURA_MOD_CHARM:
- case SPELL_AURA_AOE_CHARM:
- case SPELL_AURA_MOD_FEAR:
- case SPELL_AURA_MOD_STUN:
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_AURA_CC;
- ++count;
- break;
- }
- }
+ if (!spellInfo->_IsPositiveEffect(EFFECT_0, false))
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF0;
- if (!_isPositiveEffect(i, 0, false))
- {
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_NEGATIVE_EFF0;
- ++count;
- }
- if (!_isPositiveEffect(i, 1, false))
- {
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_NEGATIVE_EFF1;
- ++count;
- }
- if (!_isPositiveEffect(i, 2, false))
- {
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_NEGATIVE_EFF2;
- ++count;
- }
+ if (!spellInfo->_IsPositiveEffect(EFFECT_1, false))
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF1;
- if (spellInfo->SpellVisual[0] == 3879)
- {
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_CONE_BACK;
- ++count;
- }
+ if (!spellInfo->_IsPositiveEffect(EFFECT_2, false))
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF2;
- if (spellInfo->activeIconID == 2158) // flight
- {
- spellInfo->Attributes |= SPELL_ATTR0_PASSIVE;
- ++count;
- }
+ if (spellInfo->SpellVisual[0] == 3879)
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_CONE_BACK;
- // TODO: this REALLY needs to be moved to db (so it can be blamed on db guys)
- switch (i)
+ switch (spellInfo->Id)
{
case 1776: // Gouge
case 1777:
@@ -3865,8 +2691,7 @@ void SpellMgr::LoadSpellCustomAttr()
case 38764:
case 38863:
case 52743: // Head Smack
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER;
- ++count;
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER;
break;
case 53: // Backstab
case 2589:
@@ -3913,46 +2738,203 @@ void SpellMgr::LoadSpellCustomAttr()
case 23959: // Test Stab R50
case 24825: // Test Backstab
case 58563: // Assassinate Restless Lookout
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET;
- ++count;
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET;
+ break;
+ case 26029: // Dark Glare
+ case 37433: // Spout
+ case 43140: // Flame Breath
+ case 43215: // Flame Breath
+ case 70461: // Coldflame Trap
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_CONE_LINE;
+ break;
+ case 24340: // Meteor
+ case 26558: // Meteor
+ case 28884: // Meteor
+ case 36837: // Meteor
+ case 38903: // Meteor
+ case 41276: // Meteor
+ case 57467: // Meteor
+ case 26789: // Shard of the Fallen Star
+ case 31436: // Malevolent Cleave
+ case 35181: // Dive Bomb
+ case 40810: // Saber Lash
+ case 43267: // Saber Lash
+ case 43268: // Saber Lash
+ case 42384: // Brutal Swipe
+ case 45150: // Meteor Slash
+ case 64688: // Sonic Screech
+ case 72373: // Shared Suffering
+ case 71904: // Chaos Bane
+ case 70492: // Ooze Eruption
+ case 72505: // Ooze Eruption
+ case 72624: // Ooze Eruption
+ case 72625: // Ooze Eruption
+ // ONLY SPELLS WITH SPELLFAMILY_GENERIC and EFFECT_SCHOOL_DAMAGE
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_SHARE_DAMAGE;
+ break;
+ case 27820: // Mana Detonation
+ case 69782: // Ooze Flood
+ case 69796: // Ooze Flood
+ case 69798: // Ooze Flood
+ case 69801: // Ooze Flood
+ case 69538: // Ooze Combine
+ case 69553: // Ooze Combine
+ case 69610: // Ooze Combine
+ case 71447: // Bloodbolt Splash
+ case 71481: // Bloodbolt Splash
+ case 71482: // Bloodbolt Splash
+ case 71483: // Bloodbolt Splash
+ case 71390: // Pact of the Darkfallen
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_EXCLUDE_SELF;
+ break;
+ case 18500: // Wing Buffet
+ case 33086: // Wild Bite
+ case 49749: // Piercing Blow
+ case 52890: // Penetrating Strike
+ case 53454: // Impale
+ case 59446: // Impale
+ case 62383: // Shatter
+ case 64777: // Machine Gun
+ case 65239: // Machine Gun
+ case 65919: // Impale
+ case 67858: // Impale
+ case 67859: // Impale
+ case 67860: // Impale
+ case 69293: // Wing Buffet
+ case 74439: // Machine Gun
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_IGNORE_ARMOR;
+ break;
+ case 63278: // Mark of the Faceless (General Vezax)
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_IGNORE_ARMOR;
+ break;
+ case 64422: // Sonic Screech (Auriaya)
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_SHARE_DAMAGE;
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_IGNORE_ARMOR;
+ break;
+ }
+
+ switch (spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_WARRIOR:
+ // Shout
+ if (spellInfo->SpellFamilyFlags[0] & 0x20000 || spellInfo->SpellFamilyFlags[1] & 0x20)
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CC;
+ break;
+ case SPELLFAMILY_DRUID:
+ // Roar
+ if (spellInfo->SpellFamilyFlags[0] & 0x8)
+ spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CC;
+ break;
+ default:
+ break;
+ }
+ }
+
+ CreatureAI::FillAISpellInfo();
+
+ sLog->outString(">> Loaded spell custom attributes in %u ms", GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString();
+}
+
+void SpellMgr::LoadDbcDataCorrections()
+{
+ uint32 oldMSTime = getMSTime();
+
+ SpellEntry* spellInfo = NULL;
+ for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
+ {
+ spellInfo = (SpellEntry*)sSpellStore.LookupEntry(i);
+ if (!spellInfo)
+ continue;
+
+ for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
+ {
+ switch (spellInfo->Effect[j])
+ {
+ case SPELL_EFFECT_CHARGE:
+ case SPELL_EFFECT_CHARGE_DEST:
+ case SPELL_EFFECT_JUMP:
+ case SPELL_EFFECT_JUMP_DEST:
+ case SPELL_EFFECT_LEAP_BACK:
+ if (!spellInfo->speed && !spellInfo->SpellFamilyName)
+ spellInfo->speed = SPEED_CHARGE;
+ break;
+ case SPELL_EFFECT_TRIGGER_SPELL:
+ if (SpellImplicitTargetInfo::IsPosition(spellInfo->EffectImplicitTargetA[j]) ||
+ spellInfo->Targets & (TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION))
+ spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE;
+ break;
+ }
+
+ switch (SpellImplicitTargetInfo::Type[spellInfo->EffectImplicitTargetA[j]])
+ {
+ case TARGET_TYPE_UNIT_TARGET:
+ case TARGET_TYPE_DEST_TARGET:
+ spellInfo->Targets |= TARGET_FLAG_UNIT;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (spellInfo->activeIconID == 2158) // flight
+ {
+ spellInfo->Attributes |= SPELL_ATTR0_PASSIVE;
+ }
+
+ switch (spellInfo->Id)
+ {
+ case 42835: // Spout
+ spellInfo->Effect[0] = 0; // remove damage effect, only anim is needed
+ break;
+ case 30657: // Quake
+ spellInfo->EffectTriggerSpell[0] = 30571;
+ break;
+ case 30541: // Blaze (needs conditions entry)
+ spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_TARGET_ENEMY;
+ spellInfo->EffectImplicitTargetB[0] = 0;
+ break;
+ case 31447: // Mark of Kaz'rogal (needs target selection script)
+ case 31298: // Sleep (needs target selection script)
+ spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER;
+ spellInfo->EffectImplicitTargetB[0] = 0;
+ break;
+ case 31344: // Howl of Azgalor
+ spellInfo->EffectRadiusIndex[0] = 12; // 100yards instead of 50000?!
+ break;
+ case 42818: // Headless Horseman - Wisp Flight Port
+ case 42821: // Headless Horseman - Wisp Flight Missile
+ spellInfo->rangeIndex = 6; // 100 yards
break;
case 36350: //They Must Burn Bomb Aura (self)
spellInfo->EffectTriggerSpell[0] = 36325; // They Must Burn Bomb Drop (DND)
- ++count;
break;
case 49838: // Stop Time
spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_INITIAL_AGGRO;
- ++count;
break;
case 61407: // Energize Cores
case 62136: // Energize Cores
case 54069: // Energize Cores
case 56251: // Energize Cores
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_AREA_ENTRY_SRC;
- ++count;
break;
case 50785: // Energize Cores
case 59372: // Energize Cores
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_AREA_ENEMY_SRC;
- ++count;
break;
case 3286: // Bind
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_TARGET_ENEMY;
spellInfo->EffectImplicitTargetA[1] = TARGET_UNIT_TARGET_ENEMY;
- ++count;
break;
case 8494: // Mana Shield (rank 2)
// because of bug in dbc
spellInfo->procChance = 0;
- ++count;
break;
case 32182: // Heroism
spellInfo->excludeCasterAuraSpell = 57723; // Exhaustion
- ++count;
break;
case 2825: // Bloodlust
spellInfo->excludeCasterAuraSpell = 57724; // Sated
- ++count;
break;
case 20335: // Heart of the Crusader
case 20336:
@@ -3960,69 +2942,16 @@ void SpellMgr::LoadSpellCustomAttr()
case 63320: // Glyph of Life Tap
// Entries were not updated after spell effect change, we have to do that manually :/
spellInfo->AttributesEx3 |= SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED;
- ++count;
break;
case 16007: // Draco-Incarcinatrix 900
// was 46, but effect is aura effect
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_NEARBY_ENTRY;
spellInfo->EffectImplicitTargetB[0] = TARGET_DST_NEARBY_ENTRY;
- ++count;
- break;
- case 26029: // Dark Glare
- case 37433: // Spout
- case 43140: // Flame Breath
- case 43215: // Flame Breath
- case 70461: // Coldflame Trap
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_CONE_LINE;
- ++count;
- break;
- case 24340: // Meteor
- case 26558: // Meteor
- case 28884: // Meteor
- case 36837: // Meteor
- case 38903: // Meteor
- case 41276: // Meteor
- case 57467: // Meteor
- case 26789: // Shard of the Fallen Star
- case 31436: // Malevolent Cleave
- case 35181: // Dive Bomb
- case 40810: // Saber Lash
- case 43267: // Saber Lash
- case 43268: // Saber Lash
- case 42384: // Brutal Swipe
- case 45150: // Meteor Slash
- case 64688: // Sonic Screech
- case 72373: // Shared Suffering
- case 71904: // Chaos Bane
- case 70492: // Ooze Eruption
- case 72505: // Ooze Eruption
- case 72624: // Ooze Eruption
- case 72625: // Ooze Eruption
- // ONLY SPELLS WITH SPELLFAMILY_GENERIC and EFFECT_SCHOOL_DAMAGE
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_SHARE_DAMAGE;
- ++count;
break;
case 59725: // Improved Spell Reflection - aoe aura
// Target entry seems to be wrong for this spell :/
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_PARTY_CASTER;
spellInfo->EffectRadiusIndex[0] = 45;
- ++count;
- break;
- case 27820: // Mana Detonation
- case 69782: // Ooze Flood
- case 69796: // Ooze Flood
- case 69798: // Ooze Flood
- case 69801: // Ooze Flood
- case 69538: // Ooze Combine
- case 69553: // Ooze Combine
- case 69610: // Ooze Combine
- case 71447: // Bloodbolt Splash
- case 71481: // Bloodbolt Splash
- case 71482: // Bloodbolt Splash
- case 71483: // Bloodbolt Splash
- case 71390: // Pact of the Darkfallen
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_EXCLUDE_SELF;
- ++count;
break;
case 44978: case 45001: case 45002: // Wild Magic
case 45004: case 45006: case 45010: // Wild Magic
@@ -4042,14 +2971,12 @@ void SpellMgr::LoadSpellCustomAttr()
case 62374: // Pursued
case 61588: // Blazing Harpoon
spellInfo->MaxAffectedTargets = 1;
- ++count;
break;
case 52479: // Gift of the Harvester
spellInfo->MaxAffectedTargets = 1;
// a trap always has dst = src?
spellInfo->EffectImplicitTargetA[0] = TARGET_DST_CASTER;
spellInfo->EffectImplicitTargetA[1] = TARGET_DST_CASTER;
- ++count;
break;
case 41376: // Spite
case 39992: // Needle Spine
@@ -4065,12 +2992,10 @@ void SpellMgr::LoadSpellCustomAttr()
case 66588: // Flaming Spear
case 54171: // Divine Storm
spellInfo->MaxAffectedTargets = 3;
- ++count;
break;
case 38310: // Multi-Shot
case 53385: // Divine Storm (Damage)
spellInfo->MaxAffectedTargets = 4;
- ++count;
break;
case 42005: // Bloodboil
case 38296: // Spitfire Totem
@@ -4080,7 +3005,6 @@ void SpellMgr::LoadSpellCustomAttr()
case 55665: // Life Drain - Sapphiron (H)
case 28796: // Poison Bolt Volly - Faerlina
spellInfo->MaxAffectedTargets = 5;
- ++count;
break;
case 40827: // Sinful Beam
case 40859: // Sinister Beam
@@ -4089,16 +3013,13 @@ void SpellMgr::LoadSpellCustomAttr()
case 54835: // Curse of the Plaguebringer - Noth (H)
case 54098: // Poison Bolt Volly - Faerlina (H)
spellInfo->MaxAffectedTargets = 10;
- ++count;
break;
case 50312: // Unholy Frenzy
spellInfo->MaxAffectedTargets = 15;
- ++count;
break;
case 38794: case 33711: //Murmur's Touch
spellInfo->MaxAffectedTargets = 1;
spellInfo->EffectTriggerSpell[0] = 33760;
- ++count;
break;
case 17941: // Shadow Trance
case 22008: // Netherwind Focus
@@ -4113,20 +3034,16 @@ void SpellMgr::LoadSpellCustomAttr()
case 64823: // Item - Druid T8 Balance 4P Bonus
case 44401: // Missile Barrage
spellInfo->procCharges = 1;
- ++count;
break;
case 44544: // Fingers of Frost
spellInfo->EffectSpellClassMask[0] = flag96(685904631, 1151048, 0);
- ++count;
break;
case 74396: // Fingers of Frost visual buff
spellInfo->procCharges = 2;
spellInfo->StackAmount = 0;
- ++count;
break;
case 28200: // Ascendance (Talisman of Ascendance trinket)
spellInfo->procCharges = 6;
- ++count;
break;
case 47201: // Everlasting Affliction
case 47202:
@@ -4135,63 +3052,50 @@ void SpellMgr::LoadSpellCustomAttr()
case 47205:
// add corruption to affected spells
spellInfo->EffectSpellClassMask[1][0] |= 2;
- ++count;
break;
case 49305: // Teleport to Boss 1 DND
case 64981: // Summon Random Vanquished Tentacle
spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_CASTER;
- ++count;
break;
case 51852: // The Eye of Acherus (no spawn in phase 2 in db)
spellInfo->EffectMiscValue[0] |= 1;
- ++count;
break;
case 51904: // Summon Ghouls On Scarlet Crusade (core does not know the triggered spell is summon spell)
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER;
- ++count;
break;
case 29809: // Desecration Arm - 36 instead of 37 - typo? :/
spellInfo->EffectRadiusIndex[0] = 37;
- ++count;
break;
// Master Shapeshifter: missing stance data for forms other than bear - bear version has correct data
// To prevent aura staying on target after talent unlearned
case 48420:
spellInfo->Stances = 1 << (FORM_CAT - 1);
- ++count;
break;
case 48421:
spellInfo->Stances = 1 << (FORM_MOONKIN - 1);
- ++count;
break;
case 48422:
spellInfo->Stances = 1 << (FORM_TREE - 1);
- ++count;
break;
case 47569: // Improved Shadowform (Rank 1)
// with this spell atrribute aura can be stacked several times
spellInfo->Attributes &= ~SPELL_ATTR0_NOT_SHAPESHIFT;
- ++count;
break;
case 30421: // Nether Portal - Perseverence
spellInfo->EffectBasePoints[2] += 30000;
- ++count;
break;
case 16834: // Natural shapeshifter
case 16835:
spellInfo->DurationIndex = 21;
- ++count;
break;
case 51735: // Ebon Plague
case 51734:
case 51726:
spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS;
spellInfo->SpellFamilyFlags[2] = 0x10;
- ++count;
break;
case 41013: // Parasitic Shadowfiend Passive
spellInfo->EffectApplyAuraName[0] = 4; // proc debuff, and summon infinite fiends
- ++count;
break;
case 27892: // To Anchor 1
case 27928: // To Anchor 1
@@ -4200,11 +3104,9 @@ void SpellMgr::LoadSpellCustomAttr()
case 27931: // Anchor to Skulls
case 27937: // Anchor to Skulls
spellInfo->rangeIndex = 13;
- ++count;
break;
case 48743: // Death Pact
spellInfo->AttributesEx &= ~SPELL_ATTR1_CANT_TARGET_SELF;
- ++count;
break;
// target allys instead of enemies, target A is src_caster, spells with effect like that have ally target
// this is the only known exception, probably just wrong data
@@ -4212,39 +3114,17 @@ void SpellMgr::LoadSpellCustomAttr()
case 54836: // Wrath of the Plaguebringer
spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_AREA_ALLY_SRC;
spellInfo->EffectImplicitTargetB[1] = TARGET_UNIT_AREA_ALLY_SRC;
- ++count;
break;
case 31687: // Summon Water Elemental
// 322-330 switch - effect changed to dummy, target entry not changed in client:(
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER;
- ++count;
- break;
- case 18500: // Wing Buffet
- case 33086: // Wild Bite
- case 49749: // Piercing Blow
- case 52890: // Penetrating Strike
- case 53454: // Impale
- case 59446: // Impale
- case 62383: // Shatter
- case 64777: // Machine Gun
- case 65239: // Machine Gun
- case 65919: // Impale
- case 67858: // Impale
- case 67859: // Impale
- case 67860: // Impale
- case 69293: // Wing Buffet
- case 74439: // Machine Gun
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_IGNORE_ARMOR;
- ++count;
break;
case 63675: // Improved Devouring Plague
spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS;
- ++count;
break;
case 8145: // Tremor Totem (instant pulse)
case 6474: // Earthbind Totem (instant pulse)
spellInfo->AttributesEx5 |= SPELL_ATTR5_START_PERIODIC_AT_APPLY;
- ++count;
break;
case 53241: // Marked for Death (Rank 1)
case 53243: // Marked for Death (Rank 2)
@@ -4252,25 +3132,21 @@ void SpellMgr::LoadSpellCustomAttr()
case 53245: // Marked for Death (Rank 4)
case 53246: // Marked for Death (Rank 5)
spellInfo->EffectSpellClassMask[0] = flag96(423937, 276955137, 2049);
- ++count;
break;
case 70728: // Exploit Weakness
case 70840: // Devious Minds
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER;
spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_PET;
- ++count;
break;
case 70893: // Culling The Herd
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER;
spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_MASTER;
- ++count;
break;
case 54800: // Sigil of the Frozen Conscience - change class mask to custom extended flags of Icy Touch
// this is done because another spell also uses the same SpellFamilyFlags as Icy Touch
// SpellFamilyFlags[0] & 0x00000040 in SPELLFAMILY_DEATHKNIGHT is currently unused (3.3.5a)
// this needs research on modifier applying rules, does not seem to be in Attributes fields
spellInfo->EffectSpellClassMask[0] = flag96(0x00000040, 0x00000000, 0x00000000);
- ++count;
break;
case 19970: // Entangling Roots (Rank 6) -- Nature's Grasp Proc
case 19971: // Entangling Roots (Rank 5) -- Nature's Grasp Proc
@@ -4281,30 +3157,25 @@ void SpellMgr::LoadSpellCustomAttr()
case 27010: // Entangling Roots (Rank 7) -- Nature's Grasp Proc
case 53313: // Entangling Roots (Rank 8) -- Nature's Grasp Proc
spellInfo->CastingTimeIndex = 1;
- ++count;
break;
case 61719: // Easter Lay Noblegarden Egg Aura - Interrupt flags copied from aura which this aura is linked with
spellInfo->AuraInterruptFlags = AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_TAKE_DAMAGE;
- ++count;
break;
// ULDUAR SPELLS
//
case 63342: // Focused Eyebeam Summon Trigger (Kologarn)
spellInfo->MaxAffectedTargets = 1;
- ++count;
break;
case 62716: // Growth of Nature (Freya)
case 65584: // Growth of Nature (Freya)
case 64381: // Strength of the Pack (Auriaya)
spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS;
- ++count;
break;
case 63018: // Searing Light (XT-002)
case 65121: // Searing Light (25m) (XT-002)
case 63024: // Gravity Bomb (XT-002)
case 64234: // Gravity Bomb (25m) (XT-002)
spellInfo->MaxAffectedTargets = 1;
- ++count;
break;
case 62834: // Boom (XT-002)
// This hack is here because we suspect our implementation of spell effect execution on targets
@@ -4313,29 +3184,17 @@ void SpellMgr::LoadSpellCustomAttr()
// The above situation causes the visual for this spell to be bugged, so we remove the instakill
// effect and implement a script hack for that.
spellInfo->Effect[EFFECT_1] = 0;
- ++count;
break;
case 64386: // Terrifying Screech (Auriaya)
case 64389: // Sentinel Blast (Auriaya)
case 64678: // Sentinel Blast (Auriaya)
spellInfo->DurationIndex = 28; // 5 seconds, wrong DBC data?
- ++count;
- break;
- case 63278: // Mark of the Faceless (General Vezax)
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_IGNORE_ARMOR;
- ++count;
- break;
- case 64422: // Sonic Screech (Auriaya)
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_SHARE_DAMAGE;
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_IGNORE_ARMOR;
- ++count;
break;
case 64321: // Potent Pheromones (Freya)
// spell should dispel area aura, but doesn't have the attribute
// may be db data bug, or blizz may keep reapplying area auras every update with checking immunity
// that will be clear if we get more spells with problem like this
spellInfo->AttributesEx |= SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY;
- ++count;
break;
// ENDOF ULDUAR SPELLS
//
@@ -4346,7 +3205,6 @@ void SpellMgr::LoadSpellCustomAttr()
// increase duration from 15 to 18 seconds because caster is already
// unsummoned when spell missile hits the ground so nothing happen in result
spellInfo->DurationIndex = 85;
- ++count;
break;
// ENDOF TRIAL OF THE CRUSADER SPELLS
//
@@ -4363,12 +3221,10 @@ void SpellMgr::LoadSpellCustomAttr()
case 70860: // Frozen Throne Teleport
case 70861: // Sindragosa's Lair Teleport
spellInfo->EffectImplicitTargetA[0] = TARGET_DST_DB;
- ++count;
break;
case 69055: // Saber Lash (Lord Marrowgar)
case 70814: // Saber Lash (Lord Marrowgar)
spellInfo->EffectRadiusIndex[0] = 8; // 5yd
- ++count;
break;
case 69075: // Bone Storm (Lord Marrowgar)
case 70834: // Bone Storm (Lord Marrowgar)
@@ -4381,25 +3237,20 @@ void SpellMgr::LoadSpellCustomAttr()
case 71161: // Plague Stench (Stinky)
case 71123: // Decimate (Stinky & Precious)
spellInfo->EffectRadiusIndex[0] = 12; // 100yd
- ++count;
break;
case 72723: // Resistant Skin (Deathbringer Saurfang adds)
// this spell initially granted Shadow damage immunity, however it was removed but the data was left in client
spellInfo->Effect[2] = 0;
- ++count;
break;
case 70460: // Coldflame Jets (Traps after Saurfang)
spellInfo->DurationIndex = 1; // 10 seconds
- ++count;
break;
case 71413: // Green Ooze Summon (Professor Putricide)
case 71414: // Orange Ooze Summon (Professor Putricide)
spellInfo->EffectImplicitTargetA[0] = TARGET_DEST_DEST;
- ++count;
break;
case 71159: // Awaken Plagued Zombies
spellInfo->DurationIndex = 21;
- ++count;
break;
// THIS IS HERE BECAUSE COOLDOWN ON CREATURE PROCS IS NOT IMPLEMENTED
case 71604: // Mutated Strength (Professor Putricide)
@@ -4407,65 +3258,53 @@ void SpellMgr::LoadSpellCustomAttr()
case 72674: // Mutated Strength (Professor Putricide)
case 72675: // Mutated Strength (Professor Putricide)
spellInfo->Effect[1] = 0;
- ++count;
break;
case 70911: // Unbound Plague (Professor Putricide)
case 72854: // Unbound Plague (Professor Putricide)
case 72855: // Unbound Plague (Professor Putricide)
case 72856: // Unbound Plague (Professor Putricide)
spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_TARGET_ENEMY;
- ++count;
break;
case 71518: // Unholy Infusion Quest Credit (Professor Putricide)
case 72934: // Blood Infusion Quest Credit (Blood-Queen Lana'thel)
case 72289: // Frost Infusion Quest Credit (Sindragosa)
spellInfo->EffectRadiusIndex[0] = 28; // another missing radius
- ++count;
break;
case 71708: // Empowered Flare (Blood Prince Council)
case 72785: // Empowered Flare (Blood Prince Council)
case 72786: // Empowered Flare (Blood Prince Council)
case 72787: // Empowered Flare (Blood Prince Council)
spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS;
- ++count;
break;
case 71266: // Swarming Shadows
case 72890: // Swarming Shadows
spellInfo->AreaGroupId = 0; // originally, these require area 4522, which is... outside of Icecrown Citadel
- ++count;
break;
case 70602: // Corruption
spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS;
- ++count;
break;
case 70715: // Column of Frost (visual marker)
spellInfo->DurationIndex = 32; // 6 seconds (missing)
- ++count;
break;
case 71085: // Mana Void (periodic aura)
spellInfo->DurationIndex = 9; // 30 seconds (missing)
- ++count;
break;
case 70936: // Summon Suppressor
spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_TARGET_ANY;
spellInfo->EffectImplicitTargetB[0] = 0;
- ++count;
break;
case 72706: // Achievement Check (Valithria Dreamwalker)
case 71357: // Order Whelp
spellInfo->EffectRadiusIndex[0] = 22; // 200yd
- ++count;
break;
case 70598: // Sindragosa's Fury
spellInfo->EffectImplicitTargetA[0] = TARGET_DST_CASTER;
- ++count;
break;
case 69846: // Frost Bomb
spellInfo->speed = 10;
spellInfo->EffectImplicitTargetA[0] = TARGET_DEST_TARGET_ANY;
spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_TARGET_ANY;
spellInfo->Effect[1] = 0;
- ++count;
break;
default:
break;
@@ -4473,24 +3312,12 @@ void SpellMgr::LoadSpellCustomAttr()
switch (spellInfo->SpellFamilyName)
{
- case SPELLFAMILY_WARRIOR:
- // Shout
- if (spellInfo->SpellFamilyFlags[0] & 0x20000 || spellInfo->SpellFamilyFlags[1] & 0x20)
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_AURA_CC;
- else
- break;
- ++count;
- break;
case SPELLFAMILY_DRUID:
// Starfall Target Selection
if (spellInfo->SpellFamilyFlags[2] & 0x100)
spellInfo->MaxAffectedTargets = 2;
- // Roar
- else if (spellInfo->SpellFamilyFlags[0] & 0x8)
- mSpellCustomAttr[i] |= SPELL_ATTR0_CU_AURA_CC;
else
break;
- ++count;
break;
case SPELLFAMILY_PALADIN:
// Seals of the Pure should affect Seal of Righteousness
@@ -4498,13 +3325,11 @@ void SpellMgr::LoadSpellCustomAttr()
spellInfo->EffectSpellClassMask[0][1] |= 0x20000000;
else
break;
- ++count;
break;
case SPELLFAMILY_DEATHKNIGHT:
// Icy Touch - extend FamilyFlags (unused value) for Sigil of the Frozen Conscience to use
if (spellInfo->SpellIconID == 2721 && spellInfo->SpellFamilyFlags[0] & 0x2)
spellInfo->SpellFamilyFlags[0] |= 0x40;
- ++count;
break;
}
}
@@ -4514,118 +3339,6 @@ void SpellMgr::LoadSpellCustomAttr()
properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(647)); // 52893
properties->Type = SUMMON_TYPE_TOTEM;
- CreatureAI::FillAISpellInfo();
-
- sLog->outString(">> Loaded %u custom spell attributes in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
+ sLog->outString(">> Loading spell dbc data corrections in %u ms", GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
-}
-
-// Fill custom data about enchancments
-void SpellMgr::LoadEnchantCustomAttr()
-{
- uint32 oldMSTime = getMSTime();
-
- uint32 size = sSpellItemEnchantmentStore.GetNumRows();
- mEnchantCustomAttr.resize(size);
-
- uint32 count = 0;
-
- for (uint32 i = 0; i < size; ++i)
- mEnchantCustomAttr[i] = 0;
-
- for (uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
- {
- SpellEntry* spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i);
- if (!spellInfo)
- continue;
-
- // TODO: find a better check
- if (!(spellInfo->AttributesEx2 & SPELL_ATTR2_UNK13) || !(spellInfo->Attributes & SPELL_ATTR0_NOT_SHAPESHIFT))
- continue;
-
- for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j)
- {
- if (spellInfo->Effect[j] == SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY)
- {
- uint32 enchId = spellInfo->EffectMiscValue[j];
- SpellItemEnchantmentEntry const* ench = sSpellItemEnchantmentStore.LookupEntry(enchId);
- if (!ench)
- continue;
- mEnchantCustomAttr[enchId] = true;
- ++count;
- break;
- }
- }
- }
-
- sLog->outString(">> Loaded %u custom enchant attributes in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
-}
-
-void SpellMgr::LoadSpellLinked()
-{
- uint32 oldMSTime = getMSTime();
-
- mSpellLinkedMap.clear(); // need for reload case
-
- // 0 1 2
- QueryResult result = WorldDatabase.Query("SELECT spell_trigger, spell_effect, type FROM spell_linked_spell");
- if (!result)
- {
- sLog->outString(">> Loaded 0 linked spells. DB table `spell_linked_spell` is empty.");
- sLog->outString();
- return;
- }
-
- uint32 count = 0;
-
- do
- {
- Field *fields = result->Fetch();
-
- int32 trigger = fields[0].GetInt32();
- int32 effect = fields[1].GetInt32();
- int32 type = fields[2].GetInt32();
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(trigger));
- if (!spellInfo)
- {
- sLog->outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(trigger));
- continue;
- }
- spellInfo = sSpellStore.LookupEntry(abs(effect));
- if (!spellInfo)
- {
- sLog->outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(effect));
- continue;
- }
-
- if (trigger > 0)
- {
- switch (type)
- {
- case 0: mSpellCustomAttr[trigger] |= SPELL_ATTR0_CU_LINK_CAST; break;
- case 1: mSpellCustomAttr[trigger] |= SPELL_ATTR0_CU_LINK_HIT; break;
- case 2: mSpellCustomAttr[trigger] |= SPELL_ATTR0_CU_LINK_AURA; break;
- }
- }
- else
- {
- mSpellCustomAttr[-trigger] |= SPELL_ATTR0_CU_LINK_REMOVE;
- }
-
- if (type) //we will find a better way when more types are needed
- {
- if (trigger > 0)
- trigger += SPELL_LINKED_MAX_SPELLS * type;
- else
- trigger -= SPELL_LINKED_MAX_SPELLS * type;
- }
- mSpellLinkedMap[trigger].push_back(effect);
-
- ++count;
- } while (result->NextRow());
-
- sLog->outString(">> Loaded %u linked spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
- sLog->outString();
-}
+} \ No newline at end of file