From fa35d60f6027833537df0ac6c6699920cdc67f5c Mon Sep 17 00:00:00 2001 From: hondacrx Date: Wed, 28 Mar 2018 11:09:30 -0400 Subject: [PATCH] Core/Entities: Phasing rewrite --- Source/Framework/Constants/CliDBConst.cs | 17 + Source/Framework/Constants/Language.cs | 16 +- Source/Framework/Constants/MapConst.cs | 2 +- Source/Framework/Networking/SocketBase.cs | 3 + Source/Framework/Util/CollectionExtensions.cs | 5 + Source/Game/AI/SmartScripts/SmartScript.cs | 15 +- Source/Game/Chat/Commands/DebugCommands.cs | 22 +- .../Game/Chat/Commands/GameObjectCommands.cs | 9 +- Source/Game/Chat/Commands/GoCommands.cs | 8 +- Source/Game/Chat/Commands/GroupCommands.cs | 2 +- Source/Game/Chat/Commands/MMapsCommands.cs | 24 +- Source/Game/Chat/Commands/MiscCommands.cs | 45 +- Source/Game/Chat/Commands/ModifyCommands.cs | 30 +- Source/Game/Chat/Commands/NPCCommands.cs | 25 +- Source/Game/Chat/Commands/WPCommands.cs | 8 +- Source/Game/Collision/Callbacks.cs | 8 +- Source/Game/Collision/DynamicTree.cs | 16 +- .../Game/Collision/Management/VMapManager.cs | 286 +++++---- Source/Game/Collision/Maps/MapTree.cs | 136 +++-- .../Game/Collision/Models/GameObjectModel.cs | 6 +- Source/Game/Collision/Models/IModel.cs | 2 +- Source/Game/Conditions/Condition.cs | 4 +- Source/Game/Conditions/ConditionManager.cs | 55 +- Source/Game/DataStorage/DB2Manager.cs | 2 - Source/Game/DataStorage/Structs/P_Records.cs | 2 +- Source/Game/DataStorage/Structs/S_Records.cs | 2 +- .../Game/Entities/AreaTrigger/AreaTrigger.cs | 3 +- Source/Game/Entities/Conversation.cs | 2 +- Source/Game/Entities/Corpse.cs | 6 +- Source/Game/Entities/Creature/Creature.cs | 15 +- Source/Game/Entities/Creature/CreatureData.cs | 2 + Source/Game/Entities/GameObject/GameObject.cs | 13 +- .../Entities/GameObject/GameObjectData.cs | 2 + Source/Game/Entities/Object/WorldObject.cs | 284 +-------- Source/Game/Entities/Pet.cs | 2 +- Source/Game/Entities/Player/Player.DB.cs | 2 +- Source/Game/Entities/Player/Player.Fields.cs | 1 + Source/Game/Entities/Player/Player.Map.cs | 6 +- Source/Game/Entities/Player/Player.Quest.cs | 2 +- Source/Game/Entities/Player/Player.cs | 30 +- Source/Game/Entities/Transport.cs | 23 +- Source/Game/Entities/Unit/Unit.Movement.cs | 8 +- Source/Game/Entities/Unit/Unit.Pets.cs | 2 +- Source/Game/Entities/Unit/Unit.Spells.cs | 8 +- Source/Game/Entities/Unit/Unit.cs | 39 +- Source/Game/Garrisons/Garrisons.cs | 2 +- Source/Game/Globals/ObjectManager.cs | 365 +++++++---- Source/Game/Handlers/MiscHandler.cs | 11 - Source/Game/Handlers/MovementHandler.cs | 2 +- Source/Game/Handlers/QueryHandler.cs | 2 +- Source/Game/Maps/GridMap.cs | 9 +- Source/Game/Maps/Instances/InstanceScript.cs | 2 +- Source/Game/Maps/MMapManager.cs | 115 +++- Source/Game/Maps/Map.cs | 307 ++++++---- Source/Game/Maps/MapManager.cs | 182 +++--- Source/Game/Maps/TransportManager.cs | 55 +- .../Movement/Generators/FleeingGenerator.cs | 2 +- .../Game/Movement/Generators/PathGenerator.cs | 10 +- .../Movement/Generators/RandomMovement.cs | 8 +- Source/Game/Movement/MotionMaster.cs | 4 +- Source/Game/Network/Packets/MiscPackets.cs | 84 ++- Source/Game/Network/Packets/PartyPackets.cs | 21 +- Source/Game/Phasing/PhaseShift.cs | 299 +++++++++ Source/Game/Phasing/PhasingHandler.cs | 573 ++++++++++++++++++ Source/Game/Server/WorldManager.cs | 24 +- Source/Game/Spells/Auras/AuraEffect.cs | 48 +- Source/Game/Spells/Spell.cs | 18 +- Source/Game/Spells/SpellEffects.cs | 14 +- .../AzjolNerub/Ahnkahet/BossAmanitar.cs | 10 +- .../AzjolNerub/Ahnkahet/BossHeraldVolazj.cs | 9 +- .../Northrend/Ulduar/FlameLeviathan.cs | 2 +- .../world/master/2018_03_25_00_world.sql | 22 + 72 files changed, 2254 insertions(+), 1146 deletions(-) create mode 100644 Source/Game/Phasing/PhaseShift.cs create mode 100644 Source/Game/Phasing/PhasingHandler.cs create mode 100644 sql/updates/world/master/2018_03_25_00_world.sql diff --git a/Source/Framework/Constants/CliDBConst.cs b/Source/Framework/Constants/CliDBConst.cs index 783e77c34..1cafe9fad 100644 --- a/Source/Framework/Constants/CliDBConst.cs +++ b/Source/Framework/Constants/CliDBConst.cs @@ -1361,6 +1361,23 @@ namespace Framework.Constants HideIfUnknown = 0x40 } + public enum PhaseEntryFlags : ushort + { + Normal = 0x08, + Cosmetic = 0x10, + Personal = 0x20 + } + + // PhaseUseFlags fields in different db2s + public enum PhaseUseFlagsValues : byte + { + None = 0x0, + AlwaysVisible = 0x1, + Inverse = 0x2, + + All = AlwaysVisible | Inverse + } + public enum PrestigeLevelInfoFlags : byte { Disabled = 0x01 // Prestige levels with this flag won't be included to calculate max prestigelevel. diff --git a/Source/Framework/Constants/Language.cs b/Source/Framework/Constants/Language.cs index d576899bf..8fd0d250f 100644 --- a/Source/Framework/Constants/Language.cs +++ b/Source/Framework/Constants/Language.cs @@ -232,16 +232,16 @@ namespace Framework.Constants LiquidStatus = 175, InvalidGameobjectType = 176, GameobjectDamaged = 177, + GridPosition = 178, - PhasingSuccess = 178, - PhasingFailed = 179, - PhasingLastPhase = 180, - PhasingList = 181, - PhasingPhasemask = 182, - PhasingReportStatus = 183, - PhasingNoDefinitions = 184, // Phasing + PhaseshiftStatus = 179, + PhaseshiftPhases = 180, + PhaseshiftVisibleMapIds = 181, + PhaseshiftUiWorldMapAreaSwaps = 182, + PhaseFlagCosmetic = 183, + PhaseFlagPersonal = 184, - GridPosition = 185, + // 185 not used TransportPosition = 186, // Room For More Level 1 187-199 Not Used diff --git a/Source/Framework/Constants/MapConst.cs b/Source/Framework/Constants/MapConst.cs index 0e2adaa70..c861a4258 100644 --- a/Source/Framework/Constants/MapConst.cs +++ b/Source/Framework/Constants/MapConst.cs @@ -71,7 +71,7 @@ namespace Framework.Constants public const string mmapMagic = "MMAP"; public const int mmapVersion = 8; - public const string VMapMagic = "VMAP_4.5"; + public const string VMapMagic = "VMAP_4.6"; public const float VMAPInvalidHeightValue = -200000.0f; } diff --git a/Source/Framework/Networking/SocketBase.cs b/Source/Framework/Networking/SocketBase.cs index ae42bc4dc..abdb8bb01 100644 --- a/Source/Framework/Networking/SocketBase.cs +++ b/Source/Framework/Networking/SocketBase.cs @@ -144,6 +144,9 @@ namespace Framework.Networking public void CloseSocket() { + if (_socket == null) + return; + try { _closed = true; diff --git a/Source/Framework/Util/CollectionExtensions.cs b/Source/Framework/Util/CollectionExtensions.cs index 33c380732..22d41a2da 100644 --- a/Source/Framework/Util/CollectionExtensions.cs +++ b/Source/Framework/Util/CollectionExtensions.cs @@ -156,6 +156,11 @@ namespace System.Collections.Generic return default(T); } + public static IEnumerable Intersect(this IEnumerable first, IEnumerable second, Func comparer) + { + return first.Where(x => second.Count(y => comparer(x, y)) == 1); + } + public static uint[] ToBlockRange(this BitSet array) { uint[] blockValues = new uint[array.Length / 32 + 1]; diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 207635576..1d7cdc9fb 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -1090,7 +1090,12 @@ namespace Game.AI break; foreach (var obj in targets) - obj.SetInPhase(e.Action.ingamePhaseId.id, true, e.Action.ingamePhaseId.apply == 1); + { + if (e.Action.ingamePhaseId.apply == 1) + PhasingHandler.AddPhase(obj, e.Action.ingamePhaseId.id, true); + else + PhasingHandler.RemovePhase(obj, e.Action.ingamePhaseId.id, true); + } break; } @@ -1101,12 +1106,12 @@ namespace Game.AI if (targets.Empty()) break; - var phases = Global.DB2Mgr.GetPhasesForGroup(e.Action.ingamePhaseGroup.groupId); - foreach (var obj in targets) { - foreach (var phase in phases) - obj.SetInPhase(phase, true, e.Action.ingamePhaseGroup.apply == 1); + if (e.Action.ingamePhaseGroup.apply == 1) + PhasingHandler.AddPhaseGroup(obj, e.Action.ingamePhaseGroup.groupId, true); + else + PhasingHandler.RemovePhaseGroup(obj, e.Action.ingamePhaseGroup.groupId, true); } break; diff --git a/Source/Game/Chat/Commands/DebugCommands.cs b/Source/Game/Chat/Commands/DebugCommands.cs index 58fcb58e9..d2f4b38f5 100644 --- a/Source/Game/Chat/Commands/DebugCommands.cs +++ b/Source/Game/Chat/Commands/DebugCommands.cs @@ -730,14 +730,7 @@ namespace Game.Chat else if (target.GetDBPhase() < 0) handler.SendSysMessage($"Target creature's PhaseGroup in DB: {Math.Abs(target.GetDBPhase())}"); - string phases = ""; - foreach (uint phase in target.GetPhases()) - phases += phase + " "; - - if (!string.IsNullOrEmpty(phases)) - handler.SendSysMessage("Target's current phases: {0}", phases); - else - handler.SendSysMessage("Target is not phased"); + PhasingHandler.PrintToChat(handler, target.GetPhaseShift()); return true; } @@ -1171,17 +1164,18 @@ namespace Game.Chat if (args.Empty()) return false; - List terrainswap = new List(); - List phaseId = new List(); - List worldMapSwap = new List(); + PhaseShift phaseShift = new PhaseShift(); if (uint.TryParse(args.NextString(), out uint terrain)) - terrainswap.Add(terrain); + phaseShift.AddVisibleMapId(terrain, null); if (uint.TryParse(args.NextString(), out uint phase)) - phaseId.Add(phase); + phaseShift.AddPhase(phase, PhaseFlags.None, null); - handler.GetSession().SendSetPhaseShift(phaseId, terrainswap, worldMapSwap); + if (uint.TryParse(args.NextString(), out uint map)) + phaseShift.AddUiWorldMapAreaIdSwap(map); + + PhasingHandler.SendToPlayer(handler.GetSession().GetPlayer(), phaseShift); return true; } diff --git a/Source/Game/Chat/Commands/GameObjectCommands.cs b/Source/Game/Chat/Commands/GameObjectCommands.cs index 333532c7a..24e899fb4 100644 --- a/Source/Game/Chat/Commands/GameObjectCommands.cs +++ b/Source/Game/Chat/Commands/GameObjectCommands.cs @@ -477,7 +477,7 @@ namespace Game.Chat if (!obj) return false; - obj.CopyPhaseFrom(player); + PhasingHandler.InheritPhaseShift(obj, player); if (spawntimeSecs != 0) { @@ -616,6 +616,13 @@ namespace Game.Chat obj.SetByteValue(GameObjectFields.Bytes1, (byte)objectType, (byte)objectState); else if (objectType == 4) obj.SendCustomAnim(objectState); + else if (objectType == 5) + { + if (objectState < 0 || objectState > (uint)GameObjectDestructibleState.Rebuilding) + return false; + + obj.SetDestructibleState((GameObjectDestructibleState)objectState); + } handler.SendSysMessage("Set gobject type {0} state {1}", objectType, objectState); return true; diff --git a/Source/Game/Chat/Commands/GoCommands.cs b/Source/Game/Chat/Commands/GoCommands.cs index 8272c88dc..cc4b93eb7 100644 --- a/Source/Game/Chat/Commands/GoCommands.cs +++ b/Source/Game/Chat/Commands/GoCommands.cs @@ -179,7 +179,7 @@ namespace Game.Chat.Commands player.SaveRecallPosition(); Map map = Global.MapMgr.CreateBaseMap(mapId); - float z = Math.Max(map.GetHeight(x, y, MapConst.MaxHeight), map.GetWaterLevel(x, y)); + float z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y)); player.TeleportTo(mapId, x, y, z, player.GetOrientation()); return true; @@ -298,7 +298,7 @@ namespace Game.Chat.Commands player.SaveRecallPosition(); Map map = Global.MapMgr.CreateBaseMap(mapId); - z = Math.Max(map.GetHeight(x, y, MapConst.MaxHeight), map.GetWaterLevel(x, y)); + z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y)); player.TeleportTo(mapId, x, y, z, 0.0f); return true; @@ -446,7 +446,7 @@ namespace Game.Chat.Commands else player.SaveRecallPosition(); - float z = Math.Max(map.GetHeight(x, y, MapConst.MaxHeight), map.GetWaterLevel(x, y)); + float z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y)); player.TeleportTo(zoneEntry.ContinentID, x, y, z, player.GetOrientation()); return true; @@ -495,7 +495,7 @@ namespace Game.Chat.Commands return false; } Map map = Global.MapMgr.CreateBaseMap(mapId); - z = Math.Max(map.GetHeight(x, y, MapConst.MaxHeight), map.GetWaterLevel(x, y)); + z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y)); } // stop flight if need diff --git a/Source/Game/Chat/Commands/GroupCommands.cs b/Source/Game/Chat/Commands/GroupCommands.cs index 0576d6b87..370b620d7 100644 --- a/Source/Game/Chat/Commands/GroupCommands.cs +++ b/Source/Game/Chat/Commands/GroupCommands.cs @@ -321,7 +321,7 @@ namespace Game.Chat { // ... than, it prints information like "is online", where he is, etc... onlineState = "online"; - phases = string.Join(", ", p.GetPhases()); + phases = PhasingHandler.FormatPhases(p.GetPhaseShift()); AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(p.GetAreaId()); if (area != null) diff --git a/Source/Game/Chat/Commands/MMapsCommands.cs b/Source/Game/Chat/Commands/MMapsCommands.cs index 8ce5e7796..209597243 100644 --- a/Source/Game/Chat/Commands/MMapsCommands.cs +++ b/Source/Game/Chat/Commands/MMapsCommands.cs @@ -99,12 +99,16 @@ namespace Game.Chat int gx = (int)(32 - player.GetPositionX() / MapConst.SizeofGrids); int gy = (int)(32 - player.GetPositionY() / MapConst.SizeofGrids); + float x, y, z; + player.GetPosition(out x, out y, out z); + handler.SendSysMessage("{0:D4}{1:D2}{2:D2}.mmtile", player.GetMapId(), gy, gx); handler.SendSysMessage("gridloc [{0}, {1}]", gx, gy); // calculate navmesh tile location - Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(handler.GetPlayer().GetMapId()); - Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(handler.GetPlayer().GetMapId(), player.GetInstanceId()); + uint terrainMapId = PhasingHandler.GetTerrainMapId(player.GetPhaseShift(), player.GetMap(), x, y); + Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(terrainMapId); + Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(terrainMapId, player.GetInstanceId()); if (navmesh == null || navmeshquery == null) { handler.SendSysMessage("NavMesh not loaded for current map."); @@ -112,8 +116,6 @@ namespace Game.Chat } float[] min = navmesh.getParams().orig; - float x, y, z; - player.GetPosition(out x, out y, out z); float[] location = { y, z, x }; float[] extents = { 3.0f, 5.0f, 3.0f }; @@ -155,9 +157,10 @@ namespace Game.Chat [Command("loadedtiles", RBACPermissions.CommandMmapLoadedtiles)] static bool LoadedTilesCommand(StringArguments args, CommandHandler handler) { - uint mapid = handler.GetPlayer().GetMapId(); - Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(mapid); - Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(mapid, handler.GetPlayer().GetInstanceId()); + Player player = handler.GetSession().GetPlayer(); + uint terrainMapId = PhasingHandler.GetTerrainMapId(player.GetPhaseShift(), player.GetMap(), player.GetPositionX(), player.GetPositionY()); + Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(terrainMapId); + Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(terrainMapId, handler.GetPlayer().GetInstanceId()); if (navmesh == null || navmeshquery == null) { handler.SendSysMessage("NavMesh not loaded for current map."); @@ -180,12 +183,13 @@ namespace Game.Chat [Command("stats", RBACPermissions.CommandMmapStats)] static bool HandleMmapStatsCommand(StringArguments args, CommandHandler handler) { - uint mapId = handler.GetPlayer().GetMapId(); + Player player = handler.GetSession().GetPlayer(); + uint terrainMapId = PhasingHandler.GetTerrainMapId(player.GetPhaseShift(), player.GetMap(), player.GetPositionX(), player.GetPositionY()); handler.SendSysMessage("mmap stats:"); - handler.SendSysMessage(" global mmap pathfinding is {0}abled", Global.DisableMgr.IsPathfindingEnabled(mapId) ? "En" : "Dis"); + handler.SendSysMessage(" global mmap pathfinding is {0}abled", Global.DisableMgr.IsPathfindingEnabled(player.GetMapId()) ? "En" : "Dis"); handler.SendSysMessage(" {0} maps loaded with {1} tiles overall", Global.MMapMgr.getLoadedMapsCount(), Global.MMapMgr.getLoadedTilesCount()); - Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(handler.GetPlayer().GetMapId()); + Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(terrainMapId); if (navmesh == null) { handler.SendSysMessage("NavMesh not loaded for current map."); diff --git a/Source/Game/Chat/Commands/MiscCommands.cs b/Source/Game/Chat/Commands/MiscCommands.cs index f86632f7f..da6eee65b 100644 --- a/Source/Game/Chat/Commands/MiscCommands.cs +++ b/Source/Game/Chat/Commands/MiscCommands.cs @@ -195,8 +195,8 @@ namespace Game.Chat Global.DB2Mgr.Map2ZoneCoordinates(zoneId, ref zoneX, ref zoneY); Map map = obj.GetMap(); - float groundZ = map.GetHeight(obj.GetPhases(), obj.GetPositionX(), obj.GetPositionY(), MapConst.MaxHeight); - float floorZ = map.GetHeight(obj.GetPhases(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ()); + float groundZ = map.GetHeight(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), MapConst.MaxHeight); + float floorZ = map.GetHeight(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ()); GridCoord gridCoord = GridDefines.ComputeGridCoord(obj.GetPositionX(), obj.GetPositionY()); @@ -210,13 +210,13 @@ namespace Game.Chat if (haveVMap) { - if (map.IsOutdoors(obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ())) - handler.SendSysMessage("You are outdoors"); + if (map.IsOutdoors(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ())) + handler.SendSysMessage(CypherStrings.GpsPositionOutdoors); else - handler.SendSysMessage("You are indoors"); + handler.SendSysMessage(CypherStrings.GpsPositionIndoors); } else - handler.SendSysMessage("no VMAP available for area info"); + handler.SendSysMessage(CypherStrings.GpsNoVmap); string unknown = handler.GetCypherString(CypherStrings.Unknown); @@ -224,7 +224,7 @@ namespace Game.Chat mapId, (mapEntry != null ? mapEntry.MapName[handler.GetSessionDbcLocale()] : unknown), zoneId, (zoneEntry != null ? zoneEntry.AreaName[handler.GetSessionDbcLocale()] : unknown), areaId, (areaEntry != null ? areaEntry.AreaName[handler.GetSessionDbcLocale()] : unknown), - string.Join(", ", obj.GetPhases()), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), obj.GetOrientation()); + obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), obj.GetOrientation()); Transport transport = obj.GetTransport(); if (transport) @@ -237,25 +237,12 @@ namespace Game.Chat zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMap); LiquidData liquidStatus; - ZLiquidStatus status = map.getLiquidStatus(obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), MapConst.MapAllLiquidTypes, out liquidStatus); + ZLiquidStatus status = map.getLiquidStatus(obj.GetPhaseShift(), obj.GetPositionX(), obj.GetPositionY(), obj.GetPositionZ(), MapConst.MapAllLiquidTypes, out liquidStatus); if (liquidStatus != null) handler.SendSysMessage(CypherStrings.LiquidStatus, liquidStatus.level, liquidStatus.depth_level, liquidStatus.entry, liquidStatus.type_flags, status); - if (!obj.GetTerrainSwaps().Empty()) - { - string ss = ""; - foreach (uint swap in obj.GetTerrainSwaps()) - ss += swap + " "; - handler.SendSysMessage("Target's active terrain swaps: {0}", ss); - } - if (!obj.GetWorldMapAreaSwaps().Empty()) - { - string ss = ""; - foreach (uint swap in obj.GetWorldMapAreaSwaps()) - ss += swap + " "; - handler.SendSysMessage("Target's active world map area swaps: {0}", ss); - } + PhasingHandler.PrintToChat(handler, obj.GetPhaseShift()); return true; } @@ -602,7 +589,8 @@ namespace Game.Chat target.GetContactPoint(_player, out x, out y, out z); _player.TeleportTo(target.GetMapId(), x, y, z, _player.GetAngle(target), TeleportToOptions.GMMode); - _player.CopyPhaseFrom(target, true); + PhasingHandler.InheritPhaseShift(_player, target); + _player.UpdateObjectVisibility(); } else { @@ -727,7 +715,8 @@ namespace Game.Chat float x, y, z; handler.GetSession().GetPlayer().GetClosePoint(out x, out y, out z, target.GetObjectSize()); target.TeleportTo(handler.GetSession().GetPlayer().GetMapId(), x, y, z, target.GetOrientation()); - target.CopyPhaseFrom(handler.GetSession().GetPlayer(), true); + PhasingHandler.InheritPhaseShift(target, handler.GetSession().GetPlayer()); + target.UpdateObjectVisibility(); } else { @@ -1385,7 +1374,6 @@ namespace Game.Chat // Position data print uint mapId; uint areaId; - List phases = new List(); string areaName = handler.GetCypherString(CypherStrings.Unknown); string zoneName = handler.GetCypherString(CypherStrings.Unknown); @@ -1417,7 +1405,6 @@ namespace Game.Chat areaId = target.GetAreaId(); alive = target.IsAlive() ? handler.GetCypherString(CypherStrings.Yes) : handler.GetCypherString(CypherStrings.No); gender = (Gender)target.GetByteValue(PlayerFields.Bytes3, PlayerFieldOffsets.Bytes3OffsetGender); - phases = target.GetPhases(); } // get additional information from DB else @@ -1582,9 +1569,9 @@ namespace Game.Chat // Output XII. LANG_PINFO_CHR_ALIVE handler.SendSysMessage(CypherStrings.PinfoChrAlive, alive); - // Output XIII. LANG_PINFO_CHR_PHASES - if (target && !phases.Empty()) - handler.SendSysMessage(CypherStrings.PinfoChrPhases, string.Join(", ", phases)); + // Output XIII. phases + if (target) + PhasingHandler.PrintToChat(handler, target.GetPhaseShift()); // Output XIV. LANG_PINFO_CHR_MONEY ulong gold = money / MoneyConstants.Gold; diff --git a/Source/Game/Chat/Commands/ModifyCommands.cs b/Source/Game/Chat/Commands/ModifyCommands.cs index 7f55f6fd2..3b50b8a81 100644 --- a/Source/Game/Chat/Commands/ModifyCommands.cs +++ b/Source/Game/Chat/Commands/ModifyCommands.cs @@ -497,20 +497,38 @@ namespace Game.Chat return false; uint phaseId = args.NextUInt32(); - if (!CliDB.PhaseStorage.ContainsKey(phaseId)) + uint visibleMapId = args.NextUInt32(); + + if (phaseId != 0 && !CliDB.PhaseStorage.ContainsKey(phaseId)) { handler.SendSysMessage(CypherStrings.PhaseNotfound); return false; } Unit target = handler.getSelectedUnit(); - if (!target) - target = handler.GetSession().GetPlayer(); - target.SetInPhase(phaseId, true, !target.IsInPhase(phaseId)); + if (visibleMapId != 0) + { + MapRecord visibleMap = CliDB.MapStorage.LookupByKey(visibleMapId); + if (visibleMap == null || visibleMap.ParentMapID != target.GetMapId()) + { + handler.SendSysMessage(CypherStrings.PhaseNotfound); + return false; + } - if (target.IsTypeId(TypeId.Player)) - target.ToPlayer().SendUpdatePhasing(); + if (!target.GetPhaseShift().HasVisibleMapId(visibleMapId)) + PhasingHandler.AddVisibleMapId(target, visibleMapId); + else + PhasingHandler.RemoveVisibleMapId(target, visibleMapId); + } + + if (phaseId != 0) + { + if (!target.GetPhaseShift().HasPhase(phaseId)) + PhasingHandler.AddPhase(target, phaseId, true); + else + PhasingHandler.RemovePhase(target, phaseId, true); + } return true; } diff --git a/Source/Game/Chat/Commands/NPCCommands.cs b/Source/Game/Chat/Commands/NPCCommands.cs index f1c0c013c..50b6f10fb 100644 --- a/Source/Game/Chat/Commands/NPCCommands.cs +++ b/Source/Game/Chat/Commands/NPCCommands.cs @@ -87,16 +87,7 @@ namespace Game.Chat if (data != null) { handler.SendSysMessage(CypherStrings.NpcinfoPhases, data.phaseId, data.phaseGroup); - if (data.phaseGroup != 0) - { - var _phases = target.GetPhases(); - if (!_phases.Empty()) - { - handler.SendSysMessage(CypherStrings.NpcinfoPhaseIds); - foreach (uint phaseId in _phases) - handler.SendSysMessage("{0}", phaseId); - } - } + PhasingHandler.PrintToChat(handler, target.GetPhaseShift()); } handler.SendSysMessage(CypherStrings.NpcinfoArmor, target.GetArmor()); @@ -939,8 +930,8 @@ namespace Game.Chat return false; } - creature.ClearPhases(); - creature.SetInPhase(phaseId, true, true); + PhasingHandler.ResetPhaseShift(creature); + PhasingHandler.AddPhase(creature, phaseId, true); creature.SetDBPhase((int)phaseId); creature.SaveToDB(); @@ -962,12 +953,8 @@ namespace Game.Chat return false; } - creature.ClearPhases(); - - foreach (uint id in Global.DB2Mgr.GetPhasesForGroup((uint)phaseGroupId)) - creature.SetInPhase(id, false, true); // don't send update here for multiple phases, only send it once after adding all phases - - creature.UpdateObjectVisibility(); + PhasingHandler.ResetPhaseShift(creature); + PhasingHandler.AddPhaseGroup(creature, (uint)phaseGroupId, true); creature.SetDBPhase(-phaseGroupId); creature.SaveToDB(); @@ -1119,7 +1106,7 @@ namespace Game.Chat if (!creature) return false; - creature.CopyPhaseFrom(chr); + PhasingHandler.InheritPhaseShift(creature, chr); creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode()); ulong db_guid = creature.GetSpawnId(); diff --git a/Source/Game/Chat/Commands/WPCommands.cs b/Source/Game/Chat/Commands/WPCommands.cs index e4c581a8f..b70e34edb 100644 --- a/Source/Game/Chat/Commands/WPCommands.cs +++ b/Source/Game/Chat/Commands/WPCommands.cs @@ -552,7 +552,7 @@ namespace Game.Chat.Commands return false; } - creature.CopyPhaseFrom(chr); + PhasingHandler.InheritPhaseShift(creature, chr); creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode()); ulong dbGuid = creature.GetSpawnId(); @@ -772,7 +772,7 @@ namespace Game.Chat.Commands return false; } - creature.CopyPhaseFrom(chr); + PhasingHandler.InheritPhaseShift(creature, chr); creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode()); ulong dbGuid = creature.GetSpawnId(); @@ -838,7 +838,7 @@ namespace Game.Chat.Commands return false; } - creature.CopyPhaseFrom(chr); + PhasingHandler.InheritPhaseShift(creature, chr); creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode()); ulong dbGuid = creature.GetSpawnId(); @@ -893,7 +893,7 @@ namespace Game.Chat.Commands return false; } - creature.CopyPhaseFrom(chr); + PhasingHandler.InheritPhaseShift(creature, chr); creature.SaveToDB(map.GetId(), 1ul << (int)map.GetSpawnMode()); ulong dbGuid = creature.GetSpawnId(); diff --git a/Source/Game/Collision/Callbacks.cs b/Source/Game/Collision/Callbacks.cs index ef88ecc4d..6cc7f9e16 100644 --- a/Source/Game/Collision/Callbacks.cs +++ b/Source/Game/Collision/Callbacks.cs @@ -227,22 +227,22 @@ namespace Game.Collision public class DynamicTreeIntersectionCallback : WorkerCallback { - public DynamicTreeIntersectionCallback(List phases) + public DynamicTreeIntersectionCallback(PhaseShift phaseShift) { _didHit = false; - _phases = phases; + _phaseShift = phaseShift; } public override bool Invoke(Ray r, IModel obj, ref float distance) { - _didHit = obj.IntersectRay(r, ref distance, true, _phases); + _didHit = obj.IntersectRay(r, ref distance, true, _phaseShift); return _didHit; } public bool didHit() { return _didHit; } bool _didHit; - List _phases; + PhaseShift _phaseShift; } diff --git a/Source/Game/Collision/DynamicTree.cs b/Source/Game/Collision/DynamicTree.cs index 0e64a127b..9dd069915 100644 --- a/Source/Game/Collision/DynamicTree.cs +++ b/Source/Game/Collision/DynamicTree.cs @@ -57,17 +57,17 @@ namespace Game.Collision impl.update(diff); } - public bool getIntersectionTime(List phases, Ray ray, Vector3 endPos, float maxDist) + public bool getIntersectionTime(Ray ray, Vector3 endPos, PhaseShift phaseShift, float maxDist) { float distance = maxDist; - DynamicTreeIntersectionCallback callback = new DynamicTreeIntersectionCallback(phases); + DynamicTreeIntersectionCallback callback = new DynamicTreeIntersectionCallback(phaseShift); impl.intersectRay(ray, callback, ref distance, endPos); if (callback.didHit()) maxDist = distance; return callback.didHit(); } - public bool getObjectHitPos(List phases, Vector3 startPos, Vector3 endPos, ref Vector3 resultHitPos, float modifyDist) + public bool getObjectHitPos(Vector3 startPos, Vector3 endPos, ref Vector3 resultHitPos, float modifyDist, PhaseShift phaseShift) { bool result = false; float maxDist = (endPos - startPos).magnitude(); @@ -82,7 +82,7 @@ namespace Game.Collision Vector3 dir = (endPos - startPos) / maxDist; // direction with length of 1 Ray ray = new Ray(startPos, dir); float dist = maxDist; - if (getIntersectionTime(phases, ray, endPos, dist)) + if (getIntersectionTime(ray, endPos, phaseShift, dist)) { resultHitPos = startPos + dir * dist; if (modifyDist < 0) @@ -105,7 +105,7 @@ namespace Game.Collision return result; } - public bool isInLineOfSight(Vector3 startPos, Vector3 endPos, List phases) + public bool isInLineOfSight(Vector3 startPos, Vector3 endPos, PhaseShift phaseShift) { float maxDist = (endPos - startPos).magnitude(); @@ -113,17 +113,17 @@ namespace Game.Collision return true; Ray r = new Ray(startPos, (endPos - startPos) / maxDist); - DynamicTreeIntersectionCallback callback = new DynamicTreeIntersectionCallback(phases); + DynamicTreeIntersectionCallback callback = new DynamicTreeIntersectionCallback(phaseShift); impl.intersectRay(r, callback, ref maxDist, endPos); return !callback.didHit(); } - public float getHeight(float x, float y, float z, float maxSearchDist, List phases) + public float getHeight(float x, float y, float z, float maxSearchDist, PhaseShift phaseShift) { Vector3 v = new Vector3(x, y, z + 0.5f); Ray r = new Ray(v, new Vector3(0, 0, -1)); - DynamicTreeIntersectionCallback callback = new DynamicTreeIntersectionCallback(phases); + DynamicTreeIntersectionCallback callback = new DynamicTreeIntersectionCallback(phaseShift); impl.intersectZAllignedRay(r, callback, ref maxSearchDist); if (callback.didHit()) diff --git a/Source/Game/Collision/Management/VMapManager.cs b/Source/Game/Collision/Management/VMapManager.cs index c22ea6a9d..e437d2317 100644 --- a/Source/Game/Collision/Management/VMapManager.cs +++ b/Source/Game/Collision/Management/VMapManager.cs @@ -36,23 +36,39 @@ namespace Game.Collision public static string VMapPath = Global.WorldMgr.GetDataPath() + "/vmaps/"; + public void Initialize(MultiMap mapData) + { + iChildMapData = mapData; + foreach (var pair in mapData) + iParentMapData[pair.Value] = pair.Key; + } + public VMAPLoadResult loadMap(uint mapId, uint x, uint y) { var result = VMAPLoadResult.Ignored; - if (_loadMap(mapId, x, y)) - result = VMAPLoadResult.OK; - else - result = VMAPLoadResult.Error; + if (isMapLoadingEnabled()) + { + if (loadSingleMap(mapId, x, y)) + { + result = VMAPLoadResult.OK; + var childMaps = iChildMapData.LookupByKey(mapId); + foreach (uint childMapId in childMaps) + if (!loadSingleMap(childMapId, x, y)) + result = VMAPLoadResult.Error; + } + else + result = VMAPLoadResult.Error; + } return result; } - bool _loadMap(uint mapId, uint tileX, uint tileY) + bool loadSingleMap(uint mapId, uint tileX, uint tileY) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree == null) { - string filename = string.Format("{0}{1:D4}.vmtree", VMapPath, mapId); + string filename = VMapPath + getMapFileName(mapId); StaticMapTree newTree = new StaticMapTree(mapId); if (!newTree.InitMap(filename, this)) return false; @@ -65,72 +81,90 @@ namespace Game.Collision return instanceTree.LoadMapTile(tileX, tileY, this); } - public WorldModel acquireModelInstance(string filename) + public void unloadMap(uint mapId, uint x, uint y) { - var model = iLoadedModelFiles.LookupByKey(filename); - if (model == null) + var childMaps = iChildMapData.LookupByKey(mapId); + foreach (uint childMapId in childMaps) + unloadSingleMap(childMapId, x, y); + + unloadSingleMap(mapId, x, y); + } + + void unloadSingleMap(uint mapId, uint x, uint y) + { + var instanceTree = iInstanceMapTrees.LookupByKey(mapId); + if (instanceTree != null) { - WorldModel worldmodel = new WorldModel(); - if (!worldmodel.readFile(VMapPath + filename + ".vmo")) + instanceTree.UnloadMapTile(x, y, this); + if (instanceTree.numLoadedTiles() == 0) { - Log.outError(LogFilter.Server, "VMapManager: could not load '{0}.vmo'", filename); - return null; + iInstanceMapTrees.Remove(mapId); } - Log.outDebug(LogFilter.Maps, "VMapManager: loading file '{0}'", filename); - iLoadedModelFiles.Add(filename, new ManagedModel()); - model = iLoadedModelFiles.LookupByKey(filename); - model.setModel(worldmodel); } - model.incRefCount(); - return model.getModel(); } - public void releaseModelInstance(string filename) + public void unloadMap(uint mapId) { - var model = iLoadedModelFiles.LookupByKey(filename); - if (model == null) + var childMaps = iChildMapData.LookupByKey(mapId); + foreach (uint childMapId in childMaps) + unloadSingleMap(childMapId); + + unloadSingleMap(mapId); + } + + void unloadSingleMap(uint mapId) + { + var instanceTree = iInstanceMapTrees.LookupByKey(mapId); + if (instanceTree != null) { - Log.outError(LogFilter.Server, "VMapManager: trying to unload non-loaded file '{0}'", filename); - return; + instanceTree.UnloadMap(this); + if (instanceTree.numLoadedTiles() == 0) + { + iInstanceMapTrees.Remove(mapId); + } } - if (model.decRefCount() == 0) + } + + public bool isInLineOfSight(uint mapId, float x1, float y1, float z1, float x2, float y2, float z2) + { + if (!isLineOfSightCalcEnabled() || Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLOS)) + return true; + + var instanceTree = iInstanceMapTrees.LookupByKey(mapId); + if (instanceTree != null) { - Log.outDebug(LogFilter.Maps, "VMapManager: unloading file '{0}'", filename); - iLoadedModelFiles.Remove(filename); + Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1); + Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2); + if (pos1 != pos2) + return instanceTree.isInLineOfSight(pos1, pos2); } + + return true; } - public bool existsMap(uint mapId, uint x, uint y) + public bool getObjectHitPos(uint mapId, float x1, float y1, float z1, float x2, float y2, float z2, out float rx, out float ry, out float rz, float modifyDist) { - return StaticMapTree.CanLoadMap(VMapPath, mapId, x, y); - } - - public static string getMapFileName(uint mapId) - { - return string.Format("{0:D4}.vmtree", mapId); - } - - public bool GetLiquidLevel(uint mapId, float x, float y, float z, byte reqLiquidType, ref float level, ref float floor, ref uint type) - { - if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLiquidStatus)) + if (isLineOfSightCalcEnabled() && !Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLOS)) { var instanceTree = iInstanceMapTrees.LookupByKey(mapId); if (instanceTree != null) { - LocationInfo info = new LocationInfo(); - Vector3 pos = convertPositionToInternalRep(x, y, z); - if (instanceTree.GetLocationInfo(pos, info)) - { - floor = info.ground_Z; - Contract.Assert(floor < float.MaxValue); - type = info.hitModel.GetLiquidType(); // entry from LiquidType.dbc - if (reqLiquidType != 0 && !Convert.ToBoolean(Global.DB2Mgr.GetLiquidFlags(type) & reqLiquidType)) - return false; - if (info.hitInstance.GetLiquidLevel(pos, info, ref level)) - return true; - } + Vector3 resultPos; + Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1); + Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2); + bool result = instanceTree.getObjectHitPos(pos1, pos2, out resultPos, modifyDist); + resultPos = convertPositionToInternalRep(resultPos.X, resultPos.Y, resultPos.Z); + rx = resultPos.X; + ry = resultPos.Y; + rz = resultPos.Z; + return result; } } + + rx = x2; + ry = y2; + rz = z2; + return false; } @@ -175,6 +209,78 @@ namespace Game.Collision return false; } + public bool GetLiquidLevel(uint mapId, float x, float y, float z, byte reqLiquidType, ref float level, ref float floor, ref uint type) + { + if (!Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLiquidStatus)) + { + var instanceTree = iInstanceMapTrees.LookupByKey(mapId); + if (instanceTree != null) + { + LocationInfo info = new LocationInfo(); + Vector3 pos = convertPositionToInternalRep(x, y, z); + if (instanceTree.GetLocationInfo(pos, info)) + { + floor = info.ground_Z; + Contract.Assert(floor < float.MaxValue); + type = info.hitModel.GetLiquidType(); // entry from LiquidType.dbc + if (reqLiquidType != 0 && !Convert.ToBoolean(Global.DB2Mgr.GetLiquidFlags(type) & reqLiquidType)) + return false; + if (info.hitInstance.GetLiquidLevel(pos, info, ref level)) + return true; + } + } + } + return false; + } + + public WorldModel acquireModelInstance(string filename) + { + var model = iLoadedModelFiles.LookupByKey(filename); + if (model == null) + { + WorldModel worldmodel = new WorldModel(); + if (!worldmodel.readFile(VMapPath + filename + ".vmo")) + { + Log.outError(LogFilter.Server, "VMapManager: could not load '{0}.vmo'", filename); + return null; + } + Log.outDebug(LogFilter.Maps, "VMapManager: loading file '{0}'", filename); + iLoadedModelFiles.Add(filename, new ManagedModel()); + model = iLoadedModelFiles.LookupByKey(filename); + model.setModel(worldmodel); + } + model.incRefCount(); + return model.getModel(); + } + + public void releaseModelInstance(string filename) + { + var model = iLoadedModelFiles.LookupByKey(filename); + if (model == null) + { + Log.outError(LogFilter.Server, "VMapManager: trying to unload non-loaded file '{0}'", filename); + return; + } + if (model.decRefCount() == 0) + { + Log.outDebug(LogFilter.Maps, "VMapManager: unloading file '{0}'", filename); + iLoadedModelFiles.Remove(filename); + } + } + + public bool existsMap(uint mapId, uint x, uint y) + { + return StaticMapTree.CanLoadMap(VMapPath, mapId, x, y, this); + } + + public int getParentMapId(uint mapId) + { + if (iParentMapData.ContainsKey(mapId)) + return (int)iParentMapData[mapId]; + + return -1; + } + Vector3 convertPositionToInternalRep(float x, float y, float z) { Vector3 pos = new Vector3(); @@ -186,6 +292,11 @@ namespace Game.Collision return pos; } + public static string getMapFileName(uint mapId) + { + return string.Format("{0:D4}.vmtree", mapId); + } + public void setEnableLineOfSightCalc(bool pVal) { _enableLineOfSightCalc = pVal; } public void setEnableHeightCalc(bool pVal) { _enableHeightCalc = pVal; } @@ -193,79 +304,10 @@ namespace Game.Collision public bool isHeightCalcEnabled() { return _enableHeightCalc; } public bool isMapLoadingEnabled() { return _enableLineOfSightCalc || _enableHeightCalc; } - public bool getObjectHitPos(uint mapId, float x1, float y1, float z1, float x2, float y2, float z2, out float rx, out float ry, out float rz, float modifyDist) - { - if (isLineOfSightCalcEnabled() && !Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLOS)) - { - var instanceTree = iInstanceMapTrees.LookupByKey(mapId); - if (instanceTree != null) - { - Vector3 resultPos; - Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1); - Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2); - bool result = instanceTree.getObjectHitPos(pos1, pos2, out resultPos, modifyDist); - resultPos = convertPositionToInternalRep(resultPos.X, resultPos.Y, resultPos.Z); - rx = resultPos.X; - ry = resultPos.Y; - rz = resultPos.Z; - return result; - } - } - - rx = x2; - ry = y2; - rz = z2; - - return false; - } - - public bool isInLineOfSight(uint mapId, float x1, float y1, float z1, float x2, float y2, float z2) - { - if (!isLineOfSightCalcEnabled() || Global.DisableMgr.IsDisabledFor(DisableType.VMAP, mapId, null, DisableFlags.VmapLOS)) - return true; - - var instanceTree = iInstanceMapTrees.LookupByKey(mapId); - if (instanceTree != null) - { - Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1); - Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2); - if (pos1 != pos2) - { - return instanceTree.isInLineOfSight(pos1, pos2); - } - } - - return true; - } - - public void unloadMap(uint mapId) - { - var instanceTree = iInstanceMapTrees.LookupByKey(mapId); - if (instanceTree != null) - { - instanceTree.UnloadMap(this); - if (instanceTree.numLoadedTiles() == 0) - { - iInstanceMapTrees.Remove(mapId); - } - } - } - - public void unloadMap(uint mapId, uint x, uint y) - { - var instanceTree = iInstanceMapTrees.LookupByKey(mapId); - if (instanceTree != null) - { - instanceTree.UnloadMapTile(x, y, this); - if (instanceTree.numLoadedTiles() == 0) - { - iInstanceMapTrees.Remove(mapId); - } - } - } - Dictionary iLoadedModelFiles = new Dictionary(); Dictionary iInstanceMapTrees = new Dictionary(); + MultiMap iChildMapData = new MultiMap(); + Dictionary iParentMapData = new Dictionary(); bool _enableLineOfSightCalc; bool _enableHeightCalc; } diff --git a/Source/Game/Collision/Maps/MapTree.cs b/Source/Game/Collision/Maps/MapTree.cs index a2346172a..27a2795d5 100644 --- a/Source/Game/Collision/Maps/MapTree.cs +++ b/Source/Game/Collision/Maps/MapTree.cs @@ -64,6 +64,7 @@ namespace Game.Collision bool success = false; if (!File.Exists(fname)) return false; + char tiled = '0'; using (BinaryReader reader = new BinaryReader(new FileStream(fname, FileMode.Open, FileAccess.Read))) { @@ -99,6 +100,21 @@ namespace Game.Collision Log.outError(LogFilter.Server, "StaticMapTree.InitMap() : could not acquire WorldModel for '{0}'", spawn.name); } } + + if (success) + { + success = reader.ReadStringFromChars(4) == "SIDX"; + if (success) + { + uint spawnIndicesSize = reader.ReadUInt32(); + for (uint i = 0; i < spawnIndicesSize && success; ++i) + { + uint spawnId = reader.ReadUInt32(); + uint spawnIndex = reader.ReadUInt32(); + iSpawnIndices[spawnId] = spawnIndex; + } + } + } } return success; } @@ -131,15 +147,14 @@ namespace Game.Collision } bool result = true; - string tilefile = VMapManager.VMapPath + getTileFileName(iMapID, tileX, tileY); - - if (!File.Exists(tilefile)) + FileStream stream = OpenMapTileFile(VMapManager.VMapPath, iMapID, tileX, tileY, vm); + if (stream == null) { iLoadedTiles[packTileID(tileX, tileY)] = false; } else { - using (BinaryReader reader = new BinaryReader(new FileStream(tilefile, FileMode.Open, FileAccess.Read))) + using (BinaryReader reader = new BinaryReader(stream)) { if (reader.ReadStringFromChars(8) != MapConst.VMapMagic) return false; @@ -159,18 +174,27 @@ namespace Game.Collision Log.outError(LogFilter.Server, "StaticMapTree.LoadMapTile() : could not acquire WorldModel [{0}, {1}]", tileX, tileY); // update tree - uint referencedVal = reader.ReadUInt32(); - if (!iLoadedSpawns.ContainsKey(referencedVal)) + if (iSpawnIndices.ContainsKey(spawn.ID)) { - iTreeValues[referencedVal] = new ModelInstance(spawn, model); - iLoadedSpawns[referencedVal] = 1; + uint referencedVal = iSpawnIndices[spawn.ID]; + if (!iLoadedSpawns.ContainsKey(referencedVal)) + { + if (referencedVal >= iNTreeValues) + { + Log.outError(LogFilter.Maps, "StaticMapTree.LoadMapTile() : invalid tree element ({0}/{1}) referenced in tile {2}", referencedVal, iNTreeValues, stream.Name); + continue; + } + + iTreeValues[referencedVal] = new ModelInstance(spawn, model); + iLoadedSpawns[referencedVal] = 1; + } + else + ++iLoadedSpawns[referencedVal]; } else - ++iLoadedSpawns[referencedVal]; + result = false; } - else - result = false; } } iLoadedTiles[packTileID(tileX, tileY)] = true; @@ -189,36 +213,42 @@ namespace Game.Collision } if (tile) // file associated with tile { - string tilefile = VMapManager.VMapPath + getTileFileName(iMapID, tileX, tileY); - using (BinaryReader reader = new BinaryReader(new FileStream(tilefile, FileMode.Open, FileAccess.Read))) + FileStream stream = OpenMapTileFile(VMapManager.VMapPath, iMapID, tileX, tileY, vm); + if (stream != null) { - bool result = true; - if (reader.ReadStringFromChars(8) != MapConst.VMapMagic) - result = false; - - uint numSpawns = reader.ReadUInt32(); - for (uint i = 0; i < numSpawns && result; ++i) + using (BinaryReader reader = new BinaryReader(stream)) { - // read model spawns - ModelSpawn spawn; - result = ModelSpawn.readFromFile(reader, out spawn); - if (result) + bool result = true; + if (reader.ReadStringFromChars(8) != MapConst.VMapMagic) + result = false; + + uint numSpawns = reader.ReadUInt32(); + for (uint i = 0; i < numSpawns && result; ++i) { - // release model instance - vm.releaseModelInstance(spawn.name); - - // update tree - uint referencedNode = reader.ReadUInt32(); - - - if (!iLoadedSpawns.ContainsKey(referencedNode)) - Log.outError(LogFilter.Server, "StaticMapTree.UnloadMapTile() : trying to unload non-referenced model '{0}' (ID:{1})", spawn.name, spawn.ID); - else if (--iLoadedSpawns[referencedNode] == 0) + // read model spawns + ModelSpawn spawn; + result = ModelSpawn.readFromFile(reader, out spawn); + if (result) { - iTreeValues[referencedNode].setUnloaded(); - iLoadedSpawns.Remove(referencedNode); - } + // release model instance + vm.releaseModelInstance(spawn.name); + // update tree + if (!iSpawnIndices.ContainsKey(spawn.ID)) + result = false; + else + { + uint referencedNode = iSpawnIndices[spawn.ID]; + if (!iLoadedSpawns.ContainsKey(referencedNode)) + Log.outError(LogFilter.Server, "StaticMapTree.UnloadMapTile() : trying to unload non-referenced model '{0}' (ID:{1})", spawn.name, spawn.ID); + else if (--iLoadedSpawns[referencedNode] == 0) + { + iTreeValues[referencedNode].setUnloaded(); + iLoadedSpawns.Remove(referencedNode); + } + } + + } } } } @@ -228,10 +258,26 @@ namespace Game.Collision static uint packTileID(uint tileX, uint tileY) { return tileX << 16 | tileY; } static void unpackTileID(uint ID, ref uint tileX, ref uint tileY) { tileX = ID >> 16; tileY = ID & 0xFF; } - public static bool CanLoadMap(string vmapPath, uint mapID, uint tileX, uint tileY) + + static FileStream OpenMapTileFile(string vmapPath, uint mapID, uint tileX, uint tileY, VMapManager vm) + { + string tilefile = vmapPath + getTileFileName(mapID, tileX, tileY); + if (!File.Exists(tilefile)) + { + int parentMapId = vm.getParentMapId(mapID); + if (parentMapId != -1) + tilefile = vmapPath + getTileFileName((uint)parentMapId, tileX, tileY); + } + + if (!File.Exists(tilefile)) + return null; + + return new FileStream(tilefile, FileMode.Open, FileAccess.Read); + } + + public static bool CanLoadMap(string vmapPath, uint mapID, uint tileX, uint tileY, VMapManager vm) { string fullname = vmapPath + VMapManager.getMapFileName(mapID); - bool success = true; if (!File.Exists(fullname)) return false; @@ -239,21 +285,24 @@ namespace Game.Collision { if (reader.ReadStringFromChars(8) != MapConst.VMapMagic) return false; + char tiled = reader.ReadChar(); if (tiled == 1) { - string tilefile = vmapPath + getTileFileName(mapID, tileX, tileY); - if (!File.Exists(tilefile)) + + FileStream stream = OpenMapTileFile(vmapPath, mapID, tileX, tileY, vm); + if (stream == null) return false; - using (BinaryReader reader1 = new BinaryReader(new FileStream(tilefile, FileMode.Open, FileAccess.Read))) + using (BinaryReader reader1 = new BinaryReader(stream)) { if (reader1.ReadStringFromChars(8) != MapConst.VMapMagic) - success = false; + return false; } } } - return success; + + return true; } public static string getTileFileName(uint mapID, uint tileX, uint tileY) { @@ -380,6 +429,7 @@ namespace Game.Collision BIH iTree = new BIH(); ModelInstance[] iTreeValues; uint iNTreeValues; + Dictionary iSpawnIndices = new Dictionary(); Dictionary iLoadedTiles = new Dictionary(); Dictionary iLoadedSpawns = new Dictionary(); diff --git a/Source/Game/Collision/Models/GameObjectModel.cs b/Source/Game/Collision/Models/GameObjectModel.cs index b9494529e..1f491d4eb 100644 --- a/Source/Game/Collision/Models/GameObjectModel.cs +++ b/Source/Game/Collision/Models/GameObjectModel.cs @@ -31,7 +31,7 @@ namespace Game.Collision { public virtual bool IsSpawned() { return false; } public virtual uint GetDisplayId() { return 0; } - public virtual bool IsInPhase(List phases) { return false; } + public virtual bool IsInPhase(PhaseShift phaseShift) { return false; } public virtual Vector3 GetPosition() { return Vector3.Zero; } public virtual float GetOrientation() { return 0.0f; } public virtual float GetScale() { return 1.0f; } @@ -86,12 +86,12 @@ namespace Game.Collision return mdl; } - public override bool IntersectRay(Ray ray, ref float maxDist, bool stopAtFirstHit, List phases) + public override bool IntersectRay(Ray ray, ref float maxDist, bool stopAtFirstHit, PhaseShift phaseShift) { if (!isCollisionEnabled() || !owner.IsSpawned()) return false; - if (!owner.IsInPhase(phases)) + if (!owner.IsInPhase(phaseShift)) return false; float time = ray.intersectionTime(iBound); diff --git a/Source/Game/Collision/Models/IModel.cs b/Source/Game/Collision/Models/IModel.cs index 38edbe855..f4262f4ee 100644 --- a/Source/Game/Collision/Models/IModel.cs +++ b/Source/Game/Collision/Models/IModel.cs @@ -25,7 +25,7 @@ namespace Game.Collision public virtual Vector3 getPosition() { return default(Vector3); } public virtual AxisAlignedBox getBounds() { return default(AxisAlignedBox); } - public virtual bool IntersectRay(Ray ray, ref float maxDist, bool stopAtFirstHit, List phases) { return false; } + public virtual bool IntersectRay(Ray ray, ref float maxDist, bool stopAtFirstHit, PhaseShift phaseShift) { return false; } public virtual bool IntersectRay(Ray ray, ref float distance, bool stopAtFirstHit) { return false; } } } diff --git a/Source/Game/Conditions/Condition.cs b/Source/Game/Conditions/Condition.cs index 8a88b420e..8a8b7bbb5 100644 --- a/Source/Game/Conditions/Condition.cs +++ b/Source/Game/Conditions/Condition.cs @@ -271,7 +271,7 @@ namespace Game.Conditions condMeets = (ConditionValue2 == Global.WorldMgr.getWorldState((WorldStates)ConditionValue1)); break; case ConditionTypes.PhaseId: - condMeets = obj.IsInPhase(ConditionValue1); + condMeets = obj.GetPhaseShift().HasPhase(ConditionValue1); break; case ConditionTypes.Title: if (player != null) @@ -303,7 +303,7 @@ namespace Game.Conditions condMeets = unit.IsInWater(); break; case ConditionTypes.TerrainSwap: - condMeets = obj.IsInTerrainSwap(ConditionValue1); + condMeets = obj.GetPhaseShift().HasVisibleMapId(ConditionValue1); break; case ConditionTypes.StandState: { diff --git a/Source/Game/Conditions/ConditionManager.cs b/Source/Game/Conditions/ConditionManager.cs index 935db62f2..fbb5eec5a 100644 --- a/Source/Game/Conditions/ConditionManager.cs +++ b/Source/Game/Conditions/ConditionManager.cs @@ -307,17 +307,7 @@ namespace Game Global.ObjectMgr.LoadGossipMenuItems(); Global.SpellMgr.UnloadSpellInfoImplicitTargetConditionLists(); - Log.outInfo(LogFilter.Server, "Re-Loading `terrain_phase_info` Table for Conditions!"); - Global.ObjectMgr.LoadTerrainPhaseInfo(); - - Log.outInfo(LogFilter.Server, "Re-Loading `terrain_swap_defaults` Table for Conditions!"); - Global.ObjectMgr.LoadTerrainSwapDefaults(); - - Log.outInfo(LogFilter.Server, "Re-Loading `terrain_worldmap` Table for Conditions!"); - Global.ObjectMgr.LoadTerrainWorldMaps(); - - Log.outInfo(LogFilter.Server, "Re-Loading `phase_area` Table for Conditions!"); - Global.ObjectMgr.LoadAreaPhases(); + Global.ObjectMgr.UnloadPhaseConditions(); } SQLResult result = DB.World.Query("SELECT SourceTypeOrReferenceId, SourceGroup, SourceEntry, SourceId, ElseGroup, ConditionTypeOrReference, ConditionTarget, " + @@ -689,29 +679,36 @@ namespace Game { if (cond.SourceEntry == 0) { - bool found = false; - var map = Global.ObjectMgr.GetAreaAndZonePhases(); - foreach (var key in map.Keys) + PhaseInfoStruct phaseInfo = Global.ObjectMgr.GetPhaseInfo(cond.SourceGroup); + if (phaseInfo != null) { - foreach (PhaseInfoStruct phase in map[key]) + bool found = false; + foreach (uint areaId in phaseInfo.Areas) { - if (phase.Id == cond.SourceGroup) + List phases = Global.ObjectMgr.GetPhasesForArea(areaId); + if (phases != null) { - phase.Conditions.Add(cond); - found = true; + foreach (PhaseAreaInfo phase in phases) + { + if (phase.PhaseInfo.Id == cond.SourceGroup) + { + phase.Conditions.Add(cond); + found = true; + } + } } } - } - if (found) - return true; + if (found) + return true; + } } else { - var phases = Global.ObjectMgr.GetPhasesForAreaOrZoneForLoading((uint)cond.SourceEntry); - foreach (PhaseInfoStruct phase in phases) + var phases = Global.ObjectMgr.GetPhasesForArea((uint)cond.SourceEntry); + foreach (PhaseAreaInfo phase in phases) { - if (phase.Id == cond.SourceGroup) + if (phase.PhaseInfo.Id == cond.SourceGroup) { phase.Conditions.Add(cond); return true; @@ -1990,15 +1987,9 @@ namespace Game || condition.MinExpansionLevel > WorldConfig.GetIntValue(WorldCfg.Expansion))) return false; - if (condition.PhaseID != 0 && !player.IsInPhase(condition.PhaseID)) - return false; - - if (condition.PhaseGroupID != 0) - { - var phases = Global.DB2Mgr.GetPhasesForGroup(condition.PhaseGroupID); - if (!phases.Intersect(player.GetPhases()).Any()) + if (condition.PhaseID != 0 || condition.PhaseGroupID != 0 || condition.PhaseUseFlags != 0) + if (!PhasingHandler.InDbPhaseShift(player, (PhaseUseFlagsValues)condition.PhaseUseFlags, condition.PhaseID, condition.PhaseGroupID)) return false; - } if (condition.QuestKillID != 0) { diff --git a/Source/Game/DataStorage/DB2Manager.cs b/Source/Game/DataStorage/DB2Manager.cs index 73e3535ee..84b9d1bbb 100644 --- a/Source/Game/DataStorage/DB2Manager.cs +++ b/Source/Game/DataStorage/DB2Manager.cs @@ -498,8 +498,6 @@ namespace Game.DataStorage foreach (WorldMapAreaRecord worldMapArea in CliDB.WorldMapAreaStorage.Values) _worldMapAreaByAreaID[worldMapArea.AreaID] = worldMapArea; - - CliDB.WorldMapAreaStorage.Clear(); } public IDB2Storage GetStorage(uint type) diff --git a/Source/Game/DataStorage/Structs/P_Records.cs b/Source/Game/DataStorage/Structs/P_Records.cs index 249a16ad8..96190cd52 100644 --- a/Source/Game/DataStorage/Structs/P_Records.cs +++ b/Source/Game/DataStorage/Structs/P_Records.cs @@ -23,7 +23,7 @@ namespace Game.DataStorage public sealed class PhaseRecord { public uint Id; - public ushort Flags; + public PhaseEntryFlags Flags; } public sealed class PhaseXPhaseGroupRecord diff --git a/Source/Game/DataStorage/Structs/S_Records.cs b/Source/Game/DataStorage/Structs/S_Records.cs index addea0020..7b35b78fd 100644 --- a/Source/Game/DataStorage/Structs/S_Records.cs +++ b/Source/Game/DataStorage/Structs/S_Records.cs @@ -552,6 +552,6 @@ namespace Game.DataStorage public SummonCategory Control; public ushort Faction; public SummonType Title; - public byte Slot; + public sbyte Slot; } } diff --git a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs index c9c12dfea..77dcdd231 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs @@ -24,6 +24,7 @@ using Game.Network.Packets; using Game.Spells; using System; using System.Collections.Generic; +using Game; namespace Game.Entities { @@ -125,7 +126,7 @@ namespace Game.Entities if (GetMiscTemplate().ScaleInfo.ExtraScale[scaleCurveIndex].AsInt32 != 0) SetUInt32Value(AreaTriggerFields.ExtraScaleCurve + scaleCurveIndex, (uint)GetMiscTemplate().ScaleInfo.ExtraScale[scaleCurveIndex].AsInt32); - CopyPhaseFrom(caster); + PhasingHandler.InheritPhaseShift(this, caster); if (target && GetTemplate().HasFlag(AreaTriggerFlags.HasAttached)) { diff --git a/Source/Game/Entities/Conversation.cs b/Source/Game/Entities/Conversation.cs index ff4ee8539..680e99675 100644 --- a/Source/Game/Entities/Conversation.cs +++ b/Source/Game/Entities/Conversation.cs @@ -111,7 +111,7 @@ namespace Game.Entities Relocate(pos); base._Create(ObjectGuid.Create(HighGuid.Conversation, GetMapId(), conversationEntry, lowGuid)); - CopyPhaseFrom(creator); + PhasingHandler.InheritPhaseShift(this, creator); SetEntry(conversationEntry); SetObjectScale(1.0f); diff --git a/Source/Game/Entities/Corpse.cs b/Source/Game/Entities/Corpse.cs index c35a80631..0e00e86b0 100644 --- a/Source/Game/Entities/Corpse.cs +++ b/Source/Game/Entities/Corpse.cs @@ -84,7 +84,7 @@ namespace Game.Entities _cellCoord = GridDefines.ComputeCellCoord(GetPositionX(), GetPositionY()); - CopyPhaseFrom(owner); + PhasingHandler.InheritPhaseShift(this, owner); return true; } @@ -114,12 +114,12 @@ namespace Game.Entities stmt.AddValue(index++, GetInstanceId()); // instanceId trans.Append(stmt); - foreach (uint phaseId in GetPhases()) + foreach (PhaseRef phase in GetPhaseShift().GetPhases()) { index = 0; stmt = DB.Characters.GetPreparedStatement(CharStatements.INS_CORPSE_PHASES); stmt.AddValue(index++, GetOwnerGUID().GetCounter()); // OwnerGuid - stmt.AddValue(index++, phaseId); // PhaseId + stmt.AddValue(index++, phase.Id); // PhaseId trans.Append(stmt); } } diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 80eb568f6..4ac9dca8b 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -741,12 +741,11 @@ namespace Game.Entities { SetMap(map); - if (data != null && data.phaseId != 0) - SetInPhase(data.phaseId, false, true); - - if (data != null && data.phaseGroup != 0) - foreach (var ph in Global.DB2Mgr.GetPhasesForGroup(data.phaseGroup)) - SetInPhase(ph, false, true); + if (data != null) + { + PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup); + PhasingHandler.InitDbVisibleMapId(GetPhaseShift(), data.terrainSwapMap); + } CreatureTemplate cinfo = Global.ObjectMgr.GetCreatureTemplate(entry); if (cinfo == null) @@ -2623,7 +2622,7 @@ namespace Game.Entities return; // Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc) - float ground = GetMap().GetHeight(GetPhases(), GetPositionX(), GetPositionY(), GetPositionZMinusOffset()); + float ground = GetMap().GetHeight(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZMinusOffset()); bool isInAir = (MathFunctions.fuzzyGt(GetPositionZMinusOffset(), ground + 0.05f) || MathFunctions.fuzzyLt(GetPositionZMinusOffset(), ground - 0.05f)); // Can be underground too, prevent the falling @@ -2934,7 +2933,7 @@ namespace Game.Entities m_deathState = DeathState.Dead; if (CanFly()) { - float tz = map.GetHeight(GetPhases(), data.posX, data.posY, data.posZ, true, MapConst.MaxFallDistance); + float tz = map.GetHeight(GetPhaseShift(), data.posX, data.posY, data.posZ, true, MapConst.MaxFallDistance); if (data.posZ - tz > 0.1f && GridDefines.IsValidMapCoord(tz)) Relocate(data.posX, data.posY, tz); } diff --git a/Source/Game/Entities/Creature/CreatureData.cs b/Source/Game/Entities/Creature/CreatureData.cs index 73aacc808..2e7ff9551 100644 --- a/Source/Game/Entities/Creature/CreatureData.cs +++ b/Source/Game/Entities/Creature/CreatureData.cs @@ -297,8 +297,10 @@ namespace Game.Entities public uint unit_flags2; // enum UnitFlags2 mask values public uint unit_flags3; // enum UnitFlags3 mask values public uint dynamicflags; + public PhaseUseFlagsValues phaseUseFlags; public uint phaseId; public uint phaseGroup; + public int terrainSwapMap; public uint ScriptId; public bool dbData; } diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index 44a372f6f..6bd2bed7f 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -948,15 +948,8 @@ namespace Game.Entities if (!Create(entry, map, pos, data.rotation, animprogress, go_state, artKit)) return false; - if (data.phaseId != 0) - SetInPhase(data.phaseId, false, true); - - if (data.phaseGroup != 0) - { - // Set the gameobject in all the phases of the phasegroup - foreach (var ph in Global.DB2Mgr.GetPhasesForGroup(data.phaseGroup)) - SetInPhase(ph, false, true); - } + PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup); + PhasingHandler.InitDbVisibleMapId(GetPhaseShift(), data.terrainSwapMap); if (data.spawntimesecs >= 0) { @@ -2766,7 +2759,7 @@ namespace Game.Entities } public override uint GetDisplayId() { return _owner.GetDisplayId(); } - public override bool IsInPhase(List phases) { return _owner.IsInPhase(phases); } + public override bool IsInPhase(PhaseShift phaseShift) { return _owner.GetPhaseShift().CanSee(phaseShift); } public override Vector3 GetPosition() { return new Vector3(_owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ()); } public override float GetOrientation() { return _owner.GetOrientation(); } public override float GetScale() { return _owner.GetObjectScale(); } diff --git a/Source/Game/Entities/GameObject/GameObjectData.cs b/Source/Game/Entities/GameObject/GameObjectData.cs index 1705142b7..424b8d1f7 100644 --- a/Source/Game/Entities/GameObject/GameObjectData.cs +++ b/Source/Game/Entities/GameObject/GameObjectData.cs @@ -1089,8 +1089,10 @@ namespace Game.Entities public GameObjectState go_state; public ulong spawnMask; public byte artKit; + public PhaseUseFlagsValues phaseUseFlags; public uint phaseId; public uint phaseGroup; + public int terrainSwapMap; public uint ScriptId; public bool dbData = true; } diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 2195ec003..2b934e864 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -1608,17 +1608,17 @@ namespace Game.Entities public uint GetZoneId() { - return GetMap().GetZoneId(GetPositionX(), GetPositionY(), GetPositionZ()); + return GetMap().GetZoneId(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZ()); } public uint GetAreaId() { - return GetMap().GetAreaId(GetPositionX(), GetPositionY(), GetPositionZ()); + return GetMap().GetAreaId(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZ()); } public void GetZoneAndAreaId(out uint zoneid, out uint areaid) { - GetMap().GetZoneAndAreaId(out zoneid, out areaid, posX, posY, posZ); + GetMap().GetZoneAndAreaId(GetPhaseShift(), out zoneid, out areaid, posX, posY, posZ); } public InstanceScript GetInstanceScript() @@ -2047,7 +2047,7 @@ namespace Game.Entities if (!go) return null; - go.CopyPhaseFrom(this); + PhasingHandler.InheritPhaseShift(go, this); go.SetRespawnTime((int)respawnTime); if (IsTypeId(TypeId.Player) || IsTypeId(TypeId.Unit)) //not sure how to handle this @@ -2154,242 +2154,15 @@ namespace Game.Entities return (valuesCount > (uint)UnitFields.CombatReach) ? GetFloatValue(UnitFields.CombatReach) : SharedConst.DefaultWorldObjectSize; } - bool HasInPhaseList(uint phase) - { - return _phases.Contains(phase); - } - - /// - /// Updates Area based phases, does not remove phases from auras - /// Phases from gm commands are not taken into calculations, they can be lost!! - /// - public void UpdateAreaAndZonePhase() - { - bool updateNeeded = false; - var allAreasPhases = Global.ObjectMgr.GetAreaAndZonePhases(); - uint[] zoneAndArea = { GetZoneId(), GetAreaId() }; - - // We first remove all phases from other areas & zones - foreach (var key in allAreasPhases.Keys) - foreach (PhaseInfoStruct phase in allAreasPhases[key]) - if (!Global.DB2Mgr.IsInArea(GetAreaId(), key)) - updateNeeded = SetInPhase(phase.Id, false, false) || updateNeeded; // not in area, remove phase, true if there was something removed - - // Then we add the phases from this area and zone if conditions are met - // Zone is done before Area, so if Area does not meet condition, the phase will be removed - foreach (uint area in zoneAndArea) - { - var currentPhases = Global.ObjectMgr.GetPhasesForArea(area); - if (currentPhases != null) - { - foreach (PhaseInfoStruct phaseInfoStruct in currentPhases) - { - bool apply = Global.ConditionMgr.IsObjectMeetToConditions(this, phaseInfoStruct.Conditions); - - // add or remove phase depending of condition - updateNeeded = SetInPhase(phaseInfoStruct.Id, false, apply) || updateNeeded; - } - } - } - - // do not remove a phase if it would be removed by an area but we have the same phase from an aura - Unit unit = ToUnit(); - if (unit) - { - var auraPhaseList = unit.GetAuraEffectsByType(AuraType.Phase); - foreach (var eff in auraPhaseList) - { - uint phase = (uint)eff.GetMiscValueB(); - updateNeeded = SetInPhase(phase, false, true) || updateNeeded; - } - var auraPhaseGroupList = unit.GetAuraEffectsByType(AuraType.PhaseGroup); - foreach (var eff in auraPhaseGroupList) - { - uint phaseGroup = (uint)eff.GetMiscValueB(); - foreach (uint phase in Global.DB2Mgr.GetPhasesForGroup(phaseGroup)) - updateNeeded = SetInPhase(phase, false, true) || updateNeeded; - } - } - - // only update visibility and send packets if there was a change in the phase list - - if (updateNeeded && IsTypeId(TypeId.Player) && IsInWorld) - ToPlayer().GetSession().SendSetPhaseShift(GetPhases(), GetTerrainSwaps(), GetWorldMapAreaSwaps()); - - // only update visibilty once, to prevent objects appearing for a moment while adding in multiple phases - if (updateNeeded && IsInWorld) - UpdateObjectVisibility(); - } - - public virtual bool SetInPhase(uint id, bool update, bool apply) - { - if (id != 0) - { - if (apply) - { - // do not run the updates if we are already in this phase - if (_phases.Contains(id)) - return false; - - _phases.Add(id); - } - else - { - if (!_phases.Contains(id)) - return false; - - // if area phase passes the condition we should not remove it (ie: if remove called from aura remove) - // this however breaks the .mod phase command, you wont be able to remove any area based phases with it - var phases = Global.ObjectMgr.GetPhasesForArea(GetAreaId()); - if (phases != null) - { - foreach (PhaseInfoStruct phase in phases) - { - if (id == phase.Id) - if (Global.ConditionMgr.IsObjectMeetToConditions(this, phase.Conditions)) - return false; - } - } - - _phases.Remove(id); - } - } - RebuildTerrainSwaps(); - - if (update && IsInWorld) - UpdateObjectVisibility(); - return true; - } - - public void CopyPhaseFrom(WorldObject obj, bool update = false) - { - if (!obj) - return; - - foreach (uint phase in obj.GetPhases()) - SetInPhase(phase, false, true); - - if (update && IsInWorld) - UpdateObjectVisibility(); - } - - public void ClearPhases(bool update = false) - { - _phases.Clear(); - - RebuildTerrainSwaps(); - - if (update && IsInWorld) - UpdateObjectVisibility(); - } - - public bool IsInPhase(uint phase) { return _phases.Contains(phase); } - - public bool IsInPhase(List phases) - { - // PhaseId 169 is the default fallback phase - if (_phases.Empty() && phases.Empty()) - return true; - - if (_phases.Empty() && phases.Contains(169)) - return true; - - if (phases.Empty() && _phases.Contains(169)) - return true; - - return _phases.Intersect(phases).Any(); - } - public bool IsInPhase(WorldObject obj) { - // PhaseId 169 is the default fallback phase - if (_phases.Empty() && obj.GetPhases().Empty()) - return true; - - if (_phases.Empty() && obj.IsInPhase(169)) - return true; - - if (obj.GetPhases().Empty() && IsInPhase(169)) - return true; - - if (IsTypeId(TypeId.Player) && ToPlayer().IsGameMaster()) - return true; - - return IsInPhase(obj.GetPhases()); + return GetPhaseShift().CanSee(obj.GetPhaseShift()); } - public bool IsInTerrainSwap(uint terrainSwap) { return _terrainSwaps.Contains(terrainSwap); } - - public void RebuildTerrainSwaps() - { - // Clear all terrain swaps, will be rebuilt below - // Reason for this is, multiple phases can have the same terrain swap, we should not remove the swap if another phase still use it - _terrainSwaps.Clear(); - - // Check all applied phases for terrain swap and add it only once - foreach (uint phaseId in _phases) - { - var swaps = Global.ObjectMgr.GetPhaseTerrainSwaps(phaseId); - foreach (uint swap in swaps) - { - // only add terrain swaps for current map - MapRecord mapEntry = CliDB.MapStorage.LookupByKey(swap); - if (mapEntry == null || mapEntry.ParentMapID != GetMapId()) - continue; - - if (Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.TerrainSwap, swap, this)) - _terrainSwaps.Add(swap); - } - } - - // get default terrain swaps, only for current map always - var mapSwaps = Global.ObjectMgr.GetDefaultTerrainSwaps(GetMapId()); - foreach (uint swap in mapSwaps) - { - if (Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.TerrainSwap, swap, this)) - _terrainSwaps.Add(swap); - } - - // online players have a game client with world map display - if (IsTypeId(TypeId.Player)) - RebuildWorldMapAreaSwaps(); - } - - void RebuildWorldMapAreaSwaps() - { - // Clear all world map area swaps, will be rebuilt below - _worldMapAreaSwaps.Clear(); - - // get ALL default terrain swaps, if we are using it (condition is true) - // send the worldmaparea for it, to see swapped worldmaparea in client from other maps too, not just from our current - var defaults = Global.ObjectMgr.GetDefaultTerrainSwapStore(); - foreach (uint swap in defaults.Values) - { - var uiMapSwaps = Global.ObjectMgr.GetTerrainWorldMaps(swap); - if (Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.TerrainSwap, swap, this)) - { - foreach (uint worldMapAreaId in uiMapSwaps) - _worldMapAreaSwaps.Add(worldMapAreaId); - } - } - - // Check all applied phases for world map area swaps - foreach (uint phaseId in _phases) - { - var swaps = Global.ObjectMgr.GetPhaseTerrainSwaps(phaseId); - foreach (uint swap in swaps) - { - var uiMapSwaps = Global.ObjectMgr.GetTerrainWorldMaps(swap); - if (Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.TerrainSwap, swap, this)) - foreach (uint worldMapAreaId in uiMapSwaps) - _worldMapAreaSwaps.Add(worldMapAreaId); - } - } - } - - public List GetPhases() { return _phases; } - public List GetTerrainSwaps() { return _terrainSwaps; } - public List GetWorldMapAreaSwaps() { return _worldMapAreaSwaps; } + public PhaseShift GetPhaseShift() { return _phaseShift; } + public void SetPhaseShift(PhaseShift phaseShift) { _phaseShift = phaseShift; } + public PhaseShift GetSuppressedPhaseShift() { return _suppressedPhaseShift; } + public void SetSuppressedPhaseShift(PhaseShift phaseShift) { _suppressedPhaseShift = phaseShift; } public int GetDBPhase() { return _dbPhase; } // if negative it is used as PhaseGroupId @@ -2707,7 +2480,7 @@ namespace Game.Entities else GetHitSpherePointFor(new Position(ox, oy, oz), out x, out y, out z); - return GetMap().isInLineOfSight(x, y, z + 2.0f, ox, oy, oz + 2.0f, GetPhases()); + return GetMap().isInLineOfSight(GetPhaseShift(), x, y, z + 2.0f, ox, oy, oz + 2.0f); } return true; @@ -2838,7 +2611,7 @@ namespace Game.Entities public void UpdateGroundPositionZ(float x, float y, ref float z) { - float new_z = GetMap().GetHeight(GetPhases(), x, y, z, true); + float new_z = GetMap().GetHeight(GetPhaseShift(), x, y, z, true); if (new_z > MapConst.InvalidHeight) z = new_z + 0.05f; // just to be sure that we are not a few pixel under the surface } @@ -2860,8 +2633,8 @@ namespace Game.Entities bool canSwim = ToCreature().CanSwim(); float ground_z = z; float max_z = canSwim - ? GetMap().GetWaterOrGroundLevel(GetPhases(), x, y, z, ref ground_z, !ToUnit().HasAuraType(AuraType.WaterWalk)) - : ((ground_z = GetMap().GetHeight(GetPhases(), x, y, z, true))); + ? GetMap().GetWaterOrGroundLevel(GetPhaseShift(), x, y, z, ref ground_z, !ToUnit().HasAuraType(AuraType.WaterWalk)) + : ((ground_z = GetMap().GetHeight(GetPhaseShift(), x, y, z, true))); if (max_z > MapConst.InvalidHeight) { if (z > max_z) @@ -2872,7 +2645,7 @@ namespace Game.Entities } else { - float ground_z = GetMap().GetHeight(GetPhases(), x, y, z, true); + float ground_z = GetMap().GetHeight(GetPhaseShift(), x, y, z, true); if (z < ground_z) z = ground_z; } @@ -2884,7 +2657,7 @@ namespace Game.Entities if (!ToPlayer().CanFly()) { float ground_z = z; - float max_z = GetMap().GetWaterOrGroundLevel(GetPhases(), x, y, z, ref ground_z, !ToUnit().HasAuraType(AuraType.WaterWalk)); + float max_z = GetMap().GetWaterOrGroundLevel(GetPhaseShift(), x, y, z, ref ground_z, !ToUnit().HasAuraType(AuraType.WaterWalk)); if (max_z > MapConst.InvalidHeight) { if (z > max_z) @@ -2895,7 +2668,7 @@ namespace Game.Entities } else { - float ground_z = GetMap().GetHeight(GetPhases(), x, y, z, true); + float ground_z = GetMap().GetHeight(GetPhaseShift(), x, y, z, true); if (z < ground_z) z = ground_z; } @@ -2903,7 +2676,7 @@ namespace Game.Entities } default: { - float ground_z = GetMap().GetHeight(GetPhases(), x, y, z, true); + float ground_z = GetMap().GetHeight(GetPhaseShift(), x, y, z, true); if (ground_z > MapConst.InvalidHeight) z = ground_z; break; @@ -3002,8 +2775,8 @@ namespace Game.Entities return; } - ground = GetMap().GetHeight(GetPhases(), destx, desty, MapConst.MaxHeight, true); - floor = GetMap().GetHeight(GetPhases(), destx, desty, pos.posZ, true); + ground = GetMap().GetHeight(GetPhaseShift(), destx, desty, MapConst.MaxHeight, true); + floor = GetMap().GetHeight(GetPhaseShift(), destx, desty, pos.posZ, true); destz = Math.Abs(ground - pos.posZ) <= Math.Abs(floor - pos.posZ) ? ground : floor; float step = dist / 10.0f; @@ -3015,8 +2788,8 @@ namespace Game.Entities { destx -= step * (float)Math.Cos(angle); desty -= step * (float)Math.Sin(angle); - ground = GetMap().GetHeight(GetPhases(), destx, desty, MapConst.MaxHeight, true); - floor = GetMap().GetHeight(GetPhases(), destx, desty, pos.posZ, true); + ground = GetMap().GetHeight(GetPhaseShift(), destx, desty, MapConst.MaxHeight, true); + floor = GetMap().GetHeight(GetPhaseShift(), destx, desty, pos.posZ, true); destz = Math.Abs(ground - pos.posZ) <= Math.Abs(floor - pos.posZ) ? ground : floor; } // we have correct destz now @@ -3035,8 +2808,8 @@ namespace Game.Entities float NormalizeZforCollision(WorldObject obj, float x, float y, float z) { - float ground = obj.GetMap().GetHeight(obj.GetPhases(), x, y, MapConst.MaxHeight, true); - float floor = obj.GetMap().GetHeight(obj.GetPhases(), x, y, z + 2.0f, true); + float ground = obj.GetMap().GetHeight(obj.GetPhaseShift(), x, y, MapConst.MaxHeight, true); + float floor = obj.GetMap().GetHeight(obj.GetPhaseShift(), x, y, z + 2.0f, true); float helper = Math.Abs(ground - z) <= Math.Abs(floor - z) ? ground : floor; if (z > helper) // must be above ground { @@ -3047,7 +2820,7 @@ namespace Game.Entities return z; } LiquidData liquid_status; - ZLiquidStatus res = obj.GetMap().getLiquidStatus(x, y, z, MapConst.MapAllLiquidTypes, out liquid_status); + ZLiquidStatus res = obj.GetMap().getLiquidStatus(obj.GetPhaseShift(), x, y, z, MapConst.MapAllLiquidTypes, out liquid_status); if (res != 0 && liquid_status.level > helper) // water must be above ground { if (liquid_status.level > z) // z is underwater @@ -3074,7 +2847,7 @@ namespace Game.Entities } destz = NormalizeZforCollision(this, destx, desty, pos.GetPositionZ()); - bool col = Global.VMapMgr.getObjectHitPos(GetMapId(), pos.posX, pos.posY, pos.posZ + 0.5f, destx, desty, destz + 0.5f, out destx, out desty, out destz, -0.5f); + bool col = Global.VMapMgr.getObjectHitPos(PhasingHandler.GetTerrainMapId(GetPhaseShift(), GetMap(), pos.posX, pos.posY), pos.posX, pos.posY, pos.posZ + 0.5f, destx, desty, destz + 0.5f, out destx, out desty, out destz, -0.5f); // collision occured if (col) @@ -3086,7 +2859,7 @@ namespace Game.Entities } // check dynamic collision - col = GetMap().getObjectHitPos(GetPhases(), pos.posX, pos.posY, pos.posZ + 0.5f, destx, desty, destz + 0.5f, out destx, out desty, out destz, -0.5f); + col = GetMap().getObjectHitPos(GetPhaseShift(), pos.posX, pos.posY, pos.posZ + 0.5f, destx, desty, destz + 0.5f, out destx, out desty, out destz, -0.5f); // Collided with a gameobject if (col) @@ -3150,9 +2923,8 @@ namespace Game.Entities Transport m_transport; Map _currMap; uint instanceId; - List _phases = new List(); - List _terrainSwaps = new List(); - List _worldMapAreaSwaps = new List(); + PhaseShift _phaseShift= new PhaseShift(); + PhaseShift _suppressedPhaseShift = new PhaseShift(); // contains phases for current area but not applied due to conditions int _dbPhase; public bool IsInWorld { get; set; } diff --git a/Source/Game/Entities/Pet.cs b/Source/Game/Entities/Pet.cs index bc56988b7..d575a5393 100644 --- a/Source/Game/Entities/Pet.cs +++ b/Source/Game/Entities/Pet.cs @@ -172,7 +172,7 @@ namespace Game.Entities if (!Create(map.GenerateLowGuid(HighGuid.Pet), map, petEntry)) return false; - CopyPhaseFrom(owner); + PhasingHandler.InheritPhaseShift(this, owner); setPetType(petType); SetFaction(owner.getFaction()); diff --git a/Source/Game/Entities/Player/Player.DB.cs b/Source/Game/Entities/Player/Player.DB.cs index c1cec54e4..1a86b3dc4 100644 --- a/Source/Game/Entities/Player/Player.DB.cs +++ b/Source/Game/Entities/Player/Player.DB.cs @@ -3550,7 +3550,7 @@ namespace Game.Entities if (!CliDB.MapStorage.ContainsKey(map)) return 0; - zone = Global.MapMgr.GetZoneId(map, posx, posy, posz); + zone = Global.MapMgr.GetZoneId(PhasingHandler.EmptyPhaseShift, map, posx, posy, posz); if (zone > 0) { diff --git a/Source/Game/Entities/Player/Player.Fields.cs b/Source/Game/Entities/Player/Player.Fields.cs index e62ba365a..25c3dd50a 100644 --- a/Source/Game/Entities/Player/Player.Fields.cs +++ b/Source/Game/Entities/Player/Player.Fields.cs @@ -191,6 +191,7 @@ namespace Game.Entities SpecializationInfo _specializationInfo; public List m_clientGUIDs = new List(); + public List m_visibleTransports = new List(); public WorldObject seerView; // only changed for direct client control (possess, vehicle etc.), not stuff you control using pet commands public Unit m_unitMovedByMe; diff --git a/Source/Game/Entities/Player/Player.Map.cs b/Source/Game/Entities/Player/Player.Map.cs index 2ed0f0caa..43abec31a 100644 --- a/Source/Game/Entities/Player/Player.Map.cs +++ b/Source/Game/Entities/Player/Player.Map.cs @@ -120,7 +120,7 @@ namespace Game.Entities UpdatePvPState(true); UpdateAreaDependentAuras(newArea); - UpdateAreaAndZonePhase(); + PhasingHandler.OnAreaChange(this); if (IsAreaThatActivatesPvpTalents(newArea)) EnablePvpRules(); @@ -232,8 +232,6 @@ namespace Game.Entities UpdateLocalChannels(newZone); UpdateZoneDependentAuras(newZone); - - UpdateAreaAndZonePhase(); } public InstanceBind GetBoundInstance(uint mapid, Difficulty difficulty, bool withExpired = false) @@ -721,7 +719,7 @@ namespace Game.Entities public override void UpdateUnderwaterState(Map m, float x, float y, float z) { LiquidData liquid_status; - ZLiquidStatus res = m.getLiquidStatus(x, y, z, MapConst.MapAllLiquidTypes, out liquid_status); + ZLiquidStatus res = m.getLiquidStatus(GetPhaseShift(), x, y, z, MapConst.MapAllLiquidTypes, out liquid_status); if (res == 0) { m_MirrorTimerFlags &= ~(PlayerUnderwaterState.InWater | PlayerUnderwaterState.InLava | PlayerUnderwaterState.InSlime | PlayerUnderwaterState.InDarkWater); diff --git a/Source/Game/Entities/Player/Player.Quest.cs b/Source/Game/Entities/Player/Player.Quest.cs index 2b233d720..5b0a67c08 100644 --- a/Source/Game/Entities/Player/Player.Quest.cs +++ b/Source/Game/Entities/Player/Player.Quest.cs @@ -1817,7 +1817,7 @@ namespace Game.Entities } UpdateForQuestWorldObjects(); - SendUpdatePhasing(); + PhasingHandler.OnConditionChange(this); } public QuestGiverStatus GetQuestDialogStatus(WorldObject questgiver) diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index f9f44dcc2..60f89e4fb 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -1937,7 +1937,7 @@ namespace Game.Entities public override bool IsUnderWater() { return IsInWater() && - GetPositionZ() < (GetMap().GetWaterLevel(GetPositionX(), GetPositionY()) - 2); + GetPositionZ() < (GetMap().GetWaterLevel(GetPhaseShift(), GetPositionX(), GetPositionY()) - 2); } public override bool IsInWater() { @@ -2117,9 +2117,16 @@ namespace Game.Entities } public bool IsInAreaTriggerRadius(AreaTriggerRecord trigger) { - if (trigger == null || GetMapId() != trigger.ContinentID) + if (trigger == null) return false; + if (GetMapId() != trigger.ContinentID && !GetPhaseShift().HasVisibleMapId(trigger.ContinentID)) + return false; + + if (trigger.PhaseID != 0 || trigger.PhaseGroupID != 0 || trigger.PhaseUseFlags != 0) + if (!PhasingHandler.InDbPhaseShift(this, (PhaseUseFlagsValues)trigger.PhaseUseFlags, trigger.PhaseID, trigger.PhaseGroupID)) + return false; + if (trigger.Radius > 0.0f) { // if we have radius check it @@ -2203,10 +2210,13 @@ namespace Game.Entities getHostileRefManager().setOnlineOfflineState(false); CombatStopWithPets(); + PhasingHandler.SetAlwaysVisible(GetPhaseShift(), true); m_serverSideVisibilityDetect.SetValue(ServerSideVisibilityType.GM, GetSession().GetSecurity()); } else { + PhasingHandler.SetAlwaysVisible(GetPhaseShift(), false); + m_ExtraFlags &= ~PlayerExtraFlags.GMOn; SetFactionForRace(GetRace()); RemoveFlag(PlayerFields.Flags, PlayerFlags.GM); @@ -4820,7 +4830,7 @@ namespace Game.Entities return null; } - pet.CopyPhaseFrom(this); + PhasingHandler.InheritPhaseShift(pet, this); pet.SetCreatorGUID(GetGUID()); pet.SetUInt32Value(UnitFields.FactionTemplate, getFaction()); @@ -5691,6 +5701,8 @@ namespace Game.Entities SendRaidDifficulty(difficulty.Flags.HasAnyFlag(DifficultyFlags.Legacy)); } + PhasingHandler.OnMapChange(this); + if (_garrison != null) _garrison.SendRemoteInfo(); @@ -6202,7 +6214,7 @@ namespace Game.Entities return; bool isOutdoor; - uint areaId = GetMap().GetAreaId(GetPositionX(), GetPositionY(), GetPositionZ(), out isOutdoor); + uint areaId = GetMap().GetAreaId(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZ(), out isOutdoor); AreaTableRecord areaEntry = CliDB.AreaTableStorage.LookupByKey(areaId); if (WorldConfig.GetBoolValue(WorldCfg.VmapIndoorCheck) && !isOutdoor) @@ -7307,16 +7319,6 @@ namespace Game.Entities SendMessageToSet(cancelAutoRepeat, false); } - public void SendUpdatePhasing() - { - if (!IsInWorld) - return; - - RebuildTerrainSwaps(); // to set default map swaps - - GetSession().SendSetPhaseShift(GetPhases(), GetTerrainSwaps(), GetWorldMapAreaSwaps()); - } - public override void BuildCreateUpdateBlockForPlayer(UpdateData data, Player target) { if (target == this) diff --git a/Source/Game/Entities/Transport.cs b/Source/Game/Entities/Transport.cs index b358818c8..b1a3249d8 100644 --- a/Source/Game/Entities/Transport.cs +++ b/Source/Game/Entities/Transport.cs @@ -338,15 +338,8 @@ namespace Game.Entities return null; } - if (data.phaseId != 0) - creature.SetInPhase(data.phaseId, false, true); - else if (data.phaseGroup != 0) - { - foreach (var phase in Global.DB2Mgr.GetPhasesForGroup(data.phaseGroup)) - creature.SetInPhase(phase, false, true); - } - else - creature.CopyPhaseFrom(this); + PhasingHandler.InitDbPhaseShift(creature.GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup); + PhasingHandler.InitDbVisibleMapId(creature.GetPhaseShift(), data.terrainSwapMap); if (!map.AddToMap(creature)) return null; @@ -383,6 +376,9 @@ namespace Game.Entities return null; } + PhasingHandler.InitDbPhaseShift(go.GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup); + PhasingHandler.InitDbVisibleMapId(go.GetPhaseShift(), data.terrainSwapMap); + if (!map.AddToMap(go)) return null; @@ -444,12 +440,6 @@ namespace Game.Entities } } - List phases = new List(); - if (summoner) - phases = summoner.GetPhases(); - else - phases = GetPhases(); // If there was no summoner, try to use the transport phases - TempSummon summon = null; switch (mask) { @@ -477,8 +467,7 @@ namespace Game.Entities if (!summon.Create(map.GenerateLowGuid(HighGuid.Creature), map, entry, x, y, z, o, null, vehId)) return null; - foreach (var phase in phases) - summon.SetInPhase(phase, false, true); + PhasingHandler.InheritPhaseShift(summon, summoner ? (WorldObject)summoner : this); summon.SetUInt32Value(UnitFields.CreatedBySpell, spellId); diff --git a/Source/Game/Entities/Unit/Unit.Movement.cs b/Source/Game/Entities/Unit/Unit.Movement.cs index 80129dc83..a31158308 100644 --- a/Source/Game/Entities/Unit/Unit.Movement.cs +++ b/Source/Game/Entities/Unit/Unit.Movement.cs @@ -64,11 +64,11 @@ namespace Game.Entities } public virtual bool IsInWater() { - return GetMap().IsInWater(GetPositionX(), GetPositionY(), GetPositionZ()); + return GetMap().IsInWater(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZ()); } public virtual bool IsUnderWater() { - return GetMap().IsUnderWater(GetPositionX(), GetPositionY(), GetPositionZ()); + return GetMap().IsUnderWater(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZ()); } void propagateSpeedChange() { GetMotionMaster().propagateSpeedChange(); } @@ -677,7 +677,7 @@ namespace Game.Entities } LiquidData liquid; - ZLiquidStatus liquidStatus = GetMap().getLiquidStatus(GetPositionX(), GetPositionY(), GetPositionZ(), MapConst.MapAllLiquidTypes, out liquid); + ZLiquidStatus liquidStatus = GetMap().getLiquidStatus(GetPhaseShift(), GetPositionX(), GetPositionY(), GetPositionZ(), MapConst.MapAllLiquidTypes, out liquid); isSubmerged = liquidStatus.HasAnyFlag(ZLiquidStatus.UnderWater) || HasUnitMovementFlag(MovementFlag.Swimming); isInWater = liquidStatus.HasAnyFlag(ZLiquidStatus.InWater | ZLiquidStatus.UnderWater); @@ -748,7 +748,7 @@ namespace Game.Entities return; LiquidData liquid_status; - ZLiquidStatus res = m.getLiquidStatus(x, y, z, MapConst.MapAllLiquidTypes, out liquid_status); + ZLiquidStatus res = m.getLiquidStatus(GetPhaseShift(), x, y, z, MapConst.MapAllLiquidTypes, out liquid_status); if (res == 0) { if (_lastLiquid != null && _lastLiquid.SpellID != 0) diff --git a/Source/Game/Entities/Unit/Unit.Pets.cs b/Source/Game/Entities/Unit/Unit.Pets.cs index f6295ab3a..c0e05fdb3 100644 --- a/Source/Game/Entities/Unit/Unit.Pets.cs +++ b/Source/Game/Entities/Unit/Unit.Pets.cs @@ -784,7 +784,7 @@ namespace Game.Entities return false; } - pet.CopyPhaseFrom(this); + PhasingHandler.InheritPhaseShift(pet, this); pet.GetCharmInfo().SetPetNumber(Global.ObjectMgr.GeneratePetNumber(), true); // this enables pet details window (Shift+P) diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index 8afbde236..957de357b 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -3313,7 +3313,7 @@ namespace Game.Entities } } } - public void RemoveNotOwnSingleTargetAuras(uint newPhase = 0, bool phaseid = false) + public void RemoveNotOwnSingleTargetAuras(bool onPhaseChange = false) { // Iterate m_ownedAuras - aura is marked as single target in Unit::AddAura (and pushed to m_ownedAuras). // m_appliedAuras will NOT contain the aura before first Unit::Update after adding it to m_ownedAuras. @@ -3330,12 +3330,12 @@ namespace Game.Entities if (aura.GetCasterGUID() != GetGUID() && aura.IsSingleTarget()) { - if (newPhase == 0 && !phaseid) + if (onPhaseChange) RemoveOwnedAura(list[i]); else { Unit caster = aura.GetCaster(); - if (!caster || (newPhase != 0 && !caster.IsInPhase(newPhase)) || (newPhase == 0 && !caster.IsInPhase(this))) + if (!caster || !caster.IsInPhase(this)) RemoveOwnedAura(list[i]); } } @@ -3345,7 +3345,7 @@ namespace Game.Entities for (var i = 0; i < m_scAuras.Count; i++) { var aura = m_scAuras[i]; - if (aura.GetUnitOwner() != this && !aura.GetUnitOwner().IsInPhase(newPhase)) + if (aura.GetUnitOwner() != this && (!onPhaseChange || !aura.GetUnitOwner().IsInPhase(this))) aura.Remove(); } } diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index cb3cdac5b..15ad75e60 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -416,15 +416,6 @@ namespace Game.Entities } } - public override void AddToWorld() - { - if (!IsInWorld) - { - base.AddToWorld(); - } - RebuildTerrainSwaps(); - } - public override void RemoveFromWorld() { // cleanup @@ -935,7 +926,7 @@ namespace Game.Entities MoveSplineInit init = new MoveSplineInit(this); // Creatures without inhabit type air should begin falling after exiting the vehicle - if (IsTypeId(TypeId.Unit) && !ToCreature().CanFly() && height > GetMap().GetWaterOrGroundLevel(GetPhases(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), ref height) + 0.1f) + if (IsTypeId(TypeId.Unit) && !ToCreature().CanFly() && height > GetMap().GetWaterOrGroundLevel(GetPhaseShift(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), ref height) + 0.1f) init.SetFall(); init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), height, false); @@ -1099,14 +1090,12 @@ namespace Game.Entities public HostileRefManager getHostileRefManager() { return m_HostileRefManager; } - public override bool SetInPhase(uint id, bool update, bool apply) + public void OnPhaseChange() { - bool res = base.SetInPhase(id, update, apply); - if (!IsInWorld) - return res; + return; - if (IsTypeId(TypeId.Unit) || (!ToPlayer().IsGameMaster() && !ToPlayer().GetSession().PlayerLogout())) + if (IsTypeId(TypeId.Unit) || !ToPlayer().GetSession().PlayerLogout()) { HostileRefManager refManager = getHostileRefManager(); HostileReference refe = refManager.getFirst(); @@ -1143,26 +1132,8 @@ namespace Game.Entities } } } - - - foreach (var unit in m_Controlled) - if (unit.IsTypeId(TypeId.Unit)) - unit.SetInPhase(id, true, apply); - - for (byte i = 0; i < SharedConst.MaxSummonSlot; ++i) - { - if (!m_SummonSlot[i].IsEmpty()) - { - Creature summon = GetMap().GetCreature(m_SummonSlot[i]); - if (summon != null) - summon.SetInPhase(id, true, apply); - } - } - - RemoveNotOwnSingleTargetAuras(0, true); - - return res; } + public uint GetModelForForm(ShapeShiftForm form) { if (IsTypeId(TypeId.Player)) diff --git a/Source/Game/Garrisons/Garrisons.cs b/Source/Game/Garrisons/Garrisons.cs index 4adf66654..30ec2f7ac 100644 --- a/Source/Game/Garrisons/Garrisons.cs +++ b/Source/Game/Garrisons/Garrisons.cs @@ -232,7 +232,7 @@ namespace Game.Garrisons GarrisonCreateResult garrisonCreateResult = new GarrisonCreateResult(); garrisonCreateResult.GarrSiteLevelID = _siteLevel.Id; _owner.SendPacket(garrisonCreateResult); - _owner.SendUpdatePhasing(); + PhasingHandler.OnConditionChange(_owner); SendRemoteInfo(); return true; } diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index 8b4cfec52..b895a6e1c 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -842,7 +842,7 @@ namespace Game uint MapId = location.GetMapId(); // search for zone associated closest graveyard - uint zoneId = Global.MapMgr.GetZoneId(MapId, x, y, z); + uint zoneId = Global.MapMgr.GetZoneId(conditionObject ? conditionObject.GetPhaseShift() : PhasingHandler.EmptyPhaseShift, MapId, x, y, z); if (zoneId == 0) { if (z > -500) @@ -899,11 +899,17 @@ namespace Game if (data.team != 0 && team != 0 && data.team != (uint)team) continue; - if (conditionObject != null && !Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.Graveyard, data.safeLocId, conditionSource)) - continue; + if (conditionObject) + { + if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.Graveyard, data.safeLocId, conditionSource)) + continue; + + if (entry.MapID == mapEntry.ParentMapID && !conditionObject.GetPhaseShift().HasVisibleMapId(entry.MapID)) + continue; + } // find now nearest graveyard at other map - if (MapId != entry.MapID) + if (MapId != entry.MapID && entry.MapID != mapEntry.ParentMapID) { // if find graveyard at different map from where entrance placed (or no entrance data), use any first if (mapEntry == null @@ -3218,8 +3224,8 @@ namespace Game SQLResult result = DB.World.Query("SELECT creature.guid, id, map, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, " + // 11 12 13 14 15 16 17 18 19 20 21 "currentwaypoint, curhealth, curmana, MovementType, spawnMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.unit_flags2, creature.unit_flags3, " + - // 22 23 24 25 - "creature.dynamicflags, creature.phaseid, creature.phasegroup, creature.ScriptName " + + // 22 23 24 25 26 27 + "creature.dynamicflags, creature.phaseUseFlags, creature.phaseid, creature.phasegroup, creature.terrainSwapMap, creature.ScriptName " + "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid"); if (result.IsEmpty()) @@ -3240,6 +3246,8 @@ namespace Game } } + PhaseShift phaseShift = new PhaseShift(); + uint count = 0; do { @@ -3276,9 +3284,11 @@ namespace Game data.unit_flags2 = result.Read(20); data.unit_flags3 = result.Read(21); data.dynamicflags = result.Read(22); - data.phaseId = result.Read(23); - data.phaseGroup = result.Read(24); - data.ScriptId = GetScriptId(result.Read(25)); + data.phaseUseFlags = (PhaseUseFlagsValues)result.Read(23); + data.phaseId = result.Read(24); + data.phaseGroup = result.Read(25); + data.terrainSwapMap = result.Read(26); + data.ScriptId = GetScriptId(result.Read(27)); if (data.ScriptId == 0) data.ScriptId = cInfo.ScriptID; @@ -3349,6 +3359,19 @@ namespace Game data.orientation = Position.NormalizeOrientation(data.orientation); } + if (Convert.ToBoolean(data.phaseUseFlags & ~PhaseUseFlagsValues.All)) + { + Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id); + data.phaseUseFlags &= PhaseUseFlagsValues.All; + } + + if (data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.AlwaysVisible) && data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.Inverse)) + { + Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE," + + " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id); + data.phaseUseFlags &= ~PhaseUseFlagsValues.Inverse; + } + if (data.phaseGroup != 0 && data.phaseId != 0) { Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.id); @@ -3373,11 +3396,27 @@ namespace Game } } + if (data.terrainSwapMap != -1) + { + MapRecord terrainSwapEntry = CliDB.MapStorage.LookupByKey(data.terrainSwapMap); + if (terrainSwapEntry == null) + { + Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} does not exist, set to -1", guid, data.id, data.terrainSwapMap); + data.terrainSwapMap = -1; + } + else if (terrainSwapEntry.ParentMapID != data.mapid) + { + Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap); + data.terrainSwapMap = -1; + } + } + if (WorldConfig.GetBoolValue(WorldCfg.CalculateCreatureZoneAreaData)) { uint zoneId = 0; uint areaId = 0; - Global.MapMgr.GetZoneAndAreaId(out zoneId, out areaId, data.mapid, data.posX, data.posY, data.posZ); + PhasingHandler.InitDbVisibleMapId(phaseShift, data.terrainSwapMap); + Global.MapMgr.GetZoneAndAreaId(phaseShift, out zoneId, out areaId, data.mapid, data.posX, data.posY, data.posZ); PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_CREATURE_ZONE_AREA_DATA); stmt.AddValue(0, zoneId); @@ -3883,8 +3922,8 @@ namespace Game SQLResult result = DB.World.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, " + // 7 8 9 10 11 12 13 14 15 16 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, eventEntry, pool_entry, " + - // 17 18 19 - "phaseid, phasegroup, ScriptName " + + // 17 18 19 20 21 + "phaseUseFlags, phaseid, phasegroup, terrainSwapMap, ScriptName " + "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid " + "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid"); @@ -3908,6 +3947,8 @@ namespace Game } } + PhaseShift phaseShift = new PhaseShift(); + do { ulong guid = result.Read(0); @@ -3986,8 +4027,22 @@ namespace Game short gameEvent = result.Read(15); uint PoolId = result.Read(16); - data.phaseId = result.Read(17); - data.phaseGroup = result.Read(18); + data.phaseUseFlags = (PhaseUseFlagsValues)result.Read(17); + data.phaseId = result.Read(18); + data.phaseGroup = result.Read(19); + + if (Convert.ToBoolean(data.phaseUseFlags & ~PhaseUseFlagsValues.All)) + { + Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) has unknown `phaseUseFlags` set, removed unknown value.", guid, data.id); + data.phaseUseFlags &= PhaseUseFlagsValues.All; + } + + if (data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.AlwaysVisible) && data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.Inverse)) + { + Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE," + + " removing PHASE_USE_FLAGS_INVERSE.", guid, data.id); + data.phaseUseFlags &= ~PhaseUseFlagsValues.Inverse; + } if (data.phaseGroup != 0 && data.phaseId != 0) { @@ -4013,7 +4068,23 @@ namespace Game } } - data.ScriptId = GetScriptId(result.Read(19)); + data.terrainSwapMap = result.Read(20); + if (data.terrainSwapMap != -1) + { + MapRecord terrainSwapEntry = CliDB.MapStorage.LookupByKey(data.terrainSwapMap); + if (terrainSwapEntry == null) + { + Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} does not exist, set to -1", guid, data.id, data.terrainSwapMap); + data.terrainSwapMap = -1; + } + else if (terrainSwapEntry.ParentMapID != data.mapid) + { + Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `terrainSwapMap` {2} which cannot be used on spawn map, set to -1", guid, data.id, data.terrainSwapMap); + data.terrainSwapMap = -1; + } + } + + data.ScriptId = GetScriptId(result.Read(21)); if (data.ScriptId == 0) data.ScriptId = gInfo.ScriptId; @@ -4057,7 +4128,8 @@ namespace Game { uint zoneId = 0; uint areaId = 0; - Global.MapMgr.GetZoneAndAreaId(out zoneId, out areaId, data.mapid, data.posX, data.posY, data.posZ); + PhasingHandler.InitDbVisibleMapId(phaseShift, data.terrainSwapMap); + Global.MapMgr.GetZoneAndAreaId(phaseShift, out zoneId, out areaId, data.mapid, data.posX, data.posY, data.posZ); PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_GAMEOBJECT_ZONE_AREA_DATA); stmt.AddValue(0, zoneId); @@ -7365,10 +7437,74 @@ namespace Game } //Spells /Skills / Phases - public void LoadTerrainSwapDefaults() + public void LoadPhases() { - _terrainMapDefaultStore.Clear(); + foreach (PhaseRecord phase in CliDB.PhaseStorage.Values) + _phaseInfoById.Add(phase.Id, new PhaseInfoStruct(phase.Id)); + foreach (MapRecord map in CliDB.MapStorage.Values) + if (map.ParentMapID != -1) + _terrainSwapInfoById.Add(map.Id, new TerrainSwapInfo(map.Id)); + + Log.outInfo(LogFilter.ServerLoading, "Loading Terrain World Map definitions..."); + LoadTerrainWorldMaps(); + + Log.outInfo(LogFilter.ServerLoading, "Loading Terrain Swap Default definitions..."); + LoadTerrainSwapDefaults(); + + Log.outInfo(LogFilter.ServerLoading, "Loading Phase Area definitions..."); + LoadAreaPhases(); + } + public void UnloadPhaseConditions() + { + foreach (var pair in _phaseInfoByArea) + pair.Value.Conditions.Clear(); + } + void LoadTerrainWorldMaps() + { + uint oldMSTime = Time.GetMSTime(); + + // 0 1 + SQLResult result = DB.World.Query("SELECT TerrainSwapMap, WorldMapArea FROM `terrain_worldmap`"); + + if (result.IsEmpty()) + { + Log.outInfo(LogFilter.ServerLoading, "Loaded 0 terrain world maps. DB table `terrain_worldmap` is empty."); + return; + } + + uint count = 0; + do + { + uint mapId = result.Read(0); + uint worldMapArea = result.Read(1); + + if (!CliDB.MapStorage.ContainsKey(mapId)) + { + Log.outError(LogFilter.Sql, "TerrainSwapMap {0} defined in `terrain_worldmap` does not exist, skipped.", mapId); + continue; + } + + if (!CliDB.WorldMapAreaStorage.ContainsKey(worldMapArea)) + { + Log.outError(LogFilter.Sql, "WorldMapArea {0} defined in `terrain_worldmap` does not exist, skipped.", worldMapArea); + continue; + } + + if (!_terrainSwapInfoById.ContainsKey(mapId)) + _terrainSwapInfoById.Add(mapId, new TerrainSwapInfo()); + + TerrainSwapInfo terrainSwapInfo = _terrainSwapInfoById[mapId]; + terrainSwapInfo.Id = mapId; + terrainSwapInfo.UiWorldMapAreaIDSwaps.Add(worldMapArea); + + ++count; + } while (result.NextRow()); + + Log.outInfo(LogFilter.ServerLoading, "Loaded {0} terrain world maps in {1} ms.", count, Time.GetMSTimeDiffToNow(oldMSTime)); + } + void LoadTerrainSwapDefaults() + { uint oldMSTime = Time.GetMSTime(); SQLResult result = DB.World.Query("SELECT MapId, TerrainSwapMap FROM `terrain_swap_defaults`"); @@ -7395,87 +7531,20 @@ namespace Game continue; } - _terrainMapDefaultStore.Add(mapId, terrainSwap); + TerrainSwapInfo terrainSwapInfo = _terrainSwapInfoById[terrainSwap]; + terrainSwapInfo.Id = terrainSwap; + _terrainSwapInfoByMap[mapId].Add(terrainSwapInfo); ++count; } while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} terrain swap defaults in {1} ms.", count, Time.GetMSTimeDiffToNow(oldMSTime)); } - public void LoadTerrainPhaseInfo() + void LoadAreaPhases() { - _terrainPhaseInfoStore.Clear(); - uint oldMSTime = Time.GetMSTime(); - SQLResult result = DB.World.Query("SELECT Id, TerrainSwapMap FROM `terrain_phase_info`"); - if (result.IsEmpty()) - { - Log.outInfo(LogFilter.ServerLoading, "Loaded 0 terrain phase infos. DB table `terrain_phase_info` is empty."); - return; - } - - uint count = 0; - do - { - uint phaseId = result.Read(0); - if (!CliDB.PhaseStorage.ContainsKey(phaseId)) - { - Log.outError(LogFilter.Sql, "Phase {0} defined in `terrain_phase_info` does not exist, skipped.", phaseId); - continue; - } - - uint terrainSwap = result.Read(1); - - _terrainPhaseInfoStore.Add(phaseId, terrainSwap); - - ++count; - } - while (result.NextRow()); - Log.outInfo(LogFilter.ServerLoading, "Loaded {0} phase infos in {1} ms.", count, Time.GetMSTimeDiffToNow(oldMSTime)); - } - public void LoadTerrainWorldMaps() - { - _terrainWorldMapStore.Clear(); - - uint oldMSTime = Time.GetMSTime(); - - // 0 1 - SQLResult result = DB.World.Query("SELECT TerrainSwapMap, WorldMapArea FROM `terrain_worldmap`"); - - if (result.IsEmpty()) - { - Log.outInfo(LogFilter.ServerLoading, "Loaded 0 terrain world maps. DB table `terrain_worldmap` is empty."); - return; - } - - uint count = 0; - do - { - uint mapId = result.Read(0); - - if (!CliDB.MapStorage.ContainsKey(mapId)) - { - Log.outError(LogFilter.Sql, "TerrainSwapMap {0} defined in `terrain_worldmap` does not exist, skipped.", mapId); - continue; - } - - uint worldMapArea = result.Read(1); - - _terrainWorldMapStore.Add(mapId, worldMapArea); - - ++count; - } while (result.NextRow()); - - Log.outInfo(LogFilter.ServerLoading, "Loaded {0} terrain world maps in {1} ms.", count, Time.GetMSTimeDiffToNow(oldMSTime)); - } - public void LoadAreaPhases() - { - _phases.Clear(); - - uint oldMSTime = Time.GetMSTime(); - - // 0 1 + // 0 1 SQLResult result = DB.World.Query("SELECT AreaId, PhaseId FROM `phase_area`"); if (result.IsEmpty()) { @@ -7483,19 +7552,60 @@ namespace Game return; } + Func getOrCreatePhaseIfMissing = phaseId => + { + PhaseInfoStruct phaseInfo = _phaseInfoById[phaseId]; + phaseInfo.Id = phaseId; + return phaseInfo; + }; + uint count = 0; do { - PhaseInfoStruct phase = new PhaseInfoStruct(); uint area = result.Read(0); - phase.Id = result.Read(1); + uint phaseId = result.Read(1); - _phases.Add(area, phase); + if (!CliDB.AreaTableStorage.ContainsKey(area)) + { + Log.outError(LogFilter.Sql, $"Area {area} defined in `phase_area` does not exist, skipped."); + continue; + } + + if (!CliDB.PhaseStorage.ContainsKey(phaseId)) + { + Log.outError(LogFilter.Sql, $"Phase {phaseId} defined in `phase_area` does not exist, skipped."); + continue; + } + + PhaseInfoStruct phase = getOrCreatePhaseIfMissing(phaseId); + phase.Areas.Add(area); + _phaseInfoByArea[area].Add(new PhaseAreaInfo(phase)); ++count; } while (result.NextRow()); - Log.outInfo(LogFilter.ServerLoading, "Loaded {0} phase areas in {1} ms.", count, Time.GetMSTimeDiffToNow(oldMSTime)); + foreach (var pair in _phaseInfoByArea) + { + uint parentAreaId = pair.Key; + do + { + AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(parentAreaId); + if (area == null) + break; + + parentAreaId = area.ParentAreaID; + if (parentAreaId == 0) + break; + + var parentAreaPhases = _phaseInfoByArea.LookupByKey(parentAreaId); + foreach (PhaseAreaInfo parentAreaPhase in parentAreaPhases) + if (parentAreaPhase.PhaseInfo.Id == pair.Value.PhaseInfo.Id) + parentAreaPhase.SubAreaExclusions.Add(pair.Key); + + } while (true); + } + + Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} phase areas in {Time.GetMSTimeDiffToNow(oldMSTime)} ms."); } public void LoadNPCSpellClickSpells() { @@ -7623,15 +7733,21 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, "Loaded {0} skill max values in {1} ms", _skillTiers.Count, Time.GetMSTimeDiffToNow(oldMSTime)); } - public List GetPhaseTerrainSwaps(uint phaseid) { return _terrainPhaseInfoStore[phaseid]; } - public List GetDefaultTerrainSwaps(uint mapid) { return _terrainMapDefaultStore[mapid]; } - public List GetTerrainWorldMaps(uint terrainId) { return _terrainWorldMapStore[terrainId]; } - public MultiMap GetDefaultTerrainSwapStore() { return _terrainMapDefaultStore; } - public List GetPhasesForArea(uint area) { return _phases[area]; } - public MultiMap GetAreaAndZonePhases() { return _phases; } - public List GetPhasesForAreaOrZoneForLoading(uint areaOrZone) + public PhaseInfoStruct GetPhaseInfo(uint phaseId) { - return _phases.LookupByKey(areaOrZone); + return _phaseInfoById.LookupByKey(phaseId); + } + public List GetPhasesForArea(uint areaId) + { + return _phaseInfoByArea.LookupByKey(areaId); + } + public TerrainSwapInfo GetTerrainSwapInfo(uint terrainSwapId) + { + return _terrainSwapInfoById.LookupByKey(terrainSwapId); + } + public List GetTerrainSwapsForMap(uint mapId) + { + return _terrainSwapInfoByMap.LookupByKey(mapId); } public List GetSpellClickInfoMapBounds(uint creature_id) { @@ -9481,10 +9597,10 @@ namespace Game List _transportMaps = new List(); //Spells /Skills / Phases - MultiMap _terrainPhaseInfoStore = new MultiMap(); - MultiMap _terrainMapDefaultStore = new MultiMap(); - MultiMap _terrainWorldMapStore = new MultiMap(); - MultiMap _phases = new MultiMap(); + Dictionary _phaseInfoById = new Dictionary(); + Dictionary _terrainSwapInfoById = new Dictionary(); + MultiMap _phaseInfoByArea = new MultiMap(); + MultiMap _terrainSwapInfoByMap = new MultiMap(); MultiMap _spellClickInfoStorage = new MultiMap(); Dictionary _fishingBaseForAreaStorage = new Dictionary(); Dictionary _skillTiers = new Dictionary(); @@ -10296,9 +10412,46 @@ namespace Game public uint[] Value = new uint[SkillConst.MaxSkillStep]; } + public class TerrainSwapInfo + { + public TerrainSwapInfo() { } + public TerrainSwapInfo(uint id) + { + Id = id; + } + + public uint Id; + public List UiWorldMapAreaIDSwaps = new List(); + } + public class PhaseInfoStruct { + public PhaseInfoStruct(uint id) + { + Id = id; + } + + public bool IsAllowedInArea(uint areaId) + { + return Areas.Any(areaToCheck => + { + return Global.DB2Mgr.IsInArea(areaId, areaToCheck); + }); + } + public uint Id; + public List Areas = new List(); + } + + public class PhaseAreaInfo + { + public PhaseAreaInfo(PhaseInfoStruct phaseInfo) + { + PhaseInfo = phaseInfo; + } + + public PhaseInfoStruct PhaseInfo; + public List SubAreaExclusions = new List(); public List Conditions = new List(); } diff --git a/Source/Game/Handlers/MiscHandler.cs b/Source/Game/Handlers/MiscHandler.cs index 6ffabbc4b..6bbafe451 100644 --- a/Source/Game/Handlers/MiscHandler.cs +++ b/Source/Game/Handlers/MiscHandler.cs @@ -713,17 +713,6 @@ namespace Game _player.ApplyModFlag(PlayerFields.Flags, PlayerFlags.TaxiBenchmark, packet.Enable); } - public void SendSetPhaseShift(List phaseIds, List terrainswaps, List worldMapAreaSwaps) - { - PhaseShift phaseShift = new PhaseShift(); - phaseShift.ClientGUID = GetPlayer().GetGUID(); - phaseShift.PersonalGUID = GetPlayer().GetGUID(); - phaseShift.PhaseShifts = phaseIds; - phaseShift.VisibleMapIDs = terrainswaps; - phaseShift.UiWorldMapAreaIDSwaps = worldMapAreaSwaps; - SendPacket(phaseShift); - } - [WorldPacketHandler(ClientOpcodes.GuildSetFocusedAchievement)] void HandleGuildSetFocusedAchievement(GuildSetFocusedAchievement setFocusedAchievement) { diff --git a/Source/Game/Handlers/MovementHandler.cs b/Source/Game/Handlers/MovementHandler.cs index ce1abd852..d99afc5e7 100644 --- a/Source/Game/Handlers/MovementHandler.cs +++ b/Source/Game/Handlers/MovementHandler.cs @@ -132,7 +132,7 @@ namespace Game if (plrMover && movementInfo.HasMovementFlag(MovementFlag.Swimming) != plrMover.IsInWater()) { // now client not include swimming flag in case jumping under water - plrMover.SetInWater(!plrMover.IsInWater() || plrMover.GetMap().IsUnderWater(movementInfo.Pos.posX, movementInfo.Pos.posY, movementInfo.Pos.posZ)); + plrMover.SetInWater(!plrMover.IsInWater() || plrMover.GetMap().IsUnderWater(plrMover.GetPhaseShift(), movementInfo.Pos.posX, movementInfo.Pos.posY, movementInfo.Pos.posZ)); } uint mstime = Time.GetMSTime(); diff --git a/Source/Game/Handlers/QueryHandler.cs b/Source/Game/Handlers/QueryHandler.cs index d06ef6467..9ba5866d3 100644 --- a/Source/Game/Handlers/QueryHandler.cs +++ b/Source/Game/Handlers/QueryHandler.cs @@ -283,7 +283,7 @@ namespace Game mapID = (uint)corpseMapEntry.CorpseMapID; x = corpseMapEntry.Corpse.X; y = corpseMapEntry.Corpse.Y; - z = entranceMap.GetHeight(player.GetPhases(), x, y, MapConst.MaxHeight); + z = entranceMap.GetHeight(player.GetPhaseShift(), x, y, MapConst.MaxHeight); } } } diff --git a/Source/Game/Maps/GridMap.cs b/Source/Game/Maps/GridMap.cs index c45ef4b77..0b3b8d20a 100644 --- a/Source/Game/Maps/GridMap.cs +++ b/Source/Game/Maps/GridMap.cs @@ -43,6 +43,7 @@ namespace Game.Maps if (!File.Exists(filename)) return true; + _fileExists = true; using (BinaryReader reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) { mapFileHeader header = reader.ReadStruct(); @@ -72,6 +73,7 @@ namespace Game.Maps _liquidFlags = null; _liquidMap = null; _gridGetHeight = getHeightFromFlat; + _fileExists = false; } void LoadAreaData(BinaryReader reader, uint offset) @@ -435,8 +437,8 @@ namespace Game.Maps }; Cell cell = new Cell(x, y); - float gx = x - (cell.GetGridX() - MapConst.CenterGridId + 1) *MapConst.SizeofGrids; - float gy = y - (cell.GetGridY() - MapConst.CenterGridId + 1) *MapConst.SizeofGrids; + float gx = x - (cell.GetGridX() - MapConst.CenterGridId + 1) * MapConst.SizeofGrids; + float gy = y - (cell.GetGridY() - MapConst.CenterGridId + 1) * MapConst.SizeofGrids; uint quarterIndex = 0; if (cell.GetCellY() < MapConst.MaxCells / 2) @@ -595,6 +597,8 @@ namespace Game.Maps public float getHeight(float x, float y) { return _gridGetHeight(x, y); } + public bool fileExists() { return _fileExists; } + #region Fields delegate float GetHeight(float x, float y); @@ -627,6 +631,7 @@ namespace Game.Maps byte _liquidOffY; byte _liquidWidth; byte _liquidHeight; + bool _fileExists; #endregion } diff --git a/Source/Game/Maps/Instances/InstanceScript.cs b/Source/Game/Maps/Instances/InstanceScript.cs index ccdb8c1d4..769b7ebca 100644 --- a/Source/Game/Maps/Instances/InstanceScript.cs +++ b/Source/Game/Maps/Instances/InstanceScript.cs @@ -727,7 +727,7 @@ namespace Game.Maps { var players = instance.GetPlayers(); foreach (var player in players) - player.SendUpdatePhasing(); + PhasingHandler.SendToPlayer(player); } public void UpdateCombatResurrection(uint diff) diff --git a/Source/Game/Maps/MMapManager.cs b/Source/Game/Maps/MMapManager.cs index b1eb9d6bb..4f82d87f6 100644 --- a/Source/Game/Maps/MMapManager.cs +++ b/Source/Game/Maps/MMapManager.cs @@ -34,19 +34,27 @@ namespace Game const string MAP_FILE_NAME_FORMAT = "{0}/mmaps/{1:D4}.mmap"; const string TILE_FILE_NAME_FORMAT = "{0}/mmaps/{1:D4}{2:D2}{3:D2}.mmtile"; + public void Initialize(MultiMap mapData) + { + childMapData = mapData; + + foreach (var pair in mapData) + parentMapData[pair.Value] = pair.Key; + } + MMapData GetMMapData(uint mapId) { return loadedMMaps.LookupByKey(mapId); } - bool loadMapData(uint mapId) + bool loadMapData(string basePath, uint mapId) { // we already have this map loaded? if (loadedMMaps.ContainsKey(mapId) && loadedMMaps[mapId] != null) return true; // load and init dtNavMesh - read parameters from file - string filename = string.Format(MAP_FILE_NAME_FORMAT, Global.WorldMgr.GetDataPath(), mapId); + string filename = string.Format(MAP_FILE_NAME_FORMAT, basePath, mapId); if (!File.Exists(filename)) { Log.outError(LogFilter.Maps, "Could not open mmap file {0}", filename); @@ -80,15 +88,30 @@ namespace Game } } - uint packTileID(int x, int y) + uint packTileID(uint x, uint y) { - return (uint)(x << 16 | y); + return (x << 16 | y); } - public bool loadMap(uint mapId, int x, int y) + public bool loadMap(string basePath, uint mapId, uint x, uint y) { // make sure the mmap is loaded and ready to load tiles - if (!loadMapData(mapId)) + if (!loadMapImpl(basePath, mapId, x, y)) + return false; + + bool success = true; + var childMaps = childMapData.LookupByKey(mapId); + foreach (uint childMapId in childMaps) + if (!loadMapImpl(basePath, childMapId, x, y)) + success = false; + + return success; + } + + bool loadMapImpl(string basePath, uint mapId, uint x, uint y) + { + // make sure the mmap is loaded and ready to load tiles + if (!loadMapData(basePath, mapId)) return false; // get this mmap data @@ -101,14 +124,20 @@ namespace Game return false; // load this tile . mmaps/MMMXXYY.mmtile - string filename = string.Format(TILE_FILE_NAME_FORMAT, Global.WorldMgr.GetDataPath(), mapId, x, y); - if (!File.Exists(filename)) + string fileName = string.Format(TILE_FILE_NAME_FORMAT, basePath, mapId, x, y); + if (!File.Exists(fileName)) { - Log.outDebug(LogFilter.Maps, "MMAP:loadMap: Could not open mmtile file '{0}'", filename); + if (parentMapData.ContainsKey(mapId)) + fileName = string.Format(TILE_FILE_NAME_FORMAT, basePath, parentMapData[mapId], x, y); + } + + if (!File.Exists(fileName)) + { + Log.outDebug(LogFilter.Maps, "MMAP:loadMap: Could not open mmtile file '{0}'", fileName); return false; } - using (BinaryReader reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) + using (BinaryReader reader = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read))) { MmapTileHeader fileHeader = reader.ReadStruct(); Array.Reverse(fileHeader.mmapMagic); @@ -144,7 +173,52 @@ namespace Game } } + public bool loadMapInstance(string basePath, uint mapId, uint instanceId) + { + if (!loadMapInstanceImpl(basePath, mapId, instanceId)) + return false; + + bool success = true; + var childMaps = childMapData.LookupByKey(mapId); + foreach (uint childMapId in childMaps) + if (!loadMapInstanceImpl(basePath, childMapId, instanceId)) + success = false; + + return success; + } + + bool loadMapInstanceImpl(string basePath, uint mapId, uint instanceId) + { + if (!loadMapData(basePath, mapId)) + return false; + + MMapData mmap = loadedMMaps[mapId]; + if (mmap.navMeshQueries.ContainsKey(instanceId)) + return true; + + // allocate mesh query + Detour.dtNavMeshQuery query = new Detour.dtNavMeshQuery(); + if (Detour.dtStatusFailed(query.init(mmap.navMesh, 1024))) + { + Log.outError(LogFilter.Maps, "MMAP.GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {0:D4} instanceId {1}", mapId, instanceId); + return false; + } + + Log.outDebug(LogFilter.Maps, "MMAP.GetNavMeshQuery: created dtNavMeshQuery for mapId {0:D4} instanceId {1}", mapId, instanceId); + mmap.navMeshQueries.Add(instanceId, query); + return true; + } + public bool unloadMap(uint mapId, uint x, uint y) + { + var childMaps = childMapData.LookupByKey(mapId); + foreach (uint childMapId in childMaps) + unloadMapImpl(childMapId, x, y); + + return unloadMapImpl(mapId, x, y); + } + + bool unloadMapImpl(uint mapId, uint x, uint y) { // check if we have this map loaded MMapData mmap = GetMMapData(mapId); @@ -156,7 +230,7 @@ namespace Game } // check if we have this tile loaded - uint packedGridPos = packTileID((int)x, (int)y); + uint packedGridPos = packTileID(x, y); if (!mmap.loadedTileRefs.ContainsKey(packedGridPos)) { // file may not exist, therefore not loaded @@ -164,7 +238,7 @@ namespace Game return false; } - ulong tileRef = mmap.loadedTileRefs.LookupByKey(packedGridPos); + ulong tileRef = mmap.loadedTileRefs[packedGridPos]; // unload, and mark as non loaded Detour.dtRawTileData data; @@ -256,20 +330,6 @@ namespace Game if (mmap == null) return null; - if (!mmap.navMeshQueries.ContainsKey(instanceId)) - { - // allocate mesh query - Detour.dtNavMeshQuery query = new Detour.dtNavMeshQuery(); - if (Detour.dtStatusFailed(query.init(mmap.navMesh, 1024))) - { - Log.outError(LogFilter.Maps, "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {0} instanceId {1}", mapId, instanceId); - return null; - } - - Log.outInfo(LogFilter.Maps, "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId {0} instanceId {1}", mapId, instanceId); - mmap.navMeshQueries.Add(instanceId, query); - } - return mmap.navMeshQueries[instanceId]; } @@ -278,6 +338,9 @@ namespace Game Dictionary loadedMMaps = new Dictionary(); uint loadedTiles; + + MultiMap childMapData = new MultiMap(); + Dictionary parentMapData = new Dictionary(); } public class MMapData diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 1c17d0ab1..1c347d736 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -48,7 +48,16 @@ namespace Game.Maps i_gridExpiry = expiry; _defaultLight = Global.DB2Mgr.GetDefaultMapLight(id); - m_parentMap = parent ?? this; + if (parent) + { + m_parentMap = parent; + m_childTerrainMaps = m_parentMap.m_childTerrainMaps; + } + else + { + m_parentMap = this; + m_childTerrainMaps = new List(); + } for (uint x = 0; x < MapConst.MaxGrids; ++x) { @@ -69,6 +78,8 @@ namespace Game.Maps GetGuidSequenceGenerator(HighGuid.Transport).Set(Global.ObjectMgr.GetGenerator(HighGuid.Transport).GetNextAfterMaxUsed()); + Global.MMapMgr.loadMapInstance(Global.WorldMgr.GetDataPath(), GetId(), i_InstanceId); + Global.ScriptMgr.OnCreateMap(this); } @@ -112,7 +123,10 @@ namespace Game.Maps void LoadMMap(uint gx, uint gy) { - if (Global.MMapMgr.loadMap(GetId(), (int)gx, (int)gy)) + if (!Global.DisableMgr.IsPathfindingEnabled(GetId())) + return; + + if (Global.MMapMgr.loadMap(Global.WorldMgr.GetDataPath(), GetId(), gx, gy)) Log.outInfo(LogFilter.Maps, "MMAP loaded name:{0}, id:{1}, x:{2}, y:{3} (mmap rep.: x:{4}, y:{5})", GetMapName(), GetId(), gx, gy, gx, gy); else Log.outInfo(LogFilter.Maps, "Could not load MMAP name:{0}, id:{1}, x:{2}, y:{3} (mmap rep.: x:{4}, y:{5})", GetMapName(), GetId(), gx, gy, gx, gy); @@ -138,39 +152,62 @@ namespace Game.Maps void LoadMap(uint gx, uint gy, bool reload = false) { - if (i_InstanceId != 0) + LoadMapImpl(this, gx, gy, reload); + foreach (Map childBaseMap in m_childTerrainMaps) + LoadMapImpl(childBaseMap, gx, gy, reload); + } + + void LoadMapImpl(Map map, uint gx, uint gy, bool reload) + { + if (map.i_InstanceId != 0) { - if (GridMaps[gx][gy] != null) + if (map.GridMaps[gx][gy] != null) return; // load grid map for base map - if (m_parentMap.GridMaps[gx][gy] == null) - m_parentMap.EnsureGridCreated(new GridCoord(63 - gx, 63 - gy)); + if (map.m_parentMap.GridMaps[gx][gy] == null) + map.m_parentMap.EnsureGridCreated(new GridCoord(63 - gx, 63 - gy)); - ((MapInstanced)m_parentMap).AddGridMapReference(new GridCoord(gx, gy)); - GridMaps[gx][gy] = m_parentMap.GridMaps[gx][gy]; + map.m_parentMap.ToMapInstanced().AddGridMapReference(new GridCoord(gx, gy)); + map.GridMaps[gx][gy] = map.m_parentMap.GridMaps[gx][gy]; return; } - if (GridMaps[gx][gy] != null && !reload) + if (map.GridMaps[gx][gy] != null && !reload) return; - if (GridMaps[gx][gy] != null) + if (map.GridMaps[gx][gy] != null) { - Log.outInfo(LogFilter.Maps, "Unloading previously loaded map {0} before reloading.", GetId()); - Global.ScriptMgr.OnUnloadGridMap(this, GridMaps[gx][gy], gx, gy); + Log.outInfo(LogFilter.Maps, "Unloading previously loaded map {0} before reloading.", map.GetId()); + Global.ScriptMgr.OnUnloadGridMap(map, map.GridMaps[gx][gy], gx, gy); - GridMaps[gx][gy] = null; + map.GridMaps[gx][gy] = null; } // map file name - string filename = string.Format("{0}/maps/{1:D4}_{2:D2}_{3:D2}.map", Global.WorldMgr.GetDataPath(), GetId(), gx, gy); + string filename = string.Format("{0}/maps/{1:D4}_{2:D2}_{3:D2}.map", Global.WorldMgr.GetDataPath(), map.GetId(), gx, gy); Log.outInfo(LogFilter.Maps, "Loading map {0}", filename); // loading data - GridMaps[gx][gy] = new GridMap(); - if (!GridMaps[gx][gy].loadData(filename)) + map.GridMaps[gx][gy] = new GridMap(); + if (!map.GridMaps[gx][gy].loadData(filename)) Log.outError(LogFilter.Maps, "Error loading map file: {0}", filename); - Global.ScriptMgr.OnLoadGridMap(this, GridMaps[gx][gy], gx, gy); + Global.ScriptMgr.OnLoadGridMap(map, map.GridMaps[gx][gy], gx, gy); + } + + void UnloadMap(Map map, uint gx, uint gy) + { + if (map.i_InstanceId == 0) + { + if (map.GridMaps[gx][gy] != null) + { + map.GridMaps[gx][gy].unloadData(); + map.GridMaps[gx][gy] = null; + } + } + else + map.m_parentMap.ToMapInstanced().RemoveGridMapReference(new GridCoord(gx, gy)); + + map.GridMaps[gx][gy] = null; } void LoadMapAndVMap(uint gx, uint gy) @@ -415,7 +452,7 @@ namespace Game.Maps player.m_clientGUIDs.Clear(); player.UpdateObjectVisibility(false); - player.SendUpdatePhasing(); + PhasingHandler.SendToPlayer(player); if (player.IsAlive()) ConvertCorpseToBones(player.GetGUID()); @@ -466,8 +503,6 @@ namespace Game.Maps if (obj.isActiveObject()) AddToActive(obj); - obj.RebuildTerrainSwaps(); - //something, such as vehicle, needs to be update immediately //also, trigger needs to cast spell, if not update, cannot see visual obj.UpdateObjectVisibilityOnCreate(); @@ -499,6 +534,7 @@ namespace Game.Maps { var data = new UpdateData(GetId()); obj.BuildCreateUpdateBlockForPlayer(data, player); + player.m_visibleTransports.Add(obj.GetGUID()); UpdateObject packet; data.BuildPacket(out packet); player.SendPacket(packet); @@ -804,9 +840,14 @@ namespace Game.Maps UpdateObject packet; data.BuildPacket(out packet); - foreach (var pl in players) - if (pl.GetTransport() != obj) - pl.SendPacket(packet); + foreach (var player in players) + { + if (player.GetTransport() != obj) + { + player.SendPacket(packet); + player.m_visibleTransports.Remove(obj.GetGUID()); + } + } } if (!_transports.Contains(obj)) @@ -1541,20 +1582,19 @@ namespace Game.Maps uint gx = (MapConst.MaxGrids - 1) - x; uint gy = (MapConst.MaxGrids - 1) - y; + // delete grid map, but don't delete if it is from parent map (and thus only reference) + //+++if (GridMaps[gx][gy]) don't check for GridMaps[gx][gy], we might have to unload vmaps { + foreach (Map childBaseMap in m_childTerrainMaps) + UnloadMap(childBaseMap, gx, gy); + + UnloadMap(this, gx, gy); + if (i_InstanceId == 0) { - if (GridMaps[gx][gy] != null) - { - GridMaps[gx][gy].unloadData(); - } Global.VMapMgr.unloadMap(GetId(), gx, gy); Global.MMapMgr.unloadMap(GetId(), gx, gy); } - else - ((MapInstanced)m_parentMap).RemoveGridMapReference(new GridCoord(gx, gy)); - - GridMaps[gx][gy] = null; } Log.outDebug(LogFilter.Maps, "Unloading grid[{0}, {1}] for map {2} finished", x, y, GetId()); return true; @@ -1624,49 +1664,79 @@ namespace Game.Maps if (!m_scriptSchedule.Empty()) Global.MapMgr.DecreaseScheduledScriptCount((uint)m_scriptSchedule.Count); + if (m_parentMap == this) + m_childTerrainMaps = null; + Global.MMapMgr.unloadMapInstance(GetId(), i_InstanceId); } private GridMap GetGridMap(float x, float y) { // half opt method - var gx = (uint)(32 - x / MapConst.SizeofGrids); //grid x - var gy = (uint)(32 - y / MapConst.SizeofGrids); //grid y + var gx = (uint)(MapConst.CenterGridId - x / MapConst.SizeofGrids); //grid x + var gy = (uint)(MapConst.CenterGridId - y / MapConst.SizeofGrids); //grid y // ensure GridMap is loaded - EnsureGridCreated(new GridCoord(63 - gx, 63 - gy)); + EnsureGridCreated(new GridCoord((MapConst.MaxGrids - 1) - gx, (MapConst.MaxGrids - 1) - gy)); return GridMaps[gx][gy]; } - public float GetWaterOrGroundLevel(List phases, float x, float y, float z) + GridMap GetGridMap(uint mapId, float x, float y) { - float ground = 0f; - return GetWaterOrGroundLevel(phases, x, y, z, ref ground); + if (GetId() == mapId) + return GetGridMap(x, y); + + // half opt method + uint gx = (uint)(MapConst.CenterGridId - x / MapConst.SizeofGrids); //grid x + uint gy = (uint)(MapConst.CenterGridId - y / MapConst.SizeofGrids); //grid y + + // ensure GridMap is loaded + EnsureGridCreated(new GridCoord((MapConst.MaxGrids - 1) - gx, (MapConst.MaxGrids - 1) - gy)); + + GridMap grid = GridMaps[gx][gy]; + var childMap = m_childTerrainMaps.Find(childTerrainMap => childTerrainMap.GetId() == mapId); + if (childMap != null && childMap.GridMaps[gx][gy].fileExists()) + grid = childMap.GridMaps[gx][gy]; + + return grid; } - public float GetWaterOrGroundLevel(List phases, float x, float y, float z, ref float ground, bool swim = false) + public bool HasGridMap(uint mapId, uint gx, uint gy) + { + var childMap = m_childTerrainMaps.Find(childTerrainMap => childTerrainMap.GetId() == mapId); + return childMap != null && childMap.GridMaps[gx][gy] != null && childMap.GridMaps[gx][gy].fileExists(); + } + + public float GetWaterOrGroundLevel(PhaseShift phaseShift, float x, float y, float z) + { + float ground = 0f; + return GetWaterOrGroundLevel(phaseShift, x, y, z, ref ground); + } + + public float GetWaterOrGroundLevel(PhaseShift phaseShift, float x, float y, float z, ref float ground, bool swim = false) { if (GetGridMap(x, y) != null) { // we need ground level (including grid height version) for proper return water level in point - float ground_z = GetHeight(phases, x, y, z, true, 50.0f); + float ground_z = GetHeight(phaseShift, x, y, z, true, 50.0f); ground = ground_z; LiquidData liquid_status; - ZLiquidStatus res = getLiquidStatus(x, y, ground_z, MapConst.MapAllLiquidTypes, out liquid_status); + ZLiquidStatus res = getLiquidStatus(phaseShift, x, y, ground_z, MapConst.MapAllLiquidTypes, out liquid_status); return res != ZLiquidStatus.NoWater ? liquid_status.level : ground_z; } return MapConst.VMAPInvalidHeightValue; } - public float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = MapConst.DefaultHeightSearch) + public float GetStaticHeight(PhaseShift phaseShift, float x, float y, float z, bool checkVMap = true, float maxSearchDist = MapConst.DefaultHeightSearch) { // find raw .map surface under Z coordinates float mapHeight = MapConst.VMAPInvalidHeightValue; - GridMap gmap = GetGridMap(x, y); + uint terrainMapId = PhasingHandler.GetTerrainMapId(phaseShift, this, x, y); + GridMap gmap = GetGridMap(terrainMapId, x, y); if (gmap != null) { float gridHeight = gmap.getHeight(x, y); @@ -1679,7 +1749,7 @@ namespace Game.Maps if (checkVMap) { if (Global.VMapMgr.isHeightCalcEnabled()) - vmapHeight = Global.VMapMgr.getHeight(GetId(), x, y, z + 2.0f, maxSearchDist); + vmapHeight = Global.VMapMgr.getHeight(terrainMapId, x, y, z + 2.0f, maxSearchDist); // look from a bit higher pos to find the floor } @@ -1736,13 +1806,13 @@ namespace Game.Maps return outdoor; } - public bool IsOutdoors(float x, float y, float z) + public bool IsOutdoors(PhaseShift phaseShift, float x, float y, float z) { uint mogpFlags; int adtId, rootId, groupId; // no wmo found? . outside by default - if (!GetAreaInfo(x, y, z, out mogpFlags, out adtId, out rootId, out groupId)) + if (!GetAreaInfo(phaseShift, x, y, z, out mogpFlags, out adtId, out rootId, out groupId)) return true; AreaTableRecord atEntry = null; @@ -1756,7 +1826,7 @@ namespace Game.Maps return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); } - private bool GetAreaInfo(float x, float y, float z, out uint flags, out int adtId, out int rootId, out int groupId) + private bool GetAreaInfo(PhaseShift phaseShift, float x, float y, float z, out uint flags, out int adtId, out int rootId, out int groupId) { flags = 0; adtId = 0; @@ -1764,10 +1834,11 @@ namespace Game.Maps groupId = 0; float vmap_z = z; - if (Global.VMapMgr.getAreaInfo(GetId(), x, y, ref vmap_z, out flags, out adtId, out rootId, out groupId)) + uint terrainMapId = PhasingHandler.GetTerrainMapId(phaseShift, this, x, y); + if (Global.VMapMgr.getAreaInfo(terrainMapId, x, y, ref vmap_z, out flags, out adtId, out rootId, out groupId)) { // check if there's terrain between player height and object height - GridMap gmap = GetGridMap(x, y); + GridMap gmap = GetGridMap(terrainMapId, x, y); if (gmap != null) { float _mapheight = gmap.getHeight(x, y); @@ -1781,13 +1852,13 @@ namespace Game.Maps return false; } - public uint GetAreaId(float x, float y, float z) + public uint GetAreaId(PhaseShift phaseShift, float x, float y, float z) { bool throwaway; - return GetAreaId(x, y, z, out throwaway); + return GetAreaId(phaseShift, x, y, z, out throwaway); } - public uint GetAreaId(float x, float y, float z, out bool isOutdoors) + public uint GetAreaId(PhaseShift phaseShift, float x, float y, float z, out bool isOutdoors) { uint mogpFlags; int adtId, rootId, groupId; @@ -1796,7 +1867,7 @@ namespace Game.Maps bool haveAreaInfo = false; uint areaId = 0; - if (GetAreaInfo(x, y, z, out mogpFlags, out adtId, out rootId, out groupId)) + if (GetAreaInfo(phaseShift, x, y, z, out mogpFlags, out adtId, out rootId, out groupId)) { haveAreaInfo = true; wmoEntry = Global.DB2Mgr.GetWMOAreaTable(rootId, adtId, groupId); @@ -1809,7 +1880,7 @@ namespace Game.Maps if (areaId == 0) { - GridMap gmap = GetGridMap(x, y); + GridMap gmap = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y); if (gmap != null) areaId = gmap.getArea(x, y); @@ -1826,9 +1897,9 @@ namespace Game.Maps return areaId; } - public uint GetZoneId(float x, float y, float z) + public uint GetZoneId(PhaseShift phaseShift, float x, float y, float z) { - uint areaId = GetAreaId(x, y, z); + uint areaId = GetAreaId(phaseShift, x, y, z); AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(areaId); if (area != null) if (area.ParentAreaID != 0) @@ -1837,38 +1908,38 @@ namespace Game.Maps return areaId; } - public void GetZoneAndAreaId(out uint zoneid, out uint areaid, float x, float y, float z) + public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, float x, float y, float z) { - areaid = zoneid = GetAreaId(x, y, z); + areaid = zoneid = GetAreaId(phaseShift, x, y, z); AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(areaid); if (area != null) if (area.ParentAreaID != 0) zoneid = area.ParentAreaID; } - private byte GetTerrainType(float x, float y) + private byte GetTerrainType(PhaseShift phaseShift, float x, float y) { - GridMap gmap = GetGridMap(x, y); + GridMap gmap = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y); if (gmap != null) return gmap.getTerrainType(x, y); return 0; } - public ZLiquidStatus getLiquidStatus(float x, float y, float z, byte ReqLiquidType) + public ZLiquidStatus getLiquidStatus(PhaseShift phaseShift, float x, float y, float z, byte ReqLiquidType) { LiquidData throwaway; - return getLiquidStatus(x, y, z, ReqLiquidType, out throwaway); + return getLiquidStatus(phaseShift, x, y, z, ReqLiquidType, out throwaway); } - public ZLiquidStatus getLiquidStatus(float x, float y, float z, byte ReqLiquidType, out LiquidData data) + public ZLiquidStatus getLiquidStatus(PhaseShift phaseShift, float x, float y, float z, byte ReqLiquidType, out LiquidData data) { data = new LiquidData(); var result = ZLiquidStatus.NoWater; float liquid_level = MapConst.InvalidHeight; float ground_level = MapConst.InvalidHeight; uint liquid_type = 0; - - if (Global.VMapMgr.GetLiquidLevel(GetId(), x, y, z, ReqLiquidType, ref liquid_level, ref ground_level, ref liquid_type)) + uint terrainMapId = PhasingHandler.GetTerrainMapId(phaseShift, this, x, y); + if (Global.VMapMgr.GetLiquidLevel(terrainMapId, x, y, z, ReqLiquidType, ref liquid_level, ref ground_level, ref liquid_type)) { Log.outDebug(LogFilter.Maps, "getLiquidStatus(): vmap liquid level: {0} ground: {1} type: {2}", liquid_level, ground_level, liquid_type); @@ -1887,7 +1958,7 @@ namespace Game.Maps if (liquid_type != 0 && liquid_type < 21) { - AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(GetAreaId(x, y, z)); + AreaTableRecord area = CliDB.AreaTableStorage.LookupByKey(GetAreaId(phaseShift, x, y, z)); if (area != null) { uint overrideLiquid = area.LiquidTypeID[liquidFlagType]; @@ -1926,7 +1997,7 @@ namespace Game.Maps } } - GridMap gmap = GetGridMap(x, y); + GridMap gmap = GetGridMap(terrainMapId, x, y); if (gmap != null) { var map_data = new LiquidData(); @@ -1946,27 +2017,27 @@ namespace Game.Maps return result; } - public float GetWaterLevel(float x, float y) + public float GetWaterLevel(PhaseShift phaseShift, float x, float y) { - GridMap gmap = GetGridMap(x, y); + GridMap gmap = GetGridMap(PhasingHandler.GetTerrainMapId(phaseShift, this, x, y), x, y); if (gmap != null) return gmap.getLiquidLevel(x, y); return 0; } - public bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, List phases) + public bool isInLineOfSight(PhaseShift phaseShift, float x1, float y1, float z1, float x2, float y2, float z2) { - return Global.VMapMgr.isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2) - && _dynamicTree.isInLineOfSight(new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), phases); + return Global.VMapMgr.isInLineOfSight(PhasingHandler.GetTerrainMapId(phaseShift, this, x1, y1), x1, y1, z1, x2, y2, z2) + && _dynamicTree.isInLineOfSight(new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), phaseShift); } - public bool getObjectHitPos(List phases, float x1, float y1, float z1, float x2, float y2, float z2, out float rx, out float ry, out float rz, float modifyDist) + public bool getObjectHitPos(PhaseShift phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, out float rx, out float ry, out float rz, float modifyDist) { var startPos = new Vector3(x1, y1, z1); var dstPos = new Vector3(x2, y2, z2); var resultPos = new Vector3(); - bool result = _dynamicTree.getObjectHitPos(phases, startPos, dstPos, ref resultPos, modifyDist); + bool result = _dynamicTree.getObjectHitPos(startPos, dstPos, ref resultPos, modifyDist, phaseShift); rx = resultPos.X; ry = resultPos.Y; @@ -1974,22 +2045,19 @@ namespace Game.Maps return result; } - public float GetHeight(List phases, float x, float y, float z, bool vmap = true, float maxSearchDist = MapConst.DefaultHeightSearch) + public float GetHeight(PhaseShift phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = MapConst.DefaultHeightSearch) { - return Math.Max(GetHeight(x, y, z, vmap, maxSearchDist), _dynamicTree.getHeight(x, y, z, maxSearchDist, phases)); + return Math.Max(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), _dynamicTree.getHeight(x, y, z, maxSearchDist, phaseShift)); } - public bool IsInWater(float x, float y, float pZ) + public bool IsInWater(PhaseShift phaseShift, float x, float y, float pZ) { - return Convert.ToBoolean(getLiquidStatus(x, y, pZ, MapConst.MapAllLiquidTypes) & - (ZLiquidStatus.InWater | ZLiquidStatus.UnderWater)); + return Convert.ToBoolean(getLiquidStatus(phaseShift, x, y, pZ, MapConst.MapAllLiquidTypes) & (ZLiquidStatus.InWater | ZLiquidStatus.UnderWater)); } - public bool IsUnderWater(float x, float y, float z) + public bool IsUnderWater(PhaseShift phaseShift, float x, float y, float z) { - return - Convert.ToBoolean(getLiquidStatus(x, y, z, MapConst.MapLiquidTypeWater | MapConst.MapLiquidTypeOcean) & - ZLiquidStatus.UnderWater); + return Convert.ToBoolean(getLiquidStatus(phaseShift, x, y, z, MapConst.MapLiquidTypeWater | MapConst.MapLiquidTypeOcean) & ZLiquidStatus.UnderWater); } private bool CheckGridIntegrity(Creature c, bool moved) @@ -2007,30 +2075,32 @@ namespace Game.Maps return i_mapRecord != null ? i_mapRecord.MapName[Global.WorldMgr.GetDefaultDbcLocale()] : "UNNAMEDMAP"; } - public void SendInitSelf(Player pl) + public void SendInitSelf(Player player) { - var data = new UpdateData(pl.GetMapId()); + var data = new UpdateData(player.GetMapId()); // attach to player data current transport data - Transport transport = pl.GetTransport(); - if (transport != null) - transport.BuildCreateUpdateBlockForPlayer(data, pl); - - pl.BuildCreateUpdateBlockForPlayer(data, pl); - - // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map - + Transport transport = player.GetTransport(); if (transport != null) { - foreach (WorldObject obj in transport.GetPassengers()) + transport.BuildCreateUpdateBlockForPlayer(data, player); + player.m_visibleTransports.Add(transport.GetGUID()); + } + + player.BuildCreateUpdateBlockForPlayer(data, player); + + // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map + if (transport != null) + { + foreach (WorldObject passenger in transport.GetPassengers()) { - if (pl != obj && pl.HaveAtClient(obj)) - obj.BuildCreateUpdateBlockForPlayer(data, pl); + if (player != passenger && player.HaveAtClient(passenger)) + passenger.BuildCreateUpdateBlockForPlayer(data, player); } } UpdateObject packet; data.BuildPacket(out packet); - pl.SendPacket(packet); + player.SendPacket(packet); } void SendInitTransports(Player player) @@ -2040,7 +2110,10 @@ namespace Game.Maps foreach (Transport transport in _transports) { if (transport != player.GetTransport() && player.IsInPhase(transport)) + { transport.BuildCreateUpdateBlockForPlayer(transData, player); + player.m_visibleTransports.Add(transport.GetGUID()); + } } UpdateObject packet; @@ -2052,27 +2125,39 @@ namespace Game.Maps { var transData = new UpdateData(player.GetMapId()); foreach (Transport transport in _transports) + { if (transport != player.GetTransport()) + { transport.BuildOutOfRangeUpdateBlock(transData); + player.m_visibleTransports.Remove(transport.GetGUID()); + } + } UpdateObject packet; transData.BuildPacket(out packet); player.SendPacket(packet); } - public void SendUpdateTransportVisibility(Player player, List previousPhases) + public void SendUpdateTransportVisibility(Player player) { // Hack to send out transports UpdateData transData = new UpdateData(player.GetMapId()); - foreach (var trans in _transports) + foreach (var transport in _transports) { - if (trans == player.GetTransport()) - continue; - - if (player.IsInPhase(trans) && !previousPhases.Intersect(trans.GetPhases()).Any()) - trans.BuildCreateUpdateBlockForPlayer(transData, player); - else if (!player.IsInPhase(trans)) - trans.BuildOutOfRangeUpdateBlock(transData); + var hasTransport = player.m_visibleTransports.Contains(transport.GetGUID()); + if (player.IsInPhase(transport)) + { + if (!hasTransport) + { + transport.BuildCreateUpdateBlockForPlayer(transData, player); + player.m_visibleTransports.Add(transport.GetGUID()); + } + } + else + { + transport.BuildOutOfRangeUpdateBlock(transData); + player.m_visibleTransports.Remove(transport.GetGUID()); + } } UpdateObject packet; @@ -2525,7 +2610,7 @@ namespace Game.Maps continue; foreach (var phaseId in phases[guid]) - corpse.SetInPhase(phaseId, false, true); + PhasingHandler.AddPhase(corpse, phaseId, false); AddCorpse(corpse); } while (result.NextRow()); @@ -2604,7 +2689,7 @@ namespace Game.Maps bones.SetUInt32Value(CorpseFields.Flags, corpse.GetUInt32Value(CorpseFields.Flags) | (uint)CorpseFlags.Bones); - bones.CopyPhaseFrom(corpse); + PhasingHandler.InheritPhaseShift(bones, corpse); AddCorpse(bones); @@ -2847,6 +2932,10 @@ namespace Game.Maps return m_parentMap; } + public void AddChildTerrainMap(Map map) { m_childTerrainMaps.Add(map); } + + public void UnlinkAllChildTerrainMaps() { m_childTerrainMaps.Clear(); } + public uint GetInstanceId() { return i_InstanceId; @@ -3225,7 +3314,8 @@ namespace Game.Maps return null; // Set the summon to the summoner's phase - summon.CopyPhaseFrom(summoner); + if (summoner) + PhasingHandler.InheritPhaseShift(summon, summoner); summon.SetUInt32Value(UnitFields.CreatedBySpell, spellId); @@ -4219,6 +4309,7 @@ namespace Game.Maps protected List m_activeNonPlayers = new List(); protected List m_activePlayers = new List(); Map m_parentMap; + List m_childTerrainMaps = new List(); SortedMultiMap m_scriptSchedule = new SortedMultiMap(); BitArray marked_cells = new BitArray(MapConst.TotalCellsPerMap * MapConst.TotalCellsPerMap); diff --git a/Source/Game/Maps/MapManager.cs b/Source/Game/Maps/MapManager.cs index 4edcb3b32..c964e7198 100644 --- a/Source/Game/Maps/MapManager.cs +++ b/Source/Game/Maps/MapManager.cs @@ -37,10 +37,15 @@ namespace Game.Entities public void Initialize() { //todo needs alot of support for threadsafe. - //int num_threads= WorldConfig.GetIntValue(WorldCfg.Numthreads); + int num_threads = WorldConfig.GetIntValue(WorldCfg.Numthreads); // Start mtmaps if needed. - //if (num_threads > 0) - //m_updater = new MapUpdater(WorldConfig.GetIntValue(WorldCfg.Numthreads)); + if (num_threads > 0) + m_updater = new MapUpdater(WorldConfig.GetIntValue(WorldCfg.Numthreads)); + } + + public void InitializeParentMapData(MultiMap mapData) + { + _parentMapData = mapData; } public void InitializeVisibilityDistanceInfo() @@ -52,31 +57,46 @@ namespace Game.Entities public Map CreateBaseMap(uint id) { Map map = FindBaseMap(id); - if (map == null) { - lock (this) + var entry = CliDB.MapStorage.LookupByKey(id); + Contract.Assert(entry != null); + if (entry.ParentMapID != -1) { + CreateBaseMap((uint)entry.ParentMapID); - var entry = CliDB.MapStorage.LookupByKey(id); - Contract.Assert(entry != null); - - if (entry.Instanceable()) - map = new MapInstanced(id, i_gridCleanUpDelay); - else - { - map = new Map(id, i_gridCleanUpDelay, 0, Difficulty.None); - map.LoadRespawnTimes(); - map.LoadCorpseData(); - } - - i_maps[id] = map; + // must have been created by parent map + map = FindBaseMap(id); + return map; } + + lock(_mapsLock) + map = CreateBaseMap_i(entry); } Contract.Assert(map != null); return map; } + Map CreateBaseMap_i(MapRecord mapEntry) + { + Map map; + if (mapEntry.Instanceable()) + map = new MapInstanced(mapEntry.Id, i_gridCleanUpDelay); + else + { + map = new Map(mapEntry.Id, i_gridCleanUpDelay, 0, Difficulty.None); + map.LoadRespawnTimes(); + map.LoadCorpseData(); + } + + i_maps[mapEntry.Id] = map; + + foreach (uint childMapId in _parentMapData[mapEntry.Id]) + map.AddChildTerrainMap(CreateBaseMap_i(CliDB.MapStorage.LookupByKey(childMapId))); + + return map; + } + public Map FindBaseNonInstanceMap(uint mapId) { Map map = FindBaseMap(mapId); @@ -206,20 +226,14 @@ namespace Game.Entities var time = (uint)i_timer.GetCurrent(); foreach (var map in i_maps.Values.ToList()) { - //if (!m_updater) - //m_updater.Enqueue(map, (uint)i_timer.GetCurrent()); - //else - map.Update(time); + if (m_updater != null) + m_updater.Enqueue(map, (uint)i_timer.GetCurrent()); + else + map.Update(time); } - //List tasks = new List(); - //foreach (var map in i_maps.Values.ToList()) - { - //tasks.Add(System.Threading.Tasks.Task.Factory.StartNew(() => map.Update(time))); - } - //System.Threading.Tasks.Task.WaitAll(tasks.ToArray()); - //if (!m_updater) - // m_updater.Wait(); + if (m_updater != null) + m_updater.Wait(); foreach (var map in i_maps.ToList()) map.Value.DelayedUpdate(time); @@ -251,6 +265,10 @@ namespace Game.Entities public void UnloadAll() { + // first unlink child maps + foreach (var pair in i_maps) + pair.Value.UnlinkAllChildTerrainMaps(); + foreach (var pair in i_maps.ToList()) { pair.Value.UnloadAll(); @@ -260,34 +278,40 @@ namespace Game.Entities public uint GetNumInstances() { - uint ret = 0; - foreach (var pair in i_maps.ToList()) + lock (_mapsLock) { - Map map = pair.Value; - if (!map.Instanceable()) - continue; - var maps = ((MapInstanced)map).GetInstancedMaps(); - foreach (var imap in maps) - if (imap.Value.IsDungeon()) - ret++; + uint ret = 0; + foreach (var pair in i_maps.ToList()) + { + Map map = pair.Value; + if (!map.Instanceable()) + continue; + var maps = ((MapInstanced)map).GetInstancedMaps(); + foreach (var imap in maps) + if (imap.Value.IsDungeon()) + ret++; + } + return ret; } - return ret; } public uint GetNumPlayersInInstances() { - uint ret = 0; - foreach (var pair in i_maps) + lock (_mapsLock) { - Map map = pair.Value; - if (!map.Instanceable()) - continue; - var maps = ((MapInstanced)map).GetInstancedMaps(); - foreach (var imap in maps) - if (imap.Value.IsDungeon()) - ret += (uint)imap.Value.GetPlayers().Count; + uint ret = 0; + foreach (var pair in i_maps) + { + Map map = pair.Value; + if (!map.Instanceable()) + continue; + var maps = ((MapInstanced)map).GetInstancedMaps(); + foreach (var imap in maps) + if (imap.Value.IsDungeon()) + ret += (uint)imap.Value.GetPlayers().Count; + } + return ret; } - return ret; } public void InitInstanceIds() @@ -361,54 +385,60 @@ namespace Game.Entities return i_maps.LookupByKey(mapId); } - uint GetAreaId(uint mapid, float x, float y, float z) + uint GetAreaId(PhaseShift phaseShift, uint mapid, float x, float y, float z) { Map m = CreateBaseMap(mapid); - return m.GetAreaId(x, y, z); + return m.GetAreaId(phaseShift, x, y, z); } - public uint GetZoneId(uint mapid, float x, float y, float z) + public uint GetZoneId(PhaseShift phaseShift, uint mapid, float x, float y, float z) { Map m = CreateBaseMap(mapid); - return m.GetZoneId(x, y, z); + return m.GetZoneId(phaseShift, x, y, z); } - public void GetZoneAndAreaId(out uint zoneid, out uint areaid, uint mapid, float x, float y, float z) + public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, uint mapid, float x, float y, float z) { Map m = CreateBaseMap(mapid); - m.GetZoneAndAreaId(out zoneid, out areaid, x, y, z); + m.GetZoneAndAreaId(phaseShift, out zoneid, out areaid, x, y, z); } public void DoForAllMaps(Action worker) { - foreach (var map in i_maps.Values) + lock (_mapsLock) { - MapInstanced mapInstanced = map.ToMapInstanced(); - if (mapInstanced) + foreach (var map in i_maps.Values) { - var instances = mapInstanced.GetInstancedMaps(); - foreach (var instance in instances.Values) - worker(instance); + MapInstanced mapInstanced = map.ToMapInstanced(); + if (mapInstanced) + { + var instances = mapInstanced.GetInstancedMaps(); + foreach (var instance in instances.Values) + worker(instance); + } + else + worker(map); } - else - worker(map); } } public void DoForAllMapsWithMapId(uint mapId, Action worker) { - var map = i_maps.LookupByKey(mapId); - if (map != null) + lock (_mapsLock) { - MapInstanced mapInstanced = map.ToMapInstanced(); - if (mapInstanced) + var map = i_maps.LookupByKey(mapId); + if (map != null) { - var instances = mapInstanced.GetInstancedMaps(); - foreach (var instance in instances) - worker(instance.Value); + MapInstanced mapInstanced = map.ToMapInstanced(); + if (mapInstanced) + { + var instances = mapInstanced.GetInstancedMaps(); + foreach (var instance in instances) + worker(instance.Value); + } + else + worker(map); } - else - worker(map); } } @@ -419,10 +449,14 @@ namespace Game.Entities Dictionary i_maps = new Dictionary(); IntervalTimer i_timer = new IntervalTimer(); + object _mapsLock= new object(); uint i_gridCleanUpDelay; Dictionary _instanceIds = new Dictionary(); uint _nextInstanceId; - //MapUpdater m_updater; + MapUpdater m_updater; uint _scheduledScripts; + + // parent map links + MultiMap _parentMapData; } } diff --git a/Source/Game/Maps/TransportManager.cs b/Source/Game/Maps/TransportManager.cs index 1ec385eff..d51addcd4 100644 --- a/Source/Game/Maps/TransportManager.cs +++ b/Source/Game/Maps/TransportManager.cs @@ -348,7 +348,7 @@ namespace Game.Maps _transportAnimations[transportEntry] = animNode; } - public Transport CreateTransport(uint entry, ulong guid = 0, Map map = null, uint phaseid = 0, uint phasegroup = 0) + public Transport CreateTransport(uint entry, ulong guid = 0, Map map = null, PhaseUseFlagsValues phaseUseFlags = 0, uint phaseId = 0, uint phaseGroupId = 0) { // instance case, execute GetGameObjectEntry hook if (map != null) @@ -388,12 +388,7 @@ namespace Game.Maps if (!trans.Create(guidLow, entry, mapId, x, y, z, o, 255)) return null; - if (phaseid != 0) - trans.SetInPhase(phaseid, false, true); - - if (phasegroup != 0) - foreach (var ph in Global.DB2Mgr.GetPhasesForGroup(phasegroup)) - trans.SetInPhase(ph, false, true); + PhasingHandler.InitDbPhaseShift(trans.GetPhaseShift(), phaseUseFlags, phaseId, phaseGroupId); MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapId); if (mapEntry != null) @@ -424,7 +419,7 @@ namespace Game.Maps uint oldMSTime = Time.GetMSTime(); - SQLResult result = DB.World.Query("SELECT guid, entry, phaseid, phasegroup FROM transports"); + SQLResult result = DB.World.Query("SELECT guid, entry, phaseUseFlags, phaseid, phasegroup FROM transports"); uint count = 0; if (!result.IsEmpty()) @@ -433,13 +428,51 @@ namespace Game.Maps { ulong guid = result.Read(0); uint entry = result.Read(1); - uint phaseid = result.Read(2); - uint phasegroup = result.Read(3); + PhaseUseFlagsValues phaseUseFlags = (PhaseUseFlagsValues)result.Read(2); + uint phaseId = result.Read(3); + uint phaseGroupId = result.Read(4); + + if (Convert.ToBoolean(phaseUseFlags & ~PhaseUseFlagsValues.All)) + { + Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) with unknown `phaseUseFlags` set, removed unknown value."); + phaseUseFlags &= PhaseUseFlagsValues.All; + } + + if (phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.AlwaysVisible) && phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.Inverse)) + { + Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE," + + " removing PHASE_USE_FLAGS_INVERSE."); + phaseUseFlags &= ~PhaseUseFlagsValues.Inverse; + } + + if (phaseGroupId != 0 && phaseId != 0) + { + Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0"); + phaseGroupId = 0; + } + + if (phaseId != 0) + { + if (!CliDB.PhaseStorage.ContainsKey(phaseId)) + { + Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) with `phaseid` {phaseId} does not exist, set to 0"); + phaseId = 0; + } + } + + if (phaseGroupId != 0) + { + if (Global.DB2Mgr.GetPhasesForGroup(phaseGroupId).Empty()) + { + Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) with `phaseGroup` {phaseGroupId} does not exist, set to 0"); + phaseGroupId = 0; + } + } TransportTemplate tInfo = GetTransportTemplate(entry); if (tInfo != null) if (!tInfo.inInstance) - if (CreateTransport(entry, guid, null, phaseid, phasegroup)) + if (CreateTransport(entry, guid, null, phaseUseFlags, phaseId, phaseGroupId)) ++count; } while (result.NextRow()); diff --git a/Source/Game/Movement/Generators/FleeingGenerator.cs b/Source/Game/Movement/Generators/FleeingGenerator.cs index 70cafb5e3..d558a3f9f 100644 --- a/Source/Game/Movement/Generators/FleeingGenerator.cs +++ b/Source/Game/Movement/Generators/FleeingGenerator.cs @@ -52,7 +52,7 @@ namespace Game.Movement _getPoint(owner, out x, out y, out z); Position mypos = owner.GetPosition(); - bool isInLOS = Global.VMapMgr.isInLineOfSight(owner.GetMapId(), mypos.posX, mypos.posY, mypos.posZ + 2.0f, x, y, z + 2.0f); + bool isInLOS = Global.VMapMgr.isInLineOfSight(PhasingHandler.GetTerrainMapId(owner.GetPhaseShift(), owner.GetMap(), mypos.posX, mypos.posY), mypos.posX, mypos.posY, mypos.posZ + 2.0f, x, y, z + 2.0f); if (!isInLOS) { diff --git a/Source/Game/Movement/Generators/PathGenerator.cs b/Source/Game/Movement/Generators/PathGenerator.cs index c91fc8a7a..c4f8b1409 100644 --- a/Source/Game/Movement/Generators/PathGenerator.cs +++ b/Source/Game/Movement/Generators/PathGenerator.cs @@ -39,8 +39,8 @@ namespace Game.Movement _navMeshQuery = null; Log.outDebug(LogFilter.Maps, "PathGenerator:PathGenerator for {0}", _sourceUnit.GetGUID().ToString()); - uint mapId = _sourceUnit.GetMapId(); - if (Global.DisableMgr.IsPathfindingEnabled(mapId)) + uint mapId = PhasingHandler.GetTerrainMapId(_sourceUnit.GetPhaseShift(), _sourceUnit.GetMap(), _sourceUnit.GetPositionX(), _sourceUnit.GetPositionY()); + if (Global.DisableMgr.IsPathfindingEnabled(_sourceUnit.GetMapId())) { _navMesh = Global.MMapMgr.GetNavMesh(mapId); _navMeshQuery = Global.MMapMgr.GetNavMeshQuery(mapId, _sourceUnit.GetInstanceId()); @@ -175,7 +175,7 @@ namespace Game.Movement // Check both start and end points, if they're both in water, then we can *safely* let the creature move for (uint i = 0; i < _pathPoints.Length; ++i) { - ZLiquidStatus status = _sourceUnit.GetMap().getLiquidStatus(_pathPoints[i].X, _pathPoints[i].Y, _pathPoints[i].Z, MapConst.MapAllLiquidTypes); + ZLiquidStatus status = _sourceUnit.GetMap().getLiquidStatus(_sourceUnit.GetPhaseShift(), _pathPoints[i].X, _pathPoints[i].Y, _pathPoints[i].Z, MapConst.MapAllLiquidTypes); // One of the points is not in the water, cancel movement. if (status == ZLiquidStatus.NoWater) { @@ -201,7 +201,7 @@ namespace Game.Movement Creature owner = _sourceUnit.ToCreature(); Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos; - if (_sourceUnit.GetMap().IsUnderWater(p.X, p.Y, p.Z)) + if (_sourceUnit.GetMap().IsUnderWater(_sourceUnit.GetPhaseShift(), p.X, p.Y, p.Z)) { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . underWater case\n"); if (owner.CanSwim()) @@ -834,7 +834,7 @@ namespace Game.Movement NavTerrain GetNavTerrain(float x, float y, float z) { LiquidData data; - ZLiquidStatus liquidStatus = _sourceUnit.GetMap().getLiquidStatus(x, y, z, MapConst.MapAllLiquidTypes, out data); + ZLiquidStatus liquidStatus = _sourceUnit.GetMap().getLiquidStatus(_sourceUnit.GetPhaseShift(), x, y, z, MapConst.MapAllLiquidTypes, out data); if (liquidStatus == ZLiquidStatus.NoWater) return NavTerrain.Ground; diff --git a/Source/Game/Movement/Generators/RandomMovement.cs b/Source/Game/Movement/Generators/RandomMovement.cs index f737c97ec..b17330c69 100644 --- a/Source/Game/Movement/Generators/RandomMovement.cs +++ b/Source/Game/Movement/Generators/RandomMovement.cs @@ -117,7 +117,7 @@ namespace Game.Movement // Limit height change float distanceZ = (float)(RandomHelper.NextDouble() * travelDistZ / 2.0f); destZ = respZ + distanceZ; - float levelZ = map.GetWaterOrGroundLevel(creature.GetPhases(), destX, destY, destZ - 2.5f); + float levelZ = map.GetWaterOrGroundLevel(creature.GetPhaseShift(), destX, destY, destZ - 2.5f); // Problem here, we must fly above the ground and water, not under. Let's try on next tick if (levelZ >= destZ) @@ -130,17 +130,17 @@ namespace Game.Movement // The fastest way to get an accurate result 90% of the time. // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. - destZ = map.GetHeight(creature.GetPhases(), destX, destY, respZ + travelDistZ - 2.0f, false); + destZ = map.GetHeight(creature.GetPhaseShift(), destX, destY, respZ + travelDistZ - 2.0f, false); if (Math.Abs(destZ - respZ) > travelDistZ) // Map check { // Vmap Horizontal or above - destZ = map.GetHeight(creature.GetPhases(), destX, destY, respZ - 2.0f, true); + destZ = map.GetHeight(creature.GetPhaseShift(), destX, destY, respZ - 2.0f, true); if (Math.Abs(destZ - respZ) > travelDistZ) { // Vmap Higher - destZ = map.GetHeight(creature.GetPhases(), destX, destY, respZ + travelDistZ - 2.0f, true); + destZ = map.GetHeight(creature.GetPhaseShift(), destX, destY, respZ + travelDistZ - 2.0f, true); // let's forget this bad coords where a z cannot be find and retry at next tick if (Math.Abs(destZ - respZ) > travelDistZ) diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index 627ed9b75..ceffb60cf 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -436,7 +436,7 @@ namespace Game.Movement if (_owner.IsFlying()) point.Z = z; else - point.Z = _owner.GetMap().GetHeight(_owner.GetPhases(), point.X, point.Y, z); + point.Z = _owner.GetMap().GetHeight(_owner.GetPhaseShift(), point.X, point.Y, z); init.args.path[i] = point; } @@ -514,7 +514,7 @@ namespace Game.Movement public void MoveFall(uint id = 0) { // use larger distance for vmap height search than in most other cases - float tz = _owner.GetMap().GetHeight(_owner.GetPhases(), _owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ(), true, MapConst.MaxFallDistance); + float tz = _owner.GetMap().GetHeight(_owner.GetPhaseShift(), _owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ(), true, MapConst.MaxFallDistance); if (tz <= MapConst.InvalidHeight) return; diff --git a/Source/Game/Network/Packets/MiscPackets.cs b/Source/Game/Network/Packets/MiscPackets.cs index e21eddbd4..46e5bdceb 100644 --- a/Source/Game/Network/Packets/MiscPackets.cs +++ b/Source/Game/Network/Packets/MiscPackets.cs @@ -743,43 +743,38 @@ namespace Game.Network.Packets public override void Write() { } } - public class PhaseShift : ServerPacket + + class PhaseShiftChange : ServerPacket { - public PhaseShift() : base(ServerOpcodes.PhaseShiftChange, ConnectionType.Instance) { } + public PhaseShiftChange() : base(ServerOpcodes.PhaseShiftChange) { } public override void Write() { - _worldPacket.WritePackedGuid(ClientGUID); - _worldPacket.WriteUInt32(!PhaseShifts.Empty() ? 0 : 8); // PhaseShiftFlags - _worldPacket.WriteUInt32(PhaseShifts.Count); - _worldPacket.WritePackedGuid(PersonalGUID); - foreach (uint phase in PhaseShifts) - { - _worldPacket.WriteUInt16(1); // PhaseFlags - _worldPacket.WriteUInt16(phase); // PhaseID - } + _worldPacket.WritePackedGuid(Client); + Phaseshift.Write(_worldPacket); + _worldPacket.WriteUInt32(VisibleMapIDs.Count * 2); // size in bytes + foreach (ushort visibleMapId in VisibleMapIDs) + _worldPacket.WriteUInt16(visibleMapId); // Active terrain swap map id - _worldPacket.WriteUInt32(VisibleMapIDs.Count * 2); - foreach (uint map in VisibleMapIDs) - _worldPacket.WriteUInt16(map); // Active terrain swap map id + _worldPacket.WriteUInt32(PreloadMapIDs.Count * 2); // size in bytes + foreach (ushort preloadMapId in PreloadMapIDs) + _worldPacket.WriteUInt16(preloadMapId); // Inactive terrain swap map id - _worldPacket.WriteUInt32(PreloadMapIDs.Count * 2); - foreach (uint map in PreloadMapIDs) - _worldPacket.WriteUInt16(map); // Inactive terrain swap map id - - _worldPacket.WriteUInt32(UiWorldMapAreaIDSwaps.Count * 2); - foreach (uint map in UiWorldMapAreaIDSwaps) - _worldPacket.WriteUInt16(map); // UI map id, WorldMapArea.dbc, controls map display + _worldPacket.WriteUInt32(UiWorldMapAreaIDSwaps.Count * 2); // size in bytes + foreach (ushort uiWorldMapAreaIDSwap in UiWorldMapAreaIDSwaps) + _worldPacket.WriteUInt16(uiWorldMapAreaIDSwap); // UI map id, WorldMapArea.db2, controls map display } - public ObjectGuid ClientGUID; - public ObjectGuid PersonalGUID; - public List PhaseShifts = new List(); - public List PreloadMapIDs = new List(); - public List UiWorldMapAreaIDSwaps = new List(); - public List VisibleMapIDs = new List(); + public ObjectGuid Client; + public PhaseShiftData Phaseshift = new PhaseShiftData(); + public List PreloadMapIDs = new List(); + public List UiWorldMapAreaIDSwaps = new List(); + public List VisibleMapIDs = new List(); } + + + public class ZoneUnderAttack : ServerPacket { public ZoneUnderAttack() : base(ServerOpcodes.ZoneUnderAttack, ConnectionType.Instance) { } @@ -1299,4 +1294,39 @@ namespace Game.Network.Packets public ObjectGuid SourceGuid; } + + //Structs + struct PhaseShiftDataPhase + { + public PhaseShiftDataPhase(uint phaseFlags, uint id) + { + PhaseFlags = (ushort)phaseFlags; + Id = (ushort)id; + } + + public void Write(WorldPacket data) + { + data.WriteUInt16(PhaseFlags); + data.WriteUInt16(Id); + } + + public ushort PhaseFlags; + public ushort Id; + } + + class PhaseShiftData + { + public void Write(WorldPacket data) + { + data.WriteUInt32(PhaseShiftFlags); + data.WriteUInt32(Phases.Count); + data.WritePackedGuid(PersonalGUID); + foreach (PhaseShiftDataPhase phaseShiftDataPhase in Phases) + phaseShiftDataPhase.Write(data); + } + + public uint PhaseShiftFlags; + public List Phases = new List(); + public ObjectGuid PersonalGUID; + } } diff --git a/Source/Game/Network/Packets/PartyPackets.cs b/Source/Game/Network/Packets/PartyPackets.cs index 2534faa60..1b2c1a7ba 100644 --- a/Source/Game/Network/Packets/PartyPackets.cs +++ b/Source/Game/Network/Packets/PartyPackets.cs @@ -313,16 +313,7 @@ namespace Game.Network.Packets } // Phases - var phases = player.GetPhases(); - MemberStats.Phases.PhaseShiftFlags = 0x08 | (!phases.Empty() ? 0x10 : 0); - MemberStats.Phases.PersonalGUID = ObjectGuid.Empty; - foreach (uint phaseId in phases) - { - PartyMemberPhase phase; - phase.Id = (ushort)phaseId; - phase.Flags = 1; - MemberStats.Phases.List.Add(phase); - } + PhasingHandler.FillPartyMemberPhase(MemberStats.Phases, player.GetPhaseShift()); // Pet if (player.GetPet()) @@ -892,8 +883,14 @@ namespace Game.Network.Packets } //Structs - struct PartyMemberPhase + public struct PartyMemberPhase { + public PartyMemberPhase(uint flags, uint id) + { + Flags = (ushort)flags; + Id = (ushort)id; + } + public void Write(WorldPacket data) { data.WriteUInt16(Flags); @@ -904,7 +901,7 @@ namespace Game.Network.Packets public ushort Id; } - class PartyMemberPhaseStates + public class PartyMemberPhaseStates { public void Write(WorldPacket data) { diff --git a/Source/Game/Phasing/PhaseShift.cs b/Source/Game/Phasing/PhaseShift.cs new file mode 100644 index 000000000..919c20f43 --- /dev/null +++ b/Source/Game/Phasing/PhaseShift.cs @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2012-2018 CypherCore + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Text; +using Game.Conditions; +using Game.Entities; +using System.Collections.Concurrent; +using System.Linq; + +namespace Game +{ + public class PhaseShift + { + public bool AddPhase(uint phaseId, PhaseFlags flags, List areaConditions, int references = 1) + { + var phase = new PhaseRef(phaseId, flags, null); + ModifyPhasesReferences(phase, references); + if (areaConditions != null) + phase.AreaConditions = areaConditions; + + Phases.Add(phase); + return true; + } + + public bool RemovePhase(uint phaseId) + { + var phaseRef = new PhaseRef(phaseId, PhaseFlags.None, null); + if (Phases.Contains(phaseRef)) + { + ModifyPhasesReferences(phaseRef, -1); + if (phaseRef.References == 0) + { + Phases.Remove(phaseRef); + return true; + } + + return false; + } + return false; + } + + public bool AddVisibleMapId(uint visibleMapId, TerrainSwapInfo visibleMapInfo, int references = 1) + { + VisibleMapIds.Add(visibleMapId, new VisibleMapIdRef(references, visibleMapInfo)); + return true; //Tryadd? maybe Concurrent + } + + public bool RemoveVisibleMapId(uint visibleMapId) + { + if (VisibleMapIds.ContainsKey(visibleMapId)) + { + var mapIdRef = VisibleMapIds[visibleMapId]; + if ((--mapIdRef.References) == 0) + { + VisibleMapIds.Remove(visibleMapId); + return true; + } + + return false; + } + + return false; + } + + public bool AddUiWorldMapAreaIdSwap(uint uiWorldMapAreaId, int references = 1) + { + UiWorldMapAreaIdSwaps.Add(uiWorldMapAreaId, new UiWorldMapAreaIdSwapRef(references)); + return true; //Tryadd? maybe Concurrent + } + + public bool RemoveUiWorldMapAreaIdSwap(uint uiWorldMapAreaId) + { + if (UiWorldMapAreaIdSwaps.ContainsKey(uiWorldMapAreaId)) + { + var value = UiWorldMapAreaIdSwaps[uiWorldMapAreaId]; + if ((--value.References) == 0) + { + UiWorldMapAreaIdSwaps.Remove(uiWorldMapAreaId); + return true; + } + + return false; + } + + return false; + } + + public void Clear() + { + ClearPhases(); + PersonalGuid.Clear(); + VisibleMapIds.Clear(); + UiWorldMapAreaIdSwaps.Clear(); + } + + public void ClearPhases() + { + Flags &= PhaseShiftFlags.AlwaysVisible | PhaseShiftFlags.Inverse; + Phases.Clear(); + NonCosmeticReferences = 0; + CosmeticReferences = 0; + DefaultReferences = 0; + UpdateUnphasedFlag(); + } + + public bool CanSee(PhaseShift other) + { + if (Flags.HasFlag(PhaseShiftFlags.Unphased) && other.Flags.HasFlag(PhaseShiftFlags.Unphased)) + return true; + if (Flags.HasFlag(PhaseShiftFlags.AlwaysVisible) || other.Flags.HasFlag(PhaseShiftFlags.AlwaysVisible)) + return true; + if (Flags.HasFlag(PhaseShiftFlags.Inverse) && other.Flags.HasFlag(PhaseShiftFlags.Inverse)) + return true; + + PhaseFlags excludePhasesWithFlag = PhaseFlags.None; + if (Flags.HasFlag(PhaseShiftFlags.NoCosmetic) && other.Flags.HasFlag(PhaseShiftFlags.NoCosmetic)) + excludePhasesWithFlag = PhaseFlags.Cosmetic; + + if (!Flags.HasFlag(PhaseShiftFlags.Inverse) && !other.Flags.HasFlag(PhaseShiftFlags.Inverse)) + { + ObjectGuid ownerGuid = PersonalGuid; + ObjectGuid otherPersonalGuid = other.PersonalGuid; + return Phases.Intersect(other.Phases, (myPhase, otherPhase) => + { + return !myPhase.Flags.HasAnyFlag(excludePhasesWithFlag) && (!myPhase.Flags.HasFlag(PhaseFlags.Personal) || ownerGuid == otherPersonalGuid); + }).Any(); + } + + var checkInversePhaseShift = new Func((phaseShift, excludedPhaseShift) => + { + if (phaseShift.Flags.HasFlag(PhaseShiftFlags.Unphased) && !excludedPhaseShift.Flags.HasFlag(PhaseShiftFlags.InverseUnphased)) + return true; + + foreach (var itr in phaseShift.Phases) + { + if (itr.Flags.HasAnyFlag(excludePhasesWithFlag)) + continue; + + var index = excludedPhaseShift.Phases.IndexOf(itr); + if (index == -1 || excludedPhaseShift.Phases[index].Flags.HasAnyFlag(excludePhasesWithFlag)) + return true; + } + + return false; + }); + + if (other.Flags.HasFlag(PhaseShiftFlags.Inverse)) + return checkInversePhaseShift(this, other); + + return checkInversePhaseShift(other, this); + } + + public void ModifyPhasesReferences(PhaseRef phaseRef, int references) + { + phaseRef.References += references; + + if (!IsDbPhaseShift) + { + if (phaseRef.Flags.HasAnyFlag(PhaseFlags.Cosmetic)) + CosmeticReferences += references; + else if (phaseRef.Id != 169) + NonCosmeticReferences += references; + else + DefaultReferences += references; + + if (CosmeticReferences != 0) + Flags |= PhaseShiftFlags.NoCosmetic; + else + Flags &= ~PhaseShiftFlags.NoCosmetic; + + UpdateUnphasedFlag(); + } + } + + public void UpdateUnphasedFlag() + { + PhaseShiftFlags unphasedFlag = !Flags.HasAnyFlag(PhaseShiftFlags.Inverse) ? PhaseShiftFlags.Unphased : PhaseShiftFlags.InverseUnphased; + Flags &= ~(!Flags.HasFlag(PhaseShiftFlags.Inverse) ? PhaseShiftFlags.InverseUnphased : PhaseShiftFlags.Unphased); + if (NonCosmeticReferences != 0 && DefaultReferences == 0) + Flags &= ~unphasedFlag; + else + Flags |= unphasedFlag; + } + + public bool HasPhase(uint phaseId) { return Phases.Contains(new PhaseRef(phaseId, PhaseFlags.None, null)); } + public List GetPhases() { return Phases; } + + public bool HasVisibleMapId(uint visibleMapId) { return VisibleMapIds.ContainsKey(visibleMapId); } + public Dictionary GetVisibleMapIds() { return VisibleMapIds; } + + public bool HasUiWorldMapAreaIdSwap(uint uiWorldMapAreaId) { return UiWorldMapAreaIdSwaps.ContainsKey(uiWorldMapAreaId); } + public Dictionary GetUiWorldMapAreaIdSwaps() { return UiWorldMapAreaIdSwaps; } + + public PhaseShiftFlags Flags = PhaseShiftFlags.Unphased; + public ObjectGuid PersonalGuid; + public List Phases = new List(); + public Dictionary VisibleMapIds = new Dictionary(); + public Dictionary UiWorldMapAreaIdSwaps = new Dictionary(); + + int NonCosmeticReferences; + int CosmeticReferences; + int DefaultReferences; + public bool IsDbPhaseShift; + } + + public struct PhaseRef + { + public PhaseRef(uint id, PhaseFlags flags, List conditions) + { + Id = id; + Flags = flags; + References = 0; + AreaConditions = conditions; + } + + public uint Id; + public PhaseFlags Flags; + public int References; + public List AreaConditions; + + public static bool operator <(PhaseRef left, PhaseRef right) + { + return left.Id < right.Id; + } + + public static bool operator >(PhaseRef left, PhaseRef right) + { + return left.Id > right.Id; + } + + public static bool operator ==(PhaseRef left, PhaseRef right) { return left.Id == right.Id; } + public static bool operator !=(PhaseRef left, PhaseRef right) { return !(left == right); } + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + public struct VisibleMapIdRef + { + public VisibleMapIdRef(int references, TerrainSwapInfo visibleMapInfo) + { + References = references; + VisibleMapInfo = visibleMapInfo; + } + + public int References; + public TerrainSwapInfo VisibleMapInfo; + } + + public struct UiWorldMapAreaIdSwapRef + { + public UiWorldMapAreaIdSwapRef(int references) + { + References = references; + } + + public int References; + } + + public enum PhaseShiftFlags + { + None = 0x00, + AlwaysVisible = 0x01, // Ignores all phasing, can see everything and be seen by everything + Inverse = 0x02, // By default having at least one shared phase for two objects means they can see each other + // this flag makes objects see each other if they have at least one non-shared phase + InverseUnphased = 0x04, + Unphased = 0x08, + NoCosmetic = 0x10 // This flag ignores shared cosmetic phases (two players that both have shared cosmetic phase but no other phase cannot see each other) + } + + public enum PhaseFlags : ushort + { + None = 0x0, + Cosmetic = 0x1, + Personal = 0x2 + } +} diff --git a/Source/Game/Phasing/PhasingHandler.cs b/Source/Game/Phasing/PhasingHandler.cs new file mode 100644 index 000000000..3b61a717e --- /dev/null +++ b/Source/Game/Phasing/PhasingHandler.cs @@ -0,0 +1,573 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Game.DataStorage; +using Game.Entities; +using Framework.Constants; +using Game.Conditions; +using Game.Spells; +using System.Linq; +using Game.Network.Packets; +using Game.Chat; +using Game.Maps; + +namespace Game +{ + public class PhasingHandler + { + public static PhaseShift EmptyPhaseShift = new PhaseShift(); + + public static PhaseFlags GetPhaseFlags(uint phaseId) + { + PhaseRecord phase = CliDB.PhaseStorage.LookupByKey(phaseId); + if (phase != null) + { + if (phase.Flags.HasAnyFlag(PhaseEntryFlags.Cosmetic)) + return PhaseFlags.Cosmetic; + + if (phase.Flags.HasAnyFlag(PhaseEntryFlags.Personal)) + return PhaseFlags.Personal; + } + + return PhaseFlags.None; + } + + public static void ForAllControlled(Unit unit, Action func) + { + foreach (Unit controlled in unit.m_Controlled) + if (controlled.GetTypeId() != TypeId.Player) + func(controlled); + + for (byte i = 0; i < SharedConst.MaxSummonSlot; ++i) + { + if (!unit.m_SummonSlot[i].IsEmpty()) + { + Creature summon = unit.GetMap().GetCreature(unit.m_SummonSlot[i]); + if (summon) + func(summon); + } + } + } + + public static void AddPhase(WorldObject obj, uint phaseId, bool updateVisibility) + { + bool changed = obj.GetPhaseShift().AddPhase(phaseId, GetPhaseFlags(phaseId), null); + + Unit unit = obj.ToUnit(); + if (unit) + { + unit.OnPhaseChange(); + ForAllControlled(unit, controlled => + { + AddPhase(controlled, phaseId, updateVisibility); + }); + unit.RemoveNotOwnSingleTargetAuras(true); + } + + UpdateVisibilityIfNeeded(obj, updateVisibility, changed); + } + + public static void RemovePhase(WorldObject obj, uint phaseId, bool updateVisibility) + { + bool changed = obj.GetPhaseShift().RemovePhase(phaseId); + + Unit unit = obj.ToUnit(); + if (unit) + { + unit.OnPhaseChange(); + ForAllControlled(unit, controlled => + { + RemovePhase(controlled, phaseId, updateVisibility); + }); + unit.RemoveNotOwnSingleTargetAuras(true); + } + + UpdateVisibilityIfNeeded(obj, updateVisibility, changed); + } + + public static void AddPhaseGroup(WorldObject obj, uint phaseGroupId, bool updateVisibility) + { + var phasesInGroup = Global.DB2Mgr.GetPhasesForGroup(phaseGroupId); + if (phasesInGroup.Empty()) + return; + + bool changed = false; + foreach (uint phaseId in phasesInGroup) + changed = obj.GetPhaseShift().AddPhase(phaseId, GetPhaseFlags(phaseId), null) || changed; + + Unit unit = obj.ToUnit(); + if (unit) + { + unit.OnPhaseChange(); + ForAllControlled(unit, controlled => + { + AddPhaseGroup(controlled, phaseGroupId, updateVisibility); + }); + unit.RemoveNotOwnSingleTargetAuras(true); + } + + UpdateVisibilityIfNeeded(obj, updateVisibility, changed); + } + + public static void RemovePhaseGroup(WorldObject obj, uint phaseGroupId, bool updateVisibility) + { + var phasesInGroup = Global.DB2Mgr.GetPhasesForGroup(phaseGroupId); + if (phasesInGroup.Empty()) + return; + + bool changed = false; + foreach (uint phaseId in phasesInGroup) + changed = obj.GetPhaseShift().RemovePhase(phaseId) || changed; + + Unit unit = obj.ToUnit(); + if (unit) + { + unit.OnPhaseChange(); + ForAllControlled(unit, controlled => + { + RemovePhaseGroup(controlled, phaseGroupId, updateVisibility); + }); + unit.RemoveNotOwnSingleTargetAuras(true); + } + + UpdateVisibilityIfNeeded(obj, updateVisibility, changed); + } + + public static void AddVisibleMapId(WorldObject obj, uint visibleMapId) + { + TerrainSwapInfo terrainSwapInfo = Global.ObjectMgr.GetTerrainSwapInfo(visibleMapId); + bool changed = obj.GetPhaseShift().AddVisibleMapId(visibleMapId, terrainSwapInfo); + + foreach (uint uiWorldMapAreaIDSwap in terrainSwapInfo.UiWorldMapAreaIDSwaps) + changed = obj.GetPhaseShift().AddUiWorldMapAreaIdSwap(uiWorldMapAreaIDSwap) || changed; + + Unit unit = obj.ToUnit(); + if (unit) + { + ForAllControlled(unit, controlled => + { + AddVisibleMapId(controlled, visibleMapId); + }); + } + + UpdateVisibilityIfNeeded(obj, false, changed); + } + + public static void RemoveVisibleMapId(WorldObject obj, uint visibleMapId) + { + TerrainSwapInfo terrainSwapInfo = Global.ObjectMgr.GetTerrainSwapInfo(visibleMapId); + bool changed = obj.GetPhaseShift().RemoveVisibleMapId(visibleMapId); + + foreach (uint uiWorldMapAreaIDSwap in terrainSwapInfo.UiWorldMapAreaIDSwaps) + changed = obj.GetPhaseShift().RemoveUiWorldMapAreaIdSwap(uiWorldMapAreaIDSwap) || changed; + + Unit unit = obj.ToUnit(); + if (unit) + { + ForAllControlled(unit, controlled => + { + RemoveVisibleMapId(controlled, visibleMapId); + }); + } + + UpdateVisibilityIfNeeded(obj, false, changed); + } + + public static void ResetPhaseShift(WorldObject obj) + { + obj.GetPhaseShift().Clear(); + obj.GetSuppressedPhaseShift().Clear(); + } + + public static void InheritPhaseShift(WorldObject target, WorldObject source) + { + target.SetPhaseShift(source.GetPhaseShift()); + target.SetSuppressedPhaseShift(source.GetSuppressedPhaseShift()); + } + + public static void OnMapChange(WorldObject obj) + { + PhaseShift phaseShift = obj.GetPhaseShift(); + PhaseShift suppressedPhaseShift = obj.GetSuppressedPhaseShift(); + ConditionSourceInfo srcInfo = new ConditionSourceInfo(obj); + + obj.GetPhaseShift().VisibleMapIds.Clear(); + obj.GetPhaseShift().UiWorldMapAreaIdSwaps.Clear(); + obj.GetSuppressedPhaseShift().VisibleMapIds.Clear(); + + var visibleMapIds = Global.ObjectMgr.GetTerrainSwapsForMap(obj.GetMapId()); + if (!visibleMapIds.Empty()) + { + foreach (TerrainSwapInfo visibleMapInfo in visibleMapIds) + { + if (Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.TerrainSwap, visibleMapInfo.Id, srcInfo)) + { + phaseShift.AddVisibleMapId(visibleMapInfo.Id, visibleMapInfo); + foreach (uint uiWorldMapAreaIdSwap in visibleMapInfo.UiWorldMapAreaIDSwaps) + phaseShift.AddUiWorldMapAreaIdSwap(uiWorldMapAreaIdSwap); + } + else + suppressedPhaseShift.AddVisibleMapId(visibleMapInfo.Id, visibleMapInfo); + } + } + + UpdateVisibilityIfNeeded(obj, false, true); + } + + public static void OnAreaChange(WorldObject obj) + { + PhaseShift phaseShift = obj.GetPhaseShift(); + PhaseShift suppressedPhaseShift = obj.GetSuppressedPhaseShift(); + var oldPhases = phaseShift.GetPhases(); // for comparison + ConditionSourceInfo srcInfo = new ConditionSourceInfo(obj); + + obj.GetPhaseShift().ClearPhases(); + obj.GetSuppressedPhaseShift().ClearPhases(); + + uint areaId = obj.GetAreaId(); + AreaTableRecord areaEntry = CliDB.AreaTableStorage.LookupByKey(areaId); + while (areaEntry != null) + { + var newAreaPhases = Global.ObjectMgr.GetPhasesForArea(areaEntry.Id); + if (!newAreaPhases.Empty()) + { + foreach (PhaseAreaInfo phaseArea in newAreaPhases) + { + if (phaseArea.SubAreaExclusions.Contains(areaId)) + continue; + + uint phaseId = phaseArea.PhaseInfo.Id; + if (Global.ConditionMgr.IsObjectMeetToConditions(srcInfo, phaseArea.Conditions)) + phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), phaseArea.Conditions); + else + suppressedPhaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), phaseArea.Conditions); + } + } + + areaEntry = CliDB.AreaTableStorage.LookupByKey(areaEntry.ParentAreaID); + } + + bool changed = phaseShift.GetPhases() != oldPhases; + Unit unit = obj.ToUnit(); + if (unit) + { + foreach (AuraEffect aurEff in unit.GetAuraEffectsByType(AuraType.Phase)) + { + uint phaseId = (uint)aurEff.GetMiscValueB(); + changed = phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), null) || changed; + } + + foreach (AuraEffect aurEff in unit.GetAuraEffectsByType(AuraType.PhaseGroup)) + { + var phasesInGroup = Global.DB2Mgr.GetPhasesForGroup((uint)aurEff.GetMiscValueB()); + foreach (uint phaseId in phasesInGroup) + changed = phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), null) || changed; + } + + if (changed) + unit.OnPhaseChange(); + + ForAllControlled(unit, controlled => + { + InheritPhaseShift(controlled, unit); + }); + + if (changed) + unit.RemoveNotOwnSingleTargetAuras(true); + } + + UpdateVisibilityIfNeeded(obj, true, changed); + } + + public static void OnConditionChange(WorldObject obj) + { + PhaseShift phaseShift = obj.GetPhaseShift(); + PhaseShift suppressedPhaseShift = obj.GetSuppressedPhaseShift(); + PhaseShift newSuppressions = new PhaseShift(); + ConditionSourceInfo srcInfo = new ConditionSourceInfo(obj); + bool changed = false; + + foreach (var phaseRef in phaseShift.Phases.ToArray()) + { + if (!phaseRef.AreaConditions.Empty() && !Global.ConditionMgr.IsObjectMeetToConditions(srcInfo, phaseRef.AreaConditions)) + { + newSuppressions.AddPhase(phaseRef.Id, phaseRef.Flags, phaseRef.AreaConditions, phaseRef.References); + phaseShift.Phases.Remove(phaseRef); + } + } + + foreach (var phaseRef in suppressedPhaseShift.Phases.ToArray()) + { + if (Global.ConditionMgr.IsObjectMeetToConditions(srcInfo, phaseRef.AreaConditions)) + { + changed = phaseShift.AddPhase(phaseRef.Id, phaseRef.Flags, phaseRef.AreaConditions, phaseRef.References) || changed; + suppressedPhaseShift.Phases.Remove(phaseRef); + } + } + + foreach (var pair in phaseShift.VisibleMapIds.ToList()) + { + if (!Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.TerrainSwap, pair.Key, srcInfo)) + { + newSuppressions.AddVisibleMapId(pair.Key, pair.Value.VisibleMapInfo, pair.Value.References); + foreach (uint uiWorldMapAreaIdSwap in pair.Value.VisibleMapInfo.UiWorldMapAreaIDSwaps) + changed = phaseShift.RemoveUiWorldMapAreaIdSwap(uiWorldMapAreaIdSwap) || changed; + + phaseShift.VisibleMapIds.Remove(pair.Key); + } + } + + foreach (var pair in suppressedPhaseShift.VisibleMapIds.ToList()) + { + if (Global.ConditionMgr.IsObjectMeetingNotGroupedConditions(ConditionSourceType.TerrainSwap, pair.Key, srcInfo)) + { + changed = phaseShift.AddVisibleMapId(pair.Key, pair.Value.VisibleMapInfo, pair.Value.References) || changed; + foreach (uint uiWorldMapAreaIdSwap in pair.Value.VisibleMapInfo.UiWorldMapAreaIDSwaps) + changed = phaseShift.AddUiWorldMapAreaIdSwap(uiWorldMapAreaIdSwap) || changed; + + suppressedPhaseShift.VisibleMapIds.Remove(pair.Key); + } + } + + Unit unit = obj.ToUnit(); + if (unit) + { + foreach (AuraEffect aurEff in unit.GetAuraEffectsByType(AuraType.Phase)) + { + uint phaseId = (uint)aurEff.GetMiscValueB(); + var eraseResult = newSuppressions.RemovePhase(phaseId); + // if condition was met previously there is nothing to erase + if (newSuppressions.RemovePhase(phaseId)) + phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), null);//todo needs checked + } + + foreach (AuraEffect aurEff in unit.GetAuraEffectsByType(AuraType.PhaseGroup)) + { + var phasesInGroup = Global.DB2Mgr.GetPhasesForGroup((uint)aurEff.GetMiscValueB()); + if (!phasesInGroup.Empty()) + { + foreach (uint phaseId in phasesInGroup) + { + var eraseResult = newSuppressions.RemovePhase(phaseId); + // if condition was met previously there is nothing to erase + if (eraseResult) + phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), null); + } + } + } + } + + changed = changed || !newSuppressions.Phases.Empty() || !newSuppressions.VisibleMapIds.Empty(); + foreach (var phaseRef in newSuppressions.Phases) + suppressedPhaseShift.AddPhase(phaseRef.Id, phaseRef.Flags, phaseRef.AreaConditions, phaseRef.References); + + foreach (var pair in newSuppressions.VisibleMapIds) + suppressedPhaseShift.AddVisibleMapId(pair.Key, pair.Value.VisibleMapInfo, pair.Value.References); + + if (unit) + { + if (changed) + unit.OnPhaseChange(); + + ForAllControlled(unit, controlled => + { + InheritPhaseShift(controlled, unit); + }); + + if (changed) + unit.RemoveNotOwnSingleTargetAuras(true); + } + + UpdateVisibilityIfNeeded(obj, true, changed); + } + + public static void SendToPlayer(Player player, PhaseShift phaseShift) + { + PhaseShiftChange phaseShiftChange = new PhaseShiftChange(); + phaseShiftChange.Client = player.GetGUID(); + phaseShiftChange.Phaseshift.PhaseShiftFlags = (uint)phaseShift.Flags; + phaseShiftChange.Phaseshift.PersonalGUID = phaseShift.PersonalGuid; + + foreach (var phaseRef in phaseShift.Phases) + phaseShiftChange.Phaseshift.Phases.Add(new PhaseShiftDataPhase((uint)phaseRef.Flags, phaseRef.Id)); + + foreach (var visibleMapId in phaseShift.VisibleMapIds) + phaseShiftChange.VisibleMapIDs.Add((ushort)visibleMapId.Key); + + foreach (var uiWorldMapAreaIdSwap in phaseShift.UiWorldMapAreaIdSwaps) + phaseShiftChange.UiWorldMapAreaIDSwaps.Add((ushort)uiWorldMapAreaIdSwap.Key); + + player.SendPacket(phaseShiftChange); + } + + public static void SendToPlayer(Player player) + { + SendToPlayer(player, player.GetPhaseShift()); + } + + public static void FillPartyMemberPhase(PartyMemberPhaseStates partyMemberPhases, PhaseShift phaseShift) + { + partyMemberPhases.PhaseShiftFlags = (int)phaseShift.Flags; + partyMemberPhases.PersonalGUID = phaseShift.PersonalGuid; + + foreach (var phase in phaseShift.Phases) + partyMemberPhases.List.Add(new PartyMemberPhase((uint)phase.Flags, phase.Id)); + } + + public static void InitDbPhaseShift(PhaseShift phaseShift, PhaseUseFlagsValues phaseUseFlags, uint phaseId, uint phaseGroupId) + { + phaseShift.ClearPhases(); + phaseShift.IsDbPhaseShift = true; + + PhaseShiftFlags flags = PhaseShiftFlags.None; + if (phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.AlwaysVisible)) + flags = flags | PhaseShiftFlags.AlwaysVisible | PhaseShiftFlags.Unphased; + if (phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.Inverse)) + flags |= PhaseShiftFlags.Inverse; + + if (phaseId != 0) + phaseShift.AddPhase(phaseId, GetPhaseFlags(phaseId), null); + else + { + var phasesInGroup = Global.DB2Mgr.GetPhasesForGroup(phaseGroupId); + foreach (uint phaseInGroup in phasesInGroup) + phaseShift.AddPhase(phaseInGroup, GetPhaseFlags(phaseInGroup), null); + } + + if (phaseShift.Phases.Empty() || phaseShift.HasPhase(169)) + { + if (flags.HasFlag(PhaseShiftFlags.Inverse)) + flags |= PhaseShiftFlags.InverseUnphased; + else + flags |= PhaseShiftFlags.Unphased; + } + + phaseShift.Flags = flags; + } + + public static void InitDbVisibleMapId(PhaseShift phaseShift, int visibleMapId) + { + phaseShift.VisibleMapIds.Clear(); + if (visibleMapId != -1) + phaseShift.AddVisibleMapId((uint)visibleMapId, Global.ObjectMgr.GetTerrainSwapInfo((uint)visibleMapId)); + } + + public static bool InDbPhaseShift(WorldObject obj, PhaseUseFlagsValues phaseUseFlags, ushort phaseId, uint phaseGroupId) + { + PhaseShift phaseShift = new PhaseShift(); + InitDbPhaseShift(phaseShift, phaseUseFlags, phaseId, phaseGroupId); + return obj.GetPhaseShift().CanSee(phaseShift); + } + + public static uint GetTerrainMapId(PhaseShift phaseShift, Map map, float x, float y) + { + if (phaseShift.VisibleMapIds.Empty()) + return map.GetId(); + + if (phaseShift.VisibleMapIds.Count == 1) + return phaseShift.VisibleMapIds.First().Key; + + GridCoord gridCoord = GridDefines.ComputeGridCoord(x, y); + uint gx = (uint)((MapConst.MaxGrids - 1) - gridCoord.x_coord); + uint gy = (uint)((MapConst.MaxGrids - 1) - gridCoord.y_coord); + + uint gxbegin = Math.Max(gx - 1, 0); + uint gxend = Math.Min(gx + 1, MapConst.MaxGrids); + uint gybegin = Math.Max(gy - 1, 0); + uint gyend = Math.Min(gy + 1, MapConst.MaxGrids); + + foreach (var itr in phaseShift.VisibleMapIds) + for (uint gxi = gxbegin; gxi < gxend; ++gxi) + for (uint gyi = gybegin; gyi < gyend; ++gyi) + if (map.HasGridMap(itr.Key, gxi, gyi)) + return itr.Key; + + return map.GetId(); + } + + public static void SetAlwaysVisible(PhaseShift phaseShift, bool apply) + { + if (apply) + phaseShift.Flags |= PhaseShiftFlags.AlwaysVisible; + else + phaseShift.Flags &= ~PhaseShiftFlags.AlwaysVisible; + } + + public static void SetInversed(PhaseShift phaseShift, bool apply) + { + if (apply) + phaseShift.Flags |= PhaseShiftFlags.Inverse; + else + phaseShift.Flags &= ~PhaseShiftFlags.Inverse; + + phaseShift.UpdateUnphasedFlag(); + } + + public static void PrintToChat(CommandHandler chat, PhaseShift phaseShift) + { + chat.SendSysMessage(CypherStrings.PhaseshiftStatus, phaseShift.Flags, phaseShift.PersonalGuid.ToString()); + if (!phaseShift.Phases.Empty()) + { + StringBuilder phases = new StringBuilder(); + string cosmetic = Global.ObjectMgr.GetCypherString(CypherStrings.PhaseFlagCosmetic, chat.GetSessionDbcLocale()); + string personal = Global.ObjectMgr.GetCypherString(CypherStrings.PhaseFlagPersonal, chat.GetSessionDbcLocale()); + foreach (PhaseRef phase in phaseShift.Phases) + { + phases.Append(phase.Id); + if (phase.Flags.HasFlag(PhaseFlags.Cosmetic)) + phases.Append(' ' + '(' + cosmetic + ')'); + if (phase.Flags.HasFlag(PhaseFlags.Personal)) + phases.Append(' ' + '(' + personal + ')'); + phases.Append(", "); + } + + chat.SendSysMessage(CypherStrings.PhaseshiftPhases, phases.ToString()); + } + + if (!phaseShift.VisibleMapIds.Empty()) + { + StringBuilder visibleMapIds = new StringBuilder(); + foreach (var visibleMapId in phaseShift.VisibleMapIds) + visibleMapIds.Append(visibleMapId.Key + ',' + ' '); + + chat.SendSysMessage(CypherStrings.PhaseshiftVisibleMapIds, visibleMapIds.ToString()); + } + + if (!phaseShift.UiWorldMapAreaIdSwaps.Empty()) + { + StringBuilder uiWorldMapAreaIdSwaps = new StringBuilder(); + foreach (var uiWorldMapAreaIdSwap in phaseShift.UiWorldMapAreaIdSwaps) + uiWorldMapAreaIdSwaps.Append(uiWorldMapAreaIdSwap.Key + ',' + ' '); + + chat.SendSysMessage(CypherStrings.PhaseshiftUiWorldMapAreaSwaps, uiWorldMapAreaIdSwaps.ToString()); + } + } + + public static string FormatPhases(PhaseShift phaseShift) + { + StringBuilder phases = new StringBuilder(); + foreach (var phase in phaseShift.Phases) + phases.Append(phase.Id + ','); + + return phases.ToString(); + } + + static void UpdateVisibilityIfNeeded(WorldObject obj, bool updateVisibility, bool changed) + { + if (changed && obj.IsInWorld) + { + Player player = obj.ToPlayer(); + if (player) + SendToPlayer(player); + + if (updateVisibility) + { + if (player) + player.GetMap().SendUpdateTransportVisibility(player); + + obj.UpdateObjectVisibility(); + } + } + } + } +} diff --git a/Source/Game/Server/WorldManager.cs b/Source/Game/Server/WorldManager.cs index 02a6d9437..40a00512e 100644 --- a/Source/Game/Server/WorldManager.cs +++ b/Source/Game/Server/WorldManager.cs @@ -357,6 +357,18 @@ namespace Game //Load weighted graph on taxi nodes path Global.TaxiPathGraph.Initialize(); + MultiMap mapData = new MultiMap(); + foreach (MapRecord mapEntry in CliDB.MapStorage.Values) + { + if (mapEntry.ParentMapID != -1) + mapData.Add((uint)mapEntry.ParentMapID, mapEntry.Id); + } + + Global.MapMgr.InitializeParentMapData(mapData); + + Global.VMapMgr.Initialize(mapData); + Global.MMapMgr.Initialize(mapData); + Log.outInfo(LogFilter.ServerLoading, "Loading SpellInfo Storage..."); Global.SpellMgr.LoadSpellInfoStore(); @@ -742,17 +754,7 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, "Loading World States..."); // must be loaded before Battleground, outdoor PvP and conditions LoadWorldStates(); - Log.outInfo(LogFilter.ServerLoading, "Loading Terrain Phase definitions..."); - Global.ObjectMgr.LoadTerrainPhaseInfo(); - - Log.outInfo(LogFilter.ServerLoading, "Loading Terrain Swap Default definitions..."); - Global.ObjectMgr.LoadTerrainSwapDefaults(); - - Log.outInfo(LogFilter.ServerLoading, "Loading Terrain World Map definitions..."); - Global.ObjectMgr.LoadTerrainWorldMaps(); - - Log.outInfo(LogFilter.ServerLoading, "Loading Phase Area definitions..."); - Global.ObjectMgr.LoadAreaPhases(); + Global.ObjectMgr.LoadPhases(); Log.outInfo(LogFilter.ServerLoading, "Loading Conditions..."); Global.ConditionMgr.LoadConditions(); diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 098eee5b7..83f0bb9a7 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -1283,28 +1283,17 @@ namespace Game.Spells Unit target = aurApp.GetTarget(); - var oldPhases = target.GetPhases(); - target.SetInPhase((uint)GetMiscValueB(), false, apply); - - // call functions which may have additional effects after chainging state of unit - // phase auras normally not expected at BG but anyway better check if (apply) { + PhasingHandler.AddPhase(target, (uint)GetMiscValueB(), true); + + // call functions which may have additional effects after chainging state of unit + // phase auras normally not expected at BG but anyway better check // drop flag at invisibiliy in bg target.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.ImmuneOrLostSelection); } - - Player player = target.ToPlayer(); - if (player) - { - if (player.IsInWorld) - player.GetMap().SendUpdateTransportVisibility(player, oldPhases); - player.SendUpdatePhasing(); - } - - // need triggering visibility update base at phase update of not GM invisible (other GMs anyway see in any phases) - if (target.IsVisible()) - target.UpdateObjectVisibility(); + else + PhasingHandler.RemovePhase(target, (uint)GetMiscValueB(), true); } [AuraEffectHandler(AuraType.PhaseGroup)] @@ -1314,29 +1303,18 @@ namespace Game.Spells return; Unit target = aurApp.GetTarget(); - var oldPhases = target.GetPhases(); - var phases = Global.DB2Mgr.GetPhasesForGroup((uint)GetMiscValueB()); - foreach (var phase in phases) - target.SetInPhase(phase, false, apply); - // call functions which may have additional effects after chainging state of unit - // phase auras normally not expected at BG but anyway better check + if (apply) { + PhasingHandler.AddPhaseGroup(target, (uint)GetMiscValueB(), true); + + // call functions which may have additional effects after chainging state of unit + // phase auras normally not expected at BG but anyway better check // drop flag at invisibiliy in bg target.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.ImmuneOrLostSelection); } - - Player player = target.ToPlayer(); - if (player) - { - if (player.IsInWorld) - player.GetMap().SendUpdateTransportVisibility(player, oldPhases); - player.SendUpdatePhasing(); - } - - // need triggering visibility update base at phase update of not GM invisible (other GMs anyway see in any phases) - if (target.IsVisible()) - target.UpdateObjectVisibility(); + else + PhasingHandler.RemovePhaseGroup(target, (uint)GetMiscValueB(), true); } /**********************/ diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 711aa0ca5..dbdca373d 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -794,15 +794,19 @@ namespace Game.Spells break; case Targets.DestCasterFishing: { - float min_dis = m_spellInfo.GetMinRange(true); - float max_dis = m_spellInfo.GetMaxRange(true); - float dis = (float)RandomHelper.NextDouble() * (max_dis - min_dis) + min_dis; + float minDist = m_spellInfo.GetMinRange(true); + float maxDist = m_spellInfo.GetMaxRange(true); + float dis = (float)RandomHelper.NextDouble() * (maxDist - minDist) + minDist; float x, y, z; float angle = (float)RandomHelper.NextDouble() * (MathFunctions.PI * 35.0f / 180.0f) - (float)(Math.PI * 17.5f / 180.0f); m_caster.GetClosePoint(out x, out y, out z, SharedConst.DefaultWorldObjectSize, dis, angle); - float ground = z; - float liquidLevel = m_caster.GetMap().GetWaterOrGroundLevel(m_caster.GetPhases(), x, y, z, ref ground); + float ground = m_caster.GetMap().GetHeight(m_caster.GetPhaseShift(), x, y, z, true, 50.0f); + float liquidLevel = MapConst.VMAPInvalidHeightValue; + LiquidData liquidData; + if (m_caster.GetMap().getLiquidStatus(m_caster.GetPhaseShift(), x, y, z, MapConst.MapAllLiquidTypes, out liquidData) != 0) + liquidLevel = liquidData.level; + if (liquidLevel <= ground) // When there is no liquid Map.GetWaterOrGroundLevel returns ground level { SendCastResult(SpellCastResult.NotHere); @@ -4458,11 +4462,11 @@ namespace Game.Spells if (m_caster.IsTypeId(TypeId.Player) && Global.VMapMgr.isLineOfSightCalcEnabled()) { if (m_spellInfo.HasAttribute(SpellAttr0.OutdoorsOnly) && - !m_caster.GetMap().IsOutdoors(m_caster.posX, m_caster.posY, m_caster.posZ)) + !m_caster.GetMap().IsOutdoors(m_caster.GetPhaseShift(), m_caster.posX, m_caster.posY, m_caster.posZ)) return SpellCastResult.OnlyOutdoors; if (m_spellInfo.HasAttribute(SpellAttr0.IndoorsOnly) && - m_caster.GetMap().IsOutdoors(m_caster.posX, m_caster.posY, m_caster.posZ)) + m_caster.GetMap().IsOutdoors(m_caster.GetPhaseShift(), m_caster.posX, m_caster.posY, m_caster.posZ)) return SpellCastResult.OnlyIndoors; } diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 3471a11ee..f63dad263 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -2857,7 +2857,7 @@ namespace Game.Spells if (!go) return; - go.CopyPhaseFrom(m_caster); + PhasingHandler.InheritPhaseShift(go, m_caster); int duration = m_spellInfo.CalcDuration(m_caster); @@ -2883,7 +2883,7 @@ namespace Game.Spells GameObject linkedTrap = go.GetLinkedTrap(); if (linkedTrap) { - linkedTrap.CopyPhaseFrom(m_caster); + PhasingHandler.InheritPhaseShift(linkedTrap, m_caster); linkedTrap.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0); linkedTrap.SetSpellId(m_spellInfo.Id); @@ -3518,7 +3518,7 @@ namespace Game.Spells if (!go) return; - go.CopyPhaseFrom(m_caster); + PhasingHandler.InheritPhaseShift(go, m_caster); go.SetUInt32Value(GameObjectFields.Faction, m_caster.getFaction()); go.SetUInt32Value(GameObjectFields.Level, m_caster.getLevel() + 1); @@ -3858,7 +3858,7 @@ namespace Game.Spells if (!go) return; - go.CopyPhaseFrom(m_caster); + PhasingHandler.InheritPhaseShift(go, m_caster); int duration = m_spellInfo.CalcDuration(m_caster); go.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0); @@ -4545,7 +4545,7 @@ namespace Game.Spells if (!go) return; - go.CopyPhaseFrom(m_caster); + PhasingHandler.InheritPhaseShift(go, m_caster); int duration = m_spellInfo.CalcDuration(m_caster); @@ -4603,7 +4603,7 @@ namespace Game.Spells GameObject linkedTrap = go.GetLinkedTrap(); if (linkedTrap != null) { - linkedTrap.CopyPhaseFrom(m_caster); + PhasingHandler.InheritPhaseShift(linkedTrap, m_caster); linkedTrap.SetRespawnTime(duration > 0 ? duration / Time.InMilliseconds : 0); linkedTrap.SetSpellId(m_spellInfo.Id); linkedTrap.SetOwnerGUID(m_caster.GetGUID()); @@ -5656,7 +5656,7 @@ namespace Game.Spells if (!unitTarget || !unitTarget.IsTypeId(TypeId.Player)) return; - unitTarget.UpdateAreaAndZonePhase(); + PhasingHandler.OnConditionChange(unitTarget); } [SpellEffectHandler(SpellEffectName.UpdateZoneAurasPhases)] diff --git a/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossAmanitar.cs b/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossAmanitar.cs index 4266c70b5..2cd4cd1c4 100644 --- a/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossAmanitar.cs +++ b/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossAmanitar.cs @@ -102,23 +102,19 @@ namespace Scripts.Northrend.AzjolNerub.Ahnkahet.Amanitar for (byte i = 0; i < 30; ++i) { Position pos = me.GetRandomNearPosition(30.0f); - pos.posZ = me.GetMap().GetHeight(pos.GetPositionX(), pos.GetPositionY(), MapConst.MaxHeight) + 2.0f; + me.UpdateGroundPositionZ(pos.GetPositionX(), pos.GetPositionY(), ref pos.posZ); Creature trigger = me.SummonCreature(CreatureIds.Trigger, pos); if (trigger) { Creature temp1 = trigger.FindNearestCreature(CreatureIds.HealthyMushroom, 4.0f, true); Creature temp2 = trigger.FindNearestCreature(CreatureIds.PoisonousMushroom, 4.0f, true); - if (temp1 || temp2) - { - trigger.DisappearAndDie(); - } - else + if (!temp1 && !temp2) { u = 1 - u; - trigger.DisappearAndDie(); me.SummonCreature(u > 0 ? CreatureIds.PoisonousMushroom : CreatureIds.HealthyMushroom, pos, TempSummonType.TimedOrCorpseDespawn, 60 * Time.InMilliseconds); } + trigger.DespawnOrUnsummon(); } } } diff --git a/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossHeraldVolazj.cs b/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossHeraldVolazj.cs index 866cc3ec7..4bf097981 100644 --- a/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossHeraldVolazj.cs +++ b/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossHeraldVolazj.cs @@ -23,6 +23,7 @@ using Game.Scripting; using Game.Spells; using System.Collections.Generic; using System.Linq; +using Game; namespace Scripts.Northrend.AzjolNerub.Ahnkahet.HeraldVolazj { @@ -133,7 +134,7 @@ namespace Scripts.Northrend.AzjolNerub.Ahnkahet.HeraldVolazj // clone player.CastSpell(summon, SpellIds.ClonePlayer, true); // phase the summon - summon.SetInPhase((uint)spellInfo.GetEffect(0).MiscValueB, true, true); + PhasingHandler.AddPhase(summon, (uint)spellInfo.GetEffect(0).MiscValueB, true); } } ++insanityHandled; @@ -158,9 +159,9 @@ namespace Scripts.Northrend.AzjolNerub.Ahnkahet.HeraldVolazj instance.DoStopCriteriaTimer(CriteriaTimedTypes.Event, Misc.AchievQuickDemiseStartEvent); // Visible for all players in insanity - me.SetInPhase(169, true, true); for (uint i = 173; i <= 177; ++i) - me.SetInPhase(i, true, true); + PhasingHandler.AddPhase(me, i, false); + PhasingHandler.AddPhase(me, 169, true); ResetPlayersPhase(); @@ -199,7 +200,7 @@ namespace Scripts.Northrend.AzjolNerub.Ahnkahet.HeraldVolazj return; else { - nextPhase = visage.GetPhases().First(); + nextPhase = visage.GetPhaseShift().GetPhases().First().Id; break; } } diff --git a/Source/Scripts/Northrend/Ulduar/FlameLeviathan.cs b/Source/Scripts/Northrend/Ulduar/FlameLeviathan.cs index 5dbfca494..79adfa599 100644 --- a/Source/Scripts/Northrend/Ulduar/FlameLeviathan.cs +++ b/Source/Scripts/Northrend/Ulduar/FlameLeviathan.cs @@ -721,7 +721,7 @@ namespace Scripts.Northrend.Ulduar.FlameLeviathan { float x, y, z; me.GetPosition(out x, out y, out z); - z = me.GetMap().GetHeight(me.GetPhases(), x, y, z); + z = me.GetMap().GetHeight(me.GetPhaseShift(), x, y, z); me.GetMotionMaster().MovePoint(0, x, y, z); me.SetPosition(x, y, z, 0); } diff --git a/sql/updates/world/master/2018_03_25_00_world.sql b/sql/updates/world/master/2018_03_25_00_world.sql new file mode 100644 index 000000000..83f9ca2a5 --- /dev/null +++ b/sql/updates/world/master/2018_03_25_00_world.sql @@ -0,0 +1,22 @@ +ALTER TABLE `creature` + ADD `phaseUseFlags` tinyint(3) unsigned NOT NULL DEFAULT '0' AFTER `spawnMask`, + ADD `terrainSwapMap` int(11) NOT NULL DEFAULT '-1' AFTER `PhaseGroup`; + +ALTER TABLE `gameobject` + ADD `phaseUseFlags` tinyint(3) unsigned NOT NULL DEFAULT '0' AFTER `spawnMask`, + ADD `terrainSwapMap` int(11) NOT NULL DEFAULT '-1' AFTER `PhaseGroup`; + +DROP TABLE `terrain_phase_info`; + +ALTER TABLE `transports` ADD `phaseUseFlags` tinyint(3) unsigned NOT NULL DEFAULT '0' AFTER `name`; + +DELETE FROM `trinity_string` WHERE `entry` IN (101,178,179,180,181,182,183,184,185); +INSERT INTO `trinity_string` (`entry`,`content_default`) VALUES +(101,'Map: %u (%s) Zone: %u (%s) Area: %u (%s)\nX: %f Y: %f Z: %f Orientation: %f'), +(178,'grid[%u,%u]cell[%u,%u] InstanceID: %u\n ZoneX: %f ZoneY: %f\nGroundZ: %f FloorZ: %f Have height data (Map: %u VMap: %u MMap: %u)'), +(179,'PhaseShift:\n* Flags %u, PersonalGuid: %s'), +(180,'* Phases: %s'), +(181,'* VisibleMapIds: %s'), +(182,'* UiWorldMapAreaSwaps: %s'), +(183,'Cosmetic'), +(184,'Personal');