aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/SpellMgr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/SpellMgr.cpp')
-rw-r--r--src/server/game/SpellMgr.cpp3888
1 files changed, 3888 insertions, 0 deletions
diff --git a/src/server/game/SpellMgr.cpp b/src/server/game/SpellMgr.cpp
new file mode 100644
index 00000000000..510fdccb98a
--- /dev/null
+++ b/src/server/game/SpellMgr.cpp
@@ -0,0 +1,3888 @@
+/*
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "SpellMgr.h"
+#include "ObjectMgr.h"
+#include "SpellAuraDefines.h"
+#include "ProgressBar.h"
+#include "DBCStores.h"
+#include "World.h"
+#include "Chat.h"
+#include "Spell.h"
+#include "BattleGroundMgr.h"
+#include "CreatureAI.h"
+#include "MapManager.h"
+
+bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS];
+SpellEffectTargetTypes EffectTargetType[TOTAL_SPELL_EFFECTS];
+SpellSelectTargetTypes SpellTargetType[TOTAL_SPELL_TARGETS];
+
+SpellMgr::SpellMgr()
+{
+ for (int 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_JUMP:
+ case SPELL_EFFECT_JUMP2:
+ 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 (int 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:
+ SpellTargetType[i] = TARGET_TYPE_UNIT_CASTER;
+ break;
+ case TARGET_UNIT_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_PARTY_TARGET:
+ case TARGET_UNIT_CLASS_TARGET:
+ case TARGET_UNIT_CHAINHEAL:
+ case TARGET_UNIT_UNK_92:
+ SpellTargetType[i] = TARGET_TYPE_UNIT_TARGET;
+ break;
+ case TARGET_UNIT_NEARBY_ENEMY:
+ case TARGET_UNIT_NEARBY_ALLY:
+ case TARGET_UNIT_NEARBY_ALLY_UNK:
+ case TARGET_UNIT_NEARBY_ENTRY:
+ 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_OBJECT_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_OBJECT_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:
+ 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:
+ case TARGET_DEST_CHANNEL:
+ SpellTargetType[i] = TARGET_TYPE_CHANNEL;
+ break;
+ default:
+ SpellTargetType[i] = TARGET_TYPE_DEFAULT;
+ }
+ }
+
+ for (int 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_PARTY_TARGET:
+ 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_UNIT_RAID_CASTER:
+ IsAreaEffectTarget[i] = true;
+ break;
+ default:
+ IsAreaEffectTarget[i] = false;
+ break;
+ }
+ }
+}
+
+SpellMgr::~SpellMgr()
+{
+}
+
+SpellMgr& SpellMgr::Instance()
+{
+ static SpellMgr spellMgr;
+ return 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]);
+}
+
+int32 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);
+
+ return 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 (spell && spell->GetCaster())
+ spell->GetCaster()->ModSpellCastTime(spellInfo, castTime, spell);
+
+ if (spellInfo->Attributes & SPELL_ATTR_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)
+ return false;
+ return IsPassiveSpell(spellInfo);
+}
+
+bool IsPassiveSpell(SpellEntry const * spellInfo)
+{
+ if (spellInfo->Attributes & SPELL_ATTR_PASSIVE)
+ return true;
+ return false;
+}
+
+bool IsAutocastableSpell(uint32 spellId)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if (!spellInfo)
+ return false;
+ if (spellInfo->Attributes & SPELL_ATTR_PASSIVE)
+ return false;
+ if (spellInfo->AttributesEx & SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET)
+ return false;
+ return true;
+}
+
+bool IsHigherHankOfSpell(uint32 spellId_1, uint32 spellId_2)
+{
+ return spellmgr.GetSpellRank(spellId_1)<spellmgr.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_ATTR_EX_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 += spellInfo->ManaCostPercentage * caster->GetCreateHealth() / 100;
+ break;
+ case POWER_MANA:
+ powerCost += spellInfo->ManaCostPercentage * caster->GetCreateMana() / 100;
+ break;
+ case POWER_RAGE:
+ case POWER_FOCUS:
+ case POWER_ENERGY:
+ case POWER_HAPPINESS:
+ powerCost += spellInfo->ManaCostPercentage * caster->GetMaxPower(Powers(spellInfo->powerType)) / 100;
+ break;
+ case POWER_RUNE:
+ case POWER_RUNIC_POWER:
+ sLog.outDebug("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_ATTR_EX4_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_ATTR_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;
+}
+
+Unit* GetTriggeredSpellCaster(SpellEntry const * spellInfo, Unit * caster, Unit * target)
+{
+ 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)
+ return caster;
+ }
+ return target;
+}
+
+AuraState GetSpellAuraState(SpellEntry const * spellInfo)
+{
+ // 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;
+ }
+ }
+ return AURA_STATE_NONE;
+}
+
+SpellSpecific GetSpellSpecific(SpellEntry const * spellInfo)
+{
+ switch(spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ // Food / Drinks (mostly)
+ if (spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED)
+ {
+ bool food = false;
+ bool drink = false;
+ for (int i = 0; i < 3; ++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 = spellmgr.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;
+ }
+ }
+ break;
+ }
+ 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;
+
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ if (spellInfo->Id == 12292) // Death Wish
+ return SPELL_SPECIFIC_WARRIOR_ENRAGE;
+
+ 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;
+ break;
+ }
+ case SPELLFAMILY_PRIEST:
+ {
+ // Divine Spirit and Prayer of Spirit
+ if (spellInfo->SpellFamilyFlags[0] & 0x20)
+ return SPELL_SPECIFIC_PRIEST_DIVINE_SPIRIT;
+
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // 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;
+
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ 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;
+
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ if (IsElementalShield(spellInfo))
+ return SPELL_SPECIFIC_ELEMENTAL_SHIELD;
+
+ break;
+ }
+
+ case SPELLFAMILY_DEATHKNIGHT:
+ if (spellInfo->Id == 48266 || spellInfo->Id == 48263 || spellInfo->Id == 48265)
+ //if (spellInfo->Category == 47)
+ return SPELL_SPECIFIC_PRESENCE;
+ break;
+ }
+
+ for (int i = 0; i < 3; ++i)
+ {
+ if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA)
+ {
+ 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;
+ }
+ }
+ }
+
+ return SPELL_SPECIFIC_NORMAL;
+}
+
+// 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;
+ }
+}
+
+bool IsSingleFromSpellSpecificPerTarget(SpellSpecific spellSpec1, SpellSpecific spellSpec2)
+{
+ switch(spellSpec1)
+ {
+ 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;
+ default:
+ return false;
+ }
+}
+
+bool IsPositiveTarget(uint32 targetA, uint32 targetB)
+{
+ // non-positive targets
+ switch(targetA)
+ {
+ 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_DEST_DYNOBJ_ENEMY:
+ case TARGET_DST_TARGET_ENEMY:
+ case TARGET_UNIT_CHANNEL:
+ return false;
+ default:
+ break;
+ }
+ if (targetB)
+ return IsPositiveTarget(targetB, 0);
+ return true;
+}
+
+bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) const
+{
+ SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
+ if (!spellproto) return false;
+
+ // not found a single positive spell with this attribute
+ if (spellproto->Attributes & SPELL_ATTR_NEGATIVE_1)
+ return false;
+
+ switch(spellId)
+ {
+ //case 37675: // Chaos Blast removed from mangos
+ case 34700: // Allergic Reaction
+ case 61987: // Avenging Wrath Marker
+ case 61988: // Divine Shield exclude aura
+ case 50524: // Runic Power Feed
+ return false;
+ case 12042: // Arcane Power
+ case 30877: // Tag Murloc
+ return true;
+ }
+
+ switch(spellproto->Mechanic)
+ {
+ case MECHANIC_IMMUNE_SHIELD:
+ return true;
+ default:
+ break;
+ }
+
+ // 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;
+ }
+
+ 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;
+
+ // non-positive aura use
+ case SPELL_EFFECT_APPLY_AURA:
+ case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
+ {
+ switch(spellproto->EffectApplyAuraName[effIndex])
+ {
+ 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_HEALING_PCT:
+ case SPELL_AURA_MOD_HEALING_DONE:
+ case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
+ if (spellproto->CalculateSimpleValue(effIndex) < 0)
+ return false;
+ break;
+ case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from bas point sign (positive -> negative)
+ if (spellproto->CalculateSimpleValue(effIndex) > 0)
+ return false;
+ break;
+ case SPELL_AURA_MOD_CRIT_PCT:
+ case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
+ if (spellproto->CalculateSimpleValue(effIndex) > 0)
+ return true; // some expected positive spells have SPELL_ATTR_EX_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)
+ {
+ uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex];
+ SpellEntry const *spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId);
+
+ if (spellTriggeredProto)
+ {
+ // non-positive targets of main spell return early
+ for (int i = 0; i < 3; ++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;
+ }
+ }
+ }
+ 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:
+ 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 & 0x4000000 && effIndex == 0)
+ return false;
+ break;
+ case SPELL_AURA_MECHANIC_IMMUNITY:
+ {
+ // non-positive immunities
+ switch(spellproto->EffectMiscValue[effIndex])
+ {
+ case MECHANIC_BANDAGE:
+ case MECHANIC_SHIELD:
+ case MECHANIC_MOUNT:
+ case MECHANIC_INVULNERABILITY:
+ return false;
+ default:
+ break;
+ }
+ } break;
+ case SPELL_AURA_ADD_FLAT_MODIFIER: // mods
+ case SPELL_AURA_ADD_PCT_MODIFIER:
+ {
+ // non-positive mods
+ switch(spellproto->EffectMiscValue[effIndex])
+ {
+ case SPELLMOD_COST: // dependent from bas point sign (negative -> positive)
+ if (spellproto->CalculateSimpleValue(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;
+ }
+ } break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // non-positive targets
+ if (!IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex]))
+ return false;
+
+ // AttributesEx check
+ if (spellproto->AttributesEx & SPELL_ATTR_EX_NEGATIVE)
+ return false;
+
+ if (!deep && spellproto->EffectTriggerSpell[effIndex]
+ && !spellproto->EffectApplyAuraName[effIndex]
+ && IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex])
+ && !_isPositiveSpell(spellproto->EffectTriggerSpell[effIndex], true))
+ return false;
+
+ // ok, positive
+ return true;
+}
+
+bool IsPositiveSpell(uint32 spellId)
+{
+ if (!sSpellStore.LookupEntry(spellId)) // non-existing spells
+ return false;
+ return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE);
+}
+
+bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
+{
+ if (!sSpellStore.LookupEntry(spellId))
+ return false;
+ switch(effIndex)
+ {
+ default:
+ case 0: return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE_EFF0);
+ case 1: return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE_EFF1);
+ case 2: return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE_EFF2);
+ }
+}
+
+bool SpellMgr::_isPositiveSpell(uint32 spellId, bool deep) const
+{
+ SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
+ if (!spellproto) return false;
+
+ // 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 (int i = 0; i < 3; ++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_ATTR_EX5_SINGLE_TARGET_SPELL)
+ return true;
+
+ switch(GetSpellSpecific(spellInfo))
+ {
+ case SPELL_SPECIFIC_JUDGEMENT:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+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)
+ {
+ case SPELL_SPECIFIC_JUDGEMENT:
+ case SPELL_SPECIFIC_MAGE_POLYMORPH:
+ if (GetSpellSpecific(spellInfo2) == spec1)
+ return true;
+ break;
+ default:
+ break;
+ }
+
+ 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)
+ {
+ 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
+ }
+
+ if (actAsShifted)
+ {
+ if (spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) // not while shapeshifted
+ return SPELL_FAILED_NOT_SHAPESHIFT;
+ else if (spellInfo->Stances != 0) // needs other shapeshift
+ return SPELL_FAILED_ONLY_SHAPESHIFT;
+ }
+ else
+ {
+ // needs shapeshift
+ if (!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT) && spellInfo->Stances != 0)
+ return SPELL_FAILED_ONLY_SHAPESHIFT;
+ }
+
+ // 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)
+ {
+ if (!(stanceMask & spellInfo->Stances))
+ return SPELL_FAILED_ONLY_SHAPESHIFT;
+ }
+
+ return SPELL_CAST_OK;
+}
+
+void SpellMgr::LoadSpellTargetPositions()
+{
+ mSpellTargetPositions.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM spell_target_position");
+ if (!result)
+ {
+
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell target coordinates", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ 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;
+ }
+
+ 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;
+ }
+
+ bool found = false;
+ for (int i = 0; i < 3; ++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 = MapManager::Instance().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;
+ }
+ }
+
+ 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 < sSpellStore.GetNumRows(); ++i)
+ {
+ SpellEntry const * spellInfo = sSpellStore.LookupEntry(i);
+ if (!spellInfo)
+ continue;
+
+ bool found = false;
+ for (int j = 0; j < 3; ++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 (!spellmgr.GetSpellTargetPosition(i))
+// sLog.outDebug("Spell (ID: %u) does not have record in `spell_target_position`", i);
+ }
+ }
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell teleport coordinates", count);
+}
+
+bool SpellMgr::IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const
+{
+ // false for spellInfo == NULL
+ if (!spellInfo || !mod)
+ return false;
+
+ 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;
+
+ // true
+ if (mod->mask & spellInfo->SpellFamilyFlags)
+ return true;
+
+ return false;
+}
+
+void SpellMgr::LoadSpellProcEvents()
+{
+ mSpellProcEventMap.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6 7 8 9 10
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event");
+ if (!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell proc event conditions", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+ uint32 customProc = 0;
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ const SpellEntry *spell = sSpellStore.LookupEntry(entry);
+ if (!spell)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry);
+ continue;
+ }
+
+ 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();
+ if (customProc)
+ sLog.outString(">> Loaded %u extra spell proc event conditions + %u custom", count, customProc);
+ else
+ sLog.outString(">> Loaded %u extra spell proc event conditions", count);
+}
+
+void SpellMgr::LoadSpellBonusess()
+{
+ mSpellBonusMap.clear(); // need for reload case
+ uint32 count = 0;
+ // 0 1 2 3 4
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, direct_bonus, dot_bonus, ap_bonus, ap_dot_bonus FROM spell_bonus_data");
+ if (!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell bonus data", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+ uint32 entry = fields[0].GetUInt32();
+
+ const SpellEntry *spell = sSpellStore.LookupEntry(entry);
+ if (!spell)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry);
+ continue;
+ }
+
+ SpellBonusEntry sbe;
+
+ 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();
+
+ mSpellBonusMap[entry] = sbe;
+ ++count;
+ } while (result->NextRow());
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u extra spell bonus data", count);
+}
+
+bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active)
+{
+ // No extra req need
+ uint32 procEvent_procEx = PROC_EX_NONE;
+
+ // check prockFlags for condition
+ if ((procFlags & EventProcFlag) == 0)
+ return false;
+
+ bool hasFamilyMask = false;
+
+ /* Check Periodic Auras
+
+ *Dots can trigger if spell has no PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL
+ nor PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL
+
+ *Only Hots can trigger if spell has PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL
+
+ *Only dots can trigger if spell has both positivity flags or PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL
+
+ *Aura has to have PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL or spellfamily specified to trigger from Hot
+
+ */
+
+ if (procFlags & PROC_FLAG_ON_DO_PERIODIC)
+ {
+ if (EventProcFlag & PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL)
+ {
+ if (!(procExtra & PROC_EX_INTERNAL_DOT))
+ return false;
+ }
+ else if (procExtra & PROC_EX_INTERNAL_HOT)
+ procExtra |= PROC_EX_INTERNAL_REQ_FAMILY;
+ else if (EventProcFlag & PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL)
+ return false;
+ }
+
+ if (procFlags & PROC_FLAG_ON_TAKE_PERIODIC)
+ {
+ if (EventProcFlag & PROC_FLAG_TAKEN_NEGATIVE_MAGIC_SPELL)
+ {
+ if (!(procExtra & PROC_EX_INTERNAL_DOT))
+ return false;
+ }
+ else if (procExtra & PROC_EX_INTERNAL_HOT)
+ procExtra |= PROC_EX_INTERNAL_REQ_FAMILY;
+ else if (EventProcFlag & PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL)
+ return false;
+ }
+ // Trap casts are active by default
+ if (procFlags & PROC_FLAG_ON_TRAP_ACTIVATION)
+ active = true;
+
+ // Always trigger for this
+ if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH))
+ return true;
+
+ if (spellProcEvent) // Exist event data
+ {
+ // Store extra req
+ procEvent_procEx = spellProcEvent->procEx;
+
+ // For melee triggers
+ if (procSpell == NULL)
+ {
+ // Check (if set) for school (melee attack have Normal school)
+ if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0)
+ return false;
+ }
+ else // For spells need check school/spell family/family mask
+ {
+ // Check (if set) for school
+ if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpell->SchoolMask) == 0)
+ return false;
+
+ // Check (if set) for spellFamilyName
+ if (spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName))
+ return false;
+
+ // spellFamilyName is Ok need check for spellFamilyMask if present
+ if (spellProcEvent->spellFamilyMask)
+ {
+ if ((spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0)
+ return false;
+ hasFamilyMask = true;
+ // Some spells are not considered as active even with have spellfamilyflags
+ if (!(procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL))
+ active = true;
+ }
+ }
+ }
+
+ if (procExtra & (PROC_EX_INTERNAL_REQ_FAMILY))
+ {
+ if (!hasFamilyMask)
+ return false;
+ }
+
+ // Check for extra req (if none) and hit/crit
+ if (procEvent_procEx == PROC_EX_NONE)
+ {
+ // No extra req, so can trigger only for hit/crit - spell has to be active
+ if ((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && active)
+ return true;
+ }
+ else // Passive spells hits here only if resist/reflect/immune/evade
+ {
+ if (procExtra & AURA_SPELL_PROC_EX_MASK)
+ {
+ // if spell marked as procing only from not active spells
+ if (active && procEvent_procEx & PROC_EX_NOT_ACTIVE_SPELL)
+ return false;
+ // if spell marked as procing only from active spells
+ if (!active && procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL)
+ return false;
+ // Exist req for PROC_EX_EX_TRIGGER_ALWAYS
+ if (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS)
+ return true;
+ // PROC_EX_NOT_ACTIVE_SPELL and PROC_EX_ONLY_ACTIVE_SPELL flags handle: if passed checks before
+ if ((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && ((procEvent_procEx & (AURA_SPELL_PROC_EX_MASK)) == 0))
+ return true;
+ }
+ // Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other)
+ if (procEvent_procEx & procExtra)
+ return true;
+ }
+ return false;
+}
+
+void SpellMgr::LoadSpellGroups()
+{
+ mSpellSpellGroup.clear(); // need for reload case
+ mSpellGroupSpell.clear();
+
+ uint32 count = 0;
+
+ // 0 1
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, spell_id FROM spell_group");
+ if (!result)
+ {
+
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell group definitions", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ std::set<uint32> groups;
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint32 group_id = fields[0].GetUInt32();
+ if (group_id <= SPELL_GROUP_DB_RANGE_MIN && group_id >= SPELL_GROUP_CORE_RANGE_MAX)
+ {
+ sLog.outErrorDb("SpellGroup id %u listed in `spell_groups` is in core range, but is not defined in core!", group_id);
+ continue;
+ }
+ int32 spell_id = fields[1].GetInt32();
+
+ groups.insert(std::set<uint32>::value_type(group_id));
+ mSpellGroupSpell.insert(SpellGroupSpellMap::value_type((SpellGroup)group_id, spell_id));
+
+ } while (result->NextRow());
+
+ for (SpellGroupSpellMap::iterator itr = mSpellGroupSpell.begin(); itr!= mSpellGroupSpell.end() ;)
+ {
+ if (itr->second < 0)
+ {
+ if (groups.find(abs(itr->second)) == groups.end())
+ {
+ sLog.outErrorDb("SpellGroup id %u listed in `spell_groups` does not exist", abs(itr->second));
+ mSpellGroupSpell.erase(itr++);
+ }
+ else
+ ++itr;
+ }
+ else
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(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)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_group` is not first rank of spell", itr->second);
+ mSpellGroupSpell.erase(itr++);
+ }
+ else
+ ++itr;
+ }
+ }
+
+ for (std::set<uint32>::iterator groupItr = groups.begin() ; groupItr != groups.end() ; ++groupItr)
+ {
+ std::set<uint32> spells;
+ GetSetOfSpellsInSpellGroup(SpellGroup(*groupItr), spells);
+
+ for (std::set<uint32>::iterator spellItr = spells.begin() ; spellItr != spells.end() ; ++spellItr)
+ {
+ ++count;
+ mSpellSpellGroup.insert(SpellSpellGroupMap::value_type(*spellItr, SpellGroup(*groupItr)));
+ }
+ }
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell group definitions", count);
+}
+
+void SpellMgr::LoadSpellGroupStackRules()
+{
+ mSpellGroupStack.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT group_id, stack_rule FROM spell_group_stack_rules");
+ if (!result)
+ {
+
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell group stack rules", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint32 group_id = fields[0].GetUInt32();
+ uint8 stack_rule = fields[1].GetUInt32();
+ if (stack_rule >= SPELL_GROUP_STACK_RULE_MAX)
+ {
+ sLog.outErrorDb("SpellGroupStackRule %u listed in `spell_group_stack_rules` does not exist", stack_rule);
+ continue;
+ }
+
+ SpellGroupSpellMapBounds spellGroup = GetSpellGroupSpellMapBounds((SpellGroup)group_id);
+
+ if (spellGroup.first == spellGroup.second)
+ {
+ sLog.outErrorDb("SpellGroup id %u listed in `spell_group_stack_rules` does not exist", group_id);
+ continue;
+ }
+
+ mSpellGroupStack[(SpellGroup)group_id] = (SpellGroupStackRule)stack_rule;
+
+ ++count;
+ } while (result->NextRow());
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell group stack rules", count);
+}
+
+void SpellMgr::LoadSpellThreats()
+{
+ mSpellThreatMap.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, Threat FROM spell_threat");
+ if (!result)
+ {
+
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u aggro generating spells", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+ uint16 Threat = fields[1].GetUInt16();
+
+ if (!sSpellStore.LookupEntry(entry))
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_threat` does not exist", entry);
+ continue;
+ }
+
+ mSpellThreatMap[entry] = Threat;
+
+ ++count;
+ } while (result->NextRow());
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u aggro generating spells", count);
+}
+
+bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const
+{
+ SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
+ if (!spellInfo_1 || !spellInfo_2) return false;
+ if (spellInfo_1->Id == spellId_2) return false;
+
+ return GetFirstSpellInChain(spellInfo_1->Id) == GetFirstSpellInChain(spellId_2);
+}
+
+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;
+
+ if (spellmgr.IsSkillBonusSpell(spellInfo->Id))
+ return false;
+
+ // All stance spells. if any better way, change it.
+ for (int i = 0; i < 3; ++i)
+ {
+ switch(spellInfo->SpellFamilyName)
+ {
+ 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;
+ }
+ }
+ 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)
+ {
+ uint32 skill = spellInfo->EffectMiscValue[i];
+
+ bool found = IsProfessionOrRidingSkill(skill);
+ if (found)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SpellMgr::IsProfessionSpell(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)
+ {
+ uint32 skill = spellInfo->EffectMiscValue[i];
+
+ bool found = IsProfessionSkill(skill);
+ if (found)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SpellMgr::IsPrimaryProfessionSpell(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)
+ {
+ uint32 skill = spellInfo->EffectMiscValue[i];
+
+ bool found = IsPrimaryProfessionSkill(skill);
+ if (found)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const
+{
+ return IsPrimaryProfessionSpell(spellId) && GetSpellRank(spellId) == 1;
+}
+
+bool SpellMgr::IsSkillBonusSpell(uint32 spellId) const
+{
+ SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId);
+
+ for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
+ {
+ 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;
+ }
+
+ return false;
+}
+
+SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const
+{
+ // ignore passive spells
+ if (IsPassiveSpell(spellInfo->Id))
+ return spellInfo;
+
+ bool needRankSelection = false;
+ for (int i=0; i<3; ++i)
+ {
+ 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;
+ }
+ }
+
+ // not required
+ if (!needRankSelection)
+ return spellInfo;
+
+ for (uint32 nextSpellId = spellInfo->Id; nextSpellId != 0; nextSpellId = GetPrevSpellInChain(nextSpellId))
+ {
+ SpellEntry const *nextSpellInfo = sSpellStore.LookupEntry(nextSpellId);
+ if (!nextSpellInfo)
+ break;
+
+ // if found appropriate level
+ if (playerLevel + 10 >= nextSpellInfo->spellLevel)
+ return nextSpellInfo;
+
+ // one rank less then
+ }
+
+ // not found
+ return NULL;
+}
+
+void SpellMgr::LoadSpellLearnSkills()
+{
+ 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;
+ barGoLink bar(sSpellStore.GetNumRows());
+ for (uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell)
+ {
+ bar.step();
+ SpellEntry const* entry = sSpellStore.LookupEntry(spell);
+
+ if (!entry)
+ continue;
+
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ if (entry->Effect[i] == SPELL_EFFECT_SKILL)
+ {
+ SpellLearnSkillNode dbc_node;
+ dbc_node.skill = entry->EffectMiscValue[i];
+ dbc_node.step = entry->CalculateSimpleValue(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;
+
+ // FIXME: db_node not used... remove it?
+ SpellLearnSkillNode const* db_node = GetSpellLearnSkill(spell);
+
+ mSpellLearnSkills[spell] = dbc_node;
+ ++dbc_count;
+ break;
+ }
+ }
+ }
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u Spell Learn Skills from DBC", dbc_count);
+}
+
+void SpellMgr::LoadSpellLearnSpells()
+{
+ mSpellLearnSpells.clear(); // need for reload case
+
+ // 0 1 2
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell");
+ if (!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded 0 spell learn spells");
+ sLog.outErrorDb("`spell_learn_spell` table is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+
+ barGoLink bar(result->GetRowCount());
+ do
+ {
+ bar.step();
+ 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 (!sSpellStore.LookupEntry(spell_id))
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist", spell_id);
+ 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;
+ }
+
+ 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());
+
+ // 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 don't must 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();
+ sLog.outString(">> Loaded %u spell learn spells + %u found in DBC", count, dbc_count);
+}
+
+void SpellMgr::LoadSpellPetAuras()
+{
+ mSpellPetAuraMap.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2 3
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT spell, effectId, pet, aura FROM spell_pet_auras");
+ if (!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell pet auras", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint32 spell = fields[0].GetUInt32();
+ uint8 eff = fields[1].GetUInt8();
+ uint32 pet = fields[2].GetUInt32();
+ uint32 aura = fields[3].GetUInt32();
+
+ SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff);
+ if (itr != mSpellPetAuraMap.end())
+ itr->second.AddAura(pet, aura);
+ else
+ {
+ 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;
+ }
+
+ PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[eff] == TARGET_UNIT_PET, spellInfo->CalculateSimpleValue(eff));
+ mSpellPetAuraMap[(spell<<8) + eff] = pa;
+ }
+
+ ++count;
+ } while (result->NextRow());
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell pet auras", count);
+}
+
+void SpellMgr::LoadPetLevelupSpellMap()
+{
+ mPetLevelupSpellMap.clear(); // need for reload case
+
+ uint32 count = 0;
+ uint32 family_count = 0;
+
+ barGoLink bar(sCreatureFamilyStore.GetNumRows());
+
+ for (uint32 i = 0; i < sCreatureFamilyStore.GetNumRows(); ++i)
+ {
+ bar.step();
+
+ CreatureFamilyEntry const *creatureFamily = sCreatureFamilyStore.LookupEntry(i);
+ if (!creatureFamily) // not exist
+ continue;
+
+ for (uint8 j = 0; j < 2; ++j)
+ {
+ if (!creatureFamily->skillLine[j])
+ continue;
+
+ for (uint32 k = 0; k < sSkillLineAbilityStore.GetNumRows(); ++k)
+ {
+ SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(k);
+ if (!skillLine)
+ continue;
+
+ //if (skillLine->skillId != creatureFamily->skillLine[0] &&
+ // (!creatureFamily->skillLine[1] || skillLine->skillId != creatureFamily->skillLine[1]))
+ // continue;
+
+ if (skillLine->skillId != creatureFamily->skillLine[j])
+ continue;
+
+ if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
+ continue;
+
+ SpellEntry const *spell = sSpellStore.LookupEntry(skillLine->spellId);
+ if (!spell) // not exist or triggered or talent
+ continue;
+
+ if (!spell->spellLevel)
+ continue;
+
+ PetLevelupSpellSet& spellSet = mPetLevelupSpellMap[creatureFamily->ID];
+ if (spellSet.empty())
+ ++family_count;
+
+ spellSet.insert(PetLevelupSpellSet::value_type(spell->spellLevel,spell->Id));
+ ++count;
+ }
+ }
+ }
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u pet levelup and default spells for %u families", count, family_count);
+}
+
+bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntry& petDefSpells)
+{
+ // skip empty list;
+ bool have_spell = false;
+ for (uint8 j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
+ {
+ if (petDefSpells.spellid[j])
+ {
+ have_spell = true;
+ break;
+ }
+ }
+ if (!have_spell)
+ return false;
+
+ // remove duplicates with levelupSpells if any
+ if (PetLevelupSpellSet const *levelupSpells = cInfo->family ? spellmgr.GetPetLevelupSpellList(cInfo->family) : NULL)
+ {
+ for (uint8 j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
+ {
+ if (!petDefSpells.spellid[j])
+ continue;
+
+ for (PetLevelupSpellSet::const_iterator itr = levelupSpells->begin(); itr != levelupSpells->end(); ++itr)
+ {
+ if (itr->second == petDefSpells.spellid[j])
+ {
+ petDefSpells.spellid[j] = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ // skip empty list;
+ have_spell = false;
+ for (uint8 j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
+ {
+ if (petDefSpells.spellid[j])
+ {
+ have_spell = true;
+ break;
+ }
+ }
+
+ return have_spell;
+}
+
+void SpellMgr::LoadPetDefaultSpells()
+{
+ mPetDefaultSpellsMap.clear();
+
+ uint32 countCreature = 0;
+ uint32 countData = 0;
+
+ barGoLink bar(sCreatureStorage.MaxEntry + sSpellStore.GetNumRows());
+
+ for (uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
+ {
+ bar.step();
+
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
+ if (!cInfo)
+ continue;
+
+ if (!cInfo->PetSpellDataId)
+ continue;
+
+ // for creature with PetSpellDataId get default pet spells from dbc
+ CreatureSpellDataEntry const* spellDataEntry = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
+ if (!spellDataEntry)
+ continue;
+
+ int32 petSpellsId = -int32(cInfo->PetSpellDataId);
+ PetDefaultSpellsEntry petDefSpells;
+ for (uint8 j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
+ petDefSpells.spellid[j] = spellDataEntry->spellId[j];
+
+ if (LoadPetDefaultSpells_helper(cInfo, petDefSpells))
+ {
+ mPetDefaultSpellsMap[petSpellsId] = petDefSpells;
+ ++countData;
+ }
+ }
+
+ // different summon spells
+ for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
+ {
+ bar.step();
+
+ SpellEntry const* spellEntry = sSpellStore.LookupEntry(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)
+ {
+ uint32 creature_id = spellEntry->EffectMiscValue[k];
+ CreatureInfo const *cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(creature_id);
+ if (!cInfo)
+ continue;
+
+ // already loaded
+ if (cInfo->PetSpellDataId)
+ continue;
+
+ // for creature without PetSpellDataId get default pet spells from creature_template
+ int32 petSpellsId = cInfo->Entry;
+ if (mPetDefaultSpellsMap.find(cInfo->Entry) != mPetDefaultSpellsMap.end())
+ continue;
+
+ PetDefaultSpellsEntry petDefSpells;
+ for (uint8 j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
+ petDefSpells.spellid[j] = cInfo->spells[j];
+
+ if (LoadPetDefaultSpells_helper(cInfo, petDefSpells))
+ {
+ mPetDefaultSpellsMap[petSpellsId] = petDefSpells;
+ ++countCreature;
+ }
+ }
+ }
+ }
+
+ sLog.outString();
+ sLog.outString(">> Loaded addition spells for %u pet spell data entries.", countData);
+ sLog.outString(">> Loaded %u summonable creature templates.", countCreature);
+}
+
+/// 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 < 3; ++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 (!ObjectMgr::GetItemPrototype(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 < 8; ++j)
+ {
+ if (spellInfo->Reagent[j] > 0 && !ObjectMgr::GetItemPrototype(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()
+{
+ mSpellAreaMap.clear(); // need for reload case
+ mSpellAreaForQuestMap.clear();
+ mSpellAreaForActiveQuestMap.clear();
+ mSpellAreaForQuestEndMap.clear();
+ mSpellAreaForAuraMap.clear();
+
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT spell, area, quest_start, quest_start_active, quest_end, aura_spell, racemask, gender, autocast FROM spell_area");
+
+ if (!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell area requirements", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint32 spell = fields[0].GetUInt32();
+ SpellArea spellArea;
+ spellArea.spellId = spell;
+ spellArea.areaId = fields[1].GetUInt32();
+ spellArea.questStart = fields[2].GetUInt32();
+ spellArea.questStartCanActive = fields[3].GetBool();
+ spellArea.questEnd = fields[4].GetUInt32();
+ spellArea.auraSpell = fields[5].GetInt32();
+ spellArea.raceMask = fields[6].GetUInt32();
+ spellArea.gender = Gender(fields[7].GetUInt8());
+ spellArea.autocast = fields[8].GetBool();
+
+ if (const SpellEntry* spellInfo = sSpellStore.LookupEntry(spell))
+ {
+ if (spellArea.autocast)
+ const_cast<SpellEntry*>(spellInfo)->Attributes |= SPELL_ATTR_CANT_CANCEL;
+ }
+ else
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` does not exist", spell);
+ continue;
+ }
+
+ {
+ bool ok = true;
+ SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId);
+ for (SpellAreaMap::const_iterator itr = sa_bounds.first; itr != sa_bounds.second; ++itr)
+ {
+ if (spellArea.spellId != itr->second.spellId)
+ continue;
+ if (spellArea.areaId != itr->second.areaId)
+ continue;
+ if (spellArea.questStart != itr->second.questStart)
+ continue;
+ if (spellArea.auraSpell != itr->second.auraSpell)
+ continue;
+ if ((spellArea.raceMask & itr->second.raceMask) == 0)
+ continue;
+ if (spellArea.gender != itr->second.gender)
+ continue;
+
+ // duplicate by requirements
+ ok =false;
+ break;
+ }
+
+ if (!ok)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` already listed with similar requirements.", spell);
+ continue;
+ }
+ }
+
+ if (spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId))
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have wrong area (%u) requirement", spell,spellArea.areaId);
+ continue;
+ }
+
+ if (spellArea.questStart && !objmgr.GetQuestTemplate(spellArea.questStart))
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have wrong start quest (%u) requirement", spell,spellArea.questStart);
+ continue;
+ }
+
+ if (spellArea.questEnd)
+ {
+ if (!objmgr.GetQuestTemplate(spellArea.questEnd))
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell,spellArea.questEnd);
+ continue;
+ }
+
+ if (spellArea.questEnd == spellArea.questStart && !spellArea.questStartCanActive)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell,spellArea.questEnd);
+ continue;
+ }
+ }
+
+ if (spellArea.auraSpell)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell));
+ if (!spellInfo)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,abs(spellArea.auraSpell));
+ continue;
+ }
+
+ if (uint32(abs(spellArea.auraSpell)) == spellArea.spellId)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement for itself", spell,abs(spellArea.auraSpell));
+ continue;
+ }
+
+ // not allow autocast chains by auraSpell field (but allow use as alternative if not present)
+ if (spellArea.autocast && spellArea.auraSpell > 0)
+ {
+ bool chain = false;
+ SpellAreaForAuraMapBounds saBound = GetSpellAreaForAuraMapBounds(spellArea.spellId);
+ for (SpellAreaForAuraMap::const_iterator itr = saBound.first; itr != saBound.second; ++itr)
+ {
+ if (itr->second->autocast && itr->second->auraSpell > 0)
+ {
+ chain = true;
+ break;
+ }
+ }
+
+ if (chain)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell);
+ continue;
+ }
+
+ SpellAreaMapBounds saBound2 = GetSpellAreaMapBounds(spellArea.auraSpell);
+ for (SpellAreaMap::const_iterator itr2 = saBound2.first; itr2 != saBound2.second; ++itr2)
+ {
+ if (itr2->second.autocast && itr2->second.auraSpell > 0)
+ {
+ chain = true;
+ break;
+ }
+ }
+
+ if (chain)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell);
+ continue;
+ }
+ }
+ }
+
+ if (spellArea.raceMask && (spellArea.raceMask & RACEMASK_ALL_PLAYABLE) == 0)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have wrong race mask (%u) requirement", spell,spellArea.raceMask);
+ continue;
+ }
+
+ if (spellArea.gender != GENDER_NONE && spellArea.gender != GENDER_FEMALE && spellArea.gender != GENDER_MALE)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have wrong gender (%u) requirement", spell, spellArea.gender);
+ continue;
+ }
+
+ SpellArea const* sa = &mSpellAreaMap.insert(SpellAreaMap::value_type(spell,spellArea))->second;
+
+ // for search by current zone/subzone at zone/subzone change
+ if (spellArea.areaId)
+ mSpellAreaForAreaMap.insert(SpellAreaForAreaMap::value_type(spellArea.areaId,sa));
+
+ // for search at quest start/reward
+ if (spellArea.questStart)
+ {
+ if (spellArea.questStartCanActive)
+ mSpellAreaForActiveQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa));
+ else
+ mSpellAreaForQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa));
+ }
+
+ // for search at quest start/reward
+ if (spellArea.questEnd)
+ mSpellAreaForQuestEndMap.insert(SpellAreaForQuestMap::value_type(spellArea.questEnd,sa));
+
+ // for search at aura apply
+ if (spellArea.auraSpell)
+ mSpellAreaForAuraMap.insert(SpellAreaForAuraMap::value_type(abs(spellArea.auraSpell),sa));
+
+ ++count;
+ } while (result->NextRow());
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell area requirements", count);
+}
+
+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 < 6; ++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_ATTR_EX4_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_ATTR_EX6_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 = spellmgr.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()
+{
+ mSkillLineAbilityMap.clear();
+
+ barGoLink bar(sSkillLineAbilityStore.GetNumRows());
+ uint32 count = 0;
+
+ for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i)
+ {
+ bar.step();
+ SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i);
+ if (!SkillInfo)
+ continue;
+
+ mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId,SkillInfo));
+ ++count;
+ }
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count);
+}
+
+DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered)
+{
+ // Explicit Diminishing Groups
+ switch (spellproto->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ // some generic arena related spells have by some strange reason MECHANIC_TURN
+ if (spellproto->Mechanic == MECHANIC_TURN)
+ return DIMINISHING_NONE;
+ break;
+ case SPELLFAMILY_MAGE:
+ {
+ // Frostbite
+ if (spellproto->SpellFamilyFlags[1] & 0x80000000)
+ return DIMINISHING_TRIGGER_ROOT;
+ //Shattered Barrier: only flag SpellFamilyFlags[0] = 0x00080000 shared
+ //by most frost spells, using id instead
+ if (spellproto->Id == 55080)
+ return DIMINISHING_TRIGGER_ROOT;
+ // Frost Nova / Freeze (Water Elemental)
+ if (spellproto->SpellIconID == 193)
+ return DIMINISHING_CONTROL_ROOT;
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ // Sap 0x80 Gouge 0x8
+ if (spellproto->SpellFamilyFlags[0] & 0x88)
+ return DIMINISHING_POLYMORPH;
+ // Blind
+ else if (spellproto->SpellFamilyFlags[0] & 0x1000000)
+ return DIMINISHING_FEAR_BLIND;
+ // Cheap Shot
+ else if (spellproto->SpellFamilyFlags[0] & 0x400)
+ return DIMINISHING_CHEAPSHOT_POUNCE;
+ // Crippling poison - Limit to 10 seconds in PvP (No SpellFamilyFlags)
+ else if (spellproto->SpellIconID == 163)
+ return DIMINISHING_LIMITONLY;
+ break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Death Coil
+ if (spellproto->SpellFamilyFlags[0] & 0x80000)
+ return DIMINISHING_DEATHCOIL;
+ // Curses/etc
+ else if (spellproto->SpellFamilyFlags[0] & 0x80000000)
+ return DIMINISHING_LIMITONLY;
+ // Howl of Terror
+ else if (spellproto->SpellFamilyFlags[1] & 0x8)
+ return DIMINISHING_FEAR_BLIND;
+ // Seduction
+ else if (spellproto->SpellFamilyFlags[0] & 0x40000000)
+ return DIMINISHING_FEAR_BLIND;
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Pounce
+ if (spellproto->SpellFamilyFlags[0] & 0x20000)
+ return DIMINISHING_CHEAPSHOT_POUNCE;
+ // Cyclone
+ else if (spellproto->SpellFamilyFlags[1] & 0x20)
+ return DIMINISHING_CYCLONE;
+ // Entangling Roots: to force natures grasp proc to be control root
+ else if (spellproto->SpellFamilyFlags[0] & 0x00000200)
+ return DIMINISHING_CONTROL_ROOT;
+ // Faerie Fire
+ else if (spellproto->SpellFamilyFlags[0] & 0x400)
+ return DIMINISHING_LIMITONLY;
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Hamstring - limit duration to 10s in PvP
+ if (spellproto->SpellFamilyFlags[0] & 0x2)
+ return DIMINISHING_LIMITONLY;
+ // Intimidating Shout
+ else if (spellproto->SpellFamilyFlags[0] & 0x40000)
+ return DIMINISHING_FEAR_BLIND;
+ // Charge Stun
+ else if (spellproto->SpellFamilyFlags[0] & 0x01000000)
+ return DIMINISHING_NONE;
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Repentance
+ if (spellproto->SpellFamilyFlags[0] & 0x4)
+ return DIMINISHING_POLYMORPH;
+ break;
+ }
+ case SPELLFAMILY_PRIEST:
+ {
+ // Vampiric Embrace
+ if ((spellproto->SpellFamilyFlags[0] & 0x4) && spellproto->SpellIconID == 150)
+ return DIMINISHING_LIMITONLY;
+ break;
+ }
+ case SPELLFAMILY_DEATHKNIGHT:
+ {
+ // Hungering Cold (no flags)
+ if (spellproto->SpellIconID == 2797)
+ return DIMINISHING_POLYMORPH;
+ // Mark of Blood
+ else if ((spellproto->SpellFamilyFlags[0] & 0x10000000)
+ && spellproto->SpellIconID == 2285)
+ return DIMINISHING_LIMITONLY;
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Hunter's mark
+ if ((spellproto->SpellFamilyFlags[0] & 0x400) && spellproto->SpellIconID == 538)
+ return DIMINISHING_LIMITONLY;
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Get by mechanic
+ uint32 mechanic = GetAllSpellMechanicMask(spellproto);
+ if (mechanic == MECHANIC_NONE) return DIMINISHING_NONE;
+ if (mechanic & ((1<<MECHANIC_STUN) |
+ (1<<MECHANIC_SHACKLE))) return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN;
+ if (mechanic & ((1<<MECHANIC_SLEEP) |
+ (1<<MECHANIC_FREEZE))) return DIMINISHING_FREEZE_SLEEP;
+ if (mechanic & (1<<MECHANIC_POLYMORPH)) return DIMINISHING_POLYMORPH;
+ if (mechanic & (1<<MECHANIC_ROOT)) return triggered ? DIMINISHING_TRIGGER_ROOT : DIMINISHING_CONTROL_ROOT;
+ if (mechanic & ((1<<MECHANIC_FEAR) |
+ (1<<MECHANIC_TURN))) return DIMINISHING_FEAR_BLIND;
+ if (mechanic & (1<<MECHANIC_CHARM)) return DIMINISHING_CHARM;
+ if (mechanic & (1<<MECHANIC_SILENCE)) return DIMINISHING_SILENCE;
+ if (mechanic & (1<<MECHANIC_DISARM)) return DIMINISHING_DISARM;
+ if (mechanic & (1<<MECHANIC_FREEZE)) return DIMINISHING_FREEZE_SLEEP;
+ if (mechanic & ((1<<MECHANIC_KNOCKOUT) |
+ (1<<MECHANIC_SAPPED))) return DIMINISHING_KNOCKOUT;
+ if (mechanic & (1<<MECHANIC_BANISH)) return DIMINISHING_BANISH;
+ if (mechanic & (1<<MECHANIC_HORROR)) return DIMINISHING_DEATHCOIL;
+
+ // Get by effect
+ for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ if (spellproto->EffectApplyAuraName[i] == SPELL_AURA_MOD_TAUNT)
+ return DIMINISHING_TAUNT;
+ }
+ return DIMINISHING_NONE;
+}
+
+int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry const* spellproto)
+{
+ if (!IsDiminishingReturnsGroupDurationLimited(group))
+ return 0;
+
+ // Explicit diminishing duration
+ switch(spellproto->SpellFamilyName)
+ {
+ case SPELLFAMILY_HUNTER:
+ {
+ // Wyvern Sting
+ if (spellproto->SpellFamilyFlags[1] & 0x1000)
+ return 6 * IN_MILISECONDS;
+ // Hunter's Mark
+ if (spellproto->SpellFamilyFlags[0] & 0x400)
+ return 120 * IN_MILISECONDS;
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Repentance - limit to 6 seconds in PvP
+ if (spellproto->SpellFamilyFlags[0] & 0x4)
+ return 6 * IN_MILISECONDS;
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Faerie Fire - limit to 40 seconds in PvP (3.1)
+ if (spellproto->SpellFamilyFlags[0] & 0x400)
+ return 40 * IN_MILISECONDS;
+ break;
+ }
+ case SPELLFAMILY_PRIEST:
+ {
+ // Vampiric Embrace - limit to 60 seconds in PvP (3.1)
+ if ((spellproto->SpellFamilyFlags[0] & 0x4) && spellproto->SpellIconID == 150)
+ return 60 * IN_MILISECONDS;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 10 * IN_MILISECONDS;
+}
+
+bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group)
+{
+ switch(group)
+ {
+ case DIMINISHING_CONTROL_STUN:
+ case DIMINISHING_TRIGGER_STUN:
+ case DIMINISHING_FREEZE_SLEEP:
+ case DIMINISHING_CONTROL_ROOT:
+ case DIMINISHING_TRIGGER_ROOT:
+ case DIMINISHING_FEAR_BLIND:
+ case DIMINISHING_CHARM:
+ case DIMINISHING_POLYMORPH:
+ case DIMINISHING_KNOCKOUT:
+ case DIMINISHING_CYCLONE:
+ case DIMINISHING_BANISH:
+ case DIMINISHING_LIMITONLY:
+ case DIMINISHING_CHEAPSHOT_POUNCE:
+ 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_CONTROL_STUN:
+ case DIMINISHING_TRIGGER_STUN:
+ case DIMINISHING_CHEAPSHOT_POUNCE:
+ case DIMINISHING_CYCLONE:
+ return DRTYPE_ALL;
+ case DIMINISHING_FEAR_BLIND:
+ case DIMINISHING_CONTROL_ROOT:
+ case DIMINISHING_TRIGGER_ROOT:
+ case DIMINISHING_CHARM:
+ case DIMINISHING_POLYMORPH:
+ case DIMINISHING_SILENCE:
+ case DIMINISHING_DISARM:
+ case DIMINISHING_DEATHCOIL:
+ case DIMINISHING_FREEZE_SLEEP:
+ case DIMINISHING_BANISH:
+ case DIMINISHING_KNOCKOUT:
+ return DRTYPE_PLAYER;
+ default:
+ break;
+ }
+
+ return DRTYPE_NONE;
+}
+
+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 (Krasus Landing exception)
+ if (!player || player->GetAreaId() == 4564 || !player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !player->HasAuraType(SPELL_AURA_FLY) || player->HasAura(44795))
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+//-----------TRINITY-------------
+
+bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *spellInfo_2, bool sameCaster) const
+{
+ 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->AttributesEx & SPELL_ATTR_EX_STACK_FOR_DIFF_CASTERS
+ || spellInfo_1->AttributesEx3 & SPELL_ATTR_EX3_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_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:
+ return true;
+ default:
+ break;
+ }
+ }
+ }
+
+ 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;
+ // same spell with same caster should not stack
+ return false;
+ }
+
+ // use icon to check generic spells
+ if (!spellInfo_1->SpellFamilyName)
+ {
+ if (!spellInfo_1->SpellIconID || spellInfo_1->SpellIconID == 1
+ || spellInfo_1->SpellIconID != spellInfo_2->SpellIconID)
+ return true;
+ }
+ // use familyflag to check class spells
+ else
+ {
+ if (!spellInfo_1->SpellFamilyFlags
+ || spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags)
+ return true;
+ }
+
+ //use data of highest rank spell(needed for spells which ranks have different effects)
+ spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
+ spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
+
+ //if spells do not have the same effect or aura or miscvalue, they will stack
+ for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
+ if (spellInfo_1->Effect[i] != spellInfo_2->Effect[i]
+ || spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i]
+ || spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i]) // paladin resist aura
+ return true; // need itemtype check? need an example to add that check
+
+ // different spells with same effect
+ return false;
+}
+
+bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool def)
+{
+ if (!dispelSpell) return false;
+ SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
+ if (!spellproto) return false;
+
+ // Cyclone etc..
+ if (spellproto->AttributesEx & SPELL_ATTR_EX_UNAFFECTED_BY_SCHOOL_IMMUNE)
+ return false;
+
+ if (dispelSpell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)
+ return true;
+
+ return def;
+}
+
+void SpellMgr::LoadSpellEnchantProcData()
+{
+ mSpellEnchantProcEventMap.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2 3
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data");
+ if (!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u spell enchant proc event conditions", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ 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();
+ sLog.outString(">> Loaded %u enchant proc data definitions", count);
+}
+
+void SpellMgr::LoadSpellRequired()
+{
+ mSpellsReqSpell.clear(); // need for reload case
+ mSpellReq.clear(); // need for reload case
+
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required");
+
+ if (!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded 0 spell required records");
+ sLog.outErrorDb("`spell_required` table is empty!");
+ return;
+ }
+ uint32 rows = 0;
+
+ barGoLink bar(result->GetRowCount());
+ do
+ {
+ bar.step();
+ 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();
+ sLog.outString(">> Loaded %u spell required records", rows);
+}
+
+void SpellMgr::LoadSpellRanks()
+{
+ mSpellChains.clear(); // need for reload case
+
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT first_spell_id, spell_id, rank from spell_ranks ORDER BY first_spell_id , rank");
+
+ if (!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded 0 spell rank records");
+ sLog.outErrorDb("`spell_ranks` table is empty!");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ 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)
+ {
+ bar.step();
+ 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();
+ sLog.outString(">> Loaded %u spell rank records", rows);
+}
+
+// set data in core for now
+void SpellMgr::LoadSpellCustomAttr()
+{
+ mSpellCustomAttr.resize(GetSpellStore()->GetNumRows());
+
+ barGoLink bar(GetSpellStore()->GetNumRows());
+
+ uint32 count = 0;
+
+ SpellEntry *spellInfo;
+ for (uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
+ {
+ bar.step();
+
+ mSpellCustomAttr[i] = 0;
+ spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i);
+ if (!spellInfo)
+ continue;
+
+ for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
+ {
+ switch (spellInfo->Effect[j])
+ {
+ case SPELL_EFFECT_SCHOOL_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
+ case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
+ case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
+ case SPELL_EFFECT_HEAL:
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_DIRECT_DAMAGE;
+ count++;
+ break;
+ case SPELL_EFFECT_CHARGE:
+ case SPELL_EFFECT_JUMP:
+ case SPELL_EFFECT_JUMP2:
+ case SPELL_EFFECT_LEAP_BACK:
+ if (!spellInfo->speed && !spellInfo->SpellFamilyName)
+ spellInfo->speed = SPEED_CHARGE;
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_CHARGE;
+ count++;
+ break;
+ case SPELL_EFFECT_PICKPOCKET:
+ mSpellCustomAttr[i] |= SPELL_ATTR_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++;
+ break;
+ }
+
+ switch (SpellTargetType[spellInfo->EffectImplicitTargetA[j]])
+ {
+ case TARGET_TYPE_UNIT_TARGET:
+ case TARGET_TYPE_DEST_TARGET:
+ spellInfo->Targets |= TARGET_FLAG_UNIT;
+ count++;
+ 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_ATTR_CU_AURA_CC;
+ count++;
+ break;
+ }
+ }
+
+ if (!_isPositiveEffect(i, 0, false))
+ {
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF0;
+ count++;
+ }
+ if (!_isPositiveEffect(i, 1, false))
+ {
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF1;
+ count++;
+ }
+ if (!_isPositiveEffect(i, 2, false))
+ {
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF2;
+ count++;
+ }
+
+ if (spellInfo->SpellVisual[0] == 3879)
+ {
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_BACK;
+ count++;
+ }
+
+ if (spellInfo->activeIconID == 2158) // flight
+ {
+ spellInfo->Attributes |= SPELL_ATTR_PASSIVE;
+ count++;
+ }
+
+ switch(i)
+ {
+ // Bind
+ case 3286:
+ spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_TARGET_ENEMY;
+ spellInfo->EffectImplicitTargetA[1] = TARGET_UNIT_TARGET_ENEMY;
+ count++;
+ break;
+ // Heroism
+ case 32182:
+ spellInfo->excludeCasterAuraSpell = 57723; // Exhaustion
+ count++;
+ break;
+ // Blazing Harpoon
+ case 61588:
+ spellInfo->MaxAffectedTargets = 1;
+ count++;
+ break;
+ // Bloodlust
+ case 2825:
+ spellInfo->excludeCasterAuraSpell = 57724; // Sated
+ count++;
+ break;
+ // Heart of the Crusader
+ case 20335:
+ case 20336:
+ case 20337:
+ // Glyph of Life Tap
+ case 63320:
+ // Entries were not updated after spell effect change, we have to do that manually :/
+ spellInfo->AttributesEx3 |= SPELL_ATTR_EX3_CAN_PROC_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: case 43215: // flame breath
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_LINE;
+ count++;
+ break;
+ case 24340: case 26558: case 28884: // Meteor
+ case 36837: case 38903: case 41276: // Meteor
+ case 57467: // Meteor
+ case 26789: // Shard of the Fallen Star
+ case 31436: // Malevolent Cleave
+ case 35181: // Dive Bomb
+ case 40810: case 43267: case 43268: // Saber Lash
+ case 42384: // Brutal Swipe
+ case 45150: // Meteor Slash
+ case 64422: case 64688: // Sonic Screech
+ case 72373: // Shared Suffering
+ // ONLY SPELLS WITH SPELLFAMILY_GENERIC and EFFECT_SCHOOL_DAMAGE
+ mSpellCustomAttr[i] |= SPELL_ATTR_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 28062: case 39090: // Positive/Negative Charge
+ //case 28085: case 39093:
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_EXCLUDE_SELF;
+ count++;
+ break;
+ case 44978: case 45001: case 45002: // Wild Magic
+ case 45004: case 45006: case 45010: // Wild Magic
+ case 31347: // Doom
+ case 41635: // Prayer of Mending
+ case 44869: // Spectral Blast
+ case 45027: // Revitalize
+ case 45976: // Muru Portal Channel
+ case 39365: // Thundering Storm
+ case 41071: // Raise Dead (HACK)
+ case 52124: // Sky Darkener Assault
+ 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
+ case 29576: // Multi-Shot
+ case 40816: // Saber Lash
+ case 37790: // Spread Shot
+ case 46771: // Flame Sear
+ case 45248: // Shadow Blades
+ case 41303: // Soul Drain
+ case 54172: // Divine Storm (heal)
+ case 29213: // Curse of the Plaguebringer - Noth
+ case 28542: // Life Drain - Sapphiron
+ 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
+ case 37676: // Insidious Whisper
+ case 46008: // Negative Energy
+ case 45641: // Fire Bloom
+ 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
+ case 40860: // Vile Beam
+ case 40861: // Wicked Beam
+ case 54835: // Curse of the Plaguebringer - Noth (H)
+ case 54098: // Poison Bolt Volly - Faerlina (H)
+ spellInfo->MaxAffectedTargets = 10;
+ 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
+ case 31834: // Light's Grace
+ case 34754: // Clearcasting
+ case 34936: // Backlash
+ case 48108: // Hot Streak
+ case 51124: // Killing Machine
+ case 54741: // Firestarter
+ case 57761: // Fireball!
+ case 39805: // Lightning Overload
+ case 52437: // Sudden Death
+ case 64823: // Item - Druid T8 Balance 4P Bonus
+ case 44401:
+ spellInfo->procCharges = 1;
+ count++;
+ break;
+ case 53390: // Tidal Wave
+ spellInfo->procCharges = 2;
+ count++;
+ break;
+ case 44544: // Fingers of Frost
+ spellInfo->procCharges = 2;
+ spellInfo->EffectSpellClassMask[0] = flag96(685904631,1151048,0);
+ count++;
+ break;
+ case 28200: // Ascendance (Talisman of Ascendance trinket)
+ spellInfo->procCharges = 6;
+ count++;
+ break;
+ case 51852: // The Eye of Acherus (no spawn in phase 2 in db)
+ spellInfo->EffectMiscValue[0] |= 1;
+ count++;
+ break;
+ case 52025: // Cleansing Totem Effect
+ spellInfo->EffectDieSides[1] = 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 30421: // Nether Portal - Perseverence
+ spellInfo->EffectBasePoints[2] += 30000;
+ count++;
+ break;
+ // some dummy spell only has dest, should push caster in this case
+ case 62324: // Throw Passenger
+ spellInfo->Targets |= TARGET_FLAG_CASTER;
+ count++;
+ break;
+ case 51735: // Ebon Plague
+ case 51734:
+ case 51726:
+ spellInfo->SpellFamilyFlags[2] = 0x10;
+ count++;
+ break;
+ // cleansing totem pulse when it is spawned
+ case 8172:
+ spellInfo->AttributesEx5 |= SPELL_ATTR_EX5_START_PERIODIC_AT_APPLY;
+ 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
+ case 27935: // To Anchor 1
+ case 27915: // Anchor to Skulls
+ case 27931: // Anchor to Skulls
+ case 27937: // Anchor to Skulls
+ spellInfo->rangeIndex = 13;
+ count++;
+ break;
+ case 62374: // Pursued
+ spellInfo->MaxAffectedTargets = 1;
+ count++;
+ break;
+ case 48743: // Death Pact
+ spellInfo->AttributesEx &= ~SPELL_ATTR_EX_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
+ case 29214: // Wrath of the Plaguebringer
+ 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 25771: // Forbearance - wrong mechanic immunity in DBC since 3.0.x
+ spellInfo->EffectMiscValue[0] = MECHANIC_IMMUNE_SHIELD;
+ count++;
+ break;
+ case 42442: // Vengeance Landing Cannonfire
+ spellInfo->MaxAffectedTargets = 1;
+ count++;
+ break;
+ case 45863: // Cosmetic - Incinerate to Random Target
+ spellInfo->MaxAffectedTargets = 1;
+ count++;
+ break;
+ case 25425: // Shoot
+ spellInfo->MaxAffectedTargets = 1;
+ count++;
+ break;
+ case 45761: // Shoot
+ spellInfo->MaxAffectedTargets = 1;
+ count++;
+ break;
+ case 42611: // Shoot
+ spellInfo->MaxAffectedTargets = 1;
+ count++;
+ break;
+ case 66588: // Flaming Spear
+ spellInfo->MaxAffectedTargets = 3;
+ count++;
+ break;
+ case 53651:
+ spellInfo->AttributesEx3 |= SPELL_ATTR_EX3_NO_INITIAL_AGGRO;
+ count++;
+ break;
+ case 61306: // Kirin Tor Commendation Badge
+ case 61308: // Wyrmrest Commendation Badge
+ case 61311: // Argent Crusade Commendation Badge
+ case 61312: // Ebon Blade Commendadtion Badge
+ case 69757: // Sons of Hodir Commendation Badge
+ spellInfo->EffectBasePoints[0] = 519; // Some suggest a global multiplier is used for rep gain
+ count++; // but basepoints * 1,3 hard coded in the tooltip says
+ break; // otherwise.
+ default:
+ break;
+ }
+
+ switch(spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_WARRIOR:
+ // Shout
+ if (spellInfo->SpellFamilyFlags[0] & 0x20000 || spellInfo->SpellFamilyFlags[1] & 0x20)
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_CC;
+ else
+ break;
+ count++;
+ break;
+ case SPELLFAMILY_DRUID:
+ // Starfall Target Selection
+ if (spellInfo->SpellFamilyFlags[2] & 0x100)
+ spellInfo->MaxAffectedTargets = 2;
+ // Starfall AOE Damage
+ else if (spellInfo->SpellFamilyFlags[2] & 0x800000)
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_EXCLUDE_SELF;
+ // Roar
+ else if (spellInfo->SpellFamilyFlags[0] & 0x8)
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_CC;
+ else
+ break;
+ count++;
+ break;
+ // Do not allow Deadly throw and Slice and Dice to proc twice
+ case SPELLFAMILY_ROGUE:
+ if (spellInfo->SpellFamilyFlags[1] & 0x1 || spellInfo->SpellFamilyFlags[0] & 0x40000)
+ spellInfo->AttributesEx4 |= SPELL_ATTR_EX4_CANT_PROC_FROM_SELFCAST;
+ else
+ break;
+ count++;
+ break;
+ }
+ }
+
+ SummonPropertiesEntry *properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(121));
+ properties->Type = SUMMON_TYPE_TOTEM;
+ properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(647)); // 52893
+ properties->Type = SUMMON_TYPE_TOTEM;
+
+ CreatureAI::FillAISpellInfo();
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u custom spell attributes", count);
+}
+
+// Fill custom data about enchancments
+void SpellMgr::LoadEnchantCustomAttr()
+{
+ uint32 size = sSpellItemEnchantmentStore.GetNumRows();
+ mEnchantCustomAttr.resize(size);
+
+ barGoLink bar(GetSpellStore()->GetNumRows());
+
+ uint32 count = 0;
+
+ for (uint32 i = 0; i < size; ++i)
+ mEnchantCustomAttr[i] = 0;
+
+ for (uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
+ {
+ bar.step();
+
+ SpellEntry * spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i);
+ if (!spellInfo)
+ continue;
+
+ // TODO: find a better check
+ if (!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_UNK13) || !(spellInfo->Attributes & SPELL_ATTR_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();
+ sLog.outString(">> Loaded %u custom enchant attributes", count);
+}
+
+bool SpellMgr::IsSkillTypeSpell(uint32 spellId, SkillType type) const
+{
+ SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId);
+
+ for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
+ if (_spell_idx->second->skillId == uint32(type))
+ return true;
+
+ return false;
+}
+
+void SpellMgr::LoadSpellLinked()
+{
+ mSpellLinkedMap.clear(); // need for reload case
+ uint32 count = 0;
+
+ // 0 1 2
+ QueryResult_AutoPtr result = WorldDatabase.Query("SELECT spell_trigger, spell_effect, type FROM spell_linked_spell");
+ if (!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+ sLog.outString();
+ sLog.outString(">> Loaded %u linked spells", count);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ 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_ATTR_CU_LINK_CAST; break;
+ case 1: mSpellCustomAttr[trigger] |= SPELL_ATTR_CU_LINK_HIT; break;
+ case 2: mSpellCustomAttr[trigger] |= SPELL_ATTR_CU_LINK_AURA; break;
+ }
+ }
+ else
+ {
+ mSpellCustomAttr[-trigger] |= SPELL_ATTR_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();
+ sLog.outString(">> Loaded %u linked spells", count);
+}