server/src/game/SpellMgr.cpp
VladimirMangos d04dd4e5bf [9876] Fixed TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER work
TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER expected seelction friendly targets
for spell caster around spell caster (so ignore original caster faction).
This meaning that for begative spell also selected friendly targets for
spell caster object.
2010-05-12 03:33:06 +04:00

3610 lines
135 KiB
C++

/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "SpellMgr.h"
#include "ObjectMgr.h"
#include "SpellAuraDefines.h"
#include "ProgressBar.h"
#include "DBCStores.h"
#include "World.h"
#include "Chat.h"
#include "Spell.h"
#include "BattleGroundMgr.h"
#include "MapManager.h"
SpellMgr::SpellMgr()
{
}
SpellMgr::~SpellMgr()
{
}
SpellMgr& SpellMgr::Instance()
{
static SpellMgr spellMgr;
return spellMgr;
}
int32 GetSpellDuration(SpellEntry const *spellInfo)
{
if(!spellInfo)
return 0;
SpellDurationEntry const *du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex);
if(!du)
return 0;
return (du->Duration[0] == -1) ? -1 : abs(du->Duration[0]);
}
int32 GetSpellMaxDuration(SpellEntry const *spellInfo)
{
if(!spellInfo)
return 0;
SpellDurationEntry const *du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex);
if(!du)
return 0;
return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]);
}
uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell)
{
// some triggered spells have data only usable for client
if (spell && spell->IsTriggeredSpellWithRedundentData())
return 0;
SpellCastTimesEntry const *spellCastTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex);
// not all spells have cast time index and this is all is pasiive abilities
if (!spellCastTimeEntry)
return 0;
int32 castTime = spellCastTimeEntry->CastTime;
if (spell)
{
if (Player* modOwner = spell->GetCaster()->GetSpellModOwner())
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell);
if (!(spellInfo->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_TRADESPELL)))
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()))
castTime += 500;
return (castTime > 0) ? uint32(castTime) : 0;
}
uint16 GetSpellAuraMaxTicks(SpellEntry const* spellInfo)
{
int32 DotDuration = GetSpellDuration(spellInfo);
if(DotDuration == 0)
return 1;
// 200% limit
if(DotDuration > 30000)
DotDuration = 30000;
for (int j = 0; j < MAX_EFFECT_INDEX; ++j)
{
if (spellInfo->Effect[j] == SPELL_EFFECT_APPLY_AURA && (
spellInfo->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_DAMAGE ||
spellInfo->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_HEAL ||
spellInfo->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) )
{
if (spellInfo->EffectAmplitude[j] != 0)
return DotDuration / spellInfo->EffectAmplitude[j];
break;
}
}
return 6;
}
WeaponAttackType GetWeaponAttackType(SpellEntry const *spellInfo)
{
if(!spellInfo)
return BASE_ATTACK;
switch (spellInfo->DmgClass)
{
case SPELL_DAMAGE_CLASS_MELEE:
if (spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND)
return OFF_ATTACK;
else
return BASE_ATTACK;
break;
case SPELL_DAMAGE_CLASS_RANGED:
return RANGED_ATTACK;
break;
default:
// Wands
if (spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG)
return RANGED_ATTACK;
else
return BASE_ATTACK;
break;
}
}
bool IsPassiveSpell(uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if (!spellInfo)
return false;
return (spellInfo->Attributes & SPELL_ATTR_PASSIVE) != 0;
}
bool IsNoStackAuraDueToAura(uint32 spellId_1, SpellEffectIndex effIndex_1, uint32 spellId_2, SpellEffectIndex effIndex_2)
{
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])
return false;
return true;
}
int32 CompareAuraRanks(uint32 spellId_1, SpellEffectIndex effIndex_1, uint32 spellId_2, SpellEffectIndex effIndex_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->CalculateSimpleValue(effIndex_1) < 0 && spellInfo_2->CalculateSimpleValue(effIndex_2) < 0)
return -diff;
else return diff;
}
SpellSpecific GetSpellSpecific(uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo)
return SPELL_NORMAL;
switch(spellInfo->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
{
// Food / Drinks (mostly)
if(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED)
{
bool food = false;
bool drink = false;
for(int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
switch(spellInfo->EffectApplyAuraName[i])
{
// Food
case SPELL_AURA_MOD_REGEN:
case SPELL_AURA_OBS_MOD_HEALTH:
food = true;
break;
// Drink
case SPELL_AURA_MOD_POWER_REGEN:
case SPELL_AURA_OBS_MOD_MANA:
drink = true;
break;
default:
break;
}
}
if(food && drink)
return SPELL_FOOD_AND_DRINK;
else if(food)
return SPELL_FOOD;
else if(drink)
return SPELL_DRINK;
}
else
{
// Well Fed buffs (must be exclusive with Food / Drink replenishment effects, or else Well Fed will cause them to be removed)
// SpellIcon 2560 is Spell 46687, does not have this flag
if ((spellInfo->AttributesEx2 & SPELL_ATTR_EX2_FOOD_BUFF) || spellInfo->SpellIconID == 2560)
return SPELL_WELL_FED;
}
break;
}
case SPELLFAMILY_MAGE:
{
// family flags 18(Molten), 25(Frost/Ice), 28(Mage)
if (spellInfo->SpellFamilyFlags & UI64LIT(0x12040000))
return SPELL_MAGE_ARMOR;
if ((spellInfo->SpellFamilyFlags & UI64LIT(0x1000000)) && spellInfo->EffectApplyAuraName[EFFECT_INDEX_0] == SPELL_AURA_MOD_CONFUSE)
return SPELL_MAGE_POLYMORPH;
break;
}
case SPELLFAMILY_WARRIOR:
{
if (spellInfo->SpellFamilyFlags & UI64LIT(0x00008000010000))
return SPELL_POSITIVE_SHOUT;
break;
}
case SPELLFAMILY_WARLOCK:
{
// only warlock curses have this
if (spellInfo->Dispel == DISPEL_CURSE)
return SPELL_CURSE;
// Warlock (Demon Armor | Demon Skin | Fel Armor)
if (spellInfo->SpellFamilyFlags & UI64LIT(0x2000002000000000) || spellInfo->SpellFamilyFlags2 & 0x00000010)
return SPELL_WARLOCK_ARMOR;
break;
}
case SPELLFAMILY_PRIEST:
{
// "Well Fed" buff from Blessed Sunfruit, Blessed Sunfruit Juice, Alterac Spring Water
if ((spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_SITTING) &&
(spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK) &&
(spellInfo->SpellIconID == 52 || spellInfo->SpellIconID == 79))
return SPELL_WELL_FED;
break;
}
case SPELLFAMILY_HUNTER:
{
// only hunter stings have this
if (spellInfo->Dispel == DISPEL_POISON)
return SPELL_STING;
// only hunter aspects have this (but not all aspects in hunter family)
if( spellInfo->SpellFamilyFlags & UI64LIT(0x0044000000380000) || spellInfo->SpellFamilyFlags2 & 0x00001010)
return SPELL_ASPECT;
break;
}
case SPELLFAMILY_PALADIN:
{
if (IsSealSpell(spellInfo))
return SPELL_SEAL;
if (spellInfo->SpellFamilyFlags & UI64LIT(0x0000000011010002))
return SPELL_BLESSING;
if (spellInfo->SpellFamilyFlags & UI64LIT(0x0000000000002190))
return SPELL_HAND;
// skip Heart of the Crusader that have also same spell family mask
if ((spellInfo->SpellFamilyFlags & UI64LIT(0x00000820180400)) && (spellInfo->AttributesEx3 & 0x200) && (spellInfo->SpellIconID != 237))
return SPELL_JUDGEMENT;
// only paladin auras have this (for palaldin class family)
if( spellInfo->SpellFamilyFlags2 & 0x00000020 )
return SPELL_AURA;
break;
}
case SPELLFAMILY_SHAMAN:
{
if (IsElementalShield(spellInfo))
return SPELL_ELEMENTAL_SHIELD;
break;
}
case SPELLFAMILY_POTION:
return sSpellMgr.GetSpellElixirSpecific(spellInfo->Id);
case SPELLFAMILY_DEATHKNIGHT:
if (spellInfo->Category == 47)
return SPELL_PRESENCE;
break;
}
// Tracking spells
if(IsSpellHaveAura(spellInfo, SPELL_AURA_TRACK_CREATURES) || IsSpellHaveAura(spellInfo, SPELL_AURA_TRACK_RESOURCES))
return SPELL_TRACKER;
// elixirs can have different families, but potion most ofc.
if(SpellSpecific sp = sSpellMgr.GetSpellElixirSpecific(spellInfo->Id))
return sp;
return SPELL_NORMAL;
}
// target not allow have more one spell specific from same caster
bool IsSingleFromSpellSpecificPerTargetPerCaster(SpellSpecific spellSpec1,SpellSpecific spellSpec2)
{
switch(spellSpec1)
{
case SPELL_BLESSING:
case SPELL_AURA:
case SPELL_STING:
case SPELL_CURSE:
case SPELL_ASPECT:
case SPELL_POSITIVE_SHOUT:
case SPELL_JUDGEMENT:
case SPELL_HAND:
return spellSpec1==spellSpec2;
default:
return false;
}
}
// target not allow have more one ranks from spell from spell specific per target
bool IsSingleFromSpellSpecificSpellRanksPerTarget(SpellSpecific spellSpec1,SpellSpecific spellSpec2)
{
switch(spellSpec1)
{
case SPELL_BLESSING:
case SPELL_AURA:
case SPELL_CURSE:
case SPELL_ASPECT:
case SPELL_HAND:
return spellSpec1==spellSpec2;
default:
return false;
}
}
// target not allow have more one spell specific per target from any caster
bool IsSingleFromSpellSpecificPerTarget(SpellSpecific spellSpec1,SpellSpecific spellSpec2)
{
switch(spellSpec1)
{
case SPELL_SEAL:
case SPELL_TRACKER:
case SPELL_WARLOCK_ARMOR:
case SPELL_MAGE_ARMOR:
case SPELL_ELEMENTAL_SHIELD:
case SPELL_MAGE_POLYMORPH:
case SPELL_PRESENCE:
case SPELL_WELL_FED:
return spellSpec1==spellSpec2;
case SPELL_BATTLE_ELIXIR:
return spellSpec2==SPELL_BATTLE_ELIXIR
|| spellSpec2==SPELL_FLASK_ELIXIR;
case SPELL_GUARDIAN_ELIXIR:
return spellSpec2==SPELL_GUARDIAN_ELIXIR
|| spellSpec2==SPELL_FLASK_ELIXIR;
case SPELL_FLASK_ELIXIR:
return spellSpec2==SPELL_BATTLE_ELIXIR
|| spellSpec2==SPELL_GUARDIAN_ELIXIR
|| spellSpec2==SPELL_FLASK_ELIXIR;
case SPELL_FOOD:
return spellSpec2==SPELL_FOOD
|| spellSpec2==SPELL_FOOD_AND_DRINK;
case SPELL_DRINK:
return spellSpec2==SPELL_DRINK
|| spellSpec2==SPELL_FOOD_AND_DRINK;
case SPELL_FOOD_AND_DRINK:
return spellSpec2==SPELL_FOOD
|| spellSpec2==SPELL_DRINK
|| spellSpec2==SPELL_FOOD_AND_DRINK;
default:
return false;
}
}
bool IsPositiveTarget(uint32 targetA, uint32 targetB)
{
switch(targetA)
{
// non-positive targets
case TARGET_CHAIN_DAMAGE:
case TARGET_ALL_ENEMY_IN_AREA:
case TARGET_ALL_ENEMY_IN_AREA_INSTANT:
case TARGET_IN_FRONT_OF_CASTER:
case TARGET_ALL_ENEMY_IN_AREA_CHANNELED:
case TARGET_CURRENT_ENEMY_COORDINATES:
case TARGET_SINGLE_ENEMY:
case TARGET_IN_FRONT_OF_CASTER_30:
return false;
// positive or dependent
case TARGET_CASTER_COORDINATES:
return (targetB == TARGET_ALL_PARTY || targetB == TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER);
default:
break;
}
if (targetB)
return IsPositiveTarget(targetB, 0);
return true;
}
bool IsExplicitPositiveTarget(uint32 targetA)
{
// positive targets that in target selection code expect target in m_targers, so not that auto-select target by spell data by m_caster and etc
switch(targetA)
{
case TARGET_SINGLE_FRIEND:
case TARGET_SINGLE_PARTY:
case TARGET_CHAIN_HEAL:
case TARGET_SINGLE_FRIEND_2:
case TARGET_AREAEFFECT_PARTY_AND_CLASS:
return true;
default:
break;
}
return false;
}
bool IsExplicitNegativeTarget(uint32 targetA)
{
// non-positive targets that in target selection code expect target in m_targers, so not that auto-select target by spell data by m_caster and etc
switch(targetA)
{
case TARGET_CHAIN_DAMAGE:
case TARGET_CURRENT_ENEMY_COORDINATES:
case TARGET_SINGLE_ENEMY:
return true;
default:
break;
}
return false;
}
bool IsPositiveEffect(uint32 spellId, SpellEffectIndex effIndex)
{
SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
if (!spellproto) return false;
switch(spellproto->Effect[effIndex])
{
case SPELL_EFFECT_DUMMY:
// some explicitly required dummy effect sets
switch(spellId)
{
case 28441: // AB Effect 000
return false;
default:
break;
}
break;
// always positive effects (check before target checks that provided non-positive result in some case for positive effects)
case SPELL_EFFECT_HEAL:
case SPELL_EFFECT_LEARN_SPELL:
case SPELL_EFFECT_SKILL_STEP:
case SPELL_EFFECT_HEAL_PCT:
case SPELL_EFFECT_ENERGIZE_PCT:
return true;
// non-positive aura use
case SPELL_EFFECT_APPLY_AURA:
case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
{
switch(spellproto->EffectApplyAuraName[effIndex])
{
case SPELL_AURA_DUMMY:
{
// dummy aura can be positive or negative dependent from casted spell
switch(spellproto->Id)
{
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)
case 11196: // Recently Bandaged
case 44689: // Relay Race Accept Hidden Debuff - DND
case 58600: // Restricted Flight Area
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;
}
} break;
case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from base point sign (negative -> negative)
case SPELL_AURA_MOD_STAT:
case SPELL_AURA_MOD_SKILL:
case SPELL_AURA_MOD_HEALING_PCT:
case SPELL_AURA_MOD_HEALING_DONE:
if(spellproto->CalculateSimpleValue(effIndex) < 0)
return false;
break;
case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from bas point sign (positive -> negative)
if(spellproto->CalculateSimpleValue(effIndex) > 0)
return false;
break;
case SPELL_AURA_MOD_SPELL_CRIT_CHANCE:
case SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT:
case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
if(spellproto->CalculateSimpleValue(effIndex) > 0)
return true; // some expected positive spells have SPELL_ATTR_EX_NEGATIVE or unclear target modes
break;
case SPELL_AURA_ADD_TARGET_TRIGGER:
return true;
case SPELL_AURA_PERIODIC_TRIGGER_SPELL:
if (spellId != spellproto->EffectTriggerSpell[effIndex])
{
uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex];
SpellEntry const *spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId);
if (spellTriggeredProto)
{
// non-positive targets of main spell return early
for(int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
// 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,SpellEffectIndex(i)))
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;
case SPELL_AURA_MOD_STUN: //have positive and negative spells, we can't sort its correctly at this moment.
if (effIndex == EFFECT_INDEX_0 && spellproto->Effect[EFFECT_INDEX_1] == 0 && spellproto->Effect[EFFECT_INDEX_2] == 0)
return false; // but all single stun aura spells is negative
// Petrification
if(spellproto->Id == 17624)
return false;
break;
case SPELL_AURA_MOD_PACIFY_SILENCE:
switch(spellproto->Id)
{
case 24740: // Wisp Costume
case 47585: // Dispersion
return true;
default: break;
}
return false;
case SPELL_AURA_MOD_ROOT:
case SPELL_AURA_MOD_SILENCE:
case SPELL_AURA_GHOST:
case SPELL_AURA_PERIODIC_LEECH:
case SPELL_AURA_MOD_STALKED:
case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
return false;
case SPELL_AURA_PERIODIC_DAMAGE: // used in positive spells also.
// part of negative spell if casted at self (prevent cancel)
if (spellproto->EffectImplicitTargetA[effIndex] == TARGET_SELF ||
spellproto->EffectImplicitTargetA[effIndex] == TARGET_SELF2)
return false;
break;
case SPELL_AURA_MOD_DECREASE_SPEED: // used in positive spells also
// part of positive spell if casted at self
if ((spellproto->EffectImplicitTargetA[effIndex] == TARGET_SELF ||
spellproto->EffectImplicitTargetA[effIndex] == TARGET_SELF2) &&
spellproto->SpellFamilyName == SPELLFAMILY_GENERIC)
return false;
// but not this if this first effect (don't found better check)
if (spellproto->Attributes & 0x4000000 && effIndex == EFFECT_INDEX_0)
return false;
break;
case SPELL_AURA_TRANSFORM:
// some spells negative
switch(spellproto->Id)
{
case 36897: // Transporter Malfunction (race mutation to horde)
case 36899: // Transporter Malfunction (race mutation to alliance)
return false;
}
break;
case SPELL_AURA_MOD_SCALE:
// some spells negative
switch(spellproto->Id)
{
case 802: // Mutate Bug, wrongly negative by target modes
return true;
case 36900: // Soul Split: Evil!
case 36901: // Soul Split: Good
case 36893: // Transporter Malfunction (decrease size case)
case 36895: // Transporter Malfunction (increase size case)
return false;
}
break;
case SPELL_AURA_MECHANIC_IMMUNITY:
{
// non-positive immunities
switch(spellproto->EffectMiscValue[effIndex])
{
case MECHANIC_BANDAGE:
case MECHANIC_SHIELD:
case MECHANIC_MOUNT:
case MECHANIC_INVULNERABILITY:
return false;
default:
break;
}
} break;
case SPELL_AURA_ADD_FLAT_MODIFIER: // mods
case SPELL_AURA_ADD_PCT_MODIFIER:
{
// non-positive mods
switch(spellproto->EffectMiscValue[effIndex])
{
case SPELLMOD_COST: // dependent from bas point sign (negative -> positive)
if(spellproto->CalculateSimpleValue(effIndex) > 0)
return false;
break;
default:
break;
}
} break;
case SPELL_AURA_FORCE_REACTION:
if(spellproto->Id==42792) // Recently Dropped Flag (prevent cancel)
return false;
break;
default:
break;
}
break;
}
default:
break;
}
// non-positive targets
if(!IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex]))
return false;
// AttributesEx check
if(spellproto->AttributesEx & SPELL_ATTR_EX_NEGATIVE)
return false;
// ok, positive
return true;
}
bool IsPositiveSpell(uint32 spellId)
{
SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
if (!spellproto)
return false;
// spells with atleast 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 < MAX_EFFECT_INDEX; ++i)
if (!IsPositiveEffect(spellId, SpellEffectIndex(i)))
return false;
return true;
}
bool IsSingleTargetSpell(SpellEntry const *spellInfo)
{
// all other single target spells have if it has AttributesEx5
if ( spellInfo->AttributesEx5 & SPELL_ATTR_EX5_SINGLE_TARGET_SPELL )
return true;
// TODO - need found Judgements rule
switch(GetSpellSpecific(spellInfo->Id))
{
case SPELL_JUDGEMENT:
return true;
default:
break;
}
// single target triggered spell.
// Not real client side single target spell, but it' not triggered until prev. aura expired.
// This is allow store it in single target spells list for caster for spell proc checking
if(spellInfo->Id==38324) // Regeneration (triggered by 38299 (HoTs on Heals))
return true;
return false;
}
bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2)
{
// TODO - need better check
// Equal icon and spellfamily
if( spellInfo1->SpellFamilyName == spellInfo2->SpellFamilyName &&
spellInfo1->SpellIconID == spellInfo2->SpellIconID )
return true;
// TODO - need found Judgements rule
SpellSpecific spec1 = GetSpellSpecific(spellInfo1->Id);
// spell with single target specific types
switch(spec1)
{
case SPELL_JUDGEMENT:
case SPELL_MAGE_POLYMORPH:
if(GetSpellSpecific(spellInfo2->Id) == spec1)
return true;
break;
default:
break;
}
return false;
}
SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form)
{
// talents that learn spells can have stance requirements that need ignore
// (this requirement only for client-side stance show in talent description)
if( GetTalentSpellCost(spellInfo->Id) > 0 &&
(spellInfo->Effect[EFFECT_INDEX_0] == SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[EFFECT_INDEX_1] == SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[EFFECT_INDEX_2] == SPELL_EFFECT_LEARN_SPELL) )
return SPELL_CAST_OK;
uint32 stanceMask = (form ? 1 << (form - 1) : 0);
if (stanceMask & spellInfo->StancesNot) // can explicitly not be casted in this stance
return SPELL_FAILED_NOT_SHAPESHIFT;
if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance
return SPELL_CAST_OK;
bool actAsShifted = false;
if (form > 0)
{
SpellShapeshiftEntry const *shapeInfo = sSpellShapeshiftStore.LookupEntry(form);
if (!shapeInfo)
{
sLog.outError("GetErrorAtShapeshiftedCast: unknown shapeshift %u", form);
return SPELL_CAST_OK;
}
actAsShifted = !(shapeInfo->flags1 & 1); // shapeshift acts as normal form for spells
}
if(actAsShifted)
{
if (spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) // not while shapeshifted
return SPELL_FAILED_NOT_SHAPESHIFT;
else if (spellInfo->Stances != 0) // needs other shapeshift
return SPELL_FAILED_ONLY_SHAPESHIFT;
}
else
{
// needs shapeshift
if(!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT) && spellInfo->Stances != 0)
return SPELL_FAILED_ONLY_SHAPESHIFT;
}
return SPELL_CAST_OK;
}
void SpellMgr::LoadSpellTargetPositions()
{
mSpellTargetPositions.clear(); // need for reload case
uint32 count = 0;
// 0 1 2 3 4 5
QueryResult *result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM spell_target_position");
if (!result)
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString( ">> Loaded %u spell target coordinates", count );
return;
}
barGoLink bar((int)result->GetRowCount());
do
{
Field *fields = result->Fetch();
bar.step();
uint32 Spell_ID = fields[0].GetUInt32();
SpellTargetPosition st;
st.target_mapId = fields[1].GetUInt32();
st.target_X = fields[2].GetFloat();
st.target_Y = fields[3].GetFloat();
st.target_Z = fields[4].GetFloat();
st.target_Orientation = fields[5].GetFloat();
MapEntry const* mapEntry = sMapStore.LookupEntry(st.target_mapId);
if (!mapEntry)
{
sLog.outErrorDb("Spell (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.",Spell_ID,st.target_mapId);
continue;
}
if (st.target_X==0 && st.target_Y==0 && st.target_Z==0)
{
sLog.outErrorDb("Spell (ID:%u) target coordinates not provided.",Spell_ID);
continue;
}
SpellEntry const* spellInfo = sSpellStore.LookupEntry(Spell_ID);
if (!spellInfo)
{
sLog.outErrorDb("Spell (ID:%u) listed in `spell_target_position` does not exist.",Spell_ID);
continue;
}
bool found = false;
for(int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
if (spellInfo->EffectImplicitTargetA[i]==TARGET_TABLE_X_Y_Z_COORDINATES || spellInfo->EffectImplicitTargetB[i]==TARGET_TABLE_X_Y_Z_COORDINATES)
{
// additional requirements
if (spellInfo->Effect[i]==SPELL_EFFECT_BIND && spellInfo->EffectMiscValue[i])
{
uint32 zone_id = sMapMgr.GetAreaId(st.target_mapId, st.target_X, st.target_Y, st.target_Z);
if (zone_id != spellInfo->EffectMiscValue[i])
{
sLog.outErrorDb("Spell (Id: %u) listed in `spell_target_position` expected point to zone %u bit point to zone %u.",Spell_ID, spellInfo->EffectMiscValue[i], zone_id);
break;
}
}
found = true;
break;
}
}
if (!found)
{
sLog.outErrorDb("Spell (Id: %u) listed in `spell_target_position` does not have target TARGET_TABLE_X_Y_Z_COORDINATES (17).",Spell_ID);
continue;
}
mSpellTargetPositions[Spell_ID] = st;
++count;
} while( result->NextRow() );
delete result;
sLog.outString();
sLog.outString( ">> Loaded %u spell teleport coordinates", count );
}
struct DoSpellProcEvent
{
DoSpellProcEvent(SpellProcEventEntry const& _spe) : spe(_spe) {}
void operator() (uint32 spell_id) { sSpellMgr.mSpellProcEventMap[spell_id] = spe; }
SpellProcEventEntry const& spe;
};
void SpellMgr::LoadSpellProcEvents()
{
mSpellProcEventMap.clear(); // need for reload case
uint32 count = 0;
// 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;
}
barGoLink bar( (int)result->GetRowCount() );
uint32 customProc = 0;
do
{
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
const SpellEntry *spell = sSpellStore.LookupEntry(entry);
if (!spell)
{
sLog.outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry);
continue;
}
uint32 first_id = GetFirstSpellInChain(entry);
if ( first_id != entry )
{
sLog.outErrorDb("Spell %u listed in `spell_proc_event` is not first rank (%u) in chain", entry, first_id);
// prevent loading since it won't have an effect anyway
continue;
}
SpellProcEventEntry spe;
spe.schoolMask = fields[1].GetUInt32();
spe.spellFamilyName = fields[2].GetUInt32();
spe.spellFamilyMask = (uint64)fields[3].GetUInt32()|((uint64)fields[4].GetUInt32()<<32);
spe.spellFamilyMask2= 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;
// also add to high ranks
DoSpellProcEvent worker(spe);
doForHighRanks(entry,worker);
if (spell->procFlags==0)
{
if (spe.procFlags == 0)
{
sLog.outErrorDb("Spell %u listed in `spell_proc_event` probally not triggered spell", entry);
continue;
}
customProc++;
}
++count;
} while( result->NextRow() );
delete result;
sLog.outString();
if (customProc)
sLog.outString( ">> Loaded %u extra spell proc event conditions +%u custom", count, customProc );
else
sLog.outString( ">> Loaded %u extra spell proc event conditions", count );
}
struct DoSpellProcItemEnchant
{
DoSpellProcItemEnchant(float _ppm) : ppm(_ppm) {}
void operator() (uint32 spell_id) { sSpellMgr.mSpellProcItemEnchantMap[spell_id] = ppm; }
float ppm;
};
void SpellMgr::LoadSpellProcItemEnchant()
{
mSpellProcItemEnchantMap.clear(); // need for reload case
uint32 count = 0;
// 0 1
QueryResult *result = WorldDatabase.Query("SELECT entry, ppmRate FROM spell_proc_item_enchant");
if( !result )
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString( ">> Loaded %u proc item enchant definitions", count );
return;
}
barGoLink bar( (int)result->GetRowCount() );
do
{
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
float ppmRate = fields[1].GetFloat();
SpellEntry const* spellInfo = sSpellStore.LookupEntry(entry);
if (!spellInfo)
{
sLog.outErrorDb("Spell %u listed in `spell_proc_item_enchant` does not exist", entry);
continue;
}
uint32 first_id = GetFirstSpellInChain(entry);
if ( first_id != entry )
{
sLog.outErrorDb("Spell %u listed in `spell_proc_item_enchant` is not first rank (%u) in chain", entry, first_id);
// prevent loading since it won't have an effect anyway
continue;
}
mSpellProcItemEnchantMap[entry] = ppmRate;
// also add to high ranks
DoSpellProcItemEnchant worker(ppmRate);
doForHighRanks(entry,worker);
++count;
} while( result->NextRow() );
delete result;
sLog.outString();
sLog.outString( ">> Loaded %u proc item enchant definitions", count );
}
struct DoSpellBonusess
{
DoSpellBonusess(SpellBonusEntry const& _spellBonus) : spellBonus(_spellBonus) {}
void operator() (uint32 spell_id) { sSpellMgr.mSpellBonusMap[spell_id] = spellBonus; }
SpellBonusEntry const& spellBonus;
};
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 )
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString( ">> Loaded %u spell bonus data", count);
return;
}
barGoLink bar( (int)result->GetRowCount() );
do
{
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
SpellEntry const* spell = sSpellStore.LookupEntry(entry);
if (!spell)
{
sLog.outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry);
continue;
}
uint32 first_id = GetFirstSpellInChain(entry);
if ( first_id != entry )
{
sLog.outErrorDb("Spell %u listed in `spell_bonus_data` is not first rank (%u) in chain", entry, first_id);
// prevent loading since it won't have an effect anyway
continue;
}
SpellBonusEntry sbe;
sbe.direct_damage = fields[1].GetFloat();
sbe.dot_damage = fields[2].GetFloat();
sbe.ap_bonus = fields[3].GetFloat();
mSpellBonusMap[entry] = sbe;
// also add to high ranks
DoSpellBonusess worker(sbe);
doForHighRanks(entry,worker);
++count;
} while( result->NextRow() );
delete result;
sLog.outString();
sLog.outString( ">> Loaded %u extra spell bonus data", count);
}
bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active)
{
// No extra req need
uint32 procEvent_procEx = PROC_EX_NONE;
// check prockFlags for condition
if((procFlags & EventProcFlag) == 0)
return false;
// Always trigger for this
if (EventProcFlag & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_ON_TRAP_ACTIVATION))
return true;
if (spellProcEvent) // Exist event data
{
// Store extra req
procEvent_procEx = spellProcEvent->procEx;
// For melee triggers
if (procSpell == NULL)
{
// Check (if set) for school (melee attack have Normal school)
if(spellProcEvent->schoolMask && (spellProcEvent->schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0)
return false;
}
else // For spells need check school/spell family/family mask
{
// Check (if set) for school
if(spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpell->SchoolMask) == 0)
return false;
// Check (if set) for spellFamilyName
if(spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName))
return false;
// spellFamilyName is Ok need check for spellFamilyMask if present
if(spellProcEvent->spellFamilyMask || spellProcEvent->spellFamilyMask2)
{
if ((spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags ) == 0 &&
(spellProcEvent->spellFamilyMask2 & procSpell->SpellFamilyFlags2) == 0)
return false;
active = true; // Spell added manualy -> so its active spell
}
}
}
// 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)
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)
return true;
// Passive spells can`t trigger if need hit (exclude cases when procExtra include non-active flags)
if ((procEvent_procEx & PROC_EX_NORMAL_HIT & procExtra) && !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;
}
return false;
}
void SpellMgr::LoadSpellElixirs()
{
mSpellElixirs.clear(); // need for reload case
uint32 count = 0;
// 0 1
QueryResult *result = WorldDatabase.Query("SELECT entry, mask FROM spell_elixir");
if( !result )
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString( ">> Loaded %u spell elixir definitions", count );
return;
}
barGoLink bar( (int)result->GetRowCount() );
do
{
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
uint8 mask = fields[1].GetUInt8();
SpellEntry const* spellInfo = sSpellStore.LookupEntry(entry);
if (!spellInfo)
{
sLog.outErrorDb("Spell %u listed in `spell_elixir` does not exist", entry);
continue;
}
mSpellElixirs[entry] = mask;
++count;
} while( result->NextRow() );
delete result;
sLog.outString();
sLog.outString( ">> Loaded %u spell elixir definitions", count );
}
void SpellMgr::LoadSpellThreats()
{
mSpellThreatMap.clear(); // need for reload case
uint32 count = 0;
// 0 1
QueryResult *result = WorldDatabase.Query("SELECT entry, Threat FROM spell_threat");
if( !result )
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString( ">> Loaded %u aggro generating spells", count );
return;
}
barGoLink bar( (int)result->GetRowCount() );
do
{
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
uint16 Threat = fields[1].GetUInt16();
if (!sSpellStore.LookupEntry(entry))
{
sLog.outErrorDb("Spell %u listed in `spell_threat` does not exist", entry);
continue;
}
mSpellThreatMap[entry] = Threat;
++count;
} while( result->NextRow() );
delete result;
sLog.outString();
sLog.outString( ">> Loaded %u aggro generating spells", count );
}
bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const
{
SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
if(!spellInfo_1 || !spellInfo_2) return false;
if(spellInfo_1->Id == spellId_2) return false;
return GetFirstSpellInChain(spellInfo_1->Id)==GetFirstSpellInChain(spellId_2);
}
bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo)
{
if(IsPassiveSpell(spellInfo->Id)) // ranked passive spell
return false;
if(spellInfo->powerType != POWER_MANA && spellInfo->powerType != POWER_HEALTH)
return false;
if(IsProfessionOrRidingSpell(spellInfo->Id))
return false;
if(sSpellMgr.IsSkillBonusSpell(spellInfo->Id))
return false;
// All stance spells. if any better way, change it.
for (int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
switch(spellInfo->SpellFamilyName)
{
case SPELLFAMILY_PALADIN:
// Paladin aura Spell
if (spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AREA_AURA_RAID)
return false;
break;
case SPELLFAMILY_DRUID:
// Druid form Spell
if (spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA &&
spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT)
return false;
break;
case SPELLFAMILY_ROGUE:
// Rogue Stealth
if (spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA &&
spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT)
return false;
break;
}
}
return true;
}
bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) const
{
SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
if(!spellInfo_1 || !spellInfo_2)
return false;
if(spellId_1 == spellId_2)
return false;
//I think we don't check this correctly because i need a exception for spell:
//72,11327,18461...(called from 1856,1857...) Call Aura 16,31, after trigger another spell who call aura 77 and 77 remove 16 and 31, this should not happen.
if(spellInfo_2->SpellFamilyFlags == 2048)
return false;
// Resurrection sickness
if((spellInfo_1->Id == SPELL_ID_PASSIVE_RESURRECTION_SICKNESS) != (spellInfo_2->Id==SPELL_ID_PASSIVE_RESURRECTION_SICKNESS))
return false;
// Allow stack passive and not passive spells
if ((spellInfo_1->Attributes & SPELL_ATTR_PASSIVE)!=(spellInfo_2->Attributes & SPELL_ATTR_PASSIVE))
return false;
// Specific spell family spells
switch(spellInfo_1->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
switch(spellInfo_2->SpellFamilyName)
{
case SPELLFAMILY_GENERIC: // same family case
{
// Thunderfury
if ((spellInfo_1->Id == 21992 && spellInfo_2->Id == 27648) ||
(spellInfo_2->Id == 21992 && spellInfo_1->Id == 27648))
return false;
// Lightning Speed (Mongoose) and Fury of the Crashing Waves (Tsunami Talisman)
if ((spellInfo_1->Id == 28093 && spellInfo_2->Id == 42084) ||
(spellInfo_2->Id == 28093 && spellInfo_1->Id == 42084))
return false;
// Soulstone Resurrection and Twisting Nether (resurrector)
if( spellInfo_1->SpellIconID == 92 && spellInfo_2->SpellIconID == 92 && (
spellInfo_1->SpellVisual[0] == 99 && spellInfo_2->SpellVisual[0] == 0 ||
spellInfo_2->SpellVisual[0] == 99 && spellInfo_1->SpellVisual[0] == 0 ) )
return false;
// Heart of the Wild, Agility and various Idol Triggers
if(spellInfo_1->SpellIconID == 240 && spellInfo_2->SpellIconID == 240)
return false;
// Personalized Weather (thunder effect should overwrite rainy aura)
if(spellInfo_1->SpellIconID == 2606 && spellInfo_2->SpellIconID == 2606)
return false;
// Brood Affliction: Bronze
if( (spellInfo_1->Id == 23170 && spellInfo_2->Id == 23171) ||
(spellInfo_2->Id == 23170 && spellInfo_1->Id == 23171) )
return false;
// Cool Down (See PeriodicAuraTick())
if ((spellInfo_1->Id == 52441 && spellInfo_2->Id == 52443) ||
(spellInfo_2->Id == 52441 && spellInfo_1->Id == 52443))
return false;
// See Chapel Invisibility and See Noth Invisibility
if( (spellInfo_1->Id == 52950 && spellInfo_2->Id == 52707) ||
(spellInfo_2->Id == 52950 && spellInfo_1->Id == 52707) )
return false;
// Regular and Night Elf Ghost
if( (spellInfo_1->Id == 8326 && spellInfo_2->Id == 20584) ||
(spellInfo_2->Id == 8326 && spellInfo_1->Id == 20584) )
return false;
// Kindred Spirits
if( spellInfo_1->SpellIconID == 3559 && spellInfo_2->SpellIconID == 3559 )
return false;
break;
}
case SPELLFAMILY_MAGE:
// Arcane Intellect and Insight
if( spellInfo_2->SpellIconID == 125 && spellInfo_1->Id == 18820 )
return false;
break;
case SPELLFAMILY_WARRIOR:
{
// Scroll of Protection and Defensive Stance (multi-family check)
if( spellInfo_1->SpellIconID == 276 && spellInfo_1->SpellVisual[0] == 196 && spellInfo_2->Id == 71)
return false;
// Improved Hamstring -> Hamstring (multi-family check)
if( (spellInfo_2->SpellFamilyFlags & UI64LIT(0x2)) && spellInfo_1->Id == 23694 )
return false;
break;
}
case SPELLFAMILY_DRUID:
{
// Scroll of Stamina and Leader of the Pack (multi-family check)
if( spellInfo_1->SpellIconID == 312 && spellInfo_1->SpellVisual[0] == 216 && spellInfo_2->Id == 24932 )
return false;
// Dragonmaw Illusion (multi-family check)
if (spellId_1 == 40216 && spellId_2 == 42016 )
return false;
break;
}
case SPELLFAMILY_ROGUE:
{
// Garrote-Silence -> Garrote (multi-family check)
if( spellInfo_1->SpellIconID == 498 && spellInfo_1->SpellVisual[0] == 0 && spellInfo_2->SpellIconID == 498 )
return false;
break;
}
case SPELLFAMILY_HUNTER:
{
// Concussive Shot and Imp. Concussive Shot (multi-family check)
if( spellInfo_1->Id == 19410 && spellInfo_2->Id == 5116 )
return false;
// Improved Wing Clip -> Wing Clip (multi-family check)
if( (spellInfo_2->SpellFamilyFlags & UI64LIT(0x40)) && spellInfo_1->Id == 19229 )
return false;
break;
}
case SPELLFAMILY_PALADIN:
{
// Unstable Currents and other -> *Sanctity Aura (multi-family check)
if( spellInfo_2->SpellIconID==502 && spellInfo_1->SpellIconID==502 && spellInfo_1->SpellVisual[0]==969 )
return false;
// *Band of Eternal Champion and Seal of Command(multi-family check)
if( spellId_1 == 35081 && spellInfo_2->SpellIconID==561 && spellInfo_2->SpellVisual[0]==7992)
return false;
// Blessing of Sanctuary (multi-family check, some from 16 spell icon spells)
if (spellInfo_1->Id == 67480 && spellInfo_2->Id == 20911)
return false;
break;
}
}
// Dragonmaw Illusion, Blood Elf Illusion, Human Illusion, Illidari Agent Illusion, Scarlet Crusade Disguise
if(spellInfo_1->SpellIconID == 1691 && spellInfo_2->SpellIconID == 1691)
return false;
break;
case SPELLFAMILY_MAGE:
if( spellInfo_2->SpellFamilyName == SPELLFAMILY_MAGE )
{
// Blizzard & Chilled (and some other stacked with blizzard spells
if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x80)) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x100000)) ||
(spellInfo_2->SpellFamilyFlags & UI64LIT(0x80)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x100000)) )
return false;
// Blink & Improved Blink
if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x0000000000010000)) && (spellInfo_2->SpellVisual[0] == 72 && spellInfo_2->SpellIconID == 1499) ||
(spellInfo_2->SpellFamilyFlags & UI64LIT(0x0000000000010000)) && (spellInfo_1->SpellVisual[0] == 72 && spellInfo_1->SpellIconID == 1499) )
return false;
// Living Bomb & Ignite (Dots)
if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x2000000000000)) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x8000000)) ||
(spellInfo_2->SpellFamilyFlags & UI64LIT(0x2000000000000)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x8000000)) )
return false;
// Fireball & Pyroblast (Dots)
if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x1)) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x400000)) ||
(spellInfo_2->SpellFamilyFlags & UI64LIT(0x1)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x400000)) )
return false;
}
// Detect Invisibility and Mana Shield (multi-family check)
if( spellInfo_2->Id == 132 && spellInfo_1->SpellIconID == 209 && spellInfo_1->SpellVisual[0] == 968 )
return false;
// Combustion and Fire Protection Aura (multi-family check)
if( spellInfo_1->Id == 11129 && spellInfo_2->SpellIconID == 33 && spellInfo_2->SpellVisual[0] == 321 )
return false;
// Arcane Intellect and Insight
if( spellInfo_1->SpellIconID == 125 && spellInfo_2->Id == 18820 )
return false;
break;
case SPELLFAMILY_WARLOCK:
if( spellInfo_2->SpellFamilyName == SPELLFAMILY_WARLOCK )
{
// Siphon Life and Drain Life
if( spellInfo_1->SpellIconID == 152 && spellInfo_2->SpellIconID == 546 ||
spellInfo_2->SpellIconID == 152 && spellInfo_1->SpellIconID == 546 )
return false;
//Corruption & Seed of corruption
if( spellInfo_1->SpellIconID == 313 && spellInfo_2->SpellIconID == 1932 ||
spellInfo_2->SpellIconID == 313 && spellInfo_1->SpellIconID == 1932 )
if(spellInfo_1->SpellVisual[0] != 0 && spellInfo_2->SpellVisual[0] != 0)
return true; // can't be stacked
// Corruption and Unstable Affliction
if( spellInfo_1->SpellIconID == 313 && spellInfo_2->SpellIconID == 2039 ||
spellInfo_2->SpellIconID == 313 && spellInfo_1->SpellIconID == 2039 )
return false;
// (Corruption or Unstable Affliction) and (Curse of Agony or Curse of Doom)
if( (spellInfo_1->SpellIconID == 313 || spellInfo_1->SpellIconID == 2039) && (spellInfo_2->SpellIconID == 544 || spellInfo_2->SpellIconID == 91) ||
(spellInfo_2->SpellIconID == 313 || spellInfo_2->SpellIconID == 2039) && (spellInfo_1->SpellIconID == 544 || spellInfo_1->SpellIconID == 91) )
return false;
// Metamorphosis, diff effects
if (spellInfo_1->SpellIconID == 3314 && spellInfo_2->SpellIconID == 3314)
return false;
}
// Detect Invisibility and Mana Shield (multi-family check)
if( spellInfo_1->Id == 132 && spellInfo_2->SpellIconID == 209 && spellInfo_2->SpellVisual[0] == 968 )
return false;
break;
case SPELLFAMILY_WARRIOR:
if( spellInfo_2->SpellFamilyName == SPELLFAMILY_WARRIOR )
{
// Rend and Deep Wound
if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x20)) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x1000000000)) ||
(spellInfo_2->SpellFamilyFlags & UI64LIT(0x20)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x1000000000)) )
return false;
// Battle Shout and Rampage
if( (spellInfo_1->SpellIconID == 456 && spellInfo_2->SpellIconID == 2006) ||
(spellInfo_2->SpellIconID == 456 && spellInfo_1->SpellIconID == 2006) )
return false;
}
// Hamstring -> Improved Hamstring (multi-family check)
if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x2)) && spellInfo_2->Id == 23694 )
return false;
// Defensive Stance and Scroll of Protection (multi-family check)
if( spellInfo_1->Id == 71 && spellInfo_2->SpellIconID == 276 && spellInfo_2->SpellVisual[0] == 196 )
return false;
// Bloodlust and Bloodthirst (multi-family check)
if( spellInfo_2->Id == 2825 && spellInfo_1->SpellIconID == 38 && spellInfo_1->SpellVisual[0] == 0 )
return false;
break;
case SPELLFAMILY_PRIEST:
if( spellInfo_2->SpellFamilyName == SPELLFAMILY_PRIEST )
{
//Devouring Plague and Shadow Vulnerability
if ((spellInfo_1->SpellFamilyFlags & UI64LIT(0x2000000)) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x800000000)) ||
(spellInfo_2->SpellFamilyFlags & UI64LIT(0x2000000)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x800000000)))
return false;
//StarShards and Shadow Word: Pain
if ((spellInfo_1->SpellFamilyFlags & UI64LIT(0x200000)) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x8000)) ||
(spellInfo_2->SpellFamilyFlags & UI64LIT(0x200000)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x8000)))
return false;
// Dispersion
if ((spellInfo_1->Id == 47585 && spellInfo_2->Id == 60069) ||
(spellInfo_2->Id == 47585 && spellInfo_1->Id == 60069))
return false;
}
break;
case SPELLFAMILY_DRUID:
if( spellInfo_2->SpellFamilyName == SPELLFAMILY_DRUID )
{
//Omen of Clarity and Blood Frenzy
if( (spellInfo_1->SpellFamilyFlags == UI64LIT(0x0) && spellInfo_1->SpellIconID == 108) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x20000000000000)) ||
(spellInfo_2->SpellFamilyFlags == UI64LIT(0x0) && spellInfo_2->SpellIconID == 108) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x20000000000000)) )
return false;
// Tree of Life (Shapeshift) and 34123 Tree of Life (Passive)
if ((spellId_1 == 33891 && spellId_2 == 34123) ||
(spellId_2 == 33891 && spellId_1 == 34123))
return false;
// Lifebloom and Wild Growth
if (spellInfo_1->SpellIconID == 2101 && spellInfo_2->SpellIconID == 2864 ||
spellInfo_2->SpellIconID == 2101 && spellInfo_1->SpellIconID == 2864 )
return false;
// Innervate and Glyph of Innervate and some other spells
if (spellInfo_1->SpellIconID == 62 && spellInfo_2->SpellIconID == 62)
return false;
// Wrath of Elune and Nature's Grace
if( spellInfo_1->Id == 16886 && spellInfo_2->Id == 46833 || spellInfo_2->Id == 16886 && spellInfo_1->Id == 46833 )
return false;
// Bear Rage (Feral T4 (2)) and Omen of Clarity
if( spellInfo_1->Id == 16864 && spellInfo_2->Id == 37306 || spellInfo_2->Id == 16864 && spellInfo_1->Id == 37306 )
return false;
// Cat Energy (Feral T4 (2)) and Omen of Clarity
if( spellInfo_1->Id == 16864 && spellInfo_2->Id == 37311 || spellInfo_2->Id == 16864 && spellInfo_1->Id == 37311 )
return false;
// Survival Instincts and Survival Instincts
if( spellInfo_1->Id == 61336 && spellInfo_2->Id == 50322 || spellInfo_2->Id == 61336 && spellInfo_1->Id == 50322 )
return false;
// Savage Roar and Savage Roar (triggered)
if (spellInfo_1->SpellIconID == 2865 && spellInfo_2->SpellIconID == 2865)
return false;
// Frenzied Regeneration and Savage Defense
if( spellInfo_1->Id == 22842 && spellInfo_2->Id == 62606 || spellInfo_2->Id == 22842 && spellInfo_1->Id == 62606 )
return false;
}
// Leader of the Pack and Scroll of Stamina (multi-family check)
if( spellInfo_1->Id == 24932 && spellInfo_2->SpellIconID == 312 && spellInfo_2->SpellVisual[0] == 216 )
return false;
// Dragonmaw Illusion (multi-family check)
if (spellId_1 == 42016 && spellId_2 == 40216 )
return false;
break;
case SPELLFAMILY_ROGUE:
if( spellInfo_2->SpellFamilyName == SPELLFAMILY_ROGUE )
{
// Master of Subtlety
if (spellId_1 == 31665 && spellId_2 == 31666 || spellId_1 == 31666 && spellId_2 == 31665 )
return false;
// Sprint & Sprint (waterwalk)
if( spellInfo_1->SpellIconID == 516 && spellInfo_2->SpellIconID == 516 &&
(spellInfo_1->Category == 44 && spellInfo_2->Category == 0 ||
spellInfo_2->Category == 44 && spellInfo_1->Category == 0))
return false;
}
//Overkill
if( spellInfo_1->SpellIconID == 2285 && spellInfo_2->SpellIconID == 2285 )
return false;
// Garrote -> Garrote-Silence (multi-family check)
if( spellInfo_1->SpellIconID == 498 && spellInfo_2->SpellIconID == 498 && spellInfo_2->SpellVisual[0] == 0 )
return false;
break;
case SPELLFAMILY_HUNTER:
if( spellInfo_2->SpellFamilyName == SPELLFAMILY_HUNTER )
{
// Rapid Fire & Quick Shots
if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x20)) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x20000000000)) ||
(spellInfo_2->SpellFamilyFlags & UI64LIT(0x20)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x20000000000)) )
return false;
// Serpent Sting & (Immolation/Explosive Trap Effect)
if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x4)) && (spellInfo_2->SpellFamilyFlags & UI64LIT(0x00000004000)) ||
(spellInfo_2->SpellFamilyFlags & UI64LIT(0x4)) && (spellInfo_1->SpellFamilyFlags & UI64LIT(0x00000004000)) )
return false;
// Bestial Wrath
if( spellInfo_1->SpellIconID == 1680 && spellInfo_2->SpellIconID == 1680 )
return false;
}
// Wing Clip -> Improved Wing Clip (multi-family check)
if( (spellInfo_1->SpellFamilyFlags & UI64LIT(0x40)) && spellInfo_2->Id == 19229 )
return false;
// Concussive Shot and Imp. Concussive Shot (multi-family check)
if( spellInfo_2->Id == 19410 && spellInfo_1->Id == 5116 )
return false;
break;
case SPELLFAMILY_PALADIN:
if( spellInfo_2->SpellFamilyName == SPELLFAMILY_PALADIN )
{
// Paladin Seals
if (IsSealSpell(spellInfo_1) && IsSealSpell(spellInfo_2))
return true;
// Swift Retribution / Improved Devotion Aura (talents) and Paladin Auras
if ((spellInfo_1->SpellFamilyFlags2 & 0x00000020) && (spellInfo_2->SpellIconID == 291 || spellInfo_2->SpellIconID == 3028) ||
(spellInfo_2->SpellFamilyFlags2 & 0x00000020) && (spellInfo_1->SpellIconID == 291 || spellInfo_1->SpellIconID == 3028))
return false;
// Beacon of Light and Light's Beacon
if ((spellInfo_1->SpellIconID == 3032) && (spellInfo_2->SpellIconID == 3032))
return false;
// Concentration Aura and Improved Concentration Aura and Aura Mastery
if ((spellInfo_1->SpellIconID == 1487) && (spellInfo_2->SpellIconID == 1487))
return false;
// Seal of Corruption (caster/target parts stacking allow, other stacking checked by spell specs)
if (spellInfo_1->SpellIconID == 2292 && spellInfo_2->SpellIconID == 2292)
return false;
// Divine Sacrifice and Divine Guardian
if (spellInfo_1->SpellIconID == 3837 && spellInfo_2->SpellIconID == 3837)
return false;
}
// Blessing of Sanctuary (multi-family check, some from 16 spell icon spells)
if (spellInfo_2->Id == 67480 && spellInfo_1->Id == 20911)
return false;
// Combustion and Fire Protection Aura (multi-family check)
if( spellInfo_2->Id == 11129 && spellInfo_1->SpellIconID == 33 && spellInfo_1->SpellVisual[0] == 321 )
return false;
// *Sanctity Aura -> Unstable Currents and other (multi-family check)
if( spellInfo_1->SpellIconID==502 && spellInfo_2->SpellFamilyName == SPELLFAMILY_GENERIC && spellInfo_2->SpellIconID==502 && spellInfo_2->SpellVisual[0]==969 )
return false;
// *Seal of Command and Band of Eternal Champion (multi-family check)
if( spellInfo_1->SpellIconID==561 && spellInfo_1->SpellVisual[0]==7992 && spellId_2 == 35081)
return false;
break;
case SPELLFAMILY_SHAMAN:
if( spellInfo_2->SpellFamilyName == SPELLFAMILY_SHAMAN )
{
// Windfury weapon
if( spellInfo_1->SpellIconID==220 && spellInfo_2->SpellIconID==220 &&
spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags )
return false;
// Ghost Wolf
if (spellInfo_1->SpellIconID == 67 && spellInfo_2->SpellIconID == 67)
return false;
// Totem of Wrath (positive/negative), ranks checked early
if (spellInfo_1->SpellIconID == 2019 && spellInfo_2->SpellIconID == 2019)
return false;
}
// Bloodlust and Bloodthirst (multi-family check)
if( spellInfo_1->Id == 2825 && spellInfo_2->SpellIconID == 38 && spellInfo_2->SpellVisual[0] == 0 )
return false;
break;
case SPELLFAMILY_DEATHKNIGHT:
if (spellInfo_2->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT)
{
// Lichborne and Lichborne (triggered)
if (spellInfo_1->SpellIconID == 61 && spellInfo_2->SpellIconID == 61)
return false;
// Frost Presence and Frost Presence (triggered)
if (spellInfo_1->SpellIconID == 2632 && spellInfo_2->SpellIconID == 2632)
return false;
// Unholy Presence and Unholy Presence (triggered)
if (spellInfo_1->SpellIconID == 2633 && spellInfo_2->SpellIconID == 2633)
return false;
// Blood Presence and Blood Presence (triggered)
if (spellInfo_1->SpellIconID == 2636 && spellInfo_2->SpellIconID == 2636)
return false;
}
break;
default:
break;
}
// more generic checks
if (spellInfo_1->SpellIconID == spellInfo_2->SpellIconID &&
spellInfo_1->SpellIconID != 0 && spellInfo_2->SpellIconID != 0)
{
bool isModifier = false;
for (int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
if (spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_ADD_FLAT_MODIFIER ||
spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_ADD_PCT_MODIFIER ||
spellInfo_2->EffectApplyAuraName[i] == SPELL_AURA_ADD_FLAT_MODIFIER ||
spellInfo_2->EffectApplyAuraName[i] == SPELL_AURA_ADD_PCT_MODIFIER )
isModifier = true;
}
if (!isModifier)
return true;
}
if (IsRankSpellDueToSpell(spellInfo_1, spellId_2))
return true;
if (spellInfo_1->SpellFamilyName == 0 || spellInfo_2->SpellFamilyName == 0)
return false;
if (spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName)
return false;
bool dummy_only = true;
for (int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
if (spellInfo_1->Effect[i] != spellInfo_2->Effect[i] ||
spellInfo_1->EffectItemType[i] != spellInfo_2->EffectItemType[i] ||
spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i] ||
spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i])
return false;
// ignore dummy only spells
if(spellInfo_1->Effect[i] && spellInfo_1->Effect[i] != SPELL_EFFECT_DUMMY && spellInfo_1->EffectApplyAuraName[i] != SPELL_AURA_DUMMY)
dummy_only = false;
}
if (dummy_only)
return false;
return true;
}
bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo)
return false;
if (spellInfo->Effect[EFFECT_INDEX_1] != SPELL_EFFECT_SKILL)
return false;
uint32 skill = spellInfo->EffectMiscValue[EFFECT_INDEX_1];
return IsProfessionOrRidingSkill(skill);
}
bool SpellMgr::IsProfessionSpell(uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo)
return false;
if (spellInfo->Effect[EFFECT_INDEX_1] != SPELL_EFFECT_SKILL)
return false;
uint32 skill = spellInfo->EffectMiscValue[EFFECT_INDEX_1];
return IsProfessionSkill(skill);
}
bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo)
return false;
if (spellInfo->Effect[EFFECT_INDEX_1] != SPELL_EFFECT_SKILL)
return false;
uint32 skill = spellInfo->EffectMiscValue[EFFECT_INDEX_1];
return IsPrimaryProfessionSkill(skill);
}
bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const
{
return IsPrimaryProfessionSpell(spellId) && GetSpellRank(spellId)==1;
}
bool SpellMgr::IsSkillBonusSpell(uint32 spellId) const
{
SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId);
for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{
SkillLineAbilityEntry const *pAbility = _spell_idx->second;
if (!pAbility || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
continue;
if (pAbility->req_skill_value > 0)
return true;
}
return false;
}
SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const
{
// ignore passive spells
if(IsPassiveSpell(spellInfo->Id))
return spellInfo;
bool needRankSelection = false;
for(int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
if (IsPositiveEffect(spellInfo->Id, SpellEffectIndex(i)) && (
spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA ||
spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY ||
spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_RAID))
{
needRankSelection = true;
break;
}
}
// not required
if(!needRankSelection)
return spellInfo;
for(uint32 nextSpellId = spellInfo->Id; nextSpellId != 0; nextSpellId = GetPrevSpellInChain(nextSpellId))
{
SpellEntry const *nextSpellInfo = sSpellStore.LookupEntry(nextSpellId);
if(!nextSpellInfo)
break;
// if found appropriate level
if(playerLevel + 10 >= nextSpellInfo->spellLevel)
return nextSpellInfo;
// one rank less then
}
// not found
return NULL;
}
void SpellMgr::LoadSpellChains()
{
mSpellChains.clear(); // need for reload case
mSpellChainsNext.clear(); // need for reload case
QueryResult *result = WorldDatabase.Query("SELECT spell_id, prev_spell, first_spell, rank, req_spell FROM spell_chain");
if(result == NULL)
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString( ">> Loaded 0 spell chain records" );
sLog.outErrorDb("`spell_chains` table is empty!");
return;
}
uint32 count = 0;
barGoLink bar( (int)result->GetRowCount() );
do
{
bar.step();
Field *fields = result->Fetch();
uint32 spell_id = fields[0].GetUInt32();
SpellChainNode node;
node.prev = fields[1].GetUInt32();
node.first = fields[2].GetUInt32();
node.rank = fields[3].GetUInt8();
node.req = fields[4].GetUInt32();
if(!sSpellStore.LookupEntry(spell_id))
{
sLog.outErrorDb("Spell %u listed in `spell_chain` does not exist",spell_id);
continue;
}
if(node.prev!=0 && !sSpellStore.LookupEntry(node.prev))
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not existed previous rank spell.",
spell_id,node.prev,node.first,node.rank,node.req);
continue;
}
if(!sSpellStore.LookupEntry(node.first))
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not existing first rank spell.",
spell_id,node.prev,node.first,node.rank,node.req);
continue;
}
// check basic spell chain data integrity (note: rank can be equal 0 or 1 for first/single spell)
if( (spell_id == node.first) != (node.rank <= 1) ||
(spell_id == node.first) != (node.prev == 0) ||
(node.rank <= 1) != (node.prev == 0) )
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not compatible chain data.",
spell_id,node.prev,node.first,node.rank,node.req);
continue;
}
if(node.req!=0 && !sSpellStore.LookupEntry(node.req))
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not existing required spell.",
spell_id,node.prev,node.first,node.rank,node.req);
continue;
}
// talents not required data in spell chain for work, but must be checked if present for intergrity
if(TalentSpellPos const* pos = GetTalentSpellPos(spell_id))
{
if(node.rank!=pos->rank+1)
{
sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong rank.",
spell_id,node.prev,node.first,node.rank,node.req);
continue;
}
if(TalentEntry const* talentEntry = sTalentStore.LookupEntry(pos->talent_id))
{
if(node.first!=talentEntry->RankID[0])
{
sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong first rank spell.",
spell_id,node.prev,node.first,node.rank,node.req);
continue;
}
if(node.rank > 1 && node.prev != talentEntry->RankID[node.rank-1-1])
{
sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong prev rank spell.",
spell_id,node.prev,node.first,node.rank,node.req);
continue;
}
/*if(node.req!=talentEntry->DependsOnSpell)
{
sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong required spell.",
spell_id,node.prev,node.first,node.rank,node.req);
continue;
}*/
}
}
mSpellChains[spell_id] = node;
if(node.prev)
mSpellChainsNext.insert(SpellChainMapNext::value_type(node.prev,spell_id));
if(node.req)
mSpellChainsNext.insert(SpellChainMapNext::value_type(node.req,spell_id));
++count;
} while( result->NextRow() );
delete result;
// additional integrity checks
for(SpellChainMap::const_iterator i = mSpellChains.begin(); i != mSpellChains.end(); ++i)
{
if(i->second.prev)
{
SpellChainMap::const_iterator i_prev = mSpellChains.find(i->second.prev);
if(i_prev == mSpellChains.end())
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not found previous rank spell in table.",
i->first,i->second.prev,i->second.first,i->second.rank,i->second.req);
}
else if( i_prev->second.first != i->second.first )
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has different first spell in chain compared to previous rank spell (prev: %u, first: %u, rank: %d, req: %u).",
i->first,i->second.prev,i->second.first,i->second.rank,i->second.req,
i_prev->second.prev,i_prev->second.first,i_prev->second.rank,i_prev->second.req);
}
else if( i_prev->second.rank+1 != i->second.rank )
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has different rank compared to previous rank spell (prev: %u, first: %u, rank: %d, req: %u).",
i->first,i->second.prev,i->second.first,i->second.rank,i->second.req,
i_prev->second.prev,i_prev->second.first,i_prev->second.rank,i_prev->second.req);
}
}
if(i->second.req)
{
SpellChainMap::const_iterator i_req = mSpellChains.find(i->second.req);
if(i_req == mSpellChains.end())
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not found required rank spell in table.",
i->first,i->second.prev,i->second.first,i->second.rank,i->second.req);
}
else if( i_req->second.first == i->second.first )
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has required rank spell from same spell chain (prev: %u, first: %u, rank: %d, req: %u).",
i->first,i->second.prev,i->second.first,i->second.rank,i->second.req,
i_req->second.prev,i_req->second.first,i_req->second.rank,i_req->second.req);
}
else if( i_req->second.req )
{
sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has required rank spell with required spell (prev: %u, first: %u, rank: %d, req: %u).",
i->first,i->second.prev,i->second.first,i->second.rank,i->second.req,
i_req->second.prev,i_req->second.first,i_req->second.rank,i_req->second.req);
}
}
}
sLog.outString();
sLog.outString( ">> Loaded %u spell chain records", count );
}
void SpellMgr::LoadSpellLearnSkills()
{
mSpellLearnSkills.clear(); // need for reload case
// search auto-learned skills and add its to map also for use in unlearn spells/talents
uint32 dbc_count = 0;
barGoLink bar( sSpellStore.GetNumRows() );
for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell)
{
bar.step();
SpellEntry const* entry = sSpellStore.LookupEntry(spell);
if(!entry)
continue;
for(int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
if(entry->Effect[i] == SPELL_EFFECT_SKILL)
{
SpellLearnSkillNode dbc_node;
dbc_node.skill = entry->EffectMiscValue[i];
dbc_node.step = entry->CalculateSimpleValue(SpellEffectIndex(i));
if ( dbc_node.skill != SKILL_RIDING )
dbc_node.value = 1;
else
dbc_node.value = dbc_node.step * 75;
dbc_node.maxvalue = dbc_node.step * 75;
mSpellLearnSkills[spell] = dbc_node;
++dbc_count;
break;
}
}
}
sLog.outString();
sLog.outString( ">> Loaded %u Spell Learn Skills from DBC", dbc_count );
}
void SpellMgr::LoadSpellLearnSpells()
{
mSpellLearnSpells.clear(); // need for reload case
// 0 1 2
QueryResult *result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell");
if (!result)
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString( ">> Loaded 0 spell learn spells" );
sLog.outErrorDb("`spell_learn_spell` table is empty!");
return;
}
uint32 count = 0;
barGoLink bar( (int)result->GetRowCount() );
do
{
bar.step();
Field *fields = result->Fetch();
uint32 spell_id = fields[0].GetUInt32();
SpellLearnSpellNode node;
node.spell = fields[1].GetUInt32();
node.active = fields[2].GetBool();
node.autoLearned= false;
if (!sSpellStore.LookupEntry(spell_id))
{
sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",spell_id);
continue;
}
if (!sSpellStore.LookupEntry(node.spell))
{
sLog.outErrorDb("Spell %u listed in `spell_learn_spell` learning not existed spell %u",spell_id,node.spell);
continue;
}
if (GetTalentSpellCost(node.spell))
{
sLog.outErrorDb("Spell %u listed in `spell_learn_spell` attempt learning talent spell %u, skipped",spell_id,node.spell);
continue;
}
mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell_id,node));
++count;
} while( result->NextRow() );
delete result;
// search auto-learned spells and add its to map also for use in unlearn spells/talents
uint32 dbc_count = 0;
for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell)
{
SpellEntry const* entry = sSpellStore.LookupEntry(spell);
if (!entry)
continue;
for(int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
if(entry->Effect[i]==SPELL_EFFECT_LEARN_SPELL)
{
SpellLearnSpellNode dbc_node;
dbc_node.spell = entry->EffectTriggerSpell[i];
dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself)
// ignore learning not existed spells (broken/outdated/or generic learnig spell 483
if (!sSpellStore.LookupEntry(dbc_node.spell))
continue;
// talent or passive spells or skill-step spells auto-casted and not need dependent learning,
// pet teaching spells don't must be dependent learning (casted)
// other required explicit dependent learning
dbc_node.autoLearned = entry->EffectImplicitTargetA[i]==TARGET_PET || GetTalentSpellCost(spell) > 0 || IsPassiveSpell(spell) || IsSpellHaveEffect(entry,SPELL_EFFECT_SKILL_STEP);
SpellLearnSpellMapBounds db_node_bounds = GetSpellLearnSpellMapBounds(spell);
bool found = false;
for(SpellLearnSpellMap::const_iterator itr = db_node_bounds.first; itr != db_node_bounds.second; ++itr)
{
if (itr->second.spell == dbc_node.spell)
{
sLog.outErrorDb("Spell %u auto-learn spell %u in spell.dbc then the record in `spell_learn_spell` is redundant, please fix DB.",
spell,dbc_node.spell);
found = true;
break;
}
}
if (!found) // add new spell-spell pair if not found
{
mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell,dbc_node));
++dbc_count;
}
}
}
}
sLog.outString();
sLog.outString( ">> Loaded %u spell learn spells + %u found in DBC", count, dbc_count );
}
void SpellMgr::LoadSpellScriptTarget()
{
mSpellScriptTarget.clear(); // need for reload case
uint32 count = 0;
QueryResult *result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM spell_script_target");
if (!result)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outErrorDb(">> Loaded 0 SpellScriptTarget. DB table `spell_script_target` is empty.");
return;
}
barGoLink bar((int)result->GetRowCount());
do
{
Field *fields = result->Fetch();
bar.step();
uint32 spellId = fields[0].GetUInt32();
uint32 type = fields[1].GetUInt32();
uint32 targetEntry = fields[2].GetUInt32();
SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId);
if (!spellProto)
{
sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not exist.",spellId,targetEntry);
continue;
}
bool targetfound = false;
for (int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
if( spellProto->EffectImplicitTargetA[i] == TARGET_SCRIPT ||
spellProto->EffectImplicitTargetB[i] == TARGET_SCRIPT ||
spellProto->EffectImplicitTargetA[i] == TARGET_SCRIPT_COORDINATES ||
spellProto->EffectImplicitTargetB[i] == TARGET_SCRIPT_COORDINATES ||
spellProto->EffectImplicitTargetA[i] == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT ||
spellProto->EffectImplicitTargetB[i] == TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT ||
spellProto->EffectImplicitTargetA[i] == TARGET_AREAEFFECT_CUSTOM ||
spellProto->EffectImplicitTargetB[i] == TARGET_AREAEFFECT_CUSTOM)
{
targetfound = true;
break;
}
}
if (!targetfound)
{
sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_SCRIPT(38) or TARGET_SCRIPT_COORDINATES (46) or TARGET_FOCUS_OR_SCRIPTED_GAMEOBJECT (40).", spellId, targetEntry);
continue;
}
if (type >= MAX_SPELL_TARGET_TYPE)
{
sLog.outErrorDb("Table `spell_script_target`: target type %u for TargetEntry %u is incorrect.",type,targetEntry);
continue;
}
// Checks by target type
switch (type)
{
case SPELL_TARGET_TYPE_GAMEOBJECT:
{
if (!targetEntry)
break;
if (!sGOStorage.LookupEntry<GameObjectInfo>(targetEntry))
{
sLog.outErrorDb("Table `spell_script_target`: gameobject template entry %u does not exist.",targetEntry);
continue;
}
break;
}
default:
if (!targetEntry)
{
sLog.outErrorDb("Table `spell_script_target`: target entry == 0 for not GO target type (%u).",type);
continue;
}
if (const CreatureInfo* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(targetEntry))
{
if (spellId == 30427 && !cInfo->SkinLootId)
{
sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no skinlootid. Gas extraction will not work!", cInfo->Entry);
continue;
}
}
else
{
sLog.outErrorDb("Table `spell_script_target`: creature template entry %u does not exist.",targetEntry);
continue;
}
break;
}
mSpellScriptTarget.insert(SpellScriptTarget::value_type(spellId,SpellTargetEntry(SpellTargetType(type),targetEntry)));
++count;
} while (result->NextRow());
delete result;
// Check all spells
/* Disabled (lot errors at this moment)
for(uint32 i = 1; i < sSpellStore.nCount; ++i)
{
SpellEntry const * spellInfo = sSpellStore.LookupEntry(i);
if(!spellInfo)
continue;
bool found = false;
for(int j = 0; j < MAX_EFFECT_INDEX; ++j)
{
if( spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT || spellInfo->EffectImplicitTargetA[j] != TARGET_SELF && spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT )
{
SpellScriptTarget::const_iterator lower = GetBeginSpellScriptTarget(spellInfo->Id);
SpellScriptTarget::const_iterator upper = GetEndSpellScriptTarget(spellInfo->Id);
if(lower==upper)
{
sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = %u (TARGET_SCRIPT), but does not have record in `spell_script_target`",spellInfo->Id,TARGET_SCRIPT);
break; // effects of spell
}
}
}
}
*/
sLog.outString();
sLog.outString(">> Loaded %u Spell Script Targets", count);
}
void SpellMgr::LoadSpellPetAuras()
{
mSpellPetAuraMap.clear(); // need for reload case
uint32 count = 0;
// 0 1 2 3
QueryResult *result = WorldDatabase.Query("SELECT spell, effectId, pet, aura FROM spell_pet_auras");
if( !result )
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outString( ">> Loaded %u spell pet auras", count );
return;
}
barGoLink bar( (int)result->GetRowCount() );
do
{
Field *fields = result->Fetch();
bar.step();
uint32 spell = fields[0].GetUInt32();
SpellEffectIndex eff = SpellEffectIndex(fields[1].GetUInt32());
uint32 pet = fields[2].GetUInt32();
uint32 aura = fields[3].GetUInt32();
if (eff >= MAX_EFFECT_INDEX)
{
sLog.outErrorDb("Spell %u listed in `spell_pet_auras` with wrong spell effect index (%u)", spell, eff);
continue;
}
SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff);
if(itr != mSpellPetAuraMap.end())
{
itr->second.AddAura(pet, aura);
}
else
{
SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
if (!spellInfo)
{
sLog.outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell);
continue;
}
if (spellInfo->Effect[eff] != SPELL_EFFECT_DUMMY &&
(spellInfo->Effect[eff] != SPELL_EFFECT_APPLY_AURA ||
spellInfo->EffectApplyAuraName[eff] != SPELL_AURA_DUMMY))
{
sLog.outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell);
continue;
}
SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(aura);
if (!spellInfo2)
{
sLog.outErrorDb("Aura %u listed in `spell_pet_auras` does not exist", aura);
continue;
}
PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[eff] == TARGET_PET, spellInfo->CalculateSimpleValue(eff));
mSpellPetAuraMap[(spell<<8) + eff] = pa;
}
++count;
} while( result->NextRow() );
delete result;
sLog.outString();
sLog.outString( ">> Loaded %u spell pet auras", count );
}
void SpellMgr::LoadPetLevelupSpellMap()
{
uint32 count = 0;
uint32 family_count = 0;
for (uint32 i = 0; i < sCreatureFamilyStore.GetNumRows(); ++i)
{
CreatureFamilyEntry const *creatureFamily = sCreatureFamilyStore.LookupEntry(i);
if(!creatureFamily) // not exist
continue;
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
{
SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j);
if( !skillLine )
continue;
if (skillLine->skillId!=creatureFamily->skillLine[0] &&
(!creatureFamily->skillLine[1] || skillLine->skillId!=creatureFamily->skillLine[1]))
continue;
if(skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
continue;
SpellEntry const *spell = sSpellStore.LookupEntry(skillLine->spellId);
if(!spell) // not exist
continue;
PetLevelupSpellSet& spellSet = mPetLevelupSpellMap[creatureFamily->ID];
if(spellSet.empty())
++family_count;
spellSet.insert(PetLevelupSpellSet::value_type(spell->spellLevel,spell->Id));
count++;
}
}
sLog.outString();
sLog.outString( ">> Loaded %u pet levelup and default spells for %u families", count, family_count );
}
bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntry& petDefSpells)
{
// skip empty list;
bool have_spell = false;
for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
{
if(petDefSpells.spellid[j])
{
have_spell = true;
break;
}
}
if(!have_spell)
return false;
// remove duplicates with levelupSpells if any
if(PetLevelupSpellSet const *levelupSpells = cInfo->family ? sSpellMgr.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)
{
if (itr->second == petDefSpells.spellid[j])
{
petDefSpells.spellid[j] = 0;
break;
}
}
}
}
// skip empty list;
have_spell = false;
for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
{
if(petDefSpells.spellid[j])
{
have_spell = true;
break;
}
}
return have_spell;
}
void SpellMgr::LoadPetDefaultSpells()
{
ASSERT(MAX_CREATURE_SPELL_DATA_SLOT==CREATURE_MAX_SPELLS);
mPetDefaultSpellsMap.clear();
uint32 countCreature = 0;
uint32 countData = 0;
for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i )
{
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
if(!cInfo)
continue;
if(!cInfo->PetSpellDataId)
continue;
// for creature with PetSpellDataId get default pet spells from dbc
CreatureSpellDataEntry const* spellDataEntry = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
if(!spellDataEntry)
continue;
int32 petSpellsId = -(int32)cInfo->PetSpellDataId;
PetDefaultSpellsEntry petDefSpells;
for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
petDefSpells.spellid[j] = spellDataEntry->spellId[j];
if(LoadPetDefaultSpells_helper(cInfo, petDefSpells))
{
mPetDefaultSpellsMap[petSpellsId] = petDefSpells;
++countData;
}
}
// different summon spells
for(uint32 i = 0; i < sSpellStore.GetNumRows(); ++i )
{
SpellEntry const* spellEntry = sSpellStore.LookupEntry(i);
if(!spellEntry)
continue;
for(int k = 0; k < MAX_EFFECT_INDEX; ++k)
{
if(spellEntry->Effect[k]==SPELL_EFFECT_SUMMON || spellEntry->Effect[k]==SPELL_EFFECT_SUMMON_PET)
{
uint32 creature_id = spellEntry->EffectMiscValue[k];
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(creature_id);
if(!cInfo)
continue;
// already loaded
if(cInfo->PetSpellDataId)
continue;
// for creature without PetSpellDataId get default pet spells from creature_template
int32 petSpellsId = cInfo->Entry;
if(mPetDefaultSpellsMap.find(cInfo->Entry) != mPetDefaultSpellsMap.end())
continue;
PetDefaultSpellsEntry petDefSpells;
for(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 addition spells for %u pet spell data entries and %u summonable creature templates", countData, countCreature );
}
/// Some checks for spells, to prevent adding deprecated/broken spells for trainers, spell book, etc
bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg)
{
// not exist
if(!spellInfo)
return false;
bool need_check_reagents = false;
// check effects
for(int i = 0; i < MAX_EFFECT_INDEX; ++i)
{
switch(spellInfo->Effect[i])
{
case 0:
continue;
// craft spell for crafting non-existed item (break client recipes list show)
case SPELL_EFFECT_CREATE_ITEM:
case SPELL_EFFECT_CREATE_ITEM_2:
{
if (spellInfo->EffectItemType[i] == 0)
{
// skip auto-loot crafting spells, its not need explicit item info (but have special fake items sometime)
if (!IsLootCraftingSpell(spellInfo))
{
if(msg)
{
if(pl)
ChatHandler(pl).PSendSysMessage("Craft spell %u not have create item entry.",spellInfo->Id);
else
sLog.outErrorDb("Craft spell %u not have create item entry.",spellInfo->Id);
}
return false;
}
}
// also possible IsLootCraftingSpell case but fake item must exist anyway
else if (!ObjectMgr::GetItemPrototype( spellInfo->EffectItemType[i] ))
{
if(msg)
{
if(pl)
ChatHandler(pl).PSendSysMessage("Craft spell %u create item (Entry: %u) but item does not exist in item_template.",spellInfo->Id,spellInfo->EffectItemType[i]);
else
sLog.outErrorDb("Craft spell %u create item (Entry: %u) but item does not exist in item_template.",spellInfo->Id,spellInfo->EffectItemType[i]);
}
return false;
}
need_check_reagents = true;
break;
}
case SPELL_EFFECT_LEARN_SPELL:
{
SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(spellInfo->EffectTriggerSpell[i]);
if( !IsSpellValid(spellInfo2,pl,msg) )
{
if(msg)
{
if(pl)
ChatHandler(pl).PSendSysMessage("Spell %u learn to broken spell %u, and then...",spellInfo->Id,spellInfo->EffectTriggerSpell[i]);
else
sLog.outErrorDb("Spell %u learn to invalid spell %u, and then...",spellInfo->Id,spellInfo->EffectTriggerSpell[i]);
}
return false;
}
break;
}
}
}
if(need_check_reagents)
{
for(int j = 0; j < MAX_SPELL_REAGENTS; ++j)
{
if(spellInfo->Reagent[j] > 0 && !ObjectMgr::GetItemPrototype( spellInfo->Reagent[j] ))
{
if(msg)
{
if(pl)
ChatHandler(pl).PSendSysMessage("Craft spell %u requires reagent item (Entry: %u) but item does not exist in item_template.",spellInfo->Id,spellInfo->Reagent[j]);
else
sLog.outErrorDb("Craft spell %u requires reagent item (Entry: %u) but item does not exist in item_template.",spellInfo->Id,spellInfo->Reagent[j]);
}
return false;
}
}
}
return true;
}
void SpellMgr::LoadSpellAreas()
{
mSpellAreaMap.clear(); // need for reload case
mSpellAreaForQuestMap.clear();
mSpellAreaForActiveQuestMap.clear();
mSpellAreaForQuestEndMap.clear();
mSpellAreaForAuraMap.clear();
uint32 count = 0;
// 0 1 2 3 4 5 6 7 8
QueryResult *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( (int)result->GetRowCount() );
do
{
Field *fields = result->Fetch();
bar.step();
uint32 spell = fields[0].GetUInt32();
SpellArea spellArea;
spellArea.spellId = spell;
spellArea.areaId = fields[1].GetUInt32();
spellArea.questStart = fields[2].GetUInt32();
spellArea.questStartCanActive = fields[3].GetBool();
spellArea.questEnd = fields[4].GetUInt32();
spellArea.auraSpell = fields[5].GetInt32();
spellArea.raceMask = fields[6].GetUInt32();
spellArea.gender = Gender(fields[7].GetUInt8());
spellArea.autocast = fields[8].GetBool();
if(!sSpellStore.LookupEntry(spell))
{
sLog.outErrorDb("Spell %u listed in `spell_area` does not exist", spell);
continue;
}
{
bool ok = true;
SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId);
for(SpellAreaMap::const_iterator itr = sa_bounds.first; itr != sa_bounds.second; ++itr)
{
if (spellArea.spellId != itr->second.spellId)
continue;
if (spellArea.areaId != itr->second.areaId)
continue;
if (spellArea.questStart != itr->second.questStart)
continue;
if (spellArea.auraSpell != itr->second.auraSpell)
continue;
if ((spellArea.raceMask & itr->second.raceMask) == 0)
continue;
if (spellArea.gender != itr->second.gender)
continue;
// duplicate by requirements
ok =false;
break;
}
if(!ok)
{
sLog.outErrorDb("Spell %u listed in `spell_area` already listed with similar requirements.", spell);
continue;
}
}
if(spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId))
{
sLog.outErrorDb("Spell %u listed in `spell_area` have wrong area (%u) requirement", spell,spellArea.areaId);
continue;
}
if(spellArea.questStart && !sObjectMgr.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(!sObjectMgr.GetQuestTemplate(spellArea.questEnd))
{
sLog.outErrorDb("Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell,spellArea.questEnd);
continue;
}
if(spellArea.questEnd==spellArea.questStart && !spellArea.questStartCanActive)
{
sLog.outErrorDb("Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell,spellArea.questEnd);
continue;
}
}
if(spellArea.auraSpell)
{
SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell));
if(!spellInfo)
{
sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,abs(spellArea.auraSpell));
continue;
}
switch(spellInfo->EffectApplyAuraName[EFFECT_INDEX_0])
{
case SPELL_AURA_DUMMY:
case SPELL_AURA_PHASE:
case SPELL_AURA_GHOST:
break;
default:
sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy/phase/ghost aura in effect 0", spell,abs(spellArea.auraSpell));
continue;
}
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;
}
// continent limitation (virtual continent), ignore for GM
if ((spellInfo->AttributesEx4 & SPELL_ATTR_EX4_CAST_ONLY_IN_OUTLAND) && !(player && player->isGameMaster()))
{
uint32 v_map = GetVirtualMapForMapAndZone(map_id, zone_id);
MapEntry const* mapEntry = sMapStore.LookupEntry(v_map);
if (!mapEntry || mapEntry->addon < 1 || !mapEntry->IsContinent())
return SPELL_FAILED_INCORRECT_AREA;
}
// raid instance limitation
if (spellInfo->AttributesEx6 & SPELL_ATTR_EX6_NOT_IN_RAID_INSTANCE)
{
MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
if (!mapEntry || mapEntry->IsRaid())
return SPELL_FAILED_NOT_IN_RAID_INSTANCE;
}
// DB base check (if non empty then must fit at least single for allow)
SpellAreaMapBounds saBounds = 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
// do not allow spells to be cast in arenas
// - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag
// - with greater than 15 min CD
if ((spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) ||
(GetSpellRecoveryTime(spellInfo) > 15 * MINUTE * IN_MILLISECONDS && !(spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA)))
if (player && player->InArena())
return SPELL_FAILED_NOT_IN_ARENA;
// Spell casted only on battleground
if ((spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND))
if (!player || !player->InBattleGround())
return SPELL_FAILED_ONLY_BATTLEGROUNDS;
switch(spellInfo->Id)
{
// a trinket in alterac valley allows to teleport to the boss
case 22564: // recall
case 22563: // recall
{
if (!player)
return SPELL_FAILED_REQUIRES_AREA;
BattleGround* bg = player->GetBattleGround();
return map_id == 30 && bg
&& bg->GetStatus() != STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
}
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 42792: // Recently Dropped Flag
case 43681: // Inactive
{
return player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_ONLY_BATTLEGROUNDS;
}
case 22011: // Spirit Heal Channel
case 22012: // Spirit Heal
case 24171: // Resurrection Impact Visual
case 44535: // Spirit Heal (mana)
{
MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
if (!mapEntry)
return SPELL_FAILED_INCORRECT_AREA;
return mapEntry->IsBattleGround()? SPELL_CAST_OK : SPELL_FAILED_ONLY_BATTLEGROUNDS;
}
case 44521: // Preparation
{
if (!player)
return SPELL_FAILED_REQUIRES_AREA;
BattleGround* bg = player->GetBattleGround();
return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_ONLY_BATTLEGROUNDS;
}
case 32724: // Gold Team (Alliance)
case 32725: // Green Team (Alliance)
case 35774: // Gold Team (Horde)
case 35775: // Green Team (Horde)
{
return player && player->InArena() ? SPELL_CAST_OK : SPELL_FAILED_ONLY_IN_ARENA;
}
case 32727: // Arena Preparation
{
if (!player)
return SPELL_FAILED_REQUIRES_AREA;
if (!player->InArena())
return SPELL_FAILED_REQUIRES_AREA;
BattleGround* bg = player->GetBattleGround();
return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_ONLY_IN_ARENA;
}
}
return SPELL_CAST_OK;
}
void SpellMgr::LoadSkillLineAbilityMap()
{
mSkillLineAbilityMap.clear();
barGoLink bar( (int)sSkillLineAbilityStore.GetNumRows() );
uint32 count = 0;
for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i)
{
bar.step();
SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i);
if(!SkillInfo)
continue;
mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId,SkillInfo));
++count;
}
sLog.outString();
sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count);
}
void SpellMgr::CheckUsedSpells(char const* table)
{
uint32 countSpells = 0;
uint32 countMasks = 0;
// 0 1 2 3 4 5 6 7 8 9 10 11
QueryResult *result = WorldDatabase.PQuery("SELECT spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMaskB,SpellIcon,SpellVisual,SpellCategory,EffectType,EffectAura,EffectIdx,Name,Code FROM %s",table);
if( !result )
{
barGoLink bar( 1 );
bar.step();
sLog.outString();
sLog.outErrorDb("`%s` table is empty!",table);
return;
}
barGoLink bar( (int)result->GetRowCount() );
do
{
Field *fields = result->Fetch();
bar.step();
uint32 spell = fields[0].GetUInt32();
int32 family = fields[1].GetInt32();
uint64 familyMaskA = fields[2].GetUInt64();
uint32 familyMaskB = fields[3].GetUInt32();
int32 spellIcon = fields[4].GetInt32();
int32 spellVisual = fields[5].GetInt32();
int32 category = fields[6].GetInt32();
int32 effectType = fields[7].GetInt32();
int32 auraType = fields[8].GetInt32();
int32 effectIdx = fields[9].GetInt32();
std::string name = fields[10].GetCppString();
std::string code = fields[11].GetCppString();
// checks of correctness requirements itself
if (family < -1 || family > SPELLFAMILY_PET)
{
sLog.outError("Table '%s' for spell %u have wrong SpellFamily value(%u), skipped.",table,spell,family);
continue;
}
// TODO: spellIcon check need dbc loading
if (spellIcon < -1)
{
sLog.outError("Table '%s' for spell %u have wrong SpellIcon value(%u), skipped.",table,spell,spellIcon);
continue;
}
// TODO: spellVisual check need dbc loading
if (spellVisual < -1)
{
sLog.outError("Table '%s' for spell %u have wrong SpellVisual value(%u), skipped.",table,spell,spellVisual);
continue;
}
// TODO: for spellCategory better check need dbc loading
if (category < -1 || category >=0 && sSpellCategoryStore.find(category) == sSpellCategoryStore.end())
{
sLog.outError("Table '%s' for spell %u have wrong SpellCategory value(%u), skipped.",table,spell,category);
continue;
}
if (effectType < -1 || effectType >= TOTAL_SPELL_EFFECTS)
{
sLog.outError("Table '%s' for spell %u have wrong SpellEffect type value(%u), skipped.",table,spell,effectType);
continue;
}
if (auraType < -1 || auraType >= TOTAL_AURAS)
{
sLog.outError("Table '%s' for spell %u have wrong SpellAura type value(%u), skipped.",table,spell,auraType);
continue;
}
if (effectIdx < -1 || effectIdx >= 3)
{
sLog.outError("Table '%s' for spell %u have wrong EffectIdx value(%u), skipped.",table,spell,effectIdx);
continue;
}
// now checks of requirements
if(spell)
{
++countSpells;
SpellEntry const* spellEntry = sSpellStore.LookupEntry(spell);
if(!spellEntry)
{
sLog.outError("Spell %u '%s' not exist but used in %s.",spell,name.c_str(),code.c_str());
continue;
}
if(family >= 0 && spellEntry->SpellFamilyName != family)
{
sLog.outError("Spell %u '%s' family(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->SpellFamilyName,family,code.c_str());
continue;
}
if(familyMaskA != UI64LIT(0xFFFFFFFFFFFFFFFF) || familyMaskB != 0xFFFFFFFF)
{
if(familyMaskA == UI64LIT(0x0000000000000000) && familyMaskB == 0x00000000)
{
if(spellEntry->SpellFamilyFlags != 0 || spellEntry->SpellFamilyFlags2 != 0)
{
sLog.outError("Spell %u '%s' not fit to (" I64FMT "," I32FMT ") but used in %s.",
spell, name.c_str(), familyMaskA, familyMaskB, code.c_str());
continue;
}
}
else
{
if((spellEntry->SpellFamilyFlags & familyMaskA)==0 && (spellEntry->SpellFamilyFlags2 & familyMaskB)==0)
{
sLog.outError("Spell %u '%s' not fit to (" I64FMT "," I32FMT ") but used in %s.",spell,name.c_str(),familyMaskA,familyMaskB,code.c_str());
continue;
}
}
}
if(spellIcon >= 0 && spellEntry->SpellIconID != spellIcon)
{
sLog.outError("Spell %u '%s' icon(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->SpellIconID,spellIcon,code.c_str());
continue;
}
if(spellVisual >= 0 && spellEntry->SpellVisual[0] != spellVisual)
{
sLog.outError("Spell %u '%s' visual(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->SpellVisual[0],spellVisual,code.c_str());
continue;
}
if(category >= 0 && spellEntry->Category != category)
{
sLog.outError("Spell %u '%s' category(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->Category,category,code.c_str());
continue;
}
if (effectIdx >= EFFECT_INDEX_0)
{
if(effectType >= 0 && spellEntry->Effect[effectIdx] != effectType)
{
sLog.outError("Spell %u '%s' effect%d <> %u but used in %s.",spell,name.c_str(),effectIdx+1,effectType,code.c_str());
continue;
}
if(auraType >= 0 && spellEntry->EffectApplyAuraName[effectIdx] != auraType)
{
sLog.outError("Spell %u '%s' aura%d <> %u but used in %s.",spell,name.c_str(),effectIdx+1,auraType,code.c_str());
continue;
}
}
else
{
if(effectType >= 0 && !IsSpellHaveEffect(spellEntry,SpellEffects(effectType)))
{
sLog.outError("Spell %u '%s' not have effect %u but used in %s.",spell,name.c_str(),effectType,code.c_str());
continue;
}
if(auraType >= 0 && !IsSpellHaveAura(spellEntry,AuraType(auraType)))
{
sLog.outError("Spell %u '%s' not have aura %u but used in %s.",spell,name.c_str(),auraType,code.c_str());
continue;
}
}
}
else
{
++countMasks;
bool found = false;
for(uint32 spellId = 1; spellId < sSpellStore.GetNumRows(); ++spellId)
{
SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellId);
if(!spellEntry)
continue;
if(family >=0 && spellEntry->SpellFamilyName != family)
continue;
if(familyMaskA != UI64LIT(0xFFFFFFFFFFFFFFFF) || familyMaskB != 0xFFFFFFFF)
{
if(familyMaskA == UI64LIT(0x0000000000000000) && familyMaskB == 0x00000000)
{
if(spellEntry->SpellFamilyFlags != 0 || spellEntry->SpellFamilyFlags2 != 0)
continue;
}
else
{
if((spellEntry->SpellFamilyFlags & familyMaskA)==0 && (spellEntry->SpellFamilyFlags2 & familyMaskB)==0)
continue;
}
}
if(spellIcon >= 0 && spellEntry->SpellIconID != spellIcon)
continue;
if(spellVisual >= 0 && spellEntry->SpellVisual[0] != spellVisual)
continue;
if(category >= 0 && spellEntry->Category != category)
continue;
if(effectIdx >= 0)
{
if(effectType >=0 && spellEntry->Effect[effectIdx] != effectType)
continue;
if(auraType >=0 && spellEntry->EffectApplyAuraName[effectIdx] != auraType)
continue;
}
else
{
if(effectType >=0 && !IsSpellHaveEffect(spellEntry,SpellEffects(effectType)))
continue;
if(auraType >=0 && !IsSpellHaveAura(spellEntry,AuraType(auraType)))
continue;
}
found = true;
break;
}
if(!found)
{
if(effectIdx >= 0)
sLog.outError("Spells '%s' not found for family %i (" I64FMT "," I32FMT ") icon(%i) visual(%i) category(%i) effect%d(%i) aura%d(%i) but used in %s",
name.c_str(),family,familyMaskA,familyMaskB,spellIcon,spellVisual,category,effectIdx+1,effectType,effectIdx+1,auraType,code.c_str());
else
sLog.outError("Spells '%s' not found for family %i (" I64FMT "," I32FMT ") icon(%i) visual(%i) category(%i) effect(%i) aura(%i) but used in %s",
name.c_str(),family,familyMaskA,familyMaskB,spellIcon,spellVisual,category,effectType,auraType,code.c_str());
continue;
}
}
} while( result->NextRow() );
delete result;
sLog.outString();
sLog.outString( ">> Checked %u spells and %u spell masks", countSpells, countMasks );
}
DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered)
{
// Explicit Diminishing Groups
switch(spellproto->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
// some generic arena related spells have by some strange reason MECHANIC_TURN
if (spellproto->Mechanic == MECHANIC_TURN)
return DIMINISHING_NONE;
break;
case SPELLFAMILY_MAGE:
// Dragon's Breath
if (spellproto->SpellIconID == 1548)
return DIMINISHING_DISORIENT;
break;
case SPELLFAMILY_ROGUE:
{
// Blind
if (spellproto->SpellFamilyFlags & UI64LIT(0x00001000000))
return DIMINISHING_FEAR_CHARM_BLIND;
// Cheap Shot
else if (spellproto->SpellFamilyFlags & UI64LIT(0x00000000400))
return DIMINISHING_CHEAPSHOT_POUNCE;
// Crippling poison - Limit to 10 seconds in PvP (No SpellFamilyFlags)
else if (spellproto->SpellIconID == 163)
return DIMINISHING_LIMITONLY;
break;
}
case SPELLFAMILY_HUNTER:
{
// Freezing Trap & Freezing Arrow & Wyvern Sting
if (spellproto->SpellIconID == 180 || spellproto->SpellIconID == 1721)
return DIMINISHING_DISORIENT;
break;
}
case SPELLFAMILY_WARLOCK:
{
// Curses/etc
if (spellproto->SpellFamilyFlags & UI64LIT(0x00080000000))
return DIMINISHING_LIMITONLY;
break;
}
case SPELLFAMILY_DRUID:
{
// Cyclone
if (spellproto->SpellFamilyFlags & UI64LIT(0x02000000000))
return DIMINISHING_CYCLONE;
// Pounce
else if (spellproto->SpellFamilyFlags & UI64LIT(0x00000020000))
return DIMINISHING_CHEAPSHOT_POUNCE;
// Faerie Fire
else if (spellproto->SpellFamilyFlags & UI64LIT(0x00000000400))
return DIMINISHING_LIMITONLY;
break;
}
case SPELLFAMILY_WARRIOR:
{
// Hamstring - limit duration to 10s in PvP
if (spellproto->SpellFamilyFlags & UI64LIT(0x00000000002))
return DIMINISHING_LIMITONLY;
break;
}
case SPELLFAMILY_PRIEST:
{
// Shackle Undead
if (spellproto->SpellIconID == 27)
return DIMINISHING_DISORIENT;
break;
}
case SPELLFAMILY_DEATHKNIGHT:
{
// Hungering Cold (no flags)
if (spellproto->SpellIconID == 2797)
return DIMINISHING_DISORIENT;
break;
}
default:
break;
}
// Get by mechanic
uint32 mechanic = GetAllSpellMechanicMask(spellproto);
if (!mechanic)
return DIMINISHING_NONE;
if (mechanic & ((1<<(MECHANIC_STUN-1))|(1<<(MECHANIC_SHACKLE-1))))
return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN;
if (mechanic & ((1<<(MECHANIC_SLEEP-1))|(1<<(MECHANIC_FREEZE-1))))
return DIMINISHING_FREEZE_SLEEP;
if (mechanic & ((1<<(MECHANIC_KNOCKOUT-1))|(1<<(MECHANIC_POLYMORPH-1))|(1<<(MECHANIC_SAPPED-1))))
return DIMINISHING_DISORIENT;
if (mechanic & (1<<(MECHANIC_ROOT-1)))
return triggered ? DIMINISHING_TRIGGER_ROOT : DIMINISHING_CONTROL_ROOT;
if (mechanic & ((1<<(MECHANIC_FEAR-1))|(1<<(MECHANIC_CHARM-1))))
return DIMINISHING_FEAR_CHARM_BLIND;
if (mechanic & ((1<<(MECHANIC_SILENCE-1))|(1<<(MECHANIC_INTERRUPT-1))))
return DIMINISHING_SILENCE;
if (mechanic & (1<<(MECHANIC_DISARM-1)))
return DIMINISHING_DISARM;
if (mechanic & (1<<(MECHANIC_BANISH-1)))
return DIMINISHING_BANISH;
if (mechanic & (1<<(MECHANIC_HORROR-1)))
return DIMINISHING_HORROR;
return DIMINISHING_NONE;
}
int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry const* spellproto)
{
if(!IsDiminishingReturnsGroupDurationLimited(group))
return 0;
// Explicit diminishing duration
switch(spellproto->SpellFamilyName)
{
case SPELLFAMILY_HUNTER:
{
// Wyvern Sting
if (spellproto->SpellFamilyFlags & UI64LIT(0x0000100000000000))
return 6000;
break;
}
case SPELLFAMILY_PALADIN:
{
// Repentance - limit to 6 seconds in PvP
if (spellproto->SpellFamilyFlags & UI64LIT(0x00000000004))
return 6000;
break;
}
case SPELLFAMILY_DRUID:
{
// Faerie Fire - limit to 40 seconds in PvP (3.1)
if (spellproto->SpellFamilyFlags & UI64LIT(0x00000000400))
return 40000;
break;
}
default:
break;
}
return 10000;
}
bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group)
{
switch(group)
{
case DIMINISHING_CONTROL_STUN:
case DIMINISHING_TRIGGER_STUN:
case DIMINISHING_CONTROL_ROOT:
case DIMINISHING_TRIGGER_ROOT:
case DIMINISHING_FEAR_CHARM_BLIND:
case DIMINISHING_DISORIENT:
case DIMINISHING_CHEAPSHOT_POUNCE:
case DIMINISHING_FREEZE_SLEEP:
case DIMINISHING_CYCLONE:
case DIMINISHING_BANISH:
case DIMINISHING_LIMITONLY:
return true;
default:
return false;
}
return false;
}
DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group)
{
switch(group)
{
case DIMINISHING_CYCLONE:
case DIMINISHING_TRIGGER_STUN:
case DIMINISHING_CONTROL_STUN:
return DRTYPE_ALL;
case DIMINISHING_CONTROL_ROOT:
case DIMINISHING_TRIGGER_ROOT:
case DIMINISHING_FEAR_CHARM_BLIND:
case DIMINISHING_DISORIENT:
case DIMINISHING_SILENCE:
case DIMINISHING_DISARM:
case DIMINISHING_HORROR:
case DIMINISHING_FREEZE_SLEEP:
case DIMINISHING_BANISH:
case DIMINISHING_CHEAPSHOT_POUNCE:
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, EFFECT_INDEX_0);
else
// not have expected aura
return !player->HasAura(-auraSpell, EFFECT_INDEX_0);
}
return true;
}
SpellEntry const* GetSpellEntryByDifficulty(uint32 id, Difficulty difficulty)
{
SpellDifficultyEntry const* spellDiff = sSpellDifficultyStore.LookupEntry(id);
if (!spellDiff)
return NULL;
if (!spellDiff->spellId[difficulty])
return NULL;
SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellDiff->spellId[difficulty]);
return spellEntry;
}