[12195] Improve AreaTrigger teleport requirement checks

Implement AreaLockStatus concept by rsa

Also drop basicly unneeded `areatrigger_teleport`.required_failed_text field.
This concept is still in testing phase, please feedback results of some glitches that might exist!

TODO: Use Player::GetAreaLockStatus or GetAreaTriggerLockStatus for other "CanEnter" checks as well.

Signed-off-by: Schmoozerd <schmoozerd@scriptdev2.com>
This commit is contained in:
cyberium 2012-09-06 16:40:31 +02:00 committed by Antz
parent 7901613472
commit 9022705faf
22 changed files with 304 additions and 162 deletions

View file

@ -24,7 +24,7 @@ CREATE TABLE `db_version` (
`version` varchar(120) default NULL, `version` varchar(120) default NULL,
`creature_ai_version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL,
`cache_id` int(10) default '0', `cache_id` int(10) default '0',
`required_12186_01_mangos_item_template` bit(1) default NULL `required_12195_02_mangos_mangos_string"` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes';
-- --
@ -141,7 +141,6 @@ CREATE TABLE `areatrigger_teleport` (
`heroic_key2` mediumint(8) unsigned NOT NULL default '0', `heroic_key2` mediumint(8) unsigned NOT NULL default '0',
`required_quest_done` int(11) unsigned NOT NULL default '0', `required_quest_done` int(11) unsigned NOT NULL default '0',
`required_quest_done_heroic` int(11) unsigned NOT NULL default '0', `required_quest_done_heroic` int(11) unsigned NOT NULL default '0',
`required_failed_text` text,
`target_map` smallint(5) unsigned NOT NULL default '0', `target_map` smallint(5) unsigned NOT NULL default '0',
`target_position_x` float NOT NULL default '0', `target_position_x` float NOT NULL default '0',
`target_position_y` float NOT NULL default '0', `target_position_y` float NOT NULL default '0',
@ -4039,6 +4038,7 @@ INSERT INTO `mangos_string` VALUES
(815,'Initiate',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (815,'Initiate',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(816,'Your body is too exhausted to travel to the Spectral Realm.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (816,'Your body is too exhausted to travel to the Spectral Realm.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(817,'Warning: You\'ve entered a no-fly zone and are about to be dismounted!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (817,'Warning: You\'ve entered a no-fly zone and are about to be dismounted!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(818,'You can\'t enter Black Morass until you rescue Thrall from Durnholde Keep.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(1000,'Exiting daemon...',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1000,'Exiting daemon...',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(1001,'Account deleted: %s',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1001,'Account deleted: %s',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(1002,'Account %s NOT deleted (probably sql file format was updated)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (1002,'Account %s NOT deleted (probably sql file format was updated)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),

View file

@ -0,0 +1,3 @@
ALTER TABLE db_version CHANGE COLUMN required_12186_01_mangos_item_template required_12195_01_mangos_areatrigger_teleport bit;
ALTER TABLE areatrigger_teleport DROP COLUMN required_failed_text;

View file

@ -0,0 +1,5 @@
ALTER TABLE db_version CHANGE COLUMN required_12195_01_mangos_areatrigger_teleport required_12195_02_mangos_mangos_string bit;
DELETE FROM mangos_string WHERE entry=818;
INSERT INTO mangos_string VALUES
(818,'You can\'t enter Black Morass until you rescue Thrall from Durnholde Keep.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);

View file

@ -140,7 +140,6 @@ DBCStorage <LockEntry> sLockStore(LockEntryfmt);
DBCStorage <MailTemplateEntry> sMailTemplateStore(MailTemplateEntryfmt); DBCStorage <MailTemplateEntry> sMailTemplateStore(MailTemplateEntryfmt);
DBCStorage <MapEntry> sMapStore(MapEntryfmt); DBCStorage <MapEntry> sMapStore(MapEntryfmt);
// DBC used only for initialization sMapDifficultyMap at startup.
DBCStorage <MapDifficultyEntry> sMapDifficultyStore(MapDifficultyEntryfmt); // only for loading DBCStorage <MapDifficultyEntry> sMapDifficultyStore(MapDifficultyEntryfmt); // only for loading
MapDifficultyMap sMapDifficultyMap; MapDifficultyMap sMapDifficultyMap;
@ -540,8 +539,7 @@ void LoadDBCStores(const std::string& dataPath)
// fill data // fill data
for(uint32 i = 1; i < sMapDifficultyStore.GetNumRows(); ++i) for(uint32 i = 1; i < sMapDifficultyStore.GetNumRows(); ++i)
if(MapDifficultyEntry const* entry = sMapDifficultyStore.LookupEntry(i)) if(MapDifficultyEntry const* entry = sMapDifficultyStore.LookupEntry(i))
sMapDifficultyMap[MAKE_PAIR32(entry->MapId,entry->Difficulty)] = MapDifficulty(entry->resetTime,entry->maxPlayers); sMapDifficultyMap[MAKE_PAIR32(entry->MapId, entry->Difficulty)] = entry;
sMapDifficultyStore.Clear();
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMovieStore, dbcPath,"Movie.dbc"); LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMovieStore, dbcPath,"Movie.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sNumTalentsAtLevelStore, dbcPath,"NumTalentsAtLevel.dbc"); LoadDBC(availableDbcLocales,bar,bad_dbc_files,sNumTalentsAtLevelStore, dbcPath,"NumTalentsAtLevel.dbc");
@ -1024,10 +1022,10 @@ bool Map2ZoneCoordinates(float& x,float& y,uint32 zone)
return true; return true;
} }
MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty) MapDifficultyEntry const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty)
{ {
MapDifficultyMap::const_iterator itr = sMapDifficultyMap.find(MAKE_PAIR32(mapId,difficulty)); MapDifficultyMap::const_iterator itr = sMapDifficultyMap.find(MAKE_PAIR32(mapId,difficulty));
return itr != sMapDifficultyMap.end() ? &itr->second : NULL; return itr != sMapDifficultyMap.end() ? itr->second : NULL;
} }
PvPDifficultyEntry const* GetBattlegroundBracketByLevel( uint32 mapid, uint32 level ) PvPDifficultyEntry const* GetBattlegroundBracketByLevel( uint32 mapid, uint32 level )

View file

@ -63,8 +63,8 @@ bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredT
bool Zone2MapCoordinates(float& x,float& y,uint32 zone); bool Zone2MapCoordinates(float& x,float& y,uint32 zone);
bool Map2ZoneCoordinates(float& x,float& y,uint32 zone); bool Map2ZoneCoordinates(float& x,float& y,uint32 zone);
typedef std::map<uint32/*pair32(map,diff)*/,MapDifficulty> MapDifficultyMap; typedef std::map<uint32/*pair32(map,diff)*/, MapDifficultyEntry const*> MapDifficultyMap;
MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty); MapDifficultyEntry const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty);
// natural order for difficulties up-down iteration // natural order for difficulties up-down iteration
// difficulties for dungeons/battleground ordered in normal way // difficulties for dungeons/battleground ordered in normal way

View file

@ -2411,15 +2411,6 @@ typedef std::set<uint32> PetFamilySpellsSet;
typedef std::map<uint32,PetFamilySpellsSet > PetFamilySpellsStore; typedef std::map<uint32,PetFamilySpellsSet > PetFamilySpellsStore;
// Structures not used for casting to loaded DBC data and not required then packing // Structures not used for casting to loaded DBC data and not required then packing
struct MapDifficulty
{
MapDifficulty() : resetTime(0), maxPlayers(0) {}
MapDifficulty(uint32 _resetTime, uint32 _maxPlayers) : resetTime(_resetTime), maxPlayers(_maxPlayers) {}
uint32 resetTime; // in secs, 0 if no fixed reset time
uint32 maxPlayers; // some heroic dungeons have 0 when expect same value as in normal dificulty case
};
struct TalentSpellPos struct TalentSpellPos
{ {
TalentSpellPos() : talent_id(0), rank(0) {} TalentSpellPos() : talent_id(0), rank(0) {}

View file

@ -1780,7 +1780,7 @@ InstanceGroupBind* Group::GetBoundInstance(uint32 mapid, Player* player)
Difficulty difficulty = player->GetDifficulty(mapEntry->IsRaid()); Difficulty difficulty = player->GetDifficulty(mapEntry->IsRaid());
// some instances only have one difficulty // some instances only have one difficulty
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid, difficulty); MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mapid, difficulty);
if (!mapDiff) if (!mapDiff)
difficulty = DUNGEON_DIFFICULTY_NORMAL; difficulty = DUNGEON_DIFFICULTY_NORMAL;
@ -1794,7 +1794,7 @@ InstanceGroupBind* Group::GetBoundInstance(uint32 mapid, Player* player)
InstanceGroupBind* Group::GetBoundInstance(Map* aMap, Difficulty difficulty) InstanceGroupBind* Group::GetBoundInstance(Map* aMap, Difficulty difficulty)
{ {
// some instances only have one difficulty // some instances only have one difficulty
MapDifficulty const* mapDiff = GetMapDifficultyData(aMap->GetId(), difficulty); MapDifficultyEntry const* mapDiff = GetMapDifficultyData(aMap->GetId(), difficulty);
if (!mapDiff) if (!mapDiff)
return NULL; return NULL;

View file

@ -809,7 +809,8 @@ enum MangosStrings
LANG_GUILD_INITIATE = 815, LANG_GUILD_INITIATE = 815,
LANG_FAIL_ENTER_SPECTRAL_REALM = 816, LANG_FAIL_ENTER_SPECTRAL_REALM = 816,
LANG_NO_FLY_ZONE = 817, LANG_NO_FLY_ZONE = 817,
// Room for in-game strings 818-999 not used LANG_TELEREQ_QUEST_BLACK_MORASS = 818,
// Room for in-game strings 819-999 not used
// Level 4 (CLI only commands) // Level 4 (CLI only commands)
LANG_COMMAND_EXIT = 1000, LANG_COMMAND_EXIT = 1000,

View file

@ -824,21 +824,21 @@ void Map::UnloadAll(bool pForce)
} }
} }
MapDifficulty const* Map::GetMapDifficulty() const MapDifficultyEntry const* Map::GetMapDifficulty() const
{ {
return GetMapDifficultyData(GetId(), GetDifficulty()); return GetMapDifficultyData(GetId(), GetDifficulty());
} }
uint32 Map::GetMaxPlayers() const uint32 Map::GetMaxPlayers() const
{ {
if (MapDifficulty const* mapDiff = GetMapDifficulty()) if (MapDifficultyEntry const* mapDiff = GetMapDifficulty())
{ {
if (mapDiff->maxPlayers || IsRegularDifficulty()) // Normal case (expect that regular difficulty always have correct maxplayers) if (mapDiff->maxPlayers || IsRegularDifficulty()) // Normal case (expect that regular difficulty always have correct maxplayers)
return mapDiff->maxPlayers; return mapDiff->maxPlayers;
else // DBC have 0 maxplayers for heroic instances with expansion < 2 else // DBC have 0 maxplayers for heroic instances with expansion < 2
{ {
// The heroic entry exists, so we don't have to check anything, simply return normal max players // The heroic entry exists, so we don't have to check anything, simply return normal max players
MapDifficulty const* normalDiff = GetMapDifficultyData(i_id, REGULAR_DIFFICULTY); MapDifficultyEntry const* normalDiff = GetMapDifficultyData(i_id, REGULAR_DIFFICULTY);
return normalDiff ? normalDiff->maxPlayers : 0; return normalDiff ? normalDiff->maxPlayers : 0;
} }
} }

View file

@ -183,7 +183,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>
bool IsRegularDifficulty() const { return GetDifficulty() == REGULAR_DIFFICULTY; } bool IsRegularDifficulty() const { return GetDifficulty() == REGULAR_DIFFICULTY; }
uint32 GetMaxPlayers() const; // dependent from map difficulty uint32 GetMaxPlayers() const; // dependent from map difficulty
uint32 GetMaxResetDelay() const; // dependent from map difficulty uint32 GetMaxResetDelay() const; // dependent from map difficulty
MapDifficulty const* GetMapDifficulty() const; // dependent from map difficulty MapDifficultyEntry const* GetMapDifficulty() const; // dependent from map difficulty
bool Instanceable() const { return i_mapEntry && i_mapEntry->Instanceable(); } bool Instanceable() const { return i_mapEntry && i_mapEntry->Instanceable(); }
// NOTE: this duplicate of Instanceable(), but Instanceable() can be changed when BG also will be instanceable // NOTE: this duplicate of Instanceable(), but Instanceable() can be changed when BG also will be instanceable

View file

@ -182,7 +182,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player)
} }
// The player has a heroic mode and tries to enter into instance which has no a heroic mode // The player has a heroic mode and tries to enter into instance which has no a heroic mode
MapDifficulty const* mapDiff = GetMapDifficultyData(entry->MapID, player->GetDifficulty(entry->map_type == MAP_RAID)); MapDifficultyEntry const* mapDiff = GetMapDifficultyData(entry->MapID, player->GetDifficulty(entry->map_type == MAP_RAID));
if (!mapDiff) if (!mapDiff)
{ {
bool isRegularTargetMap = player->GetDifficulty(entry->IsRaid()) == REGULAR_DIFFICULTY; bool isRegularTargetMap = player->GetDifficulty(entry->IsRaid()) == REGULAR_DIFFICULTY;

View file

@ -319,7 +319,7 @@ bool BattleGroundPersistentState::CanBeUnload() const
//== DungeonResetScheduler functions ====================== //== DungeonResetScheduler functions ======================
uint32 DungeonResetScheduler::GetMaxResetTimeFor(MapDifficulty const* mapDiff) uint32 DungeonResetScheduler::GetMaxResetTimeFor(MapDifficultyEntry const* mapDiff)
{ {
if (!mapDiff || !mapDiff->resetTime) if (!mapDiff || !mapDiff->resetTime)
return 0; return 0;
@ -332,7 +332,7 @@ uint32 DungeonResetScheduler::GetMaxResetTimeFor(MapDifficulty const* mapDiff)
return delay; return delay;
} }
time_t DungeonResetScheduler::CalculateNextResetTime(MapDifficulty const* mapDiff, time_t prevResetTime) time_t DungeonResetScheduler::CalculateNextResetTime(MapDifficultyEntry const* mapDiff, time_t prevResetTime)
{ {
uint32 diff = sWorld.getConfig(CONFIG_UINT32_INSTANCE_RESET_TIME_HOUR) * HOUR; uint32 diff = sWorld.getConfig(CONFIG_UINT32_INSTANCE_RESET_TIME_HOUR) * HOUR;
uint32 period = GetMaxResetTimeFor(mapDiff); uint32 period = GetMaxResetTimeFor(mapDiff);
@ -450,7 +450,7 @@ void DungeonResetScheduler::LoadResetTimes()
uint32 map_diff_pair = itr->first; uint32 map_diff_pair = itr->first;
uint32 mapid = PAIR32_LOPART(map_diff_pair); uint32 mapid = PAIR32_LOPART(map_diff_pair);
Difficulty difficulty = Difficulty(PAIR32_HIPART(map_diff_pair)); Difficulty difficulty = Difficulty(PAIR32_HIPART(map_diff_pair));
MapDifficulty const* mapDiff = &itr->second; MapDifficultyEntry const* mapDiff = itr->second;
// skip mapDiff without global reset time // skip mapDiff without global reset time
if (!mapDiff->resetTime) if (!mapDiff->resetTime)
@ -552,7 +552,7 @@ void DungeonResetScheduler::Update()
{ {
// re-schedule the next/new global reset/warning // re-schedule the next/new global reset/warning
// calculate the next reset time // calculate the next reset time
MapDifficulty const* mapDiff = GetMapDifficultyData(event.mapid, event.difficulty); MapDifficultyEntry const* mapDiff = GetMapDifficultyData(event.mapid, event.difficulty);
MANGOS_ASSERT(mapDiff); MANGOS_ASSERT(mapDiff);
time_t next_reset = DungeonResetScheduler::CalculateNextResetTime(mapDiff, resetTime); time_t next_reset = DungeonResetScheduler::CalculateNextResetTime(mapDiff, resetTime);
@ -857,7 +857,7 @@ void MapPersistentStateManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficu
if (!warn) if (!warn)
{ {
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid, difficulty); MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mapid, difficulty);
if (!mapDiff || !mapDiff->resetTime) if (!mapDiff || !mapDiff->resetTime)
{ {
sLog.outError("MapPersistentStateManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid); sLog.outError("MapPersistentStateManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid);

View file

@ -34,7 +34,7 @@
struct InstanceTemplate; struct InstanceTemplate;
struct MapEntry; struct MapEntry;
struct MapDifficulty; struct MapDifficultyEntry;
struct GameObjectData; struct GameObjectData;
struct CreatureData; struct CreatureData;
@ -304,8 +304,8 @@ class DungeonResetScheduler
return itr != m_resetTimeByMapDifficulty.end() ? itr->second : 0; return itr != m_resetTimeByMapDifficulty.end() ? itr->second : 0;
} }
static uint32 GetMaxResetTimeFor(MapDifficulty const* mapDiff); static uint32 GetMaxResetTimeFor(MapDifficultyEntry const* mapDiff);
static time_t CalculateNextResetTime(MapDifficulty const* mapDiff, time_t prevResetTime); static time_t CalculateNextResetTime(MapDifficultyEntry const* mapDiff, time_t prevResetTime);
public: // modifiers public: // modifiers
void SetResetTimeFor(uint32 mapid, Difficulty d, time_t t) void SetResetTimeFor(uint32 mapid, Difficulty d, time_t t)
{ {

View file

@ -688,42 +688,42 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
recv_data >> Trigger_ID; recv_data >> Trigger_ID;
DEBUG_LOG("Trigger ID: %u", Trigger_ID); DEBUG_LOG("Trigger ID: %u", Trigger_ID);
Player* player = GetPlayer();
if (GetPlayer()->IsTaxiFlying()) if (player->IsTaxiFlying())
{ {
DEBUG_LOG("Player '%s' (GUID: %u) in flight, ignore Area Trigger ID: %u", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), Trigger_ID); DEBUG_LOG("Player '%s' (GUID: %u) in flight, ignore Area Trigger ID: %u", player->GetName(), player->GetGUIDLow(), Trigger_ID);
return; return;
} }
AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID); AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
if (!atEntry) if (!atEntry)
{ {
DEBUG_LOG("Player '%s' (GUID: %u) send unknown (by DBC) Area Trigger ID: %u", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), Trigger_ID); DEBUG_LOG("Player '%s' (GUID: %u) send unknown (by DBC) Area Trigger ID: %u", player->GetName(), player->GetGUIDLow(), Trigger_ID);
return; return;
} }
// delta is safe radius // delta is safe radius
const float delta = 5.0f; const float delta = 5.0f;
// check if player in the range of areatrigger
Player* pl = GetPlayer();
if (!IsPointInAreaTriggerZone(atEntry, pl->GetMapId(), pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), delta)) // check if player in the range of areatrigger
if (!IsPointInAreaTriggerZone(atEntry, player->GetMapId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), delta))
{ {
DEBUG_LOG("Player '%s' (GUID: %u) too far, ignore Area Trigger ID: %u", pl->GetName(), pl->GetGUIDLow(), Trigger_ID); DEBUG_LOG("Player '%s' (GUID: %u) too far, ignore Area Trigger ID: %u", player->GetName(), player->GetGUIDLow(), Trigger_ID);
return; return;
} }
if (sScriptMgr.OnAreaTrigger(pl, atEntry)) if (sScriptMgr.OnAreaTrigger(player, atEntry))
return; return;
uint32 quest_id = sObjectMgr.GetQuestForAreaTrigger(Trigger_ID); uint32 quest_id = sObjectMgr.GetQuestForAreaTrigger(Trigger_ID);
if (quest_id && pl->isAlive() && pl->IsActiveQuest(quest_id)) if (quest_id && player->isAlive() && player->IsActiveQuest(quest_id))
{ {
Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id); Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id);
if (pQuest) if (pQuest)
{ {
if (pl->GetQuestStatus(quest_id) == QUEST_STATUS_INCOMPLETE) if (player->GetQuestStatus(quest_id) == QUEST_STATUS_INCOMPLETE)
pl->AreaExploredOrEventHappens(quest_id); player->AreaExploredOrEventHappens(quest_id);
} }
} }
@ -731,19 +731,19 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
if (sObjectMgr.IsTavernAreaTrigger(Trigger_ID)) if (sObjectMgr.IsTavernAreaTrigger(Trigger_ID))
{ {
// set resting flag we are in the inn // set resting flag we are in the inn
if (pl->GetRestType() != REST_TYPE_IN_CITY) if (player->GetRestType() != REST_TYPE_IN_CITY)
pl->SetRestType(REST_TYPE_IN_TAVERN, Trigger_ID); player->SetRestType(REST_TYPE_IN_TAVERN, Trigger_ID);
return; return;
} }
if (BattleGround* bg = pl->GetBattleGround()) if (BattleGround* bg = player->GetBattleGround())
{ {
bg->HandleAreaTrigger(pl, Trigger_ID); bg->HandleAreaTrigger(player, Trigger_ID);
return; return;
} }
else if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(pl->GetCachedZoneId())) else if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(player->GetCachedZoneId()))
{ {
if (outdoorPvP->HandleAreaTrigger(pl, Trigger_ID)) if (outdoorPvP->HandleAreaTrigger(player, Trigger_ID))
return; return;
} }
@ -756,13 +756,11 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
if (!targetMapEntry) if (!targetMapEntry)
return; return;
if (!pl->isGameMaster())
{
// ghost resurrected at enter attempt to dungeon with corpse (including fail enter cases) // ghost resurrected at enter attempt to dungeon with corpse (including fail enter cases)
if (!pl->isAlive() && targetMapEntry->IsDungeon()) if (!player->isAlive() && targetMapEntry->IsDungeon())
{ {
int32 corpseMapId = 0; int32 corpseMapId = 0;
if (Corpse* corpse = pl->GetCorpse()) if (Corpse* corpse = player->GetCorpse())
corpseMapId = corpse->GetMapId(); corpseMapId = corpse->GetMapId();
// check back way from corpse to entrance // check back way from corpse to entrance
@ -782,7 +780,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
if (!instance_map) if (!instance_map)
{ {
WorldPacket data(SMSG_AREA_TRIGGER_NO_CORPSE); WorldPacket data(SMSG_AREA_TRIGGER_NO_CORPSE);
pl->GetSession()->SendPacket(&data); player->GetSession()->SendPacket(&data);
return; return;
} }
@ -797,70 +795,64 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
} }
// now we can resurrect player, and then check teleport requirements // now we can resurrect player, and then check teleport requirements
pl->ResurrectPlayer(0.5f); player->ResurrectPlayer(0.5f);
pl->SpawnCorpseBones(); player->SpawnCorpseBones();
} }
// check trigger requirements // check trigger requirements
bool missingItem = false; uint32 miscRequirement = 0;
bool missingLevel = false; AreaLockStatus lockStatus = player->GetAreaTriggerLockStatus(at, player->GetDifficulty(targetMapEntry->IsRaid()), miscRequirement);
bool missingQuest = false; switch (lockStatus)
if (pl->getLevel() < at->requiredLevel && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL))
missingLevel = true;
// must have one or the other, report the first one that's missing
if (at->requiredItem)
{ {
if (!pl->HasItemCount(at->requiredItem, 1) && case AREA_LOCKSTATUS_OK:
(!at->requiredItem2 || !GetPlayer()->HasItemCount(at->requiredItem2, 1))) player->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT);
missingItem = true; break;
} case AREA_LOCKSTATUS_TOO_LOW_LEVEL:
else if (at->requiredItem2 && !pl->HasItemCount(at->requiredItem2, 1)) SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED), miscRequirement);
missingItem = true; break;
case AREA_LOCKSTATUS_ZONE_IN_COMBAT:
bool isRegularTargetMap = !targetMapEntry->IsDungeon() || pl->GetDifficulty(targetMapEntry->IsRaid()) == REGULAR_DIFFICULTY; player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ZONE_IN_COMBAT);
break;
if (!isRegularTargetMap) case AREA_LOCKSTATUS_INSTANCE_IS_FULL:
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_MAX_PLAYERS);
break;
case AREA_LOCKSTATUS_QUEST_NOT_COMPLETED:
if (at->target_mapId == 269) // Exception for Black Morass
{ {
if (at->heroicKey) SendAreaTriggerMessage(GetMangosString(LANG_TELEREQ_QUEST_BLACK_MORASS));
break;
}
else if (targetMapEntry->IsContinent()) // do not report anything for quest areatrigge
{ {
if (!pl->HasItemCount(at->heroicKey, 1) && DEBUG_LOG("HandleAreaTriggerOpcode: LockAreaStatus %u, do not teleport, no message sent (trigger %u)", lockStatus, Trigger_ID);
(!at->heroicKey2 || !pl->HasItemCount(at->heroicKey2, 1))) break;
missingItem = true;
} }
else if (at->heroicKey2 && !pl->HasItemCount(at->heroicKey2, 1)) // No break here!
missingItem = true; case AREA_LOCKSTATUS_MISSING_ITEM:
} player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY, player->GetDifficulty(targetMapEntry->IsRaid()));
break;
if (!isRegularTargetMap) case AREA_LOCKSTATUS_MISSING_DIFFICULTY:
{ {
if (at->requiredQuestHeroic && !pl->GetQuestRewardStatus(at->requiredQuestHeroic)) Difficulty difficulty = player->GetDifficulty(targetMapEntry->IsRaid());
missingQuest = true; player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY, difficulty > RAID_DIFFICULTY_10MAN_HEROIC ? RAID_DIFFICULTY_10MAN_HEROIC : difficulty);
break;
} }
else case AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION:
{ player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_INSUF_EXPAN_LVL, miscRequirement);
if (at->requiredQuest && !pl->GetQuestRewardStatus(at->requiredQuest)) break;
missingQuest = true; case AREA_LOCKSTATUS_NOT_ALLOWED:
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_MAP_NOT_ALLOWED);
break;
case AREA_LOCKSTATUS_RAID_LOCKED:
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_NEED_GROUP);
break;
case AREA_LOCKSTATUS_UNKNOWN_ERROR:
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ERROR);
break;
default:
sLog.outError("HandleAreaTriggerOpcode: unhandled LockAreaStatus %u, when %s attempts to use area-trigger %u", lockStatus, player->GetGuidStr().c_str(), Trigger_ID);
break;
} }
if (missingItem || missingLevel || missingQuest)
{
// hack for "Opening of the Dark Portal"
if (missingQuest && at->target_mapId == 269)
SendAreaTriggerMessage("%s", at->requiredFailedText.c_str());
else if (missingQuest && targetMapEntry->IsContinent())// do not report anything for quest areatriggers
return;
// hack for TBC heroics
else if (missingLevel && !targetMapEntry->IsRaid() && GetPlayer()->GetDifficulty(false) == DUNGEON_DIFFICULTY_HEROIC && targetMapEntry->addon == 1)
SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED), at->requiredLevel);
else
pl->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY, pl->GetDifficulty(targetMapEntry->IsRaid()));
return;
}
}
GetPlayer()->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT);
} }
void WorldSession::HandleUpdateAccountData(WorldPacket& recv_data) void WorldSession::HandleUpdateAccountData(WorldPacket& recv_data)
@ -1416,7 +1408,8 @@ void WorldSession::HandleSetDungeonDifficultyOpcode(WorldPacket& recv_data)
return; return;
} }
if (_player->getLevel() < LEVELREQUIREMENT_HEROIC) // Exception to set mode to normal for low-level players
if (_player->getLevel() < LEVELREQUIREMENT_HEROIC && mode > REGULAR_DIFFICULTY)
return; return;
if (Group* pGroup = _player->GetGroup()) if (Group* pGroup = _player->GetGroup())
@ -1460,7 +1453,8 @@ void WorldSession::HandleSetRaidDifficultyOpcode(WorldPacket& recv_data)
return; return;
} }
if (_player->getLevel() < LEVELREQUIREMENT_HEROIC) // Exception to set mode to normal for low-level players
if (_player->getLevel() < LEVELREQUIREMENT_HEROIC && mode > REGULAR_DIFFICULTY)
return; return;
if (Group* pGroup = _player->GetGroup()) if (Group* pGroup = _player->GetGroup())

View file

@ -172,7 +172,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
if (mInstance) if (mInstance)
{ {
Difficulty diff = GetPlayer()->GetDifficulty(mEntry->IsRaid()); Difficulty diff = GetPlayer()->GetDifficulty(mEntry->IsRaid());
if (MapDifficulty const* mapDiff = GetMapDifficultyData(mEntry->MapID, diff)) if (MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mEntry->MapID, diff))
{ {
if (mapDiff->resetTime) if (mapDiff->resetTime)
{ {

View file

@ -5573,8 +5573,8 @@ void ObjectMgr::LoadAreaTriggerTeleports()
uint32 count = 0; uint32 count = 0;
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 // 0 1 2 3 4 5 6 7 8 9 10 11 12
QueryResult* result = WorldDatabase.Query("SELECT id, required_level, required_item, required_item2, heroic_key, heroic_key2, required_quest_done, required_quest_done_heroic, required_failed_text, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport"); QueryResult* result = WorldDatabase.Query("SELECT id, required_level, required_item, required_item2, heroic_key, heroic_key2, required_quest_done, required_quest_done_heroic, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
if (!result) if (!result)
{ {
@ -5608,12 +5608,11 @@ void ObjectMgr::LoadAreaTriggerTeleports()
at.heroicKey2 = fields[5].GetUInt32(); at.heroicKey2 = fields[5].GetUInt32();
at.requiredQuest = fields[6].GetUInt32(); at.requiredQuest = fields[6].GetUInt32();
at.requiredQuestHeroic = fields[7].GetUInt32(); at.requiredQuestHeroic = fields[7].GetUInt32();
at.requiredFailedText = fields[8].GetCppString(); at.target_mapId = fields[8].GetUInt32();
at.target_mapId = fields[9].GetUInt32(); at.target_X = fields[9].GetFloat();
at.target_X = fields[10].GetFloat(); at.target_Y = fields[10].GetFloat();
at.target_Y = fields[11].GetFloat(); at.target_Z = fields[11].GetFloat();
at.target_Z = fields[12].GetFloat(); at.target_Orientation = fields[12].GetFloat();
at.target_Orientation = fields[13].GetFloat();
AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID); AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
if (!atEntry) if (!atEntry)

View file

@ -83,7 +83,6 @@ struct AreaTrigger
uint32 heroicKey2; uint32 heroicKey2;
uint32 requiredQuest; uint32 requiredQuest;
uint32 requiredQuestHeroic; uint32 requiredQuestHeroic;
std::string requiredFailedText;
uint32 target_mapId; uint32 target_mapId;
float target_X; float target_X;
float target_Y; float target_Y;

View file

@ -16774,7 +16774,7 @@ void Player::_LoadBoundInstances(QueryResult* result)
continue; continue;
} }
MapDifficulty const* mapDiff = GetMapDifficultyData(mapId, Difficulty(difficulty)); MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mapId, Difficulty(difficulty));
if (!mapDiff) if (!mapDiff)
{ {
sLog.outError("_LoadBoundInstances: player %s(%d) has bind to nonexistent difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty, mapId); sLog.outError("_LoadBoundInstances: player %s(%d) has bind to nonexistent difficulty %d instance for map %u", GetName(), GetGUIDLow(), difficulty, mapId);
@ -16803,7 +16803,7 @@ void Player::_LoadBoundInstances(QueryResult* result)
InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty) InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty)
{ {
// some instances only have one difficulty // some instances only have one difficulty
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid, difficulty); MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mapid, difficulty);
if (!mapDiff) if (!mapDiff)
return NULL; return NULL;
@ -23611,3 +23611,123 @@ void Player::SendRatedBGStats()
SendDirectMessage(&data); SendDirectMessage(&data);
} }
AreaLockStatus Player::GetAreaTriggerLockStatus(AreaTrigger const* at, Difficulty difficulty, uint32& miscRequirement)
{
miscRequirement = 0;
if (!at)
return AREA_LOCKSTATUS_UNKNOWN_ERROR;
MapEntry const* mapEntry = sMapStore.LookupEntry(at->target_mapId);
if (!mapEntry)
return AREA_LOCKSTATUS_UNKNOWN_ERROR;
bool isRegularTargetMap = !mapEntry->IsDungeon() || GetDifficulty(mapEntry->IsRaid()) == REGULAR_DIFFICULTY;
MapDifficultyEntry const* mapDiff = GetMapDifficultyData(at->target_mapId, difficulty);
if (mapEntry->IsDungeon() && !mapDiff)
return AREA_LOCKSTATUS_MISSING_DIFFICULTY;
// Expansion requirement
if (GetSession()->Expansion() < mapEntry->Expansion())
{
miscRequirement = mapEntry->Expansion();
return AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION;
}
// Gamemaster can always enter
if (isGameMaster())
return AREA_LOCKSTATUS_OK;
// Level Requirements
if (getLevel() < at->requiredLevel && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL))
{
miscRequirement = at->requiredLevel;
return AREA_LOCKSTATUS_TOO_LOW_LEVEL;
}
if (!isRegularTargetMap && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL) && getLevel() < uint32(maxLevelForExpansion[mapEntry->Expansion()]))
{
miscRequirement = maxLevelForExpansion[mapEntry->Expansion()];
return AREA_LOCKSTATUS_TOO_LOW_LEVEL;
}
// Raid Requirements
if (mapEntry->IsRaid() && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_RAID))
if (!GetGroup() || !GetGroup()->isRaidGroup())
return AREA_LOCKSTATUS_RAID_LOCKED;
// Item Requirements: must have requiredItem OR requiredItem2, report the first one that's missing
if (at->requiredItem)
{
if (!HasItemCount(at->requiredItem, 1) &&
(!at->requiredItem2 || !HasItemCount(at->requiredItem2, 1)))
{
miscRequirement = at->requiredItem;
return AREA_LOCKSTATUS_MISSING_ITEM;
}
}
else if (at->requiredItem2 && !HasItemCount(at->requiredItem2, 1))
{
miscRequirement = at->requiredItem2;
return AREA_LOCKSTATUS_MISSING_ITEM;
}
// Heroic item requirements
if (!isRegularTargetMap && at->heroicKey)
{
if (!HasItemCount(at->heroicKey, 1) && (!at->heroicKey2 || !HasItemCount(at->heroicKey2, 1)))
{
miscRequirement = at->heroicKey;
return AREA_LOCKSTATUS_MISSING_ITEM;
}
}
else if (!isRegularTargetMap && at->heroicKey2 && !HasItemCount(at->heroicKey2, 1))
{
miscRequirement = at->heroicKey2;
return AREA_LOCKSTATUS_MISSING_ITEM;
}
// Quest Requirements
if (isRegularTargetMap && at->requiredQuest && !GetQuestRewardStatus(at->requiredQuest))
{
miscRequirement = at->requiredQuest;
return AREA_LOCKSTATUS_QUEST_NOT_COMPLETED;
}
if (!isRegularTargetMap && at->requiredQuestHeroic && !GetQuestRewardStatus(at->requiredQuestHeroic))
{
miscRequirement = at->requiredQuestHeroic;
return AREA_LOCKSTATUS_QUEST_NOT_COMPLETED;
}
// If the map is not created, assume it is possible to enter it.
DungeonPersistentState* state = GetBoundInstanceSaveForSelfOrGroup(at->target_mapId);
Map* map = sMapMgr.FindMap(at->target_mapId, state ? state->GetInstanceId() : 0);
// ToDo add achievement check
// Map's state check
if (map && map->IsDungeon())
{
// cannot enter if the instance is full (player cap), GMs don't count
if (((DungeonMap*)map)->GetPlayersCountExceptGMs() >= ((DungeonMap*)map)->GetMaxPlayers())
return AREA_LOCKSTATUS_INSTANCE_IS_FULL;
// In Combat check
if (map && map->GetInstanceData() && map->GetInstanceData()->IsEncounterInProgress())
return AREA_LOCKSTATUS_ZONE_IN_COMBAT;
// Bind Checks
InstancePlayerBind* pBind = GetBoundInstance(at->target_mapId, GetDifficulty(mapEntry->IsRaid()));
if (pBind && pBind->perm && pBind->state != state)
return AREA_LOCKSTATUS_HAS_BIND;
if (pBind && pBind->perm && pBind->state != map->GetPersistentState())
return AREA_LOCKSTATUS_HAS_BIND;
}
return AREA_LOCKSTATUS_OK;
};
AreaLockStatus Player::GetAreaLockStatus(uint32 mapId, Difficulty difficulty, uint32& miscRequirement)
{
return GetAreaTriggerLockStatus(sObjectMgr.GetMapEntranceTrigger(mapId), difficulty, miscRequirement);
};

View file

@ -54,6 +54,8 @@ class DungeonPersistentState;
class Spell; class Spell;
class Item; class Item;
struct AreaTrigger;
typedef std::deque<Mail*> PlayerMails; typedef std::deque<Mail*> PlayerMails;
#define PLAYER_MAX_SKILLS 128 #define PLAYER_MAX_SKILLS 128
@ -2326,6 +2328,9 @@ class MANGOS_DLL_SPEC Player : public Unit
static void ConvertInstancesToGroup(Player* player, Group* group = NULL, ObjectGuid player_guid = ObjectGuid()); static void ConvertInstancesToGroup(Player* player, Group* group = NULL, ObjectGuid player_guid = ObjectGuid());
DungeonPersistentState* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid); DungeonPersistentState* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid);
AreaLockStatus GetAreaLockStatus(uint32 mapId, Difficulty difficulty, uint32& miscRequirement);
AreaLockStatus GetAreaTriggerLockStatus(AreaTrigger const* at, Difficulty difficulty, uint32& miscRequirement);
/*********************************************************/ /*********************************************************/
/*** GROUP SYSTEM ***/ /*** GROUP SYSTEM ***/
/*********************************************************/ /*********************************************************/

View file

@ -3206,6 +3206,23 @@ enum ActivateTaxiReply
ERR_TAXINOTSTANDING = 12 ERR_TAXINOTSTANDING = 12
}; };
enum AreaLockStatus
{
AREA_LOCKSTATUS_OK = 0,
AREA_LOCKSTATUS_UNKNOWN_ERROR = 1,
AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION = 2,
AREA_LOCKSTATUS_TOO_LOW_LEVEL = 3,
AREA_LOCKSTATUS_TOO_HIGH_LEVEL = 4,
AREA_LOCKSTATUS_RAID_LOCKED = 5,
AREA_LOCKSTATUS_QUEST_NOT_COMPLETED = 6,
AREA_LOCKSTATUS_MISSING_ITEM = 7,
AREA_LOCKSTATUS_MISSING_DIFFICULTY = 8,
AREA_LOCKSTATUS_ZONE_IN_COMBAT = 9,
AREA_LOCKSTATUS_INSTANCE_IS_FULL = 10,
AREA_LOCKSTATUS_NOT_ALLOWED = 11,
AREA_LOCKSTATUS_HAS_BIND = 12,
};
// we need to stick to 1 version or half of the stuff will work for someone // we need to stick to 1 version or half of the stuff will work for someone
// others will not and opposite // others will not and opposite
// will only support WoW, WoW:TBC, WoW:WotLK and WoW:Cataclysm 4.3.4 client build 15595... // will only support WoW, WoW:TBC, WoW:WotLK and WoW:Cataclysm 4.3.4 client build 15595...
@ -3219,4 +3236,14 @@ enum ActivateTaxiReply
// that it not have expansion installed and reject // that it not have expansion installed and reject
#define MAX_EXPANSION 3 #define MAX_EXPANSION 3
// Maxlevel for expansion
enum MaxLevel
{
MAX_LEVEL_CLASSIC = 60,
MAX_LEVEL_TBC = 70,
MAX_LEVEL_WOTLK = 80,
};
static const MaxLevel maxLevelForExpansion[MAX_EXPANSION + 1] = { MAX_LEVEL_CLASSIC, MAX_LEVEL_TBC, MAX_LEVEL_WOTLK };
#endif #endif

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__ #ifndef __REVISION_NR_H__
#define __REVISION_NR_H__ #define __REVISION_NR_H__
#define REVISION_NR "12194" #define REVISION_NR "12195"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__

View file

@ -1,6 +1,6 @@
#ifndef __REVISION_SQL_H__ #ifndef __REVISION_SQL_H__
#define __REVISION_SQL_H__ #define __REVISION_SQL_H__
#define REVISION_DB_CHARACTERS "required_12161_01_characters_characters" #define REVISION_DB_CHARACTERS "required_12161_01_characters_characters"
#define REVISION_DB_MANGOS "required_12186_01_mangos_item_template" #define REVISION_DB_MANGOS "required_12195_02_mangos_mangos_string"
#define REVISION_DB_REALMD "required_12112_01_realmd_account_access" #define REVISION_DB_REALMD "required_12112_01_realmd_account_access"
#endif // __REVISION_SQL_H__ #endif // __REVISION_SQL_H__