[7349] Implement spell cast depenences from area/quest.aura state store in new table spell_area.

* It allow store requirenments: area, active or rewarded quest (until possible another quest not rewarded),
  aura present at character, character race/gender.
* Listed spell can marked as auto-casted when fit requirents. In this case spell requirements checked at
  zone/subzone update (and then resurraction also), quest start/reward, dummy aura apply.
* Old hardcoded lines for similar check removed from sources and required DB support for work now.
This commit is contained in:
VladimirMangos 2009-02-27 11:03:09 +03:00
parent 9e7e374077
commit 1fca6de6f3
13 changed files with 458 additions and 122 deletions

View file

@ -2348,7 +2348,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)
@ -2367,75 +2552,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
@ -2448,11 +2584,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);
@ -2462,7 +2598,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)
@ -2474,11 +2610,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);
@ -2488,7 +2624,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;
}
}
@ -2632,3 +2768,50 @@ 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;
}