aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/Entities/Player/Player.cpp220
-rw-r--r--src/server/game/Entities/Player/Player.h31
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp90
-rw-r--r--src/server/game/Instances/InstanceLockMgr.cpp2
-rw-r--r--src/server/game/Maps/Map.cpp75
-rw-r--r--src/server/game/Maps/Map.h66
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, &params, 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;