Merge pull request #16110 from Treeston/3.3.5-instancerevive

Game/Maps: Clean up instance zone-in handling
This commit is contained in:
Treeston
2015-12-31 19:51:07 +01:00
13 changed files with 251 additions and 143 deletions
@@ -0,0 +1,4 @@
--
DELETE FROM `trinity_string` WHERE `entry`=11014;
INSERT INTO `trinity_string` (`entry`,`content_default`) VALUES
(11014,"You are already locked to %s.");
+80 -25
View File
@@ -1913,7 +1913,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
// Check enter rights before map getting to avoid creating instance copy for player
// this check not dependent from map instance copy and same for all instance copies of selected map
if (!sMapMgr->CanPlayerEnter(mapid, this, false))
if (sMapMgr->PlayerCannotEnter(mapid, this, false))
return false;
//I think this always returns true. Correct me if I am wrong.
@@ -17096,7 +17096,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
// NOW player must have valid map
// load the player's map here if it's not already loaded
Map* map = sMapMgr->CreateMap(mapId, this);
Map* map = sMapMgr->CreateMap(mapId, this, instanceId);
AreaTrigger const* areaTrigger = NULL;
bool check = false;
@@ -17107,8 +17107,28 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
}
else if (map->IsDungeon()) // if map is dungeon...
{
if (!((InstanceMap*)map)->CanEnter(this) || !CheckInstanceLoginValid(map)) // ... and can't enter map, then look for entry point.
if (Map::EnterState denyReason = ((InstanceMap*)map)->CannotEnter(this)) // ... and can't enter map, then look for entry point.
{
switch (denyReason)
{
case Map::CANNOT_ENTER_DIFFICULTY_UNAVAILABLE:
SendTransferAborted(map->GetId(), TRANSFER_ABORT_DIFFICULTY, map->GetDifficulty());
break;
case Map::CANNOT_ENTER_INSTANCE_BIND_MISMATCH:
ChatHandler(GetSession()).PSendSysMessage(GetSession()->GetTrinityString(LANG_INSTANCE_BIND_MISMATCH), map->GetMapName());
break;
case Map::CANNOT_ENTER_TOO_MANY_INSTANCES:
SendTransferAborted(map->GetId(), TRANSFER_ABORT_TOO_MANY_INSTANCES);
break;
case Map::CANNOT_ENTER_MAX_PLAYERS:
SendTransferAborted(map->GetId(), TRANSFER_ABORT_MAX_PLAYERS);
break;
case Map::CANNOT_ENTER_ZONE_IN_COMBAT:
SendTransferAborted(map->GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
break;
default:
break;
}
areaTrigger = sObjectMgr->GetGoBackTrigger(mapId);
check = true;
}
@@ -17155,6 +17175,10 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
SetMap(map);
StoreRaidMapDifficulty();
// now that map position is determined, check instance validity
if (!CheckInstanceValidity(true) && !IsInstanceLoginGameMasterException())
m_InstanceValid = false;
// randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE]
// this must help in case next save after mass player load after server startup
m_nextSave = urand(m_nextSave/2, m_nextSave*3/2);
@@ -18667,28 +18691,6 @@ bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report
return true;
}
bool Player::CheckInstanceLoginValid(Map* map)
{
if (!map->IsDungeon() || IsGameMaster())
return true;
if (map->IsRaid())
{
// cannot be in raid instance without a group
if (!GetGroup())
return IsInstanceLoginGameMasterException();
}
else
{
// cannot be in normal instance without a group and more players than 1 in instance
if (!GetGroup() && map->GetPlayersCountExceptGMs() > 1)
return IsInstanceLoginGameMasterException();
}
// do checks for satisfy accessreqs, instance full, encounter in progress (raid), perm bind group != perm bind player
return sMapMgr->CanPlayerEnter(map->GetId(), this, true) || IsInstanceLoginGameMasterException();
}
bool Player::IsInstanceLoginGameMasterException() const
{
if (CanBeGameMaster())
@@ -18700,6 +18702,59 @@ bool Player::IsInstanceLoginGameMasterException() const
return false;
}
bool Player::CheckInstanceValidity(bool /*isLogin*/)
{
// game masters' instances are always valid
if (IsGameMaster())
return true;
// non-instances are always valid
Map* map = GetMap();
if (!map || !map->IsDungeon())
return true;
// raid instances require the player to be in a raid group to be valid
if (map->IsRaid() && !sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_RAID))
if (!GetGroup() || !GetGroup()->isRaidGroup())
return false;
if (Group* group = GetGroup())
{
// check if player's group is bound to this instance
InstanceGroupBind* bind = group->GetBoundInstance(map->GetDifficulty(), map->GetId());
if (!bind || !bind->save || bind->save->GetInstanceId() != map->GetInstanceId())
return false;
Map::PlayerList const& players = map->GetPlayers();
if (!players.isEmpty())
for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
{
if (Player* otherPlayer = it->GetSource())
{
if (otherPlayer->IsGameMaster())
continue;
if (!otherPlayer->m_InstanceValid) // ignore players that currently have a homebind timer active
continue;
if (group != otherPlayer->GetGroup())
return false;
}
}
}
else
{
// instance is invalid if we are not grouped and there are other players
if (map->GetPlayersCountExceptGMs() > 1)
return false;
// check if the player is bound to this instance
InstancePlayerBind* bind = GetBoundInstance(map->GetId(), map->GetDifficulty());
if (!bind || !bind->save || bind->save->GetInstanceId() != map->GetInstanceId())
return false;
}
return true;
}
bool Player::CheckInstanceCount(uint32 instanceId) const
{
if (_instanceResetTimes.size() < sWorld->getIntConfig(CONFIG_MAX_INSTANCES_PER_HOUR))
+1 -1
View File
@@ -2118,7 +2118,7 @@ class Player : public Unit, public GridObject<Player>
void SendSavedInstances();
static void ConvertInstancesToGroup(Player* player, Group* group, bool switchLeader);
bool Satisfy(AccessRequirement const* ar, uint32 target_map, bool report = false);
bool CheckInstanceLoginValid(Map* map);
bool CheckInstanceValidity(bool /*isLogin*/);
bool CheckInstanceCount(uint32 instanceId) const;
void AddInstanceEnterTime(uint32 instanceId, time_t enterTime);
+70 -1
View File
@@ -866,8 +866,77 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recvData)
bool teleported = false;
if (player->GetMapId() != at->target_mapId)
{
if (!sMapMgr->CanPlayerEnter(at->target_mapId, player, false))
if (Map::EnterState denyReason = sMapMgr->PlayerCannotEnter(at->target_mapId, player, false))
{
bool reviveAtTrigger = false; // should we revive the player if he is trying to enter the correct instance?
switch (denyReason)
{
case Map::CANNOT_ENTER_NO_ENTRY:
TC_LOG_DEBUG("maps", "MAP: Player '%s' attempted to enter map with id %d which has no entry", player->GetName().c_str(), at->target_mapId);
break;
case Map::CANNOT_ENTER_UNINSTANCED_DUNGEON:
TC_LOG_DEBUG("maps", "MAP: Player '%s' attempted to enter dungeon map %d but no instance template was found", player->GetName().c_str(), at->target_mapId);
break;
case Map::CANNOT_ENTER_DIFFICULTY_UNAVAILABLE:
TC_LOG_DEBUG("maps", "MAP: Player '%s' attempted to enter instance map %d but the requested difficulty was not found", player->GetName().c_str(), at->target_mapId);
if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId))
player->SendTransferAborted(entry->MapID, TRANSFER_ABORT_DIFFICULTY, player->GetDifficulty(entry->IsRaid()));
break;
case Map::CANNOT_ENTER_NOT_IN_RAID:
if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId))
{
char const* mapName = entry->name[player->GetSession()->GetSessionDbcLocale()];
TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter instance '%s'", player->GetName().c_str(), mapName);
// probably there must be special opcode, because client has this string constant in GlobalStrings.lua
player->GetSession()->SendAreaTriggerMessage(player->GetSession()->GetTrinityString(LANG_INSTANCE_RAID_GROUP_ONLY), mapName);
}
reviveAtTrigger = true;
break;
case Map::CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE:
{
WorldPacket data(SMSG_CORPSE_NOT_IN_INSTANCE);
player->GetSession()->SendPacket(&data);
TC_LOG_DEBUG("maps", "MAP: Player '%s' does not have a corpse in instance map %d and cannot enter", player->GetName().c_str(), at->target_mapId);
break;
}
case Map::CANNOT_ENTER_INSTANCE_BIND_MISMATCH:
if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId))
{
char const* mapName = entry->name[player->GetSession()->GetSessionDbcLocale()];
TC_LOG_DEBUG("maps", "MAP: Player '%s' cannot enter instance map '%s' because their permanent bind is incompatible with their group's", player->GetName().c_str(), mapName);
// is there a special opcode for this?
// @todo figure out how to get player localized difficulty string (e.g. "10 player", "Heroic" etc)
ChatHandler(player->GetSession()).PSendSysMessage(player->GetSession()->GetTrinityString(LANG_INSTANCE_BIND_MISMATCH), mapName);
}
reviveAtTrigger = true;
break;
case Map::CANNOT_ENTER_TOO_MANY_INSTANCES:
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_TOO_MANY_INSTANCES);
TC_LOG_DEBUG("maps", "MAP: Player '%s' cannot enter instance map %d because he has exceeded the maximum number of instances per hour.", player->GetName().c_str(), at->target_mapId);
reviveAtTrigger = true;
break;
case Map::CANNOT_ENTER_MAX_PLAYERS:
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_MAX_PLAYERS);
reviveAtTrigger = true;
break;
case Map::CANNOT_ENTER_ZONE_IN_COMBAT:
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ZONE_IN_COMBAT);
reviveAtTrigger = true;
break;
default:
break;
}
if (reviveAtTrigger) // check if the player is touching the areatrigger leading to the map his corpse is on
if (!player->IsAlive() && player->HasCorpse())
if (player->GetCorpseLocation().GetMapId() == at->target_mapId)
{
player->ResurrectPlayer(0.5f);
player->SpawnCorpseBones();
}
return;
}
if (Group* group = player->GetGroup())
if (group->isLFGGroup() && player->GetMap()->IsDungeon())
+12 -7
View File
@@ -75,9 +75,9 @@ void WorldSession::HandleMoveWorldportAckOpcode()
}
// relocate the player to the teleport destination
// the CanEnter checks are done in TeleporTo but conditions may change
// the CannotEnter checks are done in TeleporTo but conditions may change
// while the player is in transit, for example the map may get full
if (!newMap || !newMap->CanEnter(GetPlayer()))
if (!newMap || newMap->CannotEnter(GetPlayer()))
{
TC_LOG_ERROR("network", "Map %d (%s) could not be created for player %d (%s), porting player to homebind", loc.GetMapId(), newMap ? newMap->GetMapName() : "Unknown", GetPlayer()->GetGUID().GetCounter(), GetPlayer()->GetName().c_str());
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
@@ -142,19 +142,18 @@ void WorldSession::HandleMoveWorldportAckOpcode()
}
// resurrect character at enter into instance where his corpse exist after add to map
Corpse* corpse = GetPlayer()->GetMap()->GetCorpseByPlayer(GetPlayer()->GetGUID());
if (corpse && corpse->GetType() != CORPSE_BONES)
{
if (mEntry->IsDungeon())
if (mEntry->IsDungeon() && !GetPlayer()->IsAlive())
if (GetPlayer()->GetCorpseLocation().GetMapId() == mEntry->MapID)
{
GetPlayer()->ResurrectPlayer(0.5f, false);
GetPlayer()->SpawnCorpseBones();
}
}
bool allowMount = !mEntry->IsDungeon() || mEntry->IsBattlegroundOrArena();
if (mInstance)
{
// check if this instance has a reset time and send it to player if so
Difficulty diff = GetPlayer()->GetDifficulty(mEntry->IsRaid());
if (MapDifficulty const* mapDiff = GetMapDifficultyData(mEntry->MapID, diff))
{
@@ -167,6 +166,12 @@ void WorldSession::HandleMoveWorldportAckOpcode()
}
}
}
// check if instance is valid
if (!GetPlayer()->CheckInstanceValidity(false))
GetPlayer()->m_InstanceValid = false;
// instance mounting is handled in InstanceTemplate
allowMount = mInstance->AllowMount;
}
+1 -1
View File
@@ -157,7 +157,7 @@ class InstanceScript : public ZoneScript
virtual void Update(uint32 /*diff*/) { }
// Used by the map's CanEnter function.
// Used by the map's CannotEnter function.
// This is to prevent players from entering during boss encounters.
virtual bool IsEncounterInProgress() const;
+18 -42
View File
@@ -2947,62 +2947,38 @@ void InstanceMap::InitVisibilityDistance()
/*
Do map specific checks to see if the player can enter
*/
bool InstanceMap::CanEnter(Player* player)
Map::EnterState InstanceMap::CannotEnter(Player* player)
{
if (player->GetMapRef().getTarget() == this)
{
TC_LOG_ERROR("maps", "InstanceMap::CanEnter - player %s(%u) already in map %d, %d, %d!", player->GetName().c_str(), player->GetGUID().GetCounter(), GetId(), GetInstanceId(), GetSpawnMode());
TC_LOG_ERROR("maps", "InstanceMap::CannotEnter - player %s(%u) already in map %d, %d, %d!", player->GetName().c_str(), player->GetGUID().GetCounter(), GetId(), GetInstanceId(), GetSpawnMode());
ABORT();
return false;
return CANNOT_ENTER_ALREADY_IN_MAP;
}
// allow GM's to enter
if (player->IsGameMaster())
return Map::CanEnter(player);
return Map::CannotEnter(player);
// cannot enter if the instance is full (player cap), GMs don't count
uint32 maxPlayers = GetMaxPlayers();
if (GetPlayersCountExceptGMs() >= maxPlayers)
{
TC_LOG_WARN("maps", "MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName().c_str());
player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
return false;
return CANNOT_ENTER_MAX_PLAYERS;
}
// cannot enter while an encounter is in progress
// allow if just loading
// cannot enter while an encounter is in progress (unless this is a relog, in which case it is permitted)
if (!player->IsLoading() && IsRaid() && GetInstanceScript() && GetInstanceScript()->IsEncounterInProgress())
{
player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
return false;
}
return CANNOT_ENTER_ZONE_IN_COMBAT;
// cannot enter if instance is in use by another party/soloer that have a
// permanent save in the same instance id
// cannot enter if player is permanent saved to a different instance id
if (InstancePlayerBind* playerBind = player->GetBoundInstance(GetId(), GetDifficulty()))
if (playerBind->perm && playerBind->save)
if (playerBind->save->GetInstanceId() != GetInstanceId())
return CANNOT_ENTER_INSTANCE_BIND_MISMATCH;
PlayerList const &playerList = GetPlayers();
if (!playerList.isEmpty())
for (PlayerList::const_iterator i = playerList.begin(); i != playerList.end(); ++i)
if (Player* iPlayer = i->GetSource())
{
if (iPlayer->IsGameMaster()) // bypass GMs
continue;
if (!player->GetGroup()) // player has not group and there is someone inside, deny entry
{
player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
return false;
}
// player inside instance has no group or his groups is different to entering player's one, deny entry
if (!iPlayer->GetGroup() || iPlayer->GetGroup() != player->GetGroup())
{
player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
return false;
}
break;
}
return Map::CanEnter(player);
return Map::CannotEnter(player);
}
/*
@@ -3339,21 +3315,21 @@ void BattlegroundMap::InitVisibilityDistance()
m_VisibilityNotifyPeriod = World::GetVisibilityNotifyPeriodInBGArenas();
}
bool BattlegroundMap::CanEnter(Player* player)
Map::EnterState BattlegroundMap::CannotEnter(Player* player)
{
if (player->GetMapRef().getTarget() == this)
{
TC_LOG_ERROR("maps", "BGMap::CanEnter - player %u is already in map!", player->GetGUID().GetCounter());
TC_LOG_ERROR("maps", "BGMap::CannotEnter - player %u is already in map!", player->GetGUID().GetCounter());
ABORT();
return false;
return CANNOT_ENTER_ALREADY_IN_MAP;
}
if (player->GetBattlegroundId() != GetInstanceId())
return false;
return CANNOT_ENTER_INSTANCE_BIND_MISMATCH;
// player number limit is checked in bgmgr, no need to do it here
return Map::CanEnter(player);
return Map::CannotEnter(player);
}
bool BattlegroundMap::AddPlayerToMap(Player* player)
+19 -3
View File
@@ -373,7 +373,23 @@ class Map : public GridRefManager<NGridType>
uint32 GetInstanceId() const { return i_InstanceId; }
uint8 GetSpawnMode() const { return (i_spawnMode); }
virtual bool CanEnter(Player* /*player*/) { return true; }
enum EnterState
{
CAN_ENTER = 0,
CANNOT_ENTER_ALREADY_IN_MAP = 1, // Player is already in the map
CANNOT_ENTER_NO_ENTRY, // No map entry was found for the target map ID
CANNOT_ENTER_UNINSTANCED_DUNGEON, // No instance template was found for dungeon map
CANNOT_ENTER_DIFFICULTY_UNAVAILABLE, // Requested instance difficulty is not available for target map
CANNOT_ENTER_NOT_IN_RAID, // Target instance is a raid instance and the player is not in a raid group
CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE, // Player is dead and their corpse is not in target instance
CANNOT_ENTER_INSTANCE_BIND_MISMATCH, // Player's permanent instance save is not compatible with their group's current instance bind
CANNOT_ENTER_TOO_MANY_INSTANCES, // Player has entered too many instances recently
CANNOT_ENTER_MAX_PLAYERS, // Target map already has the maximum number of players allowed
CANNOT_ENTER_ZONE_IN_COMBAT, // A boss encounter is currently in progress on the target map
CANNOT_ENTER_UNSPECIFIED_REASON
};
virtual EnterState CannotEnter(Player* /*player*/) { return CAN_ENTER; }
const char* GetMapName() const;
// have meaning only for instanced map (that have set real difficulty)
@@ -762,7 +778,7 @@ class InstanceMap : public Map
InstanceScript* GetInstanceScript() { return i_data; }
void PermBindAllPlayers(Player* source);
void UnloadAll() override;
bool CanEnter(Player* player) override;
EnterState CannotEnter(Player* player) override;
void SendResetWarnings(uint32 timeLeft) const;
void SetResetSchedule(bool on);
@@ -785,7 +801,7 @@ class BattlegroundMap : public Map
bool AddPlayerToMap(Player*) override;
void RemovePlayerFromMap(Player*, bool) override;
bool CanEnter(Player* player) override;
EnterState CannotEnter(Player* player) override;
void SetUnload();
//void UnloadAll(bool pForce);
void RemoveAllPlayers() override;
+20 -11
View File
@@ -110,12 +110,12 @@ void MapInstanced::UnloadAll()
- create the instance if it's not created already
- the player is not actually added to the instance (only in InstanceMap::Add)
*/
Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player)
Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player, uint32 loginInstanceId)
{
if (GetId() != mapId || !player)
return NULL;
return nullptr;
Map* map = NULL;
Map* map = nullptr;
uint32 newInstanceId = 0; // instanceId of the resulting map
if (IsBattlegroundOrArena())
@@ -124,7 +124,7 @@ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player)
// the instance id is set in battlegroundid
newInstanceId = player->GetBattlegroundId();
if (!newInstanceId)
return NULL;
return nullptr;
map = sMapMgr->FindMap(mapId, newInstanceId);
if (!map)
@@ -134,20 +134,29 @@ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player)
else
{
player->TeleportToBGEntryPoint();
return NULL;
return nullptr;
}
}
}
else
{
InstancePlayerBind* pBind = player->GetBoundInstance(GetId(), player->GetDifficulty(IsRaid()));
InstanceSave* pSave = pBind ? pBind->save : NULL;
InstanceSave* pSave = pBind ? pBind->save : nullptr;
// the player's permanent player bind is taken into consideration first
// then the player's group bind and finally the solo bind.
// priority:
// 1. player's permanent bind
// 2. player's current instance id if this is at login
// 3. group's current bind
// 4. player's current bind
if (!pBind || !pBind->perm)
{
InstanceGroupBind* groupBind = NULL;
if (loginInstanceId) // if the player has a saved instance id on login, we either use this instance or relocate him out (return null)
{
map = FindInstanceMap(loginInstanceId);
return (map && map->GetId() == GetId()) ? map : nullptr; // is this check necessary? or does MapInstanced only find instances of itself?
}
InstanceGroupBind* groupBind = nullptr;
Group* group = player->GetGroup();
// use the player's difficulty setting (it may not be the same as the group's)
if (group)
@@ -278,8 +287,8 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
return true;
}
bool MapInstanced::CanEnter(Player* /*player*/)
Map::EnterState MapInstanced::CannotEnter(Player* /*player*/)
{
//ABORT();
return true;
return CAN_ENTER;
}
+3 -3
View File
@@ -37,13 +37,13 @@ class MapInstanced : public Map
void DelayedUpdate(const uint32 diff) override;
//void RelocationNotify();
void UnloadAll() override;
bool CanEnter(Player* player) override;
EnterState CannotEnter(Player* /*player*/) override;
Map* CreateInstanceForPlayer(const uint32 mapId, Player* player);
Map* CreateInstanceForPlayer(const uint32 mapId, Player* player, uint32 loginInstanceId=0);
Map* FindInstanceMap(uint32 instanceId) const
{
InstancedMaps::const_iterator i = m_InstancedMaps.find(instanceId);
return(i == m_InstancedMaps.end() ? NULL : i->second);
return(i == m_InstancedMaps.end() ? nullptr : i->second);
}
bool DestroyInstance(InstancedMaps::iterator &itr);
+19 -46
View File
@@ -97,12 +97,12 @@ Map* MapManager::FindBaseNonInstanceMap(uint32 mapId) const
return map;
}
Map* MapManager::CreateMap(uint32 id, Player* player)
Map* MapManager::CreateMap(uint32 id, Player* player, uint32 loginInstanceId)
{
Map* m = CreateBaseMap(id);
if (m && m->Instanceable())
m = ((MapInstanced*)m)->CreateInstanceForPlayer(id, player);
m = ((MapInstanced*)m)->CreateInstanceForPlayer(id, player, loginInstanceId);
return m;
}
@@ -119,48 +119,36 @@ Map* MapManager::FindMap(uint32 mapid, uint32 instanceId) const
return ((MapInstanced*)map)->FindInstanceMap(instanceId);
}
bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck)
Map::EnterState MapManager::PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck)
{
MapEntry const* entry = sMapStore.LookupEntry(mapid);
if (!entry)
return false;
return Map::CANNOT_ENTER_NO_ENTRY;
if (!entry->IsDungeon())
return true;
return Map::CAN_ENTER;
InstanceTemplate const* instance = sObjectMgr->GetInstanceTemplate(mapid);
if (!instance)
return false;
return Map::CANNOT_ENTER_UNINSTANCED_DUNGEON;
Difficulty targetDifficulty, requestedDifficulty;
targetDifficulty = requestedDifficulty = player->GetDifficulty(entry->IsRaid());
// Get the highest available difficulty if current setting is higher than the instance allows
MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(entry->MapID, targetDifficulty);
if (!mapDiff)
{
player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY, requestedDifficulty);
return false;
}
return Map::CANNOT_ENTER_DIFFICULTY_UNAVAILABLE;
//Bypass checks for GMs
if (player->IsGameMaster())
return true;
return Map::CAN_ENTER;
char const* mapName = entry->name[player->GetSession()->GetSessionDbcLocale()];
Group* group = player->GetGroup();
if (entry->IsRaid())
{
// can only enter in a raid group
if (entry->IsRaid()) // can only enter in a raid group
if ((!group || !group->isRaidGroup()) && !sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_RAID))
{
// probably there must be special opcode, because client has this string constant in GlobalStrings.lua
/// @todo this is not a good place to send the message
player->GetSession()->SendAreaTriggerMessage(player->GetSession()->GetTrinityString(LANG_INSTANCE_RAID_GROUP_ONLY), mapName);
TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter instance '%s'", player->GetName().c_str(), mapName);
return false;
}
}
return Map::CANNOT_ENTER_NOT_IN_RAID;
if (!player->IsAlive())
{
@@ -178,12 +166,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck)
} while (corpseMap);
if (!corpseMap)
{
WorldPacket data(SMSG_CORPSE_NOT_IN_INSTANCE);
player->GetSession()->SendPacket(&data);
TC_LOG_DEBUG("maps", "MAP: Player '%s' does not have a corpse in instance '%s' and cannot enter.", player->GetName().c_str(), mapName);
return false;
}
return Map::CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE;
TC_LOG_DEBUG("maps", "MAP: Player '%s' has corpse in instance '%s' and can enter.", player->GetName().c_str(), mapName);
}
@@ -192,23 +175,13 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck)
}
//Get instance where player's group is bound & its map
if (group)
if (!loginCheck && group)
{
InstanceGroupBind* boundInstance = group->GetBoundInstance(entry);
if (boundInstance && boundInstance->save)
if (Map* boundMap = sMapMgr->FindMap(mapid, boundInstance->save->GetInstanceId()))
if (!loginCheck && !boundMap->CanEnter(player))
return false;
/*
This check has to be moved to InstanceMap::CanEnter()
// Player permanently bounded to different instance than groups one
InstancePlayerBind* playerBoundedInstance = player->GetBoundInstance(mapid, player->GetDifficulty(entry->IsRaid()));
if (playerBoundedInstance && playerBoundedInstance->perm && playerBoundedInstance->save &&
boundedInstance->save->GetInstanceId() != playerBoundedInstance->save->GetInstanceId())
{
/// @todo send some kind of error message to the player
return false;
}*/
if (Map::EnterState denyReason = boundMap->CannotEnter(player))
return denyReason;
}
// players are only allowed to enter 5 instances per hour
@@ -220,14 +193,14 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck)
// instanceId can never be 0 - will not be found
if (!player->CheckInstanceCount(instanceIdToCheck) && !player->isDead())
{
player->SendTransferAborted(mapid, TRANSFER_ABORT_TOO_MANY_INSTANCES);
return false;
}
return Map::CANNOT_ENTER_TOO_MANY_INSTANCES;
}
//Other requirements
return player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, true);
if (player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, true))
return Map::CAN_ENTER;
else
return Map::CANNOT_ENTER_UNSPECIFIED_REASON;
}
void MapManager::Update(uint32 diff)
+2 -2
View File
@@ -39,7 +39,7 @@ class MapManager
Map* CreateBaseMap(uint32 mapId);
Map* FindBaseNonInstanceMap(uint32 mapId) const;
Map* CreateMap(uint32 mapId, Player* player);
Map* CreateMap(uint32 mapId, Player* player, uint32 loginInstanceId=0);
Map* FindMap(uint32 mapId, uint32 instanceId) const;
uint16 GetAreaFlag(uint32 mapid, float x, float y, float z) const
@@ -108,7 +108,7 @@ class MapManager
void DoDelayedMovesAndRemoves();
bool CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck = false);
Map::EnterState PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck = false);
void InitializeVisibilityDistanceInfo();
/* statistics */
+2 -1
View File
@@ -1203,7 +1203,8 @@ enum TrinityStrings
LANG_NPCINFO_INHABIT_TYPE = 11008,
LANG_NPCINFO_FLAGS_EXTRA = 11009,
LANG_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION = 11010
LANG_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION = 11010,
LANG_INSTANCE_BIND_MISMATCH = 11014
};
#endif