diff --git a/Source/Framework/Constants/MapConst.cs b/Source/Framework/Constants/MapConst.cs index 3889ca6d5..c78f5a6f7 100644 --- a/Source/Framework/Constants/MapConst.cs +++ b/Source/Framework/Constants/MapConst.cs @@ -205,16 +205,20 @@ namespace Framework.Constants { Creature = 0, GameObject = 1, + AreaTrigger = 2, - Max + NumSpawnTypesWithData, + NumSpawnTypes } public enum SpawnObjectTypeMask { Creature = (1 << SpawnObjectType.Creature), GameObject = (1 << SpawnObjectType.GameObject), + AreaTrigger = (1 << SpawnObjectType.AreaTrigger), - All = (1 << SpawnObjectType.Max) - 1 + WithData = (1 << SpawnObjectType.NumSpawnTypesWithData) - 1, + All = (1 << SpawnObjectType.NumSpawnTypes) - 1 } [Flags] diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index 2ff1799a8..7219be3e6 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -866,25 +866,6 @@ namespace Framework.Constants DisplayMythic = 0x80 // Controls icon displayed on minimap when inside the instance } - public enum SpawnMask - { - Continent = (1 << Difficulty.None), // Any Maps Without Spawn Modes - - DungeonNormal = (1 << Difficulty.Normal), - DungeonHeroic = (1 << Difficulty.Heroic), - DungeonAll = (DungeonNormal | DungeonHeroic), - - Raid10Normal = (1 << Difficulty.Raid10N), - Raid25Normal = (1 << Difficulty.Raid25N), - RaidNormalAll = (Raid10Normal | Raid25Normal), - - Raid10Heroic = (1 << Difficulty.Raid10HC), - Raid25Heroic = (1 << Difficulty.Raid25HC), - RaidHeroicAll = (Raid10Heroic | Raid25Heroic), - - RaidAll = (RaidNormalAll | RaidHeroicAll) - } - public enum MapFlags { CanToggleDifficulty = 0x0100, diff --git a/Source/Game/Chat/Commands/GameObjectCommands.cs b/Source/Game/Chat/Commands/GameObjectCommands.cs index b5baa9a46..17f8e901c 100644 --- a/Source/Game/Chat/Commands/GameObjectCommands.cs +++ b/Source/Game/Chat/Commands/GameObjectCommands.cs @@ -209,7 +209,7 @@ namespace Game.Chat if (data != null) { data.rotation.toEulerAnglesZYX(out float yaw, out float pitch, out float roll); - handler.SendSysMessage(CypherStrings.SpawninfoSpawnidLocation, data.spawnId, data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY(), data.spawnPoint.GetPositionZ()); + handler.SendSysMessage(CypherStrings.SpawninfoSpawnidLocation, data.SpawnId, data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY(), data.SpawnPoint.GetPositionZ()); handler.SendSysMessage(CypherStrings.SpawninfoRotation, yaw, pitch, roll); } diff --git a/Source/Game/Chat/Commands/GoCommands.cs b/Source/Game/Chat/Commands/GoCommands.cs index 40589c59a..40c368531 100644 --- a/Source/Game/Chat/Commands/GoCommands.cs +++ b/Source/Game/Chat/Commands/GoCommands.cs @@ -645,9 +645,9 @@ namespace Game.Chat.Commands return false; } - if (!GridDefines.IsValidMapCoord(goData.spawnPoint) || Global.ObjectMgr.IsTransportMap(goData.spawnPoint.GetMapId())) + if (!GridDefines.IsValidMapCoord(goData.MapId, goData.SpawnPoint) || Global.ObjectMgr.IsTransportMap(goData.MapId)) { - handler.SendSysMessage(CypherStrings.InvalidTargetCoord, goData.spawnPoint.GetPositionX(), goData.spawnPoint.GetPositionY(), goData.spawnPoint.GetMapId()); + handler.SendSysMessage(CypherStrings.InvalidTargetCoord, goData.SpawnPoint.GetPositionX(), goData.SpawnPoint.GetPositionY(), goData.MapId); return false; } @@ -657,7 +657,7 @@ namespace Game.Chat.Commands else player.SaveRecallPosition(); // save only in non-flight case - player.TeleportTo(goData.spawnPoint); + player.TeleportTo(new WorldLocation(goData.MapId, goData.SpawnPoint)); return true; } diff --git a/Source/Game/Chat/Commands/ListCommands.cs b/Source/Game/Chat/Commands/ListCommands.cs index f2fabfd94..77e47d285 100644 --- a/Source/Game/Chat/Commands/ListCommands.cs +++ b/Source/Game/Chat/Commands/ListCommands.cs @@ -580,31 +580,37 @@ namespace Game.Chat.Commands uint zoneId = player.GetZoneId(); string zoneName = GetZoneName(zoneId, locale); - for (SpawnObjectType type = 0; type < SpawnObjectType.Max; type++) + for (SpawnObjectType type = 0; type < SpawnObjectType.NumSpawnTypes; type++) { if (range != 0) handler.SendSysMessage(CypherStrings.ListRespawnsRange, type, range); else handler.SendSysMessage(CypherStrings.ListRespawnsZone, type, zoneName, zoneId); + handler.SendSysMessage(CypherStrings.ListRespawnsListheader); List respawns = new(); map.GetRespawnInfo(respawns, (SpawnObjectTypeMask)(1 << (int)type)); foreach (RespawnInfo ri in respawns) { - SpawnData data = Global.ObjectMgr.GetSpawnData(ri.type, ri.spawnId); + SpawnMetadata data = Global.ObjectMgr.GetSpawnMetadata(ri.type, ri.spawnId); if (data == null) continue; - uint respawnZoneId = map.GetZoneId(PhasingHandler.EmptyPhaseShift, data.spawnPoint); - if (range != 0) + uint respawnZoneId = 0; + SpawnData edata = data.ToSpawnData(); + if (edata != null) { - if (!player.IsInDist(data.spawnPoint, range)) - continue; - } - else - { - if (zoneId != respawnZoneId) - continue; + respawnZoneId = map.GetZoneId(PhasingHandler.EmptyPhaseShift, edata.SpawnPoint); + if (range != 0) + { + if (!player.IsInDist(edata.SpawnPoint, range)) + continue; + } + else + { + if (zoneId != respawnZoneId) + continue; + } } uint gridY = ri.gridId / MapConst.MaxGrids; @@ -652,28 +658,28 @@ namespace Game.Chat.Commands foreach (var pair in Global.ObjectMgr.GetAllCreatureData()) { SpawnData data = pair.Value; - if (data.spawnPoint.GetMapId() != mapId) + if (data.MapId != mapId) continue; CreatureTemplate cTemp = Global.ObjectMgr.GetCreatureTemplate(data.Id); if (cTemp == null) continue; - if (showAll || data.spawnPoint.IsInDist2d(player, 5000.0f)) - handler.SendSysMessage($"Type: {data.type} | SpawnId: {data.spawnId} | Entry: {data.Id} ({cTemp.Name}) | X: {data.spawnPoint.GetPositionX():3} | Y: {data.spawnPoint.GetPositionY():3} | Z: {data.spawnPoint.GetPositionZ():3}"); + if (showAll || data.SpawnPoint.IsInDist2d(player, 5000.0f)) + handler.SendSysMessage($"Type: {data.type} | SpawnId: {data.SpawnId} | Entry: {data.Id} ({cTemp.Name}) | X: {data.SpawnPoint.GetPositionX():3} | Y: {data.SpawnPoint.GetPositionY():3} | Z: {data.SpawnPoint.GetPositionZ():3}"); } foreach (var pair in Global.ObjectMgr.GetAllGameObjectData()) { SpawnData data = pair.Value; - if (data.spawnPoint.GetMapId() != mapId) + if (data.MapId != mapId) continue; GameObjectTemplate goTemp = Global.ObjectMgr.GetGameObjectTemplate(data.Id); if (goTemp == null) continue; - if (showAll || data.spawnPoint.IsInDist2d(player, 5000.0f)) - handler.SendSysMessage($"Type: {data.type} | SpawnId: {data.spawnId} | Entry: {data.Id} ({goTemp.name}) | X: {data.spawnPoint.GetPositionX():3} | Y: {data.spawnPoint.GetPositionY():3} | Z: {data.spawnPoint.GetPositionZ():3}"); + if (showAll || data.SpawnPoint.IsInDist2d(player, 5000.0f)) + handler.SendSysMessage($"Type: {data.type} | SpawnId: {data.SpawnId} | Entry: {data.Id} ({goTemp.name}) | X: {data.SpawnPoint.GetPositionX():3} | Y: {data.SpawnPoint.GetPositionY():3} | Z: {data.SpawnPoint.GetPositionZ():3}"); } return true; } diff --git a/Source/Game/Chat/Commands/NPCCommands.cs b/Source/Game/Chat/Commands/NPCCommands.cs index b41d83137..e2922ce2d 100644 --- a/Source/Game/Chat/Commands/NPCCommands.cs +++ b/Source/Game/Chat/Commands/NPCCommands.cs @@ -172,7 +172,7 @@ namespace Game.Chat CreatureData data = Global.ObjectMgr.GetCreatureData(target.GetSpawnId()); if (data != null) - handler.SendSysMessage(CypherStrings.NpcinfoPhases, data.phaseId, data.phaseGroup); + handler.SendSysMessage(CypherStrings.NpcinfoPhases, data.PhaseId, data.PhaseGroup); PhasingHandler.PrintToChat(handler, target.GetPhaseShift()); @@ -233,14 +233,14 @@ namespace Game.Chat return false; } - if (player.GetMapId() != data.spawnPoint.GetMapId()) + if (player.GetMapId() != data.MapId) { handler.SendSysMessage(CypherStrings.CommandCreatureatsamemap, lowguid); return false; } Global.ObjectMgr.RemoveCreatureFromGrid(lowguid, data); - data.spawnPoint.Relocate(player); + data.SpawnPoint.Relocate(player); Global.ObjectMgr.AddCreatureToGrid(lowguid, data); // update position in DB @@ -659,9 +659,9 @@ namespace Game.Chat { ulong guid = map.GenerateLowGuid(HighGuid.Creature); CreatureData data = Global.ObjectMgr.NewOrExistCreatureData(guid); - data.spawnId = guid; + data.SpawnId = guid; data.Id = id; - data.spawnPoint.Relocate(chr.GetTransOffsetX(), chr.GetTransOffsetY(), chr.GetTransOffsetZ(), chr.GetTransOffsetO()); + data.SpawnPoint.Relocate(chr.GetTransOffsetX(), chr.GetTransOffsetY(), chr.GetTransOffsetZ(), chr.GetTransOffsetO()); data.spawnGroupData = new(); Creature creaturePassenger = trans.CreateNPCPassenger(guid, data); diff --git a/Source/Game/DataStorage/AreaTriggerDataStorage.cs b/Source/Game/DataStorage/AreaTriggerDataStorage.cs index 3af3c274d..b8a5e7f42 100644 --- a/Source/Game/DataStorage/AreaTriggerDataStorage.cs +++ b/Source/Game/DataStorage/AreaTriggerDataStorage.cs @@ -319,10 +319,11 @@ namespace Game.DataStorage AreaTriggerSpawn spawn = new(); spawn.SpawnId = spawnId; - spawn.Id = areaTriggerId; - spawn.Location = new WorldLocation(location); + spawn.MapId = location.GetMapId(); + spawn.TriggerId = areaTriggerId; + spawn.SpawnPoint = new Position(location); - spawn.PhaseUseFlags = templates.Read(8); + spawn.PhaseUseFlags = (PhaseUseFlagsValues)templates.Read(8); spawn.PhaseId = templates.Read(9); spawn.PhaseGroup = templates.Read(10); @@ -334,13 +335,14 @@ namespace Game.DataStorage } spawn.ScriptId = Global.ObjectMgr.GetScriptId(templates.Read(18)); + spawn.spawnGroupData = Global.ObjectMgr.GetLegacySpawnGroup(); // Add the trigger to a map::cell map, which is later used by GridLoader to query - CellCoord cellCoord = GridDefines.ComputeCellCoord(spawn.Location.GetPositionX(), spawn.Location.GetPositionY()); - if (!_areaTriggerSpawnsByLocation.ContainsKey((spawn.Location.GetMapId(), cellCoord.GetId()))) - _areaTriggerSpawnsByLocation[(spawn.Location.GetMapId(), cellCoord.GetId())] = new SortedSet(); + CellCoord cellCoord = GridDefines.ComputeCellCoord(spawn.SpawnPoint.GetPositionX(), spawn.SpawnPoint.GetPositionY()); + if (!_areaTriggerSpawnsByLocation.ContainsKey((spawn.MapId, cellCoord.GetId()))) + _areaTriggerSpawnsByLocation[(spawn.MapId, cellCoord.GetId())] = new SortedSet(); - _areaTriggerSpawnsByLocation[(spawn.Location.GetMapId(), cellCoord.GetId())].Add(spawnId); + _areaTriggerSpawnsByLocation[(spawn.MapId, cellCoord.GetId())].Add(spawnId); // add the position to the map _areaTriggerSpawnsBySpawnId[spawnId] = spawn; diff --git a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs index 95cfa28e6..ded4a1c2a 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs @@ -53,6 +53,9 @@ namespace Game.Entities if (!IsInWorld) { GetMap().GetObjectsStore().Add(GetGUID(), this); + if (_spawnId != 0) + GetMap().GetAreaTriggerBySpawnIdStore().Add(_spawnId, this); + base.AddToWorld(); } } @@ -74,6 +77,9 @@ namespace Game.Entities _ai.OnRemove(); base.RemoveFromWorld(); + if (_spawnId != 0) + GetMap().GetAreaTriggerBySpawnIdStore().Remove(_spawnId, this); + GetMap().GetObjectsStore().Remove(GetGUID()); } } @@ -236,7 +242,7 @@ namespace Game.Entities if (position == null) return false; - AreaTriggerTemplate areaTriggerTemplate = Global.AreaTriggerDataStorage.GetAreaTriggerTemplate(position.Id); + AreaTriggerTemplate areaTriggerTemplate = Global.AreaTriggerDataStorage.GetAreaTriggerTemplate(position.TriggerId); if (areaTriggerTemplate == null) return false; @@ -246,7 +252,7 @@ namespace Game.Entities bool CreateServer(Map map, AreaTriggerTemplate areaTriggerTemplate, AreaTriggerSpawn position) { SetMap(map); - Relocate(position.Location); + Relocate(position.SpawnPoint); if (!IsPositionValid()) { Log.outError(LogFilter.AreaTrigger, $"AreaTriggerServer (id {areaTriggerTemplate.Id}) not created. Invalid coordinates (X: {GetPositionX()} Y: {GetPositionY()})"); diff --git a/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs b/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs index c01ce38b1..25aaea68c 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTriggerTemplate.cs @@ -17,6 +17,7 @@ using Framework.Constants; using Framework.Dynamic; +using Game.Maps; using Game.Networking; using System; using System.Collections.Generic; @@ -319,18 +320,12 @@ namespace Game.Entities public uint ScriptId; } - public class AreaTriggerSpawn + public class AreaTriggerSpawn : SpawnData { - public ulong SpawnId; - public AreaTriggerId Id; - public WorldLocation Location; - public uint PhaseId; - public uint PhaseGroup; - public byte PhaseUseFlags; - + public AreaTriggerId TriggerId; public AreaTriggerShapeInfo Shape = new(); - public uint ScriptId; + public AreaTriggerSpawn() : base(SpawnObjectType.AreaTrigger) { } } public struct AreaTriggerAction diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 42d906ad0..5d2121559 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -793,7 +793,7 @@ namespace Game.Entities if (data != null) { - PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup); + PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.PhaseUseFlags, data.PhaseId, data.PhaseGroup); PhasingHandler.InitDbVisibleMapId(GetPhaseShift(), data.terrainSwapMap); } @@ -1246,18 +1246,24 @@ namespace Game.Entities dynamicflags = 0; } - if (data.spawnId == 0) - data.spawnId = m_spawnId; - Cypher.Assert(data.spawnId == m_spawnId); + if (data.SpawnId == 0) + data.SpawnId = m_spawnId; + Cypher.Assert(data.SpawnId == m_spawnId); data.Id = GetEntry(); data.displayid = displayId; data.equipmentId = (sbyte)GetCurrentEquipmentId(); if (GetTransport() == null) - data.spawnPoint.WorldRelocate(this); + { + data.MapId = GetMapId(); + data.SpawnPoint.Relocate(this); + } else - data.spawnPoint.WorldRelocate(mapid, GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO()); + { + data.MapId = mapid; + data.SpawnPoint.Relocate(GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO()); + } data.spawntimesecs = (int)m_respawnDelay; // prevent add data integrity problems @@ -1277,8 +1283,8 @@ namespace Game.Entities if (data.spawnGroupData == null) data.spawnGroupData = Global.ObjectMgr.GetDefaultSpawnGroup(); - data.phaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.phaseId; - data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup; + data.PhaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.PhaseId; + data.PhaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.PhaseGroup; // update in DB SQLTransaction trans = new(); @@ -1294,8 +1300,8 @@ namespace Game.Entities stmt.AddValue(index++, GetEntry()); stmt.AddValue(index++, mapid); stmt.AddValue(index++, data.spawnDifficulties.Empty() ? "" : string.Join(',', data.spawnDifficulties)); - stmt.AddValue(index++, data.phaseId); - stmt.AddValue(index++, data.phaseGroup); + stmt.AddValue(index++, data.PhaseId); + stmt.AddValue(index++, data.PhaseGroup); stmt.AddValue(index++, displayId); stmt.AddValue(index++, GetCurrentEquipmentId()); stmt.AddValue(index++, GetPositionX()); @@ -1610,7 +1616,7 @@ namespace Game.Entities SQLTransaction trans = new(); - Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map => + Global.MapMgr.DoForAllMapsWithMapId(data.MapId, map => { // despawn all active creatures, and remove their respawns List toUnload = new(); @@ -2448,7 +2454,7 @@ namespace Game.Entities { if (m_creatureData != null) { - m_creatureData.spawnPoint.GetPosition(out x, out y, out z, out ori); + m_creatureData.SpawnPoint.GetPosition(out x, out y, out z, out ori); dist = m_creatureData.spawndist; } else @@ -2459,7 +2465,7 @@ namespace Game.Entities } } - bool IsSpawnedOnTransport() { return m_creatureData != null && m_creatureData.spawnPoint.GetMapId() != GetMapId(); } + bool IsSpawnedOnTransport() { return m_creatureData != null && m_creatureData.MapId != GetMapId(); } void InitializeMovementFlags() { @@ -3157,7 +3163,7 @@ namespace Game.Entities m_respawnradius = data.spawndist; m_respawnDelay = (uint)data.spawntimesecs; - if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.Id, data.spawnPoint, data, 0, !m_respawnCompatibilityMode)) + if (!Create(map.GenerateLowGuid(HighGuid.Creature), map, data.Id, data.SpawnPoint, data, 0, !m_respawnCompatibilityMode)) return false; //We should set first home position, because then AI calls home movement @@ -3198,9 +3204,9 @@ namespace Game.Entities m_deathState = DeathState.Dead; if (CanFly()) { - float tz = map.GetHeight(GetPhaseShift(), data.spawnPoint, true, MapConst.MaxFallDistance); - if (data.spawnPoint.GetPositionZ() - tz > 0.1f && GridDefines.IsValidMapCoord(tz)) - Relocate(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY(), tz); + float tz = map.GetHeight(GetPhaseShift(), data.SpawnPoint, true, MapConst.MaxFallDistance); + if (data.SpawnPoint.GetPositionZ() - tz > 0.1f && GridDefines.IsValidMapCoord(tz)) + Relocate(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY(), tz); } } diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index 05a4de018..f36391212 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -1034,12 +1034,13 @@ namespace Game.Entities // update in loaded data (changing data only in this place) GameObjectData data = Global.ObjectMgr.NewOrExistGameObjectData(m_spawnId); - if (data.spawnId == 0) - data.spawnId = m_spawnId; - Cypher.Assert(data.spawnId == m_spawnId); + if (data.SpawnId == 0) + data.SpawnId = m_spawnId; + Cypher.Assert(data.SpawnId == m_spawnId); data.Id = GetEntry(); - data.spawnPoint.WorldRelocate(this); + data.MapId = GetMapId(); + data.SpawnPoint.Relocate(this); data.rotation = m_localRotation; data.spawntimesecs = (int)(m_spawnedByDefault ? m_respawnDelayTime : -m_respawnDelayTime); data.animprogress = GetGoAnimProgress(); @@ -1049,8 +1050,8 @@ namespace Game.Entities if (data.spawnGroupData == null) data.spawnGroupData = Global.ObjectMgr.GetDefaultSpawnGroup(); - data.phaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.phaseId; - data.phaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.phaseGroup; + data.PhaseId = GetDBPhase() > 0 ? (uint)GetDBPhase() : data.PhaseId; + data.PhaseGroup = GetDBPhase() < 0 ? (uint)-GetDBPhase() : data.PhaseGroup; // Update in DB byte index = 0; @@ -1063,8 +1064,8 @@ namespace Game.Entities stmt.AddValue(index++, GetEntry()); stmt.AddValue(index++, mapid); stmt.AddValue(index++, data.spawnDifficulties.Empty() ? "" : string.Join(",", data.spawnDifficulties)); - stmt.AddValue(index++, data.phaseId); - stmt.AddValue(index++, data.phaseGroup); + stmt.AddValue(index++, data.PhaseId); + stmt.AddValue(index++, data.PhaseGroup); stmt.AddValue(index++, GetPositionX()); stmt.AddValue(index++, GetPositionY()); stmt.AddValue(index++, GetPositionZ()); @@ -1096,10 +1097,10 @@ namespace Game.Entities m_spawnId = spawnId; m_respawnCompatibilityMode = ((data.spawnGroupData.flags & SpawnGroupFlags.CompatibilityMode) != 0); - if (!Create(entry, map, data.spawnPoint, data.rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode, spawnId)) + if (!Create(entry, map, data.SpawnPoint, data.rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode, spawnId)) return false; - PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup); + PhasingHandler.InitDbPhaseShift(GetPhaseShift(), data.PhaseUseFlags, data.PhaseId, data.PhaseGroup); PhasingHandler.InitDbVisibleMapId(GetPhaseShift(), data.terrainSwapMap); if (data.spawntimesecs >= 0) @@ -1154,7 +1155,7 @@ namespace Game.Entities SQLTransaction trans = new(); - Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map => + Global.MapMgr.DoForAllMapsWithMapId(data.MapId, map => { // despawn all active objects, and remove their respawns List toUnload = new(); @@ -2805,7 +2806,7 @@ namespace Game.Entities public void GetRespawnPosition(out float x, out float y, out float z, out float ori) { if (m_goData != null) - m_goData.spawnPoint.GetPosition(out x, out y, out z, out ori); + m_goData.SpawnPoint.GetPosition(out x, out y, out z, out ori); else GetPosition(out x, out y, out z, out ori); } diff --git a/Source/Game/Entities/Transport.cs b/Source/Game/Entities/Transport.cs index 91f4a7300..0421d0ba3 100644 --- a/Source/Game/Entities/Transport.cs +++ b/Source/Game/Entities/Transport.cs @@ -326,7 +326,7 @@ namespace Game.Entities return null; float x, y, z, o; - data.spawnPoint.GetPosition(out x, out y, out z, out o); + data.SpawnPoint.GetPosition(out x, out y, out z, out o); creature.SetTransport(this); creature.m_movementInfo.transport.guid = GetGUID(); @@ -347,7 +347,7 @@ namespace Game.Entities return null; } - PhasingHandler.InitDbPhaseShift(creature.GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup); + PhasingHandler.InitDbPhaseShift(creature.GetPhaseShift(), data.PhaseUseFlags, data.PhaseId, data.PhaseGroup); PhasingHandler.InitDbVisibleMapId(creature.GetPhaseShift(), data.terrainSwapMap); if (!map.AddToMap(creature)) @@ -369,7 +369,7 @@ namespace Game.Entities return null; float x, y, z, o; - data.spawnPoint.GetPosition(out x, out y, out z, out o); + data.SpawnPoint.GetPosition(out x, out y, out z, out o); go.SetTransport(this); go.m_movementInfo.transport.guid = GetGUID(); @@ -385,7 +385,7 @@ namespace Game.Entities return null; } - PhasingHandler.InitDbPhaseShift(go.GetPhaseShift(), data.phaseUseFlags, data.phaseId, data.phaseGroup); + PhasingHandler.InitDbPhaseShift(go.GetPhaseShift(), data.PhaseUseFlags, data.PhaseId, data.PhaseGroup); PhasingHandler.InitDbVisibleMapId(go.GetPhaseShift(), data.terrainSwapMap); if (!map.AddToMap(go)) diff --git a/Source/Game/Events/GameEventManager.cs b/Source/Game/Events/GameEventManager.cs index 810e463aa..ff5d24354 100644 --- a/Source/Game/Events/GameEventManager.cs +++ b/Source/Game/Events/GameEventManager.cs @@ -1124,7 +1124,7 @@ namespace Game // get the creature data from the low guid to get the entry, to be able to find out the whole guid CreatureData data = Global.ObjectMgr.GetCreatureData(guid); if (data != null) - creaturesByMap.Add(data.spawnPoint.GetMapId(), guid); + creaturesByMap.Add(data.MapId, guid); } foreach (var key in creaturesByMap.Keys) @@ -1191,10 +1191,10 @@ namespace Game Global.ObjectMgr.AddCreatureToGrid(guid, data); // Spawn if necessary (loaded grids only) - Map map = Global.MapMgr.CreateBaseMap(data.spawnPoint.GetMapId()); + Map map = Global.MapMgr.CreateBaseMap(data.MapId); map.RemoveRespawnTime(SpawnObjectType.Creature, guid); // We use spawn coords to spawn - if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint)) + if (map != null && !map.Instanceable() && map.IsGridLoaded(data.SpawnPoint)) Creature.CreateCreatureFromDB(guid, map); } } @@ -1215,10 +1215,10 @@ namespace Game Global.ObjectMgr.AddGameObjectToGrid(guid, data); // Spawn if necessary (loaded grids only) // this base map checked as non-instanced and then only existed - Map map = Global.MapMgr.CreateBaseMap(data.spawnPoint.GetMapId()); + Map map = Global.MapMgr.CreateBaseMap(data.MapId); map.RemoveRespawnTime(SpawnObjectType.GameObject, guid); // We use current coords to unspawn, not spawn coords since creature can have changed grid - if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint)) + if (map != null && !map.Instanceable() && map.IsGridLoaded(data.SpawnPoint)) { GameObject pGameobject = GameObject.CreateGameObjectFromDB(guid, map, false); // @todo find out when it is add to map @@ -1266,7 +1266,7 @@ namespace Game { Global.ObjectMgr.RemoveCreatureFromGrid(guid, data); - Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map => + Global.MapMgr.DoForAllMapsWithMapId(data.MapId, map => { map.RemoveRespawnTime(SpawnObjectType.Creature, guid); var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid); @@ -1294,7 +1294,7 @@ namespace Game { Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data); - Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map => + Global.MapMgr.DoForAllMapsWithMapId(data.MapId, map => { map.RemoveRespawnTime(SpawnObjectType.GameObject, guid); var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid); @@ -1325,7 +1325,7 @@ namespace Game continue; // Update if spawned - Global.MapMgr.DoForAllMapsWithMapId(data.spawnPoint.GetMapId(), map => + Global.MapMgr.DoForAllMapsWithMapId(data.MapId, map => { var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(tuple.Item1); foreach (var creature in creatureBounds) diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index 98a58b380..61f743df0 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -2892,8 +2892,8 @@ namespace Game break; } - MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); - if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) + MapRecord map = CliDB.MapStorage.LookupByKey(master.MapId); + if (map == null || !map.Instanceable() || (master.MapId != slave.MapId)) { Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -2908,8 +2908,8 @@ namespace Game break; } - guid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, guidLow); - linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + guid = ObjectGuid.Create(HighGuid.Creature, slave.MapId, slave.Id, guidLow); + linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.MapId, master.Id, linkedGuidLow); break; } case CreatureLinkedRespawnType.CreatureToGO: @@ -2930,8 +2930,8 @@ namespace Game break; } - MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); - if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) + MapRecord map = CliDB.MapStorage.LookupByKey(master.MapId); + if (map == null || !map.Instanceable() || (master.MapId != slave.MapId)) { Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -2946,8 +2946,8 @@ namespace Game break; } - guid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, guidLow); - linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + guid = ObjectGuid.Create(HighGuid.Creature, slave.MapId, slave.Id, guidLow); + linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.MapId, master.Id, linkedGuidLow); break; } case CreatureLinkedRespawnType.GOToGO: @@ -2968,8 +2968,8 @@ namespace Game break; } - MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); - if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) + MapRecord map = CliDB.MapStorage.LookupByKey(master.MapId); + if (map == null || !map.Instanceable() || (master.MapId != slave.MapId)) { Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -2984,8 +2984,8 @@ namespace Game break; } - guid = ObjectGuid.Create(HighGuid.GameObject, slave.spawnPoint.GetMapId(), slave.Id, guidLow); - linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + guid = ObjectGuid.Create(HighGuid.GameObject, slave.MapId, slave.Id, guidLow); + linkedGuid = ObjectGuid.Create(HighGuid.GameObject, master.MapId, master.Id, linkedGuidLow); break; } case CreatureLinkedRespawnType.GOToCreature: @@ -3006,8 +3006,8 @@ namespace Game break; } - MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); - if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) + MapRecord map = CliDB.MapStorage.LookupByKey(master.MapId); + if (map == null || !map.Instanceable() || (master.MapId != slave.MapId)) { Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -3022,8 +3022,8 @@ namespace Game break; } - guid = ObjectGuid.Create(HighGuid.GameObject, slave.spawnPoint.GetMapId(), slave.Id, guidLow); - linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, linkedGuidLow); + guid = ObjectGuid.Create(HighGuid.GameObject, slave.MapId, slave.Id, guidLow); + linkedGuid = ObjectGuid.Create(HighGuid.Creature, master.MapId, master.Id, linkedGuidLow); break; } @@ -3408,9 +3408,10 @@ namespace Game } CreatureData data = new(); - data.spawnId = guid; + data.SpawnId = guid; data.Id = entry; - data.spawnPoint = new WorldLocation(result.Read(2), result.Read(3), result.Read(4), result.Read(5), result.Read(6)); + data.MapId = result.Read(2); + data.SpawnPoint = new Position(result.Read(3), result.Read(4), result.Read(5), result.Read(6)); data.displayid = result.Read(7); data.equipmentId = result.Read(8); data.spawntimesecs = result.Read(9); @@ -3419,7 +3420,7 @@ namespace Game data.curhealth = result.Read(12); data.curmana = result.Read(13); data.movementType = result.Read(14); - data.spawnDifficulties = ParseSpawnDifficulties(result.Read(15), "creature", guid, data.spawnPoint.GetMapId(), spawnMasks.LookupByKey(data.spawnPoint.GetMapId())); + data.spawnDifficulties = ParseSpawnDifficulties(result.Read(15), "creature", guid, data.MapId, spawnMasks.LookupByKey(data.MapId)); short gameEvent = result.Read(16); uint PoolId = result.Read(17); data.npcflag = result.Read(18); @@ -3427,17 +3428,17 @@ namespace Game data.unit_flags2 = result.Read(20); data.unit_flags3 = result.Read(21); data.dynamicflags = result.Read(22); - data.phaseUseFlags = (PhaseUseFlagsValues)result.Read(23); - data.phaseId = result.Read(24); - data.phaseGroup = 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)); - data.spawnGroupData = _spawnGroupDataStorage[IsTransportMap(data.spawnPoint.GetMapId()) ? 1 : 0u]; // transport spawns default to compatibility group + data.spawnGroupData = _spawnGroupDataStorage[IsTransportMap(data.MapId) ? 1 : 0u]; // transport spawns default to compatibility group - var mapEntry = CliDB.MapStorage.LookupByKey(data.spawnPoint.GetMapId()); + var mapEntry = CliDB.MapStorage.LookupByKey(data.MapId); if (mapEntry == null) { - Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0}) that spawned at not existed map (Id: {1}), skipped.", guid, data.spawnPoint.GetMapId()); + Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0}) that spawned at not existed map (Id: {1}), skipped.", guid, data.MapId); continue; } @@ -3498,40 +3499,40 @@ namespace Game } } - if (Convert.ToBoolean(data.phaseUseFlags & ~PhaseUseFlagsValues.All)) + 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; + data.PhaseUseFlags &= PhaseUseFlagsValues.All; } - if (data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.AlwaysVisible) && data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.Inverse)) + 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; + data.PhaseUseFlags &= ~PhaseUseFlagsValues.Inverse; } - if (data.phaseGroup != 0 && data.phaseId != 0) + 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); - data.phaseGroup = 0; + data.PhaseGroup = 0; } - if (data.phaseId != 0) + if (data.PhaseId != 0) { - if (!CliDB.PhaseStorage.ContainsKey(data.phaseId)) + if (!CliDB.PhaseStorage.ContainsKey(data.PhaseId)) { - Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `phaseid` {2} does not exist, set to 0", guid, data.Id, data.phaseId); - data.phaseId = 0; + Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `phaseid` {2} does not exist, set to 0", guid, data.Id, data.PhaseId); + data.PhaseId = 0; } } - if (data.phaseGroup != 0) + if (data.PhaseGroup != 0) { - if (Global.DB2Mgr.GetPhasesForGroup(data.phaseGroup).Empty()) + if (Global.DB2Mgr.GetPhasesForGroup(data.PhaseGroup).Empty()) { - Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `phasegroup` {2} does not exist, set to 0", guid, data.Id, data.phaseGroup); - data.phaseGroup = 0; + Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0} Entry: {1}) with `phasegroup` {2} does not exist, set to 0", guid, data.Id, data.PhaseGroup); + data.PhaseGroup = 0; } } @@ -3543,7 +3544,7 @@ namespace Game 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.spawnPoint.GetMapId()) + 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; @@ -3553,7 +3554,7 @@ namespace Game if (WorldConfig.GetBoolValue(WorldCfg.CalculateCreatureZoneAreaData)) { PhasingHandler.InitDbVisibleMapId(phaseShift, data.terrainSwapMap); - Global.MapMgr.GetZoneAndAreaId(phaseShift, out uint zoneId, out uint areaId, data.spawnPoint); + Global.MapMgr.GetZoneAndAreaId(phaseShift, out uint zoneId, out uint areaId, data.MapId, data.SpawnPoint); PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_CREATURE_ZONE_AREA_DATA); stmt.AddValue(0, zoneId); @@ -3578,8 +3579,8 @@ namespace Game { foreach (Difficulty difficulty in data.spawnDifficulties) { - CellCoord cellCoord = GridDefines.ComputeCellCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY()); - var cellguids = CreateCellObjectGuids(data.spawnPoint.GetMapId(), difficulty, cellCoord.GetId()); + CellCoord cellCoord = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY()); + var cellguids = CreateCellObjectGuids(data.MapId, difficulty, cellCoord.GetId()); cellguids.creatures.Add(guid); } } @@ -3587,8 +3588,8 @@ namespace Game { foreach (Difficulty difficulty in data.spawnDifficulties) { - CellCoord cellCoord = GridDefines.ComputeCellCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY()); - CellObjectGuids cellguids = GetCellObjectGuids(data.spawnPoint.GetMapId(), difficulty, cellCoord.GetId()); + CellCoord cellCoord = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY()); + CellObjectGuids cellguids = GetCellObjectGuids(data.MapId, difficulty, cellCoord.GetId()); if (cellguids == null) return; @@ -3611,9 +3612,10 @@ namespace Game ulong spawnId = GenerateCreatureSpawnId(); CreatureData data = NewOrExistCreatureData(spawnId); - data.spawnId = spawnId; + data.SpawnId = spawnId; data.Id = entry; - data.spawnPoint.WorldRelocate(mapId, pos); + data.MapId = mapId; + data.SpawnPoint.Relocate(pos); data.displayid = 0; data.equipmentId = 0; @@ -3633,7 +3635,7 @@ namespace Game AddCreatureToGrid(spawnId, data); // We use spawn coords to spawn - if (!map.Instanceable() && !map.IsRemovalGrid(data.spawnPoint)) + if (!map.Instanceable() && !map.IsRemovalGrid(data.SpawnPoint)) { Creature creature = Creature.CreateCreatureFromDB(spawnId, map, true, true); if (!creature) @@ -3674,13 +3676,13 @@ namespace Game return creatureTemplateStorage; } public Dictionary GetAllCreatureData() { return creatureDataStorage; } - public CreatureData GetCreatureData(ulong guid) + public CreatureData GetCreatureData(ulong spawnId) { - return creatureDataStorage.LookupByKey(guid); + return creatureDataStorage.LookupByKey(spawnId); } - public ObjectGuid GetLinkedRespawnGuid(ObjectGuid guid) + public ObjectGuid GetLinkedRespawnGuid(ObjectGuid spawnId) { - var retGuid = linkedRespawnStorage.LookupByKey(guid); + var retGuid = linkedRespawnStorage.LookupByKey(spawnId); if (retGuid.IsEmpty()) return ObjectGuid.Empty; return retGuid; @@ -3691,7 +3693,7 @@ namespace Game return false; CreatureData master = GetCreatureData(guidLow); - ObjectGuid guid = ObjectGuid.Create(HighGuid.Creature, master.spawnPoint.GetMapId(), master.Id, guidLow); + ObjectGuid guid = ObjectGuid.Create(HighGuid.Creature, master.MapId, master.Id, guidLow); PreparedStatement stmt; if (linkedGuidLow == 0) // we're removing the linking @@ -3711,8 +3713,8 @@ namespace Game return false; } - MapRecord map = CliDB.MapStorage.LookupByKey(master.spawnPoint.GetMapId()); - if (map == null || !map.Instanceable() || (master.spawnPoint.GetMapId() != slave.spawnPoint.GetMapId())) + MapRecord map = CliDB.MapStorage.LookupByKey(master.MapId); + if (map == null || !map.Instanceable() || (master.MapId != slave.MapId)) { Log.outError(LogFilter.Sql, "Creature '{0}' linking to '{1}' on an unpermitted map.", guidLow, linkedGuidLow); return false; @@ -3725,7 +3727,7 @@ namespace Game return false; } - ObjectGuid linkedGuid = ObjectGuid.Create(HighGuid.Creature, slave.spawnPoint.GetMapId(), slave.Id, linkedGuidLow); + ObjectGuid linkedGuid = ObjectGuid.Create(HighGuid.Creature, slave.MapId, slave.Id, linkedGuidLow); linkedRespawnStorage[guid] = linkedGuid; stmt = DB.World.GetPreparedStatement(WorldStatements.REP_LINKED_RESPAWN); @@ -3735,22 +3737,22 @@ namespace Game DB.World.Execute(stmt); return true; } - public CreatureData NewOrExistCreatureData(ulong guid) + public CreatureData NewOrExistCreatureData(ulong spawnId) { - if (!creatureDataStorage.ContainsKey(guid)) - creatureDataStorage[guid] = new CreatureData(); - return creatureDataStorage[guid]; + if (!creatureDataStorage.ContainsKey(spawnId)) + creatureDataStorage[spawnId] = new CreatureData(); + return creatureDataStorage[spawnId]; } - public void DeleteCreatureData(ulong guid) + public void DeleteCreatureData(ulong spawnId) { - CreatureData data = GetCreatureData(guid); + CreatureData data = GetCreatureData(spawnId); if (data != null) { - RemoveCreatureFromGrid(guid, data); + RemoveCreatureFromGrid(spawnId, data); OnDeleteSpawnData(data); } - creatureDataStorage.Remove(guid); + creatureDataStorage.Remove(spawnId); } public CreatureBaseStats GetCreatureBaseStats(uint level, uint unitClass) { @@ -4168,20 +4170,21 @@ namespace Game } GameObjectData data = new(); - data.spawnId = guid; + data.SpawnId = guid; data.Id = entry; - data.spawnPoint = new WorldLocation(result.Read(2), result.Read(3), result.Read(4), result.Read(5), result.Read(6)); + data.MapId = result.Read(2); + data.SpawnPoint = new Position(result.Read(3), result.Read(4), result.Read(5), result.Read(6)); data.rotation.X = result.Read(7); data.rotation.Y = result.Read(8); data.rotation.Z = result.Read(9); data.rotation.W = result.Read(10); data.spawntimesecs = result.Read(11); - data.spawnGroupData = _spawnGroupDataStorage[IsTransportMap(data.spawnPoint.GetMapId()) ? 1 : 0u]; // transport spawns default to compatibility group + data.spawnGroupData = IsTransportMap(data.MapId) ? GetLegacySpawnGroup() : GetDefaultSpawnGroup(); // transport spawns default to compatibility group - var mapEntry = CliDB.MapStorage.LookupByKey(data.spawnPoint.GetMapId()); + var mapEntry = CliDB.MapStorage.LookupByKey(data.MapId); if (mapEntry == null) { - Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) spawned on a non-existed map (Id: {2}), skip", guid, data.Id, data.spawnPoint.GetMapId()); + Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) spawned on a non-existed map (Id: {2}), skip", guid, data.Id, data.MapId); continue; } @@ -4204,7 +4207,7 @@ namespace Game } data.goState = (GameObjectState)gostate; - data.spawnDifficulties = ParseSpawnDifficulties(result.Read(14), "gameobject", guid, data.spawnPoint.GetMapId(), spawnMasks.LookupByKey(data.spawnPoint.GetMapId())); + data.spawnDifficulties = ParseSpawnDifficulties(result.Read(14), "gameobject", guid, data.MapId, spawnMasks.LookupByKey(data.MapId)); if (data.spawnDifficulties.Empty()) { Log.outError(LogFilter.Sql, $"Table `creature` has creature (GUID: {guid}) that is not spawned in any difficulty, skipped."); @@ -4213,44 +4216,44 @@ namespace Game short gameEvent = result.Read(15); uint PoolId = result.Read(16); - data.phaseUseFlags = (PhaseUseFlagsValues)result.Read(17); - data.phaseId = result.Read(18); - data.phaseGroup = result.Read(19); + data.PhaseUseFlags = (PhaseUseFlagsValues)result.Read(17); + data.PhaseId = result.Read(18); + data.PhaseGroup = result.Read(19); - if (Convert.ToBoolean(data.phaseUseFlags & ~PhaseUseFlagsValues.All)) + 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; + data.PhaseUseFlags &= PhaseUseFlagsValues.All; } - if (data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.AlwaysVisible) && data.phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.Inverse)) + 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; + data.PhaseUseFlags &= ~PhaseUseFlagsValues.Inverse; } - if (data.phaseGroup != 0 && data.phaseId != 0) + if (data.PhaseGroup != 0 && data.PhaseId != 0) { Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, data.Id); - data.phaseGroup = 0; + data.PhaseGroup = 0; } - if (data.phaseId != 0) + if (data.PhaseId != 0) { - if (!CliDB.PhaseStorage.ContainsKey(data.phaseId)) + if (!CliDB.PhaseStorage.ContainsKey(data.PhaseId)) { - Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `phaseid` {2} does not exist, set to 0", guid, data.Id, data.phaseId); - data.phaseId = 0; + Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `phaseid` {2} does not exist, set to 0", guid, data.Id, data.PhaseId); + data.PhaseId = 0; } } - if (data.phaseGroup != 0) + if (data.PhaseGroup != 0) { - if (Global.DB2Mgr.GetPhasesForGroup(data.phaseGroup).Empty()) + if (Global.DB2Mgr.GetPhasesForGroup(data.PhaseGroup).Empty()) { - Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `phaseGroup` {2} does not exist, set to 0", guid, data.Id, data.phaseGroup); - data.phaseGroup = 0; + Log.outError(LogFilter.Sql, "Table `gameobject` have gameobject (GUID: {0} Entry: {1}) with `phaseGroup` {2} does not exist, set to 0", guid, data.Id, data.PhaseGroup); + data.PhaseGroup = 0; } } @@ -4263,7 +4266,7 @@ namespace Game 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.spawnPoint.GetMapId()) + 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; @@ -4296,7 +4299,7 @@ namespace Game continue; } - if (!GridDefines.IsValidMapCoord(data.spawnPoint)) + if (!GridDefines.IsValidMapCoord(data.MapId, data.SpawnPoint)) { Log.outError(LogFilter.Sql, "Table `gameobject` has gameobject (GUID: {0} Entry: {1}) with invalid coordinates, skip", guid, data.Id); continue; @@ -4305,13 +4308,13 @@ namespace Game if (!(Math.Abs(Quaternion.Dot(data.rotation, data.rotation) - 1) < 1e-5)) { Log.outError(LogFilter.Sql, $"Table `gameobject` has gameobject (GUID: {guid} Entry: {data.Id}) with invalid rotation quaternion (non-unit), defaulting to orientation on Z axis only"); - data.rotation = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(data.spawnPoint.GetOrientation(), 0f, 0f)); + data.rotation = Quaternion.CreateFromRotationMatrix(Extensions.fromEulerAnglesZYX(data.SpawnPoint.GetOrientation(), 0f, 0f)); } if (WorldConfig.GetBoolValue(WorldCfg.CalculateGameobjectZoneAreaData)) { PhasingHandler.InitDbVisibleMapId(phaseShift, data.terrainSwapMap); - Global.MapMgr.GetZoneAndAreaId(phaseShift, out uint zoneId, out uint areaId, data.spawnPoint); + Global.MapMgr.GetZoneAndAreaId(phaseShift, out uint zoneId, out uint areaId, data.MapId, data.SpawnPoint); PreparedStatement stmt = DB.World.GetPreparedStatement(WorldStatements.UPD_GAMEOBJECT_ZONE_AREA_DATA); stmt.AddValue(0, zoneId); @@ -4499,8 +4502,8 @@ namespace Game { foreach (Difficulty difficulty in data.spawnDifficulties) { - CellCoord cellCoord = GridDefines.ComputeCellCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY()); - var cellguids = CreateCellObjectGuids(data.spawnPoint.GetMapId(), difficulty, cellCoord.GetId()); + CellCoord cellCoord = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY()); + var cellguids = CreateCellObjectGuids(data.MapId, difficulty, cellCoord.GetId()); cellguids.gameobjects.Add(guid); } } @@ -4508,8 +4511,8 @@ namespace Game { foreach (Difficulty difficulty in data.spawnDifficulties) { - CellCoord cellCoord = GridDefines.ComputeCellCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY()); - CellObjectGuids cellguids = GetCellObjectGuids(data.spawnPoint.GetMapId(), difficulty, cellCoord.GetId()); + CellCoord cellCoord = GridDefines.ComputeCellCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY()); + CellObjectGuids cellguids = GetCellObjectGuids(data.MapId, difficulty, cellCoord.GetId()); if (cellguids == null) return; @@ -4528,9 +4531,10 @@ namespace Game ulong spawnId = GenerateGameObjectSpawnId(); GameObjectData data = NewOrExistGameObjectData(spawnId); - data.spawnId = spawnId; + data.SpawnId = spawnId; data.Id = entry; - data.spawnPoint.WorldRelocate(mapId, pos); + data.MapId = mapId; + data.SpawnPoint.Relocate(pos); data.rotation = rot; data.spawntimesecs = (int)spawntimedelay; data.animprogress = 100; @@ -4544,7 +4548,7 @@ namespace Game // Spawn if necessary (loaded grids only) // We use spawn coords to spawn - if (!map.Instanceable() && map.IsGridLoaded(data.spawnPoint)) + if (!map.Instanceable() && map.IsGridLoaded(data.SpawnPoint)) { GameObject go = GameObject.CreateGameObjectFromDB(spawnId, map); if (!go) @@ -4554,7 +4558,7 @@ namespace Game } } - Log.outDebug(LogFilter.Maps, $"AddGameObjectData: dbguid:{spawnId} entry:{entry} map:{mapId} pos:{data.spawnPoint}"); + Log.outDebug(LogFilter.Maps, $"AddGameObjectData: dbguid:{spawnId} entry:{entry} map:{mapId} pos:{data.SpawnPoint}"); return spawnId; } @@ -4569,27 +4573,27 @@ namespace Game } MultiMap GetGameObjectQuestItemMap() { return _gameObjectQuestItemStorage; } public Dictionary GetAllGameObjectData() { return gameObjectDataStorage; } - public GameObjectData GetGameObjectData(ulong guid) + public GameObjectData GetGameObjectData(ulong spawnId) { - return gameObjectDataStorage.LookupByKey(guid); + return gameObjectDataStorage.LookupByKey(spawnId); } - public void DeleteGameObjectData(ulong guid) + public void DeleteGameObjectData(ulong spawnId) { - GameObjectData data = GetGameObjectData(guid); + GameObjectData data = GetGameObjectData(spawnId); if (data != null) { - RemoveGameObjectFromGrid(guid, data); + RemoveGameObjectFromGrid(spawnId, data); OnDeleteSpawnData(data); } - gameObjectDataStorage.Remove(guid); + gameObjectDataStorage.Remove(spawnId); } - public GameObjectData NewOrExistGameObjectData(ulong guid) + public GameObjectData NewOrExistGameObjectData(ulong spawnId) { - if (!gameObjectDataStorage.ContainsKey(guid)) - gameObjectDataStorage[guid] = new GameObjectData(); + if (!gameObjectDataStorage.ContainsKey(spawnId)) + gameObjectDataStorage[spawnId] = new GameObjectData(); - return gameObjectDataStorage[guid]; + return gameObjectDataStorage[spawnId]; } public GameObjectTemplate GetGameObjectTemplate(uint entry) { @@ -5509,14 +5513,14 @@ namespace Game { uint groupId = result.Read(0); SpawnObjectType spawnType = (SpawnObjectType)result.Read(1); - if (spawnType >= SpawnObjectType.Max) + if (!SpawnData.TypeIsValid(spawnType)) { Log.outError(LogFilter.Sql, $"Spawn data with invalid type {spawnType} listed for spawn group {groupId}. Skipped."); continue; } ulong spawnId = result.Read(2); - SpawnData data = GetSpawnData(spawnType, spawnId); + SpawnMetadata data = GetSpawnMetadata(spawnType, spawnId); if (data == null) { Log.outError(LogFilter.Sql, $"Spawn data with ID ({spawnType},{spawnId}) not found, but is listed as a member of spawn group {groupId}!"); @@ -5536,10 +5540,10 @@ namespace Game else { if (groupTemplate.mapId == 0xFFFFFFFF) - groupTemplate.mapId = data.spawnPoint.GetMapId(); - else if (groupTemplate.mapId != data.spawnPoint.GetMapId() && !groupTemplate.flags.HasAnyFlag(SpawnGroupFlags.System)) + groupTemplate.mapId = data.MapId; + else if (groupTemplate.mapId != data.MapId && !groupTemplate.flags.HasAnyFlag(SpawnGroupFlags.System)) { - Log.outError(LogFilter.Sql, $"Spawn group {groupId} has map ID {groupTemplate.mapId}, but spawn ({spawnType},{spawnId}) has map id {data.spawnPoint.GetMapId()} - spawn NOT added to group!"); + Log.outError(LogFilter.Sql, $"Spawn group {groupId} has map ID {groupTemplate.mapId}, but spawn ({spawnType},{spawnId}) has map id {data.MapId} - spawn NOT added to group!"); continue; } data.spawnGroupData = groupTemplate; @@ -5609,7 +5613,7 @@ namespace Game void OnDeleteSpawnData(SpawnData data) { var templateIt = _spawnGroupDataStorage.LookupByKey(data.spawnGroupData.groupId); - Cypher.Assert(templateIt != null, $"Creature data for ({data.type},{data.spawnId}) is being deleted and has invalid spawn group index {data.spawnGroupData.groupId}!"); + Cypher.Assert(templateIt != null, $"Creature data for ({data.type},{data.SpawnId}) is being deleted and has invalid spawn group index {data.spawnGroupData.groupId}!"); if (templateIt.flags.HasAnyFlag(SpawnGroupFlags.System)) // system groups don't store their members in the map return; @@ -5623,7 +5627,7 @@ namespace Game return; } - Cypher.Assert(false, $"Spawn data ({data.type},{data.spawnId}) being removed is member of spawn group {data.spawnGroupData.groupId}, but not actually listed in the lookup table for that group!"); + Cypher.Assert(false, $"Spawn data ({data.type},{data.SpawnId}) being removed is member of spawn group {data.spawnGroupData.groupId}, but not actually listed in the lookup table for that group!"); } public Dictionary GetInstanceTemplates() { return instanceTemplateStorage; } @@ -5691,19 +5695,38 @@ namespace Game } public bool IsTransportMap(uint mapId) { return _transportMaps.Contains((ushort)mapId); } public SpawnGroupTemplateData GetSpawnGroupData(uint groupId) { return _spawnGroupDataStorage.LookupByKey(groupId); } + public SpawnGroupTemplateData GetSpawnGroupData(SpawnObjectType type, ulong spawnId) + { + SpawnMetadata data = GetSpawnMetadata(type, spawnId); + return data != null ? data.spawnGroupData : null; + } public SpawnGroupTemplateData GetDefaultSpawnGroup() { return _spawnGroupDataStorage.ElementAt(0).Value; } public SpawnGroupTemplateData GetLegacySpawnGroup() { return _spawnGroupDataStorage.ElementAt(1).Value; } - public List GetSpawnDataForGroup(uint groupId) { return _spawnGroupMapStorage.LookupByKey(groupId); } - public SpawnData GetSpawnData(SpawnObjectType type, ulong guid) + public List GetSpawnMetadataForGroup(uint groupId) { return _spawnGroupMapStorage.LookupByKey(groupId); } + public SpawnMetadata GetSpawnMetadata(SpawnObjectType type, ulong spawnId) { - if (type == SpawnObjectType.Creature) - return GetCreatureData(guid); - else if (type == SpawnObjectType.GameObject) - return GetGameObjectData(guid); + if (SpawnData.TypeHasData(type)) + return GetSpawnData(type, spawnId); else - Cypher.Assert(false, $"Invalid spawn object type {type}"); + return null; + } + public SpawnData GetSpawnData(SpawnObjectType type, ulong spawnId) + { + if (!SpawnData.TypeHasData(type)) + return null; - return null; + switch (type) + { + case SpawnObjectType.Creature: + return GetCreatureData(spawnId); + case SpawnObjectType.GameObject: + return GetGameObjectData(spawnId); + case SpawnObjectType.AreaTrigger: + return Global.AreaTriggerDataStorage.GetAreaTriggerSpawn(spawnId); + default: + Cypher.Assert(false, $"Invalid spawn object type {type}"); + return null; + } } public List GetSpawnGroupsForInstance(uint instanceId) { return _instanceSpawnGroupStorage.LookupByKey(instanceId); } @@ -10593,7 +10616,7 @@ namespace Game public MultiMap GraveYardStorage = new(); List _transportMaps = new(); Dictionary _spawnGroupDataStorage = new(); - MultiMap _spawnGroupMapStorage = new(); + MultiMap _spawnGroupMapStorage = new(); MultiMap _instanceSpawnGroupStorage = new(); //Spells /Skills / Phases diff --git a/Source/Game/Maps/GridDefines.cs b/Source/Game/Maps/GridDefines.cs index 8060e4f64..2fbdc45da 100644 --- a/Source/Game/Maps/GridDefines.cs +++ b/Source/Game/Maps/GridDefines.cs @@ -42,7 +42,7 @@ namespace Game.Maps { return IsValidMapCoord(x, y, z) && float.IsFinite(o); } - + public static bool IsValidMapCoord(uint mapid, float x, float y) { return Global.MapMgr.IsValidMAP(mapid, false) && IsValidMapCoord(x, y); @@ -58,9 +58,14 @@ namespace Game.Maps return Global.MapMgr.IsValidMAP(mapid, false) && IsValidMapCoord(x, y, z, o); } + public static bool IsValidMapCoord(uint mapid, Position pos) + { + return IsValidMapCoord(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); + } + public static bool IsValidMapCoord(WorldLocation loc) { - return IsValidMapCoord(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation()); + return IsValidMapCoord(loc.GetMapId(), loc); } public static void NormalizeMapCoord(ref float c) diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 10fef0738..fe5b1e8bb 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -2411,6 +2411,159 @@ namespace Game.Maps return true; } + public void Respawn(SpawnObjectType type, ulong spawnId, SQLTransaction dbTrans = null) + { + RespawnInfo info = GetRespawnInfo(type, spawnId); + if (info != null) + Respawn(info, dbTrans); + } + + public void Respawn(RespawnInfo info, SQLTransaction dbTrans = null) + { + info.respawnTime = GameTime.GetGameTime(); + SaveRespawnInfoDB(info, dbTrans); + } + + public void RemoveRespawnTime(SpawnObjectType type, ulong spawnId, SQLTransaction dbTrans = null) + { + RespawnInfo info = GetRespawnInfo(type, spawnId); + if (info != null) + DeleteRespawnInfo(info, dbTrans); + } + + int DespawnAll(SpawnObjectType type, ulong spawnId) + { + List toUnload = new(); + switch (type) + { + case SpawnObjectType.Creature: + foreach (var creature in GetCreatureBySpawnIdStore().LookupByKey(spawnId)) + toUnload.Add(creature); + break; + case SpawnObjectType.GameObject: + foreach (var obj in GetGameObjectBySpawnIdStore().LookupByKey(spawnId)) + toUnload.Add(obj); + break; + default: + break; + } + + foreach (WorldObject o in toUnload) + AddObjectToRemoveList(o); + + return toUnload.Count; + } + + bool AddRespawnInfo(RespawnInfo info) + { + if (info.spawnId == 0) + { + Log.outError(LogFilter.Maps, $"Attempt to insert respawn info for zero spawn id (type {info.type})"); + return false; + } + + var bySpawnIdMap = GetRespawnMapForType(info.type); + if (bySpawnIdMap == null) + return false; + + // check if we already have the maximum possible number of respawns scheduled + if (SpawnData.TypeHasData(info.type)) + { + var existing = bySpawnIdMap.LookupByKey(info.spawnId); + if (existing != null) // spawnid already has a respawn scheduled + { + if (info.respawnTime <= existing.respawnTime) // delete existing in this case + DeleteRespawnInfo(existing); + else + return false; + } + Cypher.Assert(!bySpawnIdMap.ContainsKey(info.spawnId), $"Insertion of respawn info with id ({info.type},{info.spawnId}) into spawn id map failed - state desync."); + } + else + Cypher.Assert(false, $"Invalid respawn info for spawn id ({info.type},{info.spawnId}) being inserted"); + + RespawnInfo ri = new(info); + _respawnTimes.Add(ri); + bySpawnIdMap.Add(ri.spawnId, ri); + return true; + } + + static void PushRespawnInfoFrom(List data, Dictionary map) + { + foreach (var pair in map) + data.Add(pair.Value); + } + + public void GetRespawnInfo(List respawnData, SpawnObjectTypeMask types) + { + if ((types & SpawnObjectTypeMask.Creature) != 0) + PushRespawnInfoFrom(respawnData, _creatureRespawnTimesBySpawnId); + if ((types & SpawnObjectTypeMask.GameObject) != 0) + PushRespawnInfoFrom(respawnData, _gameObjectRespawnTimesBySpawnId); + } + + public RespawnInfo GetRespawnInfo(SpawnObjectType type, ulong spawnId) + { + var map = GetRespawnMapForType(type); + if (map == null) + return null; + + var respawnInfo = map.LookupByKey(spawnId); + if (respawnInfo == null) + return null; + + return respawnInfo; + } + + Dictionary GetRespawnMapForType(SpawnObjectType type) + { + switch (type) + { + case SpawnObjectType.Creature: + return _creatureRespawnTimesBySpawnId; + case SpawnObjectType.GameObject: + return _gameObjectRespawnTimesBySpawnId; + case SpawnObjectType.AreaTrigger: + return null; + default: + Cypher.Assert(false); + return null; + } + } + + void UnloadAllRespawnInfos() // delete everything from memory + { + _respawnTimes.Clear(); + _creatureRespawnTimesBySpawnId.Clear(); + _gameObjectRespawnTimesBySpawnId.Clear(); + } + + void DeleteRespawnInfo(RespawnInfo info, SQLTransaction dbTrans = null) + { + // Delete from all relevant containers to ensure consistency + Cypher.Assert(info != null); + + // spawnid store + var spawnMap = GetRespawnMapForType(info.type); + if (spawnMap == null) + return; + + var respawnInfo = spawnMap.LookupByKey(info.spawnId); + Cypher.Assert(respawnInfo != info, $"Respawn stores inconsistent for map {GetId()}, spawnid {info.spawnId} (type {info.type})"); + spawnMap.Remove(info.spawnId); + + // respawn heap + _respawnTimes.Remove(info); + + // database + PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_RESPAWN); + stmt.AddValue(0, (ushort)info.type); + stmt.AddValue(1, info.spawnId); + stmt.AddValue(2, GetId()); + stmt.AddValue(3, GetInstanceId()); + DB.Characters.ExecuteOrAppend(dbTrans, stmt); + } + void DoRespawn(SpawnObjectType type, ulong spawnId, uint gridId) { if (!IsGridLoaded(gridId)) // if grid isn't loaded, this will be processed in grid load handler @@ -2438,120 +2591,6 @@ namespace Game.Maps } } - public void Respawn(SpawnObjectType type, ulong spawnId, SQLTransaction dbTrans = null) - { - RespawnInfo info = GetRespawnInfo(type, spawnId); - if (info != null) - Respawn(info, dbTrans); - } - - public void Respawn(RespawnInfo info, SQLTransaction dbTrans = null) - { - if (!CheckRespawn(info)) - { - if (info.respawnTime != 0) - { - //_respawnTimes.decrease(info); - SaveRespawnInfoDB(info, dbTrans); - } - else - DeleteRespawnInfo(info, dbTrans); - - return; - } - - // remove the actual respawn record first - since this deletes it, we save what we need - SpawnObjectType type = info.type; - uint gridId = info.gridId; - ulong spawnId = info.spawnId; - DeleteRespawnInfo(info, dbTrans); - DoRespawn(type, spawnId, gridId); - } - - bool AddRespawnInfo(RespawnInfo info) - { - Cypher.Assert(info.spawnId != 0, $"Attempt to schedule respawn with zero spawnid (type {info.type})"); - - var bySpawnIdMap = GetRespawnMapForType(info.type); - - var existing = bySpawnIdMap.LookupByKey(info.spawnId); - if (existing != null) // spawnid already has a respawn scheduled - { - if (info.respawnTime < existing.respawnTime) // delete existing in this case - DeleteRespawnInfo(existing); - else - return false; - } - - // if we get to this point, we should insert the respawninfo (there either was no prior entry, or it was deleted already) - RespawnInfo ri = new(info); - _respawnTimes.Add(ri); - bool success = bySpawnIdMap.TryAdd(ri.spawnId, ri); - Cypher.Assert(success, $"Insertion of respawn info with id ({ri.type},{ri.spawnId}) into spawn id map failed - state desync."); - return true; - } - - static void PushRespawnInfoFrom(List data, Dictionary map) - { - foreach (var pair in map) - data.Add(pair.Value); - } - - public void GetRespawnInfo(List respawnData, SpawnObjectTypeMask types) - { - if (types.HasAnyFlag(SpawnObjectTypeMask.Creature)) - PushRespawnInfoFrom(respawnData, _creatureRespawnTimesBySpawnId); - if (types.HasAnyFlag(SpawnObjectTypeMask.GameObject)) - PushRespawnInfoFrom(respawnData, _gameObjectRespawnTimesBySpawnId); - } - - RespawnInfo GetRespawnInfo(SpawnObjectType type, ulong spawnId) - { - var map = GetRespawnMapForType(type); - var respawnInfo = map.LookupByKey(spawnId); - if (respawnInfo == null) - return null; - - return respawnInfo; - } - - Dictionary GetRespawnMapForType(SpawnObjectType type) { return (type == SpawnObjectType.GameObject) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; } - - void UnloadAllRespawnInfos() // delete everything from memory - { - _respawnTimes.Clear(); - _creatureRespawnTimesBySpawnId.Clear(); - _gameObjectRespawnTimesBySpawnId.Clear(); - } - - void DeleteRespawnInfo(RespawnInfo info, SQLTransaction dbTrans = null) - { - // Delete from all relevant containers to ensure consistency - Cypher.Assert(info != null); - - // spawnid store - bool removed = GetRespawnMapForType(info.type).Remove(info.spawnId); - Cypher.Assert(removed, $"Respawn stores inconsistent for map {GetId()}, spawnid {info.spawnId} (type {info.type})"); - - // respawn heap - _respawnTimes.Remove(info); - - // database - PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_RESPAWN); - stmt.AddValue(0, (ushort)info.type); - stmt.AddValue(1, info.spawnId); - stmt.AddValue(2, GetId()); - stmt.AddValue(3, GetInstanceId()); - DB.Characters.ExecuteOrAppend(dbTrans, stmt); - } - - public void RemoveRespawnTime(SpawnObjectType type, ulong spawnId, SQLTransaction dbTrans = null) - { - RespawnInfo info = GetRespawnInfo(type, spawnId); - if (info != null) - DeleteRespawnInfo(info, dbTrans); - } - void ProcessRespawns() { long now = GameTime.GetGameTime(); @@ -2603,8 +2642,11 @@ namespace Game.Maps return; } - SpawnData data = Global.ObjectMgr.GetSpawnData(type, spawnId); - if (data == null || data.spawnGroupData == null || !!data.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.DynamicSpawnRate)) + SpawnMetadata data = Global.ObjectMgr.GetSpawnMetadata(type, spawnId); + if (data == null) + return; + + if (!data.spawnGroupData.flags.HasFlag(SpawnGroupFlags.DynamicSpawnRate)) return; if (!_zonePlayerCountMap.ContainsKey(obj.GetZoneId())) @@ -2625,6 +2667,25 @@ namespace Game.Maps respawnDelay = (uint)Math.Max(Math.Ceiling(respawnDelay * adjustFactor), timeMinimum); } + public bool ShouldBeSpawnedOnGridLoad(ulong spawnId) { return ShouldBeSpawnedOnGridLoad(SpawnData.TypeFor(), spawnId); } + + bool ShouldBeSpawnedOnGridLoad(SpawnObjectType type, ulong spawnId) + { + Cypher.Assert(SpawnData.TypeHasData(type)); + // check if the object is on its respawn timer + if (GetRespawnTime(type, spawnId) != 0) + return false; + + SpawnMetadata spawnData = Global.ObjectMgr.GetSpawnMetadata(type, spawnId); + // check if the object is part of a spawn group + SpawnGroupTemplateData spawnGroup = spawnData.spawnGroupData; + if (!spawnGroup.flags.HasFlag(SpawnGroupFlags.System)) + if (!IsSpawnGroupActive(spawnGroup.groupId)) + return false; + + return true; + } + SpawnGroupTemplateData GetSpawnGroupData(uint groupId) { SpawnGroupTemplateData data = Global.ObjectMgr.GetSpawnGroupData(groupId); @@ -2644,39 +2705,52 @@ namespace Game.Maps } SetSpawnGroupActive(groupId, true); // start processing respawns for the group - foreach (var data in Global.ObjectMgr.GetSpawnDataForGroup(groupId)) + + List toSpawn = new(); + foreach (var data in Global.ObjectMgr.GetSpawnMetadataForGroup(groupId)) { - Cypher.Assert(groupData.mapId == data.spawnPoint.GetMapId()); - // Check if there's already an instance spawned - if (!force) - { - WorldObject obj = GetWorldObjectBySpawnId(data.type, data.spawnId); - if (obj != null) - if ((data.type != SpawnObjectType.Creature) || obj.ToCreature().IsAlive()) - continue; - } + Cypher.Assert(groupData.mapId == data.MapId); - long respawnTime = GetRespawnTime(data.type, data.spawnId); - if (respawnTime != 0) - { - if (!force && !ignoreRespawn && respawnTime > GameTime.GetGameTime()) - continue; - - // we need to remove the respawn time, otherwise we'd end up double spawning - RemoveRespawnTime(data.type, data.spawnId); - } - - // don't spawn if the grid isn't loaded (will be handled in grid loader) - if (!IsGridLoaded(data.spawnPoint)) + var respawnMap = GetRespawnMapForType(data.type); + if (respawnMap == null) continue; - // Everything OK, now do the actual (re)spawn + if (force || ignoreRespawn) + RemoveRespawnTime(data.type, data.SpawnId); + + bool hasRespawnTimer = respawnMap.ContainsKey(data.SpawnId); + if (SpawnData.TypeHasData(data.type)) + { + // has a respawn timer + if (hasRespawnTimer) + continue; + + // has a spawn already active + if (!force) + { + WorldObject obj = GetWorldObjectBySpawnId(data.type, data.SpawnId); + if (obj != null) + if ((data.type != SpawnObjectType.Creature) || obj.ToCreature().IsAlive()) + continue; + } + + toSpawn.Add(data.ToSpawnData()); + } + } + + foreach (SpawnData data in toSpawn) + { + // don't spawn if the grid isn't loaded (will be handled in grid loader) + if (!IsGridLoaded(data.SpawnPoint)) + continue; + + // now do the actual (re)spawn switch (data.type) { case SpawnObjectType.Creature: { Creature creature = new(); - if (!creature.LoadFromDB(data.spawnId, this, true, force)) + if (!creature.LoadFromDB(data.SpawnId, this, true, force)) creature.Dispose(); else if (spawnedObjects != null) spawnedObjects.Add(creature); @@ -2685,14 +2759,23 @@ namespace Game.Maps case SpawnObjectType.GameObject: { GameObject gameobject = new(); - if (!gameobject.LoadFromDB(data.spawnId, this, true)) + if (!gameobject.LoadFromDB(data.SpawnId, this, true)) gameobject.Dispose(); else if (spawnedObjects != null) spawnedObjects.Add(gameobject); break; } + case SpawnObjectType.AreaTrigger: + { + AreaTrigger areaTrigger = new AreaTrigger(); + if (!areaTrigger.LoadFromDB(data.SpawnId, this, true, false)) + areaTrigger.Dispose(); + else if (spawnedObjects != null) + spawnedObjects.Add(areaTrigger); + break; + } default: - Cypher.Assert(false, $"Invalid spawn type {data.type} with spawnId {data.spawnId}"); + Cypher.Assert(false, $"Invalid spawn type {data.type} with spawnId {data.SpawnId}"); return false; } } @@ -2704,6 +2787,7 @@ namespace Game.Maps { return SpawnGroupDespawn(groupId, deleteRespawnTimes, out _); } + public bool SpawnGroupDespawn(uint groupId, bool deleteRespawnTimes, out int count) { count = 0; @@ -2714,39 +2798,14 @@ namespace Game.Maps return false; } - List toUnload = new(); // unload after iterating, otherwise iterator invalidation - foreach (var data in Global.ObjectMgr.GetSpawnDataForGroup(groupId)) + foreach (var data in Global.ObjectMgr.GetSpawnMetadataForGroup(groupId)) { + Cypher.Assert(groupData.mapId == data.MapId); if (deleteRespawnTimes) - RemoveRespawnTime(data.type, data.spawnId); - switch (data.type) - { - case SpawnObjectType.Creature: - { - var bounds = GetCreatureBySpawnIdStore().LookupByKey(data.spawnId); - foreach (var creature in bounds) - toUnload.Add(creature); - break; - } - case SpawnObjectType.GameObject: - { - var bounds = GetGameObjectBySpawnIdStore().LookupByKey(data.spawnId); - foreach (var go in bounds) - toUnload.Add(go); - break; - } - default: - Cypher.Assert(false, $"Invalid spawn type {data.type} in spawn data with spawnId {data.spawnId}."); - return false; - } + RemoveRespawnTime(data.type, data.SpawnId); + count += DespawnAll(data.type, data.SpawnId); } - count = toUnload.Count; - - // now do the actual despawning - foreach (WorldObject obj in toUnload) - obj.AddObjectToRemoveList(); - SetSpawnGroupActive(groupId, false); // stop processing respawns for the group, too return true; } @@ -3030,19 +3089,23 @@ namespace Game.Maps public void SaveRespawnTime(SpawnObjectType type, ulong spawnId, uint entry, long respawnTime, uint gridId = 0, SQLTransaction dbTrans = null, bool startup = false) { - if (spawnId == 0) + SpawnMetadata data = Global.ObjectMgr.GetSpawnMetadata(type, spawnId); + if (data == null) + { + Log.outError(LogFilter.Maps, $"Map {GetId()} attempt to save respawn time for nonexistant spawnid ({type},{spawnId})."); return; + } if (respawnTime == 0) { // Delete only - RemoveRespawnTime(type, spawnId, dbTrans); + RemoveRespawnTime(data.type, data.SpawnId, dbTrans); return; } RespawnInfo ri = new(); - ri.type = type; - ri.spawnId = spawnId; + ri.type = data.type; + ri.spawnId = data.SpawnId; ri.entry = entry; ri.respawnTime = respawnTime; ri.gridId = gridId; @@ -3083,11 +3146,11 @@ namespace Game.Maps var spawnId = result.Read(1); var respawnTime = result.Read(2); - if (type < SpawnObjectType.Max) + if (SpawnData.TypeHasData(type)) { SpawnData data = Global.ObjectMgr.GetSpawnData(type, spawnId); if (data != null) - SaveRespawnTime(type, spawnId, data.Id, respawnTime, GridDefines.ComputeGridCoord(data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY()).GetId(), null, true); + SaveRespawnTime(type, spawnId, data.Id, respawnTime, GridDefines.ComputeGridCoord(data.SpawnPoint.GetPositionX(), data.SpawnPoint.GetPositionY()).GetId(), null, true); else Log.outError(LogFilter.Maps, $"Loading saved respawn time of {respawnTime} for spawnid ({type},{spawnId}) - spawn does not exist, ignoring"); } @@ -3702,6 +3765,8 @@ namespace Game.Maps public MultiMap GetGameObjectBySpawnIdStore() { return _gameobjectBySpawnIdStore; } + public MultiMap GetAreaTriggerBySpawnIdStore() { return _areaTriggerBySpawnIdStore; } + public List GetCorpsesInCell(uint cellId) { return _corpsesByCell.LookupByKey(cellId); @@ -3748,12 +3813,17 @@ namespace Game.Maps public long GetRespawnTime(SpawnObjectType type, ulong spawnId) { - var respawnDic = GetRespawnMapForType(type); - var respawnInfo = respawnDic.LookupByKey(spawnId); - return (respawnInfo == null) ? 0 : respawnInfo.respawnTime; + var map = GetRespawnMapForType(type); + if (map != null) + { + var respawnInfo = map.LookupByKey(spawnId); + return (respawnInfo == null) ? 0 : respawnInfo.respawnTime; + } + return 0; } public long GetCreatureRespawnTime(ulong spawnId) { return GetRespawnTime(SpawnObjectType.Creature, spawnId); } + public long GetGORespawnTime(ulong spawnId) { return GetRespawnTime(SpawnObjectType.GameObject, spawnId); } void SetTimer(uint t) @@ -3851,7 +3921,7 @@ namespace Game.Maps return go ? go.ToTransport() : null; } - Creature GetCreatureBySpawnId(ulong spawnId) + public Creature GetCreatureBySpawnId(ulong spawnId) { var bounds = GetCreatureBySpawnIdStore().LookupByKey(spawnId); if (bounds.Empty()) @@ -3862,7 +3932,7 @@ namespace Game.Maps return foundCreature != null ? foundCreature : bounds[0]; } - GameObject GetGameObjectBySpawnId(ulong spawnId) + public GameObject GetGameObjectBySpawnId(ulong spawnId) { var bounds = GetGameObjectBySpawnIdStore().LookupByKey(spawnId); if (bounds.Empty()) @@ -3873,6 +3943,15 @@ namespace Game.Maps return foundGameObject != null ? foundGameObject : bounds[0]; } + public AreaTrigger GetAreaTriggerBySpawnId(ulong spawnId) + { + var bounds = GetAreaTriggerBySpawnIdStore().LookupByKey(spawnId); + if (bounds.Empty()) + return null; + + return bounds.FirstOrDefault(); + } + public WorldObject GetWorldObjectBySpawnId(SpawnObjectType type, ulong spawnId) { switch (type) @@ -3881,6 +3960,8 @@ namespace Game.Maps return GetCreatureBySpawnId(spawnId); case SpawnObjectType.GameObject: return GetGameObjectBySpawnId(spawnId); + case SpawnObjectType.AreaTrigger: + return GetAreaTriggerBySpawnId(spawnId); default: return null; } @@ -4986,6 +5067,7 @@ namespace Game.Maps Dictionary _objectsStore = new(); MultiMap _creatureBySpawnIdStore = new(); MultiMap _gameobjectBySpawnIdStore = new(); + MultiMap _areaTriggerBySpawnIdStore = new(); MultiMap _corpsesByCell = new(); Dictionary _corpsesByPlayer = new(); List _corpseBones = new(); diff --git a/Source/Game/Maps/ObjectGridLoader.cs b/Source/Game/Maps/ObjectGridLoader.cs index 9e417f5d7..d44a38096 100644 --- a/Source/Game/Maps/ObjectGridLoader.cs +++ b/Source/Game/Maps/ObjectGridLoader.cs @@ -90,52 +90,18 @@ namespace Game.Maps { foreach (var guid in guid_set) { + // Don't spawn at all if there's a respawn timer + if (!map.ShouldBeSpawnedOnGridLoad(guid)) + continue; + T obj = new(); - // Don't spawn at all if there's a respawn time - if ((obj.IsTypeId(TypeId.Unit) && map.GetCreatureRespawnTime(guid) == 0) || (obj.IsTypeId(TypeId.GameObject) && map.GetGORespawnTime(guid) == 0) || obj.IsTypeId(TypeId.AreaTrigger)) + if (!obj.LoadFromDB(guid, map, false, false)) { - //TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid); - if (obj.IsTypeId(TypeId.Unit)) - { - CreatureData cdata = Global.ObjectMgr.GetCreatureData(guid); - Cypher.Assert(cdata != null, $"Tried to load creature with spawnId {guid}, but no such creature exists."); - - SpawnGroupTemplateData group = cdata.spawnGroupData; - // If creature in manual spawn group, don't spawn here, unless group is already active. - if (!group.flags.HasAnyFlag(SpawnGroupFlags.System)) - { - if (!map.IsSpawnGroupActive(group.groupId)) - { - obj.Dispose(); - continue; - } - } - } - else if (obj.IsTypeId(TypeId.GameObject)) - { - // If gameobject in manual spawn group, don't spawn here, unless group is already active. - GameObjectData godata = Global.ObjectMgr.GetGameObjectData(guid); - Cypher.Assert(godata != null, $"Tried to load gameobject with spawnId {guid}, but no such object exists."); - - if (!godata.spawnGroupData.flags.HasAnyFlag(SpawnGroupFlags.System)) - { - if (!map.IsSpawnGroupActive(godata.spawnGroupData.groupId)) - { - obj.Dispose(); - continue; - } - } - } - - if (!obj.LoadFromDB(guid, map, false, false)) - { - obj.Dispose(); - continue; - } - AddObjectHelper(cell, ref count, map, obj); - } - else obj.Dispose(); + continue; + } + + AddObjectHelper(cell, ref count, map, obj); } } diff --git a/Source/Game/Maps/SpawnData.cs b/Source/Game/Maps/SpawnData.cs index 1511bf603..2e2c3d694 100644 --- a/Source/Game/Maps/SpawnData.cs +++ b/Source/Game/Maps/SpawnData.cs @@ -29,29 +29,58 @@ namespace Game.Maps public SpawnGroupFlags flags; } - public class SpawnData + public class SpawnData : SpawnMetadata { - public SpawnObjectType type; - public ulong spawnId; public uint Id; // entry in respective _template table - public WorldLocation spawnPoint; - public PhaseUseFlagsValues phaseUseFlags; - public uint phaseId; - public uint phaseGroup; + public Position SpawnPoint; + public PhaseUseFlagsValues PhaseUseFlags; + public uint PhaseId; + public uint PhaseGroup; public int terrainSwapMap; public int spawntimesecs; public List spawnDifficulties; - public SpawnGroupTemplateData spawnGroupData; public uint ScriptId; - public bool dbData; - public SpawnData(SpawnObjectType t) + public SpawnData(SpawnObjectType t) : base(t) { - type = t; - spawnPoint = new WorldLocation(); + SpawnPoint = new Position(); terrainSwapMap = -1; spawnDifficulties = new List(); - dbData = true; + } + + public static SpawnObjectType TypeFor() + { + switch (typeof(T).Name) + { + case nameof(Creature): + return SpawnObjectType.Creature; + case nameof(GameObject): + return SpawnObjectType.GameObject; + case nameof(AreaTrigger): + return SpawnObjectType.AreaTrigger; + default: + return SpawnObjectType.NumSpawnTypes; + } } } + + public class SpawnMetadata + { + public SpawnObjectType type; + public ulong SpawnId; + public uint MapId = 0xFFFFFFFF; + public bool dbData = true; + public SpawnGroupTemplateData spawnGroupData = null; + + public static bool TypeInMask(SpawnObjectType type, SpawnObjectTypeMask mask) { return ((1 << (int)type) & (int)mask) != 0; } + public static bool TypeHasData(SpawnObjectType type) { return type < SpawnObjectType.NumSpawnTypesWithData; } + public static bool TypeIsValid(SpawnObjectType type) { return type < SpawnObjectType.NumSpawnTypes; } + + public SpawnMetadata(SpawnObjectType t) + { + type = t; + } + + public SpawnData ToSpawnData() { return TypeHasData(type) ? (SpawnData)this : null; } + } } diff --git a/Source/Game/Pools/PoolManager.cs b/Source/Game/Pools/PoolManager.cs index 06797d3c8..3458d36cc 100644 --- a/Source/Game/Pools/PoolManager.cs +++ b/Source/Game/Pools/PoolManager.cs @@ -383,6 +383,8 @@ namespace Game return IsPartOfAPool(spawnId); case SpawnObjectType.GameObject: return IsPartOfAPool(spawnId); + case SpawnObjectType.AreaTrigger: + return 0; default: Cypher.Assert(false, $"Invalid spawn type {type} passed to PoolMgr.IsPartOfPool (with spawnId {spawnId})"); return 0; @@ -481,7 +483,7 @@ namespace Game if (data != null) { Global.ObjectMgr.RemoveCreatureFromGrid(guid, data); - Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0); + Map map = Global.MapMgr.FindMap(data.MapId, 0); if (map != null && !map.Instanceable()) { var creatureBounds = map.GetCreatureBySpawnIdStore().LookupByKey(guid); @@ -503,7 +505,7 @@ namespace Game if (data != null) { Global.ObjectMgr.RemoveGameObjectFromGrid(guid, data); - Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0); + Map map = Global.MapMgr.FindMap(data.MapId, 0); if (map != null && !map.Instanceable()) { var gameobjectBounds = map.GetGameObjectBySpawnIdStore().LookupByKey(guid); @@ -620,9 +622,9 @@ namespace Game Global.ObjectMgr.AddCreatureToGrid(obj.guid, data); // Spawn if necessary (loaded grids only) - Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0); + Map map = Global.MapMgr.FindMap(data.MapId, 0); // We use spawn coords to spawn - if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint)) + if (map != null && !map.Instanceable() && map.IsGridLoaded(data.SpawnPoint)) Creature.CreateCreatureFromDB(obj.guid, map); } } @@ -635,9 +637,9 @@ namespace Game Global.ObjectMgr.AddGameObjectToGrid(obj.guid, data); // Spawn if necessary (loaded grids only) // this base map checked as non-instanced and then only existed - Map map = Global.MapMgr.FindMap(data.spawnPoint.GetMapId(), 0); + Map map = Global.MapMgr.FindMap(data.MapId, 0); // We use current coords to unspawn, not spawn coords since creature can have changed grid - if (map != null && !map.Instanceable() && map.IsGridLoaded(data.spawnPoint)) + if (map != null && !map.Instanceable() && map.IsGridLoaded(data.SpawnPoint)) { GameObject go = GameObject.CreateGameObjectFromDB(obj.guid, map, false); if (go)