aboutsummaryrefslogtreecommitdiff
path: root/src/game/SpellMgr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/SpellMgr.cpp')
-rw-r--r--src/game/SpellMgr.cpp2912
1 files changed, 1896 insertions, 1016 deletions
diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp
index 23a85246a17..63aa32aa21c 100644
--- a/src/game/SpellMgr.cpp
+++ b/src/game/SpellMgr.cpp
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
- * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
+ * Copyright (C) 2008-2009 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
@@ -22,10 +22,12 @@
#include "ObjectMgr.h"
#include "SpellAuraDefines.h"
#include "ProgressBar.h"
-#include "Database/DBCStores.h"
+#include "DBCStores.h"
#include "World.h"
#include "Chat.h"
#include "Spell.h"
+#include "BattleGroundMgr.h"
+#include "CreatureAI.h"
bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS];
@@ -38,25 +40,17 @@ SpellMgr::SpellMgr()
case SPELL_EFFECT_PERSISTENT_AREA_AURA: //27
case SPELL_EFFECT_SUMMON: //28
case SPELL_EFFECT_TRIGGER_MISSILE: //32
- case SPELL_EFFECT_SUMMON_WILD: //41
- case SPELL_EFFECT_SUMMON_GUARDIAN: //42
case SPELL_EFFECT_TRANS_DOOR: //50 summon object
case SPELL_EFFECT_SUMMON_PET: //56
case SPELL_EFFECT_ADD_FARSIGHT: //72
- case SPELL_EFFECT_SUMMON_POSSESSED: //73
- case SPELL_EFFECT_SUMMON_TOTEM: //74
case SPELL_EFFECT_SUMMON_OBJECT_WILD: //76
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT1: //87
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT2: //88
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT3: //89
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT4: //90
- case SPELL_EFFECT_SUMMON_CRITTER: //97
+ //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_SUMMON_DEMON: //112
+ //case SPELL_EFFECT_SUMMON_DEMON: //112 not 303
case SPELL_EFFECT_TRIGGER_SPELL_2: //151 ritual of summon
EffectTargetType[i] = SPELL_REQUIRE_DEST;
break;
@@ -71,8 +65,12 @@ SpellMgr::SpellMgr()
case SPELL_EFFECT_ENCHANT_ITEM:
case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
case SPELL_EFFECT_DISENCHANT:
- case SPELL_EFFECT_FEED_PET:
+ //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
@@ -81,8 +79,18 @@ SpellMgr::SpellMgr()
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_138:
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;
@@ -99,6 +107,8 @@ SpellMgr::SpellMgr()
case TARGET_UNIT_PET:
case TARGET_UNIT_PARTY_CASTER:
case TARGET_UNIT_RAID_CASTER:
+ case TARGET_UNIT_VEHICLE:
+ case TARGET_UNIT_PASSENGER:
SpellTargetType[i] = TARGET_TYPE_UNIT_CASTER;
break;
case TARGET_UNIT_MINIPET:
@@ -256,7 +266,22 @@ int32 GetSpellMaxDuration(SpellEntry const *spellInfo)
return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]);
}
-uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell)
+bool GetDispelChance(Unit* caster, uint32 spellId)
+{
+ // we assume that aura dispel chance is 100% on start
+ // need formula for level difference based chance
+ int32 miss_chance = 0;
+ // Apply dispel mod from aura caster
+ if (caster)
+ {
+ if ( Player* modOwner = caster->GetSpellModOwner() )
+ modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_DISPEL_CHANCE, miss_chance);
+ }
+ // Try dispel
+ return !roll_chance_i(miss_chance);
+}
+
+uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell * spell)
{
SpellCastTimesEntry const *spellCastTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex);
@@ -266,21 +291,10 @@ uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell)
int32 castTime = spellCastTimeEntry->CastTime;
- if (spell)
- {
- if(Player* modOwner = spell->GetCaster()->GetSpellModOwner())
- modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell);
+ if (spell && spell->GetCaster())
+ spell->GetCaster()->ModSpellCastTime(spellInfo, castTime, spell);
- if( !(spellInfo->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_UNK5)) )
- castTime = int32(castTime * spell->GetCaster()->GetFloatValue(UNIT_MOD_CAST_SPEED));
- else
- {
- if (spell->IsRangedSpell() && !spell->IsAutoRepeat())
- castTime = int32(castTime * spell->GetCaster()->m_modAttackSpeedPct[RANGED_ATTACK]);
- }
- }
-
- if (spellInfo->Attributes & SPELL_ATTR_RANGED && (!spell || !(spell->IsAutoRepeat())))
+ if (spellInfo->Attributes & SPELL_ATTR_REQ_AMMO && (!spell || !(spell->IsAutoRepeat())))
castTime += 500;
return (castTime > 0) ? uint32(castTime) : 0;
@@ -291,35 +305,30 @@ bool IsPassiveSpell(uint32 spellId)
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if (!spellInfo)
return false;
- return (spellInfo->Attributes & SPELL_ATTR_PASSIVE) != 0;
+ if(spellInfo->Attributes & SPELL_ATTR_PASSIVE)
+ return true;
+ if(spellInfo->activeIconID == 2158) //flight
+ return true;
+ return false;
}
-/*bool IsNoStackAuraDueToAura(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2)
+bool IsAutocastableSpell(uint32 spellId)
{
- SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
- SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
- if(!spellInfo_1 || !spellInfo_2) return false;
- if(spellInfo_1->Id == spellId_2) return false;
-
- if (spellInfo_1->Effect[effIndex_1] != spellInfo_2->Effect[effIndex_2] ||
- spellInfo_1->EffectItemType[effIndex_1] != spellInfo_2->EffectItemType[effIndex_2] ||
- spellInfo_1->EffectMiscValue[effIndex_1] != spellInfo_2->EffectMiscValue[effIndex_2] ||
- spellInfo_1->EffectApplyAuraName[effIndex_1] != spellInfo_2->EffectApplyAuraName[effIndex_2])
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if(!spellInfo)
+ return false;
+ if(spellInfo->Attributes & SPELL_ATTR_PASSIVE)
+ return false;
+ if(spellInfo->activeIconID == 2158)
+ return false;
+ if(spellInfo->AttributesEx & SPELL_ATTR_EX_UNAUTOCASTABLE_BY_PET)
return false;
-
return true;
-}*/
+}
-int32 CompareAuraRanks(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2)
+bool IsHigherHankOfSpell(uint32 spellId_1, uint32 spellId_2)
{
- SpellEntry const*spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
- SpellEntry const*spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
- if(!spellInfo_1 || !spellInfo_2) return 0;
- if (spellId_1 == spellId_2) return 0;
-
- int32 diff = spellInfo_1->EffectBasePoints[effIndex_1] - spellInfo_2->EffectBasePoints[effIndex_2];
- if (spellInfo_1->EffectBasePoints[effIndex_1]+1 < 0 && spellInfo_2->EffectBasePoints[effIndex_2]+1 < 0) return -diff;
- else return diff;
+ return spellmgr.GetSpellRank(spellId_1)<spellmgr.GetSpellRank(spellId_2);
}
SpellSpecific GetSpellSpecific(uint32 spellId)
@@ -336,44 +345,64 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
if (spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED)
{
for(int i = 0; i < 3; i++)
- if( spellInfo->EffectApplyAuraName[i]==SPELL_AURA_MOD_POWER_REGEN)
+ if( spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POWER_REGEN
+ || spellInfo->EffectApplyAuraName[i] == SPELL_AURA_OBS_MOD_ENERGY)
return SPELL_DRINK;
- else if ( spellInfo->EffectApplyAuraName[i]==SPELL_AURA_MOD_REGEN)
+ else if ( spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_REGEN
+ || spellInfo->EffectApplyAuraName[i] == SPELL_AURA_OBS_MOD_HEALTH)
return SPELL_FOOD;
}
// this may be a hack
else if((spellInfo->AttributesEx2 & SPELL_ATTR_EX2_FOOD)
&& !spellInfo->Category)
return SPELL_WELL_FED;
-
- switch(spellInfo->Id)
- {
- case 12880: // warrior's Enrage rank 1
- case 14201: // Enrage rank 2
- case 14202: // Enrage rank 3
- case 14203: // Enrage rank 4
- case 14204: // Enrage rank 5
- case 12292: // Death Wish
- return SPELL_WARRIOR_ENRAGE;
- break;
- default: break;
- }
+ // 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_SCROLL;
+ case 12880: // Enrage (Enrage)
+ case 57518: // Enrage (Wrecking Crew)
+ case 12292: // Death Wish
+ return SPELL_WARRIOR_ENRAGE;
+ }
+ }
break;
}
case SPELLFAMILY_MAGE:
{
// family flags 18(Molten), 25(Frost/Ice), 28(Mage)
- if (spellInfo->SpellFamilyFlags & 0x12040000)
+ if (spellInfo->SpellFamilyFlags[0] & 0x12040000)
return SPELL_MAGE_ARMOR;
- if ((spellInfo->SpellFamilyFlags & 0x1000000) && spellInfo->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE)
+ // Arcane brillance and Arcane intelect (normal check fails because of flags difference)
+ if (spellInfo->SpellFamilyFlags[0] & 0x400)
+ return SPELL_MAGE_ARCANE_BRILLANCE;
+
+ if ((spellInfo->SpellFamilyFlags[0] & 0x1000000) && spellInfo->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE)
return SPELL_MAGE_POLYMORPH;
break;
}
+ case SPELLFAMILY_PRIEST:
+ {
+ // Divine Spirit and Prayer of Spirit
+ if (spellInfo->SpellFamilyFlags[0] & 0x20)
+ return SPELL_PRIEST_DIVINE_SPIRIT;
+
+ break;
+ }
case SPELLFAMILY_WARRIOR:
{
- if (spellInfo->SpellFamilyFlags & 0x00008000010000LL)
+ if (spellInfo->SpellFamilyFlags[1] & 0x000080 || spellInfo->SpellFamilyFlags[0] & 0x10000LL)
return SPELL_POSITIVE_SHOUT;
break;
@@ -384,12 +413,12 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
if (spellInfo->Dispel == DISPEL_CURSE)
return SPELL_CURSE;
- // family flag 37 (only part spells have family name)
- if (spellInfo->SpellFamilyFlags & 0x2000000000LL)
+ // Warlock (Demon Armor | Demon Skin | Fel Armor)
+ if (spellInfo->SpellFamilyFlags[1] & 0x20000020 || spellInfo->SpellFamilyFlags[2] & 0x00000010)
return SPELL_WARLOCK_ARMOR;
//seed of corruption and corruption
- if (spellInfo->SpellFamilyFlags & 0x1000000002LL)
+ if (spellInfo->SpellFamilyFlags[1] & 0x10 || spellInfo->SpellFamilyFlags[0] & 0x2)
return SPELL_WARLOCK_CORRUPTION;
break;
}
@@ -399,6 +428,10 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
if (spellInfo->Dispel == DISPEL_POISON)
return SPELL_STING;
+ // only hunter aspects have this
+ if( spellInfo->SpellFamilyFlags[1] & 0x00440000 || spellInfo->SpellFamilyFlags[0] & 0x00380000 || spellInfo->SpellFamilyFlags[2] & 0x00001010)
+ return SPELL_ASPECT;
+
break;
}
case SPELLFAMILY_PALADIN:
@@ -406,16 +439,16 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
if (IsSealSpell(spellInfo))
return SPELL_SEAL;
- if (spellInfo->SpellFamilyFlags & 0x10000100LL)
+ if (spellInfo->SpellFamilyFlags[0] & 0x11010002)
return SPELL_BLESSING;
- if ((spellInfo->SpellFamilyFlags & 0x00000820180400LL) && (spellInfo->AttributesEx3 & 0x200))
+ if ((spellInfo->SpellFamilyFlags[1] & 0x000008 || spellInfo->SpellFamilyFlags[0] & 0x20180400) && (spellInfo->AttributesEx3 & 0x200))
return SPELL_JUDGEMENT;
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < 3; ++i)
{
- // only paladin auras have this
- if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY)
+ // only paladin auras have this (for palaldin class family)
+ if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_RAID)
return SPELL_AURA;
}
break;
@@ -430,19 +463,11 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
case SPELLFAMILY_POTION:
return spellmgr.GetSpellElixirSpecific(spellInfo->Id);
- }
-
- // only warlock armor/skin have this (in additional to family cases)
- if( spellInfo->SpellVisual == 130 && spellInfo->SpellIconID == 89)
- {
- return SPELL_WARLOCK_ARMOR;
- }
- // only hunter aspects have this (but not all aspects in hunter family)
- if( spellInfo->activeIconID == 122 && (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_NATURE) &&
- (spellInfo->Attributes & 0x50000) != 0 && (spellInfo->Attributes & 0x9000010) == 0)
- {
- return SPELL_ASPECT;
+ case SPELLFAMILY_DEATHKNIGHT:
+ if ((spellInfo->Attributes & 0x10) && (spellInfo->AttributesEx2 & 0x10) && (spellInfo->AttributesEx4 & 0x200000))
+ return SPELL_PRESENCE;
+ break;
}
for(int i = 0; i < 3; ++i)
@@ -451,18 +476,18 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
{
switch(spellInfo->EffectApplyAuraName[i])
{
- case SPELL_AURA_TRACK_CREATURES:
- case SPELL_AURA_TRACK_RESOURCES:
- case SPELL_AURA_TRACK_STEALTHED:
- return SPELL_TRACKER;
case SPELL_AURA_MOD_CHARM:
case SPELL_AURA_MOD_POSSESS_PET:
case SPELL_AURA_MOD_POSSESS:
+ case SPELL_AURA_AOE_CHARM:
return SPELL_CHARM;
+ case SPELL_AURA_TRACK_CREATURES:
+ case SPELL_AURA_TRACK_RESOURCES:
+ case SPELL_AURA_TRACK_STEALTHED:
+ return SPELL_TRACKER;
}
}
}
-
// elixirs can have different families, but potion most ofc.
if(SpellSpecific sp = spellmgr.GetSpellElixirSpecific(spellInfo->Id))
return sp;
@@ -498,11 +523,15 @@ bool IsSingleFromSpellSpecificPerTarget(uint32 spellSpec1,uint32 spellSpec2)
case SPELL_MAGE_ARMOR:
case SPELL_ELEMENTAL_SHIELD:
case SPELL_MAGE_POLYMORPH:
+ case SPELL_PRESENCE:
case SPELL_WELL_FED:
case SPELL_DRINK:
case SPELL_FOOD:
case SPELL_CHARM:
- case SPELL_WARRIOR_ENRAGE:
+ case SPELL_SCROLL:
+ case SPELL_WARRIOR_ENRAGE:
+ case SPELL_MAGE_ARCANE_BRILLANCE:
+ case SPELL_PRIEST_DIVINE_SPIRIT:
return spellSpec1==spellSpec2;
case SPELL_BATTLE_ELIXIR:
return spellSpec2==SPELL_BATTLE_ELIXIR
@@ -532,8 +561,6 @@ bool IsPositiveTarget(uint32 targetA, uint32 targetB)
case TARGET_DST_TARGET_ENEMY:
case TARGET_UNIT_CHANNEL:
return false;
- case TARGET_SRC_CASTER:
- return (targetB == TARGET_UNIT_AREA_PARTY_SRC || targetB == TARGET_UNIT_AREA_ALLY_SRC);
default:
break;
}
@@ -542,27 +569,39 @@ bool IsPositiveTarget(uint32 targetA, uint32 targetB)
return true;
}
-bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
+bool IsPositiveEffect(uint32 spellId, uint32 effIndex, bool deep)
{
SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
if (!spellproto) return false;
switch(spellId)
{
- case 23333: // BG spell
- case 23335: // BG spell
- case 34976: // BG spell
- case 31579: // Arcane Empowerment Rank1 talent aura with one positive and one negative (check not needed in wotlk)
- case 31582: // Arcane Empowerment Rank2
- case 31583: // Arcane Empowerment Rank3
- return true;
case 28441: // not positive dummy spell
case 37675: // Chaos Blast
case 41519: // Mark of Stormrage
case 34877: // Custodian of Time
case 34700: // Allergic Reaction
case 31719: // Suspension
+ case 61987: // Avenging Wrath Marker
+ case 11196: // Recently Bandadged
return false;
+ case 12042: // Arcane Power
+ 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])
@@ -588,10 +627,19 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
{
case 13139: // net-o-matic special effect
case 23445: // evil twin
+ case 35679: // Protectorate Demolitionist
case 38637: // Nether Exhaustion (red)
case 38638: // Nether Exhaustion (green)
case 38639: // Nether Exhaustion (blue)
return false;
+ // some spells have unclear target modes for selection, so just make effect positive
+ case 27184:
+ case 27190:
+ case 27191:
+ case 27201:
+ case 27202:
+ case 27203:
+ return true;
default:
break;
}
@@ -599,15 +647,19 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
case SPELL_AURA_MOD_STAT:
case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from bas point sign (negative -> negative)
case SPELL_AURA_MOD_HEALING_DONE:
- {
- if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0)
+ case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
+ if(spellproto->CalculateSimpleValue(effIndex) < 0)
return false;
break;
- }
+ case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
+ if(spellproto->CalculateSimpleValue(effIndex) > 0)
+ return true; // some expected possitive 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(spellId != spellproto->EffectTriggerSpell[effIndex])
+ if(!deep)
{
uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex];
SpellEntry const *spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId);
@@ -617,14 +669,15 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
// 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))
+ if(IsPositiveTarget(spellTriggeredProto->EffectImplicitTargetA[effIndex],spellTriggeredProto->EffectImplicitTargetB[effIndex]) && !IsPositiveEffect(spellTriggeredId,i, true))
return false;
}
}
}
- break;
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;
@@ -698,19 +751,35 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
switch(spellproto->EffectMiscValue[effIndex])
{
case SPELLMOD_COST: // dependent from bas point sign (negative -> positive)
- if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) > 0)
- return false;
+ 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;
case SPELL_AURA_MOD_HEALING_PCT:
- if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0)
+ if(spellproto->CalculateSimpleValue(effIndex) < 0)
return false;
break;
case SPELL_AURA_MOD_SKILL:
- if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0)
+ if(spellproto->CalculateSimpleValue(effIndex) < 0)
return false;
break;
case SPELL_AURA_FORCE_REACTION:
@@ -734,19 +803,25 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
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)
+bool IsPositiveSpell(uint32 spellId, bool deep)
{
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))
+ for (int i = 0; i < 3; ++i)
+ if (!IsPositiveEffect(spellId, i, deep))
return false;
return true;
}
@@ -762,6 +837,8 @@ bool IsSingleTargetSpell(SpellEntry const *spellInfo)
{
case SPELL_JUDGEMENT:
return true;
+ default:
+ break;
}
// single target triggered spell.
@@ -791,6 +868,8 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI
if(GetSpellSpecific(spellInfo2->Id) == spec1)
return true;
break;
+ default:
+ break;
}
return false;
@@ -801,19 +880,19 @@ bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId)
SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
if (!spellproto) return false;
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < 3; ++i)
if (spellproto->EffectApplyAuraName[i] == auraType)
return true;
return false;
}
-uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form)
+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 0;
+ return SPELL_CAST_OK;
uint32 stanceMask = (form ? 1 << (form - 1) : 0);
@@ -821,16 +900,17 @@ uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form)
return SPELL_FAILED_NOT_SHAPESHIFT;
if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance
- return 0;
+ return SPELL_CAST_OK;
bool actAsShifted = false;
+ SpellShapeshiftEntry const *shapeInfo = NULL;
if (form > 0)
{
- SpellShapeshiftEntry const *shapeInfo = sSpellShapeshiftStore.LookupEntry(form);
+ shapeInfo = sSpellShapeshiftStore.LookupEntry(form);
if (!shapeInfo)
{
sLog.outError("GetErrorAtShapeshiftedCast: unknown shapeshift %u", form);
- return 0;
+ return SPELL_CAST_OK;
}
actAsShifted = !(shapeInfo->flags1 & 1); // shapeshift acts as normal form for spells
}
@@ -849,7 +929,16 @@ uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form)
return SPELL_FAILED_ONLY_SHAPESHIFT;
}
- return 0;
+ // 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()
@@ -931,6 +1020,42 @@ void SpellMgr::LoadSpellTargetPositions()
} 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)
+ {
+ SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(i);
+ SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(i);
+ if(lower == upper)
+ sLog.outDetail("Spell (ID: %u) does not have record in `spell_target_position`", i);
+ }
+ }
+
delete result;
sLog.outString();
@@ -943,8 +1068,8 @@ void SpellMgr::LoadSpellAffects()
uint32 count = 0;
- // 0 1 2
- QueryResult *result = WorldDatabase.Query("SELECT entry, effectId, SpellFamilyMask FROM spell_affect");
+ // 0 1 2 3 4
+ QueryResult *result = WorldDatabase.Query("SELECT entry, effectId, SpellClassMask0, SpellClassMask1, SpellClassMask2 FROM spell_affect");
if( !result )
{
@@ -982,35 +1107,22 @@ void SpellMgr::LoadSpellAffects()
continue;
}
- if( spellInfo->Effect[effectId] != SPELL_EFFECT_APPLY_AURA ||
- spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_FLAT_MODIFIER &&
- spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_PCT_MODIFIER &&
- spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_TARGET_TRIGGER )
- {
- sLog.outErrorDb("Spell %u listed in `spell_affect` have not SPELL_AURA_ADD_FLAT_MODIFIER (%u) or SPELL_AURA_ADD_PCT_MODIFIER (%u) or SPELL_AURA_ADD_TARGET_TRIGGER (%u) for effect index (%u)", entry,SPELL_AURA_ADD_FLAT_MODIFIER,SPELL_AURA_ADD_PCT_MODIFIER,SPELL_AURA_ADD_TARGET_TRIGGER,effectId);
- continue;
- }
+ flag96 affect(fields[2].GetUInt32(), fields[3].GetUInt32(), fields[4].GetUInt32());
- uint64 spellAffectMask = fields[2].GetUInt64();
+ // Spell.dbc have own data
+ if (effectId>3)
+ continue;
- // Spell.dbc have own data for low part of SpellFamilyMask
- if( spellInfo->EffectItemType[effectId])
+ flag96 dbc_affect;
+ dbc_affect = spellInfo->EffectSpellClassMask[effectId];
+ if(dbc_affect[0] == affect[0] && dbc_affect[1] == affect[1] && dbc_affect[2] == affect[2])
{
- if(spellInfo->EffectItemType[effectId] == spellAffectMask)
- {
- sLog.outErrorDb("Spell %u listed in `spell_affect` have redundant (same with EffectItemType%d) data for effect index (%u) and not needed, skipped.", entry,effectId+1,effectId);
- continue;
- }
-
- // 24429 have wrong data in EffectItemType and overwrites by DB, possible bug in client
- if(spellInfo->Id!=24429 && spellInfo->EffectItemType[effectId] != spellAffectMask)
- {
- sLog.outErrorDb("Spell %u listed in `spell_affect` have different low part from EffectItemType%d for effect index (%u) and not needed, skipped.", entry,effectId+1,effectId);
- continue;
- }
+ char text[]="ABC";
+ sLog.outErrorDb("Spell %u listed in `spell_affect` have redundant (same with EffectSpellClassMask%c) data for effect index (%u) and not needed, skipped.", entry, text[effectId], effectId);
+ continue;
}
- mSpellAffectMap.insert(SpellAffectMap::value_type((entry<<8) + effectId,spellAffectMask));
+ mSpellAffectMap[(entry<<8) + effectId] = affect;
++count;
} while( result->NextRow() );
@@ -1018,60 +1130,22 @@ void SpellMgr::LoadSpellAffects()
delete result;
sLog.outString();
- sLog.outString( ">> Loaded %u spell affect definitions", count );
-
- for (uint32 id = 0; id < sSpellStore.GetNumRows(); ++id)
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
- if (!spellInfo)
- continue;
-
- for (int effectId = 0; effectId < 3; ++effectId)
- {
- if( spellInfo->Effect[effectId] != SPELL_EFFECT_APPLY_AURA ||
- (spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_FLAT_MODIFIER &&
- spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_PCT_MODIFIER &&
- spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_TARGET_TRIGGER) )
- continue;
-
- if(spellInfo->EffectItemType[effectId] != 0)
- continue;
-
- if(mSpellAffectMap.find((id<<8) + effectId) != mSpellAffectMap.end())
- continue;
-
- sLog.outErrorDb("Spell %u (%s) misses spell_affect for effect %u",id,spellInfo->SpellName[sWorld.GetDefaultDbcLocale()], effectId);
- }
- }
+ sLog.outString( ">> Loaded %u custom spell affect definitions", count );
}
-bool SpellMgr::IsAffectedBySpell(SpellEntry const *spellInfo, uint32 spellId, uint8 effectId, uint64 familyFlags) const
+bool SpellMgr::IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const
{
// false for spellInfo == NULL
- if (!spellInfo)
+ if (!spellInfo || !mod)
return false;
- SpellEntry const *affect_spell = sSpellStore.LookupEntry(spellId);
- // false for affect_spell == NULL
- if (!affect_spell)
+ 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;
- // False if spellFamily not equal
- if (affect_spell->SpellFamilyName != spellInfo->SpellFamilyName)
- return false;
-
- // If familyFlags == 0
- if (!familyFlags)
- {
- // Get it from spellAffect table
- familyFlags = GetSpellAffectMask(spellId,effectId);
- // false if familyFlags == 0
- if (!familyFlags)
- return false;
- }
-
// true
- if (familyFlags & spellInfo->SpellFamilyFlags)
+ if (mod->mask & spellInfo->SpellFamilyFlags)
return true;
return false;
@@ -1083,15 +1157,12 @@ void SpellMgr::LoadSpellProcEvents()
uint32 count = 0;
- // 0 1 2 3 4 5 6 7 8
- QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event");
+ // 0 1 2 3 4 5 6 7 8 9 10
+ QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event");
if( !result )
{
-
barGoLink bar( 1 );
-
bar.step();
-
sLog.outString();
sLog.outString( ">> Loaded %u spell proc event conditions", count );
return;
@@ -1105,7 +1176,7 @@ void SpellMgr::LoadSpellProcEvents()
bar.step();
- uint16 entry = fields[0].GetUInt16();
+ uint32 entry = fields[0].GetUInt32();
const SpellEntry *spell = sSpellStore.LookupEntry(entry);
if (!spell)
@@ -1118,12 +1189,14 @@ void SpellMgr::LoadSpellProcEvents()
spe.schoolMask = fields[1].GetUInt32();
spe.spellFamilyName = fields[2].GetUInt32();
- spe.spellFamilyMask = fields[3].GetUInt64();
- spe.procFlags = fields[4].GetUInt32();
- spe.procEx = fields[5].GetUInt32();
- spe.ppmRate = fields[6].GetFloat();
- spe.customChance = fields[7].GetFloat();
- spe.cooldown = fields[8].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;
@@ -1143,82 +1216,56 @@ void SpellMgr::LoadSpellProcEvents()
sLog.outString();
if (customProc)
- sLog.outString( ">> Loaded %u custom spell proc event conditions +%u custom", count, customProc );
+ sLog.outString( ">> Loaded %u extra spell proc event conditions +%u custom", count, customProc );
else
- sLog.outString( ">> Loaded %u spell proc event conditions", count );
+ sLog.outString( ">> Loaded %u extra spell proc event conditions", count );
+}
- /*
- // Commented for now, as it still produces many errors (still quite many spells miss spell_proc_event)
- for (uint32 id = 0; id < sSpellStore.GetNumRows(); ++id)
+void SpellMgr::LoadSpellBonusess()
+{
+ mSpellBonusMap.clear(); // need for reload case
+ uint32 count = 0;
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.Query("SELECT entry, direct_bonus, dot_bonus, ap_bonus FROM spell_bonus_data");
+ if( !result )
{
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
- if (!spellInfo)
- continue;
-
- bool found = false;
- for (int effectId = 0; effectId < 3; ++effectId)
- {
- // at this moment check only SPELL_AURA_PROC_TRIGGER_SPELL
- if( spellInfo->EffectApplyAuraName[effectId] == SPELL_AURA_PROC_TRIGGER_SPELL )
- {
- found = true;
- break;
- }
- }
+ barGoLink bar( 1 );
+ bar.step();
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell bonus data", count);
+ return;
+ }
- if(!found)
- continue;
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+ uint32 entry = fields[0].GetUInt32();
- if(GetSpellProcEvent(id))
+ const SpellEntry *spell = sSpellStore.LookupEntry(entry);
+ if (!spell)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry);
continue;
+ }
- sLog.outErrorDb("Spell %u (%s) misses spell_proc_event",id,spellInfo->SpellName[sWorld.GetDBClang()]);
- }
- */
-}
+ SpellBonusEntry sbe;
-/*
-bool SpellMgr::IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags )
-{
- if((procFlags & spellProcEvent->procFlags) == 0)
- return false;
-
- // Additional checks in case spell cast/hit/crit is the event
- // Check (if set) school, category, skill line, spell talent mask
- if(spellProcEvent->schoolMask && (!procSpell || (GetSpellSchoolMask(procSpell) & spellProcEvent->schoolMask) == 0))
- return false;
- if(spellProcEvent->category && (!procSpell || procSpell->Category != spellProcEvent->category))
- return false;
- if(spellProcEvent->skillId)
- {
- if (!procSpell)
- return false;
+ sbe.direct_damage = fields[1].GetFloat();
+ sbe.dot_damage = fields[2].GetFloat();
+ sbe.ap_bonus = fields[3].GetFloat();
- SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(procSpell->Id);
- SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(procSpell->Id);
+ mSpellBonusMap[entry] = sbe;
+ } while( result->NextRow() );
- bool found = false;
- for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
- {
- if(_spell_idx->second->skillId == spellProcEvent->skillId)
- {
- found = true;
- break;
- }
- }
- if (!found)
- return false;
- }
- if(spellProcEvent->spellFamilyName && (!procSpell || spellProcEvent->spellFamilyName != procSpell->SpellFamilyName))
- return false;
- if(spellProcEvent->spellFamilyMask && (!procSpell || (spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0))
- return false;
+ delete result;
- return true;
+ 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)
+bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra)
{
// No extra req need
uint32 procEvent_procEx = PROC_EX_NONE;
@@ -1227,45 +1274,46 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const * spellP
if((procFlags & EventProcFlag) == 0)
return false;
+ bool hasFamilyMask = false;
+
/* Check Periodic Auras
- * Both hots and dots can trigger if spell has no PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL
- nor PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT
+ * Both hots and dots can trigger if spell has no PROC_FLAG_SUCCESSFUL_DAMAGING_SPELL_HIT
+ nor PROC_FLAG_SUCCESSFUL_HEALING_SPELL
- *Only Hots can trigger if spell has PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL
+ *Only Hots can trigger if spell has PROC_FLAG_SUCCESSFUL_HEALING_SPELL
- *Only dots can trigger if spell has both positivity flags or PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT
+ *Only dots can trigger if spell has both positivity flags or PROC_FLAG_SUCCESSFUL_DAMAGING_SPELL_HIT
*/
if (procFlags & PROC_FLAG_ON_DO_PERIODIC)
{
- if (EventProcFlag & PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT)
+ if (EventProcFlag & PROC_FLAG_SUCCESSFUL_DAMAGING_SPELL_HIT)
{
if (!(procExtra & PROC_EX_INTERNAL_DOT))
return false;
}
- else if (EventProcFlag & PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL
+ else if (EventProcFlag & PROC_FLAG_SUCCESSFUL_HEALING_SPELL
&& !(procExtra & PROC_EX_INTERNAL_HOT))
return false;
}
if (procFlags & PROC_FLAG_ON_TAKE_PERIODIC)
{
- if (EventProcFlag & PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT)
+ if (EventProcFlag & PROC_FLAG_TAKEN_DAMAGING_SPELL_HIT)
{
if (!(procExtra & PROC_EX_INTERNAL_DOT))
return false;
}
- else if (EventProcFlag & PROC_FLAG_TAKEN_POSITIVE_SPELL
+ else if (EventProcFlag & PROC_FLAG_TAKEN_HEALING_SPELL
&& !(procExtra & PROC_EX_INTERNAL_HOT))
return false;
}
// Always trigger for this
- if (EventProcFlag & (PROC_FLAG_KILLED | PROC_FLAG_KILL_AND_GET_XP))
+ if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_ON_TRAP_ACTIVATION))
return true;
-
if (spellProcEvent) // Exist event data
{
// Store extra req
@@ -1291,27 +1339,31 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const * spellP
// spellFamilyName is Ok need check for spellFamilyMask if present
if(spellProcEvent->spellFamilyMask)
{
- if ((spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0)
+ if ((spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags ) == 0)
return false;
- active = true; // Spell added manualy -> so its active spell
+ hasFamilyMask = 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 active (damage/healing present) and hit/crit
- if((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && active)
+ // No extra req, so can trigger only for hit/crit
+ if((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)))
return true;
}
else // Passive spells hits here only if resist/reflect/immune/evade
{
// Exist req for PROC_EX_EX_TRIGGER_ALWAYS
- if (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS)
+ if ((procExtra & AURA_SPELL_PROC_EX_MASK) && (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS))
return true;
- // Passive spells can`t trigger if need hit
- if ((procEvent_procEx & PROC_EX_NORMAL_HIT) && !active)
- return false;
// Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other)
if (procEvent_procEx & procExtra)
return true;
@@ -1379,58 +1431,6 @@ void SpellMgr::LoadSpellThreats()
sLog.outString();
}
-void SpellMgr::LoadSpellEnchantProcData()
-{
- mSpellEnchantProcEventMap.clear(); // need for reload case
-
- uint32 count = 0;
-
- // 0 1 2 3
- QueryResult *result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data");
- if( !result )
- {
-
- 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() );
-
- delete result;
-
- sLog.outString( ">> Loaded %u enchant proc data definitions", count);
-}
-
bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const
{
SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
@@ -1442,113 +1442,57 @@ bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellI
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(IsProfessionSpell(spellInfo->Id))
+ 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++)
+ for (int i = 0; i < 3; ++i)
{
- // Paladin aura Spell
- if(spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN
- && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AREA_AURA_PARTY)
- return false;
- // Druid form Spell
- if(spellInfo->SpellFamilyName == SPELLFAMILY_DRUID
- && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA
- && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT)
- return false;
- // Rogue Stealth
- if(spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE
- && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA
- && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT)
- return false;
+ 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::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool sameCaster) const
-{
- //if(spellId_1 == spellId_2) // auras due to the same spell
- // return false;
- SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
- SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
-
- if(!spellInfo_1 || !spellInfo_2)
- return false;
- SpellSpecific spellId_spec_1 = GetSpellSpecific(spellId_1);
- SpellSpecific spellId_spec_2 = GetSpellSpecific(spellId_2);
- if (spellId_spec_1 && spellId_spec_2)
- if (IsSingleFromSpellSpecificPerTarget(spellId_spec_1, spellId_spec_2)
- ||(IsSingleFromSpellSpecificPerCaster(spellId_spec_1, spellId_spec_2) && sameCaster))
- return true;
-
- // spells with different specific always stack
- if(spellId_spec_1 != spellId_spec_2)
+bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if(!spellInfo)
return false;
- if(spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName)
+ if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL)
return false;
- // generic spells
- if(!spellInfo_1->SpellFamilyName)
- {
- if(!spellInfo_1->SpellIconID
- || spellInfo_1->SpellIconID == 1
- || spellInfo_1->SpellIconID != spellInfo_2->SpellIconID)
- return false;
- }
-
- // check for class spells
- else
- {
- if (spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags)
- return false;
- }
-
- if(!sameCaster)
- {
- for(uint32 i = 0; i < 3; ++i)
- if (spellInfo_1->Effect[i] == SPELL_EFFECT_APPLY_AURA
- || spellInfo_1->Effect[i] == SPELL_EFFECT_PERSISTENT_AREA_AURA)
- // not area auras (shaman totem)
- 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_MANA:
- case SPELL_AURA_OBS_MOD_HEALTH:
- return false;
- default:
- break;
- }
- }
-
-// not needed now because we compare effects last rank of spells
-// if(spellInfo_1->SpellFamilyName && IsRankSpellDueToSpell(spellInfo_1, spellId_2))
-// return true;
-
- //use data of highest rank spell(needed for spells which ranks have different effects)
- spellInfo_1=sSpellStore.LookupEntry(GetLastSpellInChain(spellId_1));
- spellInfo_2=sSpellStore.LookupEntry(GetLastSpellInChain(spellId_2));
-
- //if spells have exactly the same effect they cannot stack
- for(uint32 i = 0; i < 3; ++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 false; // need itemtype check? need an example to add that check
+ uint32 skill = spellInfo->EffectMiscValue[1];
- return true;
+ return IsProfessionOrRidingSkill(skill);
}
+
bool SpellMgr::IsProfessionSpell(uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
@@ -1582,6 +1526,24 @@ bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const
return IsPrimaryProfessionSpell(spellId) && GetSpellRank(spellId)==1;
}
+bool SpellMgr::IsSkillBonusSpell(uint32 spellId) const
+{
+ SkillLineAbilityMap::const_iterator lower = GetBeginSkillLineAbilityMap(spellId);
+ SkillLineAbilityMap::const_iterator upper = GetEndSkillLineAbilityMap(spellId);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_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
@@ -1589,11 +1551,12 @@ SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spell
return spellInfo;
bool needRankSelection = false;
- for(int i=0;i<3;i++)
+ 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_PARTY ||
+ spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_RAID
) )
{
needRankSelection = true;
@@ -1622,265 +1585,6 @@ SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spell
return NULL;
}
-void SpellMgr::LoadSpellRequired()
-{
- mSpellsReqSpell.clear(); // need for reload case
- mSpellReq.clear(); // need for reload case
-
- QueryResult *result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required");
-
- if(result == NULL)
- {
- 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();
-
- mSpellsReqSpell.insert (std::pair<uint32, uint32>(spell_req, spell_id));
- mSpellReq[spell_id] = spell_req;
- ++rows;
- } while( result->NextRow() );
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u spell required records", rows );
-}
-
-struct SpellRankEntry
-{
- uint32 SkillId;
- char const *SpellName;
- uint32 DurationIndex;
- uint32 RangeIndex;
- uint32 SpellVisual;
- uint32 ProcFlags;
- uint64 SpellFamilyFlags;
- uint32 TargetAuraState;
- uint32 ManaCost;
-
- bool operator()(const SpellRankEntry & _Left,const SpellRankEntry & _Right)const
- {
- return (_Left.SkillId != _Right.SkillId ? _Left.SkillId < _Right.SkillId
- : _Left.SpellName!=_Right.SpellName ? _Left.SpellName < _Right.SpellName
- : _Left.ProcFlags!=_Right.ProcFlags ? _Left.ProcFlags < _Right.ProcFlags
- : _Left.SpellFamilyFlags!=_Right.SpellFamilyFlags ? _Left.SpellFamilyFlags < _Right.SpellFamilyFlags
- : (_Left.SpellVisual!=_Right.SpellVisual) && (!_Left.SpellVisual || !_Right.SpellVisual) ? _Left.SpellVisual < _Right.SpellVisual
- : (_Left.ManaCost!=_Right.ManaCost) && (!_Left.ManaCost || !_Right.ManaCost) ? _Left.ManaCost < _Right.ManaCost
- : (_Left.DurationIndex!=_Right.DurationIndex) && (!_Left.DurationIndex || !_Right.DurationIndex)? _Left.DurationIndex < _Right.DurationIndex
- : (_Left.RangeIndex!=_Right.RangeIndex) && (!_Left.RangeIndex || !_Right.RangeIndex || _Left.RangeIndex==1 || !_Right.RangeIndex==1) ? _Left.RangeIndex < _Right.RangeIndex
- : _Left.TargetAuraState < _Right.TargetAuraState
- );
- }
-};
-
-struct SpellRankValue
-{
- uint32 Id;
- char const *Rank;
-};
-
-void SpellMgr::LoadSpellChains()
-{
- mSpellChains.clear(); // need for reload case
-
- std::vector<uint32> ChainedSpells;
- for (uint32 ability_id=0;ability_id<sSkillLineAbilityStore.GetNumRows();ability_id++)
- {
- SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id);
- if (!AbilityInfo)
- continue;
- if (AbilityInfo->spellId==20154) //exception to these rules (not needed in 3.0.3)
- continue;
- if (!AbilityInfo->forward_spellid)
- continue;
- ChainedSpells.push_back(AbilityInfo->forward_spellid);
- }
-
- std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry> RankMap;
-
- for (uint32 ability_id=0;ability_id<sSkillLineAbilityStore.GetNumRows();ability_id++)
- {
- SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id);
- if (!AbilityInfo)
- continue;
-
- //get only spell with lowest ability_id to prevent doubles
- uint32 spell_id=AbilityInfo->spellId;
- if (spell_id==20154) //exception to these rules (not needed in 3.0.3)
- continue;
- bool found=false;
- for (uint32 i=0; i<ChainedSpells.size(); i++)
- {
- if (ChainedSpells.at(i)==spell_id)
- found=true;
- }
- if (found)
- continue;
-
- if(mSkillLineAbilityMap.lower_bound(spell_id)->second->id!=ability_id)
- continue;
- SpellEntry const *SpellInfo=sSpellStore.LookupEntry(spell_id);
- if (!SpellInfo)
- continue;
- std::string sRank = SpellInfo->Rank[sWorld.GetDefaultDbcLocale()];
- if(sRank.empty())
- continue;
- //exception to polymorph spells-make pig and turtle other chain than sheep
- if ((SpellInfo->SpellFamilyName==SPELLFAMILY_MAGE) && (SpellInfo->SpellFamilyFlags & 0x1000000) && (SpellInfo->SpellIconID!=82))
- continue;
-
- SpellRankEntry entry;
- SpellRankValue value;
- entry.SkillId=AbilityInfo->skillId;
- entry.SpellName=SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()];
- entry.DurationIndex=SpellInfo->DurationIndex;
- entry.RangeIndex=SpellInfo->rangeIndex;
- entry.ProcFlags=SpellInfo->procFlags;
- entry.SpellFamilyFlags=SpellInfo->SpellFamilyFlags;
- entry.TargetAuraState=SpellInfo->TargetAuraState;
- entry.SpellVisual=SpellInfo->SpellVisual;
- entry.ManaCost=SpellInfo->manaCost;
-
- for (;;)
- {
- AbilityInfo=mSkillLineAbilityMap.lower_bound(spell_id)->second;
- value.Id=spell_id;
- value.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()];
- RankMap.insert(std::pair<SpellRankEntry, SpellRankValue>(entry,value));
- spell_id=AbilityInfo->forward_spellid;
- SpellInfo=sSpellStore.LookupEntry(spell_id);
- if (!SpellInfo)
- break;
- }
- }
-
- barGoLink bar(RankMap.size());
-
- uint32 count=0;
-
- for (std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator itr = RankMap.begin();itr!=RankMap.end();)
- {
- SpellRankEntry entry=itr->first;
- //trac errors in extracted data
- std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator> RankErrorMap;
- for (std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++)
- {
- bar.step();
- RankErrorMap.insert(std::pair<char const *, std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator>(itr2->second.Rank,itr2));
- }
- for (std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();)
- {
- char const * err_entry=itr2->first;
- uint32 rank_count=RankErrorMap.count(itr2->first);
- if (rank_count>1)
- for (itr2 = RankErrorMap.lower_bound(err_entry);itr2!=RankErrorMap.upper_bound(err_entry);itr2++)
- {
- sLog.outDebug("There is a duplicate rank entry (%s) for spell: %u",itr2->first,itr2->second->second.Id);
- sLog.outDebug("Spell %u removed from chain data.",itr2->second->second.Id);
- RankMap.erase(itr2->second);
- itr=RankMap.lower_bound(entry);
- }
- else
- itr2++;
- }
- //do not proceed for spells with less than 2 ranks
- uint32 spell_max_rank=RankMap.count(entry);
- if (spell_max_rank<2)
- {
- itr=RankMap.upper_bound(entry);
- continue;
- }
-
- itr=RankMap.upper_bound(entry);
-
- //order spells by spells by spellLevel
- std::list<uint32> RankedSpells;
- uint32 min_spell_lvl=0;
- std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator min_itr;
- for (;RankMap.count(entry);)
- {
- for (std::multimap<SpellRankEntry, SpellRankValue,SpellRankEntry>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++)
- {
- SpellEntry const *SpellInfo=sSpellStore.LookupEntry(itr2->second.Id);
- if (SpellInfo->spellLevel<min_spell_lvl || itr2==RankMap.lower_bound(entry))
- {
- min_spell_lvl=SpellInfo->spellLevel;
- min_itr=itr2;
- }
- }
- RankedSpells.push_back(min_itr->second.Id);
- RankMap.erase(min_itr);
- }
-
- //use data from talent.dbc
- uint16 talent_id=0;
- for(std::list<uint32>::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();)
- {
- if (TalentSpellPos const* TalentPos=GetTalentSpellPos(*itr2))
- {
- talent_id=TalentPos->talent_id;
- RankedSpells.erase(itr2);
- itr2 = RankedSpells.begin();
- }
- else
- itr2++;
- }
- if (talent_id)
- {
- TalentEntry const *TalentInfo = sTalentStore.LookupEntry(talent_id);
- for (uint8 rank=5;rank;rank--)
- {
- if (TalentInfo->RankID[rank-1])
- RankedSpells.push_front(TalentInfo->RankID[rank-1]);
- }
- }
-
- count++;
-
- itr=RankMap.upper_bound(entry);
- uint32 spell_rank=1;
- for(std::list<uint32>::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();spell_rank++)
- {
- uint32 spell_id=*itr2;
- mSpellChains[spell_id].rank=spell_rank;
- mSpellChains[spell_id].first=RankedSpells.front();
- mSpellChains[spell_id].last=RankedSpells.back();
-
- itr2++;
- if (spell_rank<2)
- mSpellChains[spell_id].prev=0;
-
- if (spell_id==RankedSpells.back())
- mSpellChains[spell_id].next=0;
- else
- {
- mSpellChains[*itr2].prev=spell_id;
- mSpellChains[spell_id].next=*itr2;
- }
- }
- }
-
-//uncomment these two lines to print yourself list of spell_chains on startup
-// for (UNORDERED_MAP<uint32, SpellChainNode>::iterator itr=mSpellChains.begin();itr!=mSpellChains.end();itr++)
-// sLog.outString( "Id: %u, Rank: %d , %s",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()]);
-
- sLog.outString();
- sLog.outString( ">> Loaded %u spell chains",count);
-}
void SpellMgr::LoadSpellLearnSkills()
{
@@ -1888,8 +1592,10 @@ void SpellMgr::LoadSpellLearnSkills()
// 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)
@@ -1902,10 +1608,10 @@ void SpellMgr::LoadSpellLearnSkills()
SpellLearnSkillNode dbc_node;
dbc_node.skill = entry->EffectMiscValue[i];
if ( dbc_node.skill != SKILL_RIDING )
- dbc_node.value = 1;
+ dbc_node.value = 1;
else
- dbc_node.value = (entry->EffectBasePoints[i]+1)*75;
- dbc_node.maxvalue = (entry->EffectBasePoints[i]+1)*75;
+ dbc_node.value = entry->CalculateSimpleValue(i)*75;
+ dbc_node.maxvalue = entry->CalculateSimpleValue(i)*75;
SpellLearnSkillNode const* db_node = GetSpellLearnSkill(spell);
@@ -1924,7 +1630,8 @@ void SpellMgr::LoadSpellLearnSpells()
{
mSpellLearnSpells.clear(); // need for reload case
- QueryResult *result = WorldDatabase.Query("SELECT entry, SpellID FROM spell_learn_spell");
+ // 0 1 2
+ QueryResult *result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell");
if(!result)
{
barGoLink bar( 1 );
@@ -1948,6 +1655,7 @@ void SpellMgr::LoadSpellLearnSpells()
SpellLearnSpellNode node;
node.spell = fields[1].GetUInt32();
+ node.active = fields[2].GetBool();
node.autoLearned= false;
if(!sSpellStore.LookupEntry(spell_id))
@@ -1984,7 +1692,16 @@ void SpellMgr::LoadSpellLearnSpells()
{
SpellLearnSpellNode dbc_node;
dbc_node.spell = entry->EffectTriggerSpell[i];
- dbc_node.autoLearned = true;
+ 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);
SpellLearnSpellMap::const_iterator db_node_begin = GetBeginSpellLearnSpell(spell);
SpellLearnSpellMap::const_iterator db_node_end = GetEndSpellLearnSpell(spell);
@@ -2029,8 +1746,7 @@ void SpellMgr::LoadSpellScriptTarget()
bar.step();
sLog.outString();
- sLog.outString( ">> Loaded 0 spell script target" );
- sLog.outErrorDb("`spell_script_target` table is empty!");
+ sLog.outErrorDb(">> Loaded 0 SpellScriptTarget. DB table `spell_script_target` is empty.");
return;
}
@@ -2123,29 +1839,48 @@ void SpellMgr::LoadSpellScriptTarget()
delete result;
// Check all spells
- /* Disabled (lot errors at this moment)
- for(uint32 i = 1; i < sSpellStore.nCount; ++i)
+ 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)
+ for(int j = 0; j < 3; ++j)
{
- if( spellInfo->EffectImplicitTargetA[j] == TARGET_UNIT_NEARBY_ENTRY || spellInfo->EffectImplicitTargetA[j] != TARGET_UNIT_CASTER && spellInfo->EffectImplicitTargetB[j] == TARGET_UNIT_NEARBY_ENTRY )
+ switch(spellInfo->EffectImplicitTargetA[j])
{
- SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(spellInfo->Id);
- SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(spellInfo->Id);
- if(lower==upper)
- {
- sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = %u (TARGET_UNIT_NEARBY_ENTRY), but does not have record in `spell_script_target`",spellInfo->Id,TARGET_UNIT_NEARBY_ENTRY);
- break; // effects of spell
- }
+ case TARGET_UNIT_AREA_ENTRY_SRC:
+ case TARGET_UNIT_AREA_ENTRY_DST:
+ case TARGET_UNIT_NEARBY_ENTRY:
+ case TARGET_DST_NEARBY_ENTRY:
+ case TARGET_UNIT_CONE_ENTRY:
+ found = true;
+ break;
+ }
+ if(found)
+ break;
+ switch(spellInfo->EffectImplicitTargetB[j])
+ {
+ case TARGET_UNIT_AREA_ENTRY_SRC:
+ case TARGET_UNIT_AREA_ENTRY_DST:
+ case TARGET_UNIT_NEARBY_ENTRY:
+ case TARGET_DST_NEARBY_ENTRY:
+ case TARGET_UNIT_CONE_ENTRY:
+ found = true;
+ break;
}
+ if(found)
+ break;
+ }
+ if(found)
+ {
+ SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(i);
+ SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(i);
+ if(lower == upper)
+ sLog.outDetail("Spell (ID: %u) does not have record in `spell_script_target`", i);
}
}
- */
sLog.outString();
sLog.outString(">> Loaded %u Spell Script Targets", count);
@@ -2157,8 +1892,8 @@ void SpellMgr::LoadSpellPetAuras()
uint32 count = 0;
- // 0 1 2
- QueryResult *result = WorldDatabase.Query("SELECT spell, pet, aura FROM spell_pet_auras");
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.Query("SELECT spell, effectId, pet, aura FROM spell_pet_auras");
if( !result )
{
@@ -2180,10 +1915,11 @@ void SpellMgr::LoadSpellPetAuras()
bar.step();
uint16 spell = fields[0].GetUInt16();
- uint16 pet = fields[1].GetUInt16();
- uint16 aura = fields[2].GetUInt16();
+ uint8 eff = fields[1].GetUInt8();
+ uint16 pet = fields[2].GetUInt16();
+ uint16 aura = fields[3].GetUInt16();
- SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find(spell);
+ SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff);
if(itr != mSpellPetAuraMap.end())
{
itr->second.AddAura(pet, aura);
@@ -2196,14 +1932,9 @@ void SpellMgr::LoadSpellPetAuras()
sLog.outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell);
continue;
}
- int i = 0;
- for(; i < 3; ++i)
- if((spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA &&
- spellInfo->EffectApplyAuraName[i] == SPELL_AURA_DUMMY) ||
- spellInfo->Effect[i] == SPELL_EFFECT_DUMMY)
- break;
-
- if(i == 3)
+ 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;
@@ -2216,8 +1947,8 @@ void SpellMgr::LoadSpellPetAuras()
continue;
}
- PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_PET, spellInfo->EffectBasePoints[i] + spellInfo->EffectBaseDice[i]);
- mSpellPetAuraMap[spell] = pa;
+ PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[eff] == TARGET_UNIT_PET, spellInfo->CalculateSimpleValue(eff));
+ mSpellPetAuraMap[(spell<<8) + eff] = pa;
}
++count;
@@ -2229,240 +1960,185 @@ void SpellMgr::LoadSpellPetAuras()
sLog.outString( ">> Loaded %u spell pet auras", count );
}
-// set data in core for now
-void SpellMgr::LoadSpellCustomAttr()
+void SpellMgr::LoadPetLevelupSpellMap()
{
- mSpellCustomAttr.resize(GetSpellStore()->GetNumRows());
+ mPetLevelupSpellMap.clear(); // need for reload case
- SpellEntry *spellInfo;
- for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
+ uint32 count = 0;
+ uint32 family_count = 0;
+
+ for (uint32 i = 0; i < sCreatureFamilyStore.GetNumRows(); ++i)
{
- mSpellCustomAttr[i] = 0;
- spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i);
- if(!spellInfo)
+ CreatureFamilyEntry const *creatureFamily = sCreatureFamilyStore.LookupEntry(i);
+ if(!creatureFamily) // not exist
continue;
- bool auraSpell = true;
- for(uint32 j = 0; j < 3; ++j)
+ for (uint8 j = 0; j < 2; ++j)
{
- if(spellInfo->Effect[j])
- if(spellInfo->Effect[j] != SPELL_EFFECT_APPLY_AURA
- || SpellTargetType[spellInfo->EffectImplicitTargetA[j]] != TARGET_TYPE_UNIT_TARGET)
- //ignore target party for now
- {
- auraSpell = false;
- break;
- }
- }
- if(auraSpell)
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_SPELL;
+ if (!creatureFamily->skillLine[j])
+ continue;
- for(uint32 j = 0; j < 3; ++j)
- {
- switch(spellInfo->EffectApplyAuraName[j])
+ for (uint32 k = 0; k < sSkillLineAbilityStore.GetNumRows(); ++k)
{
- case SPELL_AURA_PERIODIC_DAMAGE:
- case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
- case SPELL_AURA_PERIODIC_LEECH:
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_DOT;
- break;
- case SPELL_AURA_PERIODIC_HEAL:
- case SPELL_AURA_OBS_MOD_HEALTH:
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_HOT;
- break;
- case SPELL_AURA_MOD_ROOT:
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_CC;
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_MOVEMENT_IMPAIR;
- break;
- case SPELL_AURA_MOD_DECREASE_SPEED:
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_MOVEMENT_IMPAIR;
- break;
- default:
- break;
- }
+ SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(k);
+ if( !skillLine )
+ continue;
- 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;
- break;
- case SPELL_EFFECT_CHARGE:
- if(!spellInfo->speed && !spellInfo->SpellFamilyName)
- spellInfo->speed = SPEED_CHARGE;
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_CHARGE;
- break;
- case SPELL_EFFECT_TRIGGER_SPELL:
- if(spellInfo->Targets & (TARGET_FLAG_SOURCE_LOCATION|TARGET_FLAG_DEST_LOCATION))
- spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE;
- break;
+ //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++;
}
}
+ }
- for(uint32 j = 0; j < 3; ++j)
+ 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(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
+ {
+ if(petDefSpells.spellid[j])
{
- switch(spellInfo->EffectApplyAuraName[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(int 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)
{
- case SPELL_AURA_MOD_POSSESS:
- case SPELL_AURA_MOD_CONFUSE:
- case SPELL_AURA_MOD_CHARM:
- case SPELL_AURA_MOD_FEAR:
- case SPELL_AURA_MOD_STUN:
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_CC;
- mSpellCustomAttr[i] &= ~SPELL_ATTR_CU_MOVEMENT_IMPAIR;
+ if (itr->second == petDefSpells.spellid[j])
+ {
+ petDefSpells.spellid[j] = 0;
break;
+ }
}
}
+ }
- if(spellInfo->SpellVisual == 3879)
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_BACK;
-
- switch(i)
+ // skip empty list;
+ have_spell = false;
+ for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
+ {
+ if(petDefSpells.spellid[j])
{
- case 26029: // dark glare
- case 37433: // spout
- case 43140: case 43215: // flame breath
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_LINE;
- break;
- case 24340: case 26558: case 28884: // Meteor
- case 36837: case 38903: case 41276: // 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
- mSpellCustomAttr[i] |= SPELL_ATTR_CU_SHARE_DAMAGE;
- 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
- spellInfo->MaxAffectedTargets = 1;
- 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
- spellInfo->MaxAffectedTargets = 3;
- break;
- case 38310: //Multi-Shot
- spellInfo->MaxAffectedTargets = 4;
- break;
- case 42005: // Bloodboil
- case 38296: //Spitfire Totem
- case 37676: //Insidious Whisper
- case 46008: //Negative Energy
- case 45641: //Fire Bloom
- spellInfo->MaxAffectedTargets = 5;
- break;
- case 40827: //Sinful Beam
- case 40859: //Sinister Beam
- case 40860: //Vile Beam
- case 40861: //Wicked Beam
- spellInfo->MaxAffectedTargets = 10;
- break;
- case 8122: case 8124: case 10888: case 10890: // Psychic Scream
- case 12494: // Frostbite
- spellInfo->Attributes |= SPELL_ATTR_BREAKABLE_BY_DAMAGE;
- break;
- case 38794: case 33711: //Murmur's Touch
- spellInfo->MaxAffectedTargets = 1;
- spellInfo->EffectTriggerSpell[0] = 33760;
- break;
- default:
+ have_spell = true;
break;
}
}
+
+ return have_spell;
}
-void SpellMgr::LoadSpellLinked()
+void SpellMgr::LoadPetDefaultSpells()
{
- mSpellLinkedMap.clear(); // need for reload case
- uint32 count = 0;
-
- // 0 1 2
- QueryResult *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;
- }
+ mPetDefaultSpellsMap.clear();
- barGoLink bar( result->GetRowCount() );
+ uint32 countCreature = 0;
+ uint32 countData = 0;
- do
+ for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i )
{
- Field *fields = result->Fetch();
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
+ if(!cInfo)
+ continue;
- bar.step();
+ if(!cInfo->PetSpellDataId)
+ continue;
- int32 trigger = fields[0].GetInt32();
- int32 effect = fields[1].GetInt32();
- int32 type = fields[2].GetInt32();
+ // for creature with PetSpellDataId get default pet spells from dbc
+ CreatureSpellDataEntry const* spellDataEntry = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
+ if(!spellDataEntry)
+ continue;
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(trigger));
- if (!spellInfo)
+ int32 petSpellsId = -(int32)cInfo->PetSpellDataId;
+ PetDefaultSpellsEntry petDefSpells;
+ for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
+ petDefSpells.spellid[j] = spellDataEntry->spellId[j];
+
+ if(LoadPetDefaultSpells_helper(cInfo, petDefSpells))
{
- sLog.outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(trigger));
- continue;
+ mPetDefaultSpellsMap[petSpellsId] = petDefSpells;
+ ++countData;
}
- spellInfo = sSpellStore.LookupEntry(abs(effect));
- if (!spellInfo)
- {
- sLog.outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(effect));
+ }
+
+ // different summon spells
+ for(uint32 i = 0; i < sSpellStore.GetNumRows(); ++i )
+ {
+ SpellEntry const* spellEntry = sSpellStore.LookupEntry(i);
+ if(!spellEntry)
continue;
- }
- if(trigger > 0)
+ for(int k = 0; k < 3; ++k)
{
- switch(type)
+ if(spellEntry->Effect[k]==SPELL_EFFECT_SUMMON || spellEntry->Effect[k]==SPELL_EFFECT_SUMMON_PET)
{
- 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;
- }
+ uint32 creature_id = spellEntry->EffectMiscValue[k];
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(creature_id);
+ if(!cInfo)
+ continue;
- 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);
+ // already loaded
+ if(cInfo->PetSpellDataId)
+ continue;
- ++count;
- } while( result->NextRow() );
+ // for creature without PetSpellDataId get default pet spells from creature_template
+ int32 petSpellsId = cInfo->Entry;
+ if(mPetDefaultSpellsMap.find(cInfo->Entry) != mPetDefaultSpellsMap.end())
+ continue;
- delete result;
+ PetDefaultSpellsEntry petDefSpells;
+ for(int 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 %u linked spells", count );
+ sLog.outString( ">> Loaded addition spells for %u pet spell data entries and %u summonable creature templates", countData, countCreature );
}
-/// Some checks for spells, to prevent adding depricated/broken spells for trainers, spell book, etc
+/// 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
@@ -2479,8 +2155,9 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg)
case 0:
continue;
- // craft spell for crafting non-existed item (break client recipes list show)
+ // craft spell for crafting non-existed item (break client recipes list show)
case SPELL_EFFECT_CREATE_ITEM:
+ case SPELL_EFFECT_CREATE_ITEM_2:
{
if(!ObjectMgr::GetItemPrototype( spellInfo->EffectItemType[i] ))
{
@@ -2537,82 +2214,334 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg)
return true;
}
-bool IsSpellAllowedInLocation(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id)
+void SpellMgr::LoadSpellAreas()
{
- // normal case
- if( spellInfo->AreaId && spellInfo->AreaId != zone_id && spellInfo->AreaId != area_id )
- return false;
+ 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 *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() );
- // elixirs (all area dependent elixirs have family SPELLFAMILY_POTION, use this for speedup)
- if(spellInfo->SpellFamilyName==SPELLFAMILY_POTION)
+ do
{
- if(uint32 mask = spellmgr.GetSpellElixirMask(spellInfo->Id))
+ 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;
+ }
+
{
- if(mask & ELIXIR_BATTLE_MASK)
+ bool ok = true;
+ SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId);
+ for(SpellAreaMap::const_iterator itr = sa_bounds.first; itr != sa_bounds.second; ++itr)
{
- if(spellInfo->Id==45373) // Bloodberry Elixir
- return zone_id==4075;
+ if(spellArea.spellId && itr->second.spellId && spellArea.spellId != itr->second.spellId)
+ continue;
+ if(spellArea.areaId && itr->second.areaId && spellArea.areaId!= itr->second.areaId)
+ continue;
+ if(spellArea.questStart && itr->second.questStart && spellArea.questStart!= itr->second.questStart)
+ continue;
+ if(spellArea.auraSpell && itr->second.auraSpell && spellArea.auraSpell!= itr->second.auraSpell)
+ continue;
+ if(spellArea.raceMask && itr->second.raceMask && (spellArea.raceMask & itr->second.raceMask)==0)
+ continue;
+ if(spellArea.gender != GENDER_NONE && itr->second.gender != GENDER_NONE && 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(mask & ELIXIR_UNSTABLE_MASK)
+
+ }
+
+ 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))
{
- // in the Blade's Edge Mountains Plateaus and Gruul's Lair.
- return zone_id ==3522 || map_id==565;
+ sLog.outErrorDb("Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell,spellArea.questEnd);
+ continue;
}
- if(mask & ELIXIR_SHATTRATH_MASK)
+
+ if(spellArea.questEnd==spellArea.questStart && !spellArea.questStartCanActive)
{
- // in Tempest Keep, Serpentshrine Cavern, Caverns of Time: Mount Hyjal, Black Temple, Sunwell Plateau
- if(zone_id ==3607 || map_id==534 || map_id==564 || zone_id==4075)
- return true;
+ sLog.outErrorDb("Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell,spellArea.questEnd);
+ continue;
+ }
+ }
- MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
- if(!mapEntry)
- return false;
+ 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;
+ }
- return mapEntry->multimap_id==206;
+ if(spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_DUMMY && spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_PHASE)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy/phase aura in effect 0", spell,abs(spellArea.auraSpell));
+ continue;
}
- // elixirs not have another limitations
- return true;
+ if(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() );
+
+ delete result;
+
+ 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 (uint32 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;
}
- // special cases zone check (maps checked by multimap common id)
+ // 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 41618: // Bottled Nethergon Energy
- case 41620: // Bottled Nethergon Vapor
+ 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 false;
+ return SPELL_FAILED_INCORRECT_AREA;
+
+ return mapEntry->IsBattleGround() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
+ }
+ case 44521: // Preparation
+ {
+ if(!player)
+ return SPELL_FAILED_REQUIRES_AREA;
- return mapEntry->multimap_id==206;
+ 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 41617: // Cenarion Mana Salve
- case 41619: // Cenarion Healing Salve
+ 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 false;
+ 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;
- return mapEntry->multimap_id==207;
+ 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;
}
- case 40216: // Dragonmaw Illusion
- case 42016: // Dragonmaw Illusion
- return area_id == 3759 || area_id == 3966 || area_id == 3939;
}
- return true;
+ return SPELL_CAST_OK;
}
void SpellMgr::LoadSkillLineAbilityMap()
{
mSkillLineAbilityMap.clear();
+ barGoLink bar( sSkillLineAbilityStore.GetNumRows() );
uint32 count = 0;
- for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); i++)
+ for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i)
{
+ bar.step();
SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i);
if(!SkillInfo)
continue;
@@ -2622,7 +2551,7 @@ void SpellMgr::LoadSkillLineAbilityMap()
}
sLog.outString();
- sLog.outString(">> Loaded %u SkillLineAbility MultiMap", count);
+ sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count);
}
DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered)
@@ -2630,57 +2559,78 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto
// Explicit Diminishing Groups
switch(spellproto->SpellFamilyName)
{
- case SPELLFAMILY_MAGE:
- {
- // Polymorph
- if ((spellproto->SpellFamilyFlags & 0x00001000000LL) && spellproto->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE)
- return DIMINISHING_POLYMORPH;
- break;
- }
case SPELLFAMILY_ROGUE:
{
- // Kidney Shot
- if (spellproto->SpellFamilyFlags & 0x00000200000LL)
- return DIMINISHING_KIDNEYSHOT;
// Sap
- else if (spellproto->SpellFamilyFlags & 0x00000000080LL)
+ if (spellproto->SpellFamilyFlags[0] & 0x80)
return DIMINISHING_POLYMORPH;
// Gouge
- else if (spellproto->SpellFamilyFlags & 0x00000000008LL)
+ else if (spellproto->SpellFamilyFlags[0] & 0x8)
return DIMINISHING_POLYMORPH;
// Blind
- else if (spellproto->SpellFamilyFlags & 0x00001000000LL)
- return DIMINISHING_BLIND_CYCLONE;
+ else if (spellproto->SpellFamilyFlags[0] & 0x1000000)
+ return DIMINISHING_FEAR_BLIND;
+ // Cheap Shot
+ else if (spellproto->SpellFamilyFlags[0] & 0x400)
+ return DIMINISHING_CHEAPSHOT_POUNCE;
break;
}
case SPELLFAMILY_WARLOCK:
{
// Death Coil
- if (spellproto->SpellFamilyFlags & 0x00000080000LL)
+ if (spellproto->SpellFamilyFlags[0] & 0x80000)
return DIMINISHING_DEATHCOIL;
- // Seduction
- if (spellproto->SpellFamilyFlags & 0x00040000000LL)
- return DIMINISHING_FEAR;
- // Fear
- //else if (spellproto->SpellFamilyFlags & 0x40840000000LL)
- // return DIMINISHING_WARLOCK_FEAR;
// Curses/etc
- else if (spellproto->SpellFamilyFlags & 0x00080000000LL)
+ else if (spellproto->SpellFamilyFlags[0] & 0x80000000)
return DIMINISHING_LIMITONLY;
+ // Howl of Terror
+ else if (spellproto->SpellFamilyFlags[1] & 0x8)
+ return DIMINISHING_FEAR_BLIND;
break;
}
case SPELLFAMILY_DRUID:
{
- // Cyclone
- if (spellproto->SpellFamilyFlags & 0x02000000000LL)
- return DIMINISHING_BLIND_CYCLONE;
+ // Pounce
+ if (spellproto->SpellFamilyFlags[0] & 0x20000)
+ return DIMINISHING_CHEAPSHOT_POUNCE;
+
+ //Entangling Roots: to force natures grasp proc to be control root
+ else if (spellproto->SpellFamilyFlags[0] & 0x00000200)
+ return DIMINISHING_CONTROL_ROOT;
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ // Frostbite
+ if (spellproto->SpellFamilyFlags[1] & 0x80000000)
+ return DIMINISHING_TRIGGER_ROOT;
break;
}
case SPELLFAMILY_WARRIOR:
{
// Hamstring - limit duration to 10s in PvP
- if (spellproto->SpellFamilyFlags & 0x00000000002LL)
+ if (spellproto->SpellFamilyFlags[0] & 0x2)
return DIMINISHING_LIMITONLY;
+ // Intimidating Shout
+ else if (spellproto->SpellFamilyFlags[0] & 0x40000)
+ return DIMINISHING_FEAR_BLIND;
+ // Charge
+ else if (spellproto->SpellFamilyFlags[0] & 0x1)
+ return DIMINISHING_NONE;
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Repentance
+ if (spellproto->SpellFamilyFlags[0] & 0x4)
+ return DIMINISHING_POLYMORPH;
+ break;
+ }
+ case SPELLFAMILY_DEATHKNIGHT:
+ {
+ // Hungering Cold
+ if (spellproto->SpellIconID == 2797)
+ return DIMINISHING_POLYMORPH;
break;
}
default:
@@ -2688,31 +2638,26 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto
}
// Get by mechanic
- for (uint8 i=0;i<3;++i)
- {
- if (spellproto->Mechanic == MECHANIC_STUN || spellproto->EffectMechanic[i] == MECHANIC_STUN)
- return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN;
- else if (spellproto->Mechanic == MECHANIC_SLEEP || spellproto->EffectMechanic[i] == MECHANIC_SLEEP)
- return DIMINISHING_SLEEP;
- else if (spellproto->Mechanic == MECHANIC_ROOT || spellproto->EffectMechanic[i] == MECHANIC_ROOT)
- return triggered ? DIMINISHING_TRIGGER_ROOT : DIMINISHING_CONTROL_ROOT;
- else if (spellproto->Mechanic == MECHANIC_FEAR || spellproto->EffectMechanic[i] == MECHANIC_FEAR)
- return DIMINISHING_FEAR;
- else if (spellproto->Mechanic == MECHANIC_CHARM || spellproto->EffectMechanic[i] == MECHANIC_CHARM)
- return DIMINISHING_CHARM;
- else if (spellproto->Mechanic == MECHANIC_SILENCE || spellproto->EffectMechanic[i] == MECHANIC_SILENCE)
- return DIMINISHING_SILENCE;
- else if (spellproto->Mechanic == MECHANIC_DISARM || spellproto->EffectMechanic[i] == MECHANIC_DISARM)
- return DIMINISHING_DISARM;
- else if (spellproto->Mechanic == MECHANIC_FREEZE || spellproto->EffectMechanic[i] == MECHANIC_FREEZE)
- return DIMINISHING_FREEZE;
- else if (spellproto->Mechanic == MECHANIC_KNOCKOUT|| spellproto->EffectMechanic[i] == MECHANIC_KNOCKOUT ||
- spellproto->Mechanic == MECHANIC_SAPPED || spellproto->EffectMechanic[i] == MECHANIC_SAPPED)
- return DIMINISHING_KNOCKOUT;
- else if (spellproto->Mechanic == MECHANIC_BANISH || spellproto->EffectMechanic[i] == MECHANIC_BANISH)
- return DIMINISHING_BANISH;
+ uint32 mechanic = GetAllSpellMechanicMask(spellproto);
+ if (mechanic == MECHANIC_NONE) return DIMINISHING_NONE;
+ if (mechanic & (1<<MECHANIC_STUN)) return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN;
+ if (mechanic & ((1<<MECHANIC_SLEEP) | (1<<MECHANIC_FREEZE))) return DIMINISHING_SLEEP_FREEZE;
+ 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)) return DIMINISHING_FEAR;
+ 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_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;
}
@@ -2722,20 +2667,20 @@ bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group)
{
case DIMINISHING_CONTROL_STUN:
case DIMINISHING_TRIGGER_STUN:
- case DIMINISHING_KIDNEYSHOT:
- case DIMINISHING_SLEEP:
+ case DIMINISHING_SLEEP_FREEZE:
case DIMINISHING_CONTROL_ROOT:
case DIMINISHING_TRIGGER_ROOT:
case DIMINISHING_FEAR:
- case DIMINISHING_WARLOCK_FEAR:
+ case DIMINISHING_FEAR_BLIND:
case DIMINISHING_CHARM:
case DIMINISHING_POLYMORPH:
- case DIMINISHING_FREEZE:
case DIMINISHING_KNOCKOUT:
- case DIMINISHING_BLIND_CYCLONE:
case DIMINISHING_BANISH:
case DIMINISHING_LIMITONLY:
+ case DIMINISHING_CHEAPSHOT_POUNCE:
return true;
+ default:
+ return false;
}
return false;
}
@@ -2744,12 +2689,13 @@ DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group)
{
switch(group)
{
- case DIMINISHING_BLIND_CYCLONE:
+ case DIMINISHING_TAUNT:
case DIMINISHING_CONTROL_STUN:
case DIMINISHING_TRIGGER_STUN:
- case DIMINISHING_KIDNEYSHOT:
+ case DIMINISHING_CHEAPSHOT_POUNCE:
+ case DIMINISHING_FEAR_BLIND:
return DRTYPE_ALL;
- case DIMINISHING_SLEEP:
+ case DIMINISHING_BANISH:
case DIMINISHING_CONTROL_ROOT:
case DIMINISHING_TRIGGER_ROOT:
case DIMINISHING_FEAR:
@@ -2758,13 +2704,947 @@ DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group)
case DIMINISHING_SILENCE:
case DIMINISHING_DISARM:
case DIMINISHING_DEATHCOIL:
- case DIMINISHING_FREEZE:
- case DIMINISHING_BANISH:
- case DIMINISHING_WARLOCK_FEAR:
+ case DIMINISHING_SLEEP_FREEZE:
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)
+ return false;
+ if(auraSpell > 0)
+ // have expected aura
+ return player->HasAura(auraSpell);
+ else
+ // not have expected aura
+ return !player->HasAura(-auraSpell);
+ }
+
+ return true;
+}
+
+//-----------TRINITY-------------
+
+bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool sameCaster) const
+{
+ SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
+ SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
+
+ if(!spellInfo_1 || !spellInfo_2)
+ return false;
+
+ SpellSpecific spellId_spec_1 = GetSpellSpecific(spellId_1);
+ SpellSpecific spellId_spec_2 = GetSpellSpecific(spellId_2);
+ if (spellId_spec_1 && spellId_spec_2)
+ if (IsSingleFromSpellSpecificPerTarget(spellId_spec_1, spellId_spec_2)
+ ||(IsSingleFromSpellSpecificPerCaster(spellId_spec_1, spellId_spec_2) && sameCaster))
+ return true;
+
+ if(spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName)
+ return false;
+
+ if(!sameCaster)
+ {
+ for(uint32 i = 0; i < 3; ++i)
+ if (spellInfo_1->Effect[i] == SPELL_EFFECT_APPLY_AURA
+ || spellInfo_1->Effect[i] == SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ // not area auras (shaman totem)
+ 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_ENERGY:
+ case SPELL_AURA_OBS_MOD_HEALTH:
+ return false;
+ default:
+ break;
+ }
+ }
+
+ spellId_2 = GetLastSpellInChain(spellId_2);
+ spellId_1 = GetLastSpellInChain(spellId_1);
+
+ // Hack for Incanter's Absorption
+ if (spellId_1 == spellId_2 && spellId_1 == 44413)
+ return false;
+
+ if (spellId_1 == spellId_2)
+ return true;
+
+ // generic spells
+ if(!spellInfo_1->SpellFamilyName)
+ {
+ if(!spellInfo_1->SpellIconID
+ || spellInfo_1->SpellIconID == 1
+ || spellInfo_1->SpellIconID != spellInfo_2->SpellIconID)
+ return false;
+ }
+ // check for class spells
+ else
+ {
+ if (spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags)
+ return false;
+ if (!spellInfo_1->SpellFamilyFlags)
+ return false;
+ }
+
+ //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 have exactly the same effect they cannot stack
+ for(uint32 i = 0; i < 3; ++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 false; // need itemtype check? need an example to add that check
+
+ return true;
+}
+
+bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool def)
+{
+ if (!dispelSpell) return false;
+ SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
+ if (!spellproto) return false;
+
+ if (spellproto->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)
+ 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 *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() );
+
+ delete result;
+
+ 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 *result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required");
+
+ if(result == NULL)
+ {
+ 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();
+
+ mSpellsReqSpell.insert (std::pair<uint32, uint32>(spell_req, spell_id));
+ mSpellReq[spell_id] = spell_req;
+ ++rows;
+ } while( result->NextRow() );
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell required records", rows );
+}
+
+struct SpellRankEntry
+{
+ uint32 SkillId;
+ char const *SpellName;
+ uint32 DurationIndex;
+ uint32 RangeIndex;
+ uint32 SpellVisual;
+ uint32 ProcFlags;
+ flag96 SpellFamilyFlags;
+ uint32 TargetAuraState;
+ uint32 ManaCost;
+ uint32 CastingTimeIndex;
+ flag96 Effect;
+ flag96 Aura;
+ uint16 TalentID;
+
+ bool operator < (const SpellRankEntry & _Right) const
+ {
+ return (SkillId != _Right.SkillId ? SkillId < _Right.SkillId
+ : SpellName!=_Right.SpellName ? SpellName < _Right.SpellName
+ : ProcFlags!=_Right.ProcFlags ? ProcFlags < _Right.ProcFlags
+
+ : Effect!=_Right.Effect ? Effect < _Right.Effect
+ : Aura!=_Right.Aura ? Aura < _Right.Aura
+ : TalentID!=_Right.TalentID ? TalentID < _Right.TalentID
+ : (CastingTimeIndex!=_Right.CastingTimeIndex) && (!CastingTimeIndex || !_Right.CastingTimeIndex || CastingTimeIndex==1 || !_Right.CastingTimeIndex==1) ? CastingTimeIndex < _Right.CastingTimeIndex
+
+ : SpellFamilyFlags!=_Right.SpellFamilyFlags ? SpellFamilyFlags < _Right.SpellFamilyFlags
+ : (SpellVisual!=_Right.SpellVisual) && (!SpellVisual || !_Right.SpellVisual) ? SpellVisual < _Right.SpellVisual
+ : (ManaCost!=_Right.ManaCost) && (!ManaCost || !_Right.ManaCost) ? ManaCost < _Right.ManaCost
+ : (DurationIndex!=_Right.DurationIndex) && (!DurationIndex || !_Right.DurationIndex)? DurationIndex < _Right.DurationIndex
+ : (RangeIndex!=_Right.RangeIndex) && (!RangeIndex || !_Right.RangeIndex || RangeIndex==1 || !_Right.RangeIndex==1) ? RangeIndex < _Right.RangeIndex
+ : TargetAuraState < _Right.TargetAuraState
+ );
+ }
+};
+
+struct SpellRankValue
+{
+ uint32 Id;
+ char const *Rank;
+ bool strict;
+};
+
+void SpellMgr::LoadSpellChains()
+{
+ mSpellChains.clear(); // need for reload case
+
+ std::vector<uint32> ChainedSpells;
+ for (uint32 ability_id=0;ability_id<sSkillLineAbilityStore.GetNumRows();ability_id++)
+ {
+ SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id);
+ if (!AbilityInfo)
+ continue;
+ if (!AbilityInfo->forward_spellid)
+ continue;
+ ChainedSpells.push_back(AbilityInfo->forward_spellid);
+ }
+
+ std::multimap<SpellRankEntry, SpellRankValue> RankMap;
+
+ for (uint32 ability_id=0;ability_id<sSkillLineAbilityStore.GetNumRows();ability_id++)
+ {
+ SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id);
+ if (!AbilityInfo)
+ continue;
+
+ //get only spell with lowest ability_id to prevent doubles
+ uint32 spell_id=AbilityInfo->spellId;
+ bool found=false;
+ for (uint32 i=0; i<ChainedSpells.size(); i++)
+ {
+ if (ChainedSpells.at(i)==spell_id)
+ found=true;
+ }
+ if (found)
+ continue;
+
+ if(mSkillLineAbilityMap.lower_bound(spell_id)->second->id!=ability_id)
+ continue;
+ SpellEntry const *SpellInfo=sSpellStore.LookupEntry(spell_id);
+ if (!SpellInfo)
+ continue;
+ std::string sRank = SpellInfo->Rank[sWorld.GetDefaultDbcLocale()];
+ if(sRank.empty())
+ continue;
+ //exception to polymorph spells-make pig and turtle other chain than sheep
+ if ((SpellInfo->SpellFamilyName==SPELLFAMILY_MAGE) && (SpellInfo->SpellFamilyFlags[0] & 0x1000000) && (SpellInfo->SpellIconID!=82))
+ continue;
+
+ SpellRankEntry entry;
+ SpellRankValue value;
+ entry.SkillId=AbilityInfo->skillId;
+ entry.SpellName=SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()];
+ entry.DurationIndex=SpellInfo->DurationIndex;
+ entry.RangeIndex=SpellInfo->rangeIndex;
+ entry.ProcFlags=SpellInfo->procFlags;
+ entry.SpellFamilyFlags=SpellInfo->SpellFamilyFlags;
+ entry.TargetAuraState=SpellInfo->TargetAuraState;
+ entry.SpellVisual=SpellInfo->SpellVisual[0];
+ entry.ManaCost=SpellInfo->manaCost;
+ entry.CastingTimeIndex=0;
+ entry.TalentID=0;
+ for (;;)
+ {
+ AbilityInfo=mSkillLineAbilityMap.lower_bound(spell_id)->second;
+ value.Id=spell_id;
+ value.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()];
+ value.strict=false;
+ RankMap.insert(std::pair<SpellRankEntry, SpellRankValue>(entry,value));
+ spell_id=AbilityInfo->forward_spellid;
+ SpellInfo=sSpellStore.LookupEntry(spell_id);
+ if (!SpellInfo)
+ break;
+ }
+ }
+
+ barGoLink bar(RankMap.size());
+
+ uint32 count=0;
+
+ for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr = RankMap.begin();itr!=RankMap.end();)
+ {
+ SpellRankEntry entry=itr->first;
+ //trac errors in extracted data
+ std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator> RankErrorMap;
+ for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++)
+ {
+ bar.step();
+ RankErrorMap.insert(std::pair<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>(itr2->second.Rank,itr2));
+ }
+
+ bool error=false;
+ //if strict == true strict check is not needed
+ if (!itr->second.strict)
+ //check for rank duplicates, if there are any do strict check
+ for (std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();)
+ {
+ char const * err_entry=itr2->first;
+ uint32 rank_count=RankErrorMap.count(itr2->first);
+ if (rank_count>1)
+ {
+ error=true;
+ break;
+ }
+ else
+ itr2++;
+ }
+ bool allHaveTalents=true;
+ if (error)
+ {
+ std::list<uint32> ConflictedSpells;
+ for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2=RankMap.lower_bound(entry))
+ {
+ ConflictedSpells.push_back(itr2->second.Id);
+ if (!GetTalentSpellPos(itr2->second.Id))
+ allHaveTalents=false;
+ RankMap.erase(itr2);
+ }
+ SpellRankEntry nextEntry, currEntry;
+ for (;!ConflictedSpells.empty();ConflictedSpells.pop_front())
+ {
+ SpellEntry const *SpellInfo=sSpellStore.LookupEntry(ConflictedSpells.front());
+ currEntry.SkillId=entry.SkillId;
+ currEntry.SpellName=SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()];
+ currEntry.DurationIndex=SpellInfo->DurationIndex;
+ currEntry.RangeIndex=SpellInfo->rangeIndex;
+ currEntry.ProcFlags=SpellInfo->procFlags;
+ currEntry.SpellFamilyFlags=SpellInfo->SpellFamilyFlags;
+ //compare talents only when all spells from chain have entry
+ //to prevent wrong results with spells which have first rank talented and other not
+ if (allHaveTalents)
+ currEntry.TalentID=GetTalentSpellPos(ConflictedSpells.front())->talent_id;
+ else
+ currEntry.TalentID=0;
+ currEntry.TargetAuraState=SpellInfo->TargetAuraState;
+ currEntry.SpellVisual=SpellInfo->SpellVisual[0];
+ currEntry.ManaCost=SpellInfo->manaCost;
+
+ //compare effects and casting time
+ currEntry.CastingTimeIndex=SpellInfo->CastingTimeIndex;
+ currEntry.Effect[0]=SpellInfo->Effect[0];
+ currEntry.Effect[1]=SpellInfo->Effect[1];
+ currEntry.Effect[2]=SpellInfo->Effect[2];
+
+ currEntry.Aura[0]=SpellInfo->EffectApplyAuraName[0];
+ currEntry.Aura[1]=SpellInfo->EffectApplyAuraName[1];
+ currEntry.Aura[2]=SpellInfo->EffectApplyAuraName[2];
+
+ SpellRankValue currValue;
+ currValue.Id=ConflictedSpells.front();
+ currValue.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()];
+ currValue.strict=true;
+ RankMap.insert(std::pair<SpellRankEntry, SpellRankValue>(currEntry,currValue));
+ }
+ itr=RankMap.begin();
+ continue;
+ }
+ else
+ for (std::multimap<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();)
+ {
+ char const * err_entry=itr2->first;
+ uint32 rank_count=RankErrorMap.count(itr2->first);
+ if (rank_count>1)
+ for (itr2 = RankErrorMap.lower_bound(err_entry);itr2!=RankErrorMap.upper_bound(err_entry);itr2++)
+ {
+ sLog.outDebug("There is a duplicate rank entry (%s) for spell: %u",itr2->first,itr2->second->second.Id);
+ if (!(itr2->second->second.Id==47541 || itr2->second->second.Id==45902 || itr2->second->second.Id==7620))
+ {
+ sLog.outDebug("Spell %u removed from chain data.",itr2->second->second.Id);
+ RankMap.erase(itr2->second);
+ }
+ }
+ else
+ itr2++;
+ }
+
+ //order spells by spellLevel
+ std::list<uint32> RankedSpells;
+ uint32 min_spell_lvl=0;
+ std::multimap<SpellRankEntry, SpellRankValue>::iterator min_itr;
+ for (;RankMap.count(entry);)
+ {
+ for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++)
+ {
+ SpellEntry const *SpellInfo=sSpellStore.LookupEntry(itr2->second.Id);
+ if (SpellInfo->spellLevel<min_spell_lvl || itr2==RankMap.lower_bound(entry))
+ {
+ min_spell_lvl=SpellInfo->spellLevel;
+ min_itr=itr2;
+ }
+ }
+ RankedSpells.push_back(min_itr->second.Id);
+ RankMap.erase(min_itr);
+ }
+
+ //use data from talent.dbc
+ uint16 talent_id=0;
+ for(std::list<uint32>::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();)
+ {
+ if (TalentSpellPos const* TalentPos=GetTalentSpellPos(*itr2))
+ {
+ talent_id=TalentPos->talent_id;
+ RankedSpells.erase(itr2);
+ itr2 = RankedSpells.begin();
+ }
+ else
+ itr2++;
+ }
+ if (talent_id)
+ {
+ TalentEntry const *TalentInfo = sTalentStore.LookupEntry(talent_id);
+ for (uint8 rank=5;rank;rank--)
+ {
+ if (TalentInfo->RankID[rank-1])
+ RankedSpells.push_front(TalentInfo->RankID[rank-1]);
+ }
+ }
+
+ //do not proceed for spells with less than 2 ranks
+ itr=RankMap.begin();
+ if (RankedSpells.size()<2)
+ continue;
+
+ count++;
+
+ uint32 spell_rank=1;
+ for(std::list<uint32>::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();spell_rank++)
+ {
+ uint32 spell_id=*itr2;
+ mSpellChains[spell_id].rank=spell_rank;
+ mSpellChains[spell_id].first=RankedSpells.front();
+ mSpellChains[spell_id].last=RankedSpells.back();
+
+ itr2++;
+ if (spell_rank<2)
+ mSpellChains[spell_id].prev=0;
+
+ if (spell_id==RankedSpells.back())
+ mSpellChains[spell_id].next=0;
+ else
+ {
+ mSpellChains[*itr2].prev=spell_id;
+ mSpellChains[spell_id].next=*itr2;
+ }
+ }
+ }
+
+ //uncomment these two lines to print yourself list of spell_chains on startup
+ //for (UNORDERED_MAP<uint32, SpellChainNode>::iterator itr=mSpellChains.begin();itr!=mSpellChains.end();itr++)
+ //sLog.outString( "Id: %u, Rank: %d , %s, %u, %u, %u, %u",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()], itr->second.first, itr->second.last,itr->second.next ,itr->second.prev);
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u spell chains",count);
+}
+
+// set data in core for now
+void SpellMgr::LoadSpellCustomAttr()
+{
+ mSpellCustomAttr.resize(GetSpellStore()->GetNumRows());
+
+ SpellEntry *spellInfo;
+ for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
+ {
+ mSpellCustomAttr[i] = 0;
+ spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i);
+ if(!spellInfo)
+ continue;
+
+ bool auraSpell = true;
+ for(uint32 j = 0; j < 3; ++j)
+ {
+ if(spellInfo->Effect[j])
+ if(spellInfo->Effect[j] != SPELL_EFFECT_APPLY_AURA
+ || SpellTargetType[spellInfo->EffectImplicitTargetA[j]] != TARGET_TYPE_UNIT_TARGET)
+ //ignore target party for now
+ {
+ auraSpell = false;
+ break;
+ }
+ }
+ if(auraSpell)
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_SPELL;
+
+ for(uint32 j = 0; j < 3; ++j)
+ {
+ switch(spellInfo->EffectApplyAuraName[j])
+ {
+ case SPELL_AURA_PERIODIC_DAMAGE:
+ case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
+ case SPELL_AURA_PERIODIC_LEECH:
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_DOT;
+ break;
+ case SPELL_AURA_PERIODIC_HEAL:
+ case SPELL_AURA_OBS_MOD_HEALTH:
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_HOT;
+ break;
+ case SPELL_AURA_MOD_ROOT:
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_CC;
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_MOVEMENT_IMPAIR;
+ break;
+ case SPELL_AURA_MOD_DECREASE_SPEED:
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_MOVEMENT_IMPAIR;
+ break;
+ default:
+ break;
+ }
+
+ 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;
+ break;
+ case SPELL_EFFECT_CHARGE:
+ case SPELL_EFFECT_JUMP:
+ case SPELL_EFFECT_JUMP2:
+ case SPELL_EFFECT_138:
+ if(!spellInfo->speed && !spellInfo->SpellFamilyName)
+ spellInfo->speed = SPEED_CHARGE;
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_CHARGE;
+ break;
+ case SPELL_EFFECT_TRIGGER_SPELL:
+ if (SpellTargetType[spellInfo->EffectImplicitTargetA[j]]== TARGET_TYPE_DEST_CASTER ||
+ SpellTargetType[spellInfo->EffectImplicitTargetA[j]]== TARGET_TYPE_DEST_TARGET ||
+ SpellTargetType[spellInfo->EffectImplicitTargetA[j]]== TARGET_TYPE_DEST_DEST ||
+ spellInfo->Targets & (TARGET_FLAG_SOURCE_LOCATION|TARGET_FLAG_DEST_LOCATION))
+ spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE;
+ break;
+ }
+
+ switch(SpellTargetType[spellInfo->EffectImplicitTargetA[j]])
+ {
+ case TARGET_TYPE_UNIT_TARGET:
+ case TARGET_TYPE_DEST_TARGET:
+ spellInfo->Targets |= TARGET_FLAG_UNIT;
+ break;
+ //case TARGET_TYPE_AREA_DST:
+ //case TARGET_TYPE_DEST_DEST:
+ // spellInfo->Targets |= TARGET_FLAG_DEST_LOCATION;
+ // break;
+ }
+ }
+
+ for(uint32 j = 0; j < 3; ++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;
+ mSpellCustomAttr[i] &= ~SPELL_ATTR_CU_MOVEMENT_IMPAIR;
+ break;
+ }
+ }
+
+ if(spellInfo->SpellVisual[0] == 3879)
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_BACK;
+
+ switch(i)
+ {
+ // Heart of the Crusader
+ case 20335:
+ case 20336:
+ case 20337:
+ // Entries were not updated after spell effect change, we have to do that manually:/
+ spellInfo->AttributesEx3 |= SPELL_ATTR_EX3_CAN_PROC_TRIGGERED;
+ 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;
+ break;
+ case 26029: // dark glare
+ case 37433: // spout
+ case 43140: case 43215: // flame breath
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_LINE;
+ 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
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_SHARE_DAMAGE;
+ 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;
+ break;
+ case 27820: // Mana Detonation
+ //case 28062: case 39090: // Positive/Negative Charge
+ //case 28085: case 39093:
+ mSpellCustomAttr[i] |= SPELL_ATTR_CU_EXCLUDE_SELF;
+ 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
+ case 52479: // Gift of the Harvester
+ spellInfo->MaxAffectedTargets = 1;
+ 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;
+ break;
+ case 38310: //Multi-Shot
+ spellInfo->MaxAffectedTargets = 4;
+ 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;
+ 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;
+ break;
+ case 8122: case 8124: case 10888: case 10890: // Psychic Scream
+ case 12494: // Frostbite
+ spellInfo->Attributes |= SPELL_ATTR_BREAKABLE_BY_DAMAGE;
+ break;
+ case 38794: case 33711: //Murmur's Touch
+ spellInfo->MaxAffectedTargets = 1;
+ spellInfo->EffectTriggerSpell[0] = 33760;
+ 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
+ spellInfo->procCharges=1;
+ break;
+ case 44544: // Fingers of Frost
+ spellInfo->procCharges=2;
+ break;
+ case 28200: // Ascendance (Talisman of Ascendance trinket)
+ spellInfo->procCharges=6;
+ break;
+ case 51852: // The Eye of Acherus (no spawn in phase 2 in db)
+ spellInfo->EffectMiscValue[0] |= 1;
+ break;
+ case 51904: // Summon Ghouls On Scarlet Crusade (core does not know the triggered spell is summon spell)
+ spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER;
+ break;
+ 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;
+ 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;
+ 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;
+ break;
+ }
+ }
+
+ SummonPropertiesEntry *properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(121));
+ properties->Type = SUMMON_TYPE_TOTEM;
+
+ CreatureAI::FillAISpellInfo();
+}
+
+// Fill custom data about enchancments
+void SpellMgr::LoadEnchantCustomAttr()
+{
+ uint32 size = sSpellItemEnchantmentStore.GetNumRows();
+ mEnchantCustomAttr.resize(size);
+
+ for (uint32 i = 0;i<size; ++i)
+ mEnchantCustomAttr[i] = 0;
+
+ for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
+ {
+ SpellEntry * spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i);
+ if(!spellInfo)
+ continue;
+
+ // TODO: find a better check
+ if (!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_UNK13) || !(spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT))
+ continue;
+
+ for(uint32 j = 0; j < 3; ++j)
+ {
+ if(spellInfo->Effect[j] == SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY)
+ {
+ uint32 enchId = spellInfo->EffectMiscValue[j];
+ SpellItemEnchantmentEntry const *ench = sSpellItemEnchantmentStore.LookupEntry(enchId);
+ if (!enchId)
+ continue;
+ mEnchantCustomAttr[enchId] = true;
+ break;
+ }
+ }
+ }
+}
+
+bool SpellMgr::IsSkillTypeSpell(uint32 spellId, SkillType type) const
+{
+ SkillLineAbilityMap::const_iterator lower = GetBeginSkillLineAbilityMap(spellId);
+ SkillLineAbilityMap::const_iterator upper = GetEndSkillLineAbilityMap(spellId);
+ for (;lower!=upper;++lower)
+ {
+ if (lower->second->skillId==type)
+ return true;
+ }
+ return false;
+}
+
+void SpellMgr::LoadSpellLinked()
+{
+ mSpellLinkedMap.clear(); // need for reload case
+ uint32 count = 0;
+
+ // 0 1 2
+ QueryResult *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() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u linked spells", count );
+}
+
+bool SpellMgr::CheckDB() const
+{
+ SpellScriptTarget::const_iterator lower = GetBeginSpellScriptTarget(30531);
+ SpellScriptTarget::const_iterator upper = GetEndSpellScriptTarget(30531);
+ if(lower == upper || lower->second.targetEntry != 17256)
+ return false;
+
+ return true;
+}