diff --git a/src/game/InstanceData.h b/src/game/InstanceData.h index 01361df8d..326499858 100644 --- a/src/game/InstanceData.h +++ b/src/game/InstanceData.h @@ -51,7 +51,6 @@ class MANGOS_DLL_SPEC InstanceData // Called every map update virtual void Update(uint32 /*diff*/) {} - // Used by the map's CanEnter function. // This is to prevent players from entering during boss encounters. virtual bool IsEncounterInProgress() const { return false; }; diff --git a/src/game/Map.cpp b/src/game/Map.cpp index 2967815f4..ae03a479a 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -1252,37 +1252,6 @@ void DungeonMap::InitVisibilityDistance() m_VisibleDistance = World::GetMaxVisibleDistanceInInstances(); } -/* - Do map specific checks to see if the player can enter -*/ -bool DungeonMap::CanEnter(Player* player) -{ - if (player->GetMapRef().getTarget() == this) - { - sLog.outError("DungeonMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode()); - MANGOS_ASSERT(false); - return false; - } - - // cannot enter if the instance is full (player cap), GMs don't count - uint32 maxPlayers = GetMaxPlayers(); - if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= maxPlayers) - { - DETAIL_LOG("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName()); - player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); - return false; - } - - // cannot enter while an encounter in the instance is in progress - if (!player->isGameMaster() && GetInstanceData() && GetInstanceData()->IsEncounterInProgress() && player->GetMapId() != GetId()) - { - player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT); - return false; - } - - return Map::CanEnter(player); -} - /* Do map specific checks and add the player to the map if successful. */ @@ -1557,19 +1526,14 @@ void BattleGroundMap::InitVisibilityDistance() bool BattleGroundMap::CanEnter(Player* player) { - if (player->GetMapRef().getTarget() == this) - { - sLog.outError("BGMap::CanEnter - player %u already in map!", player->GetGUIDLow()); - MANGOS_ASSERT(false); + if (!Map::CanEnter(player)) return false; - } if (player->GetBattleGroundId() != GetInstanceId()) return false; // player number limit is checked in bgmgr, no need to do it here - - return Map::CanEnter(player); + return true; } bool BattleGroundMap::Add(Player* player) @@ -1611,6 +1575,18 @@ void BattleGroundMap::UnloadAll(bool pForce) Map::UnloadAll(pForce); } +bool Map::CanEnter(Player* player) +{ + if (player->GetMapRef().getTarget() == this) + { + sLog.outError("Map::CanEnter -%s already in map!", player->GetGuidStr().c_str()); + MANGOS_ASSERT(false); + return false; + } + + return true; +} + /// Put scripts in the execution queue bool Map::ScriptsStart(ScriptMapMapName const& scripts, uint32 id, Object* source, Object* target) { diff --git a/src/game/Map.h b/src/game/Map.h index 4e908a427..f5eff2b48 100644 --- a/src/game/Map.h +++ b/src/game/Map.h @@ -171,7 +171,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager bool CheckGridIntegrity(Creature* c, bool moved) const; uint32 GetInstanceId() const { return i_InstanceId; } - virtual bool CanEnter(Player* /*player*/) { return true; } + virtual bool CanEnter(Player* player); const char* GetMapName() const; // have meaning only for instanced map (that have set real difficulty), NOT USE its for BaseMap @@ -384,7 +384,6 @@ class MANGOS_DLL_SPEC DungeonMap : public Map bool Reset(InstanceResetMethod method); void PermBindAllPlayers(Player* player); void UnloadAll(bool pForce) override; - bool CanEnter(Player* player) override; void SendResetWarnings(uint32 timeLeft) const; void SetResetSchedule(bool on); diff --git a/src/game/MapManager.cpp b/src/game/MapManager.cpp index 81e681404..bb6159d7d 100644 --- a/src/game/MapManager.cpp +++ b/src/game/MapManager.cpp @@ -149,62 +149,6 @@ Map* MapManager::FindMap(uint32 mapid, uint32 instanceId) const return iter->second; } -/* - checks that do not require a map to be created - will send transfer error messages on fail -*/ -bool MapManager::CanPlayerEnter(uint32 mapid, Player* player) -{ - const MapEntry* entry = sMapStore.LookupEntry(mapid); - if (!entry) - return false; - - const char* mapName = entry->name[player->GetSession()->GetSessionDbcLocale()]; - - if (entry->IsDungeon()) - { - if (entry->IsRaid()) - { - // GMs can avoid raid limitations - if (!player->isGameMaster() && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_RAID)) - { - // can only enter in a raid group - Group* group = player->GetGroup(); - if (!group || !group->isRaidGroup()) - { - // 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("You must be in a raid group to enter %s instance", mapName); - DEBUG_LOG("MAP: Player '%s' must be in a raid group to enter instance of '%s'", player->GetName(), mapName); - return false; - } - } - } - - // The player has a heroic mode and tries to enter into instance which has no a heroic mode - MapDifficultyEntry const* mapDiff = GetMapDifficultyData(entry->MapID, player->GetDifficulty(entry->map_type == MAP_RAID)); - if (!mapDiff) - { - bool isRegularTargetMap = player->GetDifficulty(entry->IsRaid()) == REGULAR_DIFFICULTY; - - // Send aborted message - // FIX ME: what about absent normal/heroic mode with specific players limit... - player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY, isRegularTargetMap ? DUNGEON_DIFFICULTY_NORMAL : DUNGEON_DIFFICULTY_HEROIC); - return false; - } - - // TODO: move this to a map dependent location - /*if(i_data && i_data->IsEncounterInProgress()) - { - DEBUG_LOG("MAP: Player '%s' can't enter instance '%s' while an encounter is in progress.", player->GetName(), GetMapName()); - player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT); - return(false); - }*/ - } - - return true; -} - void MapManager::DeleteInstance(uint32 mapid, uint32 instanceId) { Guard _guard(*this); @@ -223,8 +167,7 @@ void MapManager::DeleteInstance(uint32 mapid, uint32 instanceId) } } -void -MapManager::Update(uint32 diff) +void MapManager::Update(uint32 diff) { i_timer.Update(diff); if (!i_timer.Passed()) diff --git a/src/game/MapManager.h b/src/game/MapManager.h index 3aa383945..8ef294430 100644 --- a/src/game/MapManager.h +++ b/src/game/MapManager.h @@ -124,7 +124,6 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton TransportMap; TransportMap m_TransportsByMap; - bool CanPlayerEnter(uint32 mapid, Player* player); void InitializeVisibilityDistanceInfo(); /* statistics */ diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp index b8e67f11a..70f7cb637 100644 --- a/src/game/MiscHandler.cpp +++ b/src/game/MiscHandler.cpp @@ -799,60 +799,8 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data) player->SpawnCorpseBones(); } - // check trigger requirements - uint32 miscRequirement = 0; - AreaLockStatus lockStatus = player->GetAreaTriggerLockStatus(at, player->GetDifficulty(targetMapEntry->IsRaid()), miscRequirement); - switch (lockStatus) - { - case AREA_LOCKSTATUS_OK: - player->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT); - break; - case AREA_LOCKSTATUS_TOO_LOW_LEVEL: - SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED), miscRequirement); - break; - case AREA_LOCKSTATUS_ZONE_IN_COMBAT: - player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ZONE_IN_COMBAT); - break; - 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 - { - SendAreaTriggerMessage(GetMangosString(LANG_TELEREQ_QUEST_BLACK_MORASS)); - break; - } - else if (targetMapEntry->IsContinent()) // do not report anything for quest areatrigge - { - DEBUG_LOG("HandleAreaTriggerOpcode: LockAreaStatus %u, do not teleport, no message sent (trigger %u)", lockStatus, Trigger_ID); - break; - } - // No break here! - case AREA_LOCKSTATUS_MISSING_ITEM: - player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY, player->GetDifficulty(targetMapEntry->IsRaid())); - break; - case AREA_LOCKSTATUS_MISSING_DIFFICULTY: - { - Difficulty difficulty = player->GetDifficulty(targetMapEntry->IsRaid()); - player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY, difficulty > RAID_DIFFICULTY_10MAN_HEROIC ? RAID_DIFFICULTY_10MAN_HEROIC : difficulty); - break; - } - case AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION: - player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_INSUF_EXPAN_LVL, miscRequirement); - break; - 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; - } + // teleport player (trigger requirement will be checked on TeleportTo) + player->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT, at); } void WorldSession::HandleUpdateAccountData(WorldPacket& recv_data) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 511e6966b..4227199e5 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -1687,7 +1687,7 @@ void Player::SendTeleportPacket(float oldX, float oldY, float oldZ, float oldO) SendDirectMessage(&data); } -bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options) +bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options /*=0*/, AreaTrigger const* at /*=NULL*/) { orientation = NormalizeOrientation(orientation); @@ -1707,24 +1707,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if (!InBattleGround() && mEntry->IsBattleGroundOrArena()) return false; - // client without expansion support - if (GetSession()->Expansion() < mEntry->Expansion()) - { - DEBUG_LOG("Player %s using client without required expansion tried teleport to non accessible map %u", GetName(), mapid); - - if (GetTransport()) - RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :) - - SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL, mEntry->Expansion()); - - return false; // normal client can't teleport to this map... - } - else - { - DEBUG_LOG("Player %s is being teleported to map %u", GetName(), mapid); - } - - if (Group* grp = GetGroup()) + if (Group* grp = GetGroup()) // TODO: Verify that this is correct place grp->SetPlayerMap(GetObjectGuid(), mapid); // if we were on a transport, leave @@ -1746,8 +1729,20 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati m_movementInfo.SetMovementFlags(MOVEFLAG_NONE); DisableSpline(); - if ((GetMapId() == mapid) && (!m_transport)) + if ((GetMapId() == mapid) && (!m_transport)) // TODO the !m_transport might have unexpected effects when teleporting from transport to other place on same map { + // If we are teleported by an areatrigger, check the requirements + if (at) + { + uint32 miscRequirement = 0; + AreaLockStatus lockStatus = GetAreaTriggerLockStatus(at, GetDifficulty(mEntry->IsRaid()), miscRequirement); + if (lockStatus != AREA_LOCKSTATUS_OK) + { + SendTransferAbortedByLockStatus(mEntry, lockStatus, miscRequirement); + return false; + } + } + // lets reset far teleport flag if it wasn't reset during chained teleports SetSemaphoreTeleportFar(false); // setup delayed teleport flag @@ -1796,15 +1791,19 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // check if we can enter before stopping combat / removing pet / totems / interrupting spells // 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)) + uint32 miscRequirement = 0; + AreaLockStatus lockStatus = GetAreaTriggerLockStatus(at ? at : sObjectMgr.GetMapEntranceTrigger(mapid), GetDifficulty(mEntry->IsRaid()), miscRequirement); + if (lockStatus != AREA_LOCKSTATUS_OK) + { + SendTransferAbortedByLockStatus(mEntry, lockStatus, miscRequirement); return false; + } // If the map is not created, assume it is possible to enter it. // It will be created in the WorldPortAck. DungeonPersistentState* state = GetBoundInstanceSaveForSelfOrGroup(mapid); Map* map = sMapMgr.FindMap(mapid, state ? state->GetInstanceId() : 0); - if (!map || map->CanEnter(this)) + if (!map || map->CanEnter(this)) { // lets reset near teleport flag if it wasn't reset during chained teleports SetSemaphoreTeleportNear(false); @@ -1925,7 +1924,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati SendSavedInstances(); } } - else + else // !map->CanEnter(this) return false; } return true; @@ -20181,14 +20180,66 @@ void Player::SendUpdateToOutOfRangeGroupMembers() pet->ResetAuraUpdateMask(); } -void Player::SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg) +void Player::SendTransferAbortedByLockStatus(MapEntry const* mapEntry, AreaLockStatus lockStatus, uint32 miscRequirement) { - WorldPacket data(SMSG_TRANSFER_ABORTED, 4 + 2); - data << uint32(mapid); - data << uint8(reason); // transfer abort reason - data << uint8(arg); + MANGOS_ASSERT(mapEntry); - GetSession()->SendPacket(&data); + DEBUG_LOG("SendTransferAbortedByLockStatus: Called for %s on map %u, LockAreaStatus %u, miscRequirement %u)", GetGuidStr().c_str(), mapEntry->MapID, lockStatus, miscRequirement); + + switch (lockStatus) + { + case AREA_LOCKSTATUS_TOO_LOW_LEVEL: + GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_LEVEL_MINREQUIRED), miscRequirement); + break; + case AREA_LOCKSTATUS_ZONE_IN_COMBAT: + GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_ZONE_IN_COMBAT); + break; + case AREA_LOCKSTATUS_INSTANCE_IS_FULL: + GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_MAX_PLAYERS); + break; + case AREA_LOCKSTATUS_QUEST_NOT_COMPLETED: + if (mapEntry->MapID == 269) // Exception for Black Morass + { + GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_TELEREQ_QUEST_BLACK_MORASS)); + break; + } + else if (mapEntry->IsContinent()) // do not report anything for quest areatrigge + { + DEBUG_LOG("SendTransferAbortedByLockStatus: LockAreaStatus %u, do not teleport, no message sent (mapId %u)", lockStatus, mapEntry->MapID); + break; + } + // No break here! + case AREA_LOCKSTATUS_MISSING_ITEM: + GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_DIFFICULTY, GetDifficulty(mapEntry->IsRaid())); + break; + case AREA_LOCKSTATUS_MISSING_DIFFICULTY: + { + Difficulty difficulty = GetDifficulty(mapEntry->IsRaid()); + GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_DIFFICULTY, difficulty > RAID_DIFFICULTY_10MAN_HEROIC ? RAID_DIFFICULTY_10MAN_HEROIC : difficulty); + break; + } + case AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION: + if (GetTransport()) + RepopAtGraveyard(); + GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_INSUF_EXPAN_LVL, miscRequirement); + break; + case AREA_LOCKSTATUS_NOT_ALLOWED: + GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_MAP_NOT_ALLOWED); + break; + case AREA_LOCKSTATUS_RAID_LOCKED: + GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_NEED_GROUP); + break; + case AREA_LOCKSTATUS_UNKNOWN_ERROR: + GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_ERROR); + break; + case AREA_LOCKSTATUS_OK: + sLog.outError("SendTransferAbortedByLockStatus: LockAreaStatus AREA_LOCKSTATUS_OK received for %s (mapId %u)", GetGuidStr().c_str(), mapEntry->MapID); + MANGOS_ASSERT(false); + break; + default: + sLog.outError("SendTransfertAbortedByLockstatus: unhandled LockAreaStatus %u, when %s attempts to enter in map %u", lockStatus, GetGuidStr().c_str(), mapEntry->MapID); + break; + } } void Player::SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time) @@ -23626,8 +23677,3 @@ AreaLockStatus Player::GetAreaTriggerLockStatus(AreaTrigger const* at, Difficult return AREA_LOCKSTATUS_OK; }; - -AreaLockStatus Player::GetAreaLockStatus(uint32 mapId, Difficulty difficulty, uint32& miscRequirement) -{ - return GetAreaTriggerLockStatus(sObjectMgr.GetMapEntranceTrigger(mapId), difficulty, miscRequirement); -}; diff --git a/src/game/Player.h b/src/game/Player.h index 1bc65f8aa..694dc538e 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1057,7 +1057,7 @@ class MANGOS_DLL_SPEC Player : public Unit void RemoveFromWorld() override; void SendTeleportPacket(float oldX, float oldY, float oldZ, float oldO); - bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0); + bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0, AreaTrigger const* at = NULL); bool TeleportTo(WorldLocation const& loc, uint32 options = 0) { @@ -1089,7 +1089,6 @@ class MANGOS_DLL_SPEC Player : public Unit void SendInitialPacketsBeforeAddToMap(); void SendInitialPacketsAfterAddToMap(); - void SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg = 0); void SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time); Creature* GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask); @@ -2318,8 +2317,8 @@ class MANGOS_DLL_SPEC Player : public Unit static void ConvertInstancesToGroup(Player* player, Group* group = NULL, ObjectGuid player_guid = ObjectGuid()); DungeonPersistentState* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid); - AreaLockStatus GetAreaLockStatus(uint32 mapId, Difficulty difficulty, uint32& miscRequirement); AreaLockStatus GetAreaTriggerLockStatus(AreaTrigger const* at, Difficulty difficulty, uint32& miscRequirement); + void SendTransferAbortedByLockStatus(MapEntry const* mapEntry, AreaLockStatus lockStatus, uint32 miscRequirement = 0); /*********************************************************/ /*** GROUP SYSTEM ***/ diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp index dea7ed36b..8b663da04 100644 --- a/src/game/WorldSession.cpp +++ b/src/game/WorldSession.cpp @@ -805,6 +805,23 @@ void WorldSession::LoadTutorialsData() m_tutorialState = TUTORIALDATA_UNCHANGED; } +// Send chat information about aborted transfer (mostly used by Player::SendTransferAbortedByLockstatus()) +void WorldSession::SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg) +{ + WorldPacket data(SMSG_TRANSFER_ABORTED, 4 + 2); + data << uint32(mapid); + data << uint8(reason); // transfer abort reason + switch (reason) + { + case TRANSFER_ABORT_INSUF_EXPAN_LVL: + case TRANSFER_ABORT_DIFFICULTY: + case TRANSFER_ABORT_UNIQUE_MESSAGE: + data << uint8(arg); + break; + } + SendPacket(&data); +} + void WorldSession::SendTutorialsData() { WorldPacket data(SMSG_TUTORIAL_FLAGS, 4 * 8); diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h index 3da7e2d3f..3dee1ed70 100644 --- a/src/game/WorldSession.h +++ b/src/game/WorldSession.h @@ -250,6 +250,7 @@ class MANGOS_DLL_SPEC WorldSession void SendPartyResult(PartyOperation operation, const std::string& member, PartyResult res); void SendGroupInvite(Player* player, bool alreadyInGroup = false); void SendAreaTriggerMessage(const char* Text, ...) ATTR_PRINTF(2, 3); + void SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg = 0); void SendSetPhaseShift(uint32 phaseMask, uint16 mapId = 0); void SendQueryTimeResponse(); void SendRedirectClient(std::string& ip, uint16 port); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index aca5cff1c..effca9c76 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "12206" + #define REVISION_NR "12207" #endif // __REVISION_NR_H__