diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/game/Chat.cpp | 1 | ||||
-rw-r--r-- | src/game/Chat.h | 1 | ||||
-rw-r--r-- | src/game/Level3.cpp | 9 | ||||
-rw-r--r-- | src/game/Player.cpp | 103 | ||||
-rw-r--r-- | src/game/Spell.cpp | 4 | ||||
-rw-r--r-- | src/game/SpellAuras.cpp | 31 | ||||
-rw-r--r-- | src/game/SpellMgr.cpp | 314 | ||||
-rw-r--r-- | src/game/SpellMgr.h | 65 | ||||
-rw-r--r-- | src/game/World.cpp | 3 | ||||
-rw-r--r-- | src/shared/revision_nr.h | 2 |
10 files changed, 412 insertions, 121 deletions
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index a028fdc860f..cc4f71890a0 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -317,6 +317,7 @@ ChatCommand * ChatHandler::getCommandTable() { "skinning_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesSkinningCommand, "", NULL }, { "spell_affect", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellAffectCommand, "", NULL }, { "spell_required", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellRequiredCommand, "", NULL }, + { "spell_area", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellLearnSpellCommand, "", NULL }, { "spell_elixir", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellElixirCommand, "", NULL }, { "spell_learn_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellLearnSpellCommand, "", NULL }, { "spell_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesSpellCommand, "", NULL }, diff --git a/src/game/Chat.h b/src/game/Chat.h index 6303a37c773..219dd8c1a51 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -272,6 +272,7 @@ class ChatHandler bool HandleReloadSkillFishingBaseLevelCommand(const char* args); bool HandleReloadSpellAffectCommand(const char* args); bool HandleReloadSpellRequiredCommand(const char* args); + bool HandleReloadSpellAreaCommand(const char* args); bool HandleReloadSpellElixirCommand(const char* args); bool HandleReloadSpellLearnSpellCommand(const char* args); bool HandleReloadSpellProcEventCommand(const char* args); diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index b00ef1764d2..998360b1c86 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -511,6 +511,7 @@ bool ChatHandler::HandleReloadAllSpellCommand(const char*) HandleReloadSkillExtraItemTemplateCommand("a"); HandleReloadSpellAffectCommand("a"); HandleReloadSpellRequiredCommand("a"); + HandleReloadSpellAreaCommand("a"); HandleReloadSpellElixirCommand("a"); HandleReloadSpellLearnSpellCommand("a"); HandleReloadSpellProcEventCommand("a"); @@ -821,6 +822,14 @@ bool ChatHandler::HandleReloadSpellAffectCommand(const char*) return true; } +bool ChatHandler::HandleReloadSpellAreaCommand(const char*) +{ + sLog.outString( "Re-Loading SpellArea Data..." ); + spellmgr.LoadSpellAreas(); + SendGlobalSysMessage("DB table `spell_area` (spell dependences from area/quest/auras state) reloaded."); + return true; +} + bool ChatHandler::HandleReloadSpellRequiredCommand(const char*) { sLog.outString( "Re-Loading Spell Required Data... " ); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index a41abff3a78..2f1c4aeeb75 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -12976,6 +12976,19 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) if(questGiver && pQuest->GetQuestStartScript()!=0) sWorld.ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this); + // Some spells applied at quest activation + SpellAreaForQuestMapBounds saBounds = spellmgr.GetSpellAreaForQuestMapBounds(quest_id,true); + if(saBounds.first != saBounds.second) + { + uint32 zone = GetZoneId(); + uint32 area = GetAreaId(); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(itr->second->autocast && itr->second->IsFitToRequirements(this,zone,area)) + if( !HasAura(itr->second->spellId,0) ) + CastSpell(this,itr->second->spellId,true); + } + UpdateForQuestsGO(); } @@ -13166,6 +13179,34 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED; GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST); + + uint32 zone = 0; + uint32 area = 0; + + // remove auras from spells with quest reward state limitations + SpellAreaForQuestMapBounds saEndBounds = spellmgr.GetSpellAreaForQuestEndMapBounds(quest_id); + if(saEndBounds.first != saEndBounds.second) + { + uint32 zone = GetZoneId(); + uint32 area = GetAreaId(); + + for(SpellAreaForAreaMap::const_iterator itr = saEndBounds.first; itr != saEndBounds.second; ++itr) + if(!itr->second->IsFitToRequirements(this,zone,area)) + RemoveAurasDueToSpell(itr->second->spellId); + } + + // Some spells applied at quest reward + SpellAreaForQuestMapBounds saBounds = spellmgr.GetSpellAreaForQuestMapBounds(quest_id,false); + if(saBounds.first != saBounds.second) + { + if(!zone) zone = GetZoneId(); + if(!area) area = GetAreaId(); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(itr->second->autocast && itr->second->IsFitToRequirements(this,zone,area)) + if( !HasAura(itr->second->spellId,0) ) + CastSpell(this,itr->second->spellId,true); + } } void Player::FailQuest( uint32 quest_id ) @@ -19549,26 +19590,12 @@ void Player::UpdateZoneDependentAuras( uint32 newZone ) RemoveSpellsCausingAura(SPELL_AURA_FLY); } - // Some spells applied at enter into zone (with subzones) - switch(newZone) - { - case 2367: // Old Hillsbrad Foothills - { - // Human Illusion - // NOTE: these are removed by RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP); - uint32 spellid = 0; - // all horde races - if( GetTeam() == HORDE ) - spellid = getGender() == GENDER_FEMALE ? 35481 : 35480; - // and some alliance races - else if( getRace() == RACE_NIGHTELF || getRace() == RACE_DRAENEI ) - spellid = getGender() == GENDER_FEMALE ? 35483 : 35482; - - if(spellid && !HasAura(spellid,0) ) - CastSpell(this,spellid,true); - break; - } - } + // Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAreaMapBounds(newZone); + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(itr->second->autocast && itr->second->IsFitToRequirements(this,newZone,0)) + if( !HasAura(itr->second->spellId,0) ) + CastSpell(this,itr->second->spellId,true); } void Player::UpdateAreaDependentAuras( uint32 newArea ) @@ -19577,42 +19604,18 @@ void Player::UpdateAreaDependentAuras( uint32 newArea ) for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();) { // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date - if(GetSpellAllowedInLocationError(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea,GetBattleGroundId())!=0) + if(spellmgr.GetSpellAllowedInLocationError(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea,this)!=0) RemoveAura(iter); else ++iter; } // some auras applied at subzone enter - switch(newArea) - { - // Dragonmaw Illusion - case 3759: // Netherwing Ledge - case 3939: // Dragonmaw Fortress - case 3966: // Dragonmaw Base Camp - if( GetDummyAura(40214) ) - { - if( !HasAura(40216,0) ) - CastSpell(this,40216,true); - if( !HasAura(42016,0) ) - CastSpell(this,42016,true); - } - break; - // Dominion Over Acherus - case 4281: // Acherus: The Ebon Hold - case 4342: // Acherus: The Ebon Hold - if( HasSpell(51721) ) - if( !HasAura(51721,0) ) - CastSpell(this,51721,true); - break; - // Mist of the Kvaldir - case 4028: //Riplash Strand - case 4029: //Riplash Ruins - case 4106: //Garrosh's Landing - case 4031: //Pal'ea - CastSpell(this,54119,true); - break; - } + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAreaMapBounds(newArea); + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(itr->second->autocast && itr->second->IsFitToRequirements(this,m_zoneUpdateId,newArea)) + if( !HasAura(itr->second->spellId,0) ) + CastSpell(this,itr->second->spellId,true); } uint32 Player::GetCorpseReclaimDelay(bool pvp) const diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index be224438257..3269f91dee6 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -3779,8 +3779,8 @@ uint8 Spell::CanCast(bool strict) return SPELL_FAILED_NOT_IN_ARENA; // zone check - if (uint8 res= GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId(), - m_caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)m_caster)->GetBattleGroundId() : 0)) + if (uint8 res= spellmgr.GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId(), + m_caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)m_caster) : NULL)) return res; // not let players cast spells at mount (and let do it to creatures) diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index d31ec2ff553..49cd9426f1c 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -1894,6 +1894,22 @@ void Aura::HandleAuraDummy(bool apply, bool Real) m_modifier.m_amount = caster->SpellHealingBonus(m_target, GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE); return; } + + // some auras applied at aura apply + if(GetEffIndex()==0 && m_target->GetTypeId()==TYPEID_PLAYER) + { + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAuraMapBounds(GetId()); + if(saBounds.first != saBounds.second) + { + uint32 zone = m_target->GetZoneId(); + uint32 area = m_target->GetAreaId(); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(itr->second->autocast && itr->second->IsFitToRequirements((Player*)m_target,zone,area)) + if( !m_target->HasAura(itr->second->spellId,0) ) + m_target->CastSpell(m_target,itr->second->spellId,true); + } + } } // AT REMOVE else @@ -1977,6 +1993,21 @@ void Aura::HandleAuraDummy(bool apply, bool Real) } } + + // some auras remove at aura remove + if(GetEffIndex()==0 && m_target->GetTypeId()==TYPEID_PLAYER) + { + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAuraMapBounds(GetId()); + if(saBounds.first != saBounds.second) + { + uint32 zone = m_target->GetZoneId(); + uint32 area = m_target->GetAreaId(); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if(!itr->second->IsFitToRequirements((Player*)m_target,zone,area)) + m_target->RemoveAurasDueToSpell(itr->second->spellId); + } + } } // AT APPLY & REMOVE diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 95a52acf57c..8a0eb06ab11 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -2649,7 +2649,192 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg) return true; } -uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id, uint32 bgInstanceId) +void SpellMgr::LoadSpellAreas() +{ + mSpellAreaMap.clear(); // need for reload case + mSpellAreaForQuestMap.clear(); + mSpellAreaForActiveQuestMap.clear(); + mSpellAreaForQuestEndMap.clear(); + mSpellAreaForAuraMap.clear(); + + uint32 count = 0; + + // 0 1 2 3 4 5 6 7 8 + QueryResult *result = WorldDatabase.Query("SELECT spell, area, quest_start, quest_start_active, quest_end, aura_spell, racemask, gender, autocast FROM spell_area"); + + if( !result ) + { + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u spell area requirements", count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field *fields = result->Fetch(); + + bar.step(); + + uint32 spell = fields[0].GetUInt32(); + SpellArea spellArea; + spellArea.spellId = spell; + spellArea.areaId = fields[1].GetUInt32(); + spellArea.questStart = fields[2].GetUInt32(); + spellArea.questStartCanActive = fields[3].GetBool(); + spellArea.questEnd = fields[4].GetUInt32(); + spellArea.auraSpell = fields[5].GetUInt32(); + spellArea.raceMask = fields[6].GetUInt32(); + spellArea.gender = Gender(fields[7].GetUInt8()); + + if(!sSpellStore.LookupEntry(spell)) + { + sLog.outErrorDb("Spell %u listed in `spell_area` does not exist", spell); + continue; + } + + if(mSpellAreaForAuraMap.find(spellArea.spellId)!=mSpellAreaForAuraMap.end()) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that already listed in table itself", spell,spellArea.auraSpell); + continue; + } + + if(spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId)) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong area (%u) requirement", spell,spellArea.areaId); + continue; + } + + if(spellArea.questStart && !objmgr.GetQuestTemplate(spellArea.questStart)) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong start quest (%u) requirement", spell,spellArea.questStart); + continue; + } + + if(spellArea.questEnd) + { + if(!objmgr.GetQuestTemplate(spellArea.questEnd)) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell,spellArea.questEnd); + continue; + } + + if(spellArea.questEnd==spellArea.questStart && !spellArea.questStartCanActive) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell,spellArea.questEnd); + continue; + } + } + + if(spellArea.auraSpell) + { + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellArea.auraSpell); + if(!spellInfo) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,spellArea.auraSpell); + continue; + } + + if(spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_DUMMY) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy aura in effect 0", spell,spellArea.auraSpell); + continue; + } + + if(spellArea.auraSpell==spellArea.spellId) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement for itself", spell,spellArea.auraSpell); + continue; + } + + // not allow autocast chains by auraSpell field + if(spellArea.autocast) + { + bool chain = false; + SpellAreaForAuraMapBounds saBound = GetSpellAreaForAuraMapBounds(spellArea.spellId); + for(SpellAreaForAuraMap::const_iterator itr = saBound.first; itr != saBound.second; ++itr) + { + if(itr->second->autocast) + { + chain = true; + break; + } + } + + if(chain) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell); + continue; + } + + SpellAreaMapBounds saBound2 = GetSpellAreaMapBounds(spellArea.auraSpell); + for(SpellAreaMap::const_iterator itr2 = saBound2.first; itr2 != saBound2.second; ++itr2) + { + if(itr2->second.autocast && itr2->second.auraSpell) + { + chain = true; + break; + } + } + + if(chain) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell); + continue; + } + } + } + + if(spellArea.raceMask && (spellArea.raceMask & RACEMASK_ALL_PLAYABLE)==0) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong race mask (%u) requirement", spell,spellArea.raceMask); + continue; + } + + if(spellArea.gender!=GENDER_NONE && spellArea.gender!=GENDER_FEMALE && spellArea.gender!=GENDER_MALE) + { + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong gender (%u) requirement", spell,spellArea.gender); + continue; + } + + SpellArea const* sa = &mSpellAreaMap.insert(SpellAreaMap::value_type(spell,spellArea))->second; + + // for search by current zone/subzone at zone/subzone change + if(spellArea.areaId) + mSpellAreaForAreaMap.insert(SpellAreaForAreaMap::value_type(spellArea.areaId,sa)); + + // for search at quest start/reward + if(spellArea.questStart) + { + if(spellArea.questStartCanActive) + mSpellAreaForActiveQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa)); + else + mSpellAreaForQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa)); + } + + // for search at quest start/reward + if(spellArea.questEnd) + mSpellAreaForQuestEndMap.insert(SpellAreaForQuestMap::value_type(spellArea.questEnd,sa)); + + // for search at aura apply + if(spellArea.auraSpell) + mSpellAreaForAuraMap.insert(SpellAreaForAuraMap::value_type(spellArea.auraSpell,sa)); + + ++count; + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u spell area requirements", count ); +} + +uint8 SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player) { // normal case if( spellInfo->AreaGroupId > 0) @@ -2668,75 +2853,26 @@ uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo,uint32 map_id,u return SPELL_FAILED_INCORRECT_AREA; } - // elixirs (all area dependent elixirs have family SPELLFAMILY_POTION, use this for speedup) - if(spellInfo->SpellFamilyName==SPELLFAMILY_POTION) + // 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) { - if(uint32 mask = spellmgr.GetSpellElixirMask(spellInfo->Id)) + for(SpellAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) { - if(mask & ELIXIR_BATTLE_MASK) - { - if(spellInfo->Id==45373) // Bloodberry Elixir - return zone_id==4075 ? 0 : SPELL_FAILED_REQUIRES_AREA; - } - if(mask & ELIXIR_UNSTABLE_MASK) - { - // in the Blade's Edge Mountains Plateaus and Gruul's Lair. - return zone_id ==3522 || map_id==565 ? 0 : SPELL_FAILED_INCORRECT_AREA; - } - if(mask & ELIXIR_SHATTRATH_MASK) - { - // in Tempest Keep, Serpentshrine Cavern, Caverns of Time: Mount Hyjal, Black Temple, Sunwell Plateau - if(zone_id ==3607 || map_id==534 || map_id==564 || zone_id==4075) - return 0; - - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if(!mapEntry) - return SPELL_FAILED_INCORRECT_AREA; - - return mapEntry->multimap_id==206 ? 0 : SPELL_FAILED_INCORRECT_AREA; - } - - // elixirs not have another limitations - return 0; + if(itr->second.IsFitToRequirements(player,zone_id,area_id)) + return 0; } + return SPELL_FAILED_INCORRECT_AREA; } - // special cases zone check (maps checked by multimap common id) + // bg spell checks switch(spellInfo->Id) { - case 41618: // Bottled Nethergon Energy - case 41620: // Bottled Nethergon Vapor - { - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if(!mapEntry) - return SPELL_FAILED_INCORRECT_AREA; - - return mapEntry->multimap_id==206 ? 0 : SPELL_FAILED_REQUIRES_AREA; - } - case 41617: // Cenarion Mana Salve - case 41619: // Cenarion Healing Salve - { - MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); - if(!mapEntry) - return SPELL_FAILED_INCORRECT_AREA; - - return mapEntry->multimap_id==207 ? 0 : SPELL_FAILED_REQUIRES_AREA; - } - case 40216: // Dragonmaw Illusion - case 42016: // Dragonmaw Illusion - return area_id == 3759 || area_id == 3966 || area_id == 3939 ? 0 : SPELL_FAILED_INCORRECT_AREA; - case 51721: // Dominion Over Acherus - case 54055: // Dominion Over Acherus - return area_id == 4281 || area_id == 4342 ? 0 : SPELL_FAILED_INCORRECT_AREA; - case 51852: // The Eye of Acherus - return map_id == 609 ? 0 : SPELL_FAILED_REQUIRES_AREA; - case 54119: // Mist of the Kvaldir - return area_id == 4028 || area_id == 4029 || area_id == 4106 || area_id == 4031 ? 0 : SPELL_FAILED_INCORRECT_AREA; case 23333: // Warsong Flag case 23335: // Silverwing Flag - return map_id == 489 && bgInstanceId ? 0 : SPELL_FAILED_REQUIRES_AREA; + return map_id == 489 && player && player->InBattleGround() ? 0 : SPELL_FAILED_REQUIRES_AREA; case 34976: // Netherstorm Flag - return map_id == 566 && bgInstanceId ? 0 : SPELL_FAILED_REQUIRES_AREA; + return map_id == 566 && player && player->InBattleGround() ? 0 : SPELL_FAILED_REQUIRES_AREA; case 2584: // Waiting to Resurrect case 22011: // Spirit Heal Channel case 22012: // Spirit Heal @@ -2749,11 +2885,11 @@ uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo,uint32 map_id,u if(!mapEntry) return SPELL_FAILED_INCORRECT_AREA; - return mapEntry->IsBattleGround() && bgInstanceId ? 0 : SPELL_FAILED_REQUIRES_AREA; + return mapEntry->IsBattleGround() && player && player->InBattleGround() ? 0 : SPELL_FAILED_REQUIRES_AREA; } case 44521: // Preparation { - if(!bgInstanceId) + if(!player) return SPELL_FAILED_REQUIRES_AREA; MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); @@ -2763,7 +2899,7 @@ uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo,uint32 map_id,u if(!mapEntry->IsBattleGround()) return SPELL_FAILED_REQUIRES_AREA; - BattleGround* bg = sBattleGroundMgr.GetBattleGround(bgInstanceId); + BattleGround* bg = player->GetBattleGround(); return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? 0 : SPELL_FAILED_REQUIRES_AREA; } case 32724: // Gold Team (Alliance) @@ -2775,11 +2911,11 @@ uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo,uint32 map_id,u if(!mapEntry) return SPELL_FAILED_INCORRECT_AREA; - return mapEntry->IsBattleArena() && bgInstanceId ? 0 : SPELL_FAILED_REQUIRES_AREA; + return mapEntry->IsBattleArena() && player && player->InBattleGround() ? 0 : SPELL_FAILED_REQUIRES_AREA; } case 32727: // Arena Preparation { - if(!bgInstanceId) + if(!player) return SPELL_FAILED_REQUIRES_AREA; MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); @@ -2789,7 +2925,7 @@ uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo,uint32 map_id,u if(!mapEntry->IsBattleArena()) return SPELL_FAILED_REQUIRES_AREA; - BattleGround* bg = sBattleGroundMgr.GetBattleGround(bgInstanceId); + BattleGround* bg = player->GetBattleGround(); return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? 0 : SPELL_FAILED_REQUIRES_AREA; } } @@ -2946,3 +3082,49 @@ DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) return DRTYPE_NONE; } +bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const +{ + if(gender!=GENDER_NONE) + { + // not in expected gender + if(!player || gender != player->getGender()) + return false; + } + + if(raceMask) + { + // not in expected race + if(!player || !(raceMask & player->getRaceMask())) + return false; + } + + if(areaId) + { + // not in expected zone + if(newZone!=areaId && newArea!=areaId) + return false; + } + + if(questStart) + { + // not in expected required quest state + if(!player || (!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)) + return false; + } + + if(questEnd) + { + // not in expected forbidden quest state + if(!player || player->GetQuestRewardStatus(questEnd)) + return false; + } + + if(auraSpell) + { + // not have expected aura + if(!player || !player->HasAura(auraSpell,0)) + return false; + } + + return true; +} diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 242390477ec..e041f67c358 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -408,8 +408,6 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId); -uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id,uint32 bgInstanceId); - extern bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS]; inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo) @@ -715,6 +713,32 @@ class PetAura }; typedef std::map<uint16, PetAura> SpellPetAuraMap; +struct SpellArea +{ + uint32 spellId; + uint32 areaId; // zone/subzone/or 0 is not limited to zone + uint32 questStart; // quest start (quest must be active or rewarded for spell apply) + uint32 questEnd; // quest end (quest don't must be rewarded for spell apply) + uint32 auraSpell; // spell aura must be applied for spell apply + uint32 raceMask; // can be applied only to races + Gender gender; // can be applied only to gender + bool questStartCanActive; // if true then quest start can be active (not only rewarded) + bool autocast; // if true then auto applied at area enter, in other case just allowed to cast + + // helpers + bool IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const; +}; + +typedef std::multimap<uint32,SpellArea> SpellAreaMap; +typedef std::multimap<uint32,SpellArea const*> SpellAreaForQuestMap; +typedef std::multimap<uint32,SpellArea const*> SpellAreaForAuraMap; +typedef std::multimap<uint32,SpellArea const*> SpellAreaForAreaMap; +typedef std::pair<SpellAreaMap::const_iterator,SpellAreaMap::const_iterator> SpellAreaMapBounds; +typedef std::pair<SpellAreaForQuestMap::const_iterator,SpellAreaForQuestMap::const_iterator> SpellAreaForQuestMapBounds; +typedef std::pair<SpellAreaForAuraMap::const_iterator, SpellAreaForAuraMap::const_iterator> SpellAreaForAuraMapBounds; +typedef std::pair<SpellAreaForAreaMap::const_iterator, SpellAreaForAreaMap::const_iterator> SpellAreaForAreaMapBounds; + + // Spell rank chain (accessed using SpellMgr functions) struct SpellChainNode { @@ -1068,6 +1092,36 @@ class SpellMgr return NULL; } + uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = NULL); + + SpellAreaMapBounds GetSpellAreaMapBounds(uint32 spell_id) const + { + return SpellAreaMapBounds(mSpellAreaMap.lower_bound(spell_id),mSpellAreaMap.upper_bound(spell_id)); + } + + SpellAreaForQuestMapBounds GetSpellAreaForQuestMapBounds(uint32 quest_id, bool active) const + { + if(active) + return SpellAreaForQuestMapBounds(mSpellAreaForActiveQuestMap.lower_bound(quest_id),mSpellAreaForActiveQuestMap.upper_bound(quest_id)); + else + return SpellAreaForQuestMapBounds(mSpellAreaForQuestMap.lower_bound(quest_id),mSpellAreaForQuestMap.upper_bound(quest_id)); + } + + SpellAreaForQuestMapBounds GetSpellAreaForQuestEndMapBounds(uint32 quest_id) const + { + return SpellAreaForQuestMapBounds(mSpellAreaForQuestEndMap.lower_bound(quest_id),mSpellAreaForQuestEndMap.upper_bound(quest_id)); + } + + SpellAreaForAuraMapBounds GetSpellAreaForAuraMapBounds(uint32 spell_id) const + { + return SpellAreaForAuraMapBounds(mSpellAreaForAuraMap.lower_bound(spell_id),mSpellAreaForAuraMap.upper_bound(spell_id)); + } + + SpellAreaForAreaMapBounds GetSpellAreaForAreaMapBounds(uint32 area_id) const + { + return SpellAreaForAreaMapBounds(mSpellAreaForAreaMap.lower_bound(area_id),mSpellAreaForAreaMap.upper_bound(area_id)); + } + // Modifiers public: static SpellMgr& Instance(); @@ -1089,6 +1143,7 @@ class SpellMgr void LoadSpellCustomAttr(); void LoadSpellLinked(); void LoadPetLevelupSpellMap(); + void LoadSpellAreas(); private: SpellScriptTarget mSpellScriptTarget; @@ -1107,6 +1162,12 @@ class SpellMgr SpellCustomAttribute mSpellCustomAttr; SpellLinkedMap mSpellLinkedMap; PetLevelupSpellMap mPetLevelupSpellMap; + SpellAreaMap mSpellAreaMap; + SpellAreaForQuestMap mSpellAreaForQuestMap; + SpellAreaForQuestMap mSpellAreaForActiveQuestMap; + SpellAreaForQuestMap mSpellAreaForQuestEndMap; + SpellAreaForAuraMap mSpellAreaForAuraMap; + SpellAreaForAreaMap mSpellAreaForAreaMap; }; #define spellmgr SpellMgr::Instance() diff --git a/src/game/World.cpp b/src/game/World.cpp index dd9beb10efb..1e9923cc065 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -1264,6 +1264,9 @@ void World::SetInitialWorldSettings() sLog.outString( ">>> Quests Relations loaded" ); sLog.outString(); + sLog.outString( "Loading SpellArea Data..." ); // must be after quest load + spellmgr.LoadSpellAreas(); + sLog.outString( "Loading AreaTrigger definitions..." ); objmgr.LoadAreaTriggerTeleports(); // must be after item template load diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index ede9681cf62..201f685b2fa 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "7348" + #define REVISION_NR "7349" #endif // __REVISION_NR_H__ |