From e5c7244893fa07c7a68d2e5b87aedd00577539be Mon Sep 17 00:00:00 2001 From: hondacrx Date: Tue, 11 Oct 2022 17:54:34 -0400 Subject: [PATCH] Core/Maps: Changed functions checking if map can be entered to use TransferAbortReason directly instead of intermediate Map::EnterState enum Port From (https://github.com/TrinityCore/TrinityCore/commit/c3e1ff90a5a217691990ea4662bcaa2405b57fde) --- Source/Framework/Constants/MapConst.cs | 18 ---- Source/Framework/Constants/PlayerConst.cs | 1 + Source/Game/Entities/Player/Player.DB.cs | 28 +----- Source/Game/Entities/Player/Player.Map.cs | 20 +++-- Source/Game/Entities/Player/Player.cs | 16 ++-- Source/Game/Handlers/MiscHandler.cs | 102 +++++++++++----------- Source/Game/Handlers/MovementHandler.cs | 2 +- Source/Game/Maps/Map.cs | 97 ++++++++++---------- 8 files changed, 122 insertions(+), 162 deletions(-) diff --git a/Source/Framework/Constants/MapConst.cs b/Source/Framework/Constants/MapConst.cs index 859a210b8..e241495d4 100644 --- a/Source/Framework/Constants/MapConst.cs +++ b/Source/Framework/Constants/MapConst.cs @@ -178,24 +178,6 @@ namespace Framework.Constants Max } - public enum EnterState - { - CanEnter = 0, - CannotEnterAlreadyInMap = 1, // Player Is Already In The Map - CannotEnterNoEntry, // No Map Entry Was Found For The Target Map Id - CannotEnterUninstancedDungeon, // No Instance Template Was Found For Dungeon Map - CannotEnterDifficultyUnavailable, // Requested Instance Difficulty Is Not Available For Target Map - CannotEnterNotInRaid, // Target Instance Is A Raid Instance And The Player Is Not In A Raid Group - CannotEnterCorpseInDifferentInstance, // Player Is Dead And Their Corpse Is Not In Target Instance - CannotEnterInstanceBindMismatch, // Player'S Permanent Instance Save Is Not Compatible With Their Group'S Current Instance Bind - CannotEnterAlreadyCompletedEncounter, // Player is locked to encounter that wasn't defeated in the instance yet - CannotEnterTooManyInstances, // Player Has Entered Too Many Instances Recently - CannotEnterMaxPlayers, // Target Map Already Has The Maximum Number Of Players Allowed - CannotEnterZoneInCombat, // A Boss Encounter Is Currently In Progress On The Target Map - CannotEnterInstanceShuttingDown, - CannotEnterUnspecifiedReason - } - public enum ModelIgnoreFlags { Nothing = 0x00, diff --git a/Source/Framework/Constants/PlayerConst.cs b/Source/Framework/Constants/PlayerConst.cs index 9f3e1537a..64da9ea22 100644 --- a/Source/Framework/Constants/PlayerConst.cs +++ b/Source/Framework/Constants/PlayerConst.cs @@ -240,6 +240,7 @@ namespace Framework.Constants DifficultyNotFound = 22, // Client Writes To Console "Unable To Resolve Requested Difficultyid %U To Actual Difficulty For Map %D" XrealmZoneDown = 24, // Transfer ed: Cross-Realm Zone Is Down SoloPlayerSwitchDifficulty = 26, // This Instance Is Already In Progress. You May Only Switch Difficulties From Inside The Instance. + NotCrossFactionCompatible = 33, // This instance isn't available for cross-faction groups } public enum RaidGroupReason diff --git a/Source/Game/Entities/Player/Player.DB.cs b/Source/Game/Entities/Player/Player.DB.cs index e4742fb24..7f230be95 100644 --- a/Source/Game/Entities/Player/Player.DB.cs +++ b/Source/Game/Entities/Player/Player.DB.cs @@ -2925,32 +2925,10 @@ namespace Game.Entities } else if (map.IsDungeon()) // if map is dungeon... { - EnterState denyReason = ((InstanceMap)map).CannotEnter(this); - if (denyReason != 0) // ... and can't enter map, then look for entry point. + TransferAbortParams denyReason = map.CannotEnter(this); // ... and can't enter map, then look for entry point. + if (denyReason != null) { - switch (denyReason) - { - case EnterState.CannotEnterDifficultyUnavailable: - SendTransferAborted(map.GetId(), TransferAbortReason.Difficulty, (byte)map.GetDifficultyID()); - break; - case EnterState.CannotEnterInstanceBindMismatch: - SendSysMessage(CypherStrings.InstanceBindMismatch, map.GetMapName()); - break; - case EnterState.CannotEnterTooManyInstances: - SendTransferAborted(map.GetId(), TransferAbortReason.TooManyInstances); - break; - case EnterState.CannotEnterMaxPlayers: - SendTransferAborted(map.GetId(), TransferAbortReason.MaxPlayers); - break; - case EnterState.CannotEnterZoneInCombat: - SendTransferAborted(map.GetId(), TransferAbortReason.ZoneInCombat); - break; - case EnterState.CannotEnterInstanceShuttingDown: - SendTransferAborted(map.GetId(), TransferAbortReason.NotFound); - break; - default: - break; - } + SendTransferAborted(map.GetId(), denyReason.Reason, denyReason.Arg, denyReason.MapDifficultyXConditionId); areaTrigger = Global.ObjectMgr.GetGoBackTrigger(mapId); check = true; } diff --git a/Source/Game/Entities/Player/Player.Map.cs b/Source/Game/Entities/Player/Player.Map.cs index 7b75a5cfe..c3875967f 100644 --- a/Source/Game/Entities/Player/Player.Map.cs +++ b/Source/Game/Entities/Player/Player.Map.cs @@ -331,7 +331,7 @@ namespace Game.Entities SendPacket(instanceInfo); } - public bool Satisfy(AccessRequirement ar, uint target_map, bool report = false) + public bool Satisfy(AccessRequirement ar, uint target_map, TransferAbortParams abortParams = null, bool report = false) { if (!IsGameMaster()) { @@ -380,12 +380,6 @@ namespace Game.Entities else if (ar.item2 != 0 && !HasItemCount(ar.item2)) missingItem = ar.item2; - if (Global.DisableMgr.IsDisabledFor(DisableType.Map, target_map, this)) - { - GetSession().SendNotification("{0}", Global.ObjectMgr.GetCypherString(CypherStrings.InstanceClosed)); - return false; - } - if (GetTeam() == Team.Alliance && ar.quest_A != 0 && !GetQuestRewardStatus(ar.quest_A)) missingQuest = ar.quest_A; else if (GetTeam() == Team.Horde && ar.quest_H != 0 && !GetQuestRewardStatus(ar.quest_H)) @@ -403,12 +397,22 @@ namespace Game.Entities if (LevelMin != 0 || LevelMax != 0 || failedMapDifficultyXCondition != 0 || missingItem != 0 || missingQuest != 0 || missingAchievement != 0) { + if (abortParams != null) + abortParams.Reason = TransferAbortReason.Error; + if (report) { if (missingQuest != 0 && !string.IsNullOrEmpty(ar.questFailedText)) SendSysMessage("{0}", ar.questFailedText); else if (mapDiff.Message[Global.WorldMgr.GetDefaultDbcLocale()][0] != '\0' || failedMapDifficultyXCondition != 0) // if (missingAchievement) covered by this case - SendTransferAborted(target_map, TransferAbortReason.Difficulty, (byte)target_difficulty, failedMapDifficultyXCondition); + { + if (abortParams != null) + { + abortParams.Reason = TransferAbortReason.Difficulty; + abortParams.Arg = (byte)target_difficulty; + abortParams.MapDifficultyXConditionId = failedMapDifficultyXCondition; + } + } else if (missingItem != 0) GetSession().SendNotification(Global.ObjectMgr.GetCypherString(CypherStrings.LevelMinrequiredAndItem), LevelMin, Global.ObjectMgr.GetItemTemplate(missingItem).GetName()); else if (LevelMin != 0) diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index c6700ad2b..5fb8c51db 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -1783,8 +1783,12 @@ namespace Game.Entities // 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) != 0) + TransferAbortParams abortParams = Map.PlayerCannotEnter(mapid, this); + if (abortParams != null) + { + SendTransferAborted(mapid, abortParams.Reason, abortParams.Arg, abortParams.MapDifficultyXConditionId); return false; + } // Seamless teleport can happen only if cosmetic maps match if (!oldmap || (oldmap.GetEntry().CosmeticParentMapID != mapid && GetMapId() != mEntry.CosmeticParentMapID && @@ -1795,13 +1799,13 @@ namespace Game.Entities SetSemaphoreTeleportNear(false); //setup delayed teleport flag SetDelayedTeleportFlag(IsCanDelayTeleport()); - //if teleport spell is casted in Unit.Update() func + //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 - teleportDest = new WorldLocation(mapid, x, y, z, orientation); + teleportDest = new(mapid, x, y, z, orientation); m_teleport_instanceId = instanceId; m_teleport_options = options; return true; @@ -1837,11 +1841,11 @@ namespace Game.Entities if (pet) UnsummonPetTemporaryIfAny(); - // remove all areatriggers entities - RemoveAllAreaTriggers(); - // remove all dyn objects RemoveAllDynObjects(); + + // remove all areatriggers entities + RemoveAllAreaTriggers(); // stop spellcasting // not attempt interrupt teleportation spell at caster teleport diff --git a/Source/Game/Handlers/MiscHandler.cs b/Source/Game/Handlers/MiscHandler.cs index 6594918ab..85af2a5c0 100644 --- a/Source/Game/Handlers/MiscHandler.cs +++ b/Source/Game/Handlers/MiscHandler.cs @@ -274,76 +274,76 @@ namespace Game bool teleported = false; if (player.GetMapId() != at.target_mapId) { - EnterState denyReason = Map.PlayerCannotEnter(at.target_mapId, player, false); - if (denyReason != 0) + 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 EnterState.CannotEnterNoEntry: - Log.outDebug(LogFilter.Maps, "MAP: Player '{0}' attempted to enter map with id {1} which has no entry", player.GetName(), at.target_mapId); - break; - case EnterState.CannotEnterUninstancedDungeon: - Log.outDebug(LogFilter.Maps, "MAP: Player '{0}' attempted to enter dungeon map {1} but no instance template was found", player.GetName(), at.target_mapId); - break; - case EnterState.CannotEnterDifficultyUnavailable: - { - Log.outDebug(LogFilter.Maps, "MAP: Player '{0}' attempted to enter instance map {1} but the requested difficulty was not found", player.GetName(), at.target_mapId); - MapRecord entry = CliDB.MapStorage.LookupByKey(at.target_mapId); - if (entry != null) - player.SendTransferAborted(entry.Id, TransferAbortReason.Difficulty, (byte)player.GetDifficultyID(entry)); - } - break; - case EnterState.CannotEnterNotInRaid: - Log.outDebug(LogFilter.Maps, "MAP: Player '{0}' must be in a raid group to enter map {1}", player.GetName(), at.target_mapId); - player.SendRaidGroupOnlyMessage(RaidGroupReason.Only, 0); - reviveAtTrigger = true; - break; - case EnterState.CannotEnterCorpseInDifferentInstance: + // let enter in ghost mode in instance that connected to inner instance with corpse + uint corpseMap = player.GetCorpseLocation().GetMapId(); + do + { + if (corpseMap == at.target_mapId) + break; + + InstanceTemplate corpseInstance = Global.ObjectMgr.GetInstanceTemplate(corpseMap); + corpseMap = corpseInstance != null ? corpseInstance.Parent : 0; + } while (corpseMap != 0); + + if (corpseMap == 0) + { SendPacket(new AreaTriggerNoCorpse()); - Log.outDebug(LogFilter.Maps, "MAP: Player '{0}' does not have a corpse in instance map {1} and cannot enter", player.GetName(), at.target_mapId); + return; + } + + Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' has corpse in instance {at.target_mapId} and can enter."); + } + else + Log.outDebug(LogFilter.Maps, $"Map::CanPlayerEnter - player '{player.GetName()}' is dead but does not have a corpse!"); + } + + TransferAbortParams denyReason = Map.PlayerCannotEnter(at.target_mapId, player); + if (denyReason != null) + { + switch (denyReason.Reason) + { + case TransferAbortReason.MapNotAllowed: + Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' attempted to enter map with id {at.target_mapId} which has no entry"); break; - case EnterState.CannotEnterInstanceBindMismatch: - player.SendTransferAborted(at.target_mapId, TransferAbortReason.LockedToDifferentInstance); + case TransferAbortReason.Difficulty: + Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' attempted to enter instance map {at.target_mapId} but the requested difficulty was not found"); + break; + case TransferAbortReason.NeedGroup: + Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' must be in a raid group to enter map {at.target_mapId}"); + player.SendRaidGroupOnlyMessage(RaidGroupReason.Only, 0); + break; + case TransferAbortReason.LockedToDifferentInstance: Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' cannot enter instance map {at.target_mapId} because their permanent bind is incompatible with their group's"); - reviveAtTrigger = true; break; - case EnterState.CannotEnterAlreadyCompletedEncounter: - player.SendTransferAborted(at.target_mapId, TransferAbortReason.AlreadyCompletedEncounter); + case TransferAbortReason.AlreadyCompletedEncounter: Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' cannot enter instance map {at.target_mapId} because their permanent bind is incompatible with their group's"); - reviveAtTrigger = true; break; - case EnterState.CannotEnterTooManyInstances: - player.SendTransferAborted(at.target_mapId, TransferAbortReason.TooManyInstances); + case TransferAbortReason.TooManyInstances: Log.outDebug(LogFilter.Maps, "MAP: Player '{0}' cannot enter instance map {1} because he has exceeded the maximum number of instances per hour.", player.GetName(), at.target_mapId); - reviveAtTrigger = true; break; - case EnterState.CannotEnterMaxPlayers: - player.SendTransferAborted(at.target_mapId, TransferAbortReason.MaxPlayers); - reviveAtTrigger = true; + case TransferAbortReason.MaxPlayers: + case TransferAbortReason.ZoneInCombat: break; - case EnterState.CannotEnterZoneInCombat: - player.SendTransferAborted(at.target_mapId, TransferAbortReason.ZoneInCombat); - reviveAtTrigger = true; - break; - case EnterState.CannotEnterInstanceShuttingDown: - player.SendTransferAborted(at.target_mapId, TransferAbortReason.NotFound); + case TransferAbortReason.NotFound: Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' cannot enter instance map {at.target_mapId} because instance is resetting."); - reviveAtTrigger = true; break; default: break; } - if (reviveAtTrigger) // check if the player is touching the areatrigger leading to the map his corpse is on + if (denyReason.Reason != TransferAbortReason.NeedGroup) + player.SendTransferAborted(at.target_mapId, denyReason.Reason, denyReason.Arg, denyReason.MapDifficultyXConditionId); + + if (!player.IsAlive() && player.HasCorpse()) { - if (!player.IsAlive() && player.HasCorpse()) + if (player.GetCorpseLocation().GetMapId() == at.target_mapId) { - if (player.GetCorpseLocation().GetMapId() == at.target_mapId) - { - player.ResurrectPlayer(0.5f); - player.SpawnCorpseBones(); - } + player.ResurrectPlayer(0.5f); + player.SpawnCorpseBones(); } } diff --git a/Source/Game/Handlers/MovementHandler.cs b/Source/Game/Handlers/MovementHandler.cs index 01e64d83d..6e3fbe347 100644 --- a/Source/Game/Handlers/MovementHandler.cs +++ b/Source/Game/Handlers/MovementHandler.cs @@ -270,7 +270,7 @@ namespace Game // relocate the player to the teleport destination // the CannotEnter checks are done in TeleporTo but conditions may change // while the player is in transit, for example the map may get full - if (newMap == null || newMap.CannotEnter(player) != 0) + if (newMap == null || newMap.CannotEnter(player) != null) { Log.outError(LogFilter.Network, $"Map {loc.GetMapId()} could not be created for {(newMap ? newMap.GetMapName() : "Unknown")} ({player.GetGUID()}), porting player to homebind"); player.TeleportTo(player.GetHomebind()); diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 6d72de399..db7ad99a2 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -1654,60 +1654,36 @@ namespace Game.Maps return result; } - public static EnterState PlayerCannotEnter(uint mapid, Player player, bool loginCheck) + public static TransferAbortParams PlayerCannotEnter(uint mapid, Player player) { var entry = CliDB.MapStorage.LookupByKey(mapid); if (entry == null) - return EnterState.CannotEnterNoEntry; + return new TransferAbortParams(TransferAbortReason.MapNotAllowed); if (!entry.IsDungeon()) - return EnterState.CanEnter; + return TransferAbortParams.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 var mapDiff = Global.DB2Mgr.GetDownscaledMapDifficultyData(mapid, ref targetDifficulty); if (mapDiff == null) - return EnterState.CannotEnterDifficultyUnavailable; + return new TransferAbortParams(TransferAbortReason.Difficulty); //Bypass checks for GMs if (player.IsGameMaster()) - return EnterState.CanEnter; + return TransferAbortParams.None; //Other requirements - if (!player.Satisfy(Global.ObjectMgr.GetAccessRequirement(mapid, targetDifficulty), mapid, true)) - return EnterState.CannotEnterUnspecifiedReason; - - string mapName = entry.MapName[Global.WorldMgr.GetDefaultDbcLocale()]; + { + TransferAbortParams abortParams = new(); + if (!player.Satisfy(Global.ObjectMgr.GetAccessRequirement(mapid, targetDifficulty), mapid, abortParams, true)) + return abortParams; + } Group group = player.GetGroup(); if (entry.IsRaid() && (int)entry.Expansion() >= WorldConfig.GetIntValue(WorldCfg.Expansion)) // can only enter in a raid group but raids from old expansion don't need a group if ((!group || !group.IsRaidGroup()) && !WorldConfig.GetBoolValue(WorldCfg.InstanceIgnoreRaid)) - return EnterState.CannotEnterNotInRaid; - - if (!player.IsAlive()) - { - if (player.HasCorpse()) - { - // let enter in ghost mode in instance that connected to inner instance with corpse - uint corpseMap = player.GetCorpseLocation().GetMapId(); - do - { - if (corpseMap == mapid) - break; - - InstanceTemplate corpseInstance = Global.ObjectMgr.GetInstanceTemplate(corpseMap); - corpseMap = corpseInstance != null ? corpseInstance.Parent : 0; - } while (corpseMap != 0); - - if (corpseMap == 0) - return EnterState.CannotEnterCorpseInDifferentInstance; - - Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' has corpse in instance '{mapName}' and can enter."); - } - else - Log.outDebug(LogFilter.Maps, $"Map::CanPlayerEnter - player '{player.GetName()}' is dead but does not have a corpse!"); - } + return new TransferAbortParams(TransferAbortReason.NeedGroup); if (entry.Instanceable()) { @@ -1716,17 +1692,17 @@ namespace Game.Maps Map boundMap = Global.MapMgr.FindMap(mapid, instanceIdToCheck); if (boundMap != null) { - EnterState denyReason = boundMap.CannotEnter(player); - if (denyReason != 0) + TransferAbortParams denyReason = boundMap.CannotEnter(player); + if (denyReason != null) 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 EnterState.CannotEnterTooManyInstances; + return new TransferAbortParams(TransferAbortReason.TooManyInstances); } - return EnterState.CanEnter; + return TransferAbortParams.None; } public string GetMapName() @@ -3243,7 +3219,7 @@ namespace Game.Maps return i_InstanceId; } - public virtual EnterState CannotEnter(Player player) { return EnterState.CanEnter; } + public virtual TransferAbortParams CannotEnter(Player player) { return TransferAbortParams.None; } public Difficulty GetDifficultyID() { @@ -4789,13 +4765,13 @@ namespace Game.Maps m_VisibilityNotifyPeriod = Global.WorldMgr.GetVisibilityNotifyPeriodInInstances(); } - public override EnterState CannotEnter(Player player) + public override TransferAbortParams CannotEnter(Player player) { if (player.GetMap() == this) { Log.outError(LogFilter.Maps, "InstanceMap:CannotEnter - player {0} ({1}) already in map {2}, {3}, {4}!", player.GetName(), player.GetGUID().ToString(), GetId(), GetInstanceId(), GetDifficultyID()); Cypher.Assert(false); - return EnterState.CannotEnterAlreadyInMap; + return new TransferAbortParams(TransferAbortReason.Error); } // allow GM's to enter @@ -4807,22 +4783,19 @@ namespace Game.Maps if (GetPlayersCountExceptGMs() >= maxPlayers) { Log.outInfo(LogFilter.Maps, "MAP: Instance '{0}' of map '{1}' cannot have more than '{2}' players. Player '{3}' rejected", GetInstanceId(), GetMapName(), maxPlayers, player.GetName()); - return EnterState.CannotEnterMaxPlayers; + return new TransferAbortParams(TransferAbortReason.MaxPlayers); } // cannot enter while an encounter is in progress (unless this is a relog, in which case it is permitted) if (!player.IsLoading() && IsRaid() && GetInstanceScript() != null && GetInstanceScript().IsEncounterInProgress()) - return EnterState.CannotEnterZoneInCombat; + return new TransferAbortParams(TransferAbortReason.ZoneInCombat); if (i_instanceLock != null) { // cannot enter if player is permanent saved to a different instance id TransferAbortReason lockError = Global.InstanceLockMgr.CanJoinInstanceLock(player.GetGUID(), new MapDb2Entries(GetEntry(), GetMapDifficulty()), i_instanceLock); - if (lockError == TransferAbortReason.LockedToDifferentInstance) - return EnterState.CannotEnterInstanceBindMismatch; - - if (lockError == TransferAbortReason.AlreadyCompletedEncounter) - return EnterState.CannotEnterAlreadyCompletedEncounter; + if (lockError != TransferAbortReason.None) + return new TransferAbortParams(lockError); } return base.CannotEnter(player); @@ -5196,17 +5169,17 @@ namespace Game.Maps m_VisibilityNotifyPeriod = IsBattleArena() ? Global.WorldMgr.GetVisibilityNotifyPeriodInArenas() : Global.WorldMgr.GetVisibilityNotifyPeriodInBG(); } - public override EnterState CannotEnter(Player player) + public override TransferAbortParams CannotEnter(Player player) { if (player.GetMap() == this) { Log.outError(LogFilter.Maps, "BGMap:CannotEnter - player {0} is already in map!", player.GetGUID().ToString()); Cypher.Assert(false); - return EnterState.CannotEnterAlreadyInMap; + return new TransferAbortParams(TransferAbortReason.Error); } if (player.GetBattlegroundId() != GetInstanceId()) - return EnterState.CannotEnterInstanceBindMismatch; + return new TransferAbortParams(TransferAbortReason.LockedToDifferentInstance); return base.CannotEnter(player); } @@ -5244,6 +5217,24 @@ namespace Game.Maps Battleground m_bg; } + public class TransferAbortParams + { + public static TransferAbortParams None = new TransferAbortParams(); + + public TransferAbortReason Reason; + public byte Arg; + public uint MapDifficultyXConditionId; + + public TransferAbortParams(TransferAbortReason reason = TransferAbortReason.None, byte arg = 0, uint mapDifficultyXConditionId = 0) + { + Reason = reason; + Arg = arg; + MapDifficultyXConditionId = mapDifficultyXConditionId; + } + + public bool IsFailed() { return Reason != TransferAbortReason.None; } + } + public struct ScriptAction { public ObjectGuid ownerGUID;