/* * Copyright (C) 2005-2009 MaNGOS * * 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" 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) { 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; } 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, uint32 effIndex_1, uint32 spellId_2, uint32 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, uint32 effIndex_1, uint32 spellId_2, uint32 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 < 3; ++i) { switch(spellInfo->EffectApplyAuraName[i]) { // Food case SPELL_AURA_MOD_REGEN: case SPELL_AURA_OBS_MOD_HEALTH: food = true; break; // Drink case SPELL_AURA_MOD_POWER_REGEN: case SPELL_AURA_OBS_MOD_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[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; if( spellInfo->SpellFamilyFlags2 & 0x00000002 ) return SPELL_TRACKER; 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; if ((spellInfo->SpellFamilyFlags & UI64LIT(0x00000820180400)) && (spellInfo->AttributesEx3 & 0x200)) 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 spellmgr.GetSpellElixirSpecific(spellInfo->Id); case SPELLFAMILY_DEATHKNIGHT: if (spellInfo->Category == 47) return SPELL_PRESENCE; break; } // elixirs can have different families, but potion most ofc. if(SpellSpecific sp = spellmgr.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_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_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_ASPECT: 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) { // non-positive targets switch(targetA) { 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; 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 IsPositiveEffect(uint32 spellId, uint32 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: return false; // AB Effect 000 default: break; } break; // always positive effects (check before target checks that provided non-positive result in some case for positive effects) case SPELL_EFFECT_HEAL: case SPELL_EFFECT_LEARN_SPELL: case SPELL_EFFECT_SKILL_STEP: case SPELL_EFFECT_HEAL_PCT: case SPELL_EFFECT_ENERGIZE_PCT: return true; // non-positive aura use case SPELL_EFFECT_APPLY_AURA: case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: { switch(spellproto->EffectApplyAuraName[effIndex]) { case SPELL_AURA_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 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 bas point sign (negative -> negative) case SPELL_AURA_MOD_STAT: case SPELL_AURA_MOD_SKILL: case SPELL_AURA_MOD_HEALING_PCT: case SPELL_AURA_MOD_HEALING_DONE: 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: if(spellproto->CalculateSimpleValue(effIndex) > 0) return true; // some expected positive spells have SPELL_ATTR_EX_NEGATIVE break; case SPELL_AURA_ADD_TARGET_TRIGGER: return true; case SPELL_AURA_PERIODIC_TRIGGER_SPELL: 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 < 3; ++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,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==0 && spellproto->Effect[1]==0 && spellproto->Effect[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: if(spellproto->Id == 24740) // Wisp Costume return true; return false; case SPELL_AURA_MOD_ROOT: case SPELL_AURA_MOD_SILENCE: case SPELL_AURA_GHOST: case SPELL_AURA_PERIODIC_LEECH: case SPELL_AURA_MOD_STALKED: case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: return false; case SPELL_AURA_PERIODIC_DAMAGE: // used in positive spells also. // part of negative spell if casted at self (prevent cancel) if(spellproto->EffectImplicitTargetA[effIndex] == TARGET_SELF) 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) return false; // but not this if this first effect (don't found batter check) if(spellproto->Attributes & 0x4000000 && effIndex==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 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 < 3; ++i) if (!IsPositiveEffect(spellId, 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[0]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[1]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[2]==SPELL_EFFECT_LEARN_SPELL) ) return SPELL_CAST_OK; uint32 stanceMask = (form ? 1 << (form - 1) : 0); if (stanceMask & spellInfo->StancesNot) // can explicitly not be casted in this stance return SPELL_FAILED_NOT_SHAPESHIFT; if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance return SPELL_CAST_OK; bool actAsShifted = false; 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( result->GetRowCount() ); do { Field *fields = result->Fetch(); bar.step(); ++count; 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(); SpellEntry const* spellInfo = sSpellStore.LookupEntry(Spell_ID); if(!spellInfo) { sLog.outErrorDb("Spell (ID:%u) listed in `spell_target_position` does not exist.",Spell_ID); continue; } bool found = false; for(int i = 0; i < 3; ++i) { if( spellInfo->EffectImplicitTargetA[i]==TARGET_TABLE_X_Y_Z_COORDINATES || spellInfo->EffectImplicitTargetB[i]==TARGET_TABLE_X_Y_Z_COORDINATES ) { 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; } 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; } mSpellTargetPositions[Spell_ID] = st; } while( result->NextRow() ); delete result; sLog.outString(); sLog.outString( ">> Loaded %u spell teleport coordinates", count ); } bool SpellMgr::IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const { // false for spellInfo == NULL if (!spellInfo || !mod) return false; SpellEntry const *affect_spell = sSpellStore.LookupEntry(mod->spellId); // False if affect_spell == NULL or spellFamily not equal if (!affect_spell || affect_spell->SpellFamilyName != spellInfo->SpellFamilyName) return false; // true if (mod->mask & spellInfo->SpellFamilyFlags || mod->mask2 & spellInfo->SpellFamilyFlags2) return true; return false; } struct DoSpellProcEvent { DoSpellProcEvent(SpellProcEventEntry const& _spe) : spe(_spe) {} void operator() (uint32 spell_id) { spellmgr.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( 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) { spellmgr.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( 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) { spellmgr.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( 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 if ((procEvent_procEx & PROC_EX_NORMAL_HIT) && !active) return false; // Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other) if (procEvent_procEx & procExtra) return true; } 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( 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( 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(spellmgr.IsSkillBonusSpell(spellInfo->Id)) return false; // All stance spells. if any better way, change it. for (int i = 0; i < 3; ++i) { switch(spellInfo->SpellFamilyName) { case SPELLFAMILY_PALADIN: // Paladin aura Spell if (spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AREA_AURA_RAID) return false; break; case SPELLFAMILY_DRUID: // Druid form Spell if (spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT) return false; break; case SPELLFAMILY_ROGUE: // Rogue Stealth if (spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT) return false; } } return true; } bool SpellMgr::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 and (Primal Instinct (Idol of Terror) triggering spell or Agility) if( spellInfo_1->SpellIconID == 240 && spellInfo_2->SpellIconID == 240 && ( spellInfo_1->SpellVisual[0] == 0 && spellInfo_2->SpellVisual[0] == 78 || spellInfo_2->SpellVisual[0] == 0 && spellInfo_1->SpellVisual[0] == 78 ) ) 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; // 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; 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; 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; } // 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; // 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; } // 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; } // 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; } // 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) { // 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; } 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 < 3; ++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; for (int i = 0; i < 3; ++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; return true; } bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); if(!spellInfo) return false; if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL) return false; uint32 skill = spellInfo->EffectMiscValue[1]; return IsProfessionOrRidingSkill(skill); } bool SpellMgr::IsProfessionSpell(uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); if(!spellInfo) return false; if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL) return false; uint32 skill = spellInfo->EffectMiscValue[1]; return IsProfessionSkill(skill); } bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); if(!spellInfo) return false; if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL) return false; uint32 skill = spellInfo->EffectMiscValue[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<3;++i) { if( IsPositiveEffect(spellInfo->Id, i) && ( spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA || spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY || spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_RAID ) ) { needRankSelection = true; break; } } // not required if(!needRankSelection) return spellInfo; for(uint32 nextSpellId = spellInfo->Id; nextSpellId != 0; nextSpellId = GetPrevSpellInChain(nextSpellId)) { SpellEntry const *nextSpellInfo = sSpellStore.LookupEntry(nextSpellId); if(!nextSpellInfo) break; // if found appropriate level if(playerLevel + 10 >= nextSpellInfo->spellLevel) return nextSpellInfo; // one rank less then } // not found return NULL; } void SpellMgr::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( 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 < 3; ++i) { if(entry->Effect[i]==SPELL_EFFECT_SKILL) { SpellLearnSkillNode dbc_node; dbc_node.skill = entry->EffectMiscValue[i]; if ( dbc_node.skill != SKILL_RIDING ) dbc_node.value = 1; else dbc_node.value = entry->CalculateSimpleValue(i)*75; dbc_node.maxvalue = entry->CalculateSimpleValue(i)*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( 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 < 3; ++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(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 <3; ++i) { if( spellProto->EffectImplicitTargetA[i]==TARGET_SCRIPT || spellProto->EffectImplicitTargetB[i]==TARGET_SCRIPT || spellProto->EffectImplicitTargetA[i]==TARGET_SCRIPT_COORDINATES || spellProto->EffectImplicitTargetB[i]==TARGET_SCRIPT_COORDINATES ) { 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).",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; } switch(type) { case SPELL_TARGET_TYPE_GAMEOBJECT: { if( targetEntry==0 ) break; if(!sGOStorage.LookupEntry(targetEntry)) { sLog.outErrorDb("Table `spell_script_target`: gameobject template entry %u does not exist.",targetEntry); continue; } break; } default: { if( targetEntry==0 ) { sLog.outErrorDb("Table `spell_script_target`: target entry == 0 for not GO target type (%u).",type); continue; } if(!sCreatureStorage.LookupEntry(targetEntry)) { sLog.outErrorDb("Table `spell_script_target`: creature template entry %u does not exist.",targetEntry); continue; } const CreatureInfo* cInfo = sCreatureStorage.LookupEntry(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; } 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<3; ++j) { if( spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT || spellInfo->EffectImplicitTargetA[j] != TARGET_SELF && spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT ) { SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(spellInfo->Id); SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(spellInfo->Id); if(lower==upper) { sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = %u (TARGET_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( result->GetRowCount() ); do { Field *fields = result->Fetch(); bar.step(); uint32 spell = fields[0].GetUInt32(); uint8 eff = fields[1].GetUInt8(); uint32 pet = fields[2].GetUInt32(); uint32 aura = fields[3].GetUInt32(); SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff); if(itr != mSpellPetAuraMap.end()) { itr->second.AddAura(pet, aura); } else { SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); if (!spellInfo) { sLog.outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell); continue; } if (spellInfo->Effect[eff] != SPELL_EFFECT_DUMMY && (spellInfo->Effect[eff] != SPELL_EFFECT_APPLY_AURA || spellInfo->EffectApplyAuraName[eff] != SPELL_AURA_DUMMY)) { sLog.outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell); continue; } SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(aura); if (!spellInfo2) { sLog.outErrorDb("Aura %u listed in `spell_pet_auras` does not exist", aura); continue; } PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[eff] == TARGET_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 ? spellmgr.GetPetLevelupSpellList(cInfo->family) : NULL) { for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j) { if(!petDefSpells.spellid[j]) continue; for(PetLevelupSpellSet::const_iterator itr = levelupSpells->begin(); itr != levelupSpells->end(); ++itr) { 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(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 < 3; ++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(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<3; ++i) { switch(spellInfo->Effect[i]) { case 0: continue; // craft spell for crafting non-existed item (break client recipes list show) case SPELL_EFFECT_CREATE_ITEM: case SPELL_EFFECT_CREATE_ITEM_2: { if(!ObjectMgr::GetItemPrototype( spellInfo->EffectItemType[i] )) { if(msg) { if(pl) ChatHandler(pl).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]); else sLog.outErrorDb("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]); } return false; } need_check_reagents = true; break; } case SPELL_EFFECT_LEARN_SPELL: { SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(spellInfo->EffectTriggerSpell[i]); if( !IsSpellValid(spellInfo2,pl,msg) ) { if(msg) { if(pl) ChatHandler(pl).PSendSysMessage("Spell %u learn to broken spell %u, and then...",spellInfo->Id,spellInfo->EffectTriggerSpell[i]); else sLog.outErrorDb("Spell %u learn to invalid spell %u, and then...",spellInfo->Id,spellInfo->EffectTriggerSpell[i]); } return false; } break; } } } if(need_check_reagents) { for(int j = 0; j < 8; ++j) { if(spellInfo->Reagent[j] > 0 && !ObjectMgr::GetItemPrototype( spellInfo->Reagent[j] )) { if(msg) { if(pl) ChatHandler(pl).PSendSysMessage("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->Reagent[j]); else sLog.outErrorDb("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->Reagent[j]); } return false; } } } return true; } void SpellMgr::LoadSpellAreas() { mSpellAreaMap.clear(); // need for reload case mSpellAreaForQuestMap.clear(); mSpellAreaForActiveQuestMap.clear(); mSpellAreaForQuestEndMap.clear(); mSpellAreaForAuraMap.clear(); uint32 count = 0; // 0 1 2 3 4 5 6 7 8 QueryResult *result = WorldDatabase.Query("SELECT spell, area, quest_start, quest_start_active, quest_end, aura_spell, racemask, gender, autocast FROM spell_area"); if( !result ) { barGoLink bar( 1 ); bar.step(); sLog.outString(); sLog.outString( ">> Loaded %u spell area requirements", count ); return; } barGoLink bar( result->GetRowCount() ); do { Field *fields = result->Fetch(); bar.step(); uint32 spell = fields[0].GetUInt32(); SpellArea spellArea; spellArea.spellId = spell; spellArea.areaId = fields[1].GetUInt32(); spellArea.questStart = fields[2].GetUInt32(); spellArea.questStartCanActive = fields[3].GetBool(); spellArea.questEnd = fields[4].GetUInt32(); spellArea.auraSpell = fields[5].GetInt32(); spellArea.raceMask = fields[6].GetUInt32(); spellArea.gender = Gender(fields[7].GetUInt8()); spellArea.autocast = fields[8].GetBool(); if(!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 && !objmgr.GetQuestTemplate(spellArea.questStart)) { sLog.outErrorDb("Spell %u listed in `spell_area` have wrong start quest (%u) requirement", spell,spellArea.questStart); continue; } if(spellArea.questEnd) { if(!objmgr.GetQuestTemplate(spellArea.questEnd)) { sLog.outErrorDb("Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell,spellArea.questEnd); continue; } if(spellArea.questEnd==spellArea.questStart && !spellArea.questStartCanActive) { sLog.outErrorDb("Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell,spellArea.questEnd); continue; } } if(spellArea.auraSpell) { SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell)); if(!spellInfo) { sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,abs(spellArea.auraSpell)); continue; } switch(spellInfo->EffectApplyAuraName[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) if (spellInfo->AttributesEx4 & SPELL_ATTR_EX4_CAST_ONLY_IN_OUTLAND) { uint32 v_map = GetVirtualMapForMapAndZone(map_id, zone_id); MapEntry const* mapEntry = sMapStore.LookupEntry(v_map); if(!mapEntry || mapEntry->addon < 1 || !mapEntry->IsContinent()) return SPELL_FAILED_INCORRECT_AREA; } // DB base check (if non empty then must fit at least single for allow) SpellAreaMapBounds saBounds = spellmgr.GetSpellAreaMapBounds(spellInfo->Id); if (saBounds.first != saBounds.second) { for(SpellAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) { if(itr->second.IsFitToRequirements(player,zone_id,area_id)) return SPELL_CAST_OK; } return SPELL_FAILED_INCORRECT_AREA; } // bg spell checks switch(spellInfo->Id) { case 23333: // Warsong Flag case 23335: // Silverwing Flag return map_id == 489 && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; case 34976: // Netherstorm Flag return map_id == 566 && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; case 2584: // Waiting to Resurrect case 22011: // Spirit Heal Channel case 22012: // Spirit Heal case 24171: // Resurrection Impact Visual case 42792: // Recently Dropped Flag case 43681: // Inactive case 44535: // Spirit Heal (mana) { MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; return mapEntry->IsBattleGround() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 44521: // Preparation { if (!player) return SPELL_FAILED_REQUIRES_AREA; MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; if (!mapEntry->IsBattleGround()) return SPELL_FAILED_REQUIRES_AREA; BattleGround* bg = player->GetBattleGround(); return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 32724: // Gold Team (Alliance) case 32725: // Green Team (Alliance) case 35774: // Gold Team (Horde) case 35775: // Green Team (Horde) { MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; return mapEntry->IsBattleArena() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 32727: // Arena Preparation { if (!player) return SPELL_FAILED_REQUIRES_AREA; MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; if (!mapEntry->IsBattleArena()) return SPELL_FAILED_REQUIRES_AREA; BattleGround* bg = player->GetBattleGround(); return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } } return SPELL_CAST_OK; } void SpellMgr::LoadSkillLineAbilityMap() { mSkillLineAbilityMap.clear(); barGoLink bar( sSkillLineAbilityStore.GetNumRows() ); uint32 count = 0; for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) { bar.step(); SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i); if(!SkillInfo) continue; mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId,SkillInfo)); ++count; } sLog.outString(); sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count); } 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( 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 >= 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_ROGUE: { // Blind if (spellproto->SpellFamilyFlags & UI64LIT(0x00001000000)) return DIMINISHING_FEAR_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_WARLOCK: { // Curses/etc if (spellproto->SpellFamilyFlags & UI64LIT(0x00080000000)) return DIMINISHING_LIMITONLY; // Seduction else if (spellproto->SpellFamilyFlags & UI64LIT(0x00040000000)) return DIMINISHING_CHARM; 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: { // Vampiric Embrace if ((spellproto->SpellFamilyFlags & UI64LIT(0x00000000004)) && spellproto->SpellIconID == 150) return DIMINISHING_LIMITONLY; break; } case SPELLFAMILY_DEATHKNIGHT: { // Hungering Cold (no flags) if (spellproto->SpellIconID == 2797) return DIMINISHING_POLYMORPH_GOUGE_SAP; break; } default: break; } // Get by mechanic uint32 mechanic = GetAllSpellMechanicMask(spellproto); if (mechanic == MECHANIC_NONE) return DIMINISHING_NONE; if (mechanic & ((1<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; } case SPELLFAMILY_PRIEST: { // Vampiric Embrace - limit to 60 seconds in PvP (3.1) if ((spellproto->SpellFamilyFlags & UI64LIT(0x00000000004)) && spellproto->SpellIconID == 150) return 60000; 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_BLIND: case DIMINISHING_CHARM: case DIMINISHING_POLYMORPH_GOUGE_SAP: 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: case DIMINISHING_CHEAPSHOT_POUNCE: return DRTYPE_ALL; case DIMINISHING_CONTROL_ROOT: case DIMINISHING_TRIGGER_ROOT: case DIMINISHING_FEAR_BLIND: case DIMINISHING_CHARM: case DIMINISHING_POLYMORPH_GOUGE_SAP: case DIMINISHING_SILENCE: case DIMINISHING_DISARM: case DIMINISHING_DEATHCOIL: case DIMINISHING_FREEZE_SLEEP: case DIMINISHING_BANISH: 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,0); else // not have expected aura return !player->HasAura(-auraSpell,0); } return true; }