mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Maps: Changed functions checking if map can be entered to use TransferAbortReason directly instead of intermediate Map::EnterState enum
This commit is contained in:
@@ -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())
|
||||
{
|
||||
//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);
|
||||
|
||||
CombatStop();
|
||||
|
||||
ResetContestedPvP();
|
||||
|
||||
// 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 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 pet on map change
|
||||
if (pet)
|
||||
UnsummonPetTemporaryIfAny();
|
||||
|
||||
// remove all dyn objects
|
||||
RemoveAllDynObjects();
|
||||
|
||||
// remove all areatriggers entities
|
||||
RemoveAllAreaTriggers();
|
||||
|
||||
// stop spellcasting
|
||||
// not attempt interrupt teleportation spell at caster teleport
|
||||
if (!(options & TELE_TO_SPELL))
|
||||
if (IsNonMeleeSpellCast(true))
|
||||
InterruptNonMeleeSpells(true);
|
||||
|
||||
//remove auras before removing from map...
|
||||
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Moving | SpellAuraInterruptFlags::Turning);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
SendDirectMessage(transferPending.Write());
|
||||
}
|
||||
|
||||
// remove from old map now
|
||||
if (oldmap)
|
||||
oldmap->RemovePlayerFromMap(this, false);
|
||||
|
||||
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;
|
||||
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
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!GetSession()->PlayerLogout())
|
||||
SetSelection(ObjectGuid::Empty);
|
||||
|
||||
CombatStop();
|
||||
|
||||
ResetContestedPvP();
|
||||
|
||||
// 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 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 pet on map change
|
||||
if (pet)
|
||||
UnsummonPetTemporaryIfAny();
|
||||
|
||||
// remove all dyn objects
|
||||
RemoveAllDynObjects();
|
||||
|
||||
// remove all areatriggers entities
|
||||
RemoveAllAreaTriggers();
|
||||
|
||||
// stop spellcasting
|
||||
// not attempt interrupt teleportation spell at caster teleport
|
||||
if (!(options & TELE_TO_SPELL))
|
||||
if (IsNonMeleeSpellCast(true))
|
||||
InterruptNonMeleeSpells(true);
|
||||
|
||||
//remove auras before removing from map...
|
||||
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Moving | SpellAuraInterruptFlags::Turning);
|
||||
|
||||
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()))
|
||||
{
|
||||
WorldPackets::Movement::SuspendToken suspendToken;
|
||||
suspendToken.SequenceIndex = m_movementCounter; // not incrementing
|
||||
suspendToken.Reason = options & TELE_TO_SEAMLESS ? 2 : 1;
|
||||
SendDirectMessage(suspendToken.Write());
|
||||
transferPending.Ship.emplace();
|
||||
transferPending.Ship->ID = transport->GetEntry();
|
||||
transferPending.Ship->OriginMapID = GetMapId();
|
||||
}
|
||||
|
||||
// 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);
|
||||
SendDirectMessage(transferPending.Write());
|
||||
}
|
||||
//else
|
||||
// return false;
|
||||
|
||||
// remove from old map now
|
||||
if (oldmap)
|
||||
oldmap->RemovePlayerFromMap(this, false);
|
||||
|
||||
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
|
||||
|
||||
if (!GetSession()->PlayerLogout())
|
||||
{
|
||||
WorldPackets::Movement::SuspendToken suspendToken;
|
||||
suspendToken.SequenceIndex = m_movementCounter; // not incrementing
|
||||
suspendToken.Reason = options & TELE_TO_SEAMLESS ? 2 : 1;
|
||||
SendDirectMessage(suspendToken.Write());
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user