mirror of
https://github.com/araxiaonline/TrinityCore.git
synced 2026-06-21 15:27:47 -04:00
Merge pull request #16110 from Treeston/3.3.5-instancerevive
Game/Maps: Clean up instance zone-in handling
This commit is contained in:
@@ -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.");
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user