From 834580f6b2c317cf5fdeb59cda57f5bfa7705d02 Mon Sep 17 00:00:00 2001 From: megamage Date: Fri, 15 May 2009 16:26:00 -0500 Subject: *Move some Trinity-only functions to the end of file. --HG-- branch : trunk --- src/game/SpellMgr.cpp | 2901 +++++++++++++++++++++++++------------------------ 1 file changed, 1451 insertions(+), 1450 deletions(-) diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 65952504ebc..d1349dc760e 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -785,18 +785,6 @@ bool IsPositiveSpell(uint32 spellId, bool deep) return true; } -bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool def) -{ - if (!dispelSpell) return false; - SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); - if (!spellproto) return false; - - if(dispelSpell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) - return true; - - return def; -} - bool IsSingleTargetSpell(SpellEntry const *spellInfo) { // all other single target spells have if it has AttributesEx5 @@ -1386,58 +1374,6 @@ void SpellMgr::LoadSpellThreats() sLog.outString(); } -void SpellMgr::LoadSpellEnchantProcData() -{ - mSpellEnchantProcEventMap.clear(); // need for reload case - - uint32 count = 0; - - // 0 1 2 3 - QueryResult *result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data"); - if( !result ) - { - - barGoLink bar( 1 ); - - bar.step(); - - sLog.outString(); - sLog.outString( ">> Loaded %u spell enchant proc event conditions", count ); - return; - } - - barGoLink bar( result->GetRowCount() ); - do - { - Field *fields = result->Fetch(); - - bar.step(); - - uint32 enchantId = fields[0].GetUInt32(); - - SpellItemEnchantmentEntry const *ench = sSpellItemEnchantmentStore.LookupEntry(enchantId); - if (!ench) - { - sLog.outErrorDb("Enchancment %u listed in `spell_enchant_proc_data` does not exist", enchantId); - continue; - } - - SpellEnchantProcEntry spe; - - spe.customChance = fields[1].GetUInt32(); - spe.PPMChance = fields[2].GetFloat(); - spe.procEx = fields[3].GetUInt32(); - - mSpellEnchantProcEventMap[enchantId] = spe; - - ++count; - } while( result->NextRow() ); - - delete result; - - sLog.outString( ">> Loaded %u enchant proc data definitions", count); -} - bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const { SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2); @@ -1485,91 +1421,6 @@ bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo) return true; } -bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool sameCaster) const -{ - SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1); - SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2); - - if(!spellInfo_1 || !spellInfo_2) - return false; - - SpellSpecific spellId_spec_1 = GetSpellSpecific(spellId_1); - SpellSpecific spellId_spec_2 = GetSpellSpecific(spellId_2); - if (spellId_spec_1 && spellId_spec_2) - if (IsSingleFromSpellSpecificPerTarget(spellId_spec_1, spellId_spec_2) - ||(IsSingleFromSpellSpecificPerCaster(spellId_spec_1, spellId_spec_2) && sameCaster)) - return true; - - if(spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName) - return false; - - if(!sameCaster) - { - for(uint32 i = 0; i < 3; ++i) - if (spellInfo_1->Effect[i] == SPELL_EFFECT_APPLY_AURA - || spellInfo_1->Effect[i] == SPELL_EFFECT_PERSISTENT_AREA_AURA) - // not area auras (shaman totem) - switch(spellInfo_1->EffectApplyAuraName[i]) - { - // DOT or HOT from different casters will stack - case SPELL_AURA_PERIODIC_DAMAGE: - case SPELL_AURA_PERIODIC_HEAL: - case SPELL_AURA_PERIODIC_TRIGGER_SPELL: - case SPELL_AURA_PERIODIC_ENERGIZE: - case SPELL_AURA_PERIODIC_MANA_LEECH: - case SPELL_AURA_PERIODIC_LEECH: - case SPELL_AURA_POWER_BURN_MANA: - case SPELL_AURA_OBS_MOD_ENERGY: - case SPELL_AURA_OBS_MOD_HEALTH: - return false; - default: - break; - } - } - - // hack for Incanter's Absorption - if (spellInfo_1->Id==44413 && spellInfo_2->Id==44413) - return false; - - spellId_2 = GetLastSpellInChain(spellId_2); - spellId_1 = GetLastSpellInChain(spellId_1); - if (spellId_1 == spellId_2) - return true; - - // generic spells - if(!spellInfo_1->SpellFamilyName) - { - if(!spellInfo_1->SpellIconID - || spellInfo_1->SpellIconID == 1 - || spellInfo_1->SpellIconID != spellInfo_2->SpellIconID) - return false; - } - // check for class spells - else - { - if (spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags) - return false; - if (!spellInfo_1->SpellFamilyFlags) - return false; - } - - //use data of highest rank spell(needed for spells which ranks have different effects) - spellInfo_1=sSpellStore.LookupEntry(spellId_1); - spellInfo_2=sSpellStore.LookupEntry(spellId_2); - - //if spells have exactly the same effect they cannot stack - for(uint32 i = 0; i < 3; ++i) - if(spellInfo_1->Effect[i] != spellInfo_2->Effect[i] - // Overkill and master of subtlety need this - || spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_DUMMY - || spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_PERIODIC_DUMMY - - || spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i] - || spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i]) // paladin resist aura - return false; // need itemtype check? need an example to add that check - - return true; -} bool SpellMgr::IsProfessionOrRidingSpell(uint32 spellId) { @@ -1636,18 +1487,6 @@ bool SpellMgr::IsSkillBonusSpell(uint32 spellId) const return false; } -bool SpellMgr::IsSkillTypeSpell(uint32 spellId, SkillType type) const -{ - SkillLineAbilityMap::const_iterator lower = GetBeginSkillLineAbilityMap(spellId); - SkillLineAbilityMap::const_iterator upper = GetEndSkillLineAbilityMap(spellId); - for (;lower!=upper;++lower) - { - if (lower->second->skillId==type) - return true; - } - return false; -} - SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const { // ignore passive spells @@ -1689,24 +1528,65 @@ SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spell return NULL; } -void SpellMgr::LoadSpellRequired() + +void SpellMgr::LoadSpellLearnSkills() { - mSpellsReqSpell.clear(); // need for reload case - mSpellReq.clear(); // need for reload case + mSpellLearnSkills.clear(); // need for reload case - QueryResult *result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required"); + // search auto-learned skills and add its to map also for use in unlearn spells/talents + uint32 dbc_count = 0; + barGoLink bar( sSpellStore.GetNumRows() ); + for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell) + { + bar.step(); + SpellEntry const* entry = sSpellStore.LookupEntry(spell); - if(result == NULL) + if(!entry) + continue; + + for(int i = 0; i < 3; ++i) + { + if(entry->Effect[i]==SPELL_EFFECT_SKILL) + { + SpellLearnSkillNode dbc_node; + dbc_node.skill = entry->EffectMiscValue[i]; + if ( dbc_node.skill != SKILL_RIDING ) + dbc_node.value = 1; + else + dbc_node.value = entry->CalculateSimpleValue(i)*75; + dbc_node.maxvalue = entry->CalculateSimpleValue(i)*75; + + SpellLearnSkillNode const* db_node = GetSpellLearnSkill(spell); + + mSpellLearnSkills[spell] = dbc_node; + ++dbc_count; + break; + } + } + } + + sLog.outString(); + sLog.outString( ">> Loaded %u Spell Learn Skills from DBC", dbc_count ); +} + +void SpellMgr::LoadSpellLearnSpells() +{ + mSpellLearnSpells.clear(); // need for reload case + + // 0 1 2 + QueryResult *result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell"); + if(!result) { barGoLink bar( 1 ); bar.step(); sLog.outString(); - sLog.outString( ">> Loaded 0 spell required records" ); - sLog.outErrorDb("`spell_required` table is empty!"); + sLog.outString( ">> Loaded 0 spell learn spells" ); + sLog.outErrorDb("`spell_learn_spell` table is empty!"); return; } - uint32 rows = 0; + + uint32 count = 0; barGoLink bar( result->GetRowCount() ); do @@ -1714,665 +1594,616 @@ void SpellMgr::LoadSpellRequired() bar.step(); Field *fields = result->Fetch(); - uint32 spell_id = fields[0].GetUInt32(); - uint32 spell_req = fields[1].GetUInt32(); + uint32 spell_id = fields[0].GetUInt32(); - mSpellsReqSpell.insert (std::pair(spell_req, spell_id)); - mSpellReq[spell_id] = spell_req; - ++rows; + SpellLearnSpellNode node; + node.spell = fields[1].GetUInt32(); + node.active = fields[2].GetBool(); + node.autoLearned= false; + + if(!sSpellStore.LookupEntry(spell_id)) + { + sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",spell_id); + continue; + } + + if(!sSpellStore.LookupEntry(node.spell)) + { + sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",node.spell); + continue; + } + + mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell_id,node)); + + ++count; } while( result->NextRow() ); + delete result; - sLog.outString(); - sLog.outString( ">> Loaded %u spell required records", rows ); -} + // 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); -struct SpellRankEntry -{ - uint32 SkillId; - char const *SpellName; - uint32 DurationIndex; - uint32 RangeIndex; - uint32 SpellVisual; - uint32 ProcFlags; - flag96 SpellFamilyFlags; - uint32 TargetAuraState; - uint32 ManaCost; - uint32 CastingTimeIndex; - flag96 Effect; - flag96 Aura; - uint16 TalentID; + if(!entry) + continue; - bool operator < (const SpellRankEntry & _Right) const - { - return (SkillId != _Right.SkillId ? SkillId < _Right.SkillId - : SpellName!=_Right.SpellName ? SpellName < _Right.SpellName - : ProcFlags!=_Right.ProcFlags ? ProcFlags < _Right.ProcFlags + for(int i = 0; i < 3; ++i) + { + if(entry->Effect[i]==SPELL_EFFECT_LEARN_SPELL) + { + SpellLearnSpellNode dbc_node; + dbc_node.spell = entry->EffectTriggerSpell[i]; + dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself) - : Effect!=_Right.Effect ? Effect < _Right.Effect - : Aura!=_Right.Aura ? Aura < _Right.Aura - : TalentID!=_Right.TalentID ? TalentID < _Right.TalentID - : (CastingTimeIndex!=_Right.CastingTimeIndex) && (!CastingTimeIndex || !_Right.CastingTimeIndex || CastingTimeIndex==1 || !_Right.CastingTimeIndex==1) ? CastingTimeIndex < _Right.CastingTimeIndex + // ignore learning not existed spells (broken/outdated/or generic learnig spell 483 + if(!sSpellStore.LookupEntry(dbc_node.spell)) + continue; - : SpellFamilyFlags!=_Right.SpellFamilyFlags ? SpellFamilyFlags < _Right.SpellFamilyFlags - : (SpellVisual!=_Right.SpellVisual) && (!SpellVisual || !_Right.SpellVisual) ? SpellVisual < _Right.SpellVisual - : (ManaCost!=_Right.ManaCost) && (!ManaCost || !_Right.ManaCost) ? ManaCost < _Right.ManaCost - : (DurationIndex!=_Right.DurationIndex) && (!DurationIndex || !_Right.DurationIndex)? DurationIndex < _Right.DurationIndex - : (RangeIndex!=_Right.RangeIndex) && (!RangeIndex || !_Right.RangeIndex || RangeIndex==1 || !_Right.RangeIndex==1) ? RangeIndex < _Right.RangeIndex - : TargetAuraState < _Right.TargetAuraState - ); + // talent or passive spells or skill-step spells auto-casted and not need dependent learning, + // pet teaching spells don't must be dependent learning (casted) + // other required explicit dependent learning + dbc_node.autoLearned = entry->EffectImplicitTargetA[i] == TARGET_UNIT_PET || GetTalentSpellCost(spell) > 0 || IsPassiveSpell(spell) || IsSpellHaveEffect(entry,SPELL_EFFECT_SKILL_STEP); + + SpellLearnSpellMap::const_iterator db_node_begin = GetBeginSpellLearnSpell(spell); + SpellLearnSpellMap::const_iterator db_node_end = GetEndSpellLearnSpell(spell); + + bool found = false; + for(SpellLearnSpellMap::const_iterator itr = db_node_begin; itr != db_node_end; ++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; + } + } + } } -}; -struct SpellRankValue -{ - uint32 Id; - char const *Rank; - bool strict; -}; + sLog.outString(); + sLog.outString( ">> Loaded %u spell learn spells + %u found in DBC", count, dbc_count ); +} -void SpellMgr::LoadSpellChains() +void SpellMgr::LoadSpellScriptTarget() { - mSpellChains.clear(); // need for reload case + mSpellScriptTarget.clear(); // need for reload case - std::vector ChainedSpells; - for (uint32 ability_id=0;ability_idforward_spellid) - continue; - ChainedSpells.push_back(AbilityInfo->forward_spellid); + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 SpellScriptTarget. DB table `spell_script_target` is empty."); + return; } - std::multimap RankMap; + barGoLink bar(result->GetRowCount()); - for (uint32 ability_id=0;ability_idFetch(); + bar.step(); + + uint32 spellId = fields[0].GetUInt32(); + uint32 type = fields[1].GetUInt32(); + uint32 targetEntry = fields[2].GetUInt32(); + + SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId); + + if(!spellProto) + { + sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not exist.",spellId,targetEntry); continue; + } - //get only spell with lowest ability_id to prevent doubles - uint32 spell_id=AbilityInfo->spellId; - bool found=false; - for (uint32 i=0; iEffectImplicitTargetA[i]==TARGET_UNIT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetB[i]==TARGET_UNIT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetA[i]==TARGET_DST_NEARBY_ENTRY || + spellProto->EffectImplicitTargetB[i]==TARGET_DST_NEARBY_ENTRY ) + { + targetfound = true; + break; + } } - if (found) + if(!targetfound) + { + sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (46).",spellId,targetEntry); continue; + }*/ - if(mSkillLineAbilityMap.lower_bound(spell_id)->second->id!=ability_id) - continue; - SpellEntry const *SpellInfo=sSpellStore.LookupEntry(spell_id); - if (!SpellInfo) - continue; - std::string sRank = SpellInfo->Rank[sWorld.GetDefaultDbcLocale()]; - if(sRank.empty()) - continue; - //exception to polymorph spells-make pig and turtle other chain than sheep - if ((SpellInfo->SpellFamilyName==SPELLFAMILY_MAGE) && (SpellInfo->SpellFamilyFlags[0] & 0x1000000) && (SpellInfo->SpellIconID!=82)) + if( type >= MAX_SPELL_TARGET_TYPE ) + { + sLog.outErrorDb("Table `spell_script_target`: target type %u for TargetEntry %u is incorrect.",type,targetEntry); continue; + } - SpellRankEntry entry; - SpellRankValue value; - entry.SkillId=AbilityInfo->skillId; - entry.SpellName=SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()]; - entry.DurationIndex=SpellInfo->DurationIndex; - entry.RangeIndex=SpellInfo->rangeIndex; - entry.ProcFlags=SpellInfo->procFlags; - entry.SpellFamilyFlags=SpellInfo->SpellFamilyFlags; - entry.TargetAuraState=SpellInfo->TargetAuraState; - entry.SpellVisual=SpellInfo->SpellVisual[0]; - entry.ManaCost=SpellInfo->manaCost; - entry.CastingTimeIndex=0; - entry.TalentID=0; - for (;;) + switch(type) { - AbilityInfo=mSkillLineAbilityMap.lower_bound(spell_id)->second; - value.Id=spell_id; - value.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()]; - value.strict=false; - RankMap.insert(std::pair(entry,value)); - spell_id=AbilityInfo->forward_spellid; - SpellInfo=sSpellStore.LookupEntry(spell_id); - if (!SpellInfo) + case SPELL_TARGET_TYPE_GAMEOBJECT: + { + if( targetEntry==0 ) + break; + + if(!sGOStorage.LookupEntry(targetEntry)) + { + sLog.outErrorDb("Table `spell_script_target`: gameobject template entry %u does not exist.",targetEntry); + continue; + } + break; + } + default: + { + //players + /*if( targetEntry==0 ) + { + sLog.outErrorDb("Table `spell_script_target`: target entry == 0 for not GO target type (%u).",type); + continue; + }*/ + if(targetEntry && !sCreatureStorage.LookupEntry(targetEntry)) + { + sLog.outErrorDb("Table `spell_script_target`: creature template entry %u does not exist.",targetEntry); + continue; + } + const CreatureInfo* cInfo = sCreatureStorage.LookupEntry(targetEntry); + + if(spellId == 30427 && !cInfo->SkinLootId) + { + sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no skinlootid. Gas extraction will not work!", cInfo->Entry); + continue; + } break; + } } - } - barGoLink bar(RankMap.size()); + mSpellScriptTarget.insert(SpellScriptTarget::value_type(spellId,SpellTargetEntry(SpellScriptTargetType(type),targetEntry))); - uint32 count=0; + ++count; + } while (result->NextRow()); - for (std::multimap::iterator itr = RankMap.begin();itr!=RankMap.end();) + delete result; + + // Check all spells + /* Disabled (lot errors at this moment) + for(uint32 i = 1; i < sSpellStore.nCount; ++i) { - SpellRankEntry entry=itr->first; - //trac errors in extracted data - std::multimap::iterator> RankErrorMap; - for (std::multimap::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++) - { - bar.step(); - RankErrorMap.insert(std::pair::iterator>(itr2->second.Rank,itr2)); - } + SpellEntry const * spellInfo = sSpellStore.LookupEntry(i); + if(!spellInfo) + continue; - bool error=false; - //if strict == true strict check is not needed - if (!itr->second.strict) - //check for rank duplicates, if there are any do strict check - for (std::multimap::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();) + bool found = false; + for(int j=0; j<3; ++j) + { + if( spellInfo->EffectImplicitTargetA[j] == TARGET_UNIT_NEARBY_ENTRY || spellInfo->EffectImplicitTargetA[j] != TARGET_UNIT_CASTER && spellInfo->EffectImplicitTargetB[j] == TARGET_UNIT_NEARBY_ENTRY ) { - char const * err_entry=itr2->first; - uint32 rank_count=RankErrorMap.count(itr2->first); - if (rank_count>1) + SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(spellInfo->Id); + SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(spellInfo->Id); + if(lower==upper) { - error=true; - break; + sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = %u (TARGET_UNIT_NEARBY_ENTRY), but does not have record in `spell_script_target`",spellInfo->Id,TARGET_UNIT_NEARBY_ENTRY); + break; // effects of spell } - else - itr2++; } - bool allHaveTalents=true; - if (error) - { - std::list ConflictedSpells; - for (std::multimap::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2=RankMap.lower_bound(entry)) - { - ConflictedSpells.push_back(itr2->second.Id); - if (!GetTalentSpellPos(itr2->second.Id)) - allHaveTalents=false; - RankMap.erase(itr2); - } - SpellRankEntry nextEntry, currEntry; - for (;!ConflictedSpells.empty();ConflictedSpells.pop_front()) - { - SpellEntry const *SpellInfo=sSpellStore.LookupEntry(ConflictedSpells.front()); - currEntry.SkillId=entry.SkillId; - currEntry.SpellName=SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()]; - currEntry.DurationIndex=SpellInfo->DurationIndex; - currEntry.RangeIndex=SpellInfo->rangeIndex; - currEntry.ProcFlags=SpellInfo->procFlags; - currEntry.SpellFamilyFlags=SpellInfo->SpellFamilyFlags; - //compare talents only when all spells from chain have entry - //to prevent wrong results with spells which have first rank talented and other not - if (allHaveTalents) - currEntry.TalentID=GetTalentSpellPos(ConflictedSpells.front())->talent_id; - else - currEntry.TalentID=0; - currEntry.TargetAuraState=SpellInfo->TargetAuraState; - currEntry.SpellVisual=SpellInfo->SpellVisual[0]; - currEntry.ManaCost=SpellInfo->manaCost; + } + } + */ - //compare effects and casting time - currEntry.CastingTimeIndex=SpellInfo->CastingTimeIndex; - currEntry.Effect[0]=SpellInfo->Effect[0]; - currEntry.Effect[1]=SpellInfo->Effect[1]; - currEntry.Effect[2]=SpellInfo->Effect[2]; + sLog.outString(); + sLog.outString(">> Loaded %u Spell Script Targets", count); +} - currEntry.Aura[0]=SpellInfo->EffectApplyAuraName[0]; - currEntry.Aura[1]=SpellInfo->EffectApplyAuraName[1]; - currEntry.Aura[2]=SpellInfo->EffectApplyAuraName[2]; +void SpellMgr::LoadSpellPetAuras() +{ + mSpellPetAuraMap.clear(); // need for reload case - SpellRankValue currValue; - currValue.Id=ConflictedSpells.front(); - currValue.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()]; - currValue.strict=true; - RankMap.insert(std::pair(currEntry,currValue)); - } - itr=RankMap.begin(); - continue; - } - else - for (std::multimap::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();) - { - char const * err_entry=itr2->first; - uint32 rank_count=RankErrorMap.count(itr2->first); - if (rank_count>1) - for (itr2 = RankErrorMap.lower_bound(err_entry);itr2!=RankErrorMap.upper_bound(err_entry);itr2++) - { - sLog.outDebug("There is a duplicate rank entry (%s) for spell: %u",itr2->first,itr2->second->second.Id); - if (!(itr2->second->second.Id==52375 || itr2->second->second.Id==45902)) - { - sLog.outDebug("Spell %u removed from chain data.",itr2->second->second.Id); - RankMap.erase(itr2->second); - } - } - else - itr2++; - } + uint32 count = 0; - //order spells by spellLevel - std::list RankedSpells; - uint32 min_spell_lvl=0; - std::multimap::iterator min_itr; - for (;RankMap.count(entry);) - { - for (std::multimap::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++) - { - SpellEntry const *SpellInfo=sSpellStore.LookupEntry(itr2->second.Id); - if (SpellInfo->spellLevelspellLevel; - min_itr=itr2; - } - } - RankedSpells.push_back(min_itr->second.Id); - RankMap.erase(min_itr); - } + // 0 1 2 + QueryResult *result = WorldDatabase.Query("SELECT spell, pet, aura FROM spell_pet_auras"); + if( !result ) + { - //use data from talent.dbc - uint16 talent_id=0; - for(std::list::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();) + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u spell pet auras", count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field *fields = result->Fetch(); + + bar.step(); + + uint16 spell = fields[0].GetUInt16(); + uint16 pet = fields[1].GetUInt16(); + uint16 aura = fields[2].GetUInt16(); + + SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find(spell); + if(itr != mSpellPetAuraMap.end()) { - if (TalentSpellPos const* TalentPos=GetTalentSpellPos(*itr2)) - { - talent_id=TalentPos->talent_id; - RankedSpells.erase(itr2); - itr2 = RankedSpells.begin(); - } - else - itr2++; + itr->second.AddAura(pet, aura); } - if (talent_id) + else { - TalentEntry const *TalentInfo = sTalentStore.LookupEntry(talent_id); - for (uint8 rank=5;rank;rank--) + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); + if (!spellInfo) { - if (TalentInfo->RankID[rank-1]) - RankedSpells.push_front(TalentInfo->RankID[rank-1]); + sLog.outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell); + continue; } - } - - //do not proceed for spells with less than 2 ranks - itr=RankMap.begin(); - if (RankedSpells.size()<2) - continue; - - count++; - - uint32 spell_rank=1; - for(std::list::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();spell_rank++) - { - uint32 spell_id=*itr2; - mSpellChains[spell_id].rank=spell_rank; - mSpellChains[spell_id].first=RankedSpells.front(); - mSpellChains[spell_id].last=RankedSpells.back(); + int i = 0; + for(; i < 3; ++i) + if((spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA && + spellInfo->EffectApplyAuraName[i] == SPELL_AURA_DUMMY) || + spellInfo->Effect[i] == SPELL_EFFECT_DUMMY) + break; - itr2++; - if (spell_rank<2) - mSpellChains[spell_id].prev=0; + if(i == 3) + { + sLog.outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell); + continue; + } - if (spell_id==RankedSpells.back()) - mSpellChains[spell_id].next=0; - else + SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(aura); + if (!spellInfo2) { - mSpellChains[*itr2].prev=spell_id; - mSpellChains[spell_id].next=*itr2; + sLog.outErrorDb("Aura %u listed in `spell_pet_auras` does not exist", aura); + continue; } + + PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_PET, spellInfo->CalculateSimpleValue(i)); + mSpellPetAuraMap[spell] = pa; } - } -//uncomment these two lines to print yourself list of spell_chains on startup - //for (UNORDERED_MAP::iterator itr=mSpellChains.begin();itr!=mSpellChains.end();itr++) - //sLog.outString( "Id: %u, Rank: %d , %s, %u, %u, %u, %u",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()], itr->second.first, itr->second.last,itr->second.next ,itr->second.prev); + ++count; + } while( result->NextRow() ); + + delete result; sLog.outString(); - sLog.outString( ">> Loaded %u spell chains",count); + sLog.outString( ">> Loaded %u spell pet auras", count ); } -void SpellMgr::LoadSpellLearnSkills() +/// Some checks for spells, to prevent adding depricated/broken spells for trainers, spell book, etc +void SpellMgr::LoadPetLevelupSpellMap() { - mSpellLearnSkills.clear(); // need for reload case + mPetLevelupSpellMap.clear(); // need for reload case - // search auto-learned skills and add its to map also for use in unlearn spells/talents - uint32 dbc_count = 0; - barGoLink bar( sSpellStore.GetNumRows() ); - for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell) + uint32 count=0; + for (uint32 i = 0; i < sCreatureFamilyStore.GetNumRows(); ++i) { - bar.step(); - SpellEntry const* entry = sSpellStore.LookupEntry(spell); - - if(!entry) + CreatureFamilyEntry const *creatureFamily=sCreatureFamilyStore.LookupEntry(i); + if(!creatureFamily) // not exist continue; - for(int i = 0; i < 3; ++i) + for (uint8 j = 0; j < 2; ++j) { - if(entry->Effect[i]==SPELL_EFFECT_SKILL) - { - SpellLearnSkillNode dbc_node; - dbc_node.skill = entry->EffectMiscValue[i]; - if ( dbc_node.skill != SKILL_RIDING ) - dbc_node.value = 1; - else - dbc_node.value = entry->CalculateSimpleValue(i)*75; - dbc_node.maxvalue = entry->CalculateSimpleValue(i)*75; - - SpellLearnSkillNode const* db_node = GetSpellLearnSkill(spell); + if (creatureFamily->skillLine[j]==0) + continue; - mSpellLearnSkills[spell] = dbc_node; - ++dbc_count; - break; + for (uint32 k=0;kskillLine[j]!=skillLine->skillId) + continue; + SpellEntry const *spell = sSpellStore.LookupEntry(skillLine->spellId); + // not exist or triggered or talent + if(!spell || !spell->spellLevel) + continue; + if(skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) + continue; + mPetLevelupSpellMap.insert(PetLevelupSpellMap::value_type(creatureFamily->ID, std::make_pair(spell->spellLevel , spell->Id ))); + count++; } } } sLog.outString(); - sLog.outString( ">> Loaded %u Spell Learn Skills from DBC", dbc_count ); + sLog.outString( ">> Loaded %u pet levelup spells", count ); } -void SpellMgr::LoadSpellLearnSpells() +/// 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) { - mSpellLearnSpells.clear(); // need for reload case + // not exist + if(!spellInfo) + return false; - // 0 1 2 - QueryResult *result = WorldDatabase.Query("SELECT entry, SpellID, Active FROM spell_learn_spell"); - if(!result) - { - barGoLink bar( 1 ); - bar.step(); - - sLog.outString(); - sLog.outString( ">> Loaded 0 spell learn spells" ); - sLog.outErrorDb("`spell_learn_spell` table is empty!"); - return; - } - - uint32 count = 0; + bool need_check_reagents = false; - barGoLink bar( result->GetRowCount() ); - do + // check effects + for(int i=0; i<3; ++i) { - bar.step(); - Field *fields = result->Fetch(); - - uint32 spell_id = fields[0].GetUInt32(); - - SpellLearnSpellNode node; - node.spell = fields[1].GetUInt32(); - node.active = fields[2].GetBool(); - node.autoLearned= false; - - if(!sSpellStore.LookupEntry(spell_id)) - { - sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",spell_id); - continue; - } - - if(!sSpellStore.LookupEntry(node.spell)) + switch(spellInfo->Effect[i]) { - sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",node.spell); - continue; - } - - mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell_id,node)); - - ++count; - } while( result->NextRow() ); - - delete result; - - // search auto-learned spells and add its to map also for use in unlearn spells/talents - uint32 dbc_count = 0; - for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell) - { - SpellEntry const* entry = sSpellStore.LookupEntry(spell); - - if(!entry) - continue; + case 0: + continue; - for(int i = 0; i < 3; ++i) - { - if(entry->Effect[i]==SPELL_EFFECT_LEARN_SPELL) + // craft spell for crafting non-existed item (break client recipes list show) + case SPELL_EFFECT_CREATE_ITEM: { - SpellLearnSpellNode dbc_node; - dbc_node.spell = entry->EffectTriggerSpell[i]; - dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself) - - // ignore learning not existed spells (broken/outdated/or generic learnig spell 483 - if(!sSpellStore.LookupEntry(dbc_node.spell)) - continue; - - // talent or passive spells or skill-step spells auto-casted and not need dependent learning, - // pet teaching spells don't must be dependent learning (casted) - // other required explicit dependent learning - dbc_node.autoLearned = entry->EffectImplicitTargetA[i] == TARGET_UNIT_PET || GetTalentSpellCost(spell) > 0 || IsPassiveSpell(spell) || IsSpellHaveEffect(entry,SPELL_EFFECT_SKILL_STEP); - - SpellLearnSpellMap::const_iterator db_node_begin = GetBeginSpellLearnSpell(spell); - SpellLearnSpellMap::const_iterator db_node_end = GetEndSpellLearnSpell(spell); + if(!ObjectMgr::GetItemPrototype( spellInfo->EffectItemType[i] )) + { + if(msg) + { + if(pl) + ChatHandler(pl).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]); + else + sLog.outErrorDb("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]); + } + return false; + } - bool found = false; - for(SpellLearnSpellMap::const_iterator itr = db_node_begin; itr != db_node_end; ++itr) + need_check_reagents = true; + break; + } + case SPELL_EFFECT_LEARN_SPELL: + { + SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(spellInfo->EffectTriggerSpell[i]); + if( !IsSpellValid(spellInfo2,pl,msg) ) { - if(itr->second.spell == dbc_node.spell) + if(msg) { - 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(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(!found) // add new spell-spell pair if not found + if(need_check_reagents) + { + for(int j = 0; j < 8; ++j) + { + if(spellInfo->Reagent[j] > 0 && !ObjectMgr::GetItemPrototype( spellInfo->Reagent[j] )) + { + if(msg) { - mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell,dbc_node)); - ++dbc_count; + 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; } } } - sLog.outString(); - sLog.outString( ">> Loaded %u spell learn spells + %u found in DBC", count, dbc_count ); + return true; } -void SpellMgr::LoadSpellScriptTarget() +void SpellMgr::LoadSpellAreas() { - mSpellScriptTarget.clear(); // need for reload case + mSpellAreaMap.clear(); // need for reload case + mSpellAreaForQuestMap.clear(); + mSpellAreaForActiveQuestMap.clear(); + mSpellAreaForQuestEndMap.clear(); + mSpellAreaForAuraMap.clear(); uint32 count = 0; - QueryResult *result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM spell_script_target"); + // 0 1 2 3 4 5 6 7 8 + QueryResult *result = WorldDatabase.Query("SELECT spell, area, quest_start, quest_start_active, quest_end, aura_spell, racemask, gender, autocast FROM spell_area"); - if(!result) + if( !result ) { - barGoLink bar(1); + barGoLink bar( 1 ); bar.step(); sLog.outString(); - sLog.outString( ">> Loaded 0 spell script target" ); - sLog.outErrorDb("`spell_script_target` table is empty!"); + sLog.outString( ">> Loaded %u spell area requirements", count ); return; } - barGoLink bar(result->GetRowCount()); + barGoLink bar( result->GetRowCount() ); do { Field *fields = result->Fetch(); - bar.step(); - uint32 spellId = fields[0].GetUInt32(); - uint32 type = fields[1].GetUInt32(); - uint32 targetEntry = fields[2].GetUInt32(); + bar.step(); - SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId); + uint32 spell = fields[0].GetUInt32(); + SpellArea spellArea; + spellArea.spellId = spell; + spellArea.areaId = fields[1].GetUInt32(); + spellArea.questStart = fields[2].GetUInt32(); + spellArea.questStartCanActive = fields[3].GetBool(); + spellArea.questEnd = fields[4].GetUInt32(); + spellArea.auraSpell = fields[5].GetInt32(); + spellArea.raceMask = fields[6].GetUInt32(); + spellArea.gender = Gender(fields[7].GetUInt8()); + spellArea.autocast = fields[8].GetBool(); - if(!spellProto) + if(!sSpellStore.LookupEntry(spell)) { - sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not exist.",spellId,targetEntry); + sLog.outErrorDb("Spell %u listed in `spell_area` does not exist", spell); continue; } - /*bool targetfound = false; - for(int i = 0; i <3; ++i) { - if( spellProto->EffectImplicitTargetA[i]==TARGET_UNIT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetB[i]==TARGET_UNIT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetA[i]==TARGET_DST_NEARBY_ENTRY || - spellProto->EffectImplicitTargetB[i]==TARGET_DST_NEARBY_ENTRY ) + bool ok = true; + SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId); + for(SpellAreaMap::const_iterator itr = sa_bounds.first; itr != sa_bounds.second; ++itr) { - targetfound = true; + if(spellArea.spellId && itr->second.spellId && spellArea.spellId != itr->second.spellId) + continue; + if(spellArea.areaId && itr->second.areaId && spellArea.areaId!= itr->second.areaId) + continue; + if(spellArea.questStart && itr->second.questStart && spellArea.questStart!= itr->second.questStart) + continue; + if(spellArea.auraSpell && itr->second.auraSpell && spellArea.auraSpell!= itr->second.auraSpell) + continue; + if(spellArea.raceMask && itr->second.raceMask && (spellArea.raceMask & itr->second.raceMask)==0) + continue; + if(spellArea.gender != GENDER_NONE && itr->second.gender != GENDER_NONE && spellArea.gender!= itr->second.gender) + continue; + + // duplicate by requirements + ok =false; break; } + + if(!ok) + { + sLog.outErrorDb("Spell %u listed in `spell_area` already listed with similar requirements.", spell); + continue; + } + } - if(!targetfound) + + if(spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId)) { - sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (46).",spellId,targetEntry); + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong area (%u) requirement", spell,spellArea.areaId); continue; - }*/ + } - if( type >= MAX_SPELL_TARGET_TYPE ) + if(spellArea.questStart && !objmgr.GetQuestTemplate(spellArea.questStart)) { - sLog.outErrorDb("Table `spell_script_target`: target type %u for TargetEntry %u is incorrect.",type,targetEntry); + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong start quest (%u) requirement", spell,spellArea.questStart); continue; } - switch(type) + if(spellArea.questEnd) { - case SPELL_TARGET_TYPE_GAMEOBJECT: + if(!objmgr.GetQuestTemplate(spellArea.questEnd)) { - if( targetEntry==0 ) - break; - - if(!sGOStorage.LookupEntry(targetEntry)) - { - sLog.outErrorDb("Table `spell_script_target`: gameobject template entry %u does not exist.",targetEntry); - continue; - } - break; + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell,spellArea.questEnd); + continue; } - default: - { - //players - /*if( targetEntry==0 ) - { - sLog.outErrorDb("Table `spell_script_target`: target entry == 0 for not GO target type (%u).",type); - continue; - }*/ - if(targetEntry && !sCreatureStorage.LookupEntry(targetEntry)) - { - sLog.outErrorDb("Table `spell_script_target`: creature template entry %u does not exist.",targetEntry); - continue; - } - const CreatureInfo* cInfo = sCreatureStorage.LookupEntry(targetEntry); - if(spellId == 30427 && !cInfo->SkinLootId) - { - sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no skinlootid. Gas extraction will not work!", cInfo->Entry); - continue; - } - break; + if(spellArea.questEnd==spellArea.questStart && !spellArea.questStartCanActive) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell,spellArea.questEnd); + continue; } } - mSpellScriptTarget.insert(SpellScriptTarget::value_type(spellId,SpellTargetEntry(SpellScriptTargetType(type),targetEntry))); + if(spellArea.auraSpell) + { + SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell)); + if(!spellInfo) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,abs(spellArea.auraSpell)); + continue; + } - ++count; - } while (result->NextRow()); - - delete result; + if(spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_DUMMY && spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_PHASE) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy/phase aura in effect 0", spell,abs(spellArea.auraSpell)); + continue; + } - // Check all spells - /* Disabled (lot errors at this moment) - for(uint32 i = 1; i < sSpellStore.nCount; ++i) - { - SpellEntry const * spellInfo = sSpellStore.LookupEntry(i); - if(!spellInfo) - continue; + if(abs(spellArea.auraSpell)==spellArea.spellId) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement for itself", spell,abs(spellArea.auraSpell)); + continue; + } - bool found = false; - for(int j=0; j<3; ++j) - { - if( spellInfo->EffectImplicitTargetA[j] == TARGET_UNIT_NEARBY_ENTRY || spellInfo->EffectImplicitTargetA[j] != TARGET_UNIT_CASTER && spellInfo->EffectImplicitTargetB[j] == TARGET_UNIT_NEARBY_ENTRY ) + // not allow autocast chains by auraSpell field (but allow use as alternative if not present) + if(spellArea.autocast && spellArea.auraSpell > 0) { - SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(spellInfo->Id); - SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(spellInfo->Id); - if(lower==upper) + bool chain = false; + SpellAreaForAuraMapBounds saBound = GetSpellAreaForAuraMapBounds(spellArea.spellId); + for(SpellAreaForAuraMap::const_iterator itr = saBound.first; itr != saBound.second; ++itr) { - sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = %u (TARGET_UNIT_NEARBY_ENTRY), but does not have record in `spell_script_target`",spellInfo->Id,TARGET_UNIT_NEARBY_ENTRY); - break; // effects of spell + if(itr->second->autocast && itr->second->auraSpell > 0) + { + chain = true; + break; + } } - } - } - } - */ - - sLog.outString(); - sLog.outString(">> Loaded %u Spell Script Targets", count); -} -void SpellMgr::LoadSpellPetAuras() -{ - mSpellPetAuraMap.clear(); // need for reload case - - uint32 count = 0; - - // 0 1 2 - QueryResult *result = WorldDatabase.Query("SELECT spell, pet, aura FROM spell_pet_auras"); - if( !result ) - { - - barGoLink bar( 1 ); + if(chain) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell); + continue; + } - bar.step(); + SpellAreaMapBounds saBound2 = GetSpellAreaMapBounds(spellArea.auraSpell); + for(SpellAreaMap::const_iterator itr2 = saBound2.first; itr2 != saBound2.second; ++itr2) + { + if(itr2->second.autocast && itr2->second.auraSpell > 0) + { + chain = true; + break; + } + } - sLog.outString(); - sLog.outString( ">> Loaded %u spell pet auras", count ); - return; - } + if(chain) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell); + continue; + } + } + } - barGoLink bar( result->GetRowCount() ); + if(spellArea.raceMask && (spellArea.raceMask & RACEMASK_ALL_PLAYABLE)==0) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong race mask (%u) requirement", spell,spellArea.raceMask); + continue; + } - do - { - Field *fields = result->Fetch(); + if(spellArea.gender!=GENDER_NONE && spellArea.gender!=GENDER_FEMALE && spellArea.gender!=GENDER_MALE) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong gender (%u) requirement", spell,spellArea.gender); + continue; + } - bar.step(); + SpellArea const* sa = &mSpellAreaMap.insert(SpellAreaMap::value_type(spell,spellArea))->second; - uint16 spell = fields[0].GetUInt16(); - uint16 pet = fields[1].GetUInt16(); - uint16 aura = fields[2].GetUInt16(); + // for search by current zone/subzone at zone/subzone change + if(spellArea.areaId) + mSpellAreaForAreaMap.insert(SpellAreaForAreaMap::value_type(spellArea.areaId,sa)); - SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find(spell); - if(itr != mSpellPetAuraMap.end()) + // for search at quest start/reward + if(spellArea.questStart) { - itr->second.AddAura(pet, aura); + if(spellArea.questStartCanActive) + mSpellAreaForActiveQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa)); + else + mSpellAreaForQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa)); } - else - { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); - if (!spellInfo) - { - sLog.outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell); - continue; - } - int i = 0; - for(; i < 3; ++i) - if((spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA && - spellInfo->EffectApplyAuraName[i] == SPELL_AURA_DUMMY) || - spellInfo->Effect[i] == SPELL_EFFECT_DUMMY) - break; - - if(i == 3) - { - 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; - } + // for search at quest start/reward + if(spellArea.questEnd) + mSpellAreaForQuestEndMap.insert(SpellAreaForQuestMap::value_type(spellArea.questEnd,sa)); - PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_PET, spellInfo->CalculateSimpleValue(i)); - mSpellPetAuraMap[spell] = pa; - } + // for search at aura apply + if(spellArea.auraSpell) + mSpellAreaForAuraMap.insert(SpellAreaForAuraMap::value_type(abs(spellArea.auraSpell),sa)); ++count; } while( result->NextRow() ); @@ -2380,943 +2211,1113 @@ void SpellMgr::LoadSpellPetAuras() delete result; sLog.outString(); - sLog.outString( ">> Loaded %u spell pet auras", count ); + sLog.outString( ">> Loaded %u spell area requirements", count ); } -// set data in core for now -void SpellMgr::LoadSpellCustomAttr() +SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player) { - mSpellCustomAttr.resize(GetSpellStore()->GetNumRows()); - - SpellEntry *spellInfo; - for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i) + // normal case + if( spellInfo->AreaGroupId > 0) { - mSpellCustomAttr[i] = 0; - spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i); - if(!spellInfo) - continue; + bool found = false; + AreaGroupEntry const* groupEntry = sAreaGroupStore.LookupEntry(spellInfo->AreaGroupId); + while (groupEntry) + { + for (uint32 i=0; i<6; ++i) + if( groupEntry->AreaId[i] == zone_id || groupEntry->AreaId[i] == area_id ) + found = true; + if (found || !groupEntry->nextGroup) + break; + // Try search in next group + groupEntry = sAreaGroupStore.LookupEntry(groupEntry->nextGroup); + } + + if(!found) + return SPELL_FAILED_INCORRECT_AREA; + } - bool auraSpell = true; - for(uint32 j = 0; j < 3; ++j) + // DB base check (if non empty then must fit at least single for allow) + SpellAreaMapBounds saBounds = spellmgr.GetSpellAreaMapBounds(spellInfo->Id); + if(saBounds.first != saBounds.second) + { + for(SpellAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) { - if(spellInfo->Effect[j]) - if(spellInfo->Effect[j] != SPELL_EFFECT_APPLY_AURA - || SpellTargetType[spellInfo->EffectImplicitTargetA[j]] != TARGET_TYPE_UNIT_TARGET) - //ignore target party for now - { - auraSpell = false; - break; - } + if(itr->second.IsFitToRequirements(player,zone_id,area_id)) + return SPELL_CAST_OK; } - if(auraSpell) - mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_SPELL; + return SPELL_FAILED_INCORRECT_AREA; + } - for(uint32 j = 0; j < 3; ++j) + // 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) { - switch(spellInfo->EffectApplyAuraName[j]) - { - case SPELL_AURA_PERIODIC_DAMAGE: - case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: - case SPELL_AURA_PERIODIC_LEECH: - mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_DOT; - break; - case SPELL_AURA_PERIODIC_HEAL: - case SPELL_AURA_OBS_MOD_HEALTH: - mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_HOT; - break; - case SPELL_AURA_MOD_ROOT: - mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_CC; - mSpellCustomAttr[i] |= SPELL_ATTR_CU_MOVEMENT_IMPAIR; - break; - case SPELL_AURA_MOD_DECREASE_SPEED: - mSpellCustomAttr[i] |= SPELL_ATTR_CU_MOVEMENT_IMPAIR; - break; - default: - break; - } + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); + if(!mapEntry) + return SPELL_FAILED_INCORRECT_AREA; - switch(spellInfo->Effect[j]) - { - case SPELL_EFFECT_SCHOOL_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: - case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: - case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: - case SPELL_EFFECT_HEAL: - mSpellCustomAttr[i] |= SPELL_ATTR_CU_DIRECT_DAMAGE; - break; - case SPELL_EFFECT_CHARGE: - case SPELL_EFFECT_JUMP: - case SPELL_EFFECT_JUMP2: - case SPELL_EFFECT_138: - if(!spellInfo->speed && !spellInfo->SpellFamilyName) - spellInfo->speed = SPEED_CHARGE; - mSpellCustomAttr[i] |= SPELL_ATTR_CU_CHARGE; - break; - case SPELL_EFFECT_TRIGGER_SPELL: - if (SpellTargetType[spellInfo->EffectImplicitTargetA[j]]== TARGET_TYPE_DEST_CASTER || - SpellTargetType[spellInfo->EffectImplicitTargetA[j]]== TARGET_TYPE_DEST_TARGET || - SpellTargetType[spellInfo->EffectImplicitTargetA[j]]== TARGET_TYPE_DEST_DEST || - spellInfo->Targets & (TARGET_FLAG_SOURCE_LOCATION|TARGET_FLAG_DEST_LOCATION)) - spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE; - break; - } - - switch(SpellTargetType[spellInfo->EffectImplicitTargetA[j]]) - { - case TARGET_TYPE_UNIT_TARGET: - case TARGET_TYPE_DEST_TARGET: - spellInfo->Targets |= TARGET_FLAG_UNIT; - break; - } + return mapEntry->IsBattleGround() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } - - for(uint32 j = 0; j < 3; ++j) + case 44521: // Preparation { - switch(spellInfo->EffectApplyAuraName[j]) - { - case SPELL_AURA_MOD_POSSESS: - case SPELL_AURA_MOD_CONFUSE: - case SPELL_AURA_MOD_CHARM: - case SPELL_AURA_MOD_FEAR: - case SPELL_AURA_MOD_STUN: - mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_CC; - mSpellCustomAttr[i] &= ~SPELL_ATTR_CU_MOVEMENT_IMPAIR; - break; - } - } + if(!player) + return SPELL_FAILED_REQUIRES_AREA; - if(spellInfo->SpellVisual[0] == 3879) - mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_BACK; + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); + if(!mapEntry) + return SPELL_FAILED_INCORRECT_AREA; - switch(i) - { - case 26029: // dark glare - case 37433: // spout - case 43140: case 43215: // flame breath - mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_LINE; - break; - case 24340: case 26558: case 28884: // Meteor - case 36837: case 38903: case 41276: // Meteor - case 26789: // Shard of the Fallen Star - case 31436: // Malevolent Cleave - case 35181: // Dive Bomb - case 40810: case 43267: case 43268: // Saber Lash - case 42384: // Brutal Swipe - case 45150: // Meteor Slash - mSpellCustomAttr[i] |= SPELL_ATTR_CU_SHARE_DAMAGE; - break; - case 27820: - mSpellCustomAttr[i] |= SPELL_ATTR_CU_EXCLUDE_SELF; - break; - case 44978: case 45001: case 45002: // Wild Magic - case 45004: case 45006: case 45010: // Wild Magic - case 31347: // Doom - case 41635: // Prayer of Mending - case 44869: // Spectral Blast - case 45027: // Revitalize - case 45976: // Muru Portal Channel - case 39365: // Thundering Storm - case 41071: // Raise Dead (HACK) - spellInfo->MaxAffectedTargets = 1; - break; - case 41376: // Spite - case 39992: // Needle Spine - case 29576: // Multi-Shot - case 40816: // Saber Lash - case 37790: // Spread Shot - case 46771: // Flame Sear - case 45248: // Shadow Blades - case 41303: // Soul Drain - case 54172: // Divine Storm (heal) - case 29213: // Curse of the Plaguebringer - Noth - case 28542: // Life Drain - Sapphiron - spellInfo->MaxAffectedTargets = 3; - break; - case 38310: //Multi-Shot - spellInfo->MaxAffectedTargets = 4; - break; - case 42005: // Bloodboil - case 38296: // Spitfire Totem - case 37676: // Insidious Whisper - case 46009: // Negative Energy - case 45641: // Fire Bloom - case 54937: // Glyph of Holy Light - case 55665: // Life Drain - Sapphiron (H) - case 28796: // Poison Bolt Volly - Faerlina - spellInfo->MaxAffectedTargets = 5; - break; - case 40827: // Sinful Beam - case 40859: // Sinister Beam - case 40860: // Vile Beam - case 40861: // Wicked Beam - case 57669: // Replenishment - case 54835: // Curse of the Plaguebringer - Noth (H) - case 54098: // Poison Bolt Volly - Faerlina (H) - spellInfo->MaxAffectedTargets = 10; - break; - case 8122: case 8124: case 10888: case 10890: // Psychic Scream - case 12494: // Frostbite - spellInfo->Attributes |= SPELL_ATTR_BREAKABLE_BY_DAMAGE; - break; - case 38794: case 33711: //Murmur's Touch - spellInfo->MaxAffectedTargets = 1; - spellInfo->EffectTriggerSpell[0] = 33760; - break; - case 1122: // Inferno - case 18662: // Curse of Doom - spellInfo->EffectBasePoints[0] = 0; //prevent summon too many of them - break; - case 17941: // Shadow Trance - case 22008: // Netherwind Focus - case 31834: // Light's Grace - case 34754: // Clearcasting - case 34936: // Backlash - case 48108: // Hot Streak - case 51124: // Killing Machine - case 54741: // Firestarter - case 57761: // Fireball! - case 39805: // Lightning Overload - case 52437: // Sudden Death - spellInfo->procCharges=1; - break; - case 44544: // Fingers of Frost - spellInfo->procCharges=2; - break; - case 28200: // Ascendance (Talisman of Ascendance trinket) - spellInfo->procCharges=6; - break; - default: - break; + 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; - switch(spellInfo->SpellFamilyName) + return mapEntry->IsBattleArena() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; + } + case 32727: // Arena Preparation { - case SPELLFAMILY_DRUID: - // starfall - if(spellInfo->SpellFamilyFlags[2] & 0x100) - spellInfo->MaxAffectedTargets = 2; - // Wild growth - else if(spellInfo->SpellFamilyFlags[1] & 0x4000000) - spellInfo->MaxAffectedTargets = 5; - break; - // circle of healing - case SPELLFAMILY_PRIEST: - if(spellInfo->SpellFamilyFlags[0] & 0x10000000) - spellInfo->MaxAffectedTargets = 5; - break; + 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; } } - SummonPropertiesEntry *properties = const_cast(sSummonPropertiesStore.LookupEntry(121)); - properties->Type = SUMMON_TYPE_TOTEM; - - CreatureAI::FillAISpellInfo(); + return SPELL_CAST_OK; } -void SpellMgr::LoadSpellLinked() +void SpellMgr::LoadSkillLineAbilityMap() { - mSpellLinkedMap.clear(); // need for reload case + mSkillLineAbilityMap.clear(); + + barGoLink bar( sSkillLineAbilityStore.GetNumRows() ); uint32 count = 0; - // 0 1 2 - QueryResult *result = WorldDatabase.Query("SELECT spell_trigger, spell_effect, type FROM spell_linked_spell"); - if( !result ) + for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) { - barGoLink bar( 1 ); bar.step(); - sLog.outString(); - sLog.outString( ">> Loaded %u linked spells", count ); - return; + SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i); + if(!SkillInfo) + continue; + + mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId,SkillInfo)); + ++count; } - barGoLink bar( result->GetRowCount() ); + sLog.outString(); + sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count); +} - do +DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered) +{ + // Explicit Diminishing Groups + switch(spellproto->SpellFamilyName) { - Field *fields = result->Fetch(); - - bar.step(); - - int32 trigger = fields[0].GetInt32(); - int32 effect = fields[1].GetInt32(); - int32 type = fields[2].GetInt32(); - - SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(trigger)); - if (!spellInfo) - { - sLog.outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(trigger)); - continue; - } - spellInfo = sSpellStore.LookupEntry(abs(effect)); - if (!spellInfo) + case SPELLFAMILY_ROGUE: { - sLog.outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(effect)); - continue; + // Kidney Shot + if (spellproto->SpellFamilyFlags[0] & 0x200000) + return DIMINISHING_KIDNEYSHOT; + // Sap + else if (spellproto->SpellFamilyFlags[0] & 0x80) + return DIMINISHING_POLYMORPH; + // Gouge + else if (spellproto->SpellFamilyFlags[0] & 0x8) + return DIMINISHING_POLYMORPH; + // Blind + else if (spellproto->SpellFamilyFlags[0] & 0x00001000000) + return DIMINISHING_BLIND_CYCLONE; + break; } - - if(trigger > 0) + case SPELLFAMILY_WARLOCK: { - switch(type) - { - case 0: mSpellCustomAttr[trigger] |= SPELL_ATTR_CU_LINK_CAST; break; - case 1: mSpellCustomAttr[trigger] |= SPELL_ATTR_CU_LINK_HIT; break; - case 2: mSpellCustomAttr[trigger] |= SPELL_ATTR_CU_LINK_AURA; break; - } + // Death Coil + if (spellproto->SpellFamilyFlags[0] & 0x00000080000) + return DIMINISHING_DEATHCOIL; + // Seduction + else if (spellproto->SpellFamilyFlags[0] & 0x00040000000) + return DIMINISHING_FEAR; + // Curses/etc + else if (spellproto->SpellFamilyFlags[0] & 0x80000000) + return DIMINISHING_LIMITONLY; + break; } - else + case SPELLFAMILY_DRUID: { - mSpellCustomAttr[-trigger] |= SPELL_ATTR_CU_LINK_REMOVE; + // Cyclone + if (spellproto->SpellFamilyFlags[1] & 0x020) + return DIMINISHING_BLIND_CYCLONE; + break; } - - if(type) //we will find a better way when more types are needed + case SPELLFAMILY_WARRIOR: { - if(trigger > 0) - trigger += SPELL_LINKED_MAX_SPELLS * type; - else - trigger -= SPELL_LINKED_MAX_SPELLS * type; + // Hamstring - limit duration to 10s in PvP + if (spellproto->SpellFamilyFlags[0] & 0x00000000002) + return DIMINISHING_LIMITONLY; + break; } - mSpellLinkedMap[trigger].push_back(effect); + default: + break; + } - ++count; - } while( result->NextRow() ); + // Get by mechanic + uint32 mechanic = GetAllSpellMechanicMask(spellproto); + if (mechanic == MECHANIC_NONE) return DIMINISHING_NONE; + if (mechanic & (1<> Loaded %u linked spells", count ); + return DIMINISHING_NONE; } -/// Some checks for spells, to prevent adding depricated/broken spells for trainers, spell book, etc -void SpellMgr::LoadPetLevelupSpellMap() +bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group) { - mPetLevelupSpellMap.clear(); // need for reload case - - uint32 count=0; - for (uint32 i = 0; i < sCreatureFamilyStore.GetNumRows(); ++i) + switch(group) { - CreatureFamilyEntry const *creatureFamily=sCreatureFamilyStore.LookupEntry(i); - if(!creatureFamily) // not exist - continue; - - for (uint8 j = 0; j < 2; ++j) - { - if (creatureFamily->skillLine[j]==0) - continue; + case DIMINISHING_CONTROL_STUN: + case DIMINISHING_TRIGGER_STUN: + case DIMINISHING_KIDNEYSHOT: + case DIMINISHING_SLEEP: + case DIMINISHING_CONTROL_ROOT: + case DIMINISHING_TRIGGER_ROOT: + case DIMINISHING_FEAR: + case DIMINISHING_WARLOCK_FEAR: + case DIMINISHING_CHARM: + case DIMINISHING_POLYMORPH: + case DIMINISHING_FREEZE: + case DIMINISHING_KNOCKOUT: + case DIMINISHING_BLIND_CYCLONE: + case DIMINISHING_BANISH: + case DIMINISHING_LIMITONLY: + return true; + default: + return false; + } + return false; +} - for (uint32 k=0;kskillLine[j]!=skillLine->skillId) - continue; - SpellEntry const *spell = sSpellStore.LookupEntry(skillLine->spellId); - // not exist or triggered or talent - if(!spell || !spell->spellLevel) - continue; - if(skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL) - continue; - mPetLevelupSpellMap.insert(PetLevelupSpellMap::value_type(creatureFamily->ID, std::make_pair(spell->spellLevel , spell->Id ))); - count++; - } - } +DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) +{ + switch(group) + { + case DIMINISHING_BLIND_CYCLONE: + case DIMINISHING_CONTROL_STUN: + case DIMINISHING_TRIGGER_STUN: + case DIMINISHING_KIDNEYSHOT: + return DRTYPE_ALL; + case DIMINISHING_SLEEP: + case DIMINISHING_CONTROL_ROOT: + case DIMINISHING_TRIGGER_ROOT: + case DIMINISHING_FEAR: + case DIMINISHING_CHARM: + case DIMINISHING_POLYMORPH: + case DIMINISHING_SILENCE: + case DIMINISHING_DISARM: + case DIMINISHING_DEATHCOIL: + case DIMINISHING_FREEZE: + case DIMINISHING_BANISH: + case DIMINISHING_WARLOCK_FEAR: + case DIMINISHING_KNOCKOUT: + return DRTYPE_PLAYER; + default: + break; } - sLog.outString(); - sLog.outString( ">> Loaded %u pet levelup spells", count ); + return DRTYPE_NONE; } -/// 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) +bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const { - // not exist - if(!spellInfo) - return false; + if(gender!=GENDER_NONE) + { + // not in expected gender + if(!player || gender != player->getGender()) + return false; + } - bool need_check_reagents = false; + if(raceMask) + { + // not in expected race + if(!player || !(raceMask & player->getRaceMask())) + return false; + } - // check effects - for(int i=0; i<3; ++i) + if(areaId) { - switch(spellInfo->Effect[i]) - { - case 0: - continue; + // not in expected zone + if(newZone!=areaId && newArea!=areaId) + return false; + } - // craft spell for crafting non-existed item (break client recipes list show) - case SPELL_EFFECT_CREATE_ITEM: - { - if(!ObjectMgr::GetItemPrototype( spellInfo->EffectItemType[i] )) - { - if(msg) - { - if(pl) - ChatHandler(pl).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]); - else - sLog.outErrorDb("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]); - } - return false; - } + if(questStart) + { + // not in expected required quest state + if(!player || (!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)) + 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(questEnd) + { + // not in expected forbidden quest state + if(!player || player->GetQuestRewardStatus(questEnd)) + return false; } - if(need_check_reagents) + if(auraSpell) { - for(int j = 0; j < 8; ++j) - { - if(spellInfo->Reagent[j] > 0 && !ObjectMgr::GetItemPrototype( spellInfo->Reagent[j] )) - { - if(msg) - { - if(pl) - ChatHandler(pl).PSendSysMessage("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->Reagent[j]); - else - sLog.outErrorDb("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->Reagent[j]); - } - return false; - } - } + // not have expected aura + if(!player) + return false; + if(auraSpell > 0) + // have expected aura + return player->HasAura(auraSpell); + else + // not have expected aura + return !player->HasAura(-auraSpell); } return true; } -void SpellMgr::LoadSpellAreas() -{ - mSpellAreaMap.clear(); // need for reload case - mSpellAreaForQuestMap.clear(); - mSpellAreaForActiveQuestMap.clear(); - mSpellAreaForQuestEndMap.clear(); - mSpellAreaForAuraMap.clear(); +//-----------TRINITY------------- - uint32 count = 0; +bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2, bool sameCaster) const +{ + SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1); + SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2); - // 0 1 2 3 4 5 6 7 8 - QueryResult *result = WorldDatabase.Query("SELECT spell, area, quest_start, quest_start_active, quest_end, aura_spell, racemask, gender, autocast FROM spell_area"); + if(!spellInfo_1 || !spellInfo_2) + return false; - if( !result ) + SpellSpecific spellId_spec_1 = GetSpellSpecific(spellId_1); + SpellSpecific spellId_spec_2 = GetSpellSpecific(spellId_2); + if (spellId_spec_1 && spellId_spec_2) + if (IsSingleFromSpellSpecificPerTarget(spellId_spec_1, spellId_spec_2) + ||(IsSingleFromSpellSpecificPerCaster(spellId_spec_1, spellId_spec_2) && sameCaster)) + return true; + + if(spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName) + return false; + + if(!sameCaster) + { + for(uint32 i = 0; i < 3; ++i) + if (spellInfo_1->Effect[i] == SPELL_EFFECT_APPLY_AURA + || spellInfo_1->Effect[i] == SPELL_EFFECT_PERSISTENT_AREA_AURA) + // not area auras (shaman totem) + switch(spellInfo_1->EffectApplyAuraName[i]) + { + // DOT or HOT from different casters will stack + case SPELL_AURA_PERIODIC_DAMAGE: + case SPELL_AURA_PERIODIC_HEAL: + case SPELL_AURA_PERIODIC_TRIGGER_SPELL: + case SPELL_AURA_PERIODIC_ENERGIZE: + case SPELL_AURA_PERIODIC_MANA_LEECH: + case SPELL_AURA_PERIODIC_LEECH: + case SPELL_AURA_POWER_BURN_MANA: + case SPELL_AURA_OBS_MOD_ENERGY: + case SPELL_AURA_OBS_MOD_HEALTH: + return false; + default: + break; + } + } + + // hack for Incanter's Absorption + if (spellInfo_1->Id==44413 && spellInfo_2->Id==44413) + return false; + + spellId_2 = GetLastSpellInChain(spellId_2); + spellId_1 = GetLastSpellInChain(spellId_1); + if (spellId_1 == spellId_2) + return true; + + // generic spells + if(!spellInfo_1->SpellFamilyName) + { + if(!spellInfo_1->SpellIconID + || spellInfo_1->SpellIconID == 1 + || spellInfo_1->SpellIconID != spellInfo_2->SpellIconID) + return false; + } + // check for class spells + else + { + if (spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags) + return false; + if (!spellInfo_1->SpellFamilyFlags) + return false; + } + + //use data of highest rank spell(needed for spells which ranks have different effects) + spellInfo_1=sSpellStore.LookupEntry(spellId_1); + spellInfo_2=sSpellStore.LookupEntry(spellId_2); + + //if spells have exactly the same effect they cannot stack + for(uint32 i = 0; i < 3; ++i) + if(spellInfo_1->Effect[i] != spellInfo_2->Effect[i] + // Overkill and master of subtlety need this + || spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_DUMMY + || spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_PERIODIC_DUMMY + + || spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i] + || spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i]) // paladin resist aura + return false; // need itemtype check? need an example to add that check + + return true; +} + +bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool def) +{ + if (!dispelSpell) return false; + SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); + if (!spellproto) return false; + + if(dispelSpell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) + return true; + + return def; +} + +void SpellMgr::LoadSpellEnchantProcData() +{ + mSpellEnchantProcEventMap.clear(); // need for reload case + + uint32 count = 0; + + // 0 1 2 3 + QueryResult *result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data"); + if( !result ) { + barGoLink bar( 1 ); bar.step(); sLog.outString(); - sLog.outString( ">> Loaded %u spell area requirements", count ); + sLog.outString( ">> Loaded %u spell enchant proc event conditions", count ); return; } barGoLink bar( result->GetRowCount() ); - do { Field *fields = result->Fetch(); bar.step(); - uint32 spell = fields[0].GetUInt32(); - SpellArea spellArea; - spellArea.spellId = spell; - spellArea.areaId = fields[1].GetUInt32(); - spellArea.questStart = fields[2].GetUInt32(); - spellArea.questStartCanActive = fields[3].GetBool(); - spellArea.questEnd = fields[4].GetUInt32(); - spellArea.auraSpell = fields[5].GetInt32(); - spellArea.raceMask = fields[6].GetUInt32(); - spellArea.gender = Gender(fields[7].GetUInt8()); - spellArea.autocast = fields[8].GetBool(); + uint32 enchantId = fields[0].GetUInt32(); - if(!sSpellStore.LookupEntry(spell)) + SpellItemEnchantmentEntry const *ench = sSpellItemEnchantmentStore.LookupEntry(enchantId); + if (!ench) { - sLog.outErrorDb("Spell %u listed in `spell_area` does not exist", spell); + sLog.outErrorDb("Enchancment %u listed in `spell_enchant_proc_data` does not exist", enchantId); continue; } - { - bool ok = true; - SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId); - for(SpellAreaMap::const_iterator itr = sa_bounds.first; itr != sa_bounds.second; ++itr) - { - if(spellArea.spellId && itr->second.spellId && spellArea.spellId != itr->second.spellId) - continue; - if(spellArea.areaId && itr->second.areaId && spellArea.areaId!= itr->second.areaId) - continue; - if(spellArea.questStart && itr->second.questStart && spellArea.questStart!= itr->second.questStart) - continue; - if(spellArea.auraSpell && itr->second.auraSpell && spellArea.auraSpell!= itr->second.auraSpell) - continue; - if(spellArea.raceMask && itr->second.raceMask && (spellArea.raceMask & itr->second.raceMask)==0) - continue; - if(spellArea.gender != GENDER_NONE && itr->second.gender != GENDER_NONE && spellArea.gender!= itr->second.gender) - continue; + SpellEnchantProcEntry spe; - // duplicate by requirements - ok =false; - break; - } + spe.customChance = fields[1].GetUInt32(); + spe.PPMChance = fields[2].GetFloat(); + spe.procEx = fields[3].GetUInt32(); - if(!ok) - { - sLog.outErrorDb("Spell %u listed in `spell_area` already listed with similar requirements.", spell); - continue; - } + mSpellEnchantProcEventMap[enchantId] = spe; - } + ++count; + } while( result->NextRow() ); - if(spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId)) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have wrong area (%u) requirement", spell,spellArea.areaId); - continue; - } + delete result; - if(spellArea.questStart && !objmgr.GetQuestTemplate(spellArea.questStart)) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have wrong start quest (%u) requirement", spell,spellArea.questStart); - continue; - } + sLog.outString( ">> Loaded %u enchant proc data definitions", count); +} - if(spellArea.questEnd) - { - if(!objmgr.GetQuestTemplate(spellArea.questEnd)) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell,spellArea.questEnd); - continue; - } +void SpellMgr::LoadSpellRequired() +{ + mSpellsReqSpell.clear(); // need for reload case + mSpellReq.clear(); // need for reload case - if(spellArea.questEnd==spellArea.questStart && !spellArea.questStartCanActive) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell,spellArea.questEnd); - continue; - } - } + QueryResult *result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required"); - if(spellArea.auraSpell) - { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell)); - if(!spellInfo) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,abs(spellArea.auraSpell)); - continue; - } + if(result == NULL) + { + barGoLink bar( 1 ); + bar.step(); - if(spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_DUMMY && spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_PHASE) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy/phase aura in effect 0", spell,abs(spellArea.auraSpell)); - continue; - } + sLog.outString(); + sLog.outString( ">> Loaded 0 spell required records" ); + sLog.outErrorDb("`spell_required` table is empty!"); + return; + } + uint32 rows = 0; - if(abs(spellArea.auraSpell)==spellArea.spellId) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement for itself", spell,abs(spellArea.auraSpell)); - continue; - } + barGoLink bar( result->GetRowCount() ); + do + { + bar.step(); + Field *fields = result->Fetch(); - // not allow autocast chains by auraSpell field (but allow use as alternative if not present) - if(spellArea.autocast && spellArea.auraSpell > 0) - { - bool chain = false; - SpellAreaForAuraMapBounds saBound = GetSpellAreaForAuraMapBounds(spellArea.spellId); - for(SpellAreaForAuraMap::const_iterator itr = saBound.first; itr != saBound.second; ++itr) - { - if(itr->second->autocast && itr->second->auraSpell > 0) - { - chain = true; - break; - } - } + uint32 spell_id = fields[0].GetUInt32(); + uint32 spell_req = fields[1].GetUInt32(); - if(chain) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell); - continue; - } + mSpellsReqSpell.insert (std::pair(spell_req, spell_id)); + mSpellReq[spell_id] = spell_req; + ++rows; + } while( result->NextRow() ); + delete result; - SpellAreaMapBounds saBound2 = GetSpellAreaMapBounds(spellArea.auraSpell); - for(SpellAreaMap::const_iterator itr2 = saBound2.first; itr2 != saBound2.second; ++itr2) - { - if(itr2->second.autocast && itr2->second.auraSpell > 0) - { - chain = true; - break; - } - } + sLog.outString(); + sLog.outString( ">> Loaded %u spell required records", rows ); +} - if(chain) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell); - continue; - } - } - } - - if(spellArea.raceMask && (spellArea.raceMask & RACEMASK_ALL_PLAYABLE)==0) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have wrong race mask (%u) requirement", spell,spellArea.raceMask); - continue; - } - - if(spellArea.gender!=GENDER_NONE && spellArea.gender!=GENDER_FEMALE && spellArea.gender!=GENDER_MALE) - { - sLog.outErrorDb("Spell %u listed in `spell_area` have wrong gender (%u) requirement", spell,spellArea.gender); - continue; - } - - SpellArea const* sa = &mSpellAreaMap.insert(SpellAreaMap::value_type(spell,spellArea))->second; - - // for search by current zone/subzone at zone/subzone change - if(spellArea.areaId) - mSpellAreaForAreaMap.insert(SpellAreaForAreaMap::value_type(spellArea.areaId,sa)); - - // for search at quest start/reward - if(spellArea.questStart) - { - if(spellArea.questStartCanActive) - mSpellAreaForActiveQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa)); - else - mSpellAreaForQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa)); - } - - // for search at quest start/reward - if(spellArea.questEnd) - mSpellAreaForQuestEndMap.insert(SpellAreaForQuestMap::value_type(spellArea.questEnd,sa)); +struct SpellRankEntry +{ + uint32 SkillId; + char const *SpellName; + uint32 DurationIndex; + uint32 RangeIndex; + uint32 SpellVisual; + uint32 ProcFlags; + flag96 SpellFamilyFlags; + uint32 TargetAuraState; + uint32 ManaCost; + uint32 CastingTimeIndex; + flag96 Effect; + flag96 Aura; + uint16 TalentID; - // for search at aura apply - if(spellArea.auraSpell) - mSpellAreaForAuraMap.insert(SpellAreaForAuraMap::value_type(abs(spellArea.auraSpell),sa)); + bool operator < (const SpellRankEntry & _Right) const + { + return (SkillId != _Right.SkillId ? SkillId < _Right.SkillId + : SpellName!=_Right.SpellName ? SpellName < _Right.SpellName + : ProcFlags!=_Right.ProcFlags ? ProcFlags < _Right.ProcFlags - ++count; - } while( result->NextRow() ); + : Effect!=_Right.Effect ? Effect < _Right.Effect + : Aura!=_Right.Aura ? Aura < _Right.Aura + : TalentID!=_Right.TalentID ? TalentID < _Right.TalentID + : (CastingTimeIndex!=_Right.CastingTimeIndex) && (!CastingTimeIndex || !_Right.CastingTimeIndex || CastingTimeIndex==1 || !_Right.CastingTimeIndex==1) ? CastingTimeIndex < _Right.CastingTimeIndex - delete result; + : SpellFamilyFlags!=_Right.SpellFamilyFlags ? SpellFamilyFlags < _Right.SpellFamilyFlags + : (SpellVisual!=_Right.SpellVisual) && (!SpellVisual || !_Right.SpellVisual) ? SpellVisual < _Right.SpellVisual + : (ManaCost!=_Right.ManaCost) && (!ManaCost || !_Right.ManaCost) ? ManaCost < _Right.ManaCost + : (DurationIndex!=_Right.DurationIndex) && (!DurationIndex || !_Right.DurationIndex)? DurationIndex < _Right.DurationIndex + : (RangeIndex!=_Right.RangeIndex) && (!RangeIndex || !_Right.RangeIndex || RangeIndex==1 || !_Right.RangeIndex==1) ? RangeIndex < _Right.RangeIndex + : TargetAuraState < _Right.TargetAuraState + ); + } +}; - sLog.outString(); - sLog.outString( ">> Loaded %u spell area requirements", count ); -} +struct SpellRankValue +{ + uint32 Id; + char const *Rank; + bool strict; +}; -SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player) +void SpellMgr::LoadSpellChains() { - // normal case - if( spellInfo->AreaGroupId > 0) - { - bool found = false; - AreaGroupEntry const* groupEntry = sAreaGroupStore.LookupEntry(spellInfo->AreaGroupId); - while (groupEntry) - { - for (uint32 i=0; i<6; ++i) - if( groupEntry->AreaId[i] == zone_id || groupEntry->AreaId[i] == area_id ) - found = true; - if (found || !groupEntry->nextGroup) - break; - // Try search in next group - groupEntry = sAreaGroupStore.LookupEntry(groupEntry->nextGroup); - } - - if(!found) - return SPELL_FAILED_INCORRECT_AREA; - } + mSpellChains.clear(); // need for reload case - // DB base check (if non empty then must fit at least single for allow) - SpellAreaMapBounds saBounds = spellmgr.GetSpellAreaMapBounds(spellInfo->Id); - if(saBounds.first != saBounds.second) + std::vector ChainedSpells; + for (uint32 ability_id=0;ability_idsecond.IsFitToRequirements(player,zone_id,area_id)) - return SPELL_CAST_OK; - } - return SPELL_FAILED_INCORRECT_AREA; + SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id); + if (!AbilityInfo) + continue; + if (!AbilityInfo->forward_spellid) + continue; + ChainedSpells.push_back(AbilityInfo->forward_spellid); } - // bg spell checks - switch(spellInfo->Id) - { - case 23333: // Warsong Flag - case 23335: // Silverwing Flag - return map_id == 489 && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; - case 34976: // Netherstorm Flag - return map_id == 566 && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; - case 2584: // Waiting to Resurrect - case 22011: // Spirit Heal Channel - case 22012: // Spirit Heal - case 24171: // Resurrection Impact Visual - case 42792: // Recently Dropped Flag - case 43681: // Inactive - case 44535: // Spirit Heal (mana) - { - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if(!mapEntry) - return SPELL_FAILED_INCORRECT_AREA; - - return mapEntry->IsBattleGround() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; - } - case 44521: // Preparation - { - if(!player) - return SPELL_FAILED_REQUIRES_AREA; - - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if(!mapEntry) - return SPELL_FAILED_INCORRECT_AREA; + std::multimap RankMap; - if(!mapEntry->IsBattleGround()) - return SPELL_FAILED_REQUIRES_AREA; + for (uint32 ability_id=0;ability_idGetBattleGround(); - 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) + //get only spell with lowest ability_id to prevent doubles + uint32 spell_id=AbilityInfo->spellId; + bool found=false; + for (uint32 i=0; iIsBattleArena() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; + if (ChainedSpells.at(i)==spell_id) + found=true; } - 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 (found) + continue; - if(!mapEntry->IsBattleArena()) - return SPELL_FAILED_REQUIRES_AREA; + if(mSkillLineAbilityMap.lower_bound(spell_id)->second->id!=ability_id) + continue; + SpellEntry const *SpellInfo=sSpellStore.LookupEntry(spell_id); + if (!SpellInfo) + continue; + std::string sRank = SpellInfo->Rank[sWorld.GetDefaultDbcLocale()]; + if(sRank.empty()) + continue; + //exception to polymorph spells-make pig and turtle other chain than sheep + if ((SpellInfo->SpellFamilyName==SPELLFAMILY_MAGE) && (SpellInfo->SpellFamilyFlags[0] & 0x1000000) && (SpellInfo->SpellIconID!=82)) + continue; - BattleGround* bg = player->GetBattleGround(); - return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; + SpellRankEntry entry; + SpellRankValue value; + entry.SkillId=AbilityInfo->skillId; + entry.SpellName=SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()]; + entry.DurationIndex=SpellInfo->DurationIndex; + entry.RangeIndex=SpellInfo->rangeIndex; + entry.ProcFlags=SpellInfo->procFlags; + entry.SpellFamilyFlags=SpellInfo->SpellFamilyFlags; + entry.TargetAuraState=SpellInfo->TargetAuraState; + entry.SpellVisual=SpellInfo->SpellVisual[0]; + entry.ManaCost=SpellInfo->manaCost; + entry.CastingTimeIndex=0; + entry.TalentID=0; + for (;;) + { + AbilityInfo=mSkillLineAbilityMap.lower_bound(spell_id)->second; + value.Id=spell_id; + value.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()]; + value.strict=false; + RankMap.insert(std::pair(entry,value)); + spell_id=AbilityInfo->forward_spellid; + SpellInfo=sSpellStore.LookupEntry(spell_id); + if (!SpellInfo) + break; } } - return SPELL_CAST_OK; -} - -void SpellMgr::LoadSkillLineAbilityMap() -{ - mSkillLineAbilityMap.clear(); + barGoLink bar(RankMap.size()); - barGoLink bar( sSkillLineAbilityStore.GetNumRows() ); - uint32 count = 0; + uint32 count=0; - for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) + for (std::multimap::iterator itr = RankMap.begin();itr!=RankMap.end();) { - bar.step(); - SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i); - if(!SkillInfo) - continue; + SpellRankEntry entry=itr->first; + //trac errors in extracted data + std::multimap::iterator> RankErrorMap; + for (std::multimap::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++) + { + bar.step(); + RankErrorMap.insert(std::pair::iterator>(itr2->second.Rank,itr2)); + } - mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId,SkillInfo)); - ++count; - } + bool error=false; + //if strict == true strict check is not needed + if (!itr->second.strict) + //check for rank duplicates, if there are any do strict check + for (std::multimap::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();) + { + char const * err_entry=itr2->first; + uint32 rank_count=RankErrorMap.count(itr2->first); + if (rank_count>1) + { + error=true; + break; + } + else + itr2++; + } + bool allHaveTalents=true; + if (error) + { + std::list ConflictedSpells; + for (std::multimap::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2=RankMap.lower_bound(entry)) + { + ConflictedSpells.push_back(itr2->second.Id); + if (!GetTalentSpellPos(itr2->second.Id)) + allHaveTalents=false; + RankMap.erase(itr2); + } + SpellRankEntry nextEntry, currEntry; + for (;!ConflictedSpells.empty();ConflictedSpells.pop_front()) + { + SpellEntry const *SpellInfo=sSpellStore.LookupEntry(ConflictedSpells.front()); + currEntry.SkillId=entry.SkillId; + currEntry.SpellName=SpellInfo->SpellName[sWorld.GetDefaultDbcLocale()]; + currEntry.DurationIndex=SpellInfo->DurationIndex; + currEntry.RangeIndex=SpellInfo->rangeIndex; + currEntry.ProcFlags=SpellInfo->procFlags; + currEntry.SpellFamilyFlags=SpellInfo->SpellFamilyFlags; + //compare talents only when all spells from chain have entry + //to prevent wrong results with spells which have first rank talented and other not + if (allHaveTalents) + currEntry.TalentID=GetTalentSpellPos(ConflictedSpells.front())->talent_id; + else + currEntry.TalentID=0; + currEntry.TargetAuraState=SpellInfo->TargetAuraState; + currEntry.SpellVisual=SpellInfo->SpellVisual[0]; + currEntry.ManaCost=SpellInfo->manaCost; + + //compare effects and casting time + currEntry.CastingTimeIndex=SpellInfo->CastingTimeIndex; + currEntry.Effect[0]=SpellInfo->Effect[0]; + currEntry.Effect[1]=SpellInfo->Effect[1]; + currEntry.Effect[2]=SpellInfo->Effect[2]; + + currEntry.Aura[0]=SpellInfo->EffectApplyAuraName[0]; + currEntry.Aura[1]=SpellInfo->EffectApplyAuraName[1]; + currEntry.Aura[2]=SpellInfo->EffectApplyAuraName[2]; + + SpellRankValue currValue; + currValue.Id=ConflictedSpells.front(); + currValue.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()]; + currValue.strict=true; + RankMap.insert(std::pair(currEntry,currValue)); + } + itr=RankMap.begin(); + continue; + } + else + for (std::multimap::iterator>::iterator itr2 = RankErrorMap.begin();itr2!=RankErrorMap.end();) + { + char const * err_entry=itr2->first; + uint32 rank_count=RankErrorMap.count(itr2->first); + if (rank_count>1) + for (itr2 = RankErrorMap.lower_bound(err_entry);itr2!=RankErrorMap.upper_bound(err_entry);itr2++) + { + sLog.outDebug("There is a duplicate rank entry (%s) for spell: %u",itr2->first,itr2->second->second.Id); + if (!(itr2->second->second.Id==52375 || itr2->second->second.Id==45902)) + { + sLog.outDebug("Spell %u removed from chain data.",itr2->second->second.Id); + RankMap.erase(itr2->second); + } + } + else + itr2++; + } + + //order spells by spellLevel + std::list RankedSpells; + uint32 min_spell_lvl=0; + std::multimap::iterator min_itr; + for (;RankMap.count(entry);) + { + for (std::multimap::iterator itr2 = RankMap.lower_bound(entry);itr2!=RankMap.upper_bound(entry);itr2++) + { + SpellEntry const *SpellInfo=sSpellStore.LookupEntry(itr2->second.Id); + if (SpellInfo->spellLevelspellLevel; + min_itr=itr2; + } + } + RankedSpells.push_back(min_itr->second.Id); + RankMap.erase(min_itr); + } + + //use data from talent.dbc + uint16 talent_id=0; + for(std::list::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();) + { + if (TalentSpellPos const* TalentPos=GetTalentSpellPos(*itr2)) + { + talent_id=TalentPos->talent_id; + RankedSpells.erase(itr2); + itr2 = RankedSpells.begin(); + } + else + itr2++; + } + if (talent_id) + { + TalentEntry const *TalentInfo = sTalentStore.LookupEntry(talent_id); + for (uint8 rank=5;rank;rank--) + { + if (TalentInfo->RankID[rank-1]) + RankedSpells.push_front(TalentInfo->RankID[rank-1]); + } + } + + //do not proceed for spells with less than 2 ranks + itr=RankMap.begin(); + if (RankedSpells.size()<2) + continue; + + count++; + + uint32 spell_rank=1; + for(std::list::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();spell_rank++) + { + uint32 spell_id=*itr2; + mSpellChains[spell_id].rank=spell_rank; + mSpellChains[spell_id].first=RankedSpells.front(); + mSpellChains[spell_id].last=RankedSpells.back(); + + itr2++; + if (spell_rank<2) + mSpellChains[spell_id].prev=0; + + if (spell_id==RankedSpells.back()) + mSpellChains[spell_id].next=0; + else + { + mSpellChains[*itr2].prev=spell_id; + mSpellChains[spell_id].next=*itr2; + } + } + } + +//uncomment these two lines to print yourself list of spell_chains on startup + //for (UNORDERED_MAP::iterator itr=mSpellChains.begin();itr!=mSpellChains.end();itr++) + //sLog.outString( "Id: %u, Rank: %d , %s, %u, %u, %u, %u",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()], itr->second.first, itr->second.last,itr->second.next ,itr->second.prev); sLog.outString(); - sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count); + sLog.outString( ">> Loaded %u spell chains",count); } -DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered) +// set data in core for now +void SpellMgr::LoadSpellCustomAttr() { - // Explicit Diminishing Groups - switch(spellproto->SpellFamilyName) + mSpellCustomAttr.resize(GetSpellStore()->GetNumRows()); + + SpellEntry *spellInfo; + for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i) { - case SPELLFAMILY_ROGUE: + mSpellCustomAttr[i] = 0; + spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i); + if(!spellInfo) + continue; + + bool auraSpell = true; + for(uint32 j = 0; j < 3; ++j) { - // Kidney Shot - if (spellproto->SpellFamilyFlags[0] & 0x200000) - return DIMINISHING_KIDNEYSHOT; - // Sap - else if (spellproto->SpellFamilyFlags[0] & 0x80) - return DIMINISHING_POLYMORPH; - // Gouge - else if (spellproto->SpellFamilyFlags[0] & 0x8) - return DIMINISHING_POLYMORPH; - // Blind - else if (spellproto->SpellFamilyFlags[0] & 0x00001000000) - return DIMINISHING_BLIND_CYCLONE; + if(spellInfo->Effect[j]) + if(spellInfo->Effect[j] != SPELL_EFFECT_APPLY_AURA + || SpellTargetType[spellInfo->EffectImplicitTargetA[j]] != TARGET_TYPE_UNIT_TARGET) + //ignore target party for now + { + auraSpell = false; + break; + } + } + if(auraSpell) + mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_SPELL; + + for(uint32 j = 0; j < 3; ++j) + { + switch(spellInfo->EffectApplyAuraName[j]) + { + case SPELL_AURA_PERIODIC_DAMAGE: + case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: + case SPELL_AURA_PERIODIC_LEECH: + mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_DOT; + break; + case SPELL_AURA_PERIODIC_HEAL: + case SPELL_AURA_OBS_MOD_HEALTH: + mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_HOT; + break; + case SPELL_AURA_MOD_ROOT: + mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_CC; + mSpellCustomAttr[i] |= SPELL_ATTR_CU_MOVEMENT_IMPAIR; + break; + case SPELL_AURA_MOD_DECREASE_SPEED: + mSpellCustomAttr[i] |= SPELL_ATTR_CU_MOVEMENT_IMPAIR; + break; + default: + break; + } + + switch(spellInfo->Effect[j]) + { + case SPELL_EFFECT_SCHOOL_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + case SPELL_EFFECT_HEAL: + mSpellCustomAttr[i] |= SPELL_ATTR_CU_DIRECT_DAMAGE; + break; + case SPELL_EFFECT_CHARGE: + case SPELL_EFFECT_JUMP: + case SPELL_EFFECT_JUMP2: + case SPELL_EFFECT_138: + if(!spellInfo->speed && !spellInfo->SpellFamilyName) + spellInfo->speed = SPEED_CHARGE; + mSpellCustomAttr[i] |= SPELL_ATTR_CU_CHARGE; + break; + case SPELL_EFFECT_TRIGGER_SPELL: + if (SpellTargetType[spellInfo->EffectImplicitTargetA[j]]== TARGET_TYPE_DEST_CASTER || + SpellTargetType[spellInfo->EffectImplicitTargetA[j]]== TARGET_TYPE_DEST_TARGET || + SpellTargetType[spellInfo->EffectImplicitTargetA[j]]== TARGET_TYPE_DEST_DEST || + spellInfo->Targets & (TARGET_FLAG_SOURCE_LOCATION|TARGET_FLAG_DEST_LOCATION)) + spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE; + break; + } + + switch(SpellTargetType[spellInfo->EffectImplicitTargetA[j]]) + { + case TARGET_TYPE_UNIT_TARGET: + case TARGET_TYPE_DEST_TARGET: + spellInfo->Targets |= TARGET_FLAG_UNIT; + break; + } + } + + for(uint32 j = 0; j < 3; ++j) + { + switch(spellInfo->EffectApplyAuraName[j]) + { + case SPELL_AURA_MOD_POSSESS: + case SPELL_AURA_MOD_CONFUSE: + case SPELL_AURA_MOD_CHARM: + case SPELL_AURA_MOD_FEAR: + case SPELL_AURA_MOD_STUN: + mSpellCustomAttr[i] |= SPELL_ATTR_CU_AURA_CC; + mSpellCustomAttr[i] &= ~SPELL_ATTR_CU_MOVEMENT_IMPAIR; + break; + } + } + + if(spellInfo->SpellVisual[0] == 3879) + mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_BACK; + + switch(i) + { + case 26029: // dark glare + case 37433: // spout + case 43140: case 43215: // flame breath + mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_LINE; + break; + case 24340: case 26558: case 28884: // Meteor + case 36837: case 38903: case 41276: // Meteor + case 26789: // Shard of the Fallen Star + case 31436: // Malevolent Cleave + case 35181: // Dive Bomb + case 40810: case 43267: case 43268: // Saber Lash + case 42384: // Brutal Swipe + case 45150: // Meteor Slash + mSpellCustomAttr[i] |= SPELL_ATTR_CU_SHARE_DAMAGE; + break; + case 27820: + mSpellCustomAttr[i] |= SPELL_ATTR_CU_EXCLUDE_SELF; + break; + case 44978: case 45001: case 45002: // Wild Magic + case 45004: case 45006: case 45010: // Wild Magic + case 31347: // Doom + case 41635: // Prayer of Mending + case 44869: // Spectral Blast + case 45027: // Revitalize + case 45976: // Muru Portal Channel + case 39365: // Thundering Storm + case 41071: // Raise Dead (HACK) + spellInfo->MaxAffectedTargets = 1; + break; + case 41376: // Spite + case 39992: // Needle Spine + case 29576: // Multi-Shot + case 40816: // Saber Lash + case 37790: // Spread Shot + case 46771: // Flame Sear + case 45248: // Shadow Blades + case 41303: // Soul Drain + case 54172: // Divine Storm (heal) + case 29213: // Curse of the Plaguebringer - Noth + case 28542: // Life Drain - Sapphiron + spellInfo->MaxAffectedTargets = 3; + break; + case 38310: //Multi-Shot + spellInfo->MaxAffectedTargets = 4; + break; + case 42005: // Bloodboil + case 38296: // Spitfire Totem + case 37676: // Insidious Whisper + case 46009: // Negative Energy + case 45641: // Fire Bloom + case 54937: // Glyph of Holy Light + case 55665: // Life Drain - Sapphiron (H) + case 28796: // Poison Bolt Volly - Faerlina + spellInfo->MaxAffectedTargets = 5; break; - } - case SPELLFAMILY_WARLOCK: - { - // Death Coil - if (spellproto->SpellFamilyFlags[0] & 0x00000080000) - return DIMINISHING_DEATHCOIL; - // Seduction - if (spellproto->SpellFamilyFlags[0] & 0x00040000000) - return DIMINISHING_FEAR; - // Fear - //else if (spellproto->SpellFamilyFlags & 0x40840000000LL) - // return DIMINISHING_WARLOCK_FEAR; - // Curses/etc - else if (spellproto->SpellFamilyFlags[0] & 0x80000000) - return DIMINISHING_LIMITONLY; + case 40827: // Sinful Beam + case 40859: // Sinister Beam + case 40860: // Vile Beam + case 40861: // Wicked Beam + case 57669: // Replenishment + case 54835: // Curse of the Plaguebringer - Noth (H) + case 54098: // Poison Bolt Volly - Faerlina (H) + spellInfo->MaxAffectedTargets = 10; break; - } - case SPELLFAMILY_DRUID: - { - // Cyclone - if (spellproto->SpellFamilyFlags[1] & 0x020) - return DIMINISHING_BLIND_CYCLONE; + case 8122: case 8124: case 10888: case 10890: // Psychic Scream + case 12494: // Frostbite + spellInfo->Attributes |= SPELL_ATTR_BREAKABLE_BY_DAMAGE; break; - } - case SPELLFAMILY_WARRIOR: - { - // Hamstring - limit duration to 10s in PvP - if (spellproto->SpellFamilyFlags[0] & 0x00000000002) - return DIMINISHING_LIMITONLY; + case 38794: case 33711: //Murmur's Touch + spellInfo->MaxAffectedTargets = 1; + spellInfo->EffectTriggerSpell[0] = 33760; + break; + case 1122: // Inferno + case 18662: // Curse of Doom + spellInfo->EffectBasePoints[0] = 0; //prevent summon too many of them + break; + case 17941: // Shadow Trance + case 22008: // Netherwind Focus + case 31834: // Light's Grace + case 34754: // Clearcasting + case 34936: // Backlash + case 48108: // Hot Streak + case 51124: // Killing Machine + case 54741: // Firestarter + case 57761: // Fireball! + case 39805: // Lightning Overload + case 52437: // Sudden Death + spellInfo->procCharges=1; + break; + case 44544: // Fingers of Frost + spellInfo->procCharges=2; + break; + case 28200: // Ascendance (Talisman of Ascendance trinket) + spellInfo->procCharges=6; break; - } default: break; - } + } - // Get by mechanic - uint32 mechanic = GetAllSpellMechanicMask(spellproto); - if (mechanic == MECHANIC_NONE) return DIMINISHING_NONE; - if (mechanic & (1<SpellFamilyName) + { + case SPELLFAMILY_DRUID: + // starfall + if(spellInfo->SpellFamilyFlags[2] & 0x100) + spellInfo->MaxAffectedTargets = 2; + // Wild growth + else if(spellInfo->SpellFamilyFlags[1] & 0x4000000) + spellInfo->MaxAffectedTargets = 5; + break; + // circle of healing + case SPELLFAMILY_PRIEST: + if(spellInfo->SpellFamilyFlags[0] & 0x10000000) + spellInfo->MaxAffectedTargets = 5; + break; + } + } + SummonPropertiesEntry *properties = const_cast(sSummonPropertiesStore.LookupEntry(121)); + properties->Type = SUMMON_TYPE_TOTEM; - return DIMINISHING_NONE; + CreatureAI::FillAISpellInfo(); } -bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group) +bool SpellMgr::IsSkillTypeSpell(uint32 spellId, SkillType type) const { - switch(group) + SkillLineAbilityMap::const_iterator lower = GetBeginSkillLineAbilityMap(spellId); + SkillLineAbilityMap::const_iterator upper = GetEndSkillLineAbilityMap(spellId); + for (;lower!=upper;++lower) { - case DIMINISHING_CONTROL_STUN: - case DIMINISHING_TRIGGER_STUN: - case DIMINISHING_KIDNEYSHOT: - case DIMINISHING_SLEEP: - case DIMINISHING_CONTROL_ROOT: - case DIMINISHING_TRIGGER_ROOT: - case DIMINISHING_FEAR: - case DIMINISHING_WARLOCK_FEAR: - case DIMINISHING_CHARM: - case DIMINISHING_POLYMORPH: - case DIMINISHING_FREEZE: - case DIMINISHING_KNOCKOUT: - case DIMINISHING_BLIND_CYCLONE: - case DIMINISHING_BANISH: - case DIMINISHING_LIMITONLY: + if (lower->second->skillId==type) return true; - default: - return false; } return false; } -DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) +void SpellMgr::LoadSpellLinked() { - switch(group) - { - case DIMINISHING_BLIND_CYCLONE: - case DIMINISHING_CONTROL_STUN: - case DIMINISHING_TRIGGER_STUN: - case DIMINISHING_KIDNEYSHOT: - return DRTYPE_ALL; - case DIMINISHING_SLEEP: - case DIMINISHING_CONTROL_ROOT: - case DIMINISHING_TRIGGER_ROOT: - case DIMINISHING_FEAR: - case DIMINISHING_CHARM: - case DIMINISHING_POLYMORPH: - case DIMINISHING_SILENCE: - case DIMINISHING_DISARM: - case DIMINISHING_DEATHCOIL: - case DIMINISHING_FREEZE: - case DIMINISHING_BANISH: - case DIMINISHING_WARLOCK_FEAR: - case DIMINISHING_KNOCKOUT: - return DRTYPE_PLAYER; - default: - break; - } - - return DRTYPE_NONE; -} + mSpellLinkedMap.clear(); // need for reload case + uint32 count = 0; -bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const -{ - if(gender!=GENDER_NONE) + // 0 1 2 + QueryResult *result = WorldDatabase.Query("SELECT spell_trigger, spell_effect, type FROM spell_linked_spell"); + if( !result ) { - // not in expected gender - if(!player || gender != player->getGender()) - return false; + barGoLink bar( 1 ); + bar.step(); + sLog.outString(); + sLog.outString( ">> Loaded %u linked spells", count ); + return; } - if(raceMask) - { - // not in expected race - if(!player || !(raceMask & player->getRaceMask())) - return false; - } + barGoLink bar( result->GetRowCount() ); - if(areaId) + do { - // not in expected zone - if(newZone!=areaId && newArea!=areaId) - return false; - } + Field *fields = result->Fetch(); - if(questStart) - { - // not in expected required quest state - if(!player || (!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)) - return false; - } + bar.step(); - if(questEnd) - { - // not in expected forbidden quest state - if(!player || player->GetQuestRewardStatus(questEnd)) - return false; - } + int32 trigger = fields[0].GetInt32(); + int32 effect = fields[1].GetInt32(); + int32 type = fields[2].GetInt32(); - if(auraSpell) - { - // not have expected aura - if(!player) - return false; - if(auraSpell > 0) - // have expected aura - return player->HasAura(auraSpell); + SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(trigger)); + if (!spellInfo) + { + sLog.outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(trigger)); + continue; + } + spellInfo = sSpellStore.LookupEntry(abs(effect)); + if (!spellInfo) + { + sLog.outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(effect)); + continue; + } + + if(trigger > 0) + { + switch(type) + { + case 0: mSpellCustomAttr[trigger] |= SPELL_ATTR_CU_LINK_CAST; break; + case 1: mSpellCustomAttr[trigger] |= SPELL_ATTR_CU_LINK_HIT; break; + case 2: mSpellCustomAttr[trigger] |= SPELL_ATTR_CU_LINK_AURA; break; + } + } else - // not have expected aura - return !player->HasAura(-auraSpell); - } + { + mSpellCustomAttr[-trigger] |= SPELL_ATTR_CU_LINK_REMOVE; + } - return true; + if(type) //we will find a better way when more types are needed + { + if(trigger > 0) + trigger += SPELL_LINKED_MAX_SPELLS * type; + else + trigger -= SPELL_LINKED_MAX_SPELLS * type; + } + mSpellLinkedMap[trigger].push_back(effect); + + ++count; + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u linked spells", count ); } + -- cgit v1.2.3