diff options
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 220 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 31 | ||||
-rw-r--r-- | src/server/game/Handlers/MiscHandler.cpp | 90 | ||||
-rw-r--r-- | src/server/game/Instances/InstanceLockMgr.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Maps/Map.cpp | 75 | ||||
-rw-r--r-- | src/server/game/Maps/Map.h | 66 |
6 files changed, 220 insertions, 264 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c201032cf77..6ba771eba41 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1434,8 +1434,11 @@ 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 (Map::PlayerCannotEnter(mapid, this, false)) + if (TransferAbortParams abortParams = Map::PlayerCannotEnter(mapid, this)) + { + SendTransferAborted(mapid, abortParams.Reason, abortParams.Arg, abortParams.MapDifficultyXConditionId); return false; + } // Seamless teleport can happen only if cosmetic maps match if (!oldmap || @@ -1443,113 +1446,104 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati !((oldmap->GetEntry()->CosmeticParentMapID != -1) ^ (oldmap->GetEntry()->CosmeticParentMapID != mEntry->CosmeticParentMapID)))) options &= ~TELE_TO_SEAMLESS; - //I think this always returns true. Correct me if I am wrong. - // If the map is not created, assume it is possible to enter it. - // It will be created in the WorldPortAck. - //Map* map = sMapMgr->FindBaseNonInstanceMap(mapid); - //if (!map || map->CanEnter(this)) - { - //lets reset near teleport flag if it wasn't reset during chained teleports - SetSemaphoreTeleportNear(false); - //setup delayed teleport flag - SetDelayedTeleportFlag(IsCanDelayTeleport()); - //if teleport spell is cast in Unit::Update() func - //then we need to delay it until update process will be finished - if (IsHasDelayedTeleport()) - { - SetSemaphoreTeleportFar(true); - //lets save teleport destination for player - m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); - m_teleport_instanceId = instanceId; - m_teleport_options = options; - return true; - } - - SetSelection(ObjectGuid::Empty); + //lets reset near teleport flag if it wasn't reset during chained teleports + SetSemaphoreTeleportNear(false); + //setup delayed teleport flag + SetDelayedTeleportFlag(IsCanDelayTeleport()); + //if teleport spell is cast in Unit::Update() func + //then we need to delay it until update process will be finished + if (IsHasDelayedTeleport()) + { + SetSemaphoreTeleportFar(true); + //lets save teleport destination for player + m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); + m_teleport_instanceId = instanceId; + m_teleport_options = options; + return true; + } - CombatStop(); + SetSelection(ObjectGuid::Empty); - ResetContestedPvP(); + CombatStop(); - // remove player from battleground on far teleport (when changing maps) - if (Battleground const* bg = GetBattleground()) - { - // Note: at battleground join battleground id set before teleport - // and we already will found "current" battleground - // just need check that this is targeted map or leave - if (bg->GetMapId() != mapid) - LeaveBattleground(false); // don't teleport to entry point - } + ResetContestedPvP(); - // remove arena spell coldowns/buffs now to also remove pet's cooldowns before it's temporarily unsummoned - if (mEntry->IsBattleArena() && !IsGameMaster()) - { - RemoveArenaSpellCooldowns(true); - RemoveArenaAuras(); - if (pet) - pet->RemoveArenaAuras(); - } + // remove player from battleground on far teleport (when changing maps) + if (Battleground const* bg = GetBattleground()) + { + // Note: at battleground join battleground id set before teleport + // and we already will found "current" battleground + // just need check that this is targeted map or leave + if (bg->GetMapId() != mapid) + LeaveBattleground(false); // don't teleport to entry point + } - // remove pet on map change + // remove arena spell coldowns/buffs now to also remove pet's cooldowns before it's temporarily unsummoned + if (mEntry->IsBattleArena() && !IsGameMaster()) + { + RemoveArenaSpellCooldowns(true); + RemoveArenaAuras(); if (pet) - UnsummonPetTemporaryIfAny(); + pet->RemoveArenaAuras(); + } - // remove all dyn objects - RemoveAllDynObjects(); + // remove pet on map change + if (pet) + UnsummonPetTemporaryIfAny(); - // remove all areatriggers entities - RemoveAllAreaTriggers(); + // remove all dyn objects + RemoveAllDynObjects(); - // stop spellcasting - // not attempt interrupt teleportation spell at caster teleport - if (!(options & TELE_TO_SPELL)) - if (IsNonMeleeSpellCast(true)) - InterruptNonMeleeSpells(true); + // remove all areatriggers entities + RemoveAllAreaTriggers(); - //remove auras before removing from map... - RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Moving | SpellAuraInterruptFlags::Turning); + // stop spellcasting + // not attempt interrupt teleportation spell at caster teleport + if (!(options & TELE_TO_SPELL)) + if (IsNonMeleeSpellCast(true)) + InterruptNonMeleeSpells(true); - if (!GetSession()->PlayerLogout() && !(options & TELE_TO_SEAMLESS)) - { - // send transfer packets - WorldPackets::Movement::TransferPending transferPending; - transferPending.MapID = mapid; - transferPending.OldMapPosition = GetPosition(); - if (Transport* transport = dynamic_cast<Transport*>(GetTransport())) - { - transferPending.Ship.emplace(); - transferPending.Ship->ID = transport->GetEntry(); - transferPending.Ship->OriginMapID = GetMapId(); - } + //remove auras before removing from map... + RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Moving | SpellAuraInterruptFlags::Turning); - SendDirectMessage(transferPending.Write()); + if (!GetSession()->PlayerLogout() && !(options & TELE_TO_SEAMLESS)) + { + // send transfer packets + WorldPackets::Movement::TransferPending transferPending; + transferPending.MapID = mapid; + transferPending.OldMapPosition = GetPosition(); + if (Transport* transport = dynamic_cast<Transport*>(GetTransport())) + { + transferPending.Ship.emplace(); + transferPending.Ship->ID = transport->GetEntry(); + transferPending.Ship->OriginMapID = GetMapId(); } - // remove from old map now - if (oldmap) - oldmap->RemovePlayerFromMap(this, false); + SendDirectMessage(transferPending.Write()); + } - m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); - m_teleport_instanceId = instanceId; - m_teleport_options = options; - SetFallInformation(0, GetPositionZ()); - // if the player is saved before worldportack (at logout for example) - // this will be used instead of the current location in SaveToDB + // remove from old map now + if (oldmap) + oldmap->RemovePlayerFromMap(this, false); - if (!GetSession()->PlayerLogout()) - { - WorldPackets::Movement::SuspendToken suspendToken; - suspendToken.SequenceIndex = m_movementCounter; // not incrementing - suspendToken.Reason = options & TELE_TO_SEAMLESS ? 2 : 1; - SendDirectMessage(suspendToken.Write()); - } + m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); + m_teleport_instanceId = instanceId; + m_teleport_options = options; + SetFallInformation(0, GetPositionZ()); + // if the player is saved before worldportack (at logout for example) + // this will be used instead of the current location in SaveToDB - // move packet sent by client always after far teleport - // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet - SetSemaphoreTeleportFar(true); + if (!GetSession()->PlayerLogout()) + { + WorldPackets::Movement::SuspendToken suspendToken; + suspendToken.SequenceIndex = m_movementCounter; // not incrementing + suspendToken.Reason = options & TELE_TO_SEAMLESS ? 2 : 1; + SendDirectMessage(suspendToken.Write()); } - //else - // return false; + + // move packet sent by client always after far teleport + // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet + SetSemaphoreTeleportFar(true); } return true; } @@ -17438,31 +17432,9 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol } else if (map->IsDungeon()) // if map is dungeon... { - if (Map::EnterState denyReason = ((InstanceMap*)map)->CannotEnter(this)) // ... and can't enter map, then look for entry point. + if (TransferAbortParams denyReason = 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->GetDifficultyID()); - 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; - case Map::CANNOT_ENTER_INSTANCE_SHUTTING_DOWN: - SendTransferAborted(map->GetId(), TRANSFER_ABORT_NOT_FOUND); - break; - default: - break; - } + SendTransferAborted(map->GetId(), denyReason.Reason, denyReason.Arg, denyReason.MapDifficultyXConditionId); areaTrigger = sObjectMgr->GetGoBackTrigger(mapId); check = true; } @@ -19014,7 +18986,7 @@ void Player::SendRaidInfo() SendDirectMessage(instanceInfo.Write()); } -bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report) +bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, TransferAbortParams* params, bool report) { if (!IsGameMaster()) { @@ -19065,12 +19037,6 @@ bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report else if (ar->item2 && !HasItemCount(ar->item2)) missingItem = ar->item2; - if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, target_map, this)) - { - GetSession()->SendNotification("%s", GetSession()->GetTrinityString(LANG_INSTANCE_CLOSED)); - return false; - } - if (GetTeam() == ALLIANCE && ar->quest_A && !GetQuestRewardStatus(ar->quest_A)) missingQuest = ar->quest_A; else if (GetTeam() == HORDE && ar->quest_H && !GetQuestRewardStatus(ar->quest_H)) @@ -19088,12 +19054,22 @@ bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report if (LevelMin || LevelMax || failedMapDifficultyXCondition || missingItem || missingQuest || missingAchievement) { + if (params) + params->Reason = TRANSFER_ABORT_ERROR; + if (report) { if (missingQuest && !ar->questFailedText.empty()) ChatHandler(GetSession()).PSendSysMessage("%s", ar->questFailedText.c_str()); else if (mapDiff->Message[sWorld->GetDefaultDbcLocale()][0] != '\0' || failedMapDifficultyXCondition) // if (missingAchievement) covered by this case - SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, target_difficulty, failedMapDifficultyXCondition); + { + if (params) + { + params->Reason = TRANSFER_ABORT_DIFFICULTY; + params->Arg = target_difficulty; + params->MapDifficultyXConditionId = failedMapDifficultyXCondition; + } + } else if (missingItem) GetSession()->SendNotification(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(missingItem))->GetName(GetSession()->GetSessionDbcLocale())); else if (LevelMin) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 160529696fe..0b24b304a86 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -63,6 +63,7 @@ struct RewardPackEntry; struct SkillRaceClassInfoEntry; struct TalentEntry; struct TrainerSpell; +struct TransferAbortParams; struct VendorItem; class AELootResult; @@ -99,8 +100,9 @@ enum ItemClass : uint8; enum LootError : uint8; enum LootType : uint8; enum PlayerRestState : uint8; -enum RestTypes : uint8; enum class PlayerCreateMode : int8; +enum RestTypes : uint8; +enum TransferAbortReason : uint32; namespace BattlePets { @@ -743,31 +745,6 @@ enum class ItemSearchCallbackResult Continue }; -enum TransferAbortReason : uint32 -{ - TRANSFER_ABORT_NONE = 0, - TRANSFER_ABORT_ERROR = 1, - TRANSFER_ABORT_MAX_PLAYERS = 2, // Transfer Aborted: instance is full - TRANSFER_ABORT_NOT_FOUND = 3, // Transfer Aborted: instance not found - TRANSFER_ABORT_TOO_MANY_INSTANCES = 4, // You have entered too many instances recently. - TRANSFER_ABORT_ZONE_IN_COMBAT = 6, // Unable to zone in while an encounter is in progress. - TRANSFER_ABORT_INSUF_EXPAN_LVL = 7, // You must have <TBC, WotLK> expansion installed to access this area. - TRANSFER_ABORT_DIFFICULTY = 8, // <Normal, Heroic, Epic> difficulty mode is not available for %s. - TRANSFER_ABORT_UNIQUE_MESSAGE = 9, // Until you've escaped TLK's grasp, you cannot leave this place! - TRANSFER_ABORT_TOO_MANY_REALM_INSTANCES = 10, // Additional instances cannot be launched, please try again later. - TRANSFER_ABORT_NEED_GROUP = 11, // Transfer Aborted: you must be in a raid group to enter this instance - TRANSFER_ABORT_NOT_FOUND_2 = 12, // Transfer Aborted: instance not found - TRANSFER_ABORT_NOT_FOUND_3 = 13, // Transfer Aborted: instance not found - TRANSFER_ABORT_NOT_FOUND_4 = 14, // Transfer Aborted: instance not found - TRANSFER_ABORT_REALM_ONLY = 15, // All players in the party must be from the same realm to enter %s. - TRANSFER_ABORT_MAP_NOT_ALLOWED = 16, // Map cannot be entered at this time. - TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE = 18, // You are already locked to %s - TRANSFER_ABORT_ALREADY_COMPLETED_ENCOUNTER = 19, // You are ineligible to participate in at least one encounter in this instance because you are already locked to an instance in which it has been defeated. - TRANSFER_ABORT_DIFFICULTY_NOT_FOUND = 22, // client writes to console "Unable to resolve requested difficultyID %u to actual difficulty for map %d" - TRANSFER_ABORT_XREALM_ZONE_DOWN = 24, // Transfer Aborted: cross-realm zone is down - TRANSFER_ABORT_SOLO_PLAYER_SWITCH_DIFFICULTY = 26, // This instance is already in progress. You may only switch difficulties from inside the instance. -}; - enum NewWorldReason { NEW_WORLD_NORMAL = 16, // Normal map change @@ -2529,7 +2506,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SetPendingBind(uint32 instanceId, uint32 bindTimer); bool HasPendingBind() const { return _pendingBindId > 0; } void SendRaidInfo(); - bool Satisfy(AccessRequirement const* ar, uint32 target_map, bool report = false); + bool Satisfy(AccessRequirement const* ar, uint32 target_map, TransferAbortParams* params = nullptr, bool report = false); bool CheckInstanceValidity(bool /*isLogin*/); bool CheckInstanceCount(uint32 instanceId) const; void AddInstanceEnterTime(uint32 instanceId, time_t enterTime); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 60cdd99619c..41695b94cab 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -573,70 +573,78 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge bool teleported = false; if (player->GetMapId() != at->target_mapId) { - if (Map::EnterState denyReason = Map::PlayerCannotEnter(at->target_mapId, player, false)) + if (!player->IsAlive()) { - bool reviveAtTrigger = false; // should we revive the player if he is trying to enter the correct instance? - switch (denyReason) + if (player->HasCorpse()) { - case Map::CANNOT_ENTER_NO_ENTRY: + // let enter in ghost mode in instance that connected to inner instance with corpse + uint32 corpseMap = player->GetCorpseLocation().GetMapId(); + do + { + if (corpseMap == at->target_mapId) + break; + + InstanceTemplate const* corpseInstance = sObjectMgr->GetInstanceTemplate(corpseMap); + corpseMap = corpseInstance ? corpseInstance->Parent : 0; + } while (corpseMap); + + if (!corpseMap) + { + SendPacket(WorldPackets::AreaTrigger::AreaTriggerNoCorpse().Write()); + return; + } + + TC_LOG_DEBUG("maps", "MAP: Player '%s' has corpse in instance %u and can enter.", player->GetName().c_str(), at->target_mapId); + } + else + TC_LOG_DEBUG("maps", "Map::CanPlayerEnter - player '%s' is dead but does not have a corpse!", player->GetName().c_str()); + } + + if (TransferAbortParams denyReason = Map::PlayerCannotEnter(at->target_mapId, player)) + { + switch (denyReason.Reason) + { + case TRANSFER_ABORT_MAP_NOT_ALLOWED: 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: + case TRANSFER_ABORT_DIFFICULTY: 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->ID, TRANSFER_ABORT_DIFFICULTY, player->GetDifficultyID(entry)); break; - case Map::CANNOT_ENTER_NOT_IN_RAID: + case TRANSFER_ABORT_NEED_GROUP: TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter map %d", player->GetName().c_str(), at->target_mapId); player->SendRaidGroupOnlyMessage(RAID_GROUP_ERR_ONLY, 0); - reviveAtTrigger = true; break; - case Map::CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE: - SendPacket(WorldPackets::AreaTrigger::AreaTriggerNoCorpse().Write()); - 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: - player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE); + case TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE: TC_LOG_DEBUG("maps", "MAP: Player '%s' cannot enter instance map %d because their permanent bind is incompatible with their group's", player->GetName().c_str(), at->target_mapId); - reviveAtTrigger = true; break; - case Map::CANNOT_ENTER_ALREADY_COMPLETED_ENCOUNTER: - player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ALREADY_COMPLETED_ENCOUNTER); + case TRANSFER_ABORT_ALREADY_COMPLETED_ENCOUNTER: TC_LOG_DEBUG("maps", "MAP: Player '%s' cannot enter instance map %d because their permanent bind is incompatible with their group's", player->GetName().c_str(), at->target_mapId); - reviveAtTrigger = true; break; - case Map::CANNOT_ENTER_TOO_MANY_INSTANCES: - player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_TOO_MANY_INSTANCES); + case 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; + case TRANSFER_ABORT_MAX_PLAYERS: break; - case Map::CANNOT_ENTER_ZONE_IN_COMBAT: - player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ZONE_IN_COMBAT); - reviveAtTrigger = true; + case TRANSFER_ABORT_ZONE_IN_COMBAT: break; - case Map::CANNOT_ENTER_INSTANCE_SHUTTING_DOWN: - player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_NOT_FOUND); + case TRANSFER_ABORT_NOT_FOUND: TC_LOG_DEBUG("maps", "MAP: Player '%s' cannot enter instance map %d because instance is resetting.", player->GetName().c_str(), at->target_mapId); - 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(); - } + if (denyReason.Reason != TRANSFER_ABORT_NEED_GROUP) + player->SendTransferAborted(at->target_mapId, denyReason.Reason, denyReason.Arg, denyReason.MapDifficultyXConditionId); + + if (!player->IsAlive() && player->HasCorpse()) + { + if (player->GetCorpseLocation().GetMapId() == at->target_mapId) + { + player->ResurrectPlayer(0.5f); + player->SpawnCorpseBones(); + } + } return; } diff --git a/src/server/game/Instances/InstanceLockMgr.cpp b/src/server/game/Instances/InstanceLockMgr.cpp index 1296fb951dd..d0353ad00b0 100644 --- a/src/server/game/Instances/InstanceLockMgr.cpp +++ b/src/server/game/Instances/InstanceLockMgr.cpp @@ -21,8 +21,8 @@ #include "Errors.h" #include "GameTime.h" #include "Log.h" +#include "Map.h" // for TransferAbortReason #include "MapManager.h" -#include "Player.h" // for TransferAbortReason #include "World.h" InstanceLockData::InstanceLockData() = default; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 9f8e38571db..a061cfacd1c 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1754,75 +1754,51 @@ bool Map::getObjectHitPos(PhaseShift const& phaseShift, float x1, float y1, floa return result; } -Map::EnterState Map::PlayerCannotEnter(uint32 mapid, Player* player, bool /*loginCheck*/) +TransferAbortParams Map::PlayerCannotEnter(uint32 mapid, Player* player) { MapEntry const* entry = sMapStore.LookupEntry(mapid); if (!entry) - return CANNOT_ENTER_NO_ENTRY; + return TRANSFER_ABORT_MAP_NOT_ALLOWED; if (!entry->IsDungeon()) - return CAN_ENTER; + return TRANSFER_ABORT_NONE; - Difficulty targetDifficulty, requestedDifficulty; - targetDifficulty = requestedDifficulty = player->GetDifficultyID(entry); + Difficulty targetDifficulty = player->GetDifficultyID(entry); // Get the highest available difficulty if current setting is higher than the instance allows MapDifficultyEntry const* mapDiff = sDB2Manager.GetDownscaledMapDifficultyData(mapid, targetDifficulty); if (!mapDiff) - return CANNOT_ENTER_DIFFICULTY_UNAVAILABLE; + return TRANSFER_ABORT_DIFFICULTY; //Bypass checks for GMs if (player->IsGameMaster()) - return CAN_ENTER; + return TRANSFER_ABORT_NONE; //Other requirements - if (!player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, true)) - return CANNOT_ENTER_UNSPECIFIED_REASON; - - char const* mapName = entry->MapName[sWorld->GetDefaultDbcLocale()]; + { + TransferAbortParams params(TRANSFER_ABORT_NONE); + if (!player->Satisfy(sObjectMgr->GetAccessRequirement(mapid, targetDifficulty), mapid, ¶ms, true)) + return params; + } Group* group = player->GetGroup(); if (entry->IsRaid() && entry->Expansion() >= sWorld->getIntConfig(CONFIG_EXPANSION)) // can only enter in a raid group but raids from old expansion don't need a group if ((!group || !group->isRaidGroup()) && !sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_RAID)) - return CANNOT_ENTER_NOT_IN_RAID; - - if (!player->IsAlive()) - { - if (player->HasCorpse()) - { - // let enter in ghost mode in instance that connected to inner instance with corpse - uint32 corpseMap = player->GetCorpseLocation().GetMapId(); - do - { - if (corpseMap == mapid) - break; - - InstanceTemplate const* corpseInstance = sObjectMgr->GetInstanceTemplate(corpseMap); - corpseMap = corpseInstance ? corpseInstance->Parent : 0; - } while (corpseMap); - - if (!corpseMap) - return 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); - } - else - TC_LOG_DEBUG("maps", "Map::CanPlayerEnter - player '%s' is dead but does not have a corpse!", player->GetName().c_str()); - } + return TRANSFER_ABORT_NEED_GROUP; if (entry->Instanceable()) { //Get instance where player's group is bound & its map uint32 instanceIdToCheck = sMapMgr->FindInstanceIdForPlayer(mapid, player); if (Map* boundMap = sMapMgr->FindMap(mapid, instanceIdToCheck)) - if (EnterState denyReason = boundMap->CannotEnter(player)) + if (TransferAbortParams denyReason = boundMap->CannotEnter(player)) return denyReason; // players are only allowed to enter 10 instances per hour if (!entry->GetFlags2().HasFlag(MapFlags2::IgnoreInstanceFarmLimit) && entry->IsDungeon() && !player->CheckInstanceCount(instanceIdToCheck) && !player->isDead()) - return CANNOT_ENTER_TOO_MANY_INSTANCES; + return TRANSFER_ABORT_TOO_MANY_INSTANCES; } - return CAN_ENTER; + return TRANSFER_ABORT_NONE; } char const* Map::GetMapName() const @@ -2807,13 +2783,13 @@ void InstanceMap::InitVisibilityDistance() /* Do map specific checks to see if the player can enter */ -Map::EnterState InstanceMap::CannotEnter(Player* player) +TransferAbortParams InstanceMap::CannotEnter(Player* player) { if (player->GetMapRef().getTarget() == this) { TC_LOG_ERROR("maps", "InstanceMap::CannotEnter - player %s %s already in map %d, %d, %d!", player->GetName().c_str(), player->GetGUID().ToString().c_str(), GetId(), GetInstanceId(), GetDifficultyID()); ABORT(); - return CANNOT_ENTER_ALREADY_IN_MAP; + return TRANSFER_ABORT_ERROR; } // allow GM's to enter @@ -2825,22 +2801,19 @@ Map::EnterState InstanceMap::CannotEnter(Player* player) 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()); - return CANNOT_ENTER_MAX_PLAYERS; + return TRANSFER_ABORT_MAX_PLAYERS; } // 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()) - return CANNOT_ENTER_ZONE_IN_COMBAT; + return TRANSFER_ABORT_ZONE_IN_COMBAT; if (i_instanceLock) { // cannot enter if player is permanent saved to a different instance id TransferAbortReason lockError = sInstanceLockMgr.CanJoinInstanceLock(player->GetGUID(), { GetEntry(), GetMapDifficulty() }, i_instanceLock); - if (lockError == TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE) - return CANNOT_ENTER_INSTANCE_BIND_MISMATCH; - - if (lockError == TRANSFER_ABORT_ALREADY_COMPLETED_ENCOUNTER) - return CANNOT_ENTER_ALREADY_COMPLETED_ENCOUNTER; + if (lockError != TRANSFER_ABORT_NONE) + return lockError; } return Map::CannotEnter(player); @@ -3290,17 +3263,17 @@ void BattlegroundMap::InitVisibilityDistance() m_VisibilityNotifyPeriod = IsBattleArena() ? World::GetVisibilityNotifyPeriodInArenas() : World::GetVisibilityNotifyPeriodInBG(); } -Map::EnterState BattlegroundMap::CannotEnter(Player* player) +TransferAbortParams BattlegroundMap::CannotEnter(Player* player) { if (player->GetMapRef().getTarget() == this) { TC_LOG_ERROR("maps", "BGMap::CannotEnter - player %s is already in map!", player->GetGUID().ToString().c_str()); ABORT(); - return CANNOT_ENTER_ALREADY_IN_MAP; + return TRANSFER_ABORT_ERROR; } if (player->GetBattlegroundId() != GetInstanceId()) - return CANNOT_ENTER_INSTANCE_BIND_MISMATCH; + return TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE; // player number limit is checked in bgmgr, no need to do it here diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 985ac6f01e2..b1c6fce1d8e 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -41,7 +41,6 @@ #include <list> #include <map> #include <memory> -#include <mutex> #include <set> #include <unordered_set> @@ -82,6 +81,46 @@ enum class ItemContext : uint8; namespace Trinity { struct ObjectUpdater; } namespace VMAP { enum class ModelIgnoreFlags : uint32; } +enum TransferAbortReason : uint32 +{ + TRANSFER_ABORT_NONE = 0, + TRANSFER_ABORT_ERROR = 1, + TRANSFER_ABORT_MAX_PLAYERS = 2, // Transfer Aborted: instance is full + TRANSFER_ABORT_NOT_FOUND = 3, // Transfer Aborted: instance not found + TRANSFER_ABORT_TOO_MANY_INSTANCES = 4, // You have entered too many instances recently. + TRANSFER_ABORT_ZONE_IN_COMBAT = 6, // Unable to zone in while an encounter is in progress. + TRANSFER_ABORT_INSUF_EXPAN_LVL = 7, // You must have <TBC, WotLK> expansion installed to access this area. + TRANSFER_ABORT_DIFFICULTY = 8, // <Normal, Heroic, Epic> difficulty mode is not available for %s. + TRANSFER_ABORT_UNIQUE_MESSAGE = 9, // Until you've escaped TLK's grasp, you cannot leave this place! + TRANSFER_ABORT_TOO_MANY_REALM_INSTANCES = 10, // Additional instances cannot be launched, please try again later. + TRANSFER_ABORT_NEED_GROUP = 11, // Transfer Aborted: you must be in a raid group to enter this instance + TRANSFER_ABORT_NOT_FOUND_2 = 12, // Transfer Aborted: instance not found + TRANSFER_ABORT_NOT_FOUND_3 = 13, // Transfer Aborted: instance not found + TRANSFER_ABORT_NOT_FOUND_4 = 14, // Transfer Aborted: instance not found + TRANSFER_ABORT_REALM_ONLY = 15, // All players in the party must be from the same realm to enter %s. + TRANSFER_ABORT_MAP_NOT_ALLOWED = 16, // Map cannot be entered at this time. + TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE = 18, // You are already locked to %s + TRANSFER_ABORT_ALREADY_COMPLETED_ENCOUNTER = 19, // You are ineligible to participate in at least one encounter in this instance because you are already locked to an instance in which it has been defeated. + TRANSFER_ABORT_DIFFICULTY_NOT_FOUND = 22, // client writes to console "Unable to resolve requested difficultyID %u to actual difficulty for map %d" + TRANSFER_ABORT_XREALM_ZONE_DOWN = 24, // Transfer Aborted: cross-realm zone is down + TRANSFER_ABORT_SOLO_PLAYER_SWITCH_DIFFICULTY = 26, // This instance is already in progress. You may only switch difficulties from inside the instance. + TRANSFER_ABORT_NOT_CROSS_FACTION_COMPATIBLE = 33, // This instance isn't available for cross-faction groups +}; + +struct TransferAbortParams +{ + TransferAbortParams(TransferAbortReason reason, uint8 arg = 0, int32 mapDifficultyXConditionId = 0) + : Reason(reason), Arg(arg), MapDifficultyXConditionId(mapDifficultyXConditionId) + { + } + + TransferAbortReason Reason; + uint8 Arg; + int32 MapDifficultyXConditionId; + + operator bool() const { return Reason != TRANSFER_ABORT_NONE; } +}; + struct ScriptAction { ObjectGuid sourceGUID; @@ -262,25 +301,8 @@ class TC_GAME_API Map : public GridRefManager<NGridType> uint32 GetInstanceId() const { return i_InstanceId; } - 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_ALREADY_COMPLETED_ENCOUNTER, // Player is locked to encounter that wasn't defeated in the instance yet - 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_INSTANCE_SHUTTING_DOWN, - CANNOT_ENTER_UNSPECIFIED_REASON - }; - static EnterState PlayerCannotEnter(uint32 mapid, Player* player, bool loginCheck = false); - virtual EnterState CannotEnter(Player* /*player*/) { return CAN_ENTER; } + static TransferAbortParams PlayerCannotEnter(uint32 mapid, Player* player); + virtual TransferAbortParams CannotEnter(Player* /*player*/) { return { TRANSFER_ABORT_NONE }; } char const* GetMapName() const; // have meaning only for instanced map (that have set real difficulty) @@ -823,7 +845,7 @@ class TC_GAME_API InstanceMap : public Map void UpdateInstanceLock(UpdateBossStateSaveDataEvent const& updateSaveDataEvent); void UpdateInstanceLock(UpdateAdditionalSaveDataEvent const& updateSaveDataEvent); void CreateInstanceLockForPlayer(Player* player); - EnterState CannotEnter(Player* player) override; + TransferAbortParams CannotEnter(Player* player) override; uint32 GetMaxPlayers() const; TeamId GetTeamIdInInstance() const; @@ -852,7 +874,7 @@ class TC_GAME_API BattlegroundMap : public Map bool AddPlayerToMap(Player* player, bool initPlayer = true) override; void RemovePlayerFromMap(Player*, bool) override; - EnterState CannotEnter(Player* player) override; + TransferAbortParams CannotEnter(Player* player) override; void SetUnload(); //void UnloadAll(bool pForce); void RemoveAllPlayers() override; |