diff options
author | QAston <qaston@gmail.com> | 2011-07-26 23:09:28 +0200 |
---|---|---|
committer | QAston <qaston@gmail.com> | 2011-07-26 23:09:28 +0200 |
commit | b0fe236265465a0f39aa98a8cee2916d1ccfaa02 (patch) | |
tree | 77ed4bde46de983c280a542d657a30b24865638c /src/server/game/Spells/SpellMgr.cpp | |
parent | 29c228a80170e4264129d4e3bed4d2fc41aca5a7 (diff) |
Core: Use new SpellInfo class in core. Sadly, this commit is not compatibile with some of the custom code. To make your code work again you may need to change:
*SpellEntry is now SpellInfo
*GetSpellProto is now GetSpellInfo
*SpellEntry::Effect*[effIndex] is now avalible under SpellInfo.Effects[effIndex].*
*sSpellStore.LookupEntry is no longer valid, use sSpellMgr->GetSpellInfo()
*SpellFunctions from SpellMgr.h like DoSpellStuff(spellId) are now: spellInfo->DoStuff()
*SpellMgr::CalculateEffectValue and similar functions are now avalible in SpellEffectInfo class.
*GET_SPELL macro is removed, code which used it is moved to SpellMgr::LoadDbcDataCorrections
*code which affected dbc data in SpellMgr::LoadSpellCustomAttr is now moved to LoadDbcDataCorrections
Diffstat (limited to 'src/server/game/Spells/SpellMgr.cpp')
-rwxr-xr-x | src/server/game/Spells/SpellMgr.cpp | 4629 |
1 files changed, 1671 insertions, 2958 deletions
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index c6f03c0e3e5..a14d53eb667 100755 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -17,9 +17,11 @@ */ #include "SpellMgr.h" +#include "SpellInfo.h" #include "ObjectMgr.h" #include "SpellAuras.h" #include "SpellAuraDefines.h" +#include "SharedDefines.h" #include "DBCStores.h" #include "World.h" #include "Chat.h" @@ -29,1313 +31,697 @@ #include "MapManager.h" #include "BattlegroundIC.h" -bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS]; -SpellEffectTargetTypes EffectTargetType[TOTAL_SPELL_EFFECTS]; -SpellSelectTargetTypes SpellTargetType[TOTAL_SPELL_TARGETS]; - -SpellMgr::SpellMgr() +bool IsPrimaryProfessionSkill(uint32 skill) { - for (uint8 i = 0; i < TOTAL_SPELL_EFFECTS; ++i) - { - switch (i) - { - case SPELL_EFFECT_PERSISTENT_AREA_AURA: //27 - case SPELL_EFFECT_SUMMON: //28 - case SPELL_EFFECT_TRIGGER_MISSILE: //32 - case SPELL_EFFECT_TRANS_DOOR: //50 summon object - case SPELL_EFFECT_SUMMON_PET: //56 - case SPELL_EFFECT_ADD_FARSIGHT: //72 - case SPELL_EFFECT_SUMMON_OBJECT_WILD: //76 - //case SPELL_EFFECT_SUMMON_CRITTER: //97 not 303 - case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: //104 - case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: //105 - case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: //106 - case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: //107 - case SPELL_EFFECT_SUMMON_DEAD_PET: //109 - case SPELL_EFFECT_TRIGGER_SPELL_2: //151 ritual of summon - EffectTargetType[i] = SPELL_REQUIRE_DEST; - break; - case SPELL_EFFECT_PARRY: // 0 - case SPELL_EFFECT_BLOCK: // 0 - case SPELL_EFFECT_SKILL: // always with dummy 3 as A - //case SPELL_EFFECT_LEARN_SPELL: // 0 may be 5 pet - case SPELL_EFFECT_TRADE_SKILL: // 0 or 1 - case SPELL_EFFECT_PROFICIENCY: // 0 - EffectTargetType[i] = SPELL_REQUIRE_NONE; - break; - case SPELL_EFFECT_ENCHANT_ITEM: - case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY: - case SPELL_EFFECT_DISENCHANT: - //in 243 this is 0, in 309 it is 1 - //so both item target and unit target is pushed, and cause crash - //case SPELL_EFFECT_FEED_PET: - case SPELL_EFFECT_PROSPECTING: - case SPELL_EFFECT_MILLING: - case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC: - EffectTargetType[i] = SPELL_REQUIRE_ITEM; - break; - //caster must be pushed otherwise no sound - case SPELL_EFFECT_APPLY_AREA_AURA_PARTY: - case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: - case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY: - case SPELL_EFFECT_APPLY_AREA_AURA_PET: - case SPELL_EFFECT_APPLY_AREA_AURA_OWNER: - case SPELL_EFFECT_APPLY_AREA_AURA_RAID: - case SPELL_EFFECT_CHARGE: - case SPELL_EFFECT_CHARGE_DEST: - case SPELL_EFFECT_JUMP: - case SPELL_EFFECT_JUMP_DEST: - case SPELL_EFFECT_LEAP_BACK: - EffectTargetType[i] = SPELL_REQUIRE_CASTER; - break; - //case SPELL_EFFECT_WMO_DAMAGE: - //case SPELL_EFFECT_WMO_REPAIR: - //case SPELL_EFFECT_WMO_CHANGE: - // EffectTargetType[i] = SPELL_REQUIRE_GOBJECT; - // break; - default: - EffectTargetType[i] = SPELL_REQUIRE_UNIT; - break; - } - } - - for (uint8 i = 0; i < TOTAL_SPELL_TARGETS; ++i) - { - switch (i) - { - case TARGET_UNIT_CASTER: - case TARGET_UNIT_CASTER_FISHING: - case TARGET_UNIT_MASTER: - case TARGET_UNIT_PET: - case TARGET_UNIT_PARTY_CASTER: - case TARGET_UNIT_RAID_CASTER: - case TARGET_UNIT_VEHICLE: - case TARGET_UNIT_PASSENGER_0: - case TARGET_UNIT_PASSENGER_1: - case TARGET_UNIT_PASSENGER_2: - case TARGET_UNIT_PASSENGER_3: - case TARGET_UNIT_PASSENGER_4: - case TARGET_UNIT_PASSENGER_5: - case TARGET_UNIT_PASSENGER_6: - case TARGET_UNIT_PASSENGER_7: - case TARGET_UNIT_SUMMONER: - SpellTargetType[i] = TARGET_TYPE_UNIT_CASTER; - break; - case TARGET_UNIT_TARGET_MINIPET: - case TARGET_UNIT_TARGET_ALLY: - case TARGET_UNIT_TARGET_RAID: - case TARGET_UNIT_TARGET_ANY: - case TARGET_UNIT_TARGET_ENEMY: - case TARGET_UNIT_TARGET_PARTY: - case TARGET_UNIT_TARGET_PASSENGER: - case TARGET_UNIT_TARGET_ALLY_PARTY: - case TARGET_UNIT_TARGET_CLASS_RAID: - case TARGET_UNIT_CHAINHEAL: - SpellTargetType[i] = TARGET_TYPE_UNIT_TARGET; - break; - case TARGET_UNIT_NEARBY_ENEMY: - case TARGET_UNIT_NEARBY_ALLY: - case TARGET_UNIT_NEARBY_ENTRY: - case TARGET_UNIT_NEARBY_PARTY: - case TARGET_UNIT_NEARBY_RAID: - case TARGET_GAMEOBJECT_NEARBY_ENTRY: - SpellTargetType[i] = TARGET_TYPE_UNIT_NEARBY; - break; - case TARGET_UNIT_AREA_ENEMY_SRC: - case TARGET_UNIT_AREA_ALLY_SRC: - case TARGET_UNIT_AREA_ENTRY_SRC: - case TARGET_UNIT_AREA_PARTY_SRC: - case TARGET_GAMEOBJECT_AREA_SRC: - SpellTargetType[i] = TARGET_TYPE_AREA_SRC; - break; - case TARGET_UNIT_AREA_ENEMY_DST: - case TARGET_UNIT_AREA_ALLY_DST: - case TARGET_UNIT_AREA_ENTRY_DST: - case TARGET_UNIT_AREA_PARTY_DST: - case TARGET_GAMEOBJECT_AREA_DST: - SpellTargetType[i] = TARGET_TYPE_AREA_DST; - break; - case TARGET_UNIT_CONE_ENEMY: - case TARGET_UNIT_CONE_ALLY: - case TARGET_UNIT_CONE_ENTRY: - case TARGET_UNIT_CONE_ENEMY_UNKNOWN: - case TARGET_UNIT_AREA_PATH: - case TARGET_GAMEOBJECT_AREA_PATH: - SpellTargetType[i] = TARGET_TYPE_AREA_CONE; - break; - case TARGET_DST_CASTER: - case TARGET_SRC_CASTER: - case TARGET_MINION: - case TARGET_DEST_CASTER_FRONT_LEAP: - case TARGET_DEST_CASTER_FRONT: - case TARGET_DEST_CASTER_BACK: - case TARGET_DEST_CASTER_RIGHT: - case TARGET_DEST_CASTER_LEFT: - case TARGET_DEST_CASTER_FRONT_LEFT: - case TARGET_DEST_CASTER_BACK_LEFT: - case TARGET_DEST_CASTER_BACK_RIGHT: - case TARGET_DEST_CASTER_FRONT_RIGHT: - case TARGET_DEST_CASTER_RANDOM: - case TARGET_DEST_CASTER_RADIUS: - SpellTargetType[i] = TARGET_TYPE_DEST_CASTER; - break; - case TARGET_DST_TARGET_ENEMY: - case TARGET_DEST_TARGET_ANY: - case TARGET_DEST_TARGET_FRONT: - case TARGET_DEST_TARGET_BACK: - case TARGET_DEST_TARGET_RIGHT: - case TARGET_DEST_TARGET_LEFT: - case TARGET_DEST_TARGET_FRONT_LEFT: - case TARGET_DEST_TARGET_BACK_LEFT: - case TARGET_DEST_TARGET_BACK_RIGHT: - case TARGET_DEST_TARGET_FRONT_RIGHT: - case TARGET_DEST_TARGET_RANDOM: - case TARGET_DEST_TARGET_RADIUS: - SpellTargetType[i] = TARGET_TYPE_DEST_TARGET; - break; - case TARGET_DEST_DYNOBJ_ENEMY: - case TARGET_DEST_DYNOBJ_ALLY: - case TARGET_DEST_DYNOBJ_NONE: - case TARGET_DEST_DEST: - case TARGET_DEST_TRAJ: - case TARGET_DEST_DEST_FRONT_LEFT: - case TARGET_DEST_DEST_BACK_LEFT: - case TARGET_DEST_DEST_BACK_RIGHT: - case TARGET_DEST_DEST_FRONT_RIGHT: - case TARGET_DEST_DEST_FRONT: - case TARGET_DEST_DEST_BACK: - case TARGET_DEST_DEST_RIGHT: - case TARGET_DEST_DEST_LEFT: - case TARGET_DEST_DEST_RANDOM: - case TARGET_DEST_DEST_RANDOM_DIR_DIST: - SpellTargetType[i] = TARGET_TYPE_DEST_DEST; - break; - case TARGET_DST_DB: - case TARGET_DST_HOME: - case TARGET_DST_NEARBY_ENTRY: - SpellTargetType[i] = TARGET_TYPE_DEST_SPECIAL; - break; - case TARGET_UNIT_CHANNEL_TARGET: - case TARGET_DEST_CHANNEL_TARGET: - case TARGET_DEST_CHANNEL_CASTER: - SpellTargetType[i] = TARGET_TYPE_CHANNEL; - break; - default: - SpellTargetType[i] = TARGET_TYPE_DEFAULT; - } - } - - for (int32 i = 0; i < TOTAL_SPELL_TARGETS; ++i) - { - switch (i) - { - case TARGET_UNIT_AREA_ENEMY_DST: - case TARGET_UNIT_AREA_ENEMY_SRC: - case TARGET_UNIT_AREA_ALLY_DST: - case TARGET_UNIT_AREA_ALLY_SRC: - case TARGET_UNIT_AREA_ENTRY_DST: - case TARGET_UNIT_AREA_ENTRY_SRC: - case TARGET_UNIT_AREA_PARTY_DST: - case TARGET_UNIT_AREA_PARTY_SRC: - case TARGET_UNIT_TARGET_ALLY_PARTY: - case TARGET_UNIT_PARTY_CASTER: - case TARGET_UNIT_CONE_ENEMY: - case TARGET_UNIT_CONE_ALLY: - case TARGET_UNIT_CONE_ENEMY_UNKNOWN: - case TARGET_UNIT_AREA_PATH: - case TARGET_GAMEOBJECT_AREA_PATH: - case TARGET_UNIT_RAID_CASTER: - IsAreaEffectTarget[i] = true; - break; - default: - IsAreaEffectTarget[i] = false; - break; - } - } -} - -SpellMgr::~SpellMgr() -{ -} - -bool SpellMgr::IsSrcTargetSpell(SpellEntry const *spellInfo) const -{ - for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i) - { - if (SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_AREA_SRC || SpellTargetType[spellInfo->EffectImplicitTargetB[i]] == TARGET_TYPE_AREA_SRC) - return true; - } - return false; -} - -int32 GetSpellDuration(SpellEntry const *spellInfo) -{ - if (!spellInfo) - return 0; - SpellDurationEntry const* du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex); - if (!du) - return 0; - return (du->Duration[0] == -1) ? -1 : abs(du->Duration[0]); -} - -int32 GetSpellMaxDuration(SpellEntry const *spellInfo) -{ - if (!spellInfo) - return 0; - SpellDurationEntry const* du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex); - if (!du) - return 0; - return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]); -} - -uint32 GetDispelChance(Unit* auraCaster, Unit* target, uint32 spellId, bool offensive, bool *result) -{ - // we assume that aura dispel chance is 100% on start - // need formula for level difference based chance - int32 resist_chance = 0; - - // Apply dispel mod from aura caster - if (auraCaster) - if (Player* modOwner = auraCaster->GetSpellModOwner()) - modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_DISPEL_CHANCE, resist_chance); - - // Dispel resistance from target SPELL_AURA_MOD_DISPEL_RESIST - // Only affects offensive dispels - if (offensive && target) - resist_chance += target->GetTotalAuraModifier(SPELL_AURA_MOD_DISPEL_RESIST); - - // Try dispel - if (result) - *result = !roll_chance_i(resist_chance); - - resist_chance = resist_chance < 0 ? 0 : resist_chance; - resist_chance = resist_chance > 100 ? 100 : resist_chance; - return 100 - resist_chance; -} - -uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell* spell) -{ - SpellCastTimesEntry const* spellCastTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex); - - // not all spells have cast time index and this is all is pasiive abilities - if (!spellCastTimeEntry) - return 0; - - int32 castTime = spellCastTimeEntry->CastTime; - - if (Unit *caster = (spell ? spell->GetCaster() : NULL)) - caster->ModSpellCastTime(spellInfo, castTime, spell); - - if (spellInfo->Attributes & SPELL_ATTR0_REQ_AMMO && (!spell || !(spell->IsAutoRepeat()))) - castTime += 500; - - return (castTime > 0) ? uint32(castTime) : 0; -} - -bool IsPassiveSpell(uint32 spellId) -{ - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) + SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(skill); + if (!pSkill) return false; - return IsPassiveSpell(spellInfo); -} - -bool IsPassiveSpell(SpellEntry const* spellInfo) -{ - if (spellInfo->Attributes & SPELL_ATTR0_PASSIVE) - return true; - return false; -} -bool IsAutocastableSpell(uint32 spellId) -{ - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) - return false; - if (spellInfo->Attributes & SPELL_ATTR0_PASSIVE) + if (pSkill->categoryId != SKILL_CATEGORY_PROFESSION) return false; - if (spellInfo->AttributesEx & SPELL_ATTR1_UNAUTOCASTABLE_BY_PET) - return false; - return true; -} - -bool IsHigherHankOfSpell(uint32 spellId_1, uint32 spellId_2) -{ - return sSpellMgr->GetSpellRank(spellId_1) < sSpellMgr->GetSpellRank(spellId_2); -} -uint32 CalculatePowerCost(SpellEntry const* spellInfo, Unit const* caster, SpellSchoolMask schoolMask) -{ - // Spell drain all exist power on cast (Only paladin lay of Hands) - if (spellInfo->AttributesEx & SPELL_ATTR1_DRAIN_ALL_POWER) - { - // If power type - health drain all - if (spellInfo->powerType == POWER_HEALTH) - return caster->GetHealth(); - // Else drain all power - if (spellInfo->powerType < MAX_POWERS) - return caster->GetPower(Powers(spellInfo->powerType)); - sLog->outError("CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id); - return 0; - } - - // Base powerCost - int32 powerCost = spellInfo->manaCost; - // PCT cost from total amount - if (spellInfo->ManaCostPercentage) - { - switch (spellInfo->powerType) - { - // health as power used - case POWER_HEALTH: - powerCost += int32(CalculatePctU(caster->GetCreateHealth(), spellInfo->ManaCostPercentage)); - break; - case POWER_MANA: - powerCost += int32(CalculatePctU(caster->GetCreateMana(), spellInfo->ManaCostPercentage)); - break; - case POWER_RAGE: - case POWER_FOCUS: - case POWER_ENERGY: - case POWER_HAPPINESS: - powerCost += int32(CalculatePctU(caster->GetMaxPower(Powers(spellInfo->powerType)), spellInfo->ManaCostPercentage)); - break; - case POWER_RUNE: - case POWER_RUNIC_POWER: - sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "CalculateManaCost: Not implemented yet!"); - break; - default: - sLog->outError("CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id); - return 0; - } - } - SpellSchools school = GetFirstSchoolInMask(schoolMask); - // Flat mod from caster auras by spell school - powerCost += caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school); - // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) - if (spellInfo->AttributesEx4 & SPELL_ATTR4_SPELL_VS_EXTEND_COST) - powerCost += caster->GetAttackTime(OFF_ATTACK) / 100; - // Apply cost mod by spell - if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, powerCost); - - if (spellInfo->Attributes & SPELL_ATTR0_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; + return true; } -bool IsSpellRequiringFocusedTarget(SpellEntry const* spellInfo) +bool IsPartOfSkillLine(uint32 skillId, uint32 spellId) { - for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS; ++i) - { - if (SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_UNIT_TARGET - || SpellTargetType[spellInfo->EffectImplicitTargetB[i]] == TARGET_TYPE_UNIT_TARGET - || SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_CHANNEL - || SpellTargetType[spellInfo->EffectImplicitTargetB[i]] == TARGET_TYPE_CHANNEL - || SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_DEST_TARGET - || SpellTargetType[spellInfo->EffectImplicitTargetB[i]] == TARGET_TYPE_DEST_TARGET) + SkillLineAbilityMapBounds skillBounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId); + for (SkillLineAbilityMap::const_iterator itr = skillBounds.first; itr != skillBounds.second; ++itr) + if (itr->second->skillId == skillId) return true; - } - return false; -} -Unit* GetTriggeredSpellCaster(SpellEntry const* spellInfo, Unit* caster, Unit* target) -{ - if (IsSpellRequiringFocusedTarget(spellInfo)) - return caster; - return target; + return false; } -AuraState GetSpellAuraState(SpellEntry const* spellInfo) +DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, bool triggered) { - // Seals - if (IsSealSpell(spellInfo)) - return AURA_STATE_JUDGEMENT; - - // Conflagrate aura state on Immolate and Shadowflame - if (spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && - // Immolate - ((spellInfo->SpellFamilyFlags[0] & 4) || - // Shadowflame - (spellInfo->SpellFamilyFlags[2] & 2))) - return AURA_STATE_CONFLAGRATE; - - // Faerie Fire (druid versions) - if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x400) - return AURA_STATE_FAERIE_FIRE; - - // Sting (hunter's pet ability) - if (spellInfo->Category == 1133) - return AURA_STATE_FAERIE_FIRE; - - // Victorious - if (spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellFamilyFlags[1] & 0x00040000) - return AURA_STATE_WARRIOR_VICTORY_RUSH; - - // Swiftmend state on Regrowth & Rejuvenation - if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x50) - return AURA_STATE_SWIFTMEND; - - // Deadly poison aura state - if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags[0] & 0x10000) - return AURA_STATE_DEADLY_POISON; - - // Enrage aura state - if (spellInfo->Dispel == DISPEL_ENRAGE) - return AURA_STATE_ENRAGE; - - // Bleeding aura state - if (GetAllSpellMechanicMask(spellInfo) & 1<<MECHANIC_BLEED) - return AURA_STATE_BLEEDING; - - if (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STUN - || spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_ROOT) - return AURA_STATE_FROZEN; + if (spellproto->IsPositive()) + return DIMINISHING_NONE; - switch (spellInfo->Id) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { - case 71465: // Divine Surge - return AURA_STATE_UNKNOWN22; - default: - break; + if (spellproto->Effects[i].ApplyAuraName == SPELL_AURA_MOD_TAUNT) + return DIMINISHING_TAUNT; } - return AURA_STATE_NONE; -} - -SpellSpecific GetSpellSpecific(SpellEntry const* spellInfo) -{ - switch (spellInfo->SpellFamilyName) + // Explicit Diminishing Groups + switch (spellproto->SpellFamilyName) { case SPELLFAMILY_GENERIC: { - // Food / Drinks (mostly) - if (spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) - { - bool food = false; - bool drink = false; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - switch (spellInfo->EffectApplyAuraName[i]) - { - // Food - case SPELL_AURA_MOD_REGEN: - case SPELL_AURA_OBS_MOD_HEALTH: - food = true; - break; - // Drink - case SPELL_AURA_MOD_POWER_REGEN: - case SPELL_AURA_OBS_MOD_POWER: - drink = true; - break; - default: - break; - } - } - - if (food && drink) - return SPELL_SPECIFIC_FOOD_AND_DRINK; - else if (food) - return SPELL_SPECIFIC_FOOD; - else if (drink) - return SPELL_SPECIFIC_DRINK; - } - // scrolls effects - else - { - uint32 firstSpell = sSpellMgr->GetFirstSpellInChain(spellInfo->Id); - switch (firstSpell) - { - case 8118: // Strength - case 8099: // Stamina - case 8112: // Spirit - case 8096: // Intellect - case 8115: // Agility - case 8091: // Armor - return SPELL_SPECIFIC_SCROLL; - case 12880: // Enrage (Enrage) - case 57518: // Enrage (Wrecking Crew) - return SPELL_SPECIFIC_WARRIOR_ENRAGE; - } - } + // Pet charge effects (Infernal Awakening, Demon Charge) + if (spellproto->SpellVisual[0] == 2816 && spellproto->SpellIconID == 15) + return DIMINISHING_CONTROLLED_STUN; + // Gnaw + else if (spellproto->Id == 47481) + return DIMINISHING_CONTROLLED_STUN; break; } + // Event spells + case SPELLFAMILY_UNK1: + return DIMINISHING_NONE; case SPELLFAMILY_MAGE: { - // family flags 18(Molten), 25(Frost/Ice), 28(Mage) - if (spellInfo->SpellFamilyFlags[0] & 0x12040000) - return SPELL_SPECIFIC_MAGE_ARMOR; - - // Arcane brillance and Arcane intelect (normal check fails because of flags difference) - if (spellInfo->SpellFamilyFlags[0] & 0x400) - return SPELL_SPECIFIC_MAGE_ARCANE_BRILLANCE; - - if ((spellInfo->SpellFamilyFlags[0] & 0x1000000) && spellInfo->EffectApplyAuraName[0] == SPELL_AURA_MOD_CONFUSE) - return SPELL_SPECIFIC_MAGE_POLYMORPH; - + // Frostbite + if (spellproto->SpellFamilyFlags[1] & 0x80000000) + return DIMINISHING_ROOT; + // Shattered Barrier + else if (spellproto->SpellVisual[0] == 12297) + return DIMINISHING_ROOT; + // Deep Freeze + else if (spellproto->SpellIconID == 2939 && spellproto->SpellVisual[0] == 9963) + return DIMINISHING_CONTROLLED_STUN; + // Frost Nova / Freeze (Water Elemental) + else if (spellproto->SpellIconID == 193) + return DIMINISHING_CONTROLLED_ROOT; + // Dragon's Breath + else if (spellproto->SpellFamilyFlags[0] & 0x800000) + return DIMINISHING_DISORIENT; break; } case SPELLFAMILY_WARRIOR: { - if (spellInfo->Id == 12292) // Death Wish - return SPELL_SPECIFIC_WARRIOR_ENRAGE; - + // Improved Hamstring + if (spellproto->AttributesEx3 & 0x80000 && spellproto->SpellIconID == 23) + return DIMINISHING_ROOT; + // Charge Stun (own diminishing) + else if (spellproto->SpellFamilyFlags[0] & 0x01000000) + return DIMINISHING_CHARGE; break; } case SPELLFAMILY_WARLOCK: { - // only warlock curses have this - if (spellInfo->Dispel == DISPEL_CURSE) - return SPELL_SPECIFIC_CURSE; - - // Warlock (Demon Armor | Demon Skin | Fel Armor) - if (spellInfo->SpellFamilyFlags[1] & 0x20000020 || spellInfo->SpellFamilyFlags[2] & 0x00000010) - return SPELL_SPECIFIC_WARLOCK_ARMOR; - - //seed of corruption and corruption - if (spellInfo->SpellFamilyFlags[1] & 0x10 || spellInfo->SpellFamilyFlags[0] & 0x2) - return SPELL_SPECIFIC_WARLOCK_CORRUPTION; + // Death Coil + if (spellproto->SpellFamilyFlags[0] & 0x80000) + return DIMINISHING_HORROR; + // Seduction + else if (spellproto->SpellFamilyFlags[1] & 0x10000000) + return DIMINISHING_FEAR; break; } case SPELLFAMILY_PRIEST: { - // Divine Spirit and Prayer of Spirit - if (spellInfo->SpellFamilyFlags[0] & 0x20) - return SPELL_SPECIFIC_PRIEST_DIVINE_SPIRIT; - + // Psychic Horror + if (spellproto->SpellFamilyFlags[2] & 0x2000) + return DIMINISHING_HORROR; break; } - case SPELLFAMILY_HUNTER: + case SPELLFAMILY_DRUID: { - // only hunter stings have this - if (spellInfo->Dispel == DISPEL_POISON) - return SPELL_SPECIFIC_STING; - - // only hunter aspects have this (but not all aspects in hunter family) - if (spellInfo->SpellFamilyFlags.HasFlag(0x00380000, 0x00440000, 0x00001010)) - return SPELL_SPECIFIC_ASPECT; - + // Pounce + if (spellproto->SpellFamilyFlags[0] & 0x20000) + return DIMINISHING_OPENING_STUN; + // Cyclone + else if (spellproto->SpellFamilyFlags[1] & 0x20) + return DIMINISHING_CYCLONE; + // Entangling Roots + // Nature's Grasp + else if (spellproto->SpellFamilyFlags[0] & 0x00000200) + return DIMINISHING_CONTROLLED_ROOT; break; } - case SPELLFAMILY_PALADIN: + case SPELLFAMILY_ROGUE: { - if (IsSealSpell(spellInfo)) - return SPELL_SPECIFIC_SEAL; - - if (spellInfo->SpellFamilyFlags[0] & 0x00002190) - return SPELL_SPECIFIC_HAND; - - // Judgement of Wisdom, Judgement of Light, Judgement of Justice - if (spellInfo->Id == 20184 || spellInfo->Id == 20185 || spellInfo->Id == 20186) - return SPELL_SPECIFIC_JUDGEMENT; - - // only paladin auras have this (for palaldin class family) - if (spellInfo->SpellFamilyFlags[2] & 0x00000020) - return SPELL_SPECIFIC_AURA; - + // Gouge + if (spellproto->SpellFamilyFlags[0] & 0x8) + return DIMINISHING_DISORIENT; + // Blind + else if (spellproto->SpellFamilyFlags[0] & 0x1000000) + return DIMINISHING_FEAR; + // Cheap Shot + else if (spellproto->SpellFamilyFlags[0] & 0x400) + return DIMINISHING_OPENING_STUN; break; } - case SPELLFAMILY_SHAMAN: + case SPELLFAMILY_HUNTER: { - if (IsElementalShield(spellInfo)) - return SPELL_SPECIFIC_ELEMENTAL_SHIELD; - + // Scatter Shot (own diminishing) + if ((spellproto->SpellFamilyFlags[0] & 0x40000) && spellproto->SpellIconID == 132) + return DIMINISHING_SCATTER_SHOT; + // Entrapment (own diminishing) + else if (spellproto->SpellVisual[0] == 7484 && spellproto->SpellIconID == 20) + return DIMINISHING_ENTRAPMENT; + // Wyvern Sting mechanic is MECHANIC_SLEEP but the diminishing is DIMINISHING_DISORIENT + else if ((spellproto->SpellFamilyFlags[1] & 0x1000) && spellproto->SpellIconID == 1721) + return DIMINISHING_DISORIENT; + // Freezing Arrow + else if (spellproto->SpellFamilyFlags[0] & 0x8) + return DIMINISHING_DISORIENT; break; } - - case SPELLFAMILY_DEATHKNIGHT: - if (spellInfo->Id == 48266 || spellInfo->Id == 48263 || spellInfo->Id == 48265) - //if (spellInfo->Category == 47) - return SPELL_SPECIFIC_PRESENCE; + case SPELLFAMILY_PALADIN: + { + // Turn Evil + if ((spellproto->SpellFamilyFlags[1] & 0x804000) && spellproto->SpellIconID == 309) + return DIMINISHING_FEAR; break; - } - - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) + } + case SPELLFAMILY_DEATHKNIGHT: { - switch (spellInfo->EffectApplyAuraName[i]) - { - case SPELL_AURA_MOD_CHARM: - case SPELL_AURA_MOD_POSSESS_PET: - case SPELL_AURA_MOD_POSSESS: - case SPELL_AURA_AOE_CHARM: - return SPELL_SPECIFIC_CHARM; - case SPELL_AURA_TRACK_CREATURES: - case SPELL_AURA_TRACK_RESOURCES: - case SPELL_AURA_TRACK_STEALTHED: - return SPELL_SPECIFIC_TRACKER; - case SPELL_AURA_PHASE: - return SPELL_SPECIFIC_PHASE; - } + // Hungering Cold (no flags) + if (spellproto->SpellIconID == 2797) + return DIMINISHING_DISORIENT; + break; } + default: + break; } - return SPELL_SPECIFIC_NORMAL; -} + // Lastly - Set diminishing depending on mechanic + uint32 mechanic = spellproto->GetAllEffectsMechanicMask(); + if (mechanic & (1 << MECHANIC_CHARM)) + return DIMINISHING_MIND_CONTROL; + if (mechanic & (1 << MECHANIC_SILENCE)) + return DIMINISHING_SILENCE; + if (mechanic & (1 << MECHANIC_SLEEP)) + return DIMINISHING_SLEEP; + if (mechanic & ((1 << MECHANIC_SAPPED) | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_SHACKLE))) + return DIMINISHING_DISORIENT; + // Mechanic Knockout, except Blast Wave + if (mechanic & (1 << MECHANIC_KNOCKOUT) && spellproto->SpellIconID != 292) + return DIMINISHING_DISORIENT; + if (mechanic & (1 << MECHANIC_DISARM)) + return DIMINISHING_DISARM; + if (mechanic & (1 << MECHANIC_FEAR)) + return DIMINISHING_FEAR; + if (mechanic & (1 << MECHANIC_STUN)) + return triggered ? DIMINISHING_STUN : DIMINISHING_CONTROLLED_STUN; + if (mechanic & (1 << MECHANIC_BANISH)) + return DIMINISHING_BANISH; + if (mechanic & (1 << MECHANIC_ROOT)) + return triggered ? DIMINISHING_ROOT : DIMINISHING_CONTROLLED_ROOT; -// target not allow have more one spell specific from same caster -bool IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1, SpellSpecific spellSpec2) -{ - switch (spellSpec1) - { - case SPELL_SPECIFIC_SEAL: - case SPELL_SPECIFIC_HAND: - case SPELL_SPECIFIC_AURA: - case SPELL_SPECIFIC_STING: - case SPELL_SPECIFIC_CURSE: - case SPELL_SPECIFIC_ASPECT: - case SPELL_SPECIFIC_JUDGEMENT: - case SPELL_SPECIFIC_WARLOCK_CORRUPTION: - return spellSpec1 == spellSpec2; - default: - return false; - } + return DIMINISHING_NONE; } -bool IsSingleFromSpellSpecificPerTarget(SpellSpecific spellSpec1, SpellSpecific spellSpec2) +DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) { - switch (spellSpec1) + switch (group) { - case SPELL_SPECIFIC_PHASE: - case SPELL_SPECIFIC_TRACKER: - case SPELL_SPECIFIC_WARLOCK_ARMOR: - case SPELL_SPECIFIC_MAGE_ARMOR: - case SPELL_SPECIFIC_ELEMENTAL_SHIELD: - case SPELL_SPECIFIC_MAGE_POLYMORPH: - case SPELL_SPECIFIC_PRESENCE: - case SPELL_SPECIFIC_CHARM: - case SPELL_SPECIFIC_SCROLL: - case SPELL_SPECIFIC_WARRIOR_ENRAGE: - case SPELL_SPECIFIC_MAGE_ARCANE_BRILLANCE: - case SPELL_SPECIFIC_PRIEST_DIVINE_SPIRIT: - return spellSpec1 == spellSpec2; - case SPELL_SPECIFIC_FOOD: - return spellSpec2 == SPELL_SPECIFIC_FOOD - || spellSpec2 == SPELL_SPECIFIC_FOOD_AND_DRINK; - case SPELL_SPECIFIC_DRINK: - return spellSpec2 == SPELL_SPECIFIC_DRINK - || spellSpec2 == SPELL_SPECIFIC_FOOD_AND_DRINK; - case SPELL_SPECIFIC_FOOD_AND_DRINK: - return spellSpec2 == SPELL_SPECIFIC_FOOD - || spellSpec2 == SPELL_SPECIFIC_DRINK - || spellSpec2 == SPELL_SPECIFIC_FOOD_AND_DRINK; + case DIMINISHING_TAUNT: + case DIMINISHING_CONTROLLED_STUN: + case DIMINISHING_STUN: + case DIMINISHING_OPENING_STUN: + case DIMINISHING_CYCLONE: + case DIMINISHING_CHARGE: + return DRTYPE_ALL; default: - return false; + return DRTYPE_PLAYER; } } -bool IsPositiveTarget(uint32 targetA, uint32 targetB) +DiminishingLevels GetDiminishingReturnsMaxLevel(DiminishingGroup group) { - // non-positive targets - switch (targetA) + switch (group) { - case TARGET_UNIT_NEARBY_ENEMY: - case TARGET_UNIT_TARGET_ENEMY: - case TARGET_UNIT_AREA_ENEMY_SRC: - case TARGET_UNIT_AREA_ENEMY_DST: - case TARGET_UNIT_CONE_ENEMY: - case TARGET_UNIT_AREA_PATH: - case TARGET_DEST_DYNOBJ_ENEMY: - case TARGET_DST_TARGET_ENEMY: - return false; + case DIMINISHING_TAUNT: + return DIMINISHING_LEVEL_TAUNT_IMMUNE; default: - break; + return DIMINISHING_LEVEL_IMMUNE; } - if (targetB) - return IsPositiveTarget(targetB, 0); - return true; } -bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) const +int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellInfo const* spellproto) { - SpellEntry const* spellproto = sSpellStore.LookupEntry(spellId); - if (!spellproto) - return false; - - // not found a single positive spell with this attribute - if (spellproto->Attributes & SPELL_ATTR0_NEGATIVE_1) - return false; + if (!IsDiminishingReturnsGroupDurationLimited(group)) + return 0; + // Explicit diminishing duration switch (spellproto->SpellFamilyName) { - case SPELLFAMILY_GENERIC: - switch (spellId) - { - case 34700: // Allergic Reaction - case 61716: // Rabbit Costume - case 61734: // Noblegarden Bunny - case 61987: // Avenging Wrath Marker - case 61988: // Divine Shield exclude aura - case 62532: // Conservator's Grip - return false; - case 30877: // Tag Murloc - case 62344: // Fists of Stone - return true; - default: - break; - } - break; - case SPELLFAMILY_MAGE: - // Amplify Magic, Dampen Magic - if (spellproto->SpellFamilyFlags[0] == 0x00002000) - return true; - // Ignite - if (spellproto->SpellIconID == 45) - return true; - break; - case SPELLFAMILY_PRIEST: - switch (spellId) - { - case 64844: // Divine Hymn - case 64904: // Hymn of Hope - case 47585: // Dispersion - return true; - default: - break; - } + case SPELLFAMILY_DRUID: + { + // Faerie Fire - limit to 40 seconds in PvP (3.1) + if (spellproto->SpellFamilyFlags[0] & 0x400) + return 40 * IN_MILLISECONDS; break; + } case SPELLFAMILY_HUNTER: - // Aspect of the Viper - if (spellId == 34074) - return true; + { + // Wyvern Sting + if (spellproto->SpellFamilyFlags[1] & 0x1000) + return 6 * IN_MILLISECONDS; + // Hunter's Mark + if (spellproto->SpellFamilyFlags[0] & 0x400) + return 120 * IN_MILLISECONDS; break; - case SPELLFAMILY_SHAMAN: - if (spellId == 30708) - return false; + } + case SPELLFAMILY_PALADIN: + { + // Repentance - limit to 6 seconds in PvP + if (spellproto->SpellFamilyFlags[0] & 0x4) + return 6 * IN_MILLISECONDS; break; + } + case SPELLFAMILY_WARLOCK: + { + // Banish - limit to 6 seconds in PvP + if (spellproto->SpellFamilyFlags[1] & 0x8000000) + return 6 * IN_MILLISECONDS; + // Curse of Tongues - limit to 12 seconds in PvP + else if (spellproto->SpellFamilyFlags[2] & 0x800) + return 12 * IN_MILLISECONDS; + // Curse of Elements - limit to 120 seconds in PvP + else if (spellproto->SpellFamilyFlags[1] & 0x200) + return 120 * IN_MILLISECONDS; + break; + } default: break; } - switch (spellproto->Mechanic) + return 10 * IN_MILLISECONDS; +} + +bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group) +{ + switch (group) { - case MECHANIC_IMMUNE_SHIELD: + case DIMINISHING_CONTROLLED_STUN: + case DIMINISHING_STUN: + case DIMINISHING_ENTRAPMENT: + case DIMINISHING_CONTROLLED_ROOT: + case DIMINISHING_ROOT: + case DIMINISHING_FEAR: + case DIMINISHING_MIND_CONTROL: + case DIMINISHING_DISORIENT: + case DIMINISHING_CYCLONE: + case DIMINISHING_BANISH: + case DIMINISHING_LIMITONLY: + case DIMINISHING_OPENING_STUN: + case DIMINISHING_HORROR: + case DIMINISHING_SLEEP: return true; default: - break; + return false; } +} - // 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; - } +SpellMgr::SpellMgr() +{ +} - 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; +SpellMgr::~SpellMgr() +{ + UnloadSpellInfoStore(); +} - // non-positive aura use - case SPELL_EFFECT_APPLY_AURA: - case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: +/// Some checks for spells, to prevent adding deprecated/broken spells for trainers, spell book, etc +bool SpellMgr::IsSpellValid(SpellInfo const *spellInfo, Player *pl, bool msg) +{ + // not exist + if (!spellInfo) + return false; + + bool need_check_reagents = false; + + // check effects + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + switch (spellInfo->Effects[i].Effect) { - switch (spellproto->EffectApplyAuraName[effIndex]) + 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: { - 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_DODGE_PERCENT: - case SPELL_AURA_MOD_HEALING_PCT: - case SPELL_AURA_MOD_HEALING_DONE: - case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: - if (SpellMgr::CalculateSpellEffectAmount(spellproto, effIndex) < 0) - return false; - break; - case SPELL_AURA_MOD_DAMAGE_TAKEN: // dependent from bas point sign (positive -> negative) - if (SpellMgr::CalculateSpellEffectAmount(spellproto, effIndex) > 0) - return false; - break; - case SPELL_AURA_MOD_CRIT_PCT: - case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: - if (SpellMgr::CalculateSpellEffectAmount(spellproto, effIndex) > 0) - return true; // some expected positive spells have SPELL_ATTR1_NEGATIVE - break; - case SPELL_AURA_ADD_TARGET_TRIGGER: - return true; - case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE: - case SPELL_AURA_PERIODIC_TRIGGER_SPELL: - if (!deep) + if (spellInfo->Effects[i].ItemType == 0) + { + // skip auto-loot crafting spells, its not need explicit item info (but have special fake items sometime) + if (!spellInfo->IsLootCrafting()) { - uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex]; - SpellEntry const* spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId); - - if (spellTriggeredProto) + if (msg) { - // non-positive targets of main spell return early - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (!spellTriggeredProto->Effect[i]) - continue; - // if non-positive trigger cast targeted to positive target this main cast is non-positive - // this will place this spell auras as debuffs - if (IsPositiveTarget(spellTriggeredProto->EffectImplicitTargetA[effIndex], spellTriggeredProto->EffectImplicitTargetB[effIndex]) && !_isPositiveEffect(spellTriggeredId, i, true)) - return false; - } + if (pl) + ChatHandler(pl).PSendSysMessage("Craft spell %u not have create item entry.", spellInfo->Id); + else + sLog->outErrorDb("Craft spell %u not have create item entry.", spellInfo->Id); } - } - case SPELL_AURA_PROC_TRIGGER_SPELL: - // many positive auras have negative triggered spells at damage for example and this not make it negative (it can be canceled for example) - break; - case SPELL_AURA_MOD_STUN: //have positive and negative spells, we can't sort its correctly at this moment. - if (effIndex == 0 && spellproto->Effect[1] == 0 && spellproto->Effect[2] == 0) - return false; // but all single stun aura spells is negative - break; - case SPELL_AURA_MOD_PACIFY_SILENCE: - if (spellproto->Id == 24740) // Wisp Costume - return true; - return false; - case SPELL_AURA_MOD_ROOT: - case SPELL_AURA_MOD_SILENCE: - case SPELL_AURA_GHOST: - case SPELL_AURA_PERIODIC_LEECH: - case SPELL_AURA_MOD_STALKED: - case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: - case SPELL_AURA_PREVENT_RESSURECTION: - return false; - case SPELL_AURA_PERIODIC_DAMAGE: // used in positive spells also. - // part of negative spell if casted at self (prevent cancel) - if (spellproto->EffectImplicitTargetA[effIndex] == TARGET_UNIT_CASTER) - return false; - break; - case SPELL_AURA_MOD_DECREASE_SPEED: // used in positive spells also - // part of positive spell if casted at self - if (spellproto->EffectImplicitTargetA[effIndex] != TARGET_UNIT_CASTER) return false; - // but not this if this first effect (didn't find better check) - if (spellproto->Attributes & SPELL_ATTR0_NEGATIVE_1 && effIndex == 0) - return false; - break; - case SPELL_AURA_MECHANIC_IMMUNITY: + } + + } + // also possible IsLootCrafting case but fake item must exist anyway + else if (!sObjectMgr->GetItemTemplate(spellInfo->Effects[i].ItemType)) { - // non-positive immunities - switch (spellproto->EffectMiscValue[effIndex]) + if (msg) { - case MECHANIC_BANDAGE: - case MECHANIC_SHIELD: - case MECHANIC_MOUNT: - case MECHANIC_INVULNERABILITY: - return false; - default: - break; + if (pl) + ChatHandler(pl).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Effects[i].ItemType); + else + sLog->outErrorDb("Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Effects[i].ItemType); } - break; + return false; } - case SPELL_AURA_ADD_FLAT_MODIFIER: // mods - case SPELL_AURA_ADD_PCT_MODIFIER: + + need_check_reagents = true; + break; + } + case SPELL_EFFECT_LEARN_SPELL: + { + SpellInfo const* spellInfo2 = sSpellMgr->GetSpellInfo(spellInfo->Effects[i].TriggerSpell); + if (!IsSpellValid(spellInfo2, pl, msg)) { - // non-positive mods - switch (spellproto->EffectMiscValue[effIndex]) + if (msg) { - case SPELLMOD_COST: // dependent from bas point sign (negative -> positive) - if (SpellMgr::CalculateSpellEffectAmount(spellproto, effIndex) > 0) - { - if (!deep) - { - bool negative = true; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (i != effIndex) - if (_isPositiveEffect(spellId, i, true)) - { - negative = false; - break; - } - } - if (negative) - return false; - } - } - break; - default: - break; + if (pl) + ChatHandler(pl).PSendSysMessage("Spell %u learn to broken spell %u, and then...", spellInfo->Id, spellInfo->Effects[i].TriggerSpell); + else + sLog->outErrorDb("Spell %u learn to invalid spell %u, and then...", spellInfo->Id, spellInfo->Effects[i].TriggerSpell); } - break; + return false; } - default: - break; + break; } - break; } - default: - break; } - // non-positive targets - if (!IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex], spellproto->EffectImplicitTargetB[effIndex])) - return false; - - if (!deep && spellproto->EffectTriggerSpell[effIndex] - && !spellproto->EffectApplyAuraName[effIndex] - && IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex], spellproto->EffectImplicitTargetB[effIndex]) - && !_isPositiveSpell(spellproto->EffectTriggerSpell[effIndex], true)) - return false; + if (need_check_reagents) + { + for (uint8 j = 0; j < MAX_SPELL_REAGENTS; ++j) + { + if (spellInfo->Reagent[j] > 0 && !sObjectMgr->GetItemTemplate(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; + } + } + } - // ok, positive return true; } -bool IsPositiveSpell(uint32 spellId) +uint32 SpellMgr::GetSpellDifficultyId(uint32 spellId) const { - if (!sSpellStore.LookupEntry(spellId)) // non-existing spells - return false; - return !(sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE); + SpellDifficultySearcherMap::const_iterator i = mSpellDifficultySearcherMap.find(spellId); + return i == mSpellDifficultySearcherMap.end() ? 0 : (*i).second; } -bool IsPositiveEffect(uint32 spellId, uint32 effIndex) +void SpellMgr::SetSpellDifficultyId(uint32 spellId, uint32 id) { - if (!sSpellStore.LookupEntry(spellId)) - return false; - switch (effIndex) - { - default: - case 0: - return !(sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE_EFF0); - case 1: - return !(sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE_EFF1); - case 2: - return !(sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE_EFF2); - } + mSpellDifficultySearcherMap[spellId] = id; } -bool SpellMgr::_isPositiveSpell(uint32 spellId, bool deep) const +uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, Unit* caster) const { - SpellEntry const* spellproto = sSpellStore.LookupEntry(spellId); - if (!spellproto) - return false; + if (!GetSpellInfo(spellId)) + return spellId; - // 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 (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (!_isPositiveEffect(spellId, i, deep)) - return false; - return true; -} - -bool IsSingleTargetSpell(SpellEntry const* spellInfo) -{ - // all other single target spells have if it has AttributesEx5 - if (spellInfo->AttributesEx5 & SPELL_ATTR5_SINGLE_TARGET_SPELL) - return true; + if (!caster || !caster->GetMap() || !caster->GetMap()->IsDungeon()) + return spellId; - switch (GetSpellSpecific(spellInfo)) + uint32 mode = uint32(caster->GetMap()->GetSpawnMode()); + if (mode >= MAX_DIFFICULTY) { - case SPELL_SPECIFIC_JUDGEMENT: - return true; - default: - break; + sLog->outError("SpellMgr::GetSpellIdForDifficulty: Incorrect Difficulty for spell %u.", spellId); + return spellId; //return source spell } - return false; -} + uint32 difficultyId = GetSpellDifficultyId(spellId); + if (!difficultyId) + return spellId; //return source spell, it has only REGULAR_DIFFICULTY -bool IsSingleTargetSpells(SpellEntry const* spellInfo1, SpellEntry const* spellInfo2) -{ - // TODO - need better check - // Equal icon and spellfamily - if (spellInfo1->SpellFamilyName == spellInfo2->SpellFamilyName && - spellInfo1->SpellIconID == spellInfo2->SpellIconID) - return true; - - // TODO - need found Judgements rule - SpellSpecific spec1 = GetSpellSpecific(spellInfo1); - // spell with single target specific types - switch (spec1) + SpellDifficultyEntry const *difficultyEntry = sSpellDifficultyStore.LookupEntry(difficultyId); + if (!difficultyEntry) { - case SPELL_SPECIFIC_JUDGEMENT: - case SPELL_SPECIFIC_MAGE_POLYMORPH: - if (GetSpellSpecific(spellInfo2) == spec1) - return true; - break; - default: - break; + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellIdForDifficulty: SpellDifficultyEntry not found for spell %u. This should never happen.", spellId); + return spellId; //return source spell } - return false; -} - -SpellCastResult GetErrorAtShapeshiftedCast(SpellEntry const* spellInfo, uint32 form) -{ - // talents that learn spells can have stance requirements that need ignore - // (this requirement only for client-side stance show in talent description) - if (GetTalentSpellCost(spellInfo->Id) > 0 && - (spellInfo->Effect[0] == SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[1] == SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[2] == SPELL_EFFECT_LEARN_SPELL)) - return SPELL_CAST_OK; - - uint32 stanceMask = (form ? 1 << (form - 1) : 0); - - if (stanceMask & spellInfo->StancesNot) // can explicitly not be casted in this stance - return SPELL_FAILED_NOT_SHAPESHIFT; - - if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance - return SPELL_CAST_OK; - - bool actAsShifted = false; - SpellShapeshiftEntry const* shapeInfo = NULL; - if (form > 0) + if (difficultyEntry->SpellID[mode] <= 0 && mode > DUNGEON_DIFFICULTY_HEROIC) { - 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 + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellIdForDifficulty: spell %u mode %u spell is NULL, using mode %u", spellId, mode, mode - 2); + mode -= 2; } - if (actAsShifted) - { - if (spellInfo->Attributes & SPELL_ATTR0_NOT_SHAPESHIFT) // not while shapeshifted - return SPELL_FAILED_NOT_SHAPESHIFT; - else if (spellInfo->Stances != 0) // needs other shapeshift - return SPELL_FAILED_ONLY_SHAPESHIFT; - } - else + if (difficultyEntry->SpellID[mode] <= 0) { - // needs shapeshift - if (!(spellInfo->AttributesEx2 & SPELL_ATTR2_NOT_NEED_SHAPESHIFT) && spellInfo->Stances != 0) - return SPELL_FAILED_ONLY_SHAPESHIFT; + sLog->outErrorDb("SpellMgr::GetSpellIdForDifficulty: spell %u mode %u spell is 0. Check spelldifficulty_dbc!", spellId, mode); + return spellId; } - // Check if stance disables cast of not-stance spells - // Example: cannot cast any other spells in zombie or ghoul form - // TODO: Find a way to disable use of these spells clientside - if (shapeInfo && shapeInfo->flags1 & 0x400) + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellIdForDifficulty: spellid for spell %u in mode %u is %d", spellId, mode, difficultyEntry->SpellID[mode]); + return uint32(difficultyEntry->SpellID[mode]); +} + +SpellInfo const* SpellMgr::GetSpellForDifficultyFromSpell(SpellInfo const* spell, Unit* caster) const +{ + uint32 newSpellId = GetSpellIdForDifficulty(spell->Id, caster); + SpellInfo const* newSpell = GetSpellInfo(newSpellId); + if (!newSpell) { - if (!(stanceMask & spellInfo->Stances)) - return SPELL_FAILED_ONLY_SHAPESHIFT; + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellForDifficultyFromSpell: spell %u not found. Check spelldifficulty_dbc!", newSpellId); + return spell; } - return SPELL_CAST_OK; + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "SpellMgr::GetSpellForDifficultyFromSpell: Spell id for instance mode is %u (original %u)", newSpell->Id, spell->Id); + return newSpell; } -void SpellMgr::LoadSpellTargetPositions() +SpellChainNode const* SpellMgr::GetSpellChainNode(uint32 spell_id) const { - uint32 oldMSTime = getMSTime(); + SpellChainMap::const_iterator itr = mSpellChains.find(spell_id); + if (itr == mSpellChains.end()) + return NULL; - mSpellTargetPositions.clear(); // need for reload case + return &itr->second; +} - // 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) - { - sLog->outString(">> Loaded 0 spell target coordinates. DB table `spell_target_position` is empty."); - sLog->outString(); - return; - } +uint32 SpellMgr::GetFirstSpellInChain(uint32 spell_id) const +{ + if (SpellChainNode const* node = GetSpellChainNode(spell_id)) + return node->first->Id; - uint32 count = 0; + return spell_id; +} - do - { - Field *fields = result->Fetch(); +uint32 SpellMgr::GetLastSpellInChain(uint32 spell_id) const +{ + if (SpellChainNode const* node = GetSpellChainNode(spell_id)) + return node->last->Id; - uint32 Spell_ID = fields[0].GetUInt32(); + return spell_id; +} - SpellTargetPosition st; +uint32 SpellMgr::GetNextSpellInChain(uint32 spell_id) const +{ + if (SpellChainNode const* node = GetSpellChainNode(spell_id)) + return node->next ? node->next->Id : NULL; - 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(); + return 0; +} - 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; - } +uint32 SpellMgr::GetPrevSpellInChain(uint32 spell_id) const +{ + if (SpellChainNode const* node = GetSpellChainNode(spell_id)) + return node->prev ? node->prev->Id : NULL; - 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; - } + return 0; +} - 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; - } +uint8 SpellMgr::GetSpellRank(uint32 spell_id) const +{ + if (SpellChainNode const* node = GetSpellChainNode(spell_id)) + return node->rank; - bool found = false; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (spellInfo->EffectImplicitTargetA[i] == TARGET_DST_DB || spellInfo->EffectImplicitTargetB[i] == TARGET_DST_DB) - { - // additional requirements - if (spellInfo->Effect[i]==SPELL_EFFECT_BIND && spellInfo->EffectMiscValue[i]) - { - uint32 area_id = sMapMgr->GetAreaId(st.target_mapId, st.target_X, st.target_Y, st.target_Z); - if (area_id != uint32(spellInfo->EffectMiscValue[i])) - { - sLog->outErrorDb("Spell (Id: %u) listed in `spell_target_position` expected point to zone %u bit point to zone %u.", Spell_ID, spellInfo->EffectMiscValue[i], area_id); - break; - } - } + return 0; +} - found = true; - break; - } - } - if (!found) - { - sLog->outErrorDb("Spell (Id: %u) listed in `spell_target_position` does not have target TARGET_DST_DB (17).", Spell_ID); - continue; - } +uint32 SpellMgr::GetSpellWithRank(uint32 spell_id, uint32 rank, bool strict) const +{ + if (SpellChainNode const* node = GetSpellChainNode(spell_id)) + { + if (rank != node->rank) + return GetSpellWithRank(node->rank < rank ? node->next->Id : node->prev->Id, rank, strict); + } + else if (strict && rank > 1) + return 0; + return spell_id; +} - mSpellTargetPositions[Spell_ID] = st; - ++count; +SpellRequiredMapBounds SpellMgr::GetSpellsRequiredForSpellBounds(uint32 spell_id) const +{ + return SpellRequiredMapBounds(mSpellReq.lower_bound(spell_id), mSpellReq.upper_bound(spell_id)); +} - } while (result->NextRow()); +SpellsRequiringSpellMapBounds SpellMgr::GetSpellsRequiringSpellBounds(uint32 spell_id) const +{ + return SpellsRequiringSpellMapBounds(mSpellsReqSpell.lower_bound(spell_id), mSpellsReqSpell.upper_bound(spell_id)); +} - // Check all spells - for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) +bool SpellMgr::IsSpellRequiringSpell(uint32 spellid, uint32 req_spellid) const +{ + SpellsRequiringSpellMapBounds spellsRequiringSpell = GetSpellsRequiringSpellBounds(req_spellid); + for (SpellsRequiringSpellMap::const_iterator itr = spellsRequiringSpell.first; itr != spellsRequiringSpell.second; ++itr) { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(i); - if (!spellInfo) - continue; - - bool found = false; - for (int j = 0; j < MAX_SPELL_EFFECTS; ++j) - { - switch (spellInfo->EffectImplicitTargetA[j]) - { - case TARGET_DST_DB: - found = true; - break; - } - if (found) - break; - switch (spellInfo->EffectImplicitTargetB[j]) - { - case TARGET_DST_DB: - found = true; - break; - } - if (found) - break; - } - if (found) - { -// if (!sSpellMgr->GetSpellTargetPosition(i)) -// sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell (ID: %u) does not have record in `spell_target_position`", i); - } + if (itr->second == spellid) + return true; } + return false; +} - sLog->outString(">> Loaded %u spell teleport coordinates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - sLog->outString(); +const SpellsRequiringSpellMap SpellMgr::GetSpellsRequiringSpell() +{ + return this->mSpellsReqSpell; } -bool SpellMgr::IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const +uint32 SpellMgr::GetSpellRequired(uint32 spell_id) const { - // false for spellInfo == NULL - if (!spellInfo || !mod) - return false; + SpellRequiredMap::const_iterator itr = mSpellReq.find(spell_id); - 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; + if (itr == mSpellReq.end()) + return 0; - // true - if (mod->mask & spellInfo->SpellFamilyFlags) - return true; + return itr->second; +} - return false; +SpellLearnSkillNode const* SpellMgr::GetSpellLearnSkill(uint32 spell_id) const +{ + SpellLearnSkillMap::const_iterator itr = mSpellLearnSkills.find(spell_id); + if (itr != mSpellLearnSkills.end()) + return &itr->second; + else + return NULL; } -void SpellMgr::LoadSpellProcEvents() +SpellLearnSpellMapBounds SpellMgr::GetSpellLearnSpellMapBounds(uint32 spell_id) const { - uint32 oldMSTime = getMSTime(); + return SpellLearnSpellMapBounds(mSpellLearnSpells.lower_bound(spell_id), mSpellLearnSpells.upper_bound(spell_id)); +} - mSpellProcEventMap.clear(); // need for reload case +bool SpellMgr::IsSpellLearnSpell(uint32 spell_id) const +{ + return mSpellLearnSpells.find(spell_id) != mSpellLearnSpells.end(); +} - uint32 count = 0; +bool SpellMgr::IsSpellLearnToSpell(uint32 spell_id1, uint32 spell_id2) const +{ + SpellLearnSpellMapBounds bounds = GetSpellLearnSpellMapBounds(spell_id1); + for (SpellLearnSpellMap::const_iterator i = bounds.first; i != bounds.second; ++i) + if (i->second.spell == spell_id2) + return true; + return false; +} - // 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) +SpellTargetPosition const* SpellMgr::GetSpellTargetPosition(uint32 spell_id) const +{ + SpellTargetPositionMap::const_iterator itr = mSpellTargetPositions.find(spell_id); + if (itr != mSpellTargetPositions.end()) + return &itr->second; + return NULL; +} + +SpellSpellGroupMapBounds SpellMgr::GetSpellSpellGroupMapBounds(uint32 spell_id) const +{ + spell_id = GetFirstSpellInChain(spell_id); + return SpellSpellGroupMapBounds(mSpellSpellGroup.lower_bound(spell_id), mSpellSpellGroup.upper_bound(spell_id)); +} + +uint32 SpellMgr::IsSpellMemberOfSpellGroup(uint32 spellid, SpellGroup groupid) const +{ + SpellSpellGroupMapBounds spellGroup = GetSpellSpellGroupMapBounds(spellid); + for (SpellSpellGroupMap::const_iterator itr = spellGroup.first; itr != spellGroup.second ; ++itr) { - sLog->outString(">> Loaded %u spell proc event conditions", count); - sLog->outString(); - return; + if (itr->second == groupid) + return true; } + return false; +} - uint32 customProc = 0; - do - { - Field *fields = result->Fetch(); +SpellGroupSpellMapBounds SpellMgr::GetSpellGroupSpellMapBounds(SpellGroup group_id) const +{ + return SpellGroupSpellMapBounds(mSpellGroupSpell.lower_bound(group_id), mSpellGroupSpell.upper_bound(group_id)); +} - uint32 entry = fields[0].GetUInt32(); +void SpellMgr::GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells) const +{ + std::set<SpellGroup> usedGroups; + GetSetOfSpellsInSpellGroup(group_id, foundSpells, usedGroups); +} - SpellEntry const* spell = sSpellStore.LookupEntry(entry); - if (!spell) +void SpellMgr::GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells, std::set<SpellGroup>& usedGroups) const +{ + if (usedGroups.find(group_id) != usedGroups.end()) + return; + usedGroups.insert(group_id); + + SpellGroupSpellMapBounds groupSpell = GetSpellGroupSpellMapBounds(group_id); + for (SpellGroupSpellMap::const_iterator itr = groupSpell.first; itr != groupSpell.second ; ++itr) + { + if (itr->second < 0) { - sLog->outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry); - continue; + SpellGroup currGroup = (SpellGroup)abs(itr->second); + GetSetOfSpellsInSpellGroup(currGroup, foundSpells, usedGroups); } + else + { + foundSpells.insert(itr->second); + } + } +} - SpellProcEventEntry spe; - - spe.schoolMask = fields[1].GetUInt32(); - spe.spellFamilyName = fields[2].GetUInt32(); - spe.spellFamilyMask[0] = fields[3].GetUInt32(); - spe.spellFamilyMask[1] = fields[4].GetUInt32(); - spe.spellFamilyMask[2] = fields[5].GetUInt32(); - spe.procFlags = fields[6].GetUInt32(); - spe.procEx = fields[7].GetUInt32(); - spe.ppmRate = fields[8].GetFloat(); - spe.customChance = fields[9].GetFloat(); - spe.cooldown = fields[10].GetUInt32(); - - mSpellProcEventMap[entry] = spe; - - if (spell->procFlags == 0) +SpellGroupStackRule SpellMgr::CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const +{ + uint32 spellid_1 = spellInfo1->GetFirstRankSpell()->Id; + uint32 spellid_2 = spellInfo2->GetFirstRankSpell()->Id; + if (spellid_1 == spellid_2) + return SPELL_GROUP_STACK_RULE_DEFAULT; + // find SpellGroups which are common for both spells + SpellSpellGroupMapBounds spellGroup1 = GetSpellSpellGroupMapBounds(spellid_1); + std::set<SpellGroup> groups; + for (SpellSpellGroupMap::const_iterator itr = spellGroup1.first; itr != spellGroup1.second ; ++itr) + { + if (IsSpellMemberOfSpellGroup(spellid_2, itr->second)) { - if (spe.procFlags == 0) + bool add = true; + SpellGroupSpellMapBounds groupSpell = GetSpellGroupSpellMapBounds(itr->second); + for (SpellGroupSpellMap::const_iterator itr2 = groupSpell.first; itr2 != groupSpell.second ; ++itr2) { - sLog->outErrorDb("Spell %u listed in `spell_proc_event` probally not triggered spell", entry); - continue; + if (itr2->second < 0) + { + SpellGroup currGroup = (SpellGroup)abs(itr2->second); + if (IsSpellMemberOfSpellGroup(spellid_1, currGroup) && IsSpellMemberOfSpellGroup(spellid_2, currGroup)) + { + add = false; + break; + } + } } - customProc++; + if (add) + groups.insert(itr->second); } - ++count; - } while (result->NextRow()); + } - if (customProc) - sLog->outString(">> Loaded %u extra and %u custom spell proc event conditions in %u ms", count, customProc, GetMSTimeDiffToNow(oldMSTime)); - else - sLog->outString(">> Loaded %u extra spell proc event conditions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - sLog->outString(); + SpellGroupStackRule rule = SPELL_GROUP_STACK_RULE_DEFAULT; + + for (std::set<SpellGroup>::iterator itr = groups.begin() ; itr!= groups.end() ; ++itr) + { + SpellGroupStackMap::const_iterator found = mSpellGroupStack.find(*itr); + if (found != mSpellGroupStack.end()) + rule = found->second; + if (rule) + break; + } + return rule; +} + +SpellProcEventEntry const* SpellMgr::GetSpellProcEvent(uint32 spellId) const +{ + SpellProcEventMap::const_iterator itr = mSpellProcEventMap.find(spellId); + if (itr != mSpellProcEventMap.end()) + return &itr->second; + return NULL; } -bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellEntry const* procSpell, uint32 procFlags, uint32 procExtra, bool active) +bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellInfo const* procSpell, uint32 procFlags, uint32 procExtra, bool active) { // No extra req need uint32 procEvent_procEx = PROC_EX_NONE; @@ -1464,146 +850,12 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr return false; } -void SpellMgr::LoadSpellProcs() +SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const { - uint32 oldMSTime = getMSTime(); - - mSpellProcMap.clear(); // need for reload case - - uint32 count = 0; - - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - QueryResult result = WorldDatabase.Query("SELECT spellId, schoolMask, spellFamilyName, spellFamilyMask0, spellFamilyMask1, spellFamilyMask2, typeMask, spellTypeMask, spellPhaseMask, hitMask, attributesMask, ratePerMinute, chance, cooldown, charges FROM spell_proc"); - if (!result) - { - sLog->outString(">> Loaded %u spell proc conditions and data", count); - sLog->outString(); - return; - } - - do - { - Field* fields = result->Fetch(); - - int32 spellId = fields[0].GetInt32(); - - bool allRanks = false; - if (spellId <=0) - { - allRanks = true; - spellId = -spellId; - } - - SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellId); - if (!spellEntry) - { - sLog->outErrorDb("Spell %u listed in `spell_proc` does not exist", spellId); - continue; - } - - if (allRanks) - { - if (sSpellMgr->GetFirstSpellInChain(spellId) != uint32(spellId)) - { - sLog->outErrorDb("Spell %u listed in `spell_proc` is not first rank of spell.", fields[0].GetInt32()); - continue; - } - } - - SpellProcEntry baseProcEntry; - - baseProcEntry.schoolMask = fields[1].GetUInt32(); - baseProcEntry.spellFamilyName = fields[2].GetUInt32(); - baseProcEntry.spellFamilyMask[0] = fields[3].GetUInt32(); - baseProcEntry.spellFamilyMask[1] = fields[4].GetUInt32(); - baseProcEntry.spellFamilyMask[2] = fields[5].GetUInt32(); - baseProcEntry.typeMask = fields[6].GetUInt32(); - baseProcEntry.spellTypeMask = fields[7].GetUInt32(); - baseProcEntry.spellPhaseMask = fields[8].GetUInt32(); - baseProcEntry.hitMask = fields[9].GetUInt32(); - baseProcEntry.attributesMask = fields[10].GetUInt32(); - baseProcEntry.ratePerMinute = fields[11].GetFloat(); - baseProcEntry.chance = fields[12].GetFloat(); - float cooldown = fields[13].GetFloat(); - baseProcEntry.cooldown = uint32(cooldown); - baseProcEntry.charges = fields[14].GetUInt32(); - - while(true) - { - if (mSpellProcMap.find(spellId) != mSpellProcMap.end()) - { - sLog->outErrorDb("Spell %u listed in `spell_proc` has duplicate entry in the table", spellId); - break; - } - SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); - - // take defaults from dbcs - if (!procEntry.typeMask) - procEntry.typeMask = spellEntry->procFlags; - if (!procEntry.charges) - procEntry.charges = spellEntry->procCharges; - if (!procEntry.chance && !procEntry.ratePerMinute) - procEntry.chance = float(spellEntry->procChance); - - // validate data - if (procEntry.schoolMask & ~SPELL_SCHOOL_MASK_ALL) - sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `schoolMask` set: %u", spellId, procEntry.schoolMask); - if (procEntry.spellFamilyName && (procEntry.spellFamilyName < 3 || procEntry.spellFamilyName > 17 || procEntry.spellFamilyName == 14 || procEntry.spellFamilyName == 16)) - sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellFamilyName` set: %u", spellId, procEntry.spellFamilyName); - if (procEntry.chance < 0) - { - sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `chance` field", spellId); - procEntry.chance = 0; - } - if (procEntry.ratePerMinute < 0) - { - sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `ratePerMinute` field", spellId); - procEntry.ratePerMinute = 0; - } - if (cooldown < 0) - { - sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `cooldown` field", spellId); - procEntry.cooldown = 0; - } - if (procEntry.chance == 0 && procEntry.ratePerMinute == 0) - sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `chance` and `ratePerMinute` values defined, proc will not be triggered", spellId); - if (procEntry.charges > 99) - { - sLog->outErrorDb("`spell_proc` table entry for spellId %u has too big value in `charges` field", spellId); - procEntry.charges = 99; - } - if (!procEntry.typeMask) - sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `typeMask` value defined, proc will not be triggered", spellId); - if (procEntry.spellTypeMask & ~PROC_SPELL_PHASE_MASK_ALL) - sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellTypeMask` set: %u", spellId, procEntry.spellTypeMask); - if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) - sLog->outErrorDb("`spell_proc` table entry for spellId %u has `spellTypeMask` value defined, but it won't be used for defined `typeMask` value", spellId); - if (!procEntry.spellPhaseMask && procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK) - sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `spellPhaseMask` value defined, but it's required for defined `typeMask` value, proc will not be triggered", spellId); - if (procEntry.spellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) - sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellPhaseMask` set: %u", spellId, procEntry.spellPhaseMask); - if (procEntry.spellPhaseMask && !(procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK)) - sLog->outErrorDb("`spell_proc` table entry for spellId %u has `spellPhaseMask` value defined, but it won't be used for defined `typeMask` value", spellId); - if (procEntry.hitMask & ~PROC_HIT_MASK_ALL) - sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `hitMask` set: %u", spellId, procEntry.hitMask); - if (procEntry.hitMask && !(procEntry.typeMask & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.typeMask & DONE_HIT_PROC_FLAG_MASK && (!procEntry.spellPhaseMask || procEntry.spellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) - sLog->outErrorDb("`spell_proc` table entry for spellId %u has `hitMask` value defined, but it won't be used for defined `typeMask` and `spellPhaseMask` values", spellId); - - mSpellProcMap[spellId] = procEntry; - - if (allRanks) - { - spellId = sSpellMgr->GetNextSpellInChain(spellId); - spellEntry = sSpellStore.LookupEntry(spellId); - } - else - break; - } - ++count; - } while (result->NextRow()); - - sLog->outString(">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - sLog->outString(); + SpellProcMap::const_iterator itr = mSpellProcMap.find(spellId); + if (itr != mSpellProcMap.end()) + return &itr->second; + return NULL; } bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) @@ -1671,43 +923,609 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE return true; } -void SpellMgr::LoadSpellBonusess() +SpellBonusEntry const* SpellMgr::GetSpellBonusData(uint32 spellId) const +{ + // Lookup data + SpellBonusMap::const_iterator itr = mSpellBonusMap.find(spellId); + if (itr != mSpellBonusMap.end()) + return &itr->second; + // Not found, try lookup for 1 spell rank if exist + if (uint32 rank_1 = GetFirstSpellInChain(spellId)) + { + SpellBonusMap::const_iterator itr2 = mSpellBonusMap.find(rank_1); + if (itr2 != mSpellBonusMap.end()) + return &itr2->second; + } + return NULL; +} + +uint16 SpellMgr::GetSpellThreat(uint32 spellid) const +{ + SpellThreatMap::const_iterator itr = mSpellThreatMap.find(spellid); + if (itr == mSpellThreatMap.end()) + return 0; + + return itr->second; +} + +SkillLineAbilityMapBounds SpellMgr::GetSkillLineAbilityMapBounds(uint32 spell_id) const +{ + return SkillLineAbilityMapBounds(mSkillLineAbilityMap.lower_bound(spell_id), mSkillLineAbilityMap.upper_bound(spell_id)); +} + +PetAura const* SpellMgr::GetPetAura(uint32 spell_id, uint8 eff) +{ + SpellPetAuraMap::const_iterator itr = mSpellPetAuraMap.find((spell_id<<8) + eff); + if (itr != mSpellPetAuraMap.end()) + return &itr->second; + else + return NULL; +} + +SpellEnchantProcEntry const* SpellMgr::GetSpellEnchantProcEvent(uint32 enchId) const +{ + SpellEnchantProcEventMap::const_iterator itr = mSpellEnchantProcEventMap.find(enchId); + if (itr != mSpellEnchantProcEventMap.end()) + return &itr->second; + return NULL; +} + +bool SpellMgr::IsArenaAllowedEnchancment(uint32 ench_id) const +{ + return mEnchantCustomAttr[ench_id]; +} + +const std::vector<int32>* SpellMgr::GetSpellLinked(int32 spell_id) const +{ + SpellLinkedMap::const_iterator itr = mSpellLinkedMap.find(spell_id); + return itr != mSpellLinkedMap.end() ? &(itr->second) : NULL; +} + +PetLevelupSpellSet const* SpellMgr::GetPetLevelupSpellList(uint32 petFamily) const +{ + PetLevelupSpellMap::const_iterator itr = mPetLevelupSpellMap.find(petFamily); + if (itr != mPetLevelupSpellMap.end()) + return &itr->second; + else + return NULL; +} + +PetDefaultSpellsEntry const* SpellMgr::GetPetDefaultSpellsEntry(int32 id) const +{ + PetDefaultSpellsMap::const_iterator itr = mPetDefaultSpellsMap.find(id); + if (itr != mPetDefaultSpellsMap.end()) + return &itr->second; + return NULL; +} + +SpellAreaMapBounds SpellMgr::GetSpellAreaMapBounds(uint32 spell_id) const +{ + return SpellAreaMapBounds(mSpellAreaMap.lower_bound(spell_id), mSpellAreaMap.upper_bound(spell_id)); +} + +SpellAreaForQuestMapBounds SpellMgr::GetSpellAreaForQuestMapBounds(uint32 quest_id, bool active) const +{ + if (active) + return SpellAreaForQuestMapBounds(mSpellAreaForActiveQuestMap.lower_bound(quest_id), mSpellAreaForActiveQuestMap.upper_bound(quest_id)); + else + return SpellAreaForQuestMapBounds(mSpellAreaForQuestMap.lower_bound(quest_id), mSpellAreaForQuestMap.upper_bound(quest_id)); +} + +SpellAreaForQuestMapBounds SpellMgr::GetSpellAreaForQuestEndMapBounds(uint32 quest_id) const +{ + return SpellAreaForQuestMapBounds(mSpellAreaForQuestEndMap.lower_bound(quest_id), mSpellAreaForQuestEndMap.upper_bound(quest_id)); +} + +SpellAreaForAuraMapBounds SpellMgr::GetSpellAreaForAuraMapBounds(uint32 spell_id) const +{ + return SpellAreaForAuraMapBounds(mSpellAreaForAuraMap.lower_bound(spell_id), mSpellAreaForAuraMap.upper_bound(spell_id)); +} + +SpellAreaForAreaMapBounds SpellMgr::GetSpellAreaForAreaMapBounds(uint32 area_id) const +{ + return SpellAreaForAreaMapBounds(mSpellAreaForAreaMap.lower_bound(area_id), mSpellAreaForAreaMap.upper_bound(area_id)); +} + +bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const +{ + if (gender != GENDER_NONE) // not in expected gender + if (!player || gender != player->getGender()) + return false; + + if (raceMask) // not in expected race + if (!player || !(raceMask & player->getRaceMask())) + return false; + + if (areaId) // not in expected zone + if (newZone != areaId && newArea != areaId) + return false; + + if (questStart) // not in expected required quest state + if (!player || ((!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart))) + return false; + + if (questEnd) // not in expected forbidden quest state + if (!player || player->GetQuestRewardStatus(questEnd)) + return false; + + if (auraSpell) // not have expected aura + if (!player || (auraSpell > 0 && !player->HasAura(auraSpell)) || (auraSpell < 0 && player->HasAura(-auraSpell))) + return false; + + // Extra conditions -- leaving the possibility add extra conditions... + switch (spellId) + { + case 58600: // No fly Zone - Dalaran + { + if (!player) + return false; + + AreaTableEntry const* pArea = GetAreaEntryByAreaID(player->GetAreaId()); + if (!(pArea && pArea->flags & AREA_FLAG_NO_FLY_ZONE)) + return false; + if (!player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !player->HasAuraType(SPELL_AURA_FLY)) + return false; + break; + } + case 68719: // Oil Refinery - Isle of Conquest. + case 68720: // Quarry - Isle of Conquest. + { + if (player->GetBattlegroundTypeId() != BATTLEGROUND_IC || !player->GetBattleground()) + return false; + + uint8 nodeType = spellId == 68719 ? NODE_TYPE_REFINERY : NODE_TYPE_QUARRY; + uint8 nodeState = player->GetTeamId() == TEAM_ALLIANCE ? NODE_STATE_CONTROLLED_A : NODE_STATE_CONTROLLED_H; + + BattlegroundIC* pIC = static_cast<BattlegroundIC*>(player->GetBattleground()); + if (pIC->GetNodeState(nodeType) == nodeState) + return true; + + return false; + } + } + + return true; +} + +void SpellMgr::LoadSpellInfos() +{ + +} + +void SpellMgr::LoadSpellRanks() { uint32 oldMSTime = getMSTime(); - mSpellBonusMap.clear(); // need for reload case - uint32 count = 0; - // 0 1 2 3 4 - QueryResult result = WorldDatabase.Query("SELECT entry, direct_bonus, dot_bonus, ap_bonus, ap_dot_bonus FROM spell_bonus_data"); + // cleanup core data before reload - remove reference to ChainNode from SpellInfo + for (SpellChainMap::iterator itr = mSpellChains.begin(); itr != mSpellChains.end(); ++itr) + { + mSpellInfoMap[itr->first]->ChainEntry = NULL; + } + mSpellChains.clear(); + + QueryResult result = WorldDatabase.Query("SELECT first_spell_id, spell_id, rank from spell_ranks ORDER BY first_spell_id , rank"); + if (!result) { - sLog->outString(">> Loaded %u spell bonus data", count); + sLog->outString(">> Loaded 0 spell rank records"); + sLog->outString(); + sLog->outErrorDb("`spell_ranks` table is empty!"); + return; + } + + uint32 rows = 0; + bool finished = false; + + do + { + // spellid, rank + std::list < std::pair < int32, int32 > > rankChain; + int32 currentSpell = -1; + int32 lastSpell = -1; + + // fill one chain + while (currentSpell == lastSpell && !finished) + { + Field *fields = result->Fetch(); + + currentSpell = fields[0].GetUInt32(); + if (lastSpell == -1) + lastSpell = currentSpell; + uint32 spell_id = fields[1].GetUInt32(); + uint32 rank = fields[2].GetUInt32(); + + // don't drop the row if we're moving to the next rank + if (currentSpell == lastSpell) + { + rankChain.push_back(std::make_pair(spell_id, rank)); + if (!result->NextRow()) + finished = true; + } + else + break; + } + // check if chain is made with valid first spell + SpellInfo const* first = sSpellMgr->GetSpellInfo(lastSpell); + if (!first) + { + sLog->outErrorDb("Spell rank identifier(first_spell_id) %u listed in `spell_ranks` does not exist!", lastSpell); + continue; + } + // check if chain is long enough + if (rankChain.size() < 2) + { + sLog->outErrorDb("There is only 1 spell rank for identifier(first_spell_id) %u in `spell_ranks`, entry is not needed!", lastSpell); + continue; + } + int32 curRank = 0; + bool valid = true; + // check spells in chain + for (std::list<std::pair<int32, int32> >::iterator itr = rankChain.begin() ; itr!= rankChain.end(); ++itr) + { + SpellInfo const* spell = sSpellMgr->GetSpellInfo(itr->first); + if (!spell) + { + sLog->outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not exist!", itr->first, itr->second, lastSpell); + valid = false; + break; + } + ++curRank; + if (itr->second != curRank) + { + sLog->outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not have proper rank value(should be %u)!", itr->first, itr->second, lastSpell, curRank); + valid = false; + break; + } + } + if (!valid) + continue; + int32 prevRank = 0; + // insert the chain + std::list<std::pair<int32, int32> >::iterator itr = rankChain.begin(); + do + { + ++rows; + int32 addedSpell = itr->first; + mSpellChains[addedSpell].first = GetSpellInfo(lastSpell); + mSpellChains[addedSpell].last = GetSpellInfo(rankChain.back().first); + mSpellChains[addedSpell].rank = itr->second; + mSpellChains[addedSpell].prev = GetSpellInfo(prevRank); + mSpellInfoMap[addedSpell]->ChainEntry = &mSpellChains[addedSpell]; + prevRank = addedSpell; + ++itr; + if (itr == rankChain.end()) + { + mSpellChains[addedSpell].next = NULL; + break; + } + else + mSpellChains[addedSpell].next = GetSpellInfo(itr->first); + } + while (true); + } while (!finished); + + sLog->outString(">> Loaded %u spell rank records in %u ms", rows, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); +} + +void SpellMgr::LoadSpellRequired() +{ + uint32 oldMSTime = getMSTime(); + + 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) + { + sLog->outString(">> Loaded 0 spell required records"); sLog->outString(); + sLog->outErrorDb("`spell_required` table is empty!"); return; } + uint32 rows = 0; do { Field *fields = result->Fetch(); - uint32 entry = fields[0].GetUInt32(); - SpellEntry const* spell = sSpellStore.LookupEntry(entry); + uint32 spell_id = fields[0].GetUInt32(); + uint32 spell_req = fields[1].GetUInt32(); + // check if chain is made with valid first spell + SpellInfo const* spell = sSpellMgr->GetSpellInfo(spell_id); if (!spell) { - sLog->outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry); + sLog->outErrorDb("spell_id %u in `spell_required` table is not found in dbcs, skipped", spell_id); + continue; + } + SpellInfo const* req_spell = sSpellMgr->GetSpellInfo(spell_req); + if (!req_spell) + { + sLog->outErrorDb("req_spell %u in `spell_required` table is not found in dbcs, skipped", spell_req); + continue; + } + if (GetFirstSpellInChain(spell_id) == GetFirstSpellInChain(spell_req)) + { + sLog->outErrorDb("req_spell %u and spell_id %u in `spell_required` table are ranks of the same spell, entry not needed, skipped", spell_req, spell_id); + continue; + } + if (IsSpellRequiringSpell(spell_id, spell_req)) + { + sLog->outErrorDb("duplicated entry of req_spell %u and spell_id %u in `spell_required`, skipped", spell_req, spell_id); continue; } - SpellBonusEntry& sbe = mSpellBonusMap[entry]; - 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(); + mSpellReq.insert (std::pair<uint32, uint32>(spell_id, spell_req)); + mSpellsReqSpell.insert (std::pair<uint32, uint32>(spell_req, spell_id)); + ++rows; + } while (result->NextRow()); + + sLog->outString(">> Loaded %u spell required records in %u ms", rows, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); +} + +void SpellMgr::LoadSpellLearnSkills() +{ + uint32 oldMSTime = getMSTime(); + + 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; + for (uint32 spell = 0; spell < sSpellMgr->GetSpellInfoStoreSize(); ++spell) + { + SpellInfo const* entry = sSpellMgr->GetSpellInfo(spell); + + if (!entry) + continue; + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (entry->Effects[i].Effect == SPELL_EFFECT_SKILL) + { + SpellLearnSkillNode dbc_node; + dbc_node.skill = entry->Effects[i].MiscValue; + dbc_node.step = entry->Effects[i].CalcValue(); + if (dbc_node.skill != SKILL_RIDING) + dbc_node.value = 1; + else + dbc_node.value = dbc_node.step * 75; + dbc_node.maxvalue = dbc_node.step * 75; + mSpellLearnSkills[spell] = dbc_node; + ++dbc_count; + break; + } + } + } + + sLog->outString(">> Loaded %u Spell Learn Skills from DBC in %u ms", dbc_count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); +} + +void SpellMgr::LoadSpellLearnSpells() +{ + uint32 oldMSTime = getMSTime(); + + mSpellLearnSpells.clear(); // need for reload case + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell"); + if (!result) + { + sLog->outString(">> Loaded 0 spell learn spells"); + sLog->outString(); + sLog->outErrorDb("`spell_learn_spell` table is empty!"); + return; + } + + uint32 count = 0; + + do + { + 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 (!sSpellMgr->GetSpellInfo(spell_id)) + { + sLog->outErrorDb("Spell %u listed in `spell_learn_spell` does not exist", spell_id); + continue; + } + + if (!sSpellMgr->GetSpellInfo(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()); - sLog->outString(">> Loaded %u extra spell bonus data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + // 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 < sSpellMgr->GetSpellInfoStoreSize(); ++spell) + { + SpellInfo const* entry = sSpellMgr->GetSpellInfo(spell); + + if (!entry) + continue; + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (entry->Effects[i].Effect == SPELL_EFFECT_LEARN_SPELL) + { + SpellLearnSpellNode dbc_node; + dbc_node.spell = entry->Effects[i].TriggerSpell; + 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 (!sSpellMgr->GetSpellInfo(dbc_node.spell)) + continue; + + // talent or passive spells or skill-step spells auto-casted and not need dependent learning, + // pet teaching spells must not be dependent learning (casted) + // other required explicit dependent learning + dbc_node.autoLearned = entry->Effects[i].TargetA == TARGET_UNIT_PET || GetTalentSpellCost(spell) > 0 || entry->IsPassive() || entry->HasEffect(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(">> Loaded %u spell learn spells + %u found in DBC in %u ms", count, dbc_count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); +} + +void SpellMgr::LoadSpellTargetPositions() +{ + uint32 oldMSTime = getMSTime(); + + mSpellTargetPositions.clear(); // need for reload case + + // 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) + { + sLog->outString(">> Loaded 0 spell target coordinates. DB table `spell_target_position` is empty."); + sLog->outString(); + return; + } + + uint32 count = 0; + + do + { + Field *fields = result->Fetch(); + + uint32 Spell_ID = fields[0].GetUInt32(); + + SpellTargetPosition st; + + st.target_mapId = fields[1].GetUInt32(); + st.target_X = fields[2].GetFloat(); + st.target_Y = fields[3].GetFloat(); + st.target_Z = fields[4].GetFloat(); + st.target_Orientation = fields[5].GetFloat(); + + MapEntry const* mapEntry = sMapStore.LookupEntry(st.target_mapId); + if (!mapEntry) + { + sLog->outErrorDb("Spell (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.", Spell_ID, st.target_mapId); + continue; + } + + if (st.target_X==0 && st.target_Y==0 && st.target_Z==0) + { + sLog->outErrorDb("Spell (ID:%u) target coordinates not provided.", Spell_ID); + continue; + } + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(Spell_ID); + if (!spellInfo) + { + sLog->outErrorDb("Spell (ID:%u) listed in `spell_target_position` does not exist.", Spell_ID); + continue; + } + + bool found = false; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + if (spellInfo->Effects[i].TargetA == TARGET_DST_DB || spellInfo->Effects[i].TargetB == TARGET_DST_DB) + { + // additional requirements + if (spellInfo->Effects[i].Effect == SPELL_EFFECT_BIND && spellInfo->Effects[i].MiscValue) + { + uint32 area_id = sMapMgr->GetAreaId(st.target_mapId, st.target_X, st.target_Y, st.target_Z); + if (area_id != uint32(spellInfo->Effects[i].MiscValue)) + { + sLog->outErrorDb("Spell (Id: %u) listed in `spell_target_position` expected point to zone %u bit point to zone %u.", Spell_ID, spellInfo->Effects[i].MiscValue, area_id); + break; + } + } + + found = true; + break; + } + } + if (!found) + { + sLog->outErrorDb("Spell (Id: %u) listed in `spell_target_position` does not have target TARGET_DST_DB (17).", Spell_ID); + continue; + } + + mSpellTargetPositions[Spell_ID] = st; + ++count; + + } while (result->NextRow()); + + /* + // Check all spells + for (uint32 i = 1; i < GetSpellInfoStoreSize; ++i) + { + SpellInfo const* spellInfo = GetSpellInfo(i); + if (!spellInfo) + continue; + + bool found = false; + for (int j = 0; j < MAX_SPELL_EFFECTS; ++j) + { + switch (spellInfo->Effects[j].TargetA) + { + case TARGET_DST_DB: + found = true; + break; + } + if (found) + break; + switch (spellInfo->Effects[j].TargetB) + { + case TARGET_DST_DB: + found = true; + break; + } + if (found) + break; + } + if (found) + { + if (!sSpellMgr->GetSpellTargetPosition(i)) + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell (ID: %u) does not have record in `spell_target_position`", i); + } + }*/ + + sLog->outString(">> Loaded %u spell teleport coordinates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } @@ -1762,14 +1580,14 @@ void SpellMgr::LoadSpellGroups() } else { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->second); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->second); if (!spellInfo) { sLog->outErrorDb("Spell %u listed in `spell_group` does not exist", itr->second); mSpellGroupSpell.erase(itr++); } - else if (GetSpellRank(itr->second) > 1) + else if (spellInfo->GetRank() > 1) { sLog->outErrorDb("Spell %u listed in `spell_group` is not first rank of spell", itr->second); mSpellGroupSpell.erase(itr++); @@ -1841,467 +1659,475 @@ void SpellMgr::LoadSpellGroupStackRules() sLog->outString(); } -void SpellMgr::LoadSpellThreats() +void SpellMgr::LoadSpellProcEvents() { uint32 oldMSTime = getMSTime(); - mSpellThreatMap.clear(); // need for reload case + mSpellProcEventMap.clear(); // need for reload case uint32 count = 0; - // 0 1 - QueryResult result = WorldDatabase.Query("SELECT entry, Threat FROM spell_threat"); + // 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) { - sLog->outString(">> Loaded %u aggro generating spells", count); + sLog->outString(">> Loaded %u spell proc event conditions", count); sLog->outString(); return; } + uint32 customProc = 0; do { Field *fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); - uint16 Threat = fields[1].GetUInt16(); - if (!sSpellStore.LookupEntry(entry)) + SpellInfo const* spell = sSpellMgr->GetSpellInfo(entry); + if (!spell) { - sLog->outErrorDb("Spell %u listed in `spell_threat` does not exist", entry); + sLog->outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry); continue; } - mSpellThreatMap[entry] = Threat; + SpellProcEventEntry spe; + + spe.schoolMask = fields[1].GetUInt32(); + spe.spellFamilyName = fields[2].GetUInt32(); + spe.spellFamilyMask[0] = fields[3].GetUInt32(); + spe.spellFamilyMask[1] = fields[4].GetUInt32(); + spe.spellFamilyMask[2] = fields[5].GetUInt32(); + spe.procFlags = fields[6].GetUInt32(); + spe.procEx = fields[7].GetUInt32(); + spe.ppmRate = fields[8].GetFloat(); + spe.customChance = fields[9].GetFloat(); + spe.cooldown = fields[10].GetUInt32(); + + mSpellProcEventMap[entry] = spe; + if (spell->ProcFlags == 0) + { + if (spe.procFlags == 0) + { + sLog->outErrorDb("Spell %u listed in `spell_proc_event` probally not triggered spell", entry); + continue; + } + customProc++; + } ++count; } while (result->NextRow()); - sLog->outString(">> Loaded %u aggro generating spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + if (customProc) + sLog->outString(">> Loaded %u extra and %u custom spell proc event conditions in %u ms", count, customProc, GetMSTimeDiffToNow(oldMSTime)); + else + sLog->outString(">> Loaded %u extra spell proc event conditions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } -bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1, uint32 spellId_2) const +void SpellMgr::LoadSpellProcs() { - SpellEntry const* spellInfo_2 = sSpellStore.LookupEntry(spellId_2); - if (!spellInfo_1 || !spellInfo_2) return false; - if (spellInfo_1->Id == spellId_2) return false; + uint32 oldMSTime = getMSTime(); - return GetFirstSpellInChain(spellInfo_1->Id) == GetFirstSpellInChain(spellId_2); -} + mSpellProcMap.clear(); // need for reload case -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; + uint32 count = 0; - if (sSpellMgr->IsSkillBonusSpell(spellInfo->Id)) - return false; + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + QueryResult result = WorldDatabase.Query("SELECT spellId, schoolMask, spellFamilyName, spellFamilyMask0, spellFamilyMask1, spellFamilyMask2, typeMask, spellTypeMask, spellPhaseMask, hitMask, attributesMask, ratePerMinute, chance, cooldown, charges FROM spell_proc"); + if (!result) + { + sLog->outString(">> Loaded %u spell proc conditions and data", count); + sLog->outString(); + return; + } - // All stance spells. if any better way, change it. - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + do { - switch (spellInfo->SpellFamilyName) + Field* fields = result->Fetch(); + + int32 spellId = fields[0].GetInt32(); + + bool allRanks = false; + if (spellId <=0) { - 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; + allRanks = true; + spellId = -spellId; } - } - return true; -} -bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId) -{ - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) - return false; - - for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i) - { - if (spellInfo->Effect[i] == SPELL_EFFECT_SKILL) + SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spellId); + if (!spellEntry) { - uint32 skill = spellInfo->EffectMiscValue[i]; + sLog->outErrorDb("Spell %u listed in `spell_proc` does not exist", spellId); + continue; + } - bool found = IsProfessionOrRidingSkill(skill); - if (found) - return true; + if (allRanks) + { + if (sSpellMgr->GetFirstSpellInChain(spellId) != uint32(spellId)) + { + sLog->outErrorDb("Spell %u listed in `spell_proc` is not first rank of spell.", fields[0].GetInt32()); + continue; + } } - } - return false; -} -bool SpellMgr::IsProfessionSpell(uint32 spellId) -{ - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) - return false; + SpellProcEntry baseProcEntry; - for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i) - { - if (spellInfo->Effect[i] == SPELL_EFFECT_SKILL) + baseProcEntry.schoolMask = fields[1].GetUInt32(); + baseProcEntry.spellFamilyName = fields[2].GetUInt32(); + baseProcEntry.spellFamilyMask[0] = fields[3].GetUInt32(); + baseProcEntry.spellFamilyMask[1] = fields[4].GetUInt32(); + baseProcEntry.spellFamilyMask[2] = fields[5].GetUInt32(); + baseProcEntry.typeMask = fields[6].GetUInt32(); + baseProcEntry.spellTypeMask = fields[7].GetUInt32(); + baseProcEntry.spellPhaseMask = fields[8].GetUInt32(); + baseProcEntry.hitMask = fields[9].GetUInt32(); + baseProcEntry.attributesMask = fields[10].GetUInt32(); + baseProcEntry.ratePerMinute = fields[11].GetFloat(); + baseProcEntry.chance = fields[12].GetFloat(); + float cooldown = fields[13].GetFloat(); + baseProcEntry.cooldown = uint32(cooldown); + baseProcEntry.charges = fields[14].GetUInt32(); + + while(true) { - uint32 skill = spellInfo->EffectMiscValue[i]; + if (mSpellProcMap.find(spellId) != mSpellProcMap.end()) + { + sLog->outErrorDb("Spell %u listed in `spell_proc` has duplicate entry in the table", spellId); + break; + } + SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); - bool found = IsProfessionSkill(skill); - if (found) - return true; - } - } - return false; -} + // take defaults from dbcs + if (!procEntry.typeMask) + procEntry.typeMask = spellEntry->ProcFlags; + if (!procEntry.charges) + procEntry.charges = spellEntry->ProcCharges; + if (!procEntry.chance && !procEntry.ratePerMinute) + procEntry.chance = float(spellEntry->ProcChance); -bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId) -{ - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId); - if (!spellInfo) - return false; + // validate data + if (procEntry.schoolMask & ~SPELL_SCHOOL_MASK_ALL) + sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `schoolMask` set: %u", spellId, procEntry.schoolMask); + if (procEntry.spellFamilyName && (procEntry.spellFamilyName < 3 || procEntry.spellFamilyName > 17 || procEntry.spellFamilyName == 14 || procEntry.spellFamilyName == 16)) + sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellFamilyName` set: %u", spellId, procEntry.spellFamilyName); + if (procEntry.chance < 0) + { + sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `chance` field", spellId); + procEntry.chance = 0; + } + if (procEntry.ratePerMinute < 0) + { + sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `ratePerMinute` field", spellId); + procEntry.ratePerMinute = 0; + } + if (cooldown < 0) + { + sLog->outErrorDb("`spell_proc` table entry for spellId %u has negative value in `cooldown` field", spellId); + procEntry.cooldown = 0; + } + if (procEntry.chance == 0 && procEntry.ratePerMinute == 0) + sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `chance` and `ratePerMinute` values defined, proc will not be triggered", spellId); + if (procEntry.charges > 99) + { + sLog->outErrorDb("`spell_proc` table entry for spellId %u has too big value in `charges` field", spellId); + procEntry.charges = 99; + } + if (!procEntry.typeMask) + sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `typeMask` value defined, proc will not be triggered", spellId); + if (procEntry.spellTypeMask & ~PROC_SPELL_PHASE_MASK_ALL) + sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellTypeMask` set: %u", spellId, procEntry.spellTypeMask); + if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) + sLog->outErrorDb("`spell_proc` table entry for spellId %u has `spellTypeMask` value defined, but it won't be used for defined `typeMask` value", spellId); + if (!procEntry.spellPhaseMask && procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK) + sLog->outErrorDb("`spell_proc` table entry for spellId %u doesn't have `spellPhaseMask` value defined, but it's required for defined `typeMask` value, proc will not be triggered", spellId); + if (procEntry.spellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) + sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `spellPhaseMask` set: %u", spellId, procEntry.spellPhaseMask); + if (procEntry.spellPhaseMask && !(procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK)) + sLog->outErrorDb("`spell_proc` table entry for spellId %u has `spellPhaseMask` value defined, but it won't be used for defined `typeMask` value", spellId); + if (procEntry.hitMask & ~PROC_HIT_MASK_ALL) + sLog->outErrorDb("`spell_proc` table entry for spellId %u has wrong `hitMask` set: %u", spellId, procEntry.hitMask); + if (procEntry.hitMask && !(procEntry.typeMask & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.typeMask & DONE_HIT_PROC_FLAG_MASK && (!procEntry.spellPhaseMask || procEntry.spellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) + sLog->outErrorDb("`spell_proc` table entry for spellId %u has `hitMask` value defined, but it won't be used for defined `typeMask` and `spellPhaseMask` values", spellId); - for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i) - { - if (spellInfo->Effect[i] == SPELL_EFFECT_SKILL) - { - uint32 skill = spellInfo->EffectMiscValue[i]; + mSpellProcMap[spellId] = procEntry; - bool found = IsPrimaryProfessionSkill(skill); - if (found) - return true; + if (allRanks) + { + spellId = sSpellMgr->GetNextSpellInChain(spellId); + spellEntry = sSpellMgr->GetSpellInfo(spellId); + } + else + break; } - } - return false; -} + ++count; + } while (result->NextRow()); -bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const -{ - return IsPrimaryProfessionSpell(spellId) && GetSpellRank(spellId) == 1; + sLog->outString(">> Loaded %u spell proc conditions and data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); } -bool SpellMgr::IsSkillBonusSpell(uint32 spellId) const +void SpellMgr::LoadSpellBonusess() { - SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId); + uint32 oldMSTime = getMSTime(); - for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) + mSpellBonusMap.clear(); // need for reload case + uint32 count = 0; + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT entry, direct_bonus, dot_bonus, ap_bonus, ap_dot_bonus FROM spell_bonus_data"); + if (!result) { - 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; + sLog->outString(">> Loaded %u spell bonus data", count); + sLog->outString(); + return; } - return false; -} + do + { + Field *fields = result->Fetch(); + uint32 entry = fields[0].GetUInt32(); -bool SpellMgr::IsSkillTypeSpell(uint32 spellId, SkillType type) const -{ - SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId); + SpellInfo const* spell = sSpellMgr->GetSpellInfo(entry); + if (!spell) + { + sLog->outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry); + continue; + } - for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx) - if (_spell_idx->second->skillId == uint32(type)) - return true; + SpellBonusEntry& sbe = mSpellBonusMap[entry]; + 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(); - return false; + ++count; + } while (result->NextRow()); + + sLog->outString(">> Loaded %u extra spell bonus data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); } -// basepoints provided here have to be valid basepoints (use SpellMgr::CalculateSpellEffectBaseAmount) -int32 SpellMgr::CalculateSpellEffectAmount(SpellEntry const* spellEntry, uint8 effIndex, Unit const* caster, int32 const* effBasePoints, Unit const* /*target*/) +void SpellMgr::LoadSpellThreats() { - float basePointsPerLevel = spellEntry->EffectRealPointsPerLevel[effIndex]; - int32 basePoints = effBasePoints ? *effBasePoints : spellEntry->EffectBasePoints[effIndex]; - int32 randomPoints = int32(spellEntry->EffectDieSides[effIndex]); + uint32 oldMSTime = getMSTime(); + + mSpellThreatMap.clear(); // need for reload case + + uint32 count = 0; - // base amount modification based on spell lvl vs caster lvl - if (caster) + // 0 1 + QueryResult result = WorldDatabase.Query("SELECT entry, Threat FROM spell_threat"); + if (!result) { - int32 level = int32(caster->getLevel()); - if (level > int32(spellEntry->maxLevel) && spellEntry->maxLevel > 0) - level = int32(spellEntry->maxLevel); - else if (level < int32(spellEntry->baseLevel)) - level = int32(spellEntry->baseLevel); - level -= int32(spellEntry->spellLevel); - basePoints += int32(level * basePointsPerLevel); + sLog->outString(">> Loaded %u aggro generating spells", count); + sLog->outString(); + return; } - // roll in a range <1;EffectDieSides> as of patch 3.3.3 - switch (randomPoints) + do { - case 0: break; - case 1: basePoints += 1; break; // range 1..1 - default: - // range can have positive (1..rand) and negative (rand..1) values, so order its for irand - int32 randvalue = (randomPoints >= 1) - ? irand(1, randomPoints) - : irand(randomPoints, 1); + Field *fields = result->Fetch(); - basePoints += randvalue; - break; - } + uint32 entry = fields[0].GetUInt32(); + uint16 Threat = fields[1].GetUInt16(); - float value = float(basePoints); + if (!sSpellMgr->GetSpellInfo(entry)) + { + sLog->outErrorDb("Spell %u listed in `spell_threat` does not exist", entry); + continue; + } - // random damage - if (caster) - { - // bonus amount from combo points - if (caster->m_movedPlayer) - if (uint8 comboPoints = caster->m_movedPlayer->GetComboPoints()) - if (float comboDamage = spellEntry->EffectPointsPerComboPoint[effIndex]) - value += comboDamage * comboPoints; - - value = caster->ApplyEffectModifiers(spellEntry, effIndex, value); - - // amount multiplication based on caster's level - if (!basePointsPerLevel && (spellEntry->Attributes & SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION && spellEntry->spellLevel) && - spellEntry->Effect[effIndex] != SPELL_EFFECT_WEAPON_PERCENT_DAMAGE && - spellEntry->Effect[effIndex] != SPELL_EFFECT_KNOCK_BACK && - spellEntry->EffectApplyAuraName[effIndex] != SPELL_AURA_MOD_SPEED_ALWAYS && - spellEntry->EffectApplyAuraName[effIndex] != SPELL_AURA_MOD_SPEED_NOT_STACK && - spellEntry->EffectApplyAuraName[effIndex] != SPELL_AURA_MOD_INCREASE_SPEED && - spellEntry->EffectApplyAuraName[effIndex] != SPELL_AURA_MOD_DECREASE_SPEED) - //there are many more: slow speed, -healing pct - value *= 0.25f * exp(caster->getLevel() * (70 - spellEntry->spellLevel) / 1000.0f); - //value = int32(value * (int32)getLevel() / (int32)(spellProto->spellLevel ? spellProto->spellLevel : 1)); - } + mSpellThreatMap[entry] = Threat; - return int32(value); -} + ++count; + } while (result->NextRow()); -int32 SpellMgr::CalculateSpellEffectBaseAmount(int32 value, SpellEntry const* spellEntry, uint8 effIndex) -{ - if (spellEntry->EffectDieSides[effIndex] == 0) - return value; - else - return value - 1; + sLog->outString(">> Loaded %u aggro generating spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); } -float SpellMgr::CalculateSpellEffectValueMultiplier(SpellEntry const* spellEntry, uint8 effIndex, Unit* caster, Spell* spell) +void SpellMgr::LoadSkillLineAbilityMap() { - float multiplier = spellEntry->EffectValueMultiplier[effIndex]; - if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(spellEntry->Id, SPELLMOD_VALUE_MULTIPLIER, multiplier, spell); - return multiplier; -} + uint32 oldMSTime = getMSTime(); -float SpellMgr::CalculateSpellEffectDamageMultiplier(SpellEntry const* spellEntry, uint8 effIndex, Unit* caster, Spell* spell) -{ - float multiplier = spellEntry->EffectDamageMultiplier[effIndex]; - if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(spellEntry->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplier, spell); - return multiplier; + mSkillLineAbilityMap.clear(); + + uint32 count = 0; + + for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) + { + SkillLineAbilityEntry const* SkillInfo = sSkillLineAbilityStore.LookupEntry(i); + if (!SkillInfo) + continue; + + mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId, SkillInfo)); + ++count; + } + + sLog->outString(">> Loaded %u SkillLineAbility MultiMap Data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); } -SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const +void SpellMgr::LoadSpellPetAuras() { - // ignore passive spells - if (IsPassiveSpell(spellInfo->Id)) - return spellInfo; + uint32 oldMSTime = getMSTime(); - bool needRankSelection = false; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + mSpellPetAuraMap.clear(); // need for reload case + + // 0 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT spell, effectId, pet, aura FROM spell_pet_auras"); + if (!result) { - 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; - } + sLog->outString(">> Loaded 0 spell pet auras. DB table `spell_pet_auras` is empty."); + sLog->outString(); + return; } - // not required - if (!needRankSelection) - return spellInfo; + uint32 count = 0; - for (uint32 nextSpellId = spellInfo->Id; nextSpellId != 0; nextSpellId = GetPrevSpellInChain(nextSpellId)) + do { - SpellEntry const* nextSpellInfo = sSpellStore.LookupEntry(nextSpellId); - if (!nextSpellInfo) - break; + Field *fields = result->Fetch(); - // if found appropriate level - if (playerLevel + 10 >= nextSpellInfo->spellLevel) - return nextSpellInfo; + uint32 spell = fields[0].GetUInt32(); + uint8 eff = fields[1].GetUInt8(); + uint32 pet = fields[2].GetUInt32(); + uint32 aura = fields[3].GetUInt32(); - // one rank less then - } + SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff); + if (itr != mSpellPetAuraMap.end()) + itr->second.AddAura(pet, aura); + else + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell); + if (!spellInfo) + { + sLog->outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell); + continue; + } + if (spellInfo->Effects[eff].Effect != SPELL_EFFECT_DUMMY && + (spellInfo->Effects[eff].Effect != SPELL_EFFECT_APPLY_AURA || + spellInfo->Effects[eff].ApplyAuraName != SPELL_AURA_DUMMY)) + { + sLog->outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell); + continue; + } - // not found - return NULL; + SpellInfo const* spellInfo2 = sSpellMgr->GetSpellInfo(aura); + if (!spellInfo2) + { + sLog->outErrorDb("Aura %u listed in `spell_pet_auras` does not exist", aura); + continue; + } + + PetAura pa(pet, aura, spellInfo->Effects[eff].TargetA == TARGET_UNIT_PET, spellInfo->Effects[eff].CalcValue()); + mSpellPetAuraMap[(spell<<8) + eff] = pa; + } + + ++count; + } while (result->NextRow()); + + sLog->outString(">> Loaded %u spell pet auras in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); } -void SpellMgr::LoadSpellLearnSkills() +// Fill custom data about enchancments +void SpellMgr::LoadEnchantCustomAttr() { uint32 oldMSTime = getMSTime(); - mSpellLearnSkills.clear(); // need for reload case + uint32 size = sSpellItemEnchantmentStore.GetNumRows(); + mEnchantCustomAttr.resize(size); - // search auto-learned skills and add its to map also for use in unlearn spells/talents - uint32 dbc_count = 0; - for (uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell) + uint32 count = 0; + + for (uint32 i = 0; i < size; ++i) + mEnchantCustomAttr[i] = 0; + + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { - SpellEntry const* entry = sSpellStore.LookupEntry(spell); + SpellInfo const* spellInfo = GetSpellInfo(i); + if (!spellInfo) + continue; - if (!entry) + // TODO: find a better check + if (!(spellInfo->AttributesEx2 & SPELL_ATTR2_UNK13) || !(spellInfo->Attributes & SPELL_ATTR0_NOT_SHAPESHIFT)) continue; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) { - if (entry->Effect[i] == SPELL_EFFECT_SKILL) + if (spellInfo->Effects[j].Effect == SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) { - SpellLearnSkillNode dbc_node; - dbc_node.skill = entry->EffectMiscValue[i]; - dbc_node.step = SpellMgr::CalculateSpellEffectAmount(entry, i); - if (dbc_node.skill != SKILL_RIDING) - dbc_node.value = 1; - else - dbc_node.value = dbc_node.step * 75; - dbc_node.maxvalue = dbc_node.step * 75; - mSpellLearnSkills[spell] = dbc_node; - ++dbc_count; + uint32 enchId = spellInfo->Effects[j].MiscValue; + SpellItemEnchantmentEntry const* ench = sSpellItemEnchantmentStore.LookupEntry(enchId); + if (!ench) + continue; + mEnchantCustomAttr[enchId] = true; + ++count; break; } } } - sLog->outString(">> Loaded %u Spell Learn Skills from DBC in %u ms", dbc_count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(">> Loaded %u custom enchant attributes in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } -void SpellMgr::LoadSpellLearnSpells() +void SpellMgr::LoadSpellEnchantProcData() { uint32 oldMSTime = getMSTime(); - mSpellLearnSpells.clear(); // need for reload case + mSpellEnchantProcEventMap.clear(); // need for reload case - // 0 1 2 - QueryResult result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell"); + uint32 count = 0; + + // 0 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data"); if (!result) { - sLog->outString(">> Loaded 0 spell learn spells"); + sLog->outString(">> Loaded %u spell enchant proc event conditions", count); sLog->outString(); - sLog->outErrorDb("`spell_learn_spell` table is empty!"); return; } - uint32 count = 0; - do { 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; + uint32 enchantId = fields[0].GetUInt32(); - if (!sSpellStore.LookupEntry(spell_id)) + SpellItemEnchantmentEntry const* ench = sSpellItemEnchantmentStore.LookupEntry(enchantId); + if (!ench) { - sLog->outErrorDb("Spell %u listed in `spell_learn_spell` does not exist", spell_id); + sLog->outErrorDb("Enchancment %u listed in `spell_enchant_proc_data` does not exist", enchantId); 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; - } + SpellEnchantProcEntry spe; - if (GetTalentSpellCost(node.spell)) - { - sLog->outErrorDb("Spell %u listed in `spell_learn_spell` attempt learning talent spell %u, skipped", spell_id, node.spell); - continue; - } + spe.customChance = fields[1].GetUInt32(); + spe.PPMChance = fields[2].GetFloat(); + spe.procEx = fields[3].GetUInt32(); - mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell_id, node)); + mSpellEnchantProcEventMap[enchantId] = spe; ++count; } while (result->NextRow()); - // search auto-learned spells and add its to map also for use in unlearn spells/talents - uint32 dbc_count = 0; - for (uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell) - { - SpellEntry const* entry = sSpellStore.LookupEntry(spell); - - if (!entry) - continue; - - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (entry->Effect[i] == SPELL_EFFECT_LEARN_SPELL) - { - SpellLearnSpellNode dbc_node; - dbc_node.spell = entry->EffectTriggerSpell[i]; - dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself) - - // ignore learning not existed spells (broken/outdated/or generic learnig spell 483 - if (!sSpellStore.LookupEntry(dbc_node.spell)) - continue; - - // talent or passive spells or skill-step spells auto-casted and not need dependent learning, - // pet teaching spells must not be dependent learning (casted) - // other required explicit dependent learning - dbc_node.autoLearned = entry->EffectImplicitTargetA[i] == TARGET_UNIT_PET || GetTalentSpellCost(spell) > 0 || IsPassiveSpell(spell) || IsSpellHaveEffect(entry, SPELL_EFFECT_SKILL_STEP); - - SpellLearnSpellMapBounds db_node_bounds = GetSpellLearnSpellMapBounds(spell); - - bool found = false; - for (SpellLearnSpellMap::const_iterator itr = db_node_bounds.first; itr != db_node_bounds.second; ++itr) - { - if (itr->second.spell == dbc_node.spell) - { - sLog->outErrorDb("Spell %u auto-learn spell %u in spell.dbc then the record in `spell_learn_spell` is redundant, please fix DB.", - spell, dbc_node.spell); - found = true; - break; - } - } - - if (!found) // add new spell-spell pair if not found - { - mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell, dbc_node)); - ++dbc_count; - } - } - } - } - - sLog->outString(">> Loaded %u spell learn spells + %u found in DBC in %u ms", count, dbc_count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(">> Loaded %u enchant proc data definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } -void SpellMgr::LoadSpellPetAuras() +void SpellMgr::LoadSpellLinked() { uint32 oldMSTime = getMSTime(); - mSpellPetAuraMap.clear(); // need for reload case + mSpellLinkedMap.clear(); // need for reload case - // 0 1 2 3 - QueryResult result = WorldDatabase.Query("SELECT spell, effectId, pet, aura FROM spell_pet_auras"); + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT spell_trigger, spell_effect, type FROM spell_linked_spell"); if (!result) { - sLog->outString(">> Loaded 0 spell pet auras. DB table `spell_pet_auras` is empty."); + sLog->outString(">> Loaded 0 linked spells. DB table `spell_linked_spell` is empty."); sLog->outString(); return; } @@ -2312,45 +2138,36 @@ void SpellMgr::LoadSpellPetAuras() { Field *fields = result->Fetch(); - uint32 spell = fields[0].GetUInt32(); - uint8 eff = fields[1].GetUInt8(); - uint32 pet = fields[2].GetUInt32(); - uint32 aura = fields[3].GetUInt32(); + int32 trigger = fields[0].GetInt32(); + int32 effect = fields[1].GetInt32(); + int32 type = fields[2].GetInt32(); - SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff); - if (itr != mSpellPetAuraMap.end()) - itr->second.AddAura(pet, aura); - else + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(abs(trigger)); + if (!spellInfo) { - 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; - } + sLog->outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(trigger)); + continue; + } + spellInfo = sSpellMgr->GetSpellInfo(abs(effect)); + if (!spellInfo) + { + sLog->outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(effect)); + continue; + } - PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[eff] == TARGET_UNIT_PET, SpellMgr::CalculateSpellEffectAmount(spellInfo, eff)); - mSpellPetAuraMap[(spell<<8) + eff] = pa; + if (type) //we will find a better way when more types are needed + { + if (trigger > 0) + trigger += SPELL_LINKED_MAX_SPELLS * type; + else + trigger -= SPELL_LINKED_MAX_SPELLS * type; } + mSpellLinkedMap[trigger].push_back(effect); ++count; } while (result->NextRow()); - sLog->outString(">> Loaded %u spell pet auras in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(">> Loaded %u linked spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } @@ -2390,18 +2207,18 @@ void SpellMgr::LoadPetLevelupSpellMap() if (skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) continue; - SpellEntry const* spell = sSpellStore.LookupEntry(skillLine->spellId); + SpellInfo const* spell = sSpellMgr->GetSpellInfo(skillLine->spellId); if (!spell) // not exist or triggered or talent continue; - if (!spell->spellLevel) + 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; } } @@ -2499,18 +2316,17 @@ void SpellMgr::LoadPetDefaultSpells() oldMSTime = getMSTime(); // different summon spells - for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i) + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { - - SpellEntry const* spellEntry = sSpellStore.LookupEntry(i); + SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(i); if (!spellEntry) continue; for (uint8 k = 0; k < MAX_SPELL_EFFECTS; ++k) { - if (spellEntry->Effect[k] == SPELL_EFFECT_SUMMON || spellEntry->Effect[k] == SPELL_EFFECT_SUMMON_PET) + if (spellEntry->Effects[k].Effect == SPELL_EFFECT_SUMMON || spellEntry->Effects[k].Effect == SPELL_EFFECT_SUMMON_PET) { - uint32 creature_id = spellEntry->EffectMiscValue[k]; + uint32 creature_id = spellEntry->Effects[k].MiscValue; CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(creature_id); if (!cInfo) continue; @@ -2541,99 +2357,6 @@ void SpellMgr::LoadPetDefaultSpells() sLog->outString(); } -/// Some checks for spells, to prevent adding deprecated/broken spells for trainers, spell book, etc -bool SpellMgr::IsSpellValid(SpellEntry const *spellInfo, Player *pl, bool msg) -{ - // not exist - if (!spellInfo) - return false; - - bool need_check_reagents = false; - - // check effects - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - switch (spellInfo->Effect[i]) - { - case 0: - continue; - - // craft spell for crafting non-existed item (break client recipes list show) - case SPELL_EFFECT_CREATE_ITEM: - case SPELL_EFFECT_CREATE_ITEM_2: - { - if (spellInfo->EffectItemType[i] == 0) - { - // skip auto-loot crafting spells, its not need explicit item info (but have special fake items sometime) - if (!IsLootCraftingSpell(spellInfo)) - { - if (msg) - { - if (pl) - ChatHandler(pl).PSendSysMessage("Craft spell %u not have create item entry.", spellInfo->Id); - else - sLog->outErrorDb("Craft spell %u not have create item entry.", spellInfo->Id); - } - return false; - } - - } - // also possible IsLootCraftingSpell case but fake item must exist anyway - else if (!sObjectMgr->GetItemTemplate(spellInfo->EffectItemType[i])) - { - if (msg) - { - if (pl) - ChatHandler(pl).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->EffectItemType[i]); - else - sLog->outErrorDb("Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->EffectItemType[i]); - } - return false; - } - - need_check_reagents = true; - break; - } - case SPELL_EFFECT_LEARN_SPELL: - { - SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(spellInfo->EffectTriggerSpell[i]); - if (!IsSpellValid(spellInfo2, pl, msg)) - { - if (msg) - { - if (pl) - ChatHandler(pl).PSendSysMessage("Spell %u learn to broken spell %u, and then...", spellInfo->Id, spellInfo->EffectTriggerSpell[i]); - else - sLog->outErrorDb("Spell %u learn to invalid spell %u, and then...", spellInfo->Id, spellInfo->EffectTriggerSpell[i]); - } - return false; - } - break; - } - } - } - - if (need_check_reagents) - { - for (uint8 j = 0; j < MAX_SPELL_REAGENTS; ++j) - { - if (spellInfo->Reagent[j] > 0 && !sObjectMgr->GetItemTemplate(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() { uint32 oldMSTime = getMSTime(); @@ -2672,10 +2395,10 @@ void SpellMgr::LoadSpellAreas() spellArea.gender = Gender(fields[7].GetUInt8()); spellArea.autocast = fields[8].GetBool(); - if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell)) + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell)) { if (spellArea.autocast) - const_cast<SpellEntry*>(spellInfo)->Attributes |= SPELL_ATTR0_CANT_CANCEL; + const_cast<SpellInfo*>(spellInfo)->Attributes |= SPELL_ATTR0_CANT_CANCEL; } else { @@ -2742,7 +2465,7 @@ void SpellMgr::LoadSpellAreas() if (spellArea.auraSpell) { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell)); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(abs(spellArea.auraSpell)); if (!spellInfo) { sLog->outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell, abs(spellArea.auraSpell)); @@ -2835,901 +2558,58 @@ void SpellMgr::LoadSpellAreas() sLog->outString(); } -SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player) -{ - // normal case - if (spellInfo->AreaGroupId > 0) - { - bool found = false; - AreaGroupEntry const* groupEntry = sAreaGroupStore.LookupEntry(spellInfo->AreaGroupId); - while (groupEntry) - { - for (uint8 i = 0; i < MAX_GROUP_AREA_IDS; ++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_ATTR4_CAST_ONLY_IN_OUTLAND) - { - uint32 v_map = GetVirtualMapForMapAndZone(map_id, zone_id); - MapEntry const* mapEntry = sMapStore.LookupEntry(v_map); - if (!mapEntry || mapEntry->addon < 1 || !mapEntry->IsContinent()) - return SPELL_FAILED_INCORRECT_AREA; - } - - // raid instance limitation - if (spellInfo->AttributesEx6 & SPELL_ATTR6_NOT_IN_RAID_INSTANCE) - { - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if (!mapEntry || mapEntry->IsRaid()) - return SPELL_FAILED_NOT_IN_RAID_INSTANCE; - } - - // DB base check (if non empty then must fit at least single for allow) - SpellAreaMapBounds saBounds = sSpellMgr->GetSpellAreaMapBounds(spellInfo->Id); - if (saBounds.first != saBounds.second) - { - for (SpellAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - { - if (itr->second.IsFitToRequirements(player, zone_id, area_id)) - return SPELL_CAST_OK; - } - return SPELL_FAILED_INCORRECT_AREA; - } - - // bg spell checks - switch (spellInfo->Id) - { - case 23333: // Warsong Flag - case 23335: // Silverwing Flag - return map_id == 489 && player && player->InBattleground() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; - case 34976: // Netherstorm Flag - return map_id == 566 && player && player->InBattleground() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; - case 2584: // Waiting to Resurrect - case 22011: // Spirit Heal Channel - case 22012: // Spirit Heal - case 24171: // Resurrection Impact Visual - case 42792: // Recently Dropped Flag - case 43681: // Inactive - case 44535: // Spirit Heal (mana) - { - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if (!mapEntry) - return SPELL_FAILED_INCORRECT_AREA; - - return zone_id == 4197 || (mapEntry->IsBattleground() && player && player->InBattleground()) ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; - } - case 44521: // Preparation - { - if (!player) - return SPELL_FAILED_REQUIRES_AREA; - - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if (!mapEntry) - return SPELL_FAILED_INCORRECT_AREA; - - if (!mapEntry->IsBattleground()) - return SPELL_FAILED_REQUIRES_AREA; - - Battleground* bg = player->GetBattleground(); - return bg && bg->GetStatus() == STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; - } - case 32724: // Gold Team (Alliance) - case 32725: // Green Team (Alliance) - case 35774: // Gold Team (Horde) - case 35775: // Green Team (Horde) - { - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if (!mapEntry) - return SPELL_FAILED_INCORRECT_AREA; - - return mapEntry->IsBattleArena() && player && player->InBattleground() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; - } - case 32727: // Arena Preparation - { - if (!player) - return SPELL_FAILED_REQUIRES_AREA; - - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if (!mapEntry) - return SPELL_FAILED_INCORRECT_AREA; - - if (!mapEntry->IsBattleArena()) - return SPELL_FAILED_REQUIRES_AREA; - - Battleground* bg = player->GetBattleground(); - return bg && bg->GetStatus() == STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; - } - } - - // aura limitations - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - switch (spellInfo->EffectApplyAuraName[i]) - { - case SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED: - case SPELL_AURA_FLY: - { - if (player && !player->IsKnowHowFlyIn(map_id, zone_id)) - return SPELL_FAILED_INCORRECT_AREA; - } - } - } - - return SPELL_CAST_OK; -} - -void SpellMgr::LoadSkillLineAbilityMap() -{ - uint32 oldMSTime = getMSTime(); - - mSkillLineAbilityMap.clear(); - - uint32 count = 0; - - for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) - { - SkillLineAbilityEntry const* SkillInfo = sSkillLineAbilityStore.LookupEntry(i); - if (!SkillInfo) - continue; - - mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId, SkillInfo)); - ++count; - } - - sLog->outString(">> Loaded %u SkillLineAbility MultiMap Data in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - sLog->outString(); -} - -DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered) -{ - if (IsPositiveSpell(spellproto->Id)) - return DIMINISHING_NONE; - - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (spellproto->EffectApplyAuraName[i] == SPELL_AURA_MOD_TAUNT) - return DIMINISHING_TAUNT; - } - - // Explicit Diminishing Groups - switch (spellproto->SpellFamilyName) - { - // Event spells - case SPELLFAMILY_UNK1: - return DIMINISHING_NONE; - case SPELLFAMILY_DEATHKNIGHT: - { - // Hungering Cold (no flags) - if (spellproto->SpellIconID == 2797) - return DIMINISHING_DISORIENT; - // Mark of Blood - else if ((spellproto->SpellFamilyFlags[0] & 0x10000000) && spellproto->SpellIconID == 2285) - return DIMINISHING_LIMITONLY; - break; - } - case SPELLFAMILY_DRUID: - { - // Pounce - if (spellproto->SpellFamilyFlags[0] & 0x20000) - return DIMINISHING_OPENING_STUN; - // Cyclone - else if (spellproto->SpellFamilyFlags[1] & 0x20) - return DIMINISHING_CYCLONE; - // Entangling Roots - // Nature's Grasp - else if (spellproto->SpellFamilyFlags[0] & 0x00000200) - return DIMINISHING_CONTROLLED_ROOT; - // Faerie Fire - else if (spellproto->SpellFamilyFlags[0] & 0x400) - return DIMINISHING_LIMITONLY; - break; - } - case SPELLFAMILY_HUNTER: - { - // Hunter's mark - if ((spellproto->SpellFamilyFlags[0] & 0x400) && spellproto->SpellIconID == 538) - return DIMINISHING_LIMITONLY; - // Scatter Shot (own diminishing) - else if ((spellproto->SpellFamilyFlags[0] & 0x40000) && spellproto->SpellIconID == 132) - return DIMINISHING_SCATTER_SHOT; - // Entrapment (own diminishing) - else if (spellproto->SpellVisual[0] == 7484 && spellproto->SpellIconID == 20) - return DIMINISHING_ENTRAPMENT; - // Wyvern Sting mechanic is MECHANIC_SLEEP but the diminishing is DIMINISHING_DISORIENT - else if ((spellproto->SpellFamilyFlags[1] & 0x1000) && spellproto->SpellIconID == 1721) - return DIMINISHING_DISORIENT; - // Freezing Arrow - else if (spellproto->SpellFamilyFlags[0] & 0x8) - return DIMINISHING_DISORIENT; - break; - } - case SPELLFAMILY_PALADIN: - { - // Judgement of Justice - limit duration to 10s in PvP - if (spellproto->SpellFamilyFlags[0] & 0x100000) - return DIMINISHING_LIMITONLY; - // Turn Evil - else if ((spellproto->SpellFamilyFlags[1] & 0x804000) && spellproto->SpellIconID == 309) - return DIMINISHING_FEAR; - break; - } - case SPELLFAMILY_PRIEST: - { - // Psychic Horror - if (spellproto->SpellFamilyFlags[2] & 0x2000) - return DIMINISHING_HORROR; - break; - } - case SPELLFAMILY_ROGUE: - { - // Gouge - if (spellproto->SpellFamilyFlags[0] & 0x8) - return DIMINISHING_DISORIENT; - // Blind - else if (spellproto->SpellFamilyFlags[0] & 0x1000000) - return DIMINISHING_FEAR; - // Cheap Shot - else if (spellproto->SpellFamilyFlags[0] & 0x400) - return DIMINISHING_OPENING_STUN; - // Crippling poison - Limit to 10 seconds in PvP (No SpellFamilyFlags) - else if (spellproto->SpellIconID == 163) - return DIMINISHING_LIMITONLY; - break; - } - case SPELLFAMILY_MAGE: - { - // Frostbite - if (spellproto->SpellFamilyFlags[1] & 0x80000000) - return DIMINISHING_ROOT; - // Shattered Barrier - else if (spellproto->SpellVisual[0] == 12297) - return DIMINISHING_ROOT; - // Deep Freeze - else if (spellproto->SpellIconID == 2939 && spellproto->SpellVisual[0] == 9963) - return DIMINISHING_CONTROLLED_STUN; - // Frost Nova / Freeze (Water Elemental) - else if (spellproto->SpellIconID == 193) - return DIMINISHING_CONTROLLED_ROOT; - // Dragon's Breath - else if (spellproto->SpellFamilyFlags[0] & 0x800000) - return DIMINISHING_DISORIENT; - break; - } - case SPELLFAMILY_WARLOCK: - { - // Death Coil - if (spellproto->SpellFamilyFlags[0] & 0x80000) - return DIMINISHING_HORROR; - // Curses/etc - else if ((spellproto->SpellFamilyFlags[0] & 0x80000000) || (spellproto->SpellFamilyFlags[1] & 0x200)) - return DIMINISHING_LIMITONLY; - // Seduction - else if (spellproto->SpellFamilyFlags[1] & 0x10000000) - return DIMINISHING_FEAR; - break; - } - case SPELLFAMILY_WARRIOR: - { - // Hamstring - limit duration to 10s in PvP - if (spellproto->SpellFamilyFlags[0] & 0x2) - return DIMINISHING_LIMITONLY; - // Improved Hamstring - else if (spellproto->AttributesEx3 & 0x80000 && spellproto->SpellIconID == 23) - return DIMINISHING_ROOT; - // Charge Stun (own diminishing) - else if (spellproto->SpellFamilyFlags[0] & 0x01000000) - return DIMINISHING_CHARGE; - break; - } - // Must be below SPELLFAMILY_WARRIOR for Charge to work - case SPELLFAMILY_GENERIC: - { - // Pet charge effects (Infernal Awakening, Demon Charge) - if (spellproto->SpellVisual[0] == 2816 && spellproto->SpellIconID == 15) - return DIMINISHING_CONTROLLED_STUN; - // Gnaw - else if (spellproto->Id == 47481) - return DIMINISHING_CONTROLLED_STUN; - } - default: - break; - } - - // Lastly - Set diminishing depending on mechanic - uint32 mechanic = GetAllSpellMechanicMask(spellproto); - if (mechanic & (1 << MECHANIC_CHARM)) - return DIMINISHING_MIND_CONTROL; - if (mechanic & (1 << MECHANIC_SILENCE)) - return DIMINISHING_SILENCE; - if (mechanic & (1 << MECHANIC_SLEEP)) - return DIMINISHING_SLEEP; - if (mechanic & ((1 << MECHANIC_SAPPED) | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_SHACKLE))) - return DIMINISHING_DISORIENT; - // Mechanic Knockout, except Blast Wave - if (mechanic & (1 << MECHANIC_KNOCKOUT) && spellproto->SpellIconID != 292) - return DIMINISHING_DISORIENT; - if (mechanic & (1 << MECHANIC_DISARM)) - return DIMINISHING_DISARM; - if (mechanic & (1 << MECHANIC_FEAR)) - return DIMINISHING_FEAR; - if (mechanic & (1 << MECHANIC_STUN)) - return triggered ? DIMINISHING_STUN : DIMINISHING_CONTROLLED_STUN; - if (mechanic & (1 << MECHANIC_BANISH)) - return DIMINISHING_BANISH; - if (mechanic & (1 << MECHANIC_ROOT)) - return triggered ? DIMINISHING_ROOT : DIMINISHING_CONTROLLED_ROOT; - - return DIMINISHING_NONE; -} - -int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry const* spellproto) -{ - if (!IsDiminishingReturnsGroupDurationLimited(group)) - return 0; - - // Explicit diminishing duration - switch (spellproto->SpellFamilyName) - { - case SPELLFAMILY_DRUID: - { - // Faerie Fire - limit to 40 seconds in PvP (3.1) - if (spellproto->SpellFamilyFlags[0] & 0x400) - return 40 * IN_MILLISECONDS; - break; - } - case SPELLFAMILY_HUNTER: - { - // Wyvern Sting - if (spellproto->SpellFamilyFlags[1] & 0x1000) - return 6 * IN_MILLISECONDS; - // Hunter's Mark - if (spellproto->SpellFamilyFlags[0] & 0x400) - return 120 * IN_MILLISECONDS; - break; - } - case SPELLFAMILY_PALADIN: - { - // Repentance - limit to 6 seconds in PvP - if (spellproto->SpellFamilyFlags[0] & 0x4) - return 6 * IN_MILLISECONDS; - break; - } - case SPELLFAMILY_WARLOCK: - { - // Banish - limit to 6 seconds in PvP - if (spellproto->SpellFamilyFlags[1] & 0x8000000) - return 6 * IN_MILLISECONDS; - // Curse of Tongues - limit to 12 seconds in PvP - else if (spellproto->SpellFamilyFlags[2] & 0x800) - return 12 * IN_MILLISECONDS; - // Curse of Elements - limit to 120 seconds in PvP - else if (spellproto->SpellFamilyFlags[1] & 0x200) - return 120 * IN_MILLISECONDS; - break; - } - default: - break; - } - - return 10 * IN_MILLISECONDS; -} - -bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group) -{ - switch (group) - { - case DIMINISHING_CONTROLLED_STUN: - case DIMINISHING_STUN: - case DIMINISHING_ENTRAPMENT: - case DIMINISHING_CONTROLLED_ROOT: - case DIMINISHING_ROOT: - case DIMINISHING_FEAR: - case DIMINISHING_MIND_CONTROL: - case DIMINISHING_DISORIENT: - case DIMINISHING_CYCLONE: - case DIMINISHING_BANISH: - case DIMINISHING_LIMITONLY: - case DIMINISHING_OPENING_STUN: - case DIMINISHING_HORROR: - case DIMINISHING_SLEEP: - return true; - default: - return false; - } -} - -DiminishingLevels GetDiminishingReturnsMaxLevel(DiminishingGroup group) -{ - switch (group) - { - case DIMINISHING_TAUNT: - return DIMINISHING_LEVEL_TAUNT_IMMUNE; - default: - return DIMINISHING_LEVEL_IMMUNE; - } -} - -DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) -{ - switch (group) - { - case DIMINISHING_TAUNT: - case DIMINISHING_CONTROLLED_STUN: - case DIMINISHING_STUN: - case DIMINISHING_OPENING_STUN: - case DIMINISHING_CYCLONE: - case DIMINISHING_CHARGE: - return DRTYPE_ALL; - case DIMINISHING_FEAR: - case DIMINISHING_CONTROLLED_ROOT: - case DIMINISHING_ROOT: - case DIMINISHING_MIND_CONTROL: - case DIMINISHING_DISORIENT: - case DIMINISHING_ENTRAPMENT: - case DIMINISHING_SILENCE: - case DIMINISHING_DISARM: - case DIMINISHING_BANISH: - case DIMINISHING_SCATTER_SHOT: - case DIMINISHING_HORROR: - case DIMINISHING_SLEEP: - return DRTYPE_PLAYER; - default: - break; - } - - return DRTYPE_NONE; -} - -bool IsPartOfSkillLine(uint32 skillId, uint32 spellId) -{ - SkillLineAbilityMapBounds skillBounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId); - for (SkillLineAbilityMap::const_iterator itr = skillBounds.first; itr != skillBounds.second; ++itr) - if (itr->second->skillId == skillId) - return true; - - return false; -} - -bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const -{ - if (gender != GENDER_NONE) // not in expected gender - if (!player || gender != player->getGender()) - return false; - - if (raceMask) // not in expected race - if (!player || !(raceMask & player->getRaceMask())) - return false; - - if (areaId) // not in expected zone - if (newZone != areaId && newArea != areaId) - return false; - - if (questStart) // not in expected required quest state - if (!player || ((!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart))) - return false; - - if (questEnd) // not in expected forbidden quest state - if (!player || player->GetQuestRewardStatus(questEnd)) - return false; - - if (auraSpell) // not have expected aura - if (!player || (auraSpell > 0 && !player->HasAura(auraSpell)) || (auraSpell < 0 && player->HasAura(-auraSpell))) - return false; - - // Extra conditions -- leaving the possibility add extra conditions... - switch (spellId) - { - case 58600: // No fly Zone - Dalaran - { - if (!player) - return false; - - AreaTableEntry const* pArea = GetAreaEntryByAreaID(player->GetAreaId()); - if (!(pArea && pArea->flags & AREA_FLAG_NO_FLY_ZONE)) - return false; - if (!player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !player->HasAuraType(SPELL_AURA_FLY)) - return false; - break; - } - case 68719: // Oil Refinery - Isle of Conquest. - case 68720: // Quarry - Isle of Conquest. - { - if (player->GetBattlegroundTypeId() != BATTLEGROUND_IC || !player->GetBattleground()) - return false; - - uint8 nodeType = spellId == 68719 ? NODE_TYPE_REFINERY : NODE_TYPE_QUARRY; - uint8 nodeState = player->GetTeamId() == TEAM_ALLIANCE ? NODE_STATE_CONTROLLED_A : NODE_STATE_CONTROLLED_H; - - BattlegroundIC* pIC = static_cast<BattlegroundIC*>(player->GetBattleground()); - if (pIC->GetNodeState(nodeType) == nodeState) - return true; - - return false; - } - } - - return true; -} - -bool SpellMgr::CanAurasStack(Aura const *aura1, Aura const *aura2, bool sameCaster) const -{ - SpellEntry const* spellInfo_1 = aura1->GetSpellProto(); - SpellEntry const* spellInfo_2 = aura2->GetSpellProto(); - SpellSpecific spellSpec_1 = GetSpellSpecific(spellInfo_1); - SpellSpecific spellSpec_2 = GetSpellSpecific(spellInfo_2); - if (spellSpec_1 && spellSpec_2) - if (IsSingleFromSpellSpecificPerTarget(spellSpec_1, spellSpec_2) - || (sameCaster && IsSingleFromSpellSpecificPerCaster(spellSpec_1, spellSpec_2))) - return false; - - SpellGroupStackRule stackRule = CheckSpellGroupStackRules(spellInfo_1->Id, spellInfo_2->Id); - if (stackRule) - { - if (stackRule == SPELL_GROUP_STACK_RULE_EXCLUSIVE) - return false; - if (sameCaster && stackRule == SPELL_GROUP_STACK_RULE_EXCLUSIVE_FROM_SAME_CASTER) - return false; - } - - if (spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName) - return true; - - if (!sameCaster) - { - if (spellInfo_1->AttributesEx3 & SPELL_ATTR3_STACK_FOR_DIFF_CASTERS) - return true; - - // check same periodic auras - for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - switch (spellInfo_1->EffectApplyAuraName[i]) - { - // DOT or HOT from different casters will stack - case SPELL_AURA_PERIODIC_DAMAGE: - case SPELL_AURA_PERIODIC_DUMMY: - case SPELL_AURA_PERIODIC_HEAL: - case SPELL_AURA_PERIODIC_TRIGGER_SPELL: - case SPELL_AURA_PERIODIC_ENERGIZE: - case SPELL_AURA_PERIODIC_MANA_LEECH: - case SPELL_AURA_PERIODIC_LEECH: - case SPELL_AURA_POWER_BURN_MANA: - case SPELL_AURA_OBS_MOD_POWER: - case SPELL_AURA_OBS_MOD_HEALTH: - case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE: - // periodic auras which target areas are not allowed to stack this way (replenishment for example) - if (IsAreaOfEffectSpellEffect(spellInfo_1, i) || IsAreaOfEffectSpellEffect(spellInfo_2, i)) - break; - return true; - default: - break; - } - } - } - - bool isVehicleAura1 = false; - bool isVehicleAura2 = false; - uint8 i = 0; - while (i < MAX_SPELL_EFFECTS && !(isVehicleAura1 && isVehicleAura2)) - { - if (spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_CONTROL_VEHICLE) - isVehicleAura1 = true; - if (spellInfo_2->EffectApplyAuraName[i] == SPELL_AURA_CONTROL_VEHICLE) - isVehicleAura2 = true; - - ++i; - } - - if (isVehicleAura1 && isVehicleAura2) - { - Vehicle* veh = NULL; - if (aura1->GetOwner()->ToUnit()) - veh = aura1->GetOwner()->ToUnit()->GetVehicleKit(); - - if (!veh) // We should probably just let it stack. Vehicle system will prevent undefined behaviour later - return true; - - if (!veh->GetAvailableSeatCount()) - return false; // No empty seat available - - return true; // Empty seat available (skip rest) - } - - uint32 spellId_1 = GetLastSpellInChain(spellInfo_1->Id); - uint32 spellId_2 = GetLastSpellInChain(spellInfo_2->Id); - - // same spell - if (spellId_1 == spellId_2) - { - // Hack for Incanter's Absorption - if (spellId_1 == 44413) - return true; - if (aura1->GetCastItemGUID() && aura2->GetCastItemGUID()) - if (aura1->GetCastItemGUID() != aura2->GetCastItemGUID() && (GetSpellCustomAttr(spellId_1) & SPELL_ATTR0_CU_ENCHANT_PROC)) - return true; - // same spell with same caster should not stack - return false; - } - - return true; -} - -bool CanSpellDispelAura(SpellEntry const* dispelSpell, SpellEntry const* aura) -{ - // These auras (like ressurection sickness) can't be dispelled - if (aura->Attributes & SPELL_ATTR0_NEGATIVE_1) - return false; - - // These spells (like Mass Dispel) can dispell all auras - if (dispelSpell->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) - return true; - - // These auras (like Divine Shield) can't be dispelled - if (aura->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) - return false; - - // These auras (Cyclone for example) are not dispelable - if (aura->AttributesEx & SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) - return false; - - return true; -} - -bool CanSpellPierceImmuneAura(SpellEntry const* pierceSpell, SpellEntry const* aura) -{ - // these spells pierce all avalible spells (Resurrection Sickness for example) - if (pierceSpell->Attributes & SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) - return true; - - // these spells (Cyclone for example) can pierce all... - if ((pierceSpell->AttributesEx & SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) - // ...but not these (Divine shield for example) - && !(aura && (aura->Mechanic == MECHANIC_IMMUNE_SHIELD || aura->Mechanic == MECHANIC_INVULNERABILITY))) - return true; - - return false; -} - -void SpellMgr::LoadSpellEnchantProcData() -{ - uint32 oldMSTime = getMSTime(); - - 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) - { - sLog->outString(">> Loaded %u spell enchant proc event conditions", count); - sLog->outString(); - return; - } - - do - { - Field *fields = result->Fetch(); - - uint32 enchantId = fields[0].GetUInt32(); - - SpellItemEnchantmentEntry const* ench = sSpellItemEnchantmentStore.LookupEntry(enchantId); - if (!ench) - { - sLog->outErrorDb("Enchancment %u listed in `spell_enchant_proc_data` does not exist", enchantId); - continue; - } - - SpellEnchantProcEntry spe; - - spe.customChance = fields[1].GetUInt32(); - spe.PPMChance = fields[2].GetFloat(); - spe.procEx = fields[3].GetUInt32(); - - mSpellEnchantProcEventMap[enchantId] = spe; - - ++count; - } while (result->NextRow()); - - sLog->outString(">> Loaded %u enchant proc data definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - sLog->outString(); -} - -void SpellMgr::LoadSpellRequired() +void SpellMgr::LoadSpellInfoStore() { uint32 oldMSTime = getMSTime(); - mSpellsReqSpell.clear(); // need for reload case - mSpellReq.clear(); // need for reload case - - QueryResult result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required"); + UnloadSpellInfoStore(); + mSpellInfoMap.resize(sSpellStore.GetNumRows(), NULL); - if (!result) + for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i) { - sLog->outString(">> Loaded 0 spell required records"); - sLog->outString(); - sLog->outErrorDb("`spell_required` table is empty!"); - return; + if (SpellEntry const* spellEntry = sSpellStore.LookupEntry(i)) + mSpellInfoMap[i] = new SpellInfo(spellEntry); } - uint32 rows = 0; - - do - { - Field *fields = result->Fetch(); - - uint32 spell_id = fields[0].GetUInt32(); - uint32 spell_req = fields[1].GetUInt32(); - // check if chain is made with valid first spell - SpellEntry const* spell = sSpellStore.LookupEntry(spell_id); - if (!spell) - { - sLog->outErrorDb("spell_id %u in `spell_required` table is not found in dbcs, skipped", spell_id); - continue; - } - SpellEntry const* req_spell = sSpellStore.LookupEntry(spell_req); - if (!req_spell) - { - sLog->outErrorDb("req_spell %u in `spell_required` table is not found in dbcs, skipped", spell_req); - continue; - } - if (GetFirstSpellInChain(spell_id) == GetFirstSpellInChain(spell_req)) - { - sLog->outErrorDb("req_spell %u and spell_id %u in `spell_required` table are ranks of the same spell, entry not needed, skipped", spell_req, spell_id); - continue; - } - if (IsSpellRequiringSpell(spell_id, spell_req)) - { - sLog->outErrorDb("duplicated entry of req_spell %u and spell_id %u in `spell_required`, skipped", spell_req, spell_id); - continue; - } - mSpellReq.insert (std::pair<uint32, uint32>(spell_id, spell_req)); - mSpellsReqSpell.insert (std::pair<uint32, uint32>(spell_req, spell_id)); - ++rows; - } while (result->NextRow()); - - sLog->outString(">> Loaded %u spell required records in %u ms", rows, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(">> Loaded spell custom attributes in %u ms", GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); } -void SpellMgr::LoadSpellRanks() +void SpellMgr::UnloadSpellInfoStore() { - uint32 oldMSTime = getMSTime(); - - mSpellChains.clear(); // need for reload case - - QueryResult result = WorldDatabase.Query("SELECT first_spell_id, spell_id, rank from spell_ranks ORDER BY first_spell_id , rank"); - - if (!result) + for (uint32 i = 0; i < mSpellInfoMap.size(); ++i) { - sLog->outString(">> Loaded 0 spell rank records"); - sLog->outString(); - sLog->outErrorDb("`spell_ranks` table is empty!"); - return; + if (mSpellInfoMap[i]) + delete mSpellInfoMap[i]; } - - uint32 rows = 0; - bool finished = false; - - do - { - // spellid, rank - std::list < std::pair < int32, int32 > > rankChain; - int32 currentSpell = -1; - int32 lastSpell = -1; - - // fill one chain - while (currentSpell == lastSpell && !finished) - { - Field *fields = result->Fetch(); - - currentSpell = fields[0].GetUInt32(); - if (lastSpell == -1) - lastSpell = currentSpell; - uint32 spell_id = fields[1].GetUInt32(); - uint32 rank = fields[2].GetUInt32(); - - // don't drop the row if we're moving to the next rank - if (currentSpell == lastSpell) - { - rankChain.push_back(std::make_pair(spell_id, rank)); - if (!result->NextRow()) - finished = true; - } - else - break; - } - // check if chain is made with valid first spell - SpellEntry const* first = sSpellStore.LookupEntry(lastSpell); - if (!first) - { - sLog->outErrorDb("Spell rank identifier(first_spell_id) %u listed in `spell_ranks` does not exist!", lastSpell); - continue; - } - // check if chain is long enough - if (rankChain.size() < 2) - { - sLog->outErrorDb("There is only 1 spell rank for identifier(first_spell_id) %u in `spell_ranks`, entry is not needed!", lastSpell); - continue; - } - int32 curRank = 0; - bool valid = true; - // check spells in chain - for (std::list<std::pair<int32, int32> >::iterator itr = rankChain.begin() ; itr!= rankChain.end(); ++itr) - { - SpellEntry const* spell = sSpellStore.LookupEntry(itr->first); - if (!spell) - { - sLog->outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not exist!", itr->first, itr->second, lastSpell); - valid = false; - break; - } - ++curRank; - if (itr->second != curRank) - { - sLog->outErrorDb("Spell %u (rank %u) listed in `spell_ranks` for chain %u does not have proper rank value(should be %u)!", itr->first, itr->second, lastSpell, curRank); - valid = false; - break; - } - } - if (!valid) - continue; - int32 prevRank = 0; - // insert the chain - std::list<std::pair<int32, int32> >::iterator itr = rankChain.begin(); - do - { - ++rows; - int32 addedSpell = itr->first; - mSpellChains[addedSpell].first = lastSpell; - mSpellChains[addedSpell].last = rankChain.back().first; - mSpellChains[addedSpell].rank = itr->second; - mSpellChains[addedSpell].prev = prevRank; - prevRank = addedSpell; - ++itr; - if (itr == rankChain.end()) - { - mSpellChains[addedSpell].next = 0; - break; - } - else - mSpellChains[addedSpell].next = itr->first; - } - while (true); - } while (!finished); - - sLog->outString(">> Loaded %u spell rank records in %u ms", rows, GetMSTimeDiffToNow(oldMSTime)); - sLog->outString(); + mSpellInfoMap.clear(); } -// set data in core for now void SpellMgr::LoadSpellCustomAttr() { uint32 oldMSTime = getMSTime(); - mSpellCustomAttr.resize(GetSpellStore()->GetNumRows(), 0); // initialize with 0 values - - uint32 count = 0; - - SpellEntry* spellInfo = NULL; - for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i) + SpellInfo* spellInfo = NULL; + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { - spellInfo = (SpellEntry*)sSpellStore.LookupEntry(i); + spellInfo = mSpellInfoMap[i]; if (!spellInfo) continue; for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) { - switch (spellInfo->Effect[j]) + switch (spellInfo->Effects[j].ApplyAuraName) + { + case SPELL_AURA_MOD_POSSESS: + case SPELL_AURA_MOD_CONFUSE: + case SPELL_AURA_MOD_CHARM: + case SPELL_AURA_AOE_CHARM: + case SPELL_AURA_MOD_FEAR: + case SPELL_AURA_MOD_STUN: + spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CC; + break; + } + switch (spellInfo->Effects[j].Effect) { case SPELL_EFFECT_SCHOOL_DAMAGE: case SPELL_EFFECT_WEAPON_DAMAGE: @@ -3737,27 +2617,17 @@ void SpellMgr::LoadSpellCustomAttr() case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: case SPELL_EFFECT_HEAL: - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_DIRECT_DAMAGE; - ++count; + spellInfo->AttributesCu |= SPELL_ATTR0_CU_DIRECT_DAMAGE; break; case SPELL_EFFECT_CHARGE: case SPELL_EFFECT_CHARGE_DEST: case SPELL_EFFECT_JUMP: case SPELL_EFFECT_JUMP_DEST: case SPELL_EFFECT_LEAP_BACK: - if (!spellInfo->speed && !spellInfo->SpellFamilyName) - spellInfo->speed = SPEED_CHARGE; - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_CHARGE; - ++count; + spellInfo->AttributesCu |= SPELL_ATTR0_CU_CHARGE; break; case SPELL_EFFECT_PICKPOCKET: - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_PICKPOCKET; - break; - case SPELL_EFFECT_TRIGGER_SPELL: - if (IsPositionTarget(spellInfo->EffectImplicitTargetA[j]) || - spellInfo->Targets & (TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION)) - spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE; - ++count; + spellInfo->AttributesCu |= SPELL_ATTR0_CU_PICKPOCKET; break; case SPELL_EFFECT_ENCHANT_ITEM: case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY: @@ -3767,88 +2637,44 @@ void SpellMgr::LoadSpellCustomAttr() // only enchanting profession enchantments procs can stack if (IsPartOfSkillLine(SKILL_ENCHANTING, i)) { - uint32 enchantId = spellInfo->EffectMiscValue[j]; + uint32 enchantId = spellInfo->Effects[j].MiscValue; SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId); for (uint8 s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s) { if (enchant->type[s] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL) continue; - SpellEntry const* procInfo = sSpellStore.LookupEntry(enchant->spellid[s]); + SpellInfo* procInfo = (SpellInfo*)sSpellMgr->GetSpellInfo(enchant->spellid[s]); if (!procInfo) continue; // if proced directly from enchantment, not via proc aura // NOTE: Enchant Weapon - Blade Ward also has proc aura spell and is proced directly // however its not expected to stack so this check is good - if (IsSpellHaveAura(procInfo, SPELL_AURA_PROC_TRIGGER_SPELL)) + if (procInfo->HasAura(SPELL_AURA_PROC_TRIGGER_SPELL)) continue; - mSpellCustomAttr[enchant->spellid[s]] |= SPELL_ATTR0_CU_ENCHANT_PROC; + procInfo->AttributesCu |= SPELL_ATTR0_CU_ENCHANT_PROC; } } break; } } - - switch (SpellTargetType[spellInfo->EffectImplicitTargetA[j]]) - { - case TARGET_TYPE_UNIT_TARGET: - case TARGET_TYPE_DEST_TARGET: - spellInfo->Targets |= TARGET_FLAG_UNIT; - ++count; - break; - default: - break; - } } - for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) - { - switch (spellInfo->EffectApplyAuraName[j]) - { - case SPELL_AURA_MOD_POSSESS: - case SPELL_AURA_MOD_CONFUSE: - case SPELL_AURA_MOD_CHARM: - case SPELL_AURA_AOE_CHARM: - case SPELL_AURA_MOD_FEAR: - case SPELL_AURA_MOD_STUN: - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_AURA_CC; - ++count; - break; - } - } + if (!spellInfo->_IsPositiveEffect(EFFECT_0, false)) + spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF0; - if (!_isPositiveEffect(i, 0, false)) - { - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_NEGATIVE_EFF0; - ++count; - } - if (!_isPositiveEffect(i, 1, false)) - { - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_NEGATIVE_EFF1; - ++count; - } - if (!_isPositiveEffect(i, 2, false)) - { - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_NEGATIVE_EFF2; - ++count; - } + if (!spellInfo->_IsPositiveEffect(EFFECT_1, false)) + spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF1; - if (spellInfo->SpellVisual[0] == 3879) - { - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_CONE_BACK; - ++count; - } + if (!spellInfo->_IsPositiveEffect(EFFECT_2, false)) + spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF2; - if (spellInfo->activeIconID == 2158) // flight - { - spellInfo->Attributes |= SPELL_ATTR0_PASSIVE; - ++count; - } + if (spellInfo->SpellVisual[0] == 3879) + spellInfo->AttributesCu |= SPELL_ATTR0_CU_CONE_BACK; - // TODO: this REALLY needs to be moved to db (so it can be blamed on db guys) - switch (i) + switch (spellInfo->Id) { case 1776: // Gouge case 1777: @@ -3865,8 +2691,7 @@ void SpellMgr::LoadSpellCustomAttr() case 38764: case 38863: case 52743: // Head Smack - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER; - ++count; + spellInfo->AttributesCu |= SPELL_ATTR0_CU_REQ_TARGET_FACING_CASTER; break; case 53: // Backstab case 2589: @@ -3913,46 +2738,203 @@ void SpellMgr::LoadSpellCustomAttr() case 23959: // Test Stab R50 case 24825: // Test Backstab case 58563: // Assassinate Restless Lookout - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET; - ++count; + spellInfo->AttributesCu |= SPELL_ATTR0_CU_REQ_CASTER_BEHIND_TARGET; + break; + case 26029: // Dark Glare + case 37433: // Spout + case 43140: // Flame Breath + case 43215: // Flame Breath + case 70461: // Coldflame Trap + spellInfo->AttributesCu |= SPELL_ATTR0_CU_CONE_LINE; + break; + case 24340: // Meteor + case 26558: // Meteor + case 28884: // Meteor + case 36837: // Meteor + case 38903: // Meteor + case 41276: // Meteor + case 57467: // Meteor + case 26789: // Shard of the Fallen Star + case 31436: // Malevolent Cleave + case 35181: // Dive Bomb + case 40810: // Saber Lash + case 43267: // Saber Lash + case 43268: // Saber Lash + case 42384: // Brutal Swipe + case 45150: // Meteor Slash + case 64688: // Sonic Screech + case 72373: // Shared Suffering + case 71904: // Chaos Bane + case 70492: // Ooze Eruption + case 72505: // Ooze Eruption + case 72624: // Ooze Eruption + case 72625: // Ooze Eruption + // ONLY SPELLS WITH SPELLFAMILY_GENERIC and EFFECT_SCHOOL_DAMAGE + spellInfo->AttributesCu |= SPELL_ATTR0_CU_SHARE_DAMAGE; + break; + case 27820: // Mana Detonation + case 69782: // Ooze Flood + case 69796: // Ooze Flood + case 69798: // Ooze Flood + case 69801: // Ooze Flood + case 69538: // Ooze Combine + case 69553: // Ooze Combine + case 69610: // Ooze Combine + case 71447: // Bloodbolt Splash + case 71481: // Bloodbolt Splash + case 71482: // Bloodbolt Splash + case 71483: // Bloodbolt Splash + case 71390: // Pact of the Darkfallen + spellInfo->AttributesCu |= SPELL_ATTR0_CU_EXCLUDE_SELF; + break; + case 18500: // Wing Buffet + case 33086: // Wild Bite + case 49749: // Piercing Blow + case 52890: // Penetrating Strike + case 53454: // Impale + case 59446: // Impale + case 62383: // Shatter + case 64777: // Machine Gun + case 65239: // Machine Gun + case 65919: // Impale + case 67858: // Impale + case 67859: // Impale + case 67860: // Impale + case 69293: // Wing Buffet + case 74439: // Machine Gun + spellInfo->AttributesCu |= SPELL_ATTR0_CU_IGNORE_ARMOR; + break; + case 63278: // Mark of the Faceless (General Vezax) + spellInfo->AttributesCu |= SPELL_ATTR0_CU_IGNORE_ARMOR; + break; + case 64422: // Sonic Screech (Auriaya) + spellInfo->AttributesCu |= SPELL_ATTR0_CU_SHARE_DAMAGE; + spellInfo->AttributesCu |= SPELL_ATTR0_CU_IGNORE_ARMOR; + break; + } + + switch (spellInfo->SpellFamilyName) + { + case SPELLFAMILY_WARRIOR: + // Shout + if (spellInfo->SpellFamilyFlags[0] & 0x20000 || spellInfo->SpellFamilyFlags[1] & 0x20) + spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CC; + break; + case SPELLFAMILY_DRUID: + // Roar + if (spellInfo->SpellFamilyFlags[0] & 0x8) + spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CC; + break; + default: + break; + } + } + + CreatureAI::FillAISpellInfo(); + + sLog->outString(">> Loaded spell custom attributes in %u ms", GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); +} + +void SpellMgr::LoadDbcDataCorrections() +{ + uint32 oldMSTime = getMSTime(); + + SpellEntry* spellInfo = NULL; + for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i) + { + spellInfo = (SpellEntry*)sSpellStore.LookupEntry(i); + if (!spellInfo) + continue; + + for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) + { + switch (spellInfo->Effect[j]) + { + case SPELL_EFFECT_CHARGE: + case SPELL_EFFECT_CHARGE_DEST: + case SPELL_EFFECT_JUMP: + case SPELL_EFFECT_JUMP_DEST: + case SPELL_EFFECT_LEAP_BACK: + if (!spellInfo->speed && !spellInfo->SpellFamilyName) + spellInfo->speed = SPEED_CHARGE; + break; + case SPELL_EFFECT_TRIGGER_SPELL: + if (SpellImplicitTargetInfo::IsPosition(spellInfo->EffectImplicitTargetA[j]) || + spellInfo->Targets & (TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION)) + spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE; + break; + } + + switch (SpellImplicitTargetInfo::Type[spellInfo->EffectImplicitTargetA[j]]) + { + case TARGET_TYPE_UNIT_TARGET: + case TARGET_TYPE_DEST_TARGET: + spellInfo->Targets |= TARGET_FLAG_UNIT; + break; + default: + break; + } + } + + if (spellInfo->activeIconID == 2158) // flight + { + spellInfo->Attributes |= SPELL_ATTR0_PASSIVE; + } + + switch (spellInfo->Id) + { + case 42835: // Spout + spellInfo->Effect[0] = 0; // remove damage effect, only anim is needed + break; + case 30657: // Quake + spellInfo->EffectTriggerSpell[0] = 30571; + break; + case 30541: // Blaze (needs conditions entry) + spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_TARGET_ENEMY; + spellInfo->EffectImplicitTargetB[0] = 0; + break; + case 31447: // Mark of Kaz'rogal (needs target selection script) + case 31298: // Sleep (needs target selection script) + spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER; + spellInfo->EffectImplicitTargetB[0] = 0; + break; + case 31344: // Howl of Azgalor + spellInfo->EffectRadiusIndex[0] = 12; // 100yards instead of 50000?! + break; + case 42818: // Headless Horseman - Wisp Flight Port + case 42821: // Headless Horseman - Wisp Flight Missile + spellInfo->rangeIndex = 6; // 100 yards break; case 36350: //They Must Burn Bomb Aura (self) spellInfo->EffectTriggerSpell[0] = 36325; // They Must Burn Bomb Drop (DND) - ++count; break; case 49838: // Stop Time spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_INITIAL_AGGRO; - ++count; break; case 61407: // Energize Cores case 62136: // Energize Cores case 54069: // Energize Cores case 56251: // Energize Cores spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_AREA_ENTRY_SRC; - ++count; break; case 50785: // Energize Cores case 59372: // Energize Cores spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_AREA_ENEMY_SRC; - ++count; break; case 3286: // Bind spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_TARGET_ENEMY; spellInfo->EffectImplicitTargetA[1] = TARGET_UNIT_TARGET_ENEMY; - ++count; break; case 8494: // Mana Shield (rank 2) // because of bug in dbc spellInfo->procChance = 0; - ++count; break; case 32182: // Heroism spellInfo->excludeCasterAuraSpell = 57723; // Exhaustion - ++count; break; case 2825: // Bloodlust spellInfo->excludeCasterAuraSpell = 57724; // Sated - ++count; break; case 20335: // Heart of the Crusader case 20336: @@ -3960,69 +2942,16 @@ void SpellMgr::LoadSpellCustomAttr() case 63320: // Glyph of Life Tap // Entries were not updated after spell effect change, we have to do that manually :/ spellInfo->AttributesEx3 |= SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED; - ++count; break; case 16007: // Draco-Incarcinatrix 900 // was 46, but effect is aura effect spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_NEARBY_ENTRY; spellInfo->EffectImplicitTargetB[0] = TARGET_DST_NEARBY_ENTRY; - ++count; - break; - case 26029: // Dark Glare - case 37433: // Spout - case 43140: // Flame Breath - case 43215: // Flame Breath - case 70461: // Coldflame Trap - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_CONE_LINE; - ++count; - break; - case 24340: // Meteor - case 26558: // Meteor - case 28884: // Meteor - case 36837: // Meteor - case 38903: // Meteor - case 41276: // Meteor - case 57467: // Meteor - case 26789: // Shard of the Fallen Star - case 31436: // Malevolent Cleave - case 35181: // Dive Bomb - case 40810: // Saber Lash - case 43267: // Saber Lash - case 43268: // Saber Lash - case 42384: // Brutal Swipe - case 45150: // Meteor Slash - case 64688: // Sonic Screech - case 72373: // Shared Suffering - case 71904: // Chaos Bane - case 70492: // Ooze Eruption - case 72505: // Ooze Eruption - case 72624: // Ooze Eruption - case 72625: // Ooze Eruption - // ONLY SPELLS WITH SPELLFAMILY_GENERIC and EFFECT_SCHOOL_DAMAGE - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_SHARE_DAMAGE; - ++count; break; case 59725: // Improved Spell Reflection - aoe aura // Target entry seems to be wrong for this spell :/ spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_PARTY_CASTER; spellInfo->EffectRadiusIndex[0] = 45; - ++count; - break; - case 27820: // Mana Detonation - case 69782: // Ooze Flood - case 69796: // Ooze Flood - case 69798: // Ooze Flood - case 69801: // Ooze Flood - case 69538: // Ooze Combine - case 69553: // Ooze Combine - case 69610: // Ooze Combine - case 71447: // Bloodbolt Splash - case 71481: // Bloodbolt Splash - case 71482: // Bloodbolt Splash - case 71483: // Bloodbolt Splash - case 71390: // Pact of the Darkfallen - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_EXCLUDE_SELF; - ++count; break; case 44978: case 45001: case 45002: // Wild Magic case 45004: case 45006: case 45010: // Wild Magic @@ -4042,14 +2971,12 @@ void SpellMgr::LoadSpellCustomAttr() case 62374: // Pursued case 61588: // Blazing Harpoon spellInfo->MaxAffectedTargets = 1; - ++count; break; case 52479: // Gift of the Harvester spellInfo->MaxAffectedTargets = 1; // a trap always has dst = src? spellInfo->EffectImplicitTargetA[0] = TARGET_DST_CASTER; spellInfo->EffectImplicitTargetA[1] = TARGET_DST_CASTER; - ++count; break; case 41376: // Spite case 39992: // Needle Spine @@ -4065,12 +2992,10 @@ void SpellMgr::LoadSpellCustomAttr() case 66588: // Flaming Spear case 54171: // Divine Storm spellInfo->MaxAffectedTargets = 3; - ++count; break; case 38310: // Multi-Shot case 53385: // Divine Storm (Damage) spellInfo->MaxAffectedTargets = 4; - ++count; break; case 42005: // Bloodboil case 38296: // Spitfire Totem @@ -4080,7 +3005,6 @@ void SpellMgr::LoadSpellCustomAttr() case 55665: // Life Drain - Sapphiron (H) case 28796: // Poison Bolt Volly - Faerlina spellInfo->MaxAffectedTargets = 5; - ++count; break; case 40827: // Sinful Beam case 40859: // Sinister Beam @@ -4089,16 +3013,13 @@ void SpellMgr::LoadSpellCustomAttr() case 54835: // Curse of the Plaguebringer - Noth (H) case 54098: // Poison Bolt Volly - Faerlina (H) spellInfo->MaxAffectedTargets = 10; - ++count; break; case 50312: // Unholy Frenzy spellInfo->MaxAffectedTargets = 15; - ++count; break; case 38794: case 33711: //Murmur's Touch spellInfo->MaxAffectedTargets = 1; spellInfo->EffectTriggerSpell[0] = 33760; - ++count; break; case 17941: // Shadow Trance case 22008: // Netherwind Focus @@ -4113,20 +3034,16 @@ void SpellMgr::LoadSpellCustomAttr() case 64823: // Item - Druid T8 Balance 4P Bonus case 44401: // Missile Barrage spellInfo->procCharges = 1; - ++count; break; case 44544: // Fingers of Frost spellInfo->EffectSpellClassMask[0] = flag96(685904631, 1151048, 0); - ++count; break; case 74396: // Fingers of Frost visual buff spellInfo->procCharges = 2; spellInfo->StackAmount = 0; - ++count; break; case 28200: // Ascendance (Talisman of Ascendance trinket) spellInfo->procCharges = 6; - ++count; break; case 47201: // Everlasting Affliction case 47202: @@ -4135,63 +3052,50 @@ void SpellMgr::LoadSpellCustomAttr() case 47205: // add corruption to affected spells spellInfo->EffectSpellClassMask[1][0] |= 2; - ++count; break; case 49305: // Teleport to Boss 1 DND case 64981: // Summon Random Vanquished Tentacle spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_CASTER; - ++count; break; case 51852: // The Eye of Acherus (no spawn in phase 2 in db) spellInfo->EffectMiscValue[0] |= 1; - ++count; break; case 51904: // Summon Ghouls On Scarlet Crusade (core does not know the triggered spell is summon spell) spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER; - ++count; break; case 29809: // Desecration Arm - 36 instead of 37 - typo? :/ spellInfo->EffectRadiusIndex[0] = 37; - ++count; break; // Master Shapeshifter: missing stance data for forms other than bear - bear version has correct data // To prevent aura staying on target after talent unlearned case 48420: spellInfo->Stances = 1 << (FORM_CAT - 1); - ++count; break; case 48421: spellInfo->Stances = 1 << (FORM_MOONKIN - 1); - ++count; break; case 48422: spellInfo->Stances = 1 << (FORM_TREE - 1); - ++count; break; case 47569: // Improved Shadowform (Rank 1) // with this spell atrribute aura can be stacked several times spellInfo->Attributes &= ~SPELL_ATTR0_NOT_SHAPESHIFT; - ++count; break; case 30421: // Nether Portal - Perseverence spellInfo->EffectBasePoints[2] += 30000; - ++count; break; case 16834: // Natural shapeshifter case 16835: spellInfo->DurationIndex = 21; - ++count; break; case 51735: // Ebon Plague case 51734: case 51726: spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS; spellInfo->SpellFamilyFlags[2] = 0x10; - ++count; break; case 41013: // Parasitic Shadowfiend Passive spellInfo->EffectApplyAuraName[0] = 4; // proc debuff, and summon infinite fiends - ++count; break; case 27892: // To Anchor 1 case 27928: // To Anchor 1 @@ -4200,11 +3104,9 @@ void SpellMgr::LoadSpellCustomAttr() case 27931: // Anchor to Skulls case 27937: // Anchor to Skulls spellInfo->rangeIndex = 13; - ++count; break; case 48743: // Death Pact spellInfo->AttributesEx &= ~SPELL_ATTR1_CANT_TARGET_SELF; - ++count; break; // target allys instead of enemies, target A is src_caster, spells with effect like that have ally target // this is the only known exception, probably just wrong data @@ -4212,39 +3114,17 @@ void SpellMgr::LoadSpellCustomAttr() case 54836: // Wrath of the Plaguebringer spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_AREA_ALLY_SRC; spellInfo->EffectImplicitTargetB[1] = TARGET_UNIT_AREA_ALLY_SRC; - ++count; break; case 31687: // Summon Water Elemental // 322-330 switch - effect changed to dummy, target entry not changed in client:( spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER; - ++count; - break; - case 18500: // Wing Buffet - case 33086: // Wild Bite - case 49749: // Piercing Blow - case 52890: // Penetrating Strike - case 53454: // Impale - case 59446: // Impale - case 62383: // Shatter - case 64777: // Machine Gun - case 65239: // Machine Gun - case 65919: // Impale - case 67858: // Impale - case 67859: // Impale - case 67860: // Impale - case 69293: // Wing Buffet - case 74439: // Machine Gun - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_IGNORE_ARMOR; - ++count; break; case 63675: // Improved Devouring Plague spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS; - ++count; break; case 8145: // Tremor Totem (instant pulse) case 6474: // Earthbind Totem (instant pulse) spellInfo->AttributesEx5 |= SPELL_ATTR5_START_PERIODIC_AT_APPLY; - ++count; break; case 53241: // Marked for Death (Rank 1) case 53243: // Marked for Death (Rank 2) @@ -4252,25 +3132,21 @@ void SpellMgr::LoadSpellCustomAttr() case 53245: // Marked for Death (Rank 4) case 53246: // Marked for Death (Rank 5) spellInfo->EffectSpellClassMask[0] = flag96(423937, 276955137, 2049); - ++count; break; case 70728: // Exploit Weakness case 70840: // Devious Minds spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER; spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_PET; - ++count; break; case 70893: // Culling The Herd spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_CASTER; spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_MASTER; - ++count; break; case 54800: // Sigil of the Frozen Conscience - change class mask to custom extended flags of Icy Touch // this is done because another spell also uses the same SpellFamilyFlags as Icy Touch // SpellFamilyFlags[0] & 0x00000040 in SPELLFAMILY_DEATHKNIGHT is currently unused (3.3.5a) // this needs research on modifier applying rules, does not seem to be in Attributes fields spellInfo->EffectSpellClassMask[0] = flag96(0x00000040, 0x00000000, 0x00000000); - ++count; break; case 19970: // Entangling Roots (Rank 6) -- Nature's Grasp Proc case 19971: // Entangling Roots (Rank 5) -- Nature's Grasp Proc @@ -4281,30 +3157,25 @@ void SpellMgr::LoadSpellCustomAttr() case 27010: // Entangling Roots (Rank 7) -- Nature's Grasp Proc case 53313: // Entangling Roots (Rank 8) -- Nature's Grasp Proc spellInfo->CastingTimeIndex = 1; - ++count; break; case 61719: // Easter Lay Noblegarden Egg Aura - Interrupt flags copied from aura which this aura is linked with spellInfo->AuraInterruptFlags = AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_TAKE_DAMAGE; - ++count; break; // ULDUAR SPELLS // case 63342: // Focused Eyebeam Summon Trigger (Kologarn) spellInfo->MaxAffectedTargets = 1; - ++count; break; case 62716: // Growth of Nature (Freya) case 65584: // Growth of Nature (Freya) case 64381: // Strength of the Pack (Auriaya) spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS; - ++count; break; case 63018: // Searing Light (XT-002) case 65121: // Searing Light (25m) (XT-002) case 63024: // Gravity Bomb (XT-002) case 64234: // Gravity Bomb (25m) (XT-002) spellInfo->MaxAffectedTargets = 1; - ++count; break; case 62834: // Boom (XT-002) // This hack is here because we suspect our implementation of spell effect execution on targets @@ -4313,29 +3184,17 @@ void SpellMgr::LoadSpellCustomAttr() // The above situation causes the visual for this spell to be bugged, so we remove the instakill // effect and implement a script hack for that. spellInfo->Effect[EFFECT_1] = 0; - ++count; break; case 64386: // Terrifying Screech (Auriaya) case 64389: // Sentinel Blast (Auriaya) case 64678: // Sentinel Blast (Auriaya) spellInfo->DurationIndex = 28; // 5 seconds, wrong DBC data? - ++count; - break; - case 63278: // Mark of the Faceless (General Vezax) - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_IGNORE_ARMOR; - ++count; - break; - case 64422: // Sonic Screech (Auriaya) - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_SHARE_DAMAGE; - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_IGNORE_ARMOR; - ++count; break; case 64321: // Potent Pheromones (Freya) // spell should dispel area aura, but doesn't have the attribute // may be db data bug, or blizz may keep reapplying area auras every update with checking immunity // that will be clear if we get more spells with problem like this spellInfo->AttributesEx |= SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY; - ++count; break; // ENDOF ULDUAR SPELLS // @@ -4346,7 +3205,6 @@ void SpellMgr::LoadSpellCustomAttr() // increase duration from 15 to 18 seconds because caster is already // unsummoned when spell missile hits the ground so nothing happen in result spellInfo->DurationIndex = 85; - ++count; break; // ENDOF TRIAL OF THE CRUSADER SPELLS // @@ -4363,12 +3221,10 @@ void SpellMgr::LoadSpellCustomAttr() case 70860: // Frozen Throne Teleport case 70861: // Sindragosa's Lair Teleport spellInfo->EffectImplicitTargetA[0] = TARGET_DST_DB; - ++count; break; case 69055: // Saber Lash (Lord Marrowgar) case 70814: // Saber Lash (Lord Marrowgar) spellInfo->EffectRadiusIndex[0] = 8; // 5yd - ++count; break; case 69075: // Bone Storm (Lord Marrowgar) case 70834: // Bone Storm (Lord Marrowgar) @@ -4381,25 +3237,20 @@ void SpellMgr::LoadSpellCustomAttr() case 71161: // Plague Stench (Stinky) case 71123: // Decimate (Stinky & Precious) spellInfo->EffectRadiusIndex[0] = 12; // 100yd - ++count; break; case 72723: // Resistant Skin (Deathbringer Saurfang adds) // this spell initially granted Shadow damage immunity, however it was removed but the data was left in client spellInfo->Effect[2] = 0; - ++count; break; case 70460: // Coldflame Jets (Traps after Saurfang) spellInfo->DurationIndex = 1; // 10 seconds - ++count; break; case 71413: // Green Ooze Summon (Professor Putricide) case 71414: // Orange Ooze Summon (Professor Putricide) spellInfo->EffectImplicitTargetA[0] = TARGET_DEST_DEST; - ++count; break; case 71159: // Awaken Plagued Zombies spellInfo->DurationIndex = 21; - ++count; break; // THIS IS HERE BECAUSE COOLDOWN ON CREATURE PROCS IS NOT IMPLEMENTED case 71604: // Mutated Strength (Professor Putricide) @@ -4407,65 +3258,53 @@ void SpellMgr::LoadSpellCustomAttr() case 72674: // Mutated Strength (Professor Putricide) case 72675: // Mutated Strength (Professor Putricide) spellInfo->Effect[1] = 0; - ++count; break; case 70911: // Unbound Plague (Professor Putricide) case 72854: // Unbound Plague (Professor Putricide) case 72855: // Unbound Plague (Professor Putricide) case 72856: // Unbound Plague (Professor Putricide) spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_TARGET_ENEMY; - ++count; break; case 71518: // Unholy Infusion Quest Credit (Professor Putricide) case 72934: // Blood Infusion Quest Credit (Blood-Queen Lana'thel) case 72289: // Frost Infusion Quest Credit (Sindragosa) spellInfo->EffectRadiusIndex[0] = 28; // another missing radius - ++count; break; case 71708: // Empowered Flare (Blood Prince Council) case 72785: // Empowered Flare (Blood Prince Council) case 72786: // Empowered Flare (Blood Prince Council) case 72787: // Empowered Flare (Blood Prince Council) spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS; - ++count; break; case 71266: // Swarming Shadows case 72890: // Swarming Shadows spellInfo->AreaGroupId = 0; // originally, these require area 4522, which is... outside of Icecrown Citadel - ++count; break; case 70602: // Corruption spellInfo->AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS; - ++count; break; case 70715: // Column of Frost (visual marker) spellInfo->DurationIndex = 32; // 6 seconds (missing) - ++count; break; case 71085: // Mana Void (periodic aura) spellInfo->DurationIndex = 9; // 30 seconds (missing) - ++count; break; case 70936: // Summon Suppressor spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_TARGET_ANY; spellInfo->EffectImplicitTargetB[0] = 0; - ++count; break; case 72706: // Achievement Check (Valithria Dreamwalker) case 71357: // Order Whelp spellInfo->EffectRadiusIndex[0] = 22; // 200yd - ++count; break; case 70598: // Sindragosa's Fury spellInfo->EffectImplicitTargetA[0] = TARGET_DST_CASTER; - ++count; break; case 69846: // Frost Bomb spellInfo->speed = 10; spellInfo->EffectImplicitTargetA[0] = TARGET_DEST_TARGET_ANY; spellInfo->EffectImplicitTargetB[0] = TARGET_UNIT_TARGET_ANY; spellInfo->Effect[1] = 0; - ++count; break; default: break; @@ -4473,24 +3312,12 @@ void SpellMgr::LoadSpellCustomAttr() switch (spellInfo->SpellFamilyName) { - case SPELLFAMILY_WARRIOR: - // Shout - if (spellInfo->SpellFamilyFlags[0] & 0x20000 || spellInfo->SpellFamilyFlags[1] & 0x20) - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_AURA_CC; - else - break; - ++count; - break; case SPELLFAMILY_DRUID: // Starfall Target Selection if (spellInfo->SpellFamilyFlags[2] & 0x100) spellInfo->MaxAffectedTargets = 2; - // Roar - else if (spellInfo->SpellFamilyFlags[0] & 0x8) - mSpellCustomAttr[i] |= SPELL_ATTR0_CU_AURA_CC; else break; - ++count; break; case SPELLFAMILY_PALADIN: // Seals of the Pure should affect Seal of Righteousness @@ -4498,13 +3325,11 @@ void SpellMgr::LoadSpellCustomAttr() spellInfo->EffectSpellClassMask[0][1] |= 0x20000000; else break; - ++count; break; case SPELLFAMILY_DEATHKNIGHT: // Icy Touch - extend FamilyFlags (unused value) for Sigil of the Frozen Conscience to use if (spellInfo->SpellIconID == 2721 && spellInfo->SpellFamilyFlags[0] & 0x2) spellInfo->SpellFamilyFlags[0] |= 0x40; - ++count; break; } } @@ -4514,118 +3339,6 @@ void SpellMgr::LoadSpellCustomAttr() properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(647)); // 52893 properties->Type = SUMMON_TYPE_TOTEM; - CreatureAI::FillAISpellInfo(); - - sLog->outString(">> Loaded %u custom spell attributes in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(">> Loading spell dbc data corrections in %u ms", GetMSTimeDiffToNow(oldMSTime)); sLog->outString(); -} - -// Fill custom data about enchancments -void SpellMgr::LoadEnchantCustomAttr() -{ - uint32 oldMSTime = getMSTime(); - - uint32 size = sSpellItemEnchantmentStore.GetNumRows(); - mEnchantCustomAttr.resize(size); - - uint32 count = 0; - - 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_ATTR2_UNK13) || !(spellInfo->Attributes & SPELL_ATTR0_NOT_SHAPESHIFT)) - continue; - - for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) - { - if (spellInfo->Effect[j] == SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) - { - uint32 enchId = spellInfo->EffectMiscValue[j]; - SpellItemEnchantmentEntry const* ench = sSpellItemEnchantmentStore.LookupEntry(enchId); - if (!ench) - continue; - mEnchantCustomAttr[enchId] = true; - ++count; - break; - } - } - } - - sLog->outString(">> Loaded %u custom enchant attributes in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - sLog->outString(); -} - -void SpellMgr::LoadSpellLinked() -{ - uint32 oldMSTime = getMSTime(); - - mSpellLinkedMap.clear(); // need for reload case - - // 0 1 2 - QueryResult result = WorldDatabase.Query("SELECT spell_trigger, spell_effect, type FROM spell_linked_spell"); - if (!result) - { - sLog->outString(">> Loaded 0 linked spells. DB table `spell_linked_spell` is empty."); - sLog->outString(); - return; - } - - uint32 count = 0; - - do - { - Field *fields = result->Fetch(); - - int32 trigger = fields[0].GetInt32(); - int32 effect = fields[1].GetInt32(); - int32 type = fields[2].GetInt32(); - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(trigger)); - if (!spellInfo) - { - sLog->outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(trigger)); - continue; - } - spellInfo = sSpellStore.LookupEntry(abs(effect)); - if (!spellInfo) - { - sLog->outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(effect)); - continue; - } - - if (trigger > 0) - { - switch (type) - { - case 0: mSpellCustomAttr[trigger] |= SPELL_ATTR0_CU_LINK_CAST; break; - case 1: mSpellCustomAttr[trigger] |= SPELL_ATTR0_CU_LINK_HIT; break; - case 2: mSpellCustomAttr[trigger] |= SPELL_ATTR0_CU_LINK_AURA; break; - } - } - else - { - mSpellCustomAttr[-trigger] |= SPELL_ATTR0_CU_LINK_REMOVE; - } - - if (type) //we will find a better way when more types are needed - { - if (trigger > 0) - trigger += SPELL_LINKED_MAX_SPELLS * type; - else - trigger -= SPELL_LINKED_MAX_SPELLS * type; - } - mSpellLinkedMap[trigger].push_back(effect); - - ++count; - } while (result->NextRow()); - - sLog->outString(">> Loaded %u linked spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - sLog->outString(); -} +}
\ No newline at end of file |