aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/game/Chat.cpp1
-rw-r--r--src/game/Chat.h1
-rw-r--r--src/game/Level3.cpp9
-rw-r--r--src/game/Player.cpp103
-rw-r--r--src/game/Spell.cpp4
-rw-r--r--src/game/SpellAuras.cpp31
-rw-r--r--src/game/SpellMgr.cpp314
-rw-r--r--src/game/SpellMgr.h65
-rw-r--r--src/game/World.cpp3
-rw-r--r--src/shared/revision_nr.h2
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__