diff options
author | maximius <none@none> | 2009-10-17 15:51:44 -0700 |
---|---|---|
committer | maximius <none@none> | 2009-10-17 15:51:44 -0700 |
commit | e585187b248f48b3c6e9247b49fa07c6565d65e5 (patch) | |
tree | 637c5b7ddacf41040bef4ea4f75a97da64c6a9bc /src/game/SpellMgr.cpp | |
parent | 26b5e033ffde3d161382fc9addbfa99738379641 (diff) |
*Backed out changeset 3be01fb200a5
--HG--
branch : trunk
Diffstat (limited to 'src/game/SpellMgr.cpp')
-rw-r--r-- | src/game/SpellMgr.cpp | 500 |
1 files changed, 499 insertions, 1 deletions
diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 3f11cfccf80..ef42f0d50ac 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -17,6 +17,7 @@ * 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" @@ -27,9 +28,11 @@ #include "Spell.h" #include "BattleGroundMgr.h" #include "CreatureAI.h" + bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS]; SpellEffectTargetTypes EffectTargetType[TOTAL_SPELL_EFFECTS]; SpellSelectTargetTypes SpellTargetType[TOTAL_SPELL_TARGETS]; + SpellMgr::SpellMgr() { for(int i = 0; i < TOTAL_SPELL_EFFECTS; ++i) @@ -94,6 +97,7 @@ SpellMgr::SpellMgr() break; } } + for(int i = 0; i < TOTAL_SPELL_TARGETS; ++i) { switch(i) @@ -206,6 +210,7 @@ SpellMgr::SpellMgr() SpellTargetType[i] = TARGET_TYPE_DEFAULT; } } + for(int i = 0; i < TOTAL_SPELL_TARGETS; ++i) { switch(i) @@ -234,14 +239,17 @@ SpellMgr::SpellMgr() } } + SpellMgr::~SpellMgr() { } + SpellMgr& SpellMgr::Instance() { static SpellMgr spellMgr; return spellMgr; } + bool SpellMgr::IsSrcTargetSpell(SpellEntry const *spellInfo) const { for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) @@ -251,6 +259,7 @@ bool SpellMgr::IsSrcTargetSpell(SpellEntry const *spellInfo) const } return false; } + int32 GetSpellDuration(SpellEntry const *spellInfo) { if(!spellInfo) @@ -260,6 +269,7 @@ int32 GetSpellDuration(SpellEntry const *spellInfo) return 0; return (du->Duration[0] == -1) ? -1 : abs(du->Duration[0]); } + int32 GetSpellMaxDuration(SpellEntry const *spellInfo) { if(!spellInfo) @@ -269,6 +279,7 @@ int32 GetSpellMaxDuration(SpellEntry const *spellInfo) return 0; return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]); } + bool GetDispelChance(Unit* caster, uint32 spellId) { // we assume that aura dispel chance is 100% on start @@ -283,19 +294,26 @@ bool GetDispelChance(Unit* caster, uint32 spellId) // Try dispel return !roll_chance_i(miss_chance); } + uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell * spell) { SpellCastTimesEntry const *spellCastTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex); + // not all spells have cast time index and this is all is pasiive abilities if(!spellCastTimeEntry) return 0; + int32 castTime = spellCastTimeEntry->CastTime; + if (spell && spell->GetCaster()) spell->GetCaster()->ModSpellCastTime(spellInfo, castTime, spell); + if (spellInfo->Attributes & SPELL_ATTR_REQ_AMMO && (!spell || !(spell->IsAutoRepeat()))) castTime += 500; + return (castTime > 0) ? uint32(castTime) : 0; } + bool IsPassiveSpell(uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); @@ -305,6 +323,7 @@ bool IsPassiveSpell(uint32 spellId) return true; return false; } + bool IsAutocastableSpell(uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); @@ -316,10 +335,12 @@ bool IsAutocastableSpell(uint32 spellId) return false; return true; } + bool IsHigherHankOfSpell(uint32 spellId_1, uint32 spellId_2) { return spellmgr.GetSpellRank(spellId_1)<spellmgr.GetSpellRank(spellId_2); } + uint32 CalculatePowerCost(SpellEntry const * spellInfo, Unit const * caster, SpellSchoolMask schoolMask) { // Spell drain all exist power on cast (Only paladin lay of Hands) @@ -334,6 +355,7 @@ uint32 CalculatePowerCost(SpellEntry const * spellInfo, Unit const * caster, Spe sLog.outError("CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id); return 0; } + // Base powerCost int32 powerCost = spellInfo->manaCost; // PCT cost from total amount @@ -372,19 +394,23 @@ uint32 CalculatePowerCost(SpellEntry const * spellInfo, Unit const * caster, Spe // Apply cost mod by spell if(Player* modOwner = caster->GetSpellModOwner()) modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, powerCost); + if(spellInfo->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION) powerCost = int32(powerCost/ (1.117f* spellInfo->spellLevel / caster->getLevel() -0.1327f)); + // PCT mod from user auras by school powerCost = int32(powerCost * (1.0f+caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+school))); if (powerCost < 0) powerCost = 0; return powerCost; } + AuraState GetSpellAuraState(SpellEntry const * spellInfo) { // Seals if (IsSealSpell(spellInfo)) return AURA_STATE_JUDGEMENT; + // Conflagrate aura state on Immolate and Shadowflame if (spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && // Immolate @@ -392,27 +418,35 @@ AuraState GetSpellAuraState(SpellEntry const * spellInfo) // Shadowflame (spellInfo->SpellFamilyFlags[2] & 2))) return AURA_STATE_CONFLAGRATE; + // Faerie Fire (druid versions) if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x400) return AURA_STATE_FAERIE_FIRE; + // Sting (hunter's pet ability) if (spellInfo->Category == 1133) return AURA_STATE_FAERIE_FIRE; + // Victorious if (spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellFamilyFlags[1] & 0x00040000) return AURA_STATE_WARRIOR_VICTORY_RUSH; + // Swiftmend state on Regrowth & Rejuvenation if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x50 ) return AURA_STATE_SWIFTMEND; + // Deadly poison aura state if(spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags[0] & 0x10000) return AURA_STATE_DEADLY_POISON; + // Enrage aura state if(spellInfo->Dispel == DISPEL_ENRAGE) return AURA_STATE_ENRAGE; + // Bleeding aura state if (GetAllSpellMechanicMask(spellInfo) & 1<<MECHANIC_BLEED) return AURA_STATE_BLEEDING; + if(GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) { for (uint8 i = 0;i<MAX_SPELL_EFFECTS;++i) @@ -424,11 +458,13 @@ AuraState GetSpellAuraState(SpellEntry const * spellInfo) } return AURA_STATE_NONE; } + SpellSpecific GetSpellSpecific(uint32 spellId) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); if(!spellInfo) return SPELL_NORMAL; + switch(spellInfo->SpellFamilyName) { case SPELLFAMILY_GENERIC: @@ -456,6 +492,7 @@ SpellSpecific GetSpellSpecific(uint32 spellId) break; } } + if(food && drink) return SPELL_FOOD_AND_DRINK; else if(food) @@ -496,11 +533,14 @@ SpellSpecific GetSpellSpecific(uint32 spellId) // family flags 18(Molten), 25(Frost/Ice), 28(Mage) if (spellInfo->SpellFamilyFlags[0] & 0x12040000) return SPELL_MAGE_ARMOR; + // Arcane brillance and Arcane intelect (normal check fails because of flags difference) if (spellInfo->SpellFamilyFlags[0] & 0x400) return SPELL_MAGE_ARCANE_BRILLANCE; + if ((spellInfo->SpellFamilyFlags[0] & 0x1000000) && spellInfo->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE) return SPELL_MAGE_POLYMORPH; + break; } case SPELLFAMILY_WARRIOR: @@ -509,6 +549,7 @@ SpellSpecific GetSpellSpecific(uint32 spellId) return SPELL_POSITIVE_SHOUT; if (spellInfo->Id == 12292) // Death Wish return SPELL_WARRIOR_ENRAGE; + break; } case SPELLFAMILY_WARLOCK: @@ -516,9 +557,11 @@ SpellSpecific GetSpellSpecific(uint32 spellId) // only warlock curses have this if (spellInfo->Dispel == DISPEL_CURSE) return SPELL_CURSE; + // Warlock (Demon Armor | Demon Skin | Fel Armor) if (spellInfo->SpellFamilyFlags[1] & 0x20000020 || spellInfo->SpellFamilyFlags[2] & 0x00000010) return SPELL_WARLOCK_ARMOR; + //seed of corruption and corruption if (spellInfo->SpellFamilyFlags[1] & 0x10 || spellInfo->SpellFamilyFlags[0] & 0x2) return SPELL_WARLOCK_CORRUPTION; @@ -531,9 +574,11 @@ SpellSpecific GetSpellSpecific(uint32 spellId) (spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK) && (spellInfo->SpellIconID == 52 || spellInfo->SpellIconID == 79)) return SPELL_WELL_FED; + // Divine Spirit and Prayer of Spirit if (spellInfo->SpellFamilyFlags[0] & 0x20) return SPELL_PRIEST_DIVINE_SPIRIT; + break; } case SPELLFAMILY_HUNTER: @@ -541,41 +586,52 @@ SpellSpecific GetSpellSpecific(uint32 spellId) // 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.HasFlag(0x00380000, 0x00440000, 0x00001010)) return SPELL_ASPECT; + break; } case SPELLFAMILY_PALADIN: { if (IsSealSpell(spellInfo)) return SPELL_SEAL; + if (spellInfo->SpellFamilyFlags[0] & 0x11010002) return SPELL_BLESSING; + if (spellInfo->SpellFamilyFlags[0] & 0x00002190) return SPELL_HAND; + // Judgement of Wisdom, Judgement of Light, Judgement of Justice if (spellInfo->Id == 20184 || spellInfo->Id == 20185 || spellInfo->Id == 20186) return SPELL_JUDGEMENT; + // only paladin auras have this (for palaldin class family) if( spellInfo->SpellFamilyFlags[2] & 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->Id == SPELL_ID_BLOOD_PRESENCE || spellInfo->Id == SPELL_ID_FROST_PRESENCE || spellInfo->Id == SPELL_ID_UNHOLY_PRESENCE) //if (spellInfo->Category == 47) return SPELL_PRESENCE; break; } + for(int i = 0; i < 3; ++i) { if(spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) @@ -599,8 +655,10 @@ SpellSpecific GetSpellSpecific(uint32 spellId) // 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 IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1,SpellSpecific spellSpec2) { @@ -621,6 +679,7 @@ bool IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1,SpellSpecific s return false; } } + bool IsSingleFromSpellSpecificPerTarget(SpellSpecific spellSpec1, SpellSpecific spellSpec2) { switch(spellSpec1) @@ -663,6 +722,7 @@ bool IsSingleFromSpellSpecificPerTarget(SpellSpecific spellSpec1, SpellSpecific return false; } } + bool IsPositiveTarget(uint32 targetA, uint32 targetB) { // non-positive targets @@ -683,10 +743,12 @@ bool IsPositiveTarget(uint32 targetA, uint32 targetB) return IsPositiveTarget(targetB, 0); return true; } + bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) const { SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); if (!spellproto) return false; + switch(spellId) { case 1852: // Silenced (GM) @@ -705,6 +767,7 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con case 12042: // Arcane Power return true; } + switch(spellproto->Mechanic) { case MECHANIC_IMMUNE_SHIELD: @@ -712,12 +775,14 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con default: break; } + // Special case: effects which determine positivity of whole spell for (uint8 i = 0;i<MAX_SPELL_EFFECTS;++i) { if (spellproto->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH) return true; } + switch(spellproto->Effect[effIndex]) { case SPELL_EFFECT_DUMMY: @@ -736,6 +801,7 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con 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: @@ -785,6 +851,7 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con { uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex]; SpellEntry const *spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId); + if(spellTriggeredProto) { // non-positive targets of main spell return early @@ -805,6 +872,7 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con 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; @@ -909,26 +977,32 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con default: break; } + // non-positive targets if(!IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex])) return false; + // AttributesEx check if(spellproto->AttributesEx & SPELL_ATTR_EX_NEGATIVE) return false; + if (!deep && spellproto->EffectTriggerSpell[effIndex] && !spellproto->EffectApplyAuraName[effIndex] && IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex]) && !_isPositiveSpell(spellproto->EffectTriggerSpell[effIndex], true)) return false; + // ok, positive return true; } + bool IsPositiveSpell(uint32 spellId) { if(!sSpellStore.LookupEntry(spellId)) // non-existing spells such as 61988 (Forbearance) return false; return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE); } + bool IsPositiveEffect(uint32 spellId, uint32 effIndex) { if(!sSpellStore.LookupEntry(spellId)) @@ -941,10 +1015,12 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) case 2: return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE_EFF2); } } + bool SpellMgr::_isPositiveSpell(uint32 spellId, bool deep) const { SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); if (!spellproto) return false; + // spells with at least one negative effect are considered negative // some self-applied spells have negative effects but in self casting case negative check ignored. for (int i = 0; i < 3; ++i) @@ -952,11 +1028,13 @@ bool SpellMgr::_isPositiveSpell(uint32 spellId, bool deep) const 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)) { @@ -965,8 +1043,10 @@ bool IsSingleTargetSpell(SpellEntry const *spellInfo) default: break; } + return false; } + bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2) { // TODO - need better check @@ -974,6 +1054,7 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI 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 @@ -987,8 +1068,10 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI default: break; } + return false; } + SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) { // talents that learn spells can have stance requirements that need ignore @@ -996,11 +1079,15 @@ SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 if( GetTalentSpellCost(spellInfo->Id) > 0 && (spellInfo->Effect[0]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[1]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[2]==SPELL_EFFECT_LEARN_SPELL) ) return SPELL_CAST_OK; + uint32 stanceMask = (form ? 1 << (form - 1) : 0); + if (stanceMask & spellInfo->StancesNot) // can explicitly not be casted in this stance return SPELL_FAILED_NOT_SHAPESHIFT; + if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance return SPELL_CAST_OK; + bool actAsShifted = false; SpellShapeshiftEntry const *shapeInfo = NULL; if (form > 0) @@ -1013,6 +1100,7 @@ SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 } actAsShifted = !(shapeInfo->flags1 & 1); // shapeshift acts as normal form for spells } + if(actAsShifted) { if (spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) // not while shapeshifted @@ -1026,6 +1114,7 @@ SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 if(!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT) && spellInfo->Stances != 0) return SPELL_FAILED_ONLY_SHAPESHIFT; } + // Check if stance disables cast of not-stance spells // Example: cannot cast any other spells in zombie or ghoul form // TODO: Find a way to disable use of these spells clientside @@ -1034,40 +1123,55 @@ SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 if(!(stanceMask & spellInfo->Stances)) return SPELL_FAILED_ONLY_SHAPESHIFT; } + return SPELL_CAST_OK; } + void SpellMgr::LoadSpellTargetPositions() { mSpellTargetPositions.clear(); // need for reload case + uint32 count = 0; + // 0 1 2 3 4 5 QueryResult *result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM spell_target_position"); if( !result ) { + barGoLink bar( 1 ); + bar.step(); + sLog.outString(); sLog.outString( ">> Loaded %u spell target coordinates", count ); return; } + barGoLink bar( result->GetRowCount() ); + do { Field *fields = result->Fetch(); + bar.step(); + uint32 Spell_ID = fields[0].GetUInt32(); + SpellTargetPosition st; + st.target_mapId = fields[1].GetUInt32(); st.target_X = fields[2].GetFloat(); st.target_Y = fields[3].GetFloat(); st.target_Z = fields[4].GetFloat(); st.target_Orientation = fields[5].GetFloat(); + 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) { @@ -1082,26 +1186,32 @@ void SpellMgr::LoadSpellTargetPositions() sLog.outErrorDb("Spell (Id: %u) listed in `spell_target_position` does not have target TARGET_DST_DB (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; ++count; + } while( result->NextRow() ); + // Check all spells for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) { SpellEntry const * spellInfo = sSpellStore.LookupEntry(i); if(!spellInfo) continue; + bool found = false; for(int j = 0; j < 3; ++j) { @@ -1128,28 +1238,37 @@ void SpellMgr::LoadSpellTargetPositions() sLog.outDebug("Spell (ID: %u) does not have record in `spell_target_position`", i); } } + 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) return true; + return false; } + void SpellMgr::LoadSpellProcEvents() { mSpellProcEventMap.clear(); // need for reload case + uint32 count = 0; + // 0 1 2 3 4 5 6 7 8 9 10 QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event"); if( !result ) @@ -1160,20 +1279,26 @@ void SpellMgr::LoadSpellProcEvents() sLog.outString( ">> Loaded %u spell proc event conditions", count ); return; } + barGoLink bar( result->GetRowCount() ); uint32 customProc = 0; do { Field *fields = result->Fetch(); + bar.step(); + uint32 entry = fields[0].GetUInt32(); + const SpellEntry *spell = sSpellStore.LookupEntry(entry); if (!spell) { sLog.outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry); continue; } + SpellProcEventEntry spe; + spe.schoolMask = fields[1].GetUInt32(); spe.spellFamilyName = fields[2].GetUInt32(); spe.spellFamilyMask[0] = fields[3].GetUInt32(); @@ -1184,7 +1309,9 @@ void SpellMgr::LoadSpellProcEvents() spe.ppmRate = fields[8].GetFloat(); spe.customChance = fields[9].GetFloat(); spe.cooldown = fields[10].GetUInt32(); + mSpellProcEventMap[entry] = spe; + if (spell->procFlags==0) { if (spe.procFlags == 0) @@ -1196,13 +1323,16 @@ void SpellMgr::LoadSpellProcEvents() } ++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 ); } + void SpellMgr::LoadSpellBonusess() { mSpellBonusMap.clear(); // need for reload case @@ -1217,45 +1347,62 @@ void SpellMgr::LoadSpellBonusess() sLog.outString( ">> Loaded %u spell bonus data", count); return; } + barGoLink bar( result->GetRowCount() ); do { Field *fields = result->Fetch(); bar.step(); uint32 entry = fields[0].GetUInt32(); + const SpellEntry *spell = sSpellStore.LookupEntry(entry); if (!spell) { sLog.outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry); continue; } + SpellBonusEntry sbe; + sbe.direct_damage = fields[1].GetFloat(); sbe.dot_damage = fields[2].GetFloat(); sbe.ap_bonus = fields[3].GetFloat(); sbe.ap_dot_bonus = fields[4].GetFloat(); + mSpellBonusMap[entry] = sbe; ++count; } while( result->NextRow() ); + 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; + bool hasFamilyMask = false; + /* Check Periodic Auras + *Dots can trigger if spell has no PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL nor PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL + *Only Hots can trigger if spell has PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL + *Only dots can trigger if spell has both positivity flags or PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL + *Aura has to have PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL or spellfamily specified to trigger from Hot + */ + if (procFlags & PROC_FLAG_ON_DO_PERIODIC) { if (EventProcFlag & PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL) @@ -1268,6 +1415,7 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr else if (EventProcFlag & PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL) return false; } + if (procFlags & PROC_FLAG_ON_TAKE_PERIODIC) { if (EventProcFlag & PROC_FLAG_TAKEN_NEGATIVE_MAGIC_SPELL) @@ -1283,13 +1431,16 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr // Trap casts are active by default if (procFlags & PROC_FLAG_ON_TRAP_ACTIVATION) active = true; + // Always trigger for this if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH)) return true; + if (spellProcEvent) // Exist event data { // Store extra req procEvent_procEx = spellProcEvent->procEx; + // For melee triggers if (procSpell == NULL) { @@ -1302,9 +1453,11 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr // 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) { @@ -1317,11 +1470,13 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr } } } + if (procExtra & (PROC_EX_INTERNAL_REQ_FAMILY)) { if (!hasFamilyMask) return false; } + // Check for extra req (if none) and hit/crit if (procEvent_procEx == PROC_EX_NONE) { @@ -1352,80 +1507,114 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr } 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 @@ -1434,8 +1623,10 @@ bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo) 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) { @@ -1462,58 +1653,77 @@ bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo) 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) { @@ -1527,26 +1737,33 @@ SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spell break; } } + // not required if(!needRankSelection) return spellInfo; + for(uint32 nextSpellId = spellInfo->Id; nextSpellId != 0; nextSpellId = GetPrevSpellInChain(nextSpellId)) { SpellEntry const *nextSpellInfo = sSpellStore.LookupEntry(nextSpellId); if(!nextSpellInfo) break; + // if found appropriate level if(playerLevel + 10 >= nextSpellInfo->spellLevel) return nextSpellInfo; + // one rank less then } + // not found return NULL; } + void SpellMgr::LoadSpellLearnSkills() { mSpellLearnSkills.clear(); // need for reload case + // search auto-learned skills and add its to map also for use in unlearn spells/talents uint32 dbc_count = 0; barGoLink bar( sSpellStore.GetNumRows() ); @@ -1554,8 +1771,10 @@ void SpellMgr::LoadSpellLearnSkills() { 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) @@ -1567,67 +1786,86 @@ void SpellMgr::LoadSpellLearnSkills() else dbc_node.value = entry->CalculateSimpleValue(i)*75; dbc_node.maxvalue = entry->CalculateSimpleValue(i)*75; + SpellLearnSkillNode const* db_node = GetSpellLearnSkill(spell); + mSpellLearnSkills[spell] = dbc_node; ++dbc_count; break; } } } + sLog.outString(); sLog.outString( ">> Loaded %u Spell Learn Skills from DBC", dbc_count ); } + void SpellMgr::LoadSpellLearnSpells() { mSpellLearnSpells.clear(); // need for reload case + // 0 1 2 QueryResult *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) @@ -1635,14 +1873,18 @@ void SpellMgr::LoadSpellLearnSpells() SpellLearnSpellNode dbc_node; dbc_node.spell = entry->EffectTriggerSpell[i]; dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself) + // ignore learning not existed spells (broken/outdated/or generic learnig spell 483 if(!sSpellStore.LookupEntry(dbc_node.spell)) continue; + // talent or passive spells or skill-step spells auto-casted and not need dependent learning, // pet teaching spells don't must be dependent learning (casted) // other required explicit dependent learning dbc_node.autoLearned = entry->EffectImplicitTargetA[i] == TARGET_UNIT_PET || GetTalentSpellCost(spell) > 0 || IsPassiveSpell(spell) || IsSpellHaveEffect(entry,SPELL_EFFECT_SKILL_STEP); + SpellLearnSpellMapBounds db_node_bounds = GetSpellLearnSpellMapBounds(spell); + bool found = false; for(SpellLearnSpellMap::const_iterator itr = db_node_bounds.first; itr != db_node_bounds.second; ++itr) { @@ -1654,6 +1896,7 @@ void SpellMgr::LoadSpellLearnSpells() break; } } + if(!found) // add new spell-spell pair if not found { mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell,dbc_node)); @@ -1662,36 +1905,49 @@ void SpellMgr::LoadSpellLearnSpells() } } } + 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) { @@ -1709,17 +1965,20 @@ void SpellMgr::LoadSpellScriptTarget() sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (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<GameObjectInfo>(targetEntry)) { sLog.outErrorDb("Table `spell_script_target`: gameobject template entry %u does not exist.",targetEntry); @@ -1744,6 +2003,7 @@ void SpellMgr::LoadSpellScriptTarget() continue; } 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); @@ -1752,16 +2012,21 @@ void SpellMgr::LoadSpellScriptTarget() break; } } + mSpellScriptTarget.insert(SpellScriptTarget::value_type(spellId,SpellTargetEntry(SpellScriptTargetType(type),targetEntry))); + ++count; } while (result->NextRow()); + delete result; + // Check all spells for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) { SpellEntry const * spellInfo = sSpellStore.LookupEntry(i); if(!spellInfo) continue; + bool found = false; for(int j = 0; j < 3; ++j) { @@ -1797,32 +2062,44 @@ void SpellMgr::LoadSpellScriptTarget() sLog.outDebug("Spell (ID: %u) does not have record in `spell_script_target`", i); } } + 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()) { @@ -1843,63 +2120,82 @@ void SpellMgr::LoadSpellPetAuras() sLog.outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell); continue; } + SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(aura); if (!spellInfo2) { sLog.outErrorDb("Aura %u listed in `spell_pet_auras` does not exist", aura); continue; } + PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[eff] == TARGET_UNIT_PET, spellInfo->CalculateSimpleValue(eff)); mSpellPetAuraMap[(spell<<8) + eff] = pa; } + ++count; } while( result->NextRow() ); + delete result; + sLog.outString(); sLog.outString( ">> Loaded %u spell pet auras", count ); } + void SpellMgr::LoadPetLevelupSpellMap() { mPetLevelupSpellMap.clear(); // need for reload case + uint32 count = 0; uint32 family_count = 0; + for (uint32 i = 0; i < sCreatureFamilyStore.GetNumRows(); ++i) { CreatureFamilyEntry const *creatureFamily = sCreatureFamilyStore.LookupEntry(i); if(!creatureFamily) // not exist continue; + for (uint8 j = 0; j < 2; ++j) { if (!creatureFamily->skillLine[j]) continue; + for (uint32 k = 0; k < sSkillLineAbilityStore.GetNumRows(); ++k) { SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(k); if( !skillLine ) continue; + //if (skillLine->skillId!=creatureFamily->skillLine[0] && // (!creatureFamily->skillLine[1] || skillLine->skillId!=creatureFamily->skillLine[1])) // continue; + if (skillLine->skillId!=creatureFamily->skillLine[j]) continue; + if(skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) continue; + SpellEntry const *spell = sSpellStore.LookupEntry(skillLine->spellId); if(!spell) // not exist or triggered or talent continue; + if(!spell->spellLevel) continue; + PetLevelupSpellSet& spellSet = mPetLevelupSpellMap[creatureFamily->ID]; if(spellSet.empty()) ++family_count; - spellSet.insert(PetLevelupSpellSet::value_type(spell->spellLevel,spell->Id)); + + 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; @@ -1914,6 +2210,7 @@ bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntr } if(!have_spell) return false; + // remove duplicates with levelupSpells if any if(PetLevelupSpellSet const *levelupSpells = cInfo->family ? spellmgr.GetPetLevelupSpellList(cInfo->family) : NULL) { @@ -1921,6 +2218,7 @@ bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntr { if(!petDefSpells.spellid[j]) continue; + for(PetLevelupSpellSet::const_iterator itr = levelupSpells->begin(); itr != levelupSpells->end(); ++itr) { if (itr->second == petDefSpells.spellid[j]) @@ -1931,6 +2229,7 @@ bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntr } } } + // skip empty list; have_spell = false; for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j) @@ -1941,40 +2240,50 @@ bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntr break; } } + return have_spell; } + void SpellMgr::LoadPetDefaultSpells() { 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 < 3; ++k) { if(spellEntry->Effect[k]==SPELL_EFFECT_SUMMON || spellEntry->Effect[k]==SPELL_EFFECT_SUMMON_PET) @@ -1983,16 +2292,20 @@ void SpellMgr::LoadPetDefaultSpells() 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; @@ -2001,16 +2314,20 @@ void SpellMgr::LoadPetDefaultSpells() } } } + 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) { @@ -2018,6 +2335,7 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg) { case 0: continue; + // craft spell for crafting non-existed item (break client recipes list show) case SPELL_EFFECT_CREATE_ITEM: case SPELL_EFFECT_CREATE_ITEM_2: @@ -2033,6 +2351,7 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg) } return false; } + need_check_reagents = true; break; } @@ -2054,6 +2373,7 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg) } } } + if(need_check_reagents) { for(int j = 0; j < 8; ++j) @@ -2071,8 +2391,10 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg) } } } + return true; } + void SpellMgr::LoadSpellAreas() { mSpellAreaMap.clear(); // need for reload case @@ -2080,22 +2402,31 @@ void SpellMgr::LoadSpellAreas() 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; @@ -2107,6 +2438,7 @@ void SpellMgr::LoadSpellAreas() spellArea.raceMask = fields[6].GetUInt32(); spellArea.gender = Gender(fields[7].GetUInt8()); spellArea.autocast = fields[8].GetBool(); + if(const SpellEntry* spellInfo = sSpellStore.LookupEntry(spell)) { if(spellArea.autocast) @@ -2117,6 +2449,7 @@ void SpellMgr::LoadSpellAreas() sLog.outErrorDb("Spell %u listed in `spell_area` does not exist", spell); continue; } + { bool ok = true; SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId); @@ -2134,26 +2467,32 @@ void SpellMgr::LoadSpellAreas() 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)) @@ -2161,12 +2500,14 @@ void SpellMgr::LoadSpellAreas() 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)); @@ -2175,6 +2516,7 @@ void SpellMgr::LoadSpellAreas() 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: @@ -2185,11 +2527,13 @@ void SpellMgr::LoadSpellAreas() 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) { @@ -2203,11 +2547,13 @@ void SpellMgr::LoadSpellAreas() 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) { @@ -2217,6 +2563,7 @@ void SpellMgr::LoadSpellAreas() break; } } + if(chain) { sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell); @@ -2224,20 +2571,25 @@ void SpellMgr::LoadSpellAreas() } } } + 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) { @@ -2246,18 +2598,24 @@ void SpellMgr::LoadSpellAreas() 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 @@ -2275,9 +2633,11 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell // 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) { @@ -2286,6 +2646,7 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell 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) { @@ -2293,6 +2654,7 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell if (!mapEntry || mapEntry->IsRaid()) return SPELL_FAILED_NOT_IN_RAID_INSTANCE; } + // DB base check (if non empty then must fit at least single for allow) SpellAreaMapBounds saBounds = spellmgr.GetSpellAreaMapBounds(spellInfo->Id); if (saBounds.first != saBounds.second) @@ -2304,6 +2666,7 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell } return SPELL_FAILED_INCORRECT_AREA; } + // bg spell checks switch(spellInfo->Id) { @@ -2323,17 +2686,21 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; + return zone_id == 4197 || mapEntry->IsBattleGround() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 44521: // Preparation { if (!player) return SPELL_FAILED_REQUIRES_AREA; + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; + if (!mapEntry->IsBattleGround()) return SPELL_FAILED_REQUIRES_AREA; + BattleGround* bg = player->GetBattleGround(); return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } @@ -2345,21 +2712,26 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if (!mapEntry) return SPELL_FAILED_INCORRECT_AREA; + return mapEntry->IsBattleArena() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 32727: // Arena Preparation { if (!player) return SPELL_FAILED_REQUIRES_AREA; + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); if(!mapEntry) return SPELL_FAILED_INCORRECT_AREA; + if(!mapEntry->IsBattleArena()) return SPELL_FAILED_REQUIRES_AREA; + BattleGround* bg = player->GetBattleGround(); return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } } + // aura limitations for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { @@ -2373,25 +2745,32 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell } } } + return SPELL_CAST_OK; } + void SpellMgr::LoadSkillLineAbilityMap() { mSkillLineAbilityMap.clear(); + barGoLink bar( sSkillLineAbilityStore.GetNumRows() ); uint32 count = 0; + for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) { bar.step(); SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i); if(!SkillInfo) continue; + mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId,SkillInfo)); ++count; } + sLog.outString(); sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count); } + DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered) { // Explicit Diminishing Groups @@ -2494,6 +2873,7 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto default: break; } + // Get by mechanic uint32 mechanic = GetAllSpellMechanicMask(spellproto); if (mechanic == MECHANIC_NONE) return DIMINISHING_NONE; @@ -2513,6 +2893,7 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto (1<<MECHANIC_SAPPED))) return DIMINISHING_KNOCKOUT; if (mechanic & (1<<MECHANIC_BANISH)) return DIMINISHING_BANISH; if (mechanic & (1<<MECHANIC_HORROR)) return DIMINISHING_DEATHCOIL; + // Get by effect for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS; ++i) { @@ -2521,10 +2902,12 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto } return DIMINISHING_NONE; } + int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry const* spellproto) { if(!IsDiminishingReturnsGroupDurationLimited(group)) return 0; + // Explicit diminishing duration switch(spellproto->SpellFamilyName) { @@ -2559,8 +2942,10 @@ int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry cons default: break; } + return 10 * IN_MILISECONDS; } + bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group) { switch(group) @@ -2584,6 +2969,7 @@ bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group) } return false; } + DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) { switch(group) @@ -2609,8 +2995,10 @@ DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) default: break; } + return DRTYPE_NONE; } + bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const { if(gender!=GENDER_NONE) @@ -2619,30 +3007,35 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 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 @@ -2655,9 +3048,12 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 // not have expected aura return !player->HasAura(-auraSpell); } + return true; } + //-----------TRINITY------------- + bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *spellInfo_2, bool sameCaster) const { SpellSpecific spellSpec_1 = GetSpellSpecific(spellInfo_1->Id); @@ -2666,13 +3062,16 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp if (IsSingleFromSpellSpecificPerTarget(spellSpec_1, spellSpec_2) || sameCaster && IsSingleFromSpellSpecificPerCaster(spellSpec_1, spellSpec_2)) return false; + if(spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName) return true; + if(!sameCaster) { if(spellInfo_1->AttributesEx & SPELL_ATTR_EX_STACK_FOR_DIFF_CASTERS || spellInfo_1->AttributesEx3 & SPELL_ATTR_EX3_STACK_FOR_DIFF_CASTERS) return true; + // check same periodic auras for(uint32 i = 0; i < 3; ++i) { @@ -2680,10 +3079,12 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp if(spellInfo_1->Effect[i] != SPELL_EFFECT_APPLY_AURA && spellInfo_1->Effect[i] != SPELL_EFFECT_PERSISTENT_AREA_AURA) continue; + // not channeled AOE effects should not stack (blizzard should, but Consecration should not) if((IsAreaEffectTarget[spellInfo_1->EffectImplicitTargetA[i]] || IsAreaEffectTarget[spellInfo_1->EffectImplicitTargetB[i]]) && !IsChanneledSpell(spellInfo_1)) continue; + switch(spellInfo_1->EffectApplyAuraName[i]) { // DOT or HOT from different casters will stack @@ -2703,8 +3104,10 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp } } } + uint32 spellId_1 = GetLastSpellInChain(spellInfo_1->Id); uint32 spellId_2 = GetLastSpellInChain(spellInfo_2->Id); + // same spell if (spellId_1 == spellId_2) { @@ -2714,6 +3117,7 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp // same spell with same caster should not stack return false; } + // use icon to check generic spells if(!spellInfo_1->SpellFamilyName) { @@ -2728,95 +3132,127 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp || spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags) return true; } + //use data of highest rank spell(needed for spells which ranks have different effects) spellInfo_1 = sSpellStore.LookupEntry(spellId_1); spellInfo_2 = sSpellStore.LookupEntry(spellId_2); + //if spells do not have the same effect or aura or miscvalue, they will stack for(uint32 i = 0; i < 3; ++i) if(spellInfo_1->Effect[i] != spellInfo_2->Effect[i] || spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i] || spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i]) // paladin resist aura return true; // need itemtype check? need an example to add that check + // different spells with same effect return false; } + bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool def) { if (!dispelSpell) return false; SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); if (!spellproto) return false; + if (spellproto->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) return false; + if(dispelSpell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) return true; + return def; } + void SpellMgr::LoadSpellEnchantProcData() { mSpellEnchantProcEventMap.clear(); // need for reload case + uint32 count = 0; + // 0 1 2 3 QueryResult *result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data"); if( !result ) { + barGoLink bar( 1 ); + bar.step(); + sLog.outString(); sLog.outString( ">> Loaded %u spell enchant proc event conditions", count ); return; } + barGoLink bar( result->GetRowCount() ); do { Field *fields = result->Fetch(); + bar.step(); + uint32 enchantId = fields[0].GetUInt32(); + SpellItemEnchantmentEntry const *ench = sSpellItemEnchantmentStore.LookupEntry(enchantId); if (!ench) { sLog.outErrorDb("Enchancment %u listed in `spell_enchant_proc_data` does not exist", enchantId); continue; } + SpellEnchantProcEntry spe; + spe.customChance = fields[1].GetUInt32(); spe.PPMChance = fields[2].GetFloat(); spe.procEx = fields[3].GetUInt32(); + mSpellEnchantProcEventMap[enchantId] = spe; + ++count; } while( result->NextRow() ); + delete result; + sLog.outString( ">> Loaded %u enchant proc data definitions", count); } + void SpellMgr::LoadSpellRequired() { mSpellsReqSpell.clear(); // need for reload case mSpellReq.clear(); // need for reload case + QueryResult *result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required"); + if(result == NULL) { barGoLink bar( 1 ); bar.step(); + sLog.outString(); sLog.outString( ">> Loaded 0 spell required records" ); sLog.outErrorDb("`spell_required` table is empty!"); return; } uint32 rows = 0; + barGoLink bar( result->GetRowCount() ); do { bar.step(); Field *fields = result->Fetch(); + uint32 spell_id = fields[0].GetUInt32(); uint32 spell_req = fields[1].GetUInt32(); + mSpellsReqSpell.insert (std::pair<uint32, uint32>(spell_req, spell_id)); mSpellReq[spell_id] = spell_req; ++rows; } while( result->NextRow() ); delete result; + sLog.outString(); sLog.outString( ">> Loaded %u spell required records", rows ); } + struct SpellRankEntry { uint32 SkillId; @@ -2832,15 +3268,18 @@ struct SpellRankEntry flag96 Effect; flag96 Aura; uint16 TalentID; + bool operator < (const SpellRankEntry & _Right) const { return (SkillId != _Right.SkillId ? SkillId < _Right.SkillId : SpellName!=_Right.SpellName ? SpellName < _Right.SpellName : ProcFlags!=_Right.ProcFlags ? ProcFlags < _Right.ProcFlags + : Effect!=_Right.Effect ? Effect < _Right.Effect : Aura!=_Right.Aura ? Aura < _Right.Aura : TalentID!=_Right.TalentID ? TalentID < _Right.TalentID : (CastingTimeIndex!=_Right.CastingTimeIndex) && (!CastingTimeIndex || !_Right.CastingTimeIndex || CastingTimeIndex==1 || !_Right.CastingTimeIndex==1) ? CastingTimeIndex < _Right.CastingTimeIndex + : SpellFamilyFlags!=_Right.SpellFamilyFlags ? SpellFamilyFlags < _Right.SpellFamilyFlags : (SpellVisual!=_Right.SpellVisual) && (!SpellVisual || !_Right.SpellVisual) ? SpellVisual < _Right.SpellVisual : (ManaCost!=_Right.ManaCost) && (!ManaCost || !_Right.ManaCost) ? ManaCost < _Right.ManaCost @@ -2850,15 +3289,18 @@ struct SpellRankEntry ); } }; + struct SpellRankValue { uint32 Id; char const *Rank; bool strict; }; + void SpellMgr::LoadSpellChains() { mSpellChains.clear(); // need for reload case + std::vector<uint32> ChainedSpells; for (uint32 ability_id=0;ability_id<sSkillLineAbilityStore.GetNumRows();ability_id++) { @@ -2869,12 +3311,15 @@ void SpellMgr::LoadSpellChains() continue; ChainedSpells.push_back(AbilityInfo->forward_spellid); } + std::multimap<SpellRankEntry, SpellRankValue> RankMap; + for (uint32 ability_id=0;ability_id<sSkillLineAbilityStore.GetNumRows();ability_id++) { SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id); if (!AbilityInfo) continue; + //get only spell with lowest ability_id to prevent doubles uint32 spell_id=AbilityInfo->spellId; bool found=false; @@ -2885,6 +3330,7 @@ void SpellMgr::LoadSpellChains() } if (found) continue; + if(mSkillLineAbilityMap.lower_bound(spell_id)->second->id!=ability_id) continue; SpellEntry const *SpellInfo=sSpellStore.LookupEntry(spell_id); @@ -2896,6 +3342,7 @@ void SpellMgr::LoadSpellChains() //exception to polymorph spells-make pig and turtle other chain than sheep if ((SpellInfo->SpellFamilyName==SPELLFAMILY_MAGE) && (SpellInfo->SpellFamilyFlags[0] & 0x1000000) && (SpellInfo->SpellIconID!=82)) continue; + SpellRankEntry entry; SpellRankValue value; entry.SkillId=AbilityInfo->skillId; @@ -2922,8 +3369,11 @@ void SpellMgr::LoadSpellChains() break; } } + barGoLink bar(RankMap.size()); + uint32 count=0; + for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr = RankMap.begin();itr!=RankMap.end();) { SpellRankEntry entry=itr->first; @@ -2934,6 +3384,7 @@ void SpellMgr::LoadSpellChains() bar.step(); RankErrorMap.insert(std::pair<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>(itr2->second.Rank,itr2)); } + bool error=false; //if strict == true strict check is not needed if (!itr->second.strict) @@ -2980,14 +3431,17 @@ void SpellMgr::LoadSpellChains() currEntry.TargetAuraState=SpellInfo->TargetAuraState; currEntry.SpellVisual=SpellInfo->SpellVisual[0]; currEntry.ManaCost=SpellInfo->manaCost; + //compare effects and casting time currEntry.CastingTimeIndex=SpellInfo->CastingTimeIndex; currEntry.Effect[0]=SpellInfo->Effect[0]; currEntry.Effect[1]=SpellInfo->Effect[1]; currEntry.Effect[2]=SpellInfo->Effect[2]; + currEntry.Aura[0]=SpellInfo->EffectApplyAuraName[0]; currEntry.Aura[1]=SpellInfo->EffectApplyAuraName[1]; currEntry.Aura[2]=SpellInfo->EffectApplyAuraName[2]; + SpellRankValue currValue; currValue.Id=ConflictedSpells.front(); currValue.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()]; @@ -3015,6 +3469,7 @@ void SpellMgr::LoadSpellChains() else itr2++; } + //order spells by spellLevel std::list<uint32> RankedSpells; uint32 min_spell_lvl=0; @@ -3033,6 +3488,7 @@ void SpellMgr::LoadSpellChains() RankedSpells.push_back(min_itr->second.Id); RankMap.erase(min_itr); } + //use data from talent.dbc uint16 talent_id=0; for(std::list<uint32>::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();) @@ -3055,11 +3511,14 @@ void SpellMgr::LoadSpellChains() RankedSpells.push_front(TalentInfo->RankID[rank-1]); } } + //do not proceed for spells with less than 2 ranks itr=RankMap.begin(); if (RankedSpells.size()<2) continue; + count++; + uint32 spell_rank=1; for(std::list<uint32>::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();spell_rank++) { @@ -3067,9 +3526,11 @@ void SpellMgr::LoadSpellChains() mSpellChains[spell_id].rank=spell_rank; mSpellChains[spell_id].first=RankedSpells.front(); mSpellChains[spell_id].last=RankedSpells.back(); + itr2++; if (spell_rank<2) mSpellChains[spell_id].prev=0; + if (spell_id==RankedSpells.back()) mSpellChains[spell_id].next=0; else @@ -3079,16 +3540,20 @@ void SpellMgr::LoadSpellChains() } } } + //uncomment these two lines to print yourself list of spell_chains on startup //for (UNORDERED_MAP<uint32, SpellChainNode>::iterator itr=mSpellChains.begin();itr!=mSpellChains.end();itr++) //sLog.outString( "Id: %u, Rank: %d , %s, %u, %u, %u, %u",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()], itr->second.first, itr->second.last,itr->second.next ,itr->second.prev); + sLog.outString(); sLog.outString( ">> Loaded %u spell chains",count); } + // set data in core for now void SpellMgr::LoadSpellCustomAttr() { mSpellCustomAttr.resize(GetSpellStore()->GetNumRows()); + SpellEntry *spellInfo; for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i) { @@ -3096,6 +3561,7 @@ void SpellMgr::LoadSpellCustomAttr() spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i); if(!spellInfo) continue; + for(uint32 j = 0; j < 3; ++j) { switch(spellInfo->EffectApplyAuraName[j]) @@ -3110,6 +3576,7 @@ void SpellMgr::LoadSpellCustomAttr() default: break; } + switch(spellInfo->Effect[j]) { case SPELL_EFFECT_SCHOOL_DAMAGE: @@ -3134,6 +3601,7 @@ void SpellMgr::LoadSpellCustomAttr() spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE; break; } + switch(SpellTargetType[spellInfo->EffectImplicitTargetA[j]]) { case TARGET_TYPE_UNIT_TARGET: @@ -3146,6 +3614,7 @@ void SpellMgr::LoadSpellCustomAttr() // break; } } + for(uint32 j = 0; j < 3; ++j) { switch(spellInfo->EffectApplyAuraName[j]) @@ -3161,16 +3630,20 @@ void SpellMgr::LoadSpellCustomAttr() break; } } + if(!_isPositiveEffect(i, 0, false)) mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF0; if(!_isPositiveEffect(i, 1, false)) mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF1; if(!_isPositiveEffect(i, 2, false)) mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF2; + if(spellInfo->SpellVisual[0] == 3879) mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_BACK; + if(spellInfo->activeIconID == 2158) //flight spellInfo->Attributes |= SPELL_ATTR_PASSIVE; + switch(i) { // Heart of the Crusader @@ -3322,6 +3795,7 @@ void SpellMgr::LoadSpellCustomAttr() default: break; } + switch(spellInfo->SpellFamilyName) { case SPELLFAMILY_WARRIOR: @@ -3348,27 +3822,34 @@ void SpellMgr::LoadSpellCustomAttr() break; } } + SummonPropertiesEntry *properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(121)); properties->Type = SUMMON_TYPE_TOTEM; properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(647)); // 52893 properties->Type = SUMMON_TYPE_TOTEM; + CreatureAI::FillAISpellInfo(); } + // Fill custom data about enchancments void SpellMgr::LoadEnchantCustomAttr() { uint32 size = sSpellItemEnchantmentStore.GetNumRows(); mEnchantCustomAttr.resize(size); + for (uint32 i = 0;i<size; ++i) mEnchantCustomAttr[i] = 0; + for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i) { SpellEntry * spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i); if(!spellInfo) continue; + // TODO: find a better check if (!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_UNK13) || !(spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT)) continue; + for(uint32 j = 0; j < 3; ++j) { if(spellInfo->Effect[j] == SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) @@ -3383,18 +3864,23 @@ void SpellMgr::LoadEnchantCustomAttr() } } } + bool SpellMgr::IsSkillTypeSpell(uint32 spellId, SkillType type) const { SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId); + for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) if (_spell_idx->second->skillId == type) return true; + return false; } + void SpellMgr::LoadSpellLinked() { mSpellLinkedMap.clear(); // need for reload case uint32 count = 0; + // 0 1 2 QueryResult *result = WorldDatabase.Query("SELECT spell_trigger, spell_effect, type FROM spell_linked_spell"); if( !result ) @@ -3405,14 +3891,19 @@ void SpellMgr::LoadSpellLinked() sLog.outString( ">> Loaded %u linked spells", count ); return; } + barGoLink bar( result->GetRowCount() ); + do { Field *fields = result->Fetch(); + bar.step(); + int32 trigger = fields[0].GetInt32(); int32 effect = fields[1].GetInt32(); int32 type = fields[2].GetInt32(); + SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(trigger)); if (!spellInfo) { @@ -3425,6 +3916,7 @@ void SpellMgr::LoadSpellLinked() sLog.outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(effect)); continue; } + if(trigger > 0) { switch(type) @@ -3438,6 +3930,7 @@ void SpellMgr::LoadSpellLinked() { mSpellCustomAttr[-trigger] |= SPELL_ATTR_CU_LINK_REMOVE; } + if(type) //we will find a better way when more types are needed { if(trigger > 0) @@ -3446,16 +3939,21 @@ void SpellMgr::LoadSpellLinked() trigger -= SPELL_LINKED_MAX_SPELLS * type; } mSpellLinkedMap[trigger].push_back(effect); + ++count; } while( result->NextRow() ); + delete result; + sLog.outString(); sLog.outString( ">> Loaded %u linked spells", count ); } + bool SpellMgr::CheckDB() const { SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(30531); if(bounds.first == bounds.second || bounds.first->second.targetEntry != 17256) return false; + return true; } |